openxiangda-contracts 2.13.0 → 2.14.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.
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/native-compiler/compiler.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,kCAAkC,IAAI,uCAAuC,EACnF,MAAM,aAAa,CAAC;AAwBrB,QAAA,MAAM,iBAAiB,mDAAmD,CAAC;AAC3E,QAAA,MAAM,yBAAyB,YAA2C,CAAC;AAgF3E,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;EAc7B,CAAC;AACZ,eAAO,MAAM,mBAAmB,6HAItB,CAAC;AACX,eAAO,MAAM,uBAAuB,u/CAkC1B,CAAC;AAGX,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEtC,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;IAC1C,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;IAC5C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,6BAA6B,CAAC,CAAC,GAAG,UAAU;IAC3D,KAAK,EAAE,CAAC,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iCAAiC;IAChD,aAAa,EAAE,OAAO,iBAAiB,CAAC;IACxC,uBAAuB,EAAE,OAAO,yBAAyB,CAAC;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QACN,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,6BAA6B,CAAC;QACrC,IAAI,EAAE,6BAA6B,CAAC;QACpC,MAAM,EAAE,6BAA6B,CAAC;QACtC,SAAS,EAAE,6BAA6B,CAAC;QACzC,OAAO,EAAE,6BAA6B,CAAC;QACvC,SAAS,EAAE,6BAA6B,CAAC;KAC1C,CAAC;IACF,4BAA4B,EAAE,uCAAuC,EAAE,CAAC;IACxE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAG3C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;CAY5C;AAED,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,gCAAgC,GACtC,iCAAiC,CA8InC;AAED;;;GAGG;AACH,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,UAAU,GACjB,uCAAuC,EAAE,CA2R3C;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE;IACtD,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,aAAa,EAAE,UAAU,EAAE,CAAC;IAC5B,eAAe,CAAC,EAAE,UAAU,EAAE,CAAC;CAChC,GAAG,wBAAwB,EAAE,CA4J7B"}
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/native-compiler/compiler.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,KAAK,kCAAkC,IAAI,uCAAuC,EACnF,MAAM,aAAa,CAAC;AAwBrB,QAAA,MAAM,iBAAiB,mDAAmD,CAAC;AAC3E,QAAA,MAAM,yBAAyB,YAA2C,CAAC;AAgF3E,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;EAc7B,CAAC;AACZ,eAAO,MAAM,mBAAmB,6HAItB,CAAC;AACX,eAAO,MAAM,uBAAuB,u/CAkC1B,CAAC;AAGX,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEtC,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;IAC1C,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;IAC5C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,6BAA6B,CAAC,CAAC,GAAG,UAAU;IAC3D,KAAK,EAAE,CAAC,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iCAAiC;IAChD,aAAa,EAAE,OAAO,iBAAiB,CAAC;IACxC,uBAAuB,EAAE,OAAO,yBAAyB,CAAC;IAC1D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE;QACN,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,6BAA6B,CAAC;QACrC,IAAI,EAAE,6BAA6B,CAAC;QACpC,MAAM,EAAE,6BAA6B,CAAC;QACtC,SAAS,EAAE,6BAA6B,CAAC;QACzC,OAAO,EAAE,6BAA6B,CAAC;QACvC,SAAS,EAAE,6BAA6B,CAAC;KAC1C,CAAC;IACF,4BAA4B,EAAE,uCAAuC,EAAE,CAAC;IACxE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAG3C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;CAY5C;AAED,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,gCAAgC,GACtC,iCAAiC,CA+InC;AAED;;;GAGG;AACH,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,UAAU,GACjB,uCAAuC,EAAE,CA2R3C;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE;IACtD,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,aAAa,EAAE,UAAU,EAAE,CAAC;IAC5B,eAAe,CAAC,EAAE,UAAU,EAAE,CAAC;CAChC,GAAG,wBAAwB,EAAE,CA4J7B"}
@@ -181,6 +181,7 @@ export function compileNativeApplicationConfiguration(input) {
181
181
  validateConfigurationEnvelope(config, appCode);
182
182
  validateContractEnvelope(contract, appCode, expectedConfigDigest);
183
183
  const expectedContract = compileExpectedContract(config, expectedConfigDigest, contract.generatorVersion);
184
+ mergeUserSurfaceRoutes(expectedContract, contract, config);
184
185
  if (canonicalJson(expectedContract) !== canonicalJson(contract)) {
185
186
  fail('NATIVE_CONTRACT_CLOSURE_MISMATCH', firstDifferencePointer(expectedContract, contract, '/contracts'));
186
187
  }
@@ -1531,6 +1532,11 @@ function validateApplicationAuthentication(value, routeDeclarations, config, poi
1531
1532
  const route = object(rawRoute, `/config/frontend/routes/${index}`);
1532
1533
  routesByCode.set(String(route.code || ''), route);
1533
1534
  }
1535
+ // 生成式用户标准面路由码:user.<resource>.(records|submit)。路由事实
1536
+ // 由 routeManifest 携带;这里按同应用已声明资源判定有效性。
1537
+ const userSurfaceResourceCodes = new Set((Array.isArray(config.data?.resources) ? config.data.resources : [])
1538
+ .map(resource => String(resource.code || ''))
1539
+ .filter(code => /^[a-z][a-z0-9-]{0,62}$/.test(code)));
1534
1540
  for (const [device, surface] of [
1535
1541
  ['desktop', desktop],
1536
1542
  ['mobile', mobile],
@@ -1538,8 +1544,16 @@ function validateApplicationAuthentication(value, routeDeclarations, config, poi
1538
1544
  if (routesByCode.has(surface.routeCode)) {
1539
1545
  fail('NATIVE_APPLICATION_AUTH_SURFACE_CODE_CONFLICT', `${pointer}/surfaces/${device}/routeCode`);
1540
1546
  }
1541
- const defaultRoute = routesByCode.get(surface.defaultRouteCode);
1542
- const defaultPath = String(defaultRoute?.path || '');
1547
+ const generatedUserRoute = /^user\.([a-z][a-z0-9-]{0,62})\.(records|submit)$/.exec(String(surface.defaultRouteCode || ''));
1548
+ let defaultRoute = routesByCode.get(surface.defaultRouteCode);
1549
+ let defaultPath = String(defaultRoute?.path || '');
1550
+ if (generatedUserRoute && userSurfaceResourceCodes.has(generatedUserRoute[1])) {
1551
+ const base = generatedUserRoute[2] === 'records'
1552
+ ? `/my/${generatedUserRoute[1]}`
1553
+ : `/my/${generatedUserRoute[1]}/submit`;
1554
+ defaultRoute = { surface: 'user', path: device === 'mobile' ? `/m${base}` : base };
1555
+ defaultPath = String(defaultRoute.path);
1556
+ }
1543
1557
  const deviceMismatch = device === 'desktop'
1544
1558
  ? defaultPath === '/m' || defaultPath.startsWith('/m/')
1545
1559
  : !(defaultPath === '/m' || defaultPath.startsWith('/m/'));
@@ -2441,6 +2455,8 @@ const ROUTE_MANIFEST_KINDS = new Set([
2441
2455
  'workflow-launch',
2442
2456
  'workflow-task',
2443
2457
  'workflow-instance',
2458
+ 'resource-records',
2459
+ 'resource-submit',
2444
2460
  ]);
2445
2461
  function validateRouteManifest(value, pointer, appCode) {
2446
2462
  const manifest = object(value, pointer);
@@ -2464,8 +2480,10 @@ function validateRouteManifest(value, pointer, appCode) {
2464
2480
  routes.forEach((rawEntry, index) => {
2465
2481
  const entryPointer = `${pointer}/routes/${index}`;
2466
2482
  const entry = object(rawEntry, entryPointer);
2483
+ const resourceKinds = entry.kind === 'resource-records' || entry.kind === 'resource-submit';
2467
2484
  exactKeys(entry, ['code', 'kind', 'desktop', 'mobile'], entryPointer, [
2468
2485
  'workflowCode',
2486
+ ...(resourceKinds ? ['resourceCode'] : []),
2469
2487
  ]);
2470
2488
  const code = requiredString(entry.code, `${entryPointer}/code`, 255);
2471
2489
  if (codes.has(code))
@@ -2478,6 +2496,13 @@ function validateRouteManifest(value, pointer, appCode) {
2478
2496
  if (entry.workflowCode !== undefined) {
2479
2497
  stableCode(entry.workflowCode, `${entryPointer}/workflowCode`);
2480
2498
  }
2499
+ if (resourceKinds) {
2500
+ const resourceCodeValue = resourceCode(entry.resourceCode, `${entryPointer}/resourceCode`);
2501
+ equal(code, `user.${resourceCodeValue}.${entry.kind === 'resource-records' ? 'records' : 'submit'}`, `${entryPointer}/code`);
2502
+ }
2503
+ else if (entry.resourceCode !== undefined) {
2504
+ fail('NATIVE_PROPERTY_UNKNOWN', `${entryPointer}/resourceCode`);
2505
+ }
2481
2506
  const desktop = validateRouteManifestRoute(entry.desktop, `${entryPointer}/desktop`, appCode);
2482
2507
  const mobile = validateRouteManifestRoute(entry.mobile, `${entryPointer}/mobile`, appCode);
2483
2508
  if (desktop.surface !== 'user') {
@@ -2593,25 +2618,41 @@ function compileRouteManifestRoute(routeCode, path, surface, page) {
2593
2618
  requiresAuthentication: true,
2594
2619
  };
2595
2620
  }
2596
- function standardRouteCode(kind, device, workflowCode) {
2621
+ function standardRouteCode(kind, device, workflowCode, resourceCode) {
2597
2622
  if (kind === 'workflow-launch') {
2598
2623
  return `workflow.${workflowCode}.launch.${device}`;
2599
2624
  }
2600
2625
  if (kind === 'application-todo-center') {
2601
2626
  return `application.todo-center.${device}`;
2602
2627
  }
2628
+ if (kind === 'resource-records') {
2629
+ return `user.${resourceCode}.records.${device}`;
2630
+ }
2631
+ if (kind === 'resource-submit') {
2632
+ return `user.${resourceCode}.submit.${device}`;
2633
+ }
2603
2634
  return `${kind.replace(/-/g, '.')}.${device}`;
2604
2635
  }
2636
+ function resourceCapabilitiesOf(appCode, resourceCodeValue) {
2637
+ const prefix = `app:${appCode}:data:${resourceCodeValue}`;
2638
+ return {
2639
+ read: `${prefix}:read`,
2640
+ create: `${prefix}:create`,
2641
+ update: `${prefix}:update`,
2642
+ delete: `${prefix}:delete`,
2643
+ };
2644
+ }
2605
2645
  function compileRouteManifest(config) {
2606
2646
  const appCode = config.appCode;
2607
2647
  const routes = [];
2608
- const addRoute = (code, kind, desktopPath, mobilePath, workflowCode, access) => {
2648
+ const addRoute = (code, kind, desktopPath, mobilePath, workflowCode, access, resourceCodeValue) => {
2609
2649
  routes.push({
2610
2650
  code,
2611
2651
  kind,
2612
2652
  ...(workflowCode ? { workflowCode } : {}),
2613
- desktop: compileRouteManifestRoute(standardRouteCode(kind, 'desktop', workflowCode), desktopPath, 'user', access),
2614
- mobile: compileRouteManifestRoute(standardRouteCode(kind, 'mobile', workflowCode), mobilePath, 'user', access),
2653
+ ...(resourceCodeValue ? { resourceCode: resourceCodeValue } : {}),
2654
+ desktop: compileRouteManifestRoute(standardRouteCode(kind, 'desktop', workflowCode, resourceCodeValue), desktopPath, 'user', access),
2655
+ mobile: compileRouteManifestRoute(standardRouteCode(kind, 'mobile', workflowCode, resourceCodeValue), mobilePath, 'user', access),
2615
2656
  });
2616
2657
  };
2617
2658
  if (config.frontend.user.applicationTodoCenter === true) {
@@ -4131,6 +4172,75 @@ function validateWorkflowDefinitionBinding(definition, binding, pointer) {
4131
4172
  }
4132
4173
  }
4133
4174
  }
4175
+ /**
4176
+ * 生成式用户标准面的路由事实由工具链编译器产出;配置 bundle 不携带
4177
+ * userSurface。闭合时按配置事实校验这些路由(资源存在、能力等于该资源
4178
+ * read/create、路径与码形状稳定),通过后并入期望契约并重算清单摘要。
4179
+ * 路由本身仍受能力门禁约束,注入不产生任何额外授权。
4180
+ */
4181
+ function mergeUserSurfaceRoutes(expectedContract, contract, config) {
4182
+ const manifest = object(contract.routeManifest, '/contracts/routeManifest');
4183
+ const expectedManifest = object(expectedContract.routeManifest, '/contracts/routeManifest');
4184
+ const resources = new Map();
4185
+ for (const raw of boundedArray(config.data?.resources, '/config/data/resources', 100)) {
4186
+ const resource = object(raw, '/config/data/resources');
4187
+ resources.set(String(resource.code || ''), resource);
4188
+ }
4189
+ const existingCodes = new Set(boundedArray(expectedManifest.routes, '/contracts/routeManifest/routes', 1000).map(entry => String(object(entry, '/contracts/routeManifest/routes').code || '')));
4190
+ const accepted = [];
4191
+ for (const rawEntry of boundedArray(manifest.routes, '/contracts/routeManifest/routes', 1000)) {
4192
+ const entry = object(rawEntry, '/contracts/routeManifest/routes');
4193
+ const kind = String(entry.kind || '');
4194
+ if (kind !== 'resource-records' && kind !== 'resource-submit')
4195
+ continue;
4196
+ const resourceCodeValue = String(entry.resourceCode || '');
4197
+ const resource = resources.get(resourceCodeValue);
4198
+ if (!resource) {
4199
+ fail('NATIVE_ROUTE_MANIFEST_RESOURCE_UNKNOWN', `/contracts/routeManifest/routes`);
4200
+ }
4201
+ const suffix = kind === 'resource-records' ? 'records' : 'submit';
4202
+ const code = `user.${resourceCodeValue}.${suffix}`;
4203
+ equal(String(entry.code || ''), code, '/contracts/routeManifest/routes/code');
4204
+ const operation = kind === 'resource-records' ? 'read' : 'create';
4205
+ const capability = String(object(resource.capabilities, '/config/data/resources/capabilities')[operation] || '');
4206
+ for (const device of ['desktop', 'mobile']) {
4207
+ const route = object(entry[device], `/contracts/routeManifest/routes/${device}`);
4208
+ const prefix = device === 'mobile' ? '/m/my/' : '/my/';
4209
+ const suffixPath = kind === 'resource-records'
4210
+ ? `${prefix}${resourceCodeValue}`
4211
+ : `${prefix}${resourceCodeValue}/submit`;
4212
+ equal(String(route.path || ''), suffixPath, `/contracts/routeManifest/routes/${device}/path`);
4213
+ equal(String(route.capability || ''), capability, `/contracts/routeManifest/routes/${device}/capability`);
4214
+ equal(String(route.routeCode || ''), `${code}.${device}`, `/contracts/routeManifest/routes/${device}/routeCode`);
4215
+ }
4216
+ if (existingCodes.has(code) || accepted.some(item => String(item.code) === code)) {
4217
+ fail('NATIVE_ROUTE_MANIFEST_DUPLICATE', '/contracts/routeManifest/routes/code');
4218
+ }
4219
+ accepted.push(entry);
4220
+ }
4221
+ if (!accepted.length)
4222
+ return;
4223
+ expectedManifest.routes = boundedArray([...expectedManifest.routes, ...accepted], '/contracts/routeManifest/routes', 1000).sort((left, right) => compareText(String(object(left, 'x').code || ''), String(object(right, 'x').code || '')));
4224
+ // rootEntry 指向已接受的 records 路由时同样并入。
4225
+ const rootCode = String(object(manifest.rootEntry, 'x').code || '');
4226
+ const homeEntry = accepted.find(entry => String(entry.code) === rootCode && entry.kind === 'resource-records');
4227
+ if (homeEntry) {
4228
+ const root = object(manifest.rootEntry, 'x');
4229
+ expectedManifest.rootEntry = {
4230
+ code: rootCode,
4231
+ desktop: String(root.desktop || ''),
4232
+ mobile: String(root.mobile || ''),
4233
+ };
4234
+ }
4235
+ expectedManifest.digest = sha256Canonical({
4236
+ schemaVersion: expectedManifest.schemaVersion,
4237
+ appCode: expectedManifest.appCode,
4238
+ devicePolicy: expectedManifest.devicePolicy,
4239
+ rootEntry: expectedManifest.rootEntry,
4240
+ authentication: expectedManifest.authentication,
4241
+ routes: expectedManifest.routes,
4242
+ });
4243
+ }
4134
4244
  function validateResource(raw, pointer, appCode) {
4135
4245
  const resource = object(raw, pointer);
4136
4246
  exactKeys(resource, [