openxiangda-devkit-core 2.0.0-alpha.88 → 2.0.0-alpha.91

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.
@@ -1,4 +1,4 @@
1
- import { SCHEMA_VERSIONS, DATA_FIELD_TYPES, PLATFORM_EVENT_TYPES_V2, nativePlatformCapabilityCatalog, validateDataResource, } from 'openxiangda-contracts';
1
+ 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
2
  import { Ajv2020 } from 'ajv/dist/2020.js';
3
3
  import { CronExpressionParser } from 'cron-parser';
4
4
  import { fieldNullable, supportsGeneratedMutation, } from './field-codec.js';
@@ -338,6 +338,16 @@ export const openXiangdaAppConfigSchema = {
338
338
  admin: { type: 'object' },
339
339
  authentication: { type: 'object' },
340
340
  publicAccess: { type: 'object' },
341
+ devicePolicy: {
342
+ type: 'object',
343
+ additionalProperties: false,
344
+ required: ['kind', 'mobileMaxWidthPx', 'desktopMinWidthPx'],
345
+ properties: {
346
+ kind: { const: 'viewport-family' },
347
+ mobileMaxWidthPx: { type: 'integer', minimum: 320, maximum: 1600 },
348
+ desktopMinWidthPx: { type: 'integer', minimum: 320, maximum: 1600 },
349
+ },
350
+ },
341
351
  },
342
352
  },
343
353
  backend: {
@@ -817,6 +827,7 @@ export function validateAppConfig(value) {
817
827
  diagnostics.push(diagnostic('APP_CONFIG_BACKEND_RESOURCE_PROFILE_UNSUPPORTED', 'backend.resourceProfile 必须是 light 或 standard', 'backend.resourceProfile'));
818
828
  }
819
829
  validateApplicationAuthentication(config, diagnostics);
830
+ validateFrontendDevicePolicy(config, diagnostics);
820
831
  validateFrontendRoutes(config, diagnostics);
821
832
  validateAnonymousPublicAccess(config, diagnostics);
822
833
  validateAdminNavigation(config, diagnostics);
@@ -1512,6 +1523,33 @@ export function validateAppConfig(value) {
1512
1523
  })),
1513
1524
  ];
1514
1525
  }));
1526
+ const workflowSummaryResourceFields = new Map(eventDataResources.map(rawResource => {
1527
+ const resource = object(rawResource);
1528
+ const fields = Array.isArray(object(resource.schema).fields)
1529
+ ? object(resource.schema).fields
1530
+ : [];
1531
+ const surfaceFields = object(object(resource.surface).fields);
1532
+ return [
1533
+ string(resource.code),
1534
+ new Map(fields.map(rawField => {
1535
+ const field = object(rawField);
1536
+ const code = string(field.code);
1537
+ const surface = object(surfaceFields[code]);
1538
+ const maxLength = typeof field.maxLength === 'number' &&
1539
+ Number.isFinite(field.maxLength)
1540
+ ? field.maxLength
1541
+ : undefined;
1542
+ return [
1543
+ code,
1544
+ {
1545
+ type: string(field.type),
1546
+ system: surface.system === true || field.system === true,
1547
+ ...(maxLength !== undefined ? { maxLength } : {}),
1548
+ },
1549
+ ];
1550
+ })),
1551
+ ];
1552
+ }));
1515
1553
  const platformEventTypes = new Set(PLATFORM_EVENT_TYPES_V2);
1516
1554
  const declaredEventTypes = new Set();
1517
1555
  const applicationEventSchemas = new Map();
@@ -1936,7 +1974,29 @@ export function validateAppConfig(value) {
1936
1974
  const factProjection = object(subject.factProjection);
1937
1975
  const factEntries = Object.entries(factProjection);
1938
1976
  const subjectFields = eventResourceFields.get(string(subject.resourceCode));
1939
- if (Object.keys(subject).some(key => !['resourceCode', 'factProjection'].includes(key)) ||
1977
+ const summaryFieldsValue = subject.summaryFields;
1978
+ const summaryFields = Array.isArray(summaryFieldsValue)
1979
+ ? summaryFieldsValue
1980
+ : [];
1981
+ const summaryResourceFields = workflowSummaryResourceFields.get(string(subject.resourceCode));
1982
+ const summaryFieldsInvalid = summaryFieldsValue !== undefined &&
1983
+ (!Array.isArray(summaryFieldsValue) ||
1984
+ summaryFields.length > WORKFLOW_SUMMARY_MAX_FIELDS ||
1985
+ new Set(summaryFields).size !== summaryFields.length ||
1986
+ summaryFields.some(field => {
1987
+ if (typeof field !== 'string' ||
1988
+ !/^[A-Za-z][A-Za-z0-9_]{0,62}$/.test(field)) {
1989
+ return true;
1990
+ }
1991
+ const metadata = summaryResourceFields?.get(field);
1992
+ return (!metadata ||
1993
+ metadata.system ||
1994
+ !isWorkflowSummaryFieldType(metadata.type) ||
1995
+ (metadata.type === 'text.long' &&
1996
+ metadata.maxLength !== undefined &&
1997
+ metadata.maxLength > WORKFLOW_SUMMARY_TEXT_LONG_MAX_BYTES));
1998
+ }));
1999
+ if (Object.keys(subject).some(key => !['resourceCode', 'factProjection', 'summaryFields'].includes(key)) ||
1940
2000
  !subjectFields ||
1941
2001
  !isRecord(subject.factProjection) ||
1942
2002
  factEntries.length < 1 ||
@@ -1946,6 +2006,9 @@ export function validateAppConfig(value) {
1946
2006
  !subjectFields.has(string(fieldCode)))) {
1947
2007
  diagnostics.push(diagnostic('APP_CONFIG_WORKFLOW_SUBJECT_INVALID', 'Workflow subject 必须绑定已声明资源,并将 1 到 64 个 fact key 映射到该资源的已声明字段', `${path}.definition.subject`));
1948
2008
  }
2009
+ if (summaryFieldsInvalid) {
2010
+ diagnostics.push(diagnostic('APP_CONFIG_WORKFLOW_SUMMARY_FIELDS_INVALID', `Workflow summaryFields 最多 ${WORKFLOW_SUMMARY_MAX_FIELDS} 个,必须是不重复的已声明 scalar/reference 字段;禁止 system/rich/json/subtable/file/image/signature,且 text.long 不超过 ${WORKFLOW_SUMMARY_TEXT_LONG_MAX_BYTES} bytes`, `${path}.definition.subject.summaryFields`));
2011
+ }
1949
2012
  for (const error of validateWorkflowDefinition(definition)) {
1950
2013
  diagnostics.push(diagnostic('APP_CONFIG_WORKFLOW_DEFINITION_INVALID', error, path));
1951
2014
  }
@@ -2374,6 +2437,26 @@ function generatedPlatformRouteClaims(config) {
2374
2437
  }
2375
2438
  return claims;
2376
2439
  }
2440
+ function validateFrontendDevicePolicy(config, diagnostics) {
2441
+ const frontend = object(config.frontend);
2442
+ if (frontend.devicePolicy === undefined)
2443
+ return;
2444
+ const policy = object(frontend.devicePolicy);
2445
+ const mobileMaxWidthPx = policy.mobileMaxWidthPx;
2446
+ const desktopMinWidthPx = policy.desktopMinWidthPx;
2447
+ const invalid = Object.keys(policy).some(key => !['kind', 'mobileMaxWidthPx', 'desktopMinWidthPx'].includes(key)) ||
2448
+ policy.kind !== 'viewport-family' ||
2449
+ !Number.isInteger(mobileMaxWidthPx) ||
2450
+ !Number.isInteger(desktopMinWidthPx) ||
2451
+ Number(mobileMaxWidthPx) < 320 ||
2452
+ Number(mobileMaxWidthPx) > 1600 ||
2453
+ Number(desktopMinWidthPx) < 320 ||
2454
+ Number(desktopMinWidthPx) > 1600 ||
2455
+ Number(desktopMinWidthPx) !== Number(mobileMaxWidthPx) + 1;
2456
+ if (invalid) {
2457
+ diagnostics.push(diagnostic('APP_CONFIG_FRONTEND_DEVICE_POLICY_INVALID', 'frontend.devicePolicy 必须是 viewport-family,移动最大宽度和桌面最小宽度为 320-1600 的相邻整数', 'frontend.devicePolicy'));
2458
+ }
2459
+ }
2377
2460
  function validateFrontendRoutes(config, diagnostics) {
2378
2461
  const frontend = object(config.frontend);
2379
2462
  const rawRoutes = frontend.routes;
@@ -3059,7 +3142,7 @@ function validAppPath(value) {
3059
3142
  !value.includes('..') &&
3060
3143
  !value.includes('?') &&
3061
3144
  !value.includes('#') &&
3062
- value.length <= 512);
3145
+ value.length <= 2048);
3063
3146
  }
3064
3147
  function isRecord(value) {
3065
3148
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);