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.
Files changed (44) hide show
  1. package/dist/application-services.d.ts +1 -0
  2. package/dist/application-services.d.ts.map +1 -1
  3. package/dist/application-services.js +32 -6
  4. package/dist/application-services.js.map +1 -1
  5. package/dist/compiler/ai-catalog.d.ts.map +1 -1
  6. package/dist/compiler/ai-catalog.js +2 -10
  7. package/dist/compiler/ai-catalog.js.map +1 -1
  8. package/dist/compiler/bundle.d.ts.map +1 -1
  9. package/dist/compiler/bundle.js +56 -18
  10. package/dist/compiler/bundle.js.map +1 -1
  11. package/dist/compiler/config.d.ts +1 -1
  12. package/dist/compiler/config.d.ts.map +1 -1
  13. package/dist/compiler/config.js +72 -14
  14. package/dist/compiler/config.js.map +1 -1
  15. package/dist/compiler/permission-review.d.ts.map +1 -1
  16. package/dist/compiler/schema-composition.js +3 -0
  17. package/dist/compiler/schema-composition.js.map +1 -1
  18. package/dist/connected-development-vite.d.ts +15 -0
  19. package/dist/connected-development-vite.d.ts.map +1 -0
  20. package/dist/connected-development-vite.js +27 -0
  21. package/dist/connected-development-vite.js.map +1 -0
  22. package/dist/connected-development.d.ts +5 -0
  23. package/dist/connected-development.d.ts.map +1 -1
  24. package/dist/connected-development.js +99 -26
  25. package/dist/connected-development.js.map +1 -1
  26. package/dist/control-plane-client.d.ts +8 -0
  27. package/dist/control-plane-client.d.ts.map +1 -1
  28. package/dist/control-plane-client.js +36 -0
  29. package/dist/control-plane-client.js.map +1 -1
  30. package/dist/development-backend-relay.d.ts +28 -0
  31. package/dist/development-backend-relay.d.ts.map +1 -0
  32. package/dist/development-backend-relay.js +184 -0
  33. package/dist/development-backend-relay.js.map +1 -0
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +1 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/internal/workflow.d.ts.map +1 -1
  39. package/dist/internal/workflow.js +18 -5
  40. package/dist/internal/workflow.js.map +1 -1
  41. package/dist/workspace-loader.js +1 -1
  42. package/dist/workspace-loader.js.map +1 -1
  43. package/package.json +2 -2
  44. package/templates/backend/Dockerfile +1 -0
@@ -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(applicationEventSchemas.get(string(trigger.eventType)), {
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 => key !== 'roleCodes');
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 > 3 ||
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) {