openxiangda-devkit-core 2.0.0-alpha.81 → 2.0.0-alpha.84

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.
@@ -7,6 +7,7 @@ import { physicalPlanForField } from './field-physical-plan.js';
7
7
  import { SEMANTIC_FIELD_PATH_PATTERN, semanticFieldPathRoot, validateAuthzSemanticBindings, } from './field-policy-path.js';
8
8
  import { compatibleWidgets, isCompatibleFieldWidget, resolveDataFieldSurfaceWidget, } from './field-surface.js';
9
9
  import { DATA_POLICY_EXPRESSION_MAX_DEPTH, DATA_POLICY_EXPRESSION_MAX_GROUP_ITEMS, DATA_POLICY_EXPRESSION_MAX_LEAVES, dataPolicyExpressionToCnf, } from './data-policy-expression.js';
10
+ import { AUTHORIZATION_TRANSITION_CAPABILITY_CODE_PATTERN, AUTHORIZATION_TRANSITION_KEYS, AUTHORIZATION_TRANSITION_ROLE_CODE_PATTERN, exactKeys, isExactStringArray, isPlainRecord, } from './authorization-transition.js';
10
11
  // The compiler owns the application source contract. Devkit and every adapter
11
12
  // re-export this module instead of maintaining separate configuration models.
12
13
  import { validateWorkflowBinding, validateWorkflowDefinition, } from '../internal/workflow.js';
@@ -269,6 +270,43 @@ const relationshipGrantSourceInputSchema = {
269
270
  ...authorizationProjectionControlProperties,
270
271
  },
271
272
  };
273
+ const authorizationTransitionInputSchema = {
274
+ type: 'object',
275
+ additionalProperties: false,
276
+ required: ['fromAuthzDigest', 'reason'],
277
+ properties: {
278
+ fromAuthzDigest: {
279
+ type: 'string',
280
+ pattern: '^[0-9a-f]{64}$',
281
+ },
282
+ removeRoleCodes: {
283
+ type: 'array',
284
+ maxItems: 2000,
285
+ uniqueItems: true,
286
+ items: {
287
+ type: 'string',
288
+ pattern: '^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*$',
289
+ maxLength: 128,
290
+ },
291
+ },
292
+ removeCapabilityCodes: {
293
+ type: 'array',
294
+ maxItems: 2000,
295
+ uniqueItems: true,
296
+ items: {
297
+ type: 'string',
298
+ pattern: '^[A-Za-z*][A-Za-z0-9:._*-]{0,254}$',
299
+ maxLength: 255,
300
+ },
301
+ },
302
+ reason: {
303
+ type: 'string',
304
+ minLength: 1,
305
+ maxLength: 2000,
306
+ pattern: '\\S',
307
+ },
308
+ },
309
+ };
272
310
  export const openXiangdaAppConfigSchema = {
273
311
  $id: 'openxiangda.app-config/v3',
274
312
  type: 'object',
@@ -443,7 +481,7 @@ export const openXiangdaAppConfigSchema = {
443
481
  authorizationTransitions: {
444
482
  type: 'array',
445
483
  maxItems: 100,
446
- items: { type: 'object' },
484
+ items: authorizationTransitionInputSchema,
447
485
  },
448
486
  },
449
487
  },
@@ -1625,6 +1663,16 @@ export function validateAppConfig(value) {
1625
1663
  referencedFields.forEach(field => captured.add(field));
1626
1664
  captureFieldsByResource.set(resourceCode, captured);
1627
1665
  }
1666
+ if (subscription.platformAccess !== undefined) {
1667
+ const access = object(subscription.platformAccess);
1668
+ const notification = object(access.notification);
1669
+ if (Object.keys(access).some(key => key !== 'notification') ||
1670
+ access.notification === undefined ||
1671
+ notification.mode !== 'business-standard' ||
1672
+ Object.keys(notification).some(key => key !== 'mode')) {
1673
+ diagnostics.push(diagnostic('APP_CONFIG_EVENT_PLATFORM_ACCESS_INVALID', '事件订阅 platformAccess 只能显式声明标准业务通知依赖', `${path}.platformAccess`));
1674
+ }
1675
+ }
1628
1676
  const delivery = {
1629
1677
  timeoutMs: 10_000,
1630
1678
  maxAttempts: 8,
@@ -2724,23 +2772,35 @@ function validateAuthorizationTransitions(rawTransitions, _roleCodes, diagnostic
2724
2772
  }
2725
2773
  const digests = new Set();
2726
2774
  (Array.isArray(rawTransitions) ? rawTransitions : []).forEach((raw, index) => {
2727
- const transition = object(raw);
2728
- const digest = string(transition.fromAuthzDigest);
2729
2775
  const path = `authz.authorizationTransitions[${index}]`;
2730
- const removeRoles = Array.isArray(transition.removeRoleCodes)
2731
- ? transition.removeRoleCodes.map(string)
2732
- : [];
2733
- const removeCapabilities = Array.isArray(transition.removeCapabilityCodes)
2734
- ? transition.removeCapabilityCodes.map(string)
2735
- : [];
2736
- if (!/^[0-9a-f]{64}$/.test(digest) ||
2776
+ if (!isPlainRecord(raw)) {
2777
+ diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_TRANSITION_INVALID', '授权 transition 必须是只含 canonical 字段的对象', path));
2778
+ return;
2779
+ }
2780
+ if (!exactKeys(raw, AUTHORIZATION_TRANSITION_KEYS)) {
2781
+ const unknownKey = Object.keys(raw).find(key => !AUTHORIZATION_TRANSITION_KEYS.includes(key));
2782
+ diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_TRANSITION_INVALID', '授权 transition 只允许 fromAuthzDigest、removeRoleCodes、removeCapabilityCodes、reason', unknownKey ? `${path}.${unknownKey}` : path));
2783
+ return;
2784
+ }
2785
+ const transition = raw;
2786
+ const digest = transition.fromAuthzDigest;
2787
+ const removeRolesValid = transition.removeRoleCodes === undefined ||
2788
+ isExactStringArray(transition.removeRoleCodes, AUTHORIZATION_TRANSITION_ROLE_CODE_PATTERN, 128);
2789
+ const removeCapabilitiesValid = transition.removeCapabilityCodes === undefined ||
2790
+ isExactStringArray(transition.removeCapabilityCodes, AUTHORIZATION_TRANSITION_CAPABILITY_CODE_PATTERN, 255);
2791
+ if (typeof digest !== 'string' ||
2792
+ !/^[0-9a-f]{64}$/.test(digest) ||
2737
2793
  digests.has(digest) ||
2738
- !string(transition.reason) ||
2739
- new Set(removeRoles).size !== removeRoles.length ||
2740
- new Set(removeCapabilities).size !== removeCapabilities.length) {
2741
- diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_TRANSITION_INVALID', '授权 transition 必须按来源摘要唯一,并明确不重复的移除项和 reason', path));
2794
+ typeof transition.reason !== 'string' ||
2795
+ !transition.reason.trim() ||
2796
+ transition.reason.length > 2000 ||
2797
+ !removeRolesValid ||
2798
+ !removeCapabilitiesValid) {
2799
+ diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_TRANSITION_INVALID', '授权 transition 必须按来源摘要唯一,并使用有界、不重复的 canonical 移除项和 reason', path));
2800
+ }
2801
+ if (typeof digest === 'string' && /^[0-9a-f]{64}$/.test(digest)) {
2802
+ digests.add(digest);
2742
2803
  }
2743
- digests.add(digest);
2744
2804
  });
2745
2805
  }
2746
2806
  function validateCapabilityClosure(appCode, config, declarations, diagnostics) {