openxiangda-devkit-core 2.0.0-alpha.107 → 2.0.0-alpha.109
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 +181 -1
- package/dist/application-services.d.ts.map +1 -1
- package/dist/application-services.js +33 -0
- package/dist/application-services.js.map +1 -1
- package/dist/application-ui-contract.d.ts +5 -0
- package/dist/application-ui-contract.d.ts.map +1 -0
- package/dist/application-ui-contract.js +149 -0
- package/dist/application-ui-contract.js.map +1 -0
- package/dist/command-registry.d.ts +2 -2
- package/dist/command-registry.js +1 -1
- package/dist/command-registry.js.map +1 -1
- package/dist/compiler/application-model.d.ts +60 -0
- package/dist/compiler/application-model.d.ts.map +1 -0
- package/dist/compiler/application-model.js +141 -0
- package/dist/compiler/application-model.js.map +1 -0
- package/dist/compiler/bundle.d.ts.map +1 -1
- package/dist/compiler/bundle.js +19 -9
- package/dist/compiler/bundle.js.map +1 -1
- package/dist/compiler/config.d.ts +19 -5
- package/dist/compiler/config.d.ts.map +1 -1
- package/dist/compiler/config.js +137 -60
- package/dist/compiler/config.js.map +1 -1
- package/dist/compiler/field-surface.js +1 -1
- package/dist/compiler/field-surface.js.map +1 -1
- package/dist/compiler/permission-review.d.ts +139 -0
- package/dist/compiler/permission-review.d.ts.map +1 -0
- package/dist/compiler/permission-review.js +131 -0
- package/dist/compiler/permission-review.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/optional-backend.d.ts +14 -0
- package/dist/optional-backend.d.ts.map +1 -0
- package/dist/optional-backend.js +145 -0
- package/dist/optional-backend.js.map +1 -0
- package/dist/workspace-loader.d.ts.map +1 -1
- package/dist/workspace-loader.js +16 -1
- package/dist/workspace-loader.js.map +1 -1
- package/package.json +3 -2
- package/templates/backend/.turbo/turbo-build.log +4 -0
- package/templates/backend/.turbo/turbo-check.log +4 -0
- package/templates/backend/.turbo/turbo-test.log +13 -0
- package/templates/backend/Dockerfile +20 -0
- package/templates/backend/node_modules/.bin/openxiangda +21 -0
- package/templates/backend/node_modules/.bin/tsc +21 -0
- package/templates/backend/node_modules/.bin/tsserver +21 -0
- package/templates/backend/node_modules/.bin/tsx +21 -0
- package/templates/backend/package.json +27 -0
- package/templates/backend/src/app.module.ts +23 -0
- package/templates/backend/src/main.ts +5 -0
- package/templates/backend/test/smoke.test.ts +17 -0
- package/templates/backend/tsconfig.json +9 -0
package/dist/compiler/config.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { materializeApplicationModules } from './application-model.js';
|
|
1
2
|
import { SCHEMA_VERSIONS, DATA_FIELD_TYPES, PLATFORM_EVENT_TYPES_V2, nativePlatformCapabilityCatalog, validateDataResource, WORKFLOW_SUMMARY_MAX_FIELDS, WORKFLOW_SUMMARY_TEXT_LONG_MAX_BYTES, isWorkflowSummaryFieldType, } from 'openxiangda-contracts';
|
|
2
3
|
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
3
4
|
import { CronExpressionParser } from 'cron-parser';
|
|
@@ -33,8 +34,9 @@ export function defineAdminNavigation(groups) {
|
|
|
33
34
|
export function adminNavigationGroup(code, label, items, options = {}) {
|
|
34
35
|
return Object.freeze({ code, label, items: Object.freeze([...items]), ...options });
|
|
35
36
|
}
|
|
36
|
-
export function adminResourcePage(resourceCode, options) {
|
|
37
|
-
|
|
37
|
+
export function adminResourcePage(resourceCode, options = {}) {
|
|
38
|
+
const { viewCode, ...display } = options;
|
|
39
|
+
return adminNavigationItem({ kind: 'resource', resourceCode, ...(viewCode ? { viewCode } : {}) }, display);
|
|
38
40
|
}
|
|
39
41
|
export function adminOperationPage(routeCode, options) {
|
|
40
42
|
return adminNavigationItem({ kind: 'operation', routeCode }, options);
|
|
@@ -92,10 +94,18 @@ function normalizeLegacyUserSurfaceAuthoring(declaration) {
|
|
|
92
94
|
export function suggestAdminNavigation(config) {
|
|
93
95
|
const groups = [];
|
|
94
96
|
const resources = [...(config.data?.resources || [])]
|
|
95
|
-
.filter(resource => resource.surface?.generated?.list !== false)
|
|
96
97
|
.sort((left, right) => left.code < right.code ? -1 : left.code > right.code ? 1 : 0);
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
const resourceItems = resources.flatMap(resource => [
|
|
99
|
+
...(resource.surface?.generated?.list !== false
|
|
100
|
+
? [adminResourcePage(resource.code, { label: resource.name })]
|
|
101
|
+
: []),
|
|
102
|
+
...[...(resource.surface?.views || [])]
|
|
103
|
+
.filter(view => view.generated.list !== false)
|
|
104
|
+
.sort((left, right) => left.code.localeCompare(right.code, 'en'))
|
|
105
|
+
.map(view => adminResourcePage(resource.code, { viewCode: view.code, label: view.name })),
|
|
106
|
+
]);
|
|
107
|
+
if (resourceItems.length > 0) {
|
|
108
|
+
groups.push(adminNavigationGroup('data-management', '数据管理', resourceItems, { icon: 'database', order: 100 }));
|
|
99
109
|
}
|
|
100
110
|
const routes = [...(config.frontend.routes || [])]
|
|
101
111
|
.filter(route => route.surface === 'admin' && !/[:*]/.test(route.path))
|
|
@@ -107,6 +117,9 @@ export function suggestAdminNavigation(config) {
|
|
|
107
117
|
}
|
|
108
118
|
function renderAdminNavigationOptions(options) {
|
|
109
119
|
const entries = [
|
|
120
|
+
...(options.viewCode !== undefined
|
|
121
|
+
? [`viewCode: ${JSON.stringify(options.viewCode)}`]
|
|
122
|
+
: []),
|
|
110
123
|
...(options.label !== undefined
|
|
111
124
|
? [`label: ${JSON.stringify(options.label)}`]
|
|
112
125
|
: []),
|
|
@@ -127,7 +140,9 @@ export function renderAdminNavigationSuggestion(config) {
|
|
|
127
140
|
const groups = navigation.map(group => {
|
|
128
141
|
imports.add('adminNavigationGroup');
|
|
129
142
|
const items = group.items.map(item => {
|
|
130
|
-
const options = renderAdminNavigationOptions(item
|
|
143
|
+
const options = renderAdminNavigationOptions({ ...item,
|
|
144
|
+
...(item.page.kind === 'resource' && item.page.viewCode ? { viewCode: item.page.viewCode } : {}),
|
|
145
|
+
});
|
|
131
146
|
let expression;
|
|
132
147
|
switch (item.page.kind) {
|
|
133
148
|
case 'resource':
|
|
@@ -605,7 +620,6 @@ export function backendRuntimeRequired(config) {
|
|
|
605
620
|
config.events?.subscriptions?.length ||
|
|
606
621
|
config.events?.timers?.length ||
|
|
607
622
|
config.events?.dateTriggers?.length ||
|
|
608
|
-
config.workflows?.activations?.length ||
|
|
609
623
|
config.workflows?.providers?.length)));
|
|
610
624
|
}
|
|
611
625
|
function object(value) {
|
|
@@ -870,10 +884,8 @@ export function validateAppConfig(value) {
|
|
|
870
884
|
Array.isArray(eventConfig.timers) && eventConfig.timers.length > 0 ||
|
|
871
885
|
Array.isArray(eventConfig.dateTriggers) &&
|
|
872
886
|
eventConfig.dateTriggers.length > 0 ||
|
|
873
|
-
Array.isArray(workflowConfig.activations) &&
|
|
874
|
-
workflowConfig.activations.length > 0 ||
|
|
875
887
|
Array.isArray(workflowConfig.providers) && workflowConfig.providers.length > 0)) {
|
|
876
|
-
diagnostics.push(diagnostic('APP_CONFIG_BACKEND_DISABLED_WITH_DECLARATIONS', 'backend.enabled=false 时不能声明后端操作、Secret
|
|
888
|
+
diagnostics.push(diagnostic('APP_CONFIG_BACKEND_DISABLED_WITH_DECLARATIONS', 'backend.enabled=false 时不能声明后端操作、Secret、事件消费者或工作流人员提供器', 'backend.enabled'));
|
|
877
889
|
}
|
|
878
890
|
const secretNames = new Set();
|
|
879
891
|
const secretEnvs = new Set();
|
|
@@ -2637,26 +2649,33 @@ function generatedPlatformRouteClaims(config) {
|
|
|
2637
2649
|
const code = string(resource.code);
|
|
2638
2650
|
if (!code)
|
|
2639
2651
|
continue;
|
|
2640
|
-
const
|
|
2641
|
-
const
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
[
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2652
|
+
const resourceSurface = object(resource.surface);
|
|
2653
|
+
for (const selected of [resourceSurface, ...(Array.isArray(resourceSurface.views) ? resourceSurface.views.map(object) : [])]) {
|
|
2654
|
+
const surface = { ...resourceSurface, ...selected };
|
|
2655
|
+
const viewCode = string(selected.code);
|
|
2656
|
+
const generated = object(surface.generated);
|
|
2657
|
+
const native = !surface.mutationOwner || surface.mutationOwner === 'native';
|
|
2658
|
+
const operations = {
|
|
2659
|
+
list: generated.list === undefined ? true : generated.list === true,
|
|
2660
|
+
detail: generated.detail === undefined ? true : generated.detail === true,
|
|
2661
|
+
create: generated.create === undefined ? native : generated.create === true,
|
|
2662
|
+
update: generated.update === undefined ? native : generated.update === true,
|
|
2663
|
+
};
|
|
2664
|
+
const base = `/admin/resources/${code}${viewCode ? `/views/${viewCode}` : ""}`;
|
|
2665
|
+
const candidates = [
|
|
2666
|
+
['list', base],
|
|
2667
|
+
['detail', `${base}/:id`],
|
|
2668
|
+
['create', `${base}/new`],
|
|
2669
|
+
['update', `${base}/:id/edit`],
|
|
2670
|
+
];
|
|
2671
|
+
for (const [operation, path] of candidates) {
|
|
2672
|
+
if (!operations[operation])
|
|
2673
|
+
continue;
|
|
2674
|
+
const owner = `resource:${code}${viewCode ? `:view:${viewCode}` : ''}:${operation}`;
|
|
2675
|
+
claims.push({ owner, path });
|
|
2676
|
+
if (object(surface.mobile).enabled !== false)
|
|
2677
|
+
claims.push({ owner: `${owner}:mobile`, path: `/m${path}` });
|
|
2678
|
+
}
|
|
2660
2679
|
}
|
|
2661
2680
|
}
|
|
2662
2681
|
const frontend = object(config.frontend);
|
|
@@ -3291,10 +3310,12 @@ function validateAdminNavigation(config, diagnostics) {
|
|
|
3291
3310
|
if (kind === 'resource') {
|
|
3292
3311
|
const resourceCode = string(page.resourceCode);
|
|
3293
3312
|
const resource = resources.get(resourceCode);
|
|
3294
|
-
|
|
3295
|
-
|
|
3313
|
+
const viewCode = string(page.viewCode);
|
|
3314
|
+
const view = resource?.surface?.views?.find((item) => item.code === viewCode);
|
|
3315
|
+
pageKey = `resource:${resourceCode}${viewCode ? `:view:${viewCode}` : ""}:list`;
|
|
3316
|
+
defaultLabel = (viewCode ? view?.name : resource?.name) || '';
|
|
3296
3317
|
internalCode = resourceCode;
|
|
3297
|
-
exists = Boolean(resource?.surface?.generated?.list);
|
|
3318
|
+
exists = Boolean(viewCode ? view?.generated?.list : resource?.surface?.generated?.list);
|
|
3298
3319
|
}
|
|
3299
3320
|
else if (kind === 'operation') {
|
|
3300
3321
|
const routeCode = string(page.routeCode);
|
|
@@ -3500,24 +3521,21 @@ export function resourceCapabilityCodes(appCode, resourceCode) {
|
|
|
3500
3521
|
delete: `${prefix}:delete`,
|
|
3501
3522
|
};
|
|
3502
3523
|
}
|
|
3503
|
-
function materializeRoleCapabilities(
|
|
3524
|
+
function materializeRoleCapabilities(role) {
|
|
3504
3525
|
const capabilities = new Set(role.capabilities);
|
|
3505
|
-
for (const resource of config.data?.resources || []) {
|
|
3506
|
-
if (resource.surface?.mutationOwner !== 'native' ||
|
|
3507
|
-
!capabilities.has(resource.capabilities.read)) {
|
|
3508
|
-
continue;
|
|
3509
|
-
}
|
|
3510
|
-
for (const operation of ['create', 'update', 'delete']) {
|
|
3511
|
-
if (resource.surface?.generated?.[operation] === true) {
|
|
3512
|
-
capabilities.add(resource.capabilities[operation]);
|
|
3513
|
-
}
|
|
3514
|
-
}
|
|
3515
|
-
}
|
|
3516
3526
|
for (const denied of role.deniedCapabilities || []) {
|
|
3517
3527
|
capabilities.delete(denied);
|
|
3518
3528
|
}
|
|
3519
3529
|
return [...capabilities].sort();
|
|
3520
3530
|
}
|
|
3531
|
+
/** Explicit business permission preset; adding a page never changes grants. */
|
|
3532
|
+
export function resourceRoleCapabilities(appCode, resourceCode, access) {
|
|
3533
|
+
const codes = resourceCapabilityCodes(appCode, resourceCode);
|
|
3534
|
+
const operations = access === 'manage'
|
|
3535
|
+
? ['read', 'create', 'update', 'delete']
|
|
3536
|
+
: access === 'read' ? ['read'] : access;
|
|
3537
|
+
return [...new Set(operations.map(operation => codes[operation]))].sort();
|
|
3538
|
+
}
|
|
3521
3539
|
export function currentUserDataPolicy(input) {
|
|
3522
3540
|
return {
|
|
3523
3541
|
code: input.code,
|
|
@@ -3609,9 +3627,12 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
3609
3627
|
physicalPlanForField(definition, field.searchable === true);
|
|
3610
3628
|
return definition;
|
|
3611
3629
|
});
|
|
3612
|
-
const
|
|
3613
|
-
.filter(field => field.list !== false)
|
|
3614
|
-
|
|
3630
|
+
const listCandidates = declaration.fields
|
|
3631
|
+
.filter(field => field.list !== false && (field.hidden ?? field.system) !== true);
|
|
3632
|
+
const visible = declaration.list?.fields
|
|
3633
|
+
? declaration.list.fields.flatMap(code => listCandidates.filter(field => field.code === code))
|
|
3634
|
+
: listCandidates.slice(0, 8);
|
|
3635
|
+
const defaultSortField = visible.find(field => supportsSort(field.type));
|
|
3615
3636
|
const searchableFields = declaration.fields
|
|
3616
3637
|
.filter(field => field.searchable === true)
|
|
3617
3638
|
.map(field => field.code);
|
|
@@ -3634,6 +3655,7 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
3634
3655
|
return [capabilities[operation]];
|
|
3635
3656
|
};
|
|
3636
3657
|
const surface = {
|
|
3658
|
+
...(declaration.views?.length ? { views: declaration.views } : {}),
|
|
3637
3659
|
mutationOwner,
|
|
3638
3660
|
generated,
|
|
3639
3661
|
fields: Object.fromEntries(declaration.fields.map(field => [
|
|
@@ -3645,6 +3667,9 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
3645
3667
|
...(field.section ? { section: field.section } : {}),
|
|
3646
3668
|
...(field.required === true ? { requiredHint: true } : {}),
|
|
3647
3669
|
...(field.system !== undefined ? { system: field.system } : {}),
|
|
3670
|
+
...(field.hidden !== undefined || field.system
|
|
3671
|
+
? { hidden: field.hidden ?? true }
|
|
3672
|
+
: {}),
|
|
3648
3673
|
readCapabilities: access(field, 'read'),
|
|
3649
3674
|
createCapabilities: access(field, 'create'),
|
|
3650
3675
|
updateCapabilities: access(field, 'update'),
|
|
@@ -3678,22 +3703,23 @@ export function materializeDataResource(appCode, declaration) {
|
|
|
3678
3703
|
},
|
|
3679
3704
|
])),
|
|
3680
3705
|
list: {
|
|
3706
|
+
fieldOrder: visible.map(field => field.code),
|
|
3681
3707
|
defaultPageSize: declaration.list?.defaultPageSize || 20,
|
|
3682
3708
|
...(searchableFields.length ? { searchableFields } : {}),
|
|
3683
3709
|
...(filterFields.length ? { filterFields } : {}),
|
|
3684
3710
|
...(declaration.list?.defaultSort
|
|
3685
3711
|
? { defaultSort: declaration.list.defaultSort }
|
|
3686
|
-
:
|
|
3687
|
-
? { defaultSort: { field:
|
|
3712
|
+
: defaultSortField
|
|
3713
|
+
? { defaultSort: { field: defaultSortField.code, order: 'asc' } }
|
|
3688
3714
|
: {}),
|
|
3689
3715
|
},
|
|
3690
3716
|
form: {
|
|
3691
3717
|
layout: declaration.form?.layout || 'flat',
|
|
3692
|
-
fieldOrder: declaration.fields.map(field => field.code),
|
|
3718
|
+
fieldOrder: declaration.form?.fields || declaration.fields.map(field => field.code),
|
|
3693
3719
|
},
|
|
3694
3720
|
detail: {
|
|
3695
3721
|
layout: declaration.detail?.layout || 'flat',
|
|
3696
|
-
fieldOrder: declaration.fields.map(field => field.code),
|
|
3722
|
+
fieldOrder: declaration.detail?.fields || declaration.fields.map(field => field.code),
|
|
3697
3723
|
},
|
|
3698
3724
|
mobile: declaration.mobile || { enabled: true },
|
|
3699
3725
|
};
|
|
@@ -3730,6 +3756,7 @@ export function validateAppDeclaration(value) {
|
|
|
3730
3756
|
const resources = Array.isArray(data.resources) ? data.resources : [];
|
|
3731
3757
|
const diagnostics = [];
|
|
3732
3758
|
const resourceKeys = new Set([
|
|
3759
|
+
'views',
|
|
3733
3760
|
'code',
|
|
3734
3761
|
'name',
|
|
3735
3762
|
'mutationOwner',
|
|
@@ -3764,6 +3791,7 @@ export function validateAppDeclaration(value) {
|
|
|
3764
3791
|
'widget',
|
|
3765
3792
|
'section',
|
|
3766
3793
|
'system',
|
|
3794
|
+
'hidden',
|
|
3767
3795
|
'list',
|
|
3768
3796
|
'filter',
|
|
3769
3797
|
'searchable',
|
|
@@ -3843,12 +3871,6 @@ export function validateAppDeclaration(value) {
|
|
|
3843
3871
|
else if (!rangeType && field.rangeBoundary !== undefined) {
|
|
3844
3872
|
diagnostics.push(diagnostic('APP_CONFIG_DATA_FIELD_RANGE_BOUNDARY_TYPE_INVALID', 'rangeBoundary 只能用于 date-range 或 datetime-range', `${fieldPath}.rangeBoundary`));
|
|
3845
3873
|
}
|
|
3846
|
-
if (field.indexed === false &&
|
|
3847
|
-
(field.filter === true ||
|
|
3848
|
-
field.searchable === true ||
|
|
3849
|
-
field.sortable === true)) {
|
|
3850
|
-
diagnostics.push(diagnostic('APP_CONFIG_DATA_FIELD_INDEX_REQUIRED', '启用筛选、搜索或排序时不能显式关闭索引', `${fieldPath}.indexed`));
|
|
3851
|
-
}
|
|
3852
3874
|
if (['radio', 'checkbox'].includes(widget) &&
|
|
3853
3875
|
object(field.source).loadMode !== 'all' &&
|
|
3854
3876
|
field.source !== undefined) {
|
|
@@ -3862,6 +3884,7 @@ export function validateAppDeclaration(value) {
|
|
|
3862
3884
|
'required',
|
|
3863
3885
|
'indexed',
|
|
3864
3886
|
'system',
|
|
3887
|
+
'hidden',
|
|
3865
3888
|
'list',
|
|
3866
3889
|
'filter',
|
|
3867
3890
|
'searchable',
|
|
@@ -3890,6 +3913,27 @@ export function validateAppDeclaration(value) {
|
|
|
3890
3913
|
if (code)
|
|
3891
3914
|
declaredCodes.add(code);
|
|
3892
3915
|
});
|
|
3916
|
+
for (const view of ['list', 'form', 'detail']) {
|
|
3917
|
+
const selected = object(resource[view]).fields;
|
|
3918
|
+
if (selected === undefined)
|
|
3919
|
+
continue;
|
|
3920
|
+
if (!Array.isArray(selected) || new Set(selected).size !== selected.length ||
|
|
3921
|
+
selected.some(code => typeof code !== 'string' || !declaredCodes.has(code))) {
|
|
3922
|
+
diagnostics.push(diagnostic('APP_CONFIG_DATA_VIEW_FIELDS_INVALID', '列表/表单/详情 fields 必须是当前资源中不重复的字段列表', `${resourcePath}.${view}.fields`));
|
|
3923
|
+
materializable = false;
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
const selectedFormFields = object(resource.form).fields ?? fields.map(raw => object(raw).code);
|
|
3927
|
+
if (Array.isArray(selectedFormFields) && (generated.create ?? mutationOwner === 'native')) {
|
|
3928
|
+
for (const rawField of fields) {
|
|
3929
|
+
const field = object(rawField);
|
|
3930
|
+
if (field.required === true && field.system !== true &&
|
|
3931
|
+
supportsGeneratedMutation({ type: field.type, system: false }) &&
|
|
3932
|
+
object(field.access).create !== false && (field.hidden === true || !selectedFormFields.includes(field.code))) {
|
|
3933
|
+
diagnostics.push(diagnostic('APP_CONFIG_DATA_FORM_REQUIRED_FIELD_MISSING', `标准新增表单缺少必填字段 ${field.label || field.code};请加入表单或关闭标准新增`, `${resourcePath}.form.fields`));
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3893
3937
|
for (const operation of ['create', 'update']) {
|
|
3894
3938
|
const enabled = generated[operation] ?? mutationOwner === 'native';
|
|
3895
3939
|
const writable = fields.some(rawField => {
|
|
@@ -3911,6 +3955,19 @@ export function validateAppDeclaration(value) {
|
|
|
3911
3955
|
}
|
|
3912
3956
|
if (appCode && fields.length && materializable) {
|
|
3913
3957
|
const canonical = materializeDataResource(appCode, rawResource);
|
|
3958
|
+
for (const [viewIndex, view] of (canonical.surface?.views || []).entries()) {
|
|
3959
|
+
for (const [property, values, supports] of [
|
|
3960
|
+
['searchableFields', view.list?.searchableFields || [], supportsSearch],
|
|
3961
|
+
['filterFields', view.list?.filterFields || [], supportsFilter],
|
|
3962
|
+
['defaultSort', view.list?.defaultSort ? [view.list.defaultSort.field] : [], supportsSort],
|
|
3963
|
+
]) {
|
|
3964
|
+
for (const code of values) {
|
|
3965
|
+
const field = canonical.schema.fields.find(field => field.code === code);
|
|
3966
|
+
if (!field || !supports(field.type))
|
|
3967
|
+
diagnostics.push(diagnostic('APP_CONFIG_DATA_VIEW_QUERY_FIELD_INVALID', '视图查询字段不存在或不支持此查询类型', `${resourcePath}.views[${viewIndex}].list.${property}`));
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3914
3971
|
diagnostics.push(...validateDataResource(canonical).map(item => ({
|
|
3915
3972
|
...item,
|
|
3916
3973
|
path: `${resourcePath}.${item.path}`,
|
|
@@ -3978,7 +4035,27 @@ export function validateAppDeclaration(value) {
|
|
|
3978
4035
|
return diagnostics;
|
|
3979
4036
|
}
|
|
3980
4037
|
export function defineOpenXiangdaApp(declaration) {
|
|
3981
|
-
const
|
|
4038
|
+
const { modules, ...input } = declaration;
|
|
4039
|
+
const projected = materializeApplicationModules(modules || []);
|
|
4040
|
+
if (projected.diagnostics.length)
|
|
4041
|
+
throw new AppConfigValidationError(projected.diagnostics);
|
|
4042
|
+
const normalizedDeclaration = normalizeLegacyUserSurfaceAuthoring({
|
|
4043
|
+
...input,
|
|
4044
|
+
schemaVersion: declaration.schemaVersion ?? 3,
|
|
4045
|
+
frontend: { root: 'apps/web', ...declaration.frontend },
|
|
4046
|
+
backend: {
|
|
4047
|
+
root: 'apps/server', runtime: 'node', framework: 'nestjs',
|
|
4048
|
+
enabled: Boolean(declaration.backend?.operations?.length || declaration.backend?.secrets?.length ||
|
|
4049
|
+
declaration.events?.subscriptions?.length || declaration.events?.timers?.length ||
|
|
4050
|
+
declaration.events?.dateTriggers?.length ||
|
|
4051
|
+
declaration.workflows?.providers?.length),
|
|
4052
|
+
...declaration.backend,
|
|
4053
|
+
},
|
|
4054
|
+
platform: { root: 'platform', ...declaration.platform },
|
|
4055
|
+
...(modules || declaration.data ? {
|
|
4056
|
+
data: { resources: [...(declaration.data?.resources || []), ...projected.resources] },
|
|
4057
|
+
} : {}),
|
|
4058
|
+
});
|
|
3982
4059
|
const declarationDiagnostics = validateAppDeclaration(normalizedDeclaration);
|
|
3983
4060
|
if (declarationDiagnostics.length > 0) {
|
|
3984
4061
|
throw new AppConfigValidationError(declarationDiagnostics);
|
|
@@ -4001,7 +4078,7 @@ export function defineOpenXiangdaApp(declaration) {
|
|
|
4001
4078
|
...materialized.authz,
|
|
4002
4079
|
roles: materialized.authz.roles.map(role => ({
|
|
4003
4080
|
...role,
|
|
4004
|
-
capabilities: materializeRoleCapabilities(
|
|
4081
|
+
capabilities: materializeRoleCapabilities(role),
|
|
4005
4082
|
})),
|
|
4006
4083
|
},
|
|
4007
4084
|
}
|