openxiangda-devkit-core 2.48.2 → 2.51.1
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 +1 -0
- package/dist/application-services.d.ts.map +1 -1
- package/dist/application-services.js +32 -6
- package/dist/application-services.js.map +1 -1
- package/dist/compiler/ai-catalog.d.ts.map +1 -1
- package/dist/compiler/ai-catalog.js +2 -10
- package/dist/compiler/ai-catalog.js.map +1 -1
- package/dist/compiler/bundle.d.ts.map +1 -1
- package/dist/compiler/bundle.js +56 -18
- package/dist/compiler/bundle.js.map +1 -1
- package/dist/compiler/config.d.ts +1 -1
- package/dist/compiler/config.d.ts.map +1 -1
- package/dist/compiler/config.js +72 -14
- package/dist/compiler/config.js.map +1 -1
- package/dist/compiler/permission-review.d.ts.map +1 -1
- package/dist/compiler/schema-composition.js +3 -0
- package/dist/compiler/schema-composition.js.map +1 -1
- package/dist/connected-development-vite.d.ts +15 -0
- package/dist/connected-development-vite.d.ts.map +1 -0
- package/dist/connected-development-vite.js +27 -0
- package/dist/connected-development-vite.js.map +1 -0
- package/dist/connected-development.d.ts +5 -0
- package/dist/connected-development.d.ts.map +1 -1
- package/dist/connected-development.js +99 -26
- package/dist/connected-development.js.map +1 -1
- package/dist/control-plane-client.d.ts +8 -0
- package/dist/control-plane-client.d.ts.map +1 -1
- package/dist/control-plane-client.js +36 -0
- package/dist/control-plane-client.js.map +1 -1
- package/dist/development-backend-relay.d.ts +28 -0
- package/dist/development-backend-relay.d.ts.map +1 -0
- package/dist/development-backend-relay.js +184 -0
- package/dist/development-backend-relay.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/workflow.d.ts.map +1 -1
- package/dist/internal/workflow.js +18 -5
- package/dist/internal/workflow.js.map +1 -1
- package/dist/workspace-loader.js +1 -1
- package/dist/workspace-loader.js.map +1 -1
- package/package.json +2 -2
- package/templates/backend/Dockerfile +1 -0
package/dist/compiler/config.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import { assertWorkflowNativeStagePolicy } from 'openxiangda-contracts';
|
|
2
|
+
import { validateAuthenticatedPublicRead, AuthenticatedPublicReadContractError } from 'openxiangda-contracts/native-compiler';
|
|
3
|
+
import { DATE_EVENT_RESERVED_DATA_FIELDS_V2, dateEventProjectionFieldsV2, dateEventProjectionEnvelopeSchemaV2 } from 'openxiangda-contracts/native-compiler';
|
|
1
4
|
import { validateUserCandidateReferences, validateWorkflowUserCandidateBindings, UserCandidateContractError } from 'openxiangda-contracts';
|
|
2
5
|
import { parseNativeUniqueKeys, DATA_SUBTABLE_MAX_TOTAL_ROWS } from 'openxiangda-contracts';
|
|
6
|
+
import { assertWorkflowOwnedSubjectCreate } from 'openxiangda-contracts';
|
|
7
|
+
export { workflowBusinessCommandInvocationSchema } from 'openxiangda-contracts';
|
|
3
8
|
import { validateManagedConcurrency } from 'openxiangda-contracts/native-compiler';
|
|
4
9
|
import { normalizeDecimalReservationEventDeclaration, decimalReservationEventContext } from 'openxiangda-contracts';
|
|
5
10
|
import { materializeApplicationModules } from './application-model.js';
|
|
@@ -18,14 +23,7 @@ import { AUTHORIZATION_TRANSITION_CAPABILITY_CODE_PATTERN, AUTHORIZATION_TRANSIT
|
|
|
18
23
|
// The compiler owns the application source contract. Devkit and every adapter
|
|
19
24
|
// re-export this module instead of maintaining separate configuration models.
|
|
20
25
|
import { validateWorkflowBinding, validateWorkflowDefinition, } from '../internal/workflow.js';
|
|
21
|
-
const DATE_TRIGGER_RESERVED_DATA_FIELDS = new Set(
|
|
22
|
-
'triggerCode',
|
|
23
|
-
'resourceCode',
|
|
24
|
-
'recordId',
|
|
25
|
-
'recordRevision',
|
|
26
|
-
'field',
|
|
27
|
-
'dueAt',
|
|
28
|
-
]);
|
|
26
|
+
const DATE_TRIGGER_RESERVED_DATA_FIELDS = new Set(DATE_EVENT_RESERVED_DATA_FIELDS_V2);
|
|
29
27
|
const APP_DATA_FIELD_TYPES = new Set(DATA_FIELD_TYPES);
|
|
30
28
|
const WORKFLOW_RUNTIME_CODE_PATTERN = /^[a-z][a-z0-9-]{2,63}$/;
|
|
31
29
|
function adminNavigationItem(page, options = {}) {
|
|
@@ -1452,6 +1450,7 @@ export function validateAppConfig(value) {
|
|
|
1452
1450
|
'name',
|
|
1453
1451
|
'resourceCode',
|
|
1454
1452
|
'unrestrictedRoleCodes',
|
|
1453
|
+
'publicRead',
|
|
1455
1454
|
'operations',
|
|
1456
1455
|
'matchMode',
|
|
1457
1456
|
'rules',
|
|
@@ -1494,6 +1493,20 @@ export function validateAppConfig(value) {
|
|
|
1494
1493
|
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(string(policy.resourceCode))) {
|
|
1495
1494
|
diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_POLICY_RESOURCE_REQUIRED', 'data policy 必须声明唯一目标 resourceCode', `authz.dataPolicies[${index}].resourceCode`));
|
|
1496
1495
|
}
|
|
1496
|
+
if (policy.publicRead !== undefined) {
|
|
1497
|
+
const resource = dataResources.map(object).find(resource => resource.code === policy.resourceCode);
|
|
1498
|
+
const baseline = roles.map(object).find(role => role.code === authenticatedUserRoleCode);
|
|
1499
|
+
try {
|
|
1500
|
+
validateAuthenticatedPublicRead({ policy, resource, baselineRoleCode: authenticatedUserRoleCode,
|
|
1501
|
+
baselineCapabilities: Array.isArray(baseline?.capabilities) ? baseline.capabilities : [],
|
|
1502
|
+
baselineDeniedCapabilities: Array.isArray(baseline?.deniedCapabilities) ? baseline.deniedCapabilities : [], pointer: policyPath });
|
|
1503
|
+
}
|
|
1504
|
+
catch (error) {
|
|
1505
|
+
if (!(error instanceof AuthenticatedPublicReadContractError))
|
|
1506
|
+
throw error;
|
|
1507
|
+
diagnostics.push(diagnostic(error.code, '认证公共投影必须闭合公共字段,所有非公开字段与历史继续拒绝基线成员', error.pointer));
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1497
1510
|
if (policy.unrestrictedRoleCodes !== undefined) {
|
|
1498
1511
|
const unrestrictedRoleCodes = Array.isArray(policy.unrestrictedRoleCodes)
|
|
1499
1512
|
? policy.unrestrictedRoleCodes
|
|
@@ -2012,6 +2025,16 @@ export function validateAppConfig(value) {
|
|
|
2012
2025
|
const field = string(trigger.field);
|
|
2013
2026
|
const fieldType = eventResourceFields.get(resourceCode)?.get(field);
|
|
2014
2027
|
const payload = object(trigger.payload);
|
|
2028
|
+
let projectionFields = [];
|
|
2029
|
+
let generatedSchema = applicationEventSchemas.get(string(trigger.eventType));
|
|
2030
|
+
try {
|
|
2031
|
+
projectionFields = dateEventProjectionFieldsV2(resourceCode, (Array.isArray(events.subscriptions) ? events.subscriptions : []).map(object)
|
|
2032
|
+
.filter(subscription => Array.isArray(subscription.eventTypes) && subscription.eventTypes.includes(trigger.eventType)), eventDataResources.map(object).find(resource => resource.code === resourceCode));
|
|
2033
|
+
generatedSchema = dateEventProjectionEnvelopeSchemaV2(generatedSchema, projectionFields);
|
|
2034
|
+
}
|
|
2035
|
+
catch (error) {
|
|
2036
|
+
diagnostics.push(diagnostic('APP_CONFIG_DATE_TRIGGER_PROJECTION_INVALID', `日期事件资料投影无效:${error instanceof Error ? error.message : 'invalid'}`, path));
|
|
2037
|
+
}
|
|
2015
2038
|
let payloadBytes = Number.POSITIVE_INFINITY;
|
|
2016
2039
|
try {
|
|
2017
2040
|
payloadBytes = Buffer.byteLength(JSON.stringify(trigger.payload || {}), 'utf8');
|
|
@@ -2030,7 +2053,7 @@ export function validateAppConfig(value) {
|
|
|
2030
2053
|
Array.isArray(trigger.payload) ||
|
|
2031
2054
|
payloadBytes > 64 * 1024 ||
|
|
2032
2055
|
Object.keys(payload).some(key => DATE_TRIGGER_RESERVED_DATA_FIELDS.has(key)) ||
|
|
2033
|
-
!eventDataMatchesSchema(
|
|
2056
|
+
!eventDataMatchesSchema(generatedSchema, {
|
|
2034
2057
|
...payload,
|
|
2035
2058
|
triggerCode: code,
|
|
2036
2059
|
resourceCode,
|
|
@@ -2038,6 +2061,7 @@ export function validateAppConfig(value) {
|
|
|
2038
2061
|
recordRevision: 1,
|
|
2039
2062
|
field,
|
|
2040
2063
|
dueAt: '2026-01-01T00:00:00.000Z',
|
|
2064
|
+
...(projectionFields.length ? { projection: {} } : {}),
|
|
2041
2065
|
})) {
|
|
2042
2066
|
diagnostics.push(diagnostic('APP_CONFIG_DATE_TRIGGER_INVALID', 'dateTrigger 必须使用唯一 code、date/datetime 字段、十年内 ISO-8601 offset、已注册应用事件、无保留字段且完整生成数据满足 JSON Schema', path));
|
|
2043
2067
|
}
|
|
@@ -2388,6 +2412,11 @@ export function validateAppConfig(value) {
|
|
|
2388
2412
|
const property = workflowInputProperties[factKey];
|
|
2389
2413
|
const propertyRecord = isRecord(property) ? property : {};
|
|
2390
2414
|
const propertyType = string(propertyRecord.type);
|
|
2415
|
+
const nullableObject = Array.isArray(propertyRecord.type) &&
|
|
2416
|
+
propertyRecord.type.length === 2 &&
|
|
2417
|
+
new Set(propertyRecord.type).size === 2 &&
|
|
2418
|
+
propertyRecord.type.includes('object') &&
|
|
2419
|
+
propertyRecord.type.includes('null');
|
|
2391
2420
|
const itemRecord = isRecord(propertyRecord.items)
|
|
2392
2421
|
? propertyRecord.items
|
|
2393
2422
|
: {};
|
|
@@ -2400,14 +2429,14 @@ export function validateAppConfig(value) {
|
|
|
2400
2429
|
? 'array(items.type=array,items.items.type=object)'
|
|
2401
2430
|
: fieldType.endsWith('.multiple')
|
|
2402
2431
|
? 'array(items.type=object)'
|
|
2403
|
-
: 'object';
|
|
2432
|
+
: 'object 或 [object, null]';
|
|
2404
2433
|
const shapeValid = fieldType === 'cascade.multiple'
|
|
2405
2434
|
? propertyType === 'array' &&
|
|
2406
2435
|
itemType === 'array' &&
|
|
2407
2436
|
nestedItemType === 'object'
|
|
2408
2437
|
: fieldType.endsWith('.multiple')
|
|
2409
2438
|
? propertyType === 'array' && itemType === 'object'
|
|
2410
|
-
: propertyType === 'object';
|
|
2439
|
+
: propertyType === 'object' || nullableObject;
|
|
2411
2440
|
if (!shapeValid) {
|
|
2412
2441
|
diagnostics.push(diagnostic('APP_CONFIG_WORKFLOW_FACT_PROJECTION_SHAPE_INVALID', `fact "${factKey}" 投影的字段 ${fieldCode} 是 ${fieldType} 快照字段;inputSchema.properties.${factKey} 必须声明 ${expected}`, `${path}.definition.inputSchema.properties.${factKey}`));
|
|
2413
2442
|
}
|
|
@@ -2800,15 +2829,38 @@ function validateBackendOperations(rawOperations, declaredResources, declaredRes
|
|
|
2800
2829
|
'roleAssertions',
|
|
2801
2830
|
'decimalReservation',
|
|
2802
2831
|
'dataCommands',
|
|
2832
|
+
'workflowStage',
|
|
2803
2833
|
'recordEdit',
|
|
2834
|
+
'ownedSubject',
|
|
2804
2835
|
]);
|
|
2805
2836
|
let invalid = !isRecord(operation.platformAccess) ||
|
|
2806
2837
|
Object.keys(access).length === 0 ||
|
|
2807
2838
|
Object.keys(access).some(key => !allowedAccessKeys.has(key));
|
|
2839
|
+
if (access.ownedSubject !== undefined) {
|
|
2840
|
+
try {
|
|
2841
|
+
assertWorkflowOwnedSubjectCreate(access.ownedSubject);
|
|
2842
|
+
}
|
|
2843
|
+
catch {
|
|
2844
|
+
invalid = true;
|
|
2845
|
+
}
|
|
2846
|
+
invalid ||= access.workflow === undefined || access.decimalReservation !== undefined;
|
|
2847
|
+
}
|
|
2808
2848
|
if (access.dataCommands !== undefined) {
|
|
2809
2849
|
const commands = object(access.dataCommands);
|
|
2810
2850
|
invalid ||= commands.mode !== 'recoverable-native' || Object.keys(commands).some(key => key !== 'mode');
|
|
2811
2851
|
}
|
|
2852
|
+
if (access.workflowStage !== undefined) {
|
|
2853
|
+
try {
|
|
2854
|
+
assertWorkflowNativeStagePolicy(access.workflowStage);
|
|
2855
|
+
}
|
|
2856
|
+
catch {
|
|
2857
|
+
invalid = true;
|
|
2858
|
+
}
|
|
2859
|
+
const stage = object(access.workflowStage);
|
|
2860
|
+
invalid ||= object(access.dataCommands).mode !== 'recoverable-native' || access.decimalReservation !== undefined ||
|
|
2861
|
+
!declaredResources.has(string(stage.resourceCode)) || !declaredWorkflowCodes.has(string(stage.workflowCode)) ||
|
|
2862
|
+
!Array.isArray(object(access.workflow).codes) || !object(access.workflow).codes.includes(stage.workflowCode);
|
|
2863
|
+
}
|
|
2812
2864
|
if (access.recordEdit !== undefined) {
|
|
2813
2865
|
const edit = object(access.recordEdit);
|
|
2814
2866
|
const fields = Array.isArray(edit.fieldCodes) ? edit.fieldCodes : [];
|
|
@@ -2822,7 +2874,8 @@ function validateBackendOperations(rawOperations, declaredResources, declaredRes
|
|
|
2822
2874
|
const roles = Array.isArray(assertion.roleCodes) ? assertion.roleCodes : [];
|
|
2823
2875
|
invalid ||= !isRecord(access.roleAssertions) || roles.length < 1 || roles.length > 20 ||
|
|
2824
2876
|
new Set(roles).size !== roles.length || roles.some(role => !declaredRoleCodes.has(string(role))) ||
|
|
2825
|
-
Object.keys(assertion).some(key =>
|
|
2877
|
+
Object.keys(assertion).some(key => !['roleCodes', 'actorAuthority'].includes(key)) ||
|
|
2878
|
+
(Object.hasOwn(assertion, 'actorAuthority') && assertion.actorAuthority !== true);
|
|
2826
2879
|
}
|
|
2827
2880
|
if (access.directory !== undefined) {
|
|
2828
2881
|
const directory = object(access.directory);
|
|
@@ -2937,8 +2990,8 @@ function validateBackendOperations(rawOperations, declaredResources, declaredRes
|
|
|
2937
2990
|
Object.keys(workflow).some(key => !['codes', 'businessCommands'].includes(key));
|
|
2938
2991
|
if (workflow.businessCommands !== undefined) {
|
|
2939
2992
|
const commands = workflow.businessCommands;
|
|
2940
|
-
invalid ||= !Array.isArray(commands) || commands.length === 0 || commands.length >
|
|
2941
|
-
new Set(commands).size !== commands.length || commands.some(command => !['approve', 'reject', 'withdraw'].includes(command));
|
|
2993
|
+
invalid ||= !Array.isArray(commands) || commands.length === 0 || commands.length > 4 ||
|
|
2994
|
+
new Set(commands).size !== commands.length || commands.some(command => !['approve', 'reject', 'withdraw', 'resubmit'].includes(command));
|
|
2942
2995
|
}
|
|
2943
2996
|
}
|
|
2944
2997
|
if (access.decimalReservation !== undefined) {
|
|
@@ -5043,6 +5096,11 @@ export function validateAppDeclaration(value) {
|
|
|
5043
5096
|
return;
|
|
5044
5097
|
const path = `data.resources[${declaration.resourceIndex}].fields[${fieldIndex}].subtable`;
|
|
5045
5098
|
const subtable = object(field.subtable);
|
|
5099
|
+
if (subtable.minRows !== undefined &&
|
|
5100
|
+
(!Number.isSafeInteger(subtable.minRows) || Number(subtable.minRows) < 0 ||
|
|
5101
|
+
Number(subtable.minRows) > Number(subtable.maxRows ?? 20))) {
|
|
5102
|
+
diagnostics.push(diagnostic('APP_CONFIG_DATA_SUBTABLE_MIN_ROWS_INVALID', '子表 minRows 必须是非负整数且不能超过 maxRows(默认20)', `${path}.minRows`));
|
|
5103
|
+
}
|
|
5046
5104
|
const targetCode = string(subtable.resourceCode);
|
|
5047
5105
|
const target = declaredResources.get(targetCode);
|
|
5048
5106
|
if (!target || targetCode === resourceCode) {
|