openxiangda-devkit-core 2.41.6 → 2.48.2
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 +5 -1
- package/dist/compiler/application-model.d.ts.map +1 -1
- package/dist/compiler/application-model.js +5 -1
- package/dist/compiler/application-model.js.map +1 -1
- package/dist/compiler/bundle.d.ts.map +1 -1
- package/dist/compiler/bundle.js +51 -14
- package/dist/compiler/bundle.js.map +1 -1
- package/dist/compiler/config.d.ts +15 -0
- package/dist/compiler/config.d.ts.map +1 -1
- package/dist/compiler/config.js +161 -12
- package/dist/compiler/config.js.map +1 -1
- package/dist/compiler/permission-review.d.ts.map +1 -1
- package/dist/connected-development.d.ts.map +1 -1
- package/dist/connected-development.js +6 -1
- package/dist/connected-development.js.map +1 -1
- package/dist/deployment.d.ts.map +1 -1
- package/dist/deployment.js +6 -1
- package/dist/deployment.js.map +1 -1
- package/dist/internal/workflow.d.ts.map +1 -1
- package/dist/internal/workflow.js +5 -3
- package/dist/internal/workflow.js.map +1 -1
- package/package.json +2 -2
package/dist/compiler/config.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { validateUserCandidateReferences, validateWorkflowUserCandidateBindings, UserCandidateContractError } from 'openxiangda-contracts';
|
|
1
2
|
import { parseNativeUniqueKeys, DATA_SUBTABLE_MAX_TOTAL_ROWS } from 'openxiangda-contracts';
|
|
2
3
|
import { validateManagedConcurrency } from 'openxiangda-contracts/native-compiler';
|
|
3
4
|
import { normalizeDecimalReservationEventDeclaration, decimalReservationEventContext } from 'openxiangda-contracts';
|
|
4
5
|
import { materializeApplicationModules } from './application-model.js';
|
|
5
6
|
import { nativeFieldRequiresCreateInputV2 } from 'openxiangda-contracts/native-compiler';
|
|
6
7
|
import { NativeDecimalLifecycleContractError, validateDecimalReservationLifecycles } from 'openxiangda-contracts/native-compiler';
|
|
7
|
-
import { SCHEMA_VERSIONS, DATA_AUDIT_METADATA_FIELDS, isDataAuditMetadataField, isDataSystemSortField, DATA_FIELD_TYPES, PLATFORM_EVENT_TYPES_V2, nativePlatformCapabilityCatalog, validateDataResource, validateWorkflowInstanceCommandPolicies, validateWorkflowTaskPages, validateWorkflowAssignmentRoutingBindings, WORKFLOW_SUMMARY_MAX_FIELDS, WORKFLOW_SUMMARY_TEXT_LONG_MAX_BYTES, isWorkflowSummaryFieldType, } from 'openxiangda-contracts';
|
|
8
|
+
import { SCHEMA_VERSIONS, DATA_AUDIT_METADATA_FIELDS, isDataAuditMetadataField, isDataSystemSortField, DATA_FIELD_TYPES, PLATFORM_EVENT_TYPES_V2, nativePlatformCapabilityCatalog, validateDataResource, validateWorkflowInstanceCommandPolicies, validateWorkflowTaskPages, validateWorkflowCommandHandlers, validateWorkflowAssignmentRoutingBindings, WORKFLOW_SUMMARY_MAX_FIELDS, WORKFLOW_SUMMARY_TEXT_LONG_MAX_BYTES, isWorkflowSummaryFieldType, } from 'openxiangda-contracts';
|
|
8
9
|
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
9
10
|
import { CronExpressionParser } from 'cron-parser';
|
|
10
11
|
import { fieldNullable, supportsGeneratedMutation, } from './field-codec.js';
|
|
@@ -966,6 +967,7 @@ export function validateAppConfig(value) {
|
|
|
966
967
|
secretNames.add(name);
|
|
967
968
|
secretEnvs.add(env);
|
|
968
969
|
});
|
|
970
|
+
const authz = object(config.authz);
|
|
969
971
|
const data = object(config.data);
|
|
970
972
|
if (config.data !== undefined && !Array.isArray(data.resources)) {
|
|
971
973
|
diagnostics.push(diagnostic('APP_CONFIG_DATA_RESOURCES_REQUIRED', 'data.resources 必须是数组', 'data.resources'));
|
|
@@ -995,6 +997,15 @@ export function validateAppConfig(value) {
|
|
|
995
997
|
resources.forEach((resource, index) => {
|
|
996
998
|
const resourceRecord = object(resource);
|
|
997
999
|
const fields = object(resourceRecord.schema).fields;
|
|
1000
|
+
try {
|
|
1001
|
+
validateUserCandidateReferences((Array.isArray(fields) ? fields : []), new Set((Array.isArray(authz.roles) ? authz.roles : []).map(item => string(object(item).code))), new Set((Array.isArray(authz.scopeDimensions) ? authz.scopeDimensions : []).map(item => string(object(item).code))), `data.resources[${index}].schema.fields`);
|
|
1002
|
+
}
|
|
1003
|
+
catch (error) {
|
|
1004
|
+
if (!(error instanceof UserCandidateContractError))
|
|
1005
|
+
throw error;
|
|
1006
|
+
const issue = error;
|
|
1007
|
+
diagnostics.push(diagnostic(issue.code, '人员候选必须引用同一应用声明的职责、范围和兼容字段', issue.pointer));
|
|
1008
|
+
}
|
|
998
1009
|
(Array.isArray(fields) ? fields : []).forEach((rawField, fieldIndex) => {
|
|
999
1010
|
const field = object(rawField);
|
|
1000
1011
|
const source = object(field.source);
|
|
@@ -1048,7 +1059,6 @@ export function validateAppConfig(value) {
|
|
|
1048
1059
|
});
|
|
1049
1060
|
});
|
|
1050
1061
|
}
|
|
1051
|
-
const authz = object(config.authz);
|
|
1052
1062
|
const declaredCapabilities = Array.isArray(authz.capabilities)
|
|
1053
1063
|
? authz.capabilities
|
|
1054
1064
|
: [];
|
|
@@ -1069,7 +1079,13 @@ export function validateAppConfig(value) {
|
|
|
1069
1079
|
const mutationOwner = string(object(resource.surface).mutationOwner) || 'native';
|
|
1070
1080
|
if (['native', 'queued-command'].includes(mutationOwner))
|
|
1071
1081
|
continue;
|
|
1082
|
+
const recordDeletionCapabilities = object(resource.recordDeletion).delete;
|
|
1072
1083
|
for (const operation of ['create', 'update', 'delete']) {
|
|
1084
|
+
// This capability authorizes the server's controlled deletion entry;
|
|
1085
|
+
// ordinary Native mutations still belong to the declared owner.
|
|
1086
|
+
if (operation === 'delete' && Array.isArray(recordDeletionCapabilities) &&
|
|
1087
|
+
recordDeletionCapabilities.length > 0)
|
|
1088
|
+
continue;
|
|
1073
1089
|
const capability = string(object(resource.capabilities)[operation]);
|
|
1074
1090
|
if (capability) {
|
|
1075
1091
|
forbiddenNativeMutationCapabilities.set(capability, {
|
|
@@ -2397,7 +2413,7 @@ export function validateAppConfig(value) {
|
|
|
2397
2413
|
}
|
|
2398
2414
|
}
|
|
2399
2415
|
}
|
|
2400
|
-
for (const error of validateWorkflowDefinition(definition)) {
|
|
2416
|
+
for (const error of [...validateWorkflowDefinition(definition), ...validateWorkflowCommandHandlers(definition, object(config.backend).operations)]) {
|
|
2401
2417
|
diagnostics.push(diagnostic('APP_CONFIG_WORKFLOW_DEFINITION_INVALID', error, path));
|
|
2402
2418
|
}
|
|
2403
2419
|
const policyResource = object((Array.isArray(data.resources) ? data.resources : []).find(resource => object(resource).code === definition.subject?.resourceCode));
|
|
@@ -2474,6 +2490,18 @@ export function validateAppConfig(value) {
|
|
|
2474
2490
|
for (const error of validateWorkflowBinding(definition, binding)) {
|
|
2475
2491
|
diagnostics.push(diagnostic('APP_CONFIG_WORKFLOW_BINDING_INVALID', error, path));
|
|
2476
2492
|
}
|
|
2493
|
+
try {
|
|
2494
|
+
const resource = (Array.isArray(data.resources) ? data.resources : []).map(object).find(item => item.code === definition.subject.resourceCode);
|
|
2495
|
+
const fields = object(resource?.schema).fields;
|
|
2496
|
+
validateWorkflowUserCandidateBindings(definition, binding, new Map((Array.isArray(fields) ? fields : []).map(item => [String(object(item).code), item])), `${path}.bindings`, new Map((Array.isArray(data.resources) ? data.resources : []).map(object).map(resource => [String(resource.code),
|
|
2497
|
+
new Map((Array.isArray(object(resource.schema).fields) ? object(resource.schema).fields : []).map(field => [field.code, field]))])));
|
|
2498
|
+
}
|
|
2499
|
+
catch (error) {
|
|
2500
|
+
if (!(error instanceof UserCandidateContractError))
|
|
2501
|
+
throw error;
|
|
2502
|
+
const issue = error;
|
|
2503
|
+
diagnostics.push(diagnostic(issue.code, '流程选人必须引用原人员字段及固定范围事实,办理页不能修改选人范围', issue.pointer));
|
|
2504
|
+
}
|
|
2477
2505
|
activeRoutingBindings.push(binding);
|
|
2478
2506
|
for (const entry of Object.values(binding.bindings || {})) {
|
|
2479
2507
|
if (entry.provider === 'application_provider' &&
|
|
@@ -2771,10 +2799,24 @@ function validateBackendOperations(rawOperations, declaredResources, declaredRes
|
|
|
2771
2799
|
'workflow',
|
|
2772
2800
|
'roleAssertions',
|
|
2773
2801
|
'decimalReservation',
|
|
2802
|
+
'dataCommands',
|
|
2803
|
+
'recordEdit',
|
|
2774
2804
|
]);
|
|
2775
2805
|
let invalid = !isRecord(operation.platformAccess) ||
|
|
2776
2806
|
Object.keys(access).length === 0 ||
|
|
2777
2807
|
Object.keys(access).some(key => !allowedAccessKeys.has(key));
|
|
2808
|
+
if (access.dataCommands !== undefined) {
|
|
2809
|
+
const commands = object(access.dataCommands);
|
|
2810
|
+
invalid ||= commands.mode !== 'recoverable-native' || Object.keys(commands).some(key => key !== 'mode');
|
|
2811
|
+
}
|
|
2812
|
+
if (access.recordEdit !== undefined) {
|
|
2813
|
+
const edit = object(access.recordEdit);
|
|
2814
|
+
const fields = Array.isArray(edit.fieldCodes) ? edit.fieldCodes : [];
|
|
2815
|
+
invalid ||= !isRecord(access.recordEdit) || !string(edit.resourceCode) || fields.length < 1 || fields.length > 200 ||
|
|
2816
|
+
new Set(fields).size !== fields.length || fields.some(field => !string(field)) ||
|
|
2817
|
+
Object.keys(edit).some(key => !['resourceCode', 'fieldCodes'].includes(key)) ||
|
|
2818
|
+
object(access.dataCommands).mode !== 'recoverable-native';
|
|
2819
|
+
}
|
|
2778
2820
|
if (access.roleAssertions !== undefined) {
|
|
2779
2821
|
const assertion = object(access.roleAssertions);
|
|
2780
2822
|
const roles = Array.isArray(assertion.roleCodes) ? assertion.roleCodes : [];
|
|
@@ -2790,6 +2832,7 @@ function validateBackendOperations(rawOperations, declaredResources, declaredRes
|
|
|
2790
2832
|
'employeeNumber',
|
|
2791
2833
|
'primaryDepartment',
|
|
2792
2834
|
'departments',
|
|
2835
|
+
...(directory.mode === 'current-initiator' ? ['phone'] : []),
|
|
2793
2836
|
]);
|
|
2794
2837
|
invalid ||=
|
|
2795
2838
|
!['current-initiator', 'selected-user'].includes(String(directory.mode)) ||
|
|
@@ -2891,7 +2934,12 @@ function validateBackendOperations(rawOperations, declaredResources, declaredRes
|
|
|
2891
2934
|
workflowCodes.length > 16 ||
|
|
2892
2935
|
new Set(workflowCodes).size !== workflowCodes.length ||
|
|
2893
2936
|
workflowCodes.some(code => !declaredWorkflowCodes.has(code)) ||
|
|
2894
|
-
Object.keys(workflow).some(key =>
|
|
2937
|
+
Object.keys(workflow).some(key => !['codes', 'businessCommands'].includes(key));
|
|
2938
|
+
if (workflow.businessCommands !== undefined) {
|
|
2939
|
+
const commands = workflow.businessCommands;
|
|
2940
|
+
invalid ||= !Array.isArray(commands) || commands.length === 0 || commands.length > 3 ||
|
|
2941
|
+
new Set(commands).size !== commands.length || commands.some(command => !['approve', 'reject', 'withdraw'].includes(command));
|
|
2942
|
+
}
|
|
2895
2943
|
}
|
|
2896
2944
|
if (access.decimalReservation !== undefined) {
|
|
2897
2945
|
const reservation = object(access.decimalReservation);
|
|
@@ -4226,7 +4274,10 @@ function validateCapabilityClosure(appCode, config, declarations, diagnostics) {
|
|
|
4226
4274
|
if (code)
|
|
4227
4275
|
catalogOwners.set(code, 'authz.capabilities');
|
|
4228
4276
|
}
|
|
4229
|
-
|
|
4277
|
+
const resources = Array.isArray(data.resources) ? data.resources : [];
|
|
4278
|
+
// Register every real definition before inspecting field references. A field
|
|
4279
|
+
// may reference a later resource without claiming that resource's catalog.
|
|
4280
|
+
for (const rawResource of resources) {
|
|
4230
4281
|
const resource = object(rawResource);
|
|
4231
4282
|
const resourceOwner = `data.resources.${string(resource.code)}`;
|
|
4232
4283
|
Object.values(object(resource.capabilities)).forEach(value => {
|
|
@@ -4234,18 +4285,42 @@ function validateCapabilityClosure(appCode, config, declarations, diagnostics) {
|
|
|
4234
4285
|
registerCapabilityOwner(code, resourceOwner, catalogOwners, diagnostics);
|
|
4235
4286
|
catalog.add(code);
|
|
4236
4287
|
});
|
|
4288
|
+
}
|
|
4289
|
+
const definedCapabilities = new Set(catalog);
|
|
4290
|
+
for (const [resourceIndex, rawResource] of resources.entries()) {
|
|
4291
|
+
const resource = object(rawResource);
|
|
4292
|
+
const resourceOwner = `data.resources.${string(resource.code)}`;
|
|
4293
|
+
for (const mode of ['read', 'create']) {
|
|
4294
|
+
const list = object(resource.recordComments)[mode];
|
|
4295
|
+
for (const capability of Array.isArray(list) ? list : []) {
|
|
4296
|
+
references.push({ capability: string(capability), path: `data.resources[${resourceIndex}].recordComments.${mode}` });
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
const printRead = object(resource.recordPrint).read;
|
|
4300
|
+
const recordDelete = object(resource.recordDeletion).delete;
|
|
4301
|
+
for (const capability of Array.isArray(recordDelete) ? recordDelete : []) {
|
|
4302
|
+
references.push({ capability: string(capability), path: `data.resources[${resourceIndex}].recordDeletion.delete` });
|
|
4303
|
+
}
|
|
4304
|
+
for (const capability of Array.isArray(printRead) ? printRead : []) {
|
|
4305
|
+
references.push({ capability: string(capability), path: `data.resources[${resourceIndex}].recordPrint.read` });
|
|
4306
|
+
}
|
|
4307
|
+
const historyRead = object(resource.workflowHistory).read;
|
|
4308
|
+
for (const capability of Array.isArray(historyRead) ? historyRead : []) {
|
|
4309
|
+
references.push({ capability: string(capability), path: `data.resources[${resourceIndex}].workflowHistory.read` });
|
|
4310
|
+
}
|
|
4237
4311
|
Object.entries(object(resource.fieldPolicies)).forEach(([fieldCode, rawPolicy]) => {
|
|
4238
4312
|
const policy = object(rawPolicy);
|
|
4239
4313
|
for (const key of ['read', 'create', 'update']) {
|
|
4240
4314
|
for (const value of Array.isArray(policy[key]) ? policy[key] : []) {
|
|
4241
4315
|
const code = string(value);
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4316
|
+
references.push({
|
|
4317
|
+
capability: code,
|
|
4318
|
+
path: `data.resources[${resourceIndex}].fieldPolicies.${fieldCode}.${key}`,
|
|
4319
|
+
});
|
|
4320
|
+
if (isDataAuditMetadataField(fieldCode) || definedCapabilities.has(code))
|
|
4247
4321
|
continue;
|
|
4248
|
-
|
|
4322
|
+
// Preserve resource-owned implicit field capabilities. Reusing an
|
|
4323
|
+
// explicit/CRUD definition is different from two implicit owners.
|
|
4249
4324
|
registerCapabilityOwner(code, resourceOwner, catalogOwners, diagnostics);
|
|
4250
4325
|
catalog.add(code);
|
|
4251
4326
|
}
|
|
@@ -4444,6 +4519,7 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
4444
4519
|
Boolean(field.filter || field.searchable || field.sortable),
|
|
4445
4520
|
...(field.options ? { options: field.options } : {}),
|
|
4446
4521
|
...(field.source ? { source: field.source } : {}),
|
|
4522
|
+
...(field.userCandidates !== undefined ? { userCandidates: field.userCandidates } : {}),
|
|
4447
4523
|
...(field.maxLength !== undefined ? { maxLength: field.maxLength } : {}),
|
|
4448
4524
|
...(field.precision !== undefined ? { precision: field.precision } : {}),
|
|
4449
4525
|
...(field.scale !== undefined ? { scale: field.scale } : {}),
|
|
@@ -4525,6 +4601,7 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
4525
4601
|
...(field.file?.accept ? { accept: field.file.accept } : {}),
|
|
4526
4602
|
...(field.options ? { options: field.options } : {}),
|
|
4527
4603
|
...(field.source ? { source: field.source } : {}),
|
|
4604
|
+
...(field.userCandidates !== undefined ? { userCandidates: field.userCandidates } : {}),
|
|
4528
4605
|
...(field.timePrecision ? { timePrecision: field.timePrecision } : {}),
|
|
4529
4606
|
...(field.serial ? { serial: field.serial } : {}),
|
|
4530
4607
|
...(field.subtable ? { subtable: field.subtable } : {}),
|
|
@@ -4572,6 +4649,24 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
4572
4649
|
...(declaration.invariants ? { invariants: declaration.invariants } : {}),
|
|
4573
4650
|
...(declaration.decimalReservationLifecycle !== undefined ? { decimalReservationLifecycle: declaration.decimalReservationLifecycle } : {}),
|
|
4574
4651
|
...(uniqueKeys !== undefined ? { uniqueKeys } : {}),
|
|
4652
|
+
...(declaration.recordComments === undefined ? {} : { recordComments: {
|
|
4653
|
+
read: declaration.recordComments.read === false ? [] : declaration.recordComments.read === true
|
|
4654
|
+
? [capabilities.read] : [...declaration.recordComments.read],
|
|
4655
|
+
create: declaration.recordComments.create === false ? [] : declaration.recordComments.create === true
|
|
4656
|
+
? [capabilities.read] : [...declaration.recordComments.create],
|
|
4657
|
+
} }),
|
|
4658
|
+
...(declaration.recordPrint === undefined ? {} : { recordPrint: {
|
|
4659
|
+
read: declaration.recordPrint.read === false ? [] : declaration.recordPrint.read === true
|
|
4660
|
+
? [capabilities.read] : [...declaration.recordPrint.read],
|
|
4661
|
+
} }),
|
|
4662
|
+
...(declaration.recordDeletion === undefined ? {} : { recordDeletion: {
|
|
4663
|
+
delete: declaration.recordDeletion.delete === false ? [] : declaration.recordDeletion.delete === true
|
|
4664
|
+
? [capabilities.delete] : [...declaration.recordDeletion.delete],
|
|
4665
|
+
} }),
|
|
4666
|
+
...(declaration.workflowHistory === undefined ? {} : { workflowHistory: {
|
|
4667
|
+
read: declaration.workflowHistory.read === false ? [] : declaration.workflowHistory.read === true
|
|
4668
|
+
? [capabilities.read] : [...declaration.workflowHistory.read],
|
|
4669
|
+
} }),
|
|
4575
4670
|
surface,
|
|
4576
4671
|
capabilities,
|
|
4577
4672
|
...(declaration.dataPolicyCode
|
|
@@ -4621,6 +4716,10 @@ export function validateAppDeclaration(value) {
|
|
|
4621
4716
|
'detailRouteCode',
|
|
4622
4717
|
'userSurface',
|
|
4623
4718
|
'audit',
|
|
4719
|
+
'workflowHistory',
|
|
4720
|
+
'recordPrint',
|
|
4721
|
+
'recordComments',
|
|
4722
|
+
'recordDeletion',
|
|
4624
4723
|
]);
|
|
4625
4724
|
const fieldKeys = new Set([
|
|
4626
4725
|
'code',
|
|
@@ -4630,6 +4729,7 @@ export function validateAppDeclaration(value) {
|
|
|
4630
4729
|
'indexed',
|
|
4631
4730
|
'options',
|
|
4632
4731
|
'source',
|
|
4732
|
+
'userCandidates',
|
|
4633
4733
|
'maxLength',
|
|
4634
4734
|
'precision',
|
|
4635
4735
|
'scale',
|
|
@@ -4672,6 +4772,55 @@ export function validateAppDeclaration(value) {
|
|
|
4672
4772
|
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'] }`));
|
|
4673
4773
|
}
|
|
4674
4774
|
}
|
|
4775
|
+
let recordCommentsValid = true;
|
|
4776
|
+
if (resource.recordComments !== undefined) {
|
|
4777
|
+
const rule = object(resource.recordComments);
|
|
4778
|
+
if (!resource.recordComments || typeof resource.recordComments !== 'object' || Array.isArray(resource.recordComments) ||
|
|
4779
|
+
Object.keys(rule).some(key => !['read', 'create'].includes(key)) || ['read', 'create'].some(mode => {
|
|
4780
|
+
const list = rule[mode];
|
|
4781
|
+
return list !== false && list !== true && (!Array.isArray(list) || list.length < 1 || list.length > 20 ||
|
|
4782
|
+
list.some(value => typeof value !== 'string' || !value.trim()) || new Set(list).size !== list.length);
|
|
4783
|
+
})) {
|
|
4784
|
+
recordCommentsValid = false;
|
|
4785
|
+
diagnostics.push(diagnostic('APP_CONFIG_RECORD_COMMENTS_POLICY_INVALID', 'recordComments.read/create 必须为 true、false 或不重复的非空 capability 数组(最多20项)', `${resourcePath}.recordComments`, 'true绑定本资源read;每项操作的能力需由同一成员全部持有。'));
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
let recordDeletionValid = true;
|
|
4789
|
+
if (resource.recordDeletion !== undefined) {
|
|
4790
|
+
const rule = object(resource.recordDeletion);
|
|
4791
|
+
const remove = rule.delete;
|
|
4792
|
+
if (!resource.recordDeletion || typeof resource.recordDeletion !== 'object' || Array.isArray(resource.recordDeletion) ||
|
|
4793
|
+
Object.keys(rule).some(key => key !== 'delete') ||
|
|
4794
|
+
(remove !== true && remove !== false && (!Array.isArray(remove) || remove.length < 1 || remove.length > 20 ||
|
|
4795
|
+
remove.some(value => typeof value !== 'string' || !value.trim()) || new Set(remove).size !== remove.length))) {
|
|
4796
|
+
recordDeletionValid = false;
|
|
4797
|
+
diagnostics.push(diagnostic('APP_CONFIG_RECORD_DELETION_DELETE_INVALID', 'recordDeletion.delete 必须为 true、false 或不重复的非空 capability 数组(最多20项)', `${resourcePath}.recordDeletion`, 'true绑定本资源delete;同一成员需同时有资料read、delete及全部维护能力。'));
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
let recordPrintValid = true;
|
|
4801
|
+
if (resource.recordPrint !== undefined) {
|
|
4802
|
+
const rule = object(resource.recordPrint);
|
|
4803
|
+
const read = rule.read;
|
|
4804
|
+
if (!resource.recordPrint || typeof resource.recordPrint !== 'object' || Array.isArray(resource.recordPrint) ||
|
|
4805
|
+
Object.keys(rule).some(key => key !== 'read') ||
|
|
4806
|
+
(read !== true && read !== false && (!Array.isArray(read) || read.length < 1 || read.length > 20 ||
|
|
4807
|
+
read.some(value => typeof value !== 'string' || !value.trim()) || new Set(read).size !== read.length))) {
|
|
4808
|
+
recordPrintValid = false;
|
|
4809
|
+
diagnostics.push(diagnostic('APP_CONFIG_RECORD_PRINT_READ_INVALID', 'recordPrint.read 必须为 true、false 或不重复的非空 capability 数组(最多 20 项)', `${resourcePath}.recordPrint`, 'true 绑定本资源 read;独立能力数组按同一成员全部持有校验。'));
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
let workflowHistoryValid = true;
|
|
4813
|
+
if (resource.workflowHistory !== undefined) {
|
|
4814
|
+
const rule = object(resource.workflowHistory);
|
|
4815
|
+
const read = rule.read;
|
|
4816
|
+
if (!resource.workflowHistory || typeof resource.workflowHistory !== 'object' || Array.isArray(resource.workflowHistory) ||
|
|
4817
|
+
Object.keys(rule).some(key => key !== 'read') ||
|
|
4818
|
+
(read !== true && read !== false && (!Array.isArray(read) || read.length < 1 || read.length > 20 ||
|
|
4819
|
+
read.some(value => typeof value !== 'string' || !value.trim()) || new Set(read).size !== read.length))) {
|
|
4820
|
+
workflowHistoryValid = false;
|
|
4821
|
+
diagnostics.push(diagnostic('APP_CONFIG_WORKFLOW_HISTORY_READ_INVALID', 'workflowHistory.read 必须为 true、false 或不重复的非空 capability 数组(最多 20 项)', `${resourcePath}.workflowHistory`, 'true 绑定本资源 read;独立能力数组按同一成员全部持有校验。'));
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4675
4824
|
const userSurface = resource.userSurface;
|
|
4676
4825
|
if (userSurface !== undefined) {
|
|
4677
4826
|
const surface = object(userSurface);
|
|
@@ -4708,7 +4857,7 @@ export function validateAppDeclaration(value) {
|
|
|
4708
4857
|
return;
|
|
4709
4858
|
}
|
|
4710
4859
|
const declaredCodes = new Set();
|
|
4711
|
-
let materializable = auditValid;
|
|
4860
|
+
let materializable = auditValid && workflowHistoryValid && recordPrintValid && recordCommentsValid && recordDeletionValid;
|
|
4712
4861
|
fields.forEach((rawField, fieldIndex) => {
|
|
4713
4862
|
const field = object(rawField);
|
|
4714
4863
|
const fieldPath = `${resourcePath}.fields[${fieldIndex}]`;
|