openxiangda-devkit-core 2.11.0 → 2.13.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/dist/application-services.d.ts.map +1 -1
- package/dist/compiler/application-model.d.ts +8 -0
- package/dist/compiler/application-model.d.ts.map +1 -1
- package/dist/compiler/application-model.js +14 -2
- package/dist/compiler/application-model.js.map +1 -1
- package/dist/compiler/bundle.d.ts.map +1 -1
- package/dist/compiler/bundle.js +25 -7
- package/dist/compiler/bundle.js.map +1 -1
- package/dist/compiler/config.d.ts +12 -1
- package/dist/compiler/config.d.ts.map +1 -1
- package/dist/compiler/config.js +63 -6
- package/dist/compiler/config.js.map +1 -1
- package/dist/compiler/permission-review.d.ts.map +1 -1
- package/dist/development-lifecycle.d.ts.map +1 -1
- package/dist/development-lifecycle.js +11 -3
- package/dist/development-lifecycle.js.map +1 -1
- package/dist/documentation.d.ts +7 -3
- package/dist/documentation.d.ts.map +1 -1
- package/dist/documentation.js +1 -0
- package/dist/documentation.js.map +1 -1
- package/package.json +2 -2
package/dist/compiler/config.js
CHANGED
|
@@ -2441,7 +2441,7 @@ function validateCapabilityDeclarations(appCode, declarations, diagnostics) {
|
|
|
2441
2441
|
diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_CAPABILITY_DECLARATION_INVALID', '显式 capability 必须属于当前应用、code 唯一,并声明 backend/ui kind 与 name', path));
|
|
2442
2442
|
}
|
|
2443
2443
|
if (platformCapabilityCodes(appCode).has(code)) {
|
|
2444
|
-
diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_PLATFORM_CAPABILITY_RESERVED', '平台 capability 由平台保留 catalog 提供,应用不能重复声明', `${path}.code`));
|
|
2444
|
+
diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_PLATFORM_CAPABILITY_RESERVED', '平台 capability 由平台保留 catalog 提供,应用不能重复声明', `${path}.code`, `删除 capabilities 里的该声明,直接在角色中引用即可,例如 roles: [{ code: 'admin', capabilities: [directoryRead] }],其中 directoryRead = app:${appCode}:directory:read`));
|
|
2445
2445
|
}
|
|
2446
2446
|
seen.add(code);
|
|
2447
2447
|
});
|
|
@@ -2649,7 +2649,7 @@ function validateBackendOperations(rawOperations, declaredResources, declaredWor
|
|
|
2649
2649
|
Number(ai.timeoutMs) < 100 ||
|
|
2650
2650
|
Number(ai.timeoutMs) > 30000)) ||
|
|
2651
2651
|
aiKeys.some(key => !allowedAiKeys.has(key))) {
|
|
2652
|
-
diagnostics.push(diagnostic('APP_CONFIG_BACKEND_OPERATION_AI_INVALID', 'AI 操作必须显式声明名称、说明、风险、现有资源、副作用和有界执行策略;GET 只能只读,写操作必须确认', `${path}.ai
|
|
2652
|
+
diagnostics.push(diagnostic('APP_CONFIG_BACKEND_OPERATION_AI_INVALID', 'AI 操作必须显式声明名称、说明、风险、现有资源、副作用和有界执行策略;GET 只能只读,写操作必须确认', `${path}.ai`, '写操作的 ai.sideEffects 至少列一条具体副作用,例如 sideEffects: [\'更新报修单状态为处理中\', \'写入一条派工记录\'];resources 必须引用已声明资源(1-16 个)'));
|
|
2653
2653
|
}
|
|
2654
2654
|
}
|
|
2655
2655
|
codes.add(code);
|
|
@@ -3297,6 +3297,13 @@ function validateApplicationAuthentication(config, diagnostics) {
|
|
|
3297
3297
|
return [string(route.code), route];
|
|
3298
3298
|
}));
|
|
3299
3299
|
const routeCodes = new Set(routeByCode.keys());
|
|
3300
|
+
// 生成式用户标准面的路由码(仅参与登录默认路由解析,不占用声明码空间)。
|
|
3301
|
+
const declaredResources = (Array.isArray(object(config.data).resources)
|
|
3302
|
+
? object(config.data).resources
|
|
3303
|
+
: []);
|
|
3304
|
+
const userSurfaceResourceCodes = new Set(declaredResources
|
|
3305
|
+
.filter(raw => object(object(raw).userSurface))
|
|
3306
|
+
.map(raw => string(object(raw).code)));
|
|
3300
3307
|
const claimedShapes = new Map(generatedPlatformRouteClaims(config).map(claim => [
|
|
3301
3308
|
canonicalFrontendRouteShape(claim.path),
|
|
3302
3309
|
claim,
|
|
@@ -3309,7 +3316,21 @@ function validateApplicationAuthentication(config, diagnostics) {
|
|
|
3309
3316
|
const routeCode = string(surface.routeCode);
|
|
3310
3317
|
const routePath = string(surface.path);
|
|
3311
3318
|
const defaultRouteCode = string(surface.defaultRouteCode);
|
|
3312
|
-
const
|
|
3319
|
+
const generatedUserRoute = /^user:([a-z][a-z0-9-]{0,62}):(records|submit)$/.exec(defaultRouteCode);
|
|
3320
|
+
const defaultRoute = generatedUserRoute
|
|
3321
|
+
? userSurfaceResourceCodes.has(generatedUserRoute[1])
|
|
3322
|
+
? {
|
|
3323
|
+
surface: 'user',
|
|
3324
|
+
path: generatedUserRoute[2] === 'records'
|
|
3325
|
+
? device === 'mobile'
|
|
3326
|
+
? `/m/my/${generatedUserRoute[1]}`
|
|
3327
|
+
: `/my/${generatedUserRoute[1]}`
|
|
3328
|
+
: device === 'mobile'
|
|
3329
|
+
? `/m/my/${generatedUserRoute[1]}/submit`
|
|
3330
|
+
: `/my/${generatedUserRoute[1]}/submit`,
|
|
3331
|
+
}
|
|
3332
|
+
: undefined
|
|
3333
|
+
: routeByCode.get(defaultRouteCode);
|
|
3313
3334
|
const shape = canonicalFrontendRouteShape(routePath);
|
|
3314
3335
|
const expectedPath = device === 'mobile' ? '/m/login' : '/login';
|
|
3315
3336
|
const invalid = Object.keys(surface).some(key => !['routeCode', 'path', 'defaultRouteCode'].includes(key)) ||
|
|
@@ -3730,6 +3751,12 @@ export function resourceReadPolicy(input) {
|
|
|
3730
3751
|
}
|
|
3731
3752
|
export function materializeDataResource(appCode, declaration) {
|
|
3732
3753
|
const capabilities = resourceCapabilityCodes(appCode, declaration.code);
|
|
3754
|
+
// audit.read: true 是声明糖:绑定本资源的 read 能力。
|
|
3755
|
+
const auditRead = declaration.audit === undefined || declaration.audit.read === false
|
|
3756
|
+
? false
|
|
3757
|
+
: declaration.audit.read === true
|
|
3758
|
+
? [capabilities.read]
|
|
3759
|
+
: [...declaration.audit.read];
|
|
3733
3760
|
const mutationOwner = declaration.mutationOwner || 'native';
|
|
3734
3761
|
const nativeMutations = mutationOwner === 'native';
|
|
3735
3762
|
const generated = {
|
|
@@ -3876,6 +3903,7 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
3876
3903
|
...(declaration.dataPolicyCode
|
|
3877
3904
|
? { dataPolicyCode: declaration.dataPolicyCode }
|
|
3878
3905
|
: {}),
|
|
3906
|
+
...(declaration.userSurface ? { userSurface: { ...declaration.userSurface } } : {}),
|
|
3879
3907
|
...(declaration.detailRouteCode
|
|
3880
3908
|
? { detailRouteCode: { ...declaration.detailRouteCode } }
|
|
3881
3909
|
: {}),
|
|
@@ -3890,7 +3918,7 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
3890
3918
|
},
|
|
3891
3919
|
])),
|
|
3892
3920
|
...(declaration.audit === undefined ? {} : Object.fromEntries(DATA_AUDIT_METADATA_FIELDS.map(code => [code, {
|
|
3893
|
-
read:
|
|
3921
|
+
read: auditRead === false ? [] : [...auditRead],
|
|
3894
3922
|
}]))),
|
|
3895
3923
|
},
|
|
3896
3924
|
};
|
|
@@ -3915,6 +3943,7 @@ export function validateAppDeclaration(value) {
|
|
|
3915
3943
|
'mobile',
|
|
3916
3944
|
'dataPolicyCode',
|
|
3917
3945
|
'detailRouteCode',
|
|
3946
|
+
'userSurface',
|
|
3918
3947
|
'audit',
|
|
3919
3948
|
]);
|
|
3920
3949
|
const fieldKeys = new Set([
|
|
@@ -3960,10 +3989,21 @@ export function validateAppDeclaration(value) {
|
|
|
3960
3989
|
const read = audit.read;
|
|
3961
3990
|
if (!resource.audit || typeof resource.audit !== 'object' || Array.isArray(resource.audit) ||
|
|
3962
3991
|
Object.keys(audit).some(key => key !== 'read') ||
|
|
3963
|
-
(read !== false && (!Array.isArray(read) || read.length < 1 || read.length > 20 ||
|
|
3992
|
+
(read !== false && read !== true && (!Array.isArray(read) || read.length < 1 || read.length > 20 ||
|
|
3964
3993
|
read.some(value => typeof value !== 'string' || !value.trim()) || new Set(read).size !== read.length))) {
|
|
3965
3994
|
auditValid = false;
|
|
3966
|
-
diagnostics.push(diagnostic('APP_CONFIG_DATA_AUDIT_READ_INVALID', 'audit.read 必须为 false 或不重复的非空 capability 数组(最多 20 项)', `${resourcePath}.audit`));
|
|
3995
|
+
diagnostics.push(diagnostic('APP_CONFIG_DATA_AUDIT_READ_INVALID', 'audit.read 必须为 true(绑定资源读能力)、false 或不重复的非空 capability 数组(最多 20 项)', `${resourcePath}.audit`, `audit: { read: true } 会把审计读取绑定到本资源的 read 能力;需要更细粒度时写能力数组,例如 audit: { read: ['app:${appCode}:data:<resource>:read'] }`));
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
const userSurface = resource.userSurface;
|
|
3999
|
+
if (userSurface !== undefined) {
|
|
4000
|
+
const surface = object(userSurface);
|
|
4001
|
+
const keys = Object.keys(surface).filter(key => !['home', 'listLabel', 'submitLabel'].includes(key));
|
|
4002
|
+
const labels = [surface.listLabel, surface.submitLabel];
|
|
4003
|
+
if (keys.length ||
|
|
4004
|
+
(surface.home !== undefined && typeof surface.home !== 'boolean') ||
|
|
4005
|
+
labels.some(label => label !== undefined && (typeof label !== 'string' || !label.trim() || label.length > 64))) {
|
|
4006
|
+
diagnostics.push(diagnostic('APP_CONFIG_DATA_USER_SURFACE_INVALID', 'userSurface 只接受 home 布尔与 listLabel/submitLabel(1-64 字符)', `${resourcePath}.userSurface`, `例如 userSurface: { home: true, listLabel: '我的领用', submitLabel: '新建领用' };直接在 CRUD 视图上写 user: true 即可自动生成`));
|
|
3967
4007
|
}
|
|
3968
4008
|
}
|
|
3969
4009
|
const mutationOwner = string(resource.mutationOwner) || 'native';
|
|
@@ -4215,6 +4255,23 @@ export function defineOpenXiangdaApp(declaration) {
|
|
|
4215
4255
|
data: { resources: [...(declaration.data?.resources || []), ...projected.resources] },
|
|
4216
4256
|
} : {}),
|
|
4217
4257
|
});
|
|
4258
|
+
// 生成式用户标准面:登录默认路由仍是模板占位时,指向首页资源的标准页。
|
|
4259
|
+
const homeResourceCode = (normalizedDeclaration.data?.resources || [])
|
|
4260
|
+
.filter(resource => resource.userSurface)
|
|
4261
|
+
.sort((left, right) => left.code.localeCompare(right.code))
|
|
4262
|
+
.find(resource => resource.userSurface?.home === true)?.code
|
|
4263
|
+
|| (normalizedDeclaration.data?.resources || [])
|
|
4264
|
+
.filter(resource => resource.userSurface)
|
|
4265
|
+
.sort((left, right) => left.code.localeCompare(right.code))[0]?.code;
|
|
4266
|
+
if (homeResourceCode && normalizedDeclaration.frontend.authentication) {
|
|
4267
|
+
const surfaces = normalizedDeclaration.frontend.authentication.surfaces;
|
|
4268
|
+
if (surfaces.desktop.defaultRouteCode === 'application-home') {
|
|
4269
|
+
surfaces.desktop.defaultRouteCode = `user:${homeResourceCode}:records`;
|
|
4270
|
+
}
|
|
4271
|
+
if (surfaces.mobile.defaultRouteCode === 'application-home-mobile') {
|
|
4272
|
+
surfaces.mobile.defaultRouteCode = `user:${homeResourceCode}:records`;
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4218
4275
|
const declarationDiagnostics = validateAppDeclaration(normalizedDeclaration);
|
|
4219
4276
|
if (declarationDiagnostics.length > 0) {
|
|
4220
4277
|
throw new AppConfigValidationError(declarationDiagnostics);
|