openxiangda-devkit-core 2.0.0-alpha.98 → 2.0.0

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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -9
  3. package/dist/app-spec.d.ts +30 -12
  4. package/dist/app-spec.d.ts.map +1 -1
  5. package/dist/app-spec.js +238 -57
  6. package/dist/app-spec.js.map +1 -1
  7. package/dist/application-services.d.ts +625 -42
  8. package/dist/application-services.d.ts.map +1 -1
  9. package/dist/application-services.js +321 -85
  10. package/dist/application-services.js.map +1 -1
  11. package/dist/application-ui-contract.d.ts +5 -0
  12. package/dist/application-ui-contract.d.ts.map +1 -0
  13. package/dist/application-ui-contract.js +149 -0
  14. package/dist/application-ui-contract.js.map +1 -0
  15. package/dist/backend-image-build.js +6 -11
  16. package/dist/backend-image-build.js.map +1 -1
  17. package/dist/command-process.d.ts +14 -0
  18. package/dist/command-process.d.ts.map +1 -0
  19. package/dist/command-process.js +51 -0
  20. package/dist/command-process.js.map +1 -0
  21. package/dist/command-registry.d.ts +44 -4
  22. package/dist/command-registry.d.ts.map +1 -1
  23. package/dist/command-registry.js +10 -6
  24. package/dist/command-registry.js.map +1 -1
  25. package/dist/compiler/ai-catalog.d.ts.map +1 -1
  26. package/dist/compiler/ai-catalog.js +2 -1
  27. package/dist/compiler/ai-catalog.js.map +1 -1
  28. package/dist/compiler/application-model.d.ts +60 -0
  29. package/dist/compiler/application-model.d.ts.map +1 -0
  30. package/dist/compiler/application-model.js +141 -0
  31. package/dist/compiler/application-model.js.map +1 -0
  32. package/dist/compiler/bundle.d.ts.map +1 -1
  33. package/dist/compiler/bundle.js +196 -98
  34. package/dist/compiler/bundle.js.map +1 -1
  35. package/dist/compiler/config.d.ts +36 -10
  36. package/dist/compiler/config.d.ts.map +1 -1
  37. package/dist/compiler/config.js +536 -171
  38. package/dist/compiler/config.js.map +1 -1
  39. package/dist/compiler/field-surface.js +1 -1
  40. package/dist/compiler/field-surface.js.map +1 -1
  41. package/dist/compiler/package-compiler.d.ts.map +1 -1
  42. package/dist/compiler/package-compiler.js +12 -0
  43. package/dist/compiler/package-compiler.js.map +1 -1
  44. package/dist/compiler/permission-review.d.ts +139 -0
  45. package/dist/compiler/permission-review.d.ts.map +1 -0
  46. package/dist/compiler/permission-review.js +131 -0
  47. package/dist/compiler/permission-review.js.map +1 -0
  48. package/dist/configuration-preflight.d.ts +7 -0
  49. package/dist/configuration-preflight.d.ts.map +1 -0
  50. package/dist/configuration-preflight.js +13 -0
  51. package/dist/configuration-preflight.js.map +1 -0
  52. package/dist/control-plane-client.d.ts +56 -61
  53. package/dist/control-plane-client.d.ts.map +1 -1
  54. package/dist/control-plane-client.js +89 -8
  55. package/dist/control-plane-client.js.map +1 -1
  56. package/dist/delivery-cache.d.ts +34 -0
  57. package/dist/delivery-cache.d.ts.map +1 -0
  58. package/dist/delivery-cache.js +186 -0
  59. package/dist/delivery-cache.js.map +1 -0
  60. package/dist/delivery-source.d.ts +17 -0
  61. package/dist/delivery-source.d.ts.map +1 -0
  62. package/dist/delivery-source.js +85 -0
  63. package/dist/delivery-source.js.map +1 -0
  64. package/dist/deployment-observer.d.ts +14 -0
  65. package/dist/deployment-observer.d.ts.map +1 -0
  66. package/dist/deployment-observer.js +68 -0
  67. package/dist/deployment-observer.js.map +1 -0
  68. package/dist/deployment.d.ts +17 -1
  69. package/dist/deployment.d.ts.map +1 -1
  70. package/dist/deployment.js +55 -15
  71. package/dist/deployment.js.map +1 -1
  72. package/dist/design-readiness.d.ts +24 -0
  73. package/dist/design-readiness.d.ts.map +1 -0
  74. package/dist/design-readiness.js +102 -0
  75. package/dist/design-readiness.js.map +1 -0
  76. package/dist/developer-errors.d.ts +11 -0
  77. package/dist/developer-errors.d.ts.map +1 -0
  78. package/dist/developer-errors.js +16 -0
  79. package/dist/developer-errors.js.map +1 -0
  80. package/dist/developer-operations.d.ts +190 -0
  81. package/dist/developer-operations.d.ts.map +1 -0
  82. package/dist/developer-operations.js +65 -0
  83. package/dist/developer-operations.js.map +1 -0
  84. package/dist/development-lifecycle.d.ts +75 -0
  85. package/dist/development-lifecycle.d.ts.map +1 -0
  86. package/dist/development-lifecycle.js +189 -0
  87. package/dist/development-lifecycle.js.map +1 -0
  88. package/dist/documentation.d.ts +111 -0
  89. package/dist/documentation.d.ts.map +1 -0
  90. package/dist/documentation.js +112 -0
  91. package/dist/documentation.js.map +1 -0
  92. package/dist/index.d.ts +10 -0
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +10 -0
  95. package/dist/index.js.map +1 -1
  96. package/dist/operation-progress.d.ts +26 -0
  97. package/dist/operation-progress.d.ts.map +1 -0
  98. package/dist/operation-progress.js +92 -0
  99. package/dist/operation-progress.js.map +1 -0
  100. package/dist/optional-backend.d.ts +14 -0
  101. package/dist/optional-backend.d.ts.map +1 -0
  102. package/dist/optional-backend.js +145 -0
  103. package/dist/optional-backend.js.map +1 -0
  104. package/dist/session.d.ts +22 -0
  105. package/dist/session.d.ts.map +1 -1
  106. package/dist/session.js +36 -0
  107. package/dist/session.js.map +1 -1
  108. package/dist/workspace-loader.d.ts.map +1 -1
  109. package/dist/workspace-loader.js +17 -2
  110. package/dist/workspace-loader.js.map +1 -1
  111. package/dist/workspace-operation.d.ts +3 -0
  112. package/dist/workspace-operation.d.ts.map +1 -0
  113. package/dist/workspace-operation.js +45 -0
  114. package/dist/workspace-operation.js.map +1 -0
  115. package/dist/workspace.d.ts.map +1 -1
  116. package/dist/workspace.js +1 -0
  117. package/dist/workspace.js.map +1 -1
  118. package/package.json +18 -4
  119. package/templates/backend/Dockerfile +20 -0
  120. package/templates/backend/package.json +27 -0
  121. package/templates/backend/src/app.module.ts +23 -0
  122. package/templates/backend/src/main.ts +5 -0
  123. package/templates/backend/tsconfig.json +9 -0
@@ -1,3 +1,5 @@
1
+ import { projectDataResourceView } from 'openxiangda-contracts';
2
+ import { nativeFieldRequiresCreateInputV2 } from 'openxiangda-contracts/native-compiler';
1
3
  import { createHash } from 'node:crypto';
2
4
  import { canonicalJson, applicationEventHandlerPathV2, DATA_EVENT_TYPES_V2, nativePlatformCapabilityCatalog, OPENXIANGDA_COMPILER_CONTRACT_VERSION, SCHEMA_VERSIONS, WORKFLOW_EVENT_TYPES_V2, sha256Digest, } from 'openxiangda-contracts';
3
5
  import { AppConfigValidationError, } from './config.js';
@@ -237,6 +239,9 @@ export function normalizeConfiguration(config) {
237
239
  ...(operation.platformAccess
238
240
  ? {
239
241
  platformAccess: {
242
+ ...(operation.platformAccess.roleAssertions ? {
243
+ roleAssertions: { roleCodes: uniqueSorted(operation.platformAccess.roleAssertions.roleCodes) },
244
+ } : {}),
240
245
  ...(operation.platformAccess.directory
241
246
  ? {
242
247
  directory: {
@@ -306,6 +311,19 @@ export function normalizeConfiguration(config) {
306
311
  },
307
312
  data: {
308
313
  resources: sorted((config.data?.resources || []).map(normalizeDataResource), item => item.code),
314
+ ...((config.data?.resources || []).some(resource => resource.detailRouteCode)
315
+ ? {
316
+ resourceDetailRoutes: sorted((config.data?.resources || []).flatMap(resource => resource.detailRouteCode
317
+ ? [
318
+ {
319
+ resourceCode: resource.code,
320
+ desktop: resource.detailRouteCode.desktop,
321
+ mobile: resource.detailRouteCode.mobile,
322
+ },
323
+ ]
324
+ : []), item => item.resourceCode),
325
+ }
326
+ : {}),
309
327
  },
310
328
  events: {
311
329
  schemas: sorted((config.events?.schemas || []).map(schema => ({
@@ -381,7 +399,7 @@ export function normalizeConfiguration(config) {
381
399
  definitions: sorted((config.workflows?.definitions || []).map(item => ({
382
400
  version: item.version,
383
401
  definition: normalizeWorkflowDefinition(item.definition),
384
- launch: { mode: item.launch?.mode || 'work-center-only' },
402
+ launch: normalizeWorkflowLaunchDeclaration(item.launch),
385
403
  ...(item.detailRouteCode
386
404
  ? { detailRouteCode: { ...item.detailRouteCode } }
387
405
  : {}),
@@ -427,12 +445,35 @@ export function normalizeConfiguration(config) {
427
445
  },
428
446
  frontend: {
429
447
  routes: compileFrontendRoutes(config),
448
+ user: {
449
+ applicationTodoCenter: config.frontend.user?.applicationTodoCenter === true,
450
+ },
430
451
  devicePolicy: compileRouteManifestDevicePolicy(config),
431
452
  authentication: compileApplicationAuthentication(config),
432
453
  ...(config.frontend.publicAccess
433
454
  ? { publicAccess: compileAnonymousPublicAccess(config) }
434
455
  : {}),
435
456
  admin: {
457
+ ...(config.frontend.admin?.access
458
+ ? {
459
+ access: {
460
+ ...(config.frontend.admin.access.allOf
461
+ ? {
462
+ allOf: uniqueSorted([
463
+ ...config.frontend.admin.access.allOf,
464
+ ]),
465
+ }
466
+ : {}),
467
+ ...(config.frontend.admin.access.anyOf
468
+ ? {
469
+ anyOf: uniqueSorted([
470
+ ...config.frontend.admin.access.anyOf,
471
+ ]),
472
+ }
473
+ : {}),
474
+ },
475
+ }
476
+ : {}),
436
477
  navigation: (config.frontend.admin?.navigation || []).map(group => ({
437
478
  code: group.code,
438
479
  label: group.label,
@@ -593,7 +634,7 @@ function compileContractBundleFromNormalized(config, configDigest, generatorVers
593
634
  appCode: config.app.code,
594
635
  configDigest,
595
636
  perspectives: normalizedConfiguration.perspectives,
596
- resources: compileResources(config),
637
+ resources: compileResources(normalizedConfiguration),
597
638
  capabilities,
598
639
  operations: compileOperations(config),
599
640
  eventConsumers,
@@ -620,7 +661,10 @@ function compileContractBundleFromNormalized(config, configDigest, generatorVers
620
661
  ...(config.frontend.publicAccess
621
662
  ? { publicAccess: compileAnonymousPublicAccess(config) }
622
663
  : {}),
623
- routeManifest: compileStandardRouteManifest(config, adminPages),
664
+ ...(normalizedConfiguration.frontend.admin.access
665
+ ? { adminAccess: normalizedConfiguration.frontend.admin.access }
666
+ : {}),
667
+ routeManifest: compileStandardRouteManifest(config),
624
668
  adminPages,
625
669
  adminNavigation: compileAdminNavigation(config),
626
670
  };
@@ -727,8 +771,79 @@ function generatedResourceOperations(resource) {
727
771
  delete: resource.surface?.generated?.delete ?? native,
728
772
  };
729
773
  }
730
- function generatedAdminResourcePath(resourceCode, operation) {
731
- const base = `/admin/resources/${resourceCode}`;
774
+ function normalizeWorkflowNamedOperationIntent(intent) {
775
+ return {
776
+ operationCode: intent.operationCode,
777
+ inputs: Object.fromEntries(Object.entries(intent.inputs)
778
+ .sort(([left], [right]) => compare(left, right))
779
+ .map(([inputCode, binding]) => [inputCode, { ...binding }])),
780
+ output: { ...intent.output },
781
+ };
782
+ }
783
+ function normalizeWorkflowLaunchDeclaration(launch) {
784
+ return {
785
+ mode: launch?.mode || 'work-center-only',
786
+ ...(launch?.submission
787
+ ? {
788
+ submission: {
789
+ kind: 'named-operation',
790
+ ...(launch.submission.create
791
+ ? {
792
+ create: normalizeWorkflowNamedOperationIntent(launch.submission.create),
793
+ }
794
+ : {}),
795
+ ...(launch.submission.existing
796
+ ? {
797
+ existing: normalizeWorkflowNamedOperationIntent(launch.submission.existing),
798
+ }
799
+ : {}),
800
+ ...(launch.submission.context
801
+ ? {
802
+ context: [...launch.submission.context]
803
+ .map(context => ({ ...context }))
804
+ .sort((left, right) => compare(left.queryParameter, right.queryParameter)),
805
+ }
806
+ : {}),
807
+ },
808
+ }
809
+ : {}),
810
+ };
811
+ }
812
+ function compileWorkflowNamedOperationIntent(config, intent) {
813
+ const operation = (config.backend?.operations || []).find(candidate => candidate.code === intent.operationCode);
814
+ return {
815
+ ...normalizeWorkflowNamedOperationIntent(intent),
816
+ method: 'POST',
817
+ path: operation.path,
818
+ requiredCapability: operation.capability,
819
+ requestSchemaDigest: sha256Digest(operation.requestSchema),
820
+ responseSchemaDigest: sha256Digest(operation.responseSchema),
821
+ };
822
+ }
823
+ function compileWorkflowLaunchContract(config, launch) {
824
+ const normalized = normalizeWorkflowLaunchDeclaration(launch);
825
+ if (!normalized.submission)
826
+ return { mode: normalized.mode };
827
+ return {
828
+ mode: normalized.mode,
829
+ submission: {
830
+ kind: 'named-operation',
831
+ ...(normalized.submission.create
832
+ ? {
833
+ create: compileWorkflowNamedOperationIntent(config, normalized.submission.create),
834
+ }
835
+ : {}),
836
+ ...(normalized.submission.existing
837
+ ? {
838
+ existing: compileWorkflowNamedOperationIntent(config, normalized.submission.existing),
839
+ }
840
+ : {}),
841
+ context: [...(normalized.submission.context || [])],
842
+ },
843
+ };
844
+ }
845
+ function generatedAdminResourcePath(resourceCode, operation, viewCode) {
846
+ const base = `/admin/resources/${resourceCode}${viewCode ? `/views/${viewCode}` : ""}`;
732
847
  if (operation === 'list')
733
848
  return base;
734
849
  if (operation === 'create')
@@ -739,13 +854,18 @@ function generatedAdminResourcePath(resourceCode, operation) {
739
854
  }
740
855
  function compileAdminPages(config) {
741
856
  const pages = [];
742
- for (const resource of sorted(config.data?.resources || [], item => item.code)) {
857
+ for (const selection of sorted(config.data?.resources || [], item => item.code).flatMap(resource => [
858
+ { resource, viewCode: undefined },
859
+ ...(resource.surface?.views || []).map(view => ({ viewCode: view.code,
860
+ resource: { ...resource, name: view.name, surface: projectDataResourceView(resource.surface, view.code) } })),
861
+ ])) {
862
+ const { resource, viewCode } = selection;
743
863
  const generated = generatedResourceOperations(resource);
744
864
  const candidates = [
745
865
  {
746
866
  operation: 'list',
747
867
  kind: 'resource-list',
748
- path: generatedAdminResourcePath(resource.code, 'list'),
868
+ path: generatedAdminResourcePath(resource.code, 'list', viewCode),
749
869
  label: resource.name,
750
870
  capability: resource.capabilities.read,
751
871
  navigationEligible: true,
@@ -753,7 +873,7 @@ function compileAdminPages(config) {
753
873
  {
754
874
  operation: 'detail',
755
875
  kind: 'resource-detail',
756
- path: generatedAdminResourcePath(resource.code, 'detail'),
876
+ path: generatedAdminResourcePath(resource.code, 'detail', viewCode),
757
877
  label: `${resource.name}详情`,
758
878
  capability: resource.capabilities.read,
759
879
  navigationEligible: false,
@@ -761,7 +881,7 @@ function compileAdminPages(config) {
761
881
  {
762
882
  operation: 'create',
763
883
  kind: 'resource-create',
764
- path: generatedAdminResourcePath(resource.code, 'create'),
884
+ path: generatedAdminResourcePath(resource.code, 'create', viewCode),
765
885
  label: `新增${resource.name}`,
766
886
  capability: resource.capabilities.create,
767
887
  navigationEligible: false,
@@ -769,7 +889,7 @@ function compileAdminPages(config) {
769
889
  {
770
890
  operation: 'update',
771
891
  kind: 'resource-update',
772
- path: generatedAdminResourcePath(resource.code, 'update'),
892
+ path: generatedAdminResourcePath(resource.code, 'update', viewCode),
773
893
  label: `编辑${resource.name}`,
774
894
  capability: resource.capabilities.update,
775
895
  navigationEligible: false,
@@ -779,13 +899,14 @@ function compileAdminPages(config) {
779
899
  if (!generated[candidate.operation])
780
900
  continue;
781
901
  pages.push({
782
- code: `resource:${resource.code}:${candidate.operation}`,
902
+ code: `resource:${resource.code}${viewCode ? `:view:${viewCode}` : ""}:${candidate.operation}`,
783
903
  kind: candidate.kind,
784
904
  path: candidate.path,
785
905
  label: candidate.label,
786
906
  navigationEligible: candidate.navigationEligible,
787
907
  capability: candidate.capability,
788
908
  resourceCode: resource.code,
909
+ ...(viewCode ? { viewCode } : {}),
789
910
  });
790
911
  }
791
912
  }
@@ -797,53 +918,10 @@ function compileAdminPages(config) {
797
918
  kind: 'operation',
798
919
  path: route.path,
799
920
  label: route.label,
800
- navigationEligible: !/[:*]/.test(route.path),
921
+ navigationEligible: !route.path.startsWith('/m/admin') && !/[:*]/.test(route.path),
801
922
  routeCode: route.code,
802
923
  });
803
924
  }
804
- if ((config.frontend.admin?.navigation || []).some(group => group.items.some(item => item.page.kind === 'application-todo-center'))) {
805
- pages.push({
806
- code: 'application:todo-center',
807
- kind: 'application-todo-center',
808
- path: '/admin/todos',
809
- label: '待办中心',
810
- navigationEligible: true,
811
- });
812
- }
813
- const workflows = compileWorkflows(config);
814
- if (workflows.length) {
815
- pages.push({
816
- code: 'workflow:work-center',
817
- kind: 'workflow-work-center',
818
- path: '/admin/work-center',
819
- label: '我的审批',
820
- navigationEligible: true,
821
- }, {
822
- code: 'workflow:task',
823
- kind: 'workflow-task',
824
- path: '/tasks/:taskId',
825
- label: '审批任务',
826
- navigationEligible: false,
827
- }, {
828
- code: 'workflow:instance',
829
- kind: 'workflow-instance',
830
- path: '/workflows/:instanceId',
831
- label: '流程详情',
832
- navigationEligible: false,
833
- });
834
- }
835
- for (const workflow of workflows) {
836
- if (!['standalone', 'hidden-handoff'].includes(workflow.launch.mode))
837
- continue;
838
- pages.push({
839
- code: `workflow:${workflow.code}:launch`,
840
- kind: 'workflow-launch',
841
- path: `/admin/workflows/${workflow.code}/new`,
842
- label: `发起${workflow.title}`,
843
- navigationEligible: workflow.launch.mode === 'standalone',
844
- workflowCode: workflow.code,
845
- });
846
- }
847
925
  return pages.sort((left, right) => compare(left.code, right.code));
848
926
  }
849
927
  function routePathParams(path) {
@@ -860,14 +938,14 @@ function compileRouteManifestDevicePolicy(config) {
860
938
  ...(config.frontend.devicePolicy || {}),
861
939
  };
862
940
  }
863
- function compileStandardRouteManifestRoute(routeCode, path, surface, page) {
941
+ function compileStandardRouteManifestRoute(routeCode, path, surface, access) {
864
942
  return {
865
943
  routeCode,
866
944
  path,
867
945
  surface,
868
946
  pathParams: routePathParams(path),
869
- ...(page?.capability ? { capability: page.capability } : {}),
870
- ...(page?.access ? { access: page.access } : {}),
947
+ ...(access?.capability ? { capability: access.capability } : {}),
948
+ ...(access?.access ? { access: access.access } : {}),
871
949
  requiresAuthentication: true,
872
950
  };
873
951
  }
@@ -878,35 +956,38 @@ function standardRouteCode(kind, device, workflowCode) {
878
956
  return `application.todo-center.${device}`;
879
957
  return `${kind.replaceAll('-', '.')}.${device}`;
880
958
  }
881
- function compileStandardRouteManifest(config, adminPages) {
959
+ function compileStandardRouteManifest(config) {
882
960
  const routes = [];
883
- for (const page of adminPages) {
884
- if (page.kind !== 'application-todo-center' &&
885
- page.kind !== 'workflow-work-center' &&
886
- page.kind !== 'workflow-task' &&
887
- page.kind !== 'workflow-instance' &&
888
- page.kind !== 'workflow-launch')
889
- continue;
890
- const workflowCode = page.workflowCode;
891
- const desktopPath = page.path;
892
- const mobilePath = page.kind === 'application-todo-center'
893
- ? '/m/todos'
894
- : page.kind === 'workflow-work-center'
895
- ? '/m/work-center'
896
- : page.kind === 'workflow-task'
897
- ? '/m/tasks/:taskId'
898
- : page.kind === 'workflow-instance'
899
- ? '/m/workflows/:instanceId'
900
- : '/m/workflows/:workflowCode/start';
901
- const desktop = compileStandardRouteManifestRoute(standardRouteCode(page.kind, 'desktop', workflowCode), desktopPath, 'admin', page);
902
- const mobile = compileStandardRouteManifestRoute(standardRouteCode(page.kind, 'mobile', workflowCode), mobilePath, 'user', page);
961
+ const addRoute = (code, kind, desktopPath, mobilePath, workflowCode, access) => {
903
962
  routes.push({
904
- code: page.code,
905
- kind: page.kind,
963
+ code,
964
+ kind,
906
965
  ...(workflowCode ? { workflowCode } : {}),
907
- desktop,
908
- mobile,
966
+ desktop: compileStandardRouteManifestRoute(standardRouteCode(kind, 'desktop', workflowCode), desktopPath, 'user', access),
967
+ mobile: compileStandardRouteManifestRoute(standardRouteCode(kind, 'mobile', workflowCode), mobilePath, 'user', access),
909
968
  });
969
+ };
970
+ if (config.frontend.user?.applicationTodoCenter === true) {
971
+ addRoute('application:todo-center', 'application-todo-center', '/todos', '/m/todos');
972
+ }
973
+ const workflows = compileWorkflows(config);
974
+ if (workflows.length > 0) {
975
+ addRoute('workflow:work-center', 'workflow-work-center', '/work-center', '/m/work-center');
976
+ addRoute('workflow:task', 'workflow-task', '/tasks/:taskId', '/m/tasks/:taskId');
977
+ addRoute('workflow:instance', 'workflow-instance', '/workflows/:instanceId', '/m/workflows/:instanceId');
978
+ }
979
+ for (const workflow of workflows) {
980
+ if (!['standalone', 'hidden-handoff'].includes(workflow.launch.mode))
981
+ continue;
982
+ const namedCapabilities = uniqueSorted([
983
+ workflow.launch.submission?.create?.requiredCapability,
984
+ workflow.launch.submission?.existing?.requiredCapability,
985
+ ].filter((capability) => Boolean(capability)));
986
+ addRoute(`workflow:${workflow.code}:launch`, 'workflow-launch', '/workflows/:workflowCode/start', '/m/workflows/:workflowCode/start', workflow.code, namedCapabilities.length === 1
987
+ ? { capability: namedCapabilities[0] }
988
+ : namedCapabilities.length > 1
989
+ ? { access: { anyOf: namedCapabilities } }
990
+ : undefined);
910
991
  }
911
992
  const normalizedRoutes = routes.sort((left, right) => compare(left.code, right.code));
912
993
  const staticRoute = (device) => {
@@ -975,14 +1056,8 @@ function validManifestStaticPath(path) {
975
1056
  }
976
1057
  function pageCodeForReference(reference) {
977
1058
  if (reference.kind === 'resource')
978
- return `resource:${reference.resourceCode}:list`;
979
- if (reference.kind === 'operation')
980
- return `operation:${reference.routeCode}`;
981
- if (reference.kind === 'application-todo-center')
982
- return 'application:todo-center';
983
- if (reference.kind === 'workflow-work-center')
984
- return 'workflow:work-center';
985
- return `workflow:${reference.workflowCode}:launch`;
1059
+ return `resource:${reference.resourceCode}${reference.viewCode ? `:view:${reference.viewCode}` : ""}:list`;
1060
+ return `operation:${reference.routeCode}`;
986
1061
  }
987
1062
  function compileAdminNavigation(config) {
988
1063
  return (config.frontend.admin?.navigation || [])
@@ -1061,8 +1136,12 @@ function compileCapabilities(configuration) {
1061
1136
  return [...capabilities.values()].sort((left, right) => compare(left.code, right.code));
1062
1137
  }
1063
1138
  function compileResources(config) {
1064
- return sorted((config.data?.resources || []).map(sourceResource => {
1065
- const resource = normalizeDataResource(sourceResource);
1139
+ const detailRouteByResourceCode = new Map((config.data.resourceDetailRoutes || []).map(declaration => [
1140
+ declaration.resourceCode,
1141
+ declaration,
1142
+ ]));
1143
+ return sorted(config.data.resources.map(resource => {
1144
+ const detailRouteCode = detailRouteByResourceCode.get(resource.code);
1066
1145
  return {
1067
1146
  code: resource.code,
1068
1147
  // The platform only receives immutable artifact bytes. Bind the
@@ -1073,6 +1152,14 @@ function compileResources(config) {
1073
1152
  code: field.code,
1074
1153
  type: field.type,
1075
1154
  })), field => field.code),
1155
+ ...(detailRouteCode
1156
+ ? {
1157
+ detailRouteCode: {
1158
+ desktop: detailRouteCode.desktop,
1159
+ mobile: detailRouteCode.mobile,
1160
+ },
1161
+ }
1162
+ : {}),
1076
1163
  };
1077
1164
  }), item => item.code);
1078
1165
  }
@@ -1090,6 +1177,9 @@ function compileOperations(config) {
1090
1177
  ...(operation.platformAccess
1091
1178
  ? {
1092
1179
  platformAccess: {
1180
+ ...(operation.platformAccess.roleAssertions ? {
1181
+ roleAssertions: { roleCodes: uniqueSorted(operation.platformAccess.roleAssertions.roleCodes) },
1182
+ } : {}),
1093
1183
  ...(operation.platformAccess.directory
1094
1184
  ? {
1095
1185
  directory: {
@@ -1164,10 +1254,8 @@ function compileWorkflows(config) {
1164
1254
  ? [...currentDefinition.definition.subject.summaryFields]
1165
1255
  : [],
1166
1256
  },
1167
- launch: {
1168
- mode: currentDefinition.launch?.mode || 'work-center-only',
1169
- },
1170
- ...(['standalone', 'hidden-handoff'].includes(currentDefinition.launch?.mode || 'work-center-only')
1257
+ launch: compileWorkflowLaunchContract(config, currentDefinition.launch),
1258
+ ...(['standalone', 'hidden-handoff'].includes(currentDefinition.launch?.mode || 'work-center-only') && !currentDefinition.launch?.submission
1171
1259
  ? {
1172
1260
  processOperationCode: `openxiangda.workflow.${code}.submit`,
1173
1261
  }
@@ -1426,6 +1514,9 @@ export function renderGeneratedContracts(config, contract = compileContractBundl
1426
1514
  .map(item => ` ${JSON.stringify(item.code)}: {\n` +
1427
1515
  ` code: ${JSON.stringify(item.code)},\n` +
1428
1516
  ` name: ${JSON.stringify(item.name)},\n` +
1517
+ (item.detailRouteCode
1518
+ ? ` detailRouteCode: ${JSON.stringify(item.detailRouteCode)},\n`
1519
+ : '') +
1429
1520
  ` capabilities: ${JSON.stringify(item.capabilities, null, 2)
1430
1521
  .split('\n')
1431
1522
  .map((line, index) => (index === 0 ? line : ` ${line}`))
@@ -1491,6 +1582,9 @@ export function renderGeneratedContracts(config, contract = compileContractBundl
1491
1582
  platformAuthManifest
1492
1583
  ? `export const platformAuthManifest = ${JSON.stringify(platformAuthManifest, null, 2)} as const;`
1493
1584
  : 'export const platformAuthManifest = null;',
1585
+ contract.adminAccess
1586
+ ? `export const adminAccess = ${JSON.stringify(contract.adminAccess, null, 2)} as const;`
1587
+ : 'export const adminAccess = undefined;',
1494
1588
  `export const routeManifest = ${JSON.stringify(contract.routeManifest, null, 2)} as const;`,
1495
1589
  `export const adminPages = ${JSON.stringify(contract.adminPages, null, 2)} as const;`,
1496
1590
  `export const adminNavigation = ${JSON.stringify(contract.adminNavigation, null, 2)} as const;`,
@@ -1526,6 +1620,7 @@ export function renderGeneratedContracts(config, contract = compileContractBundl
1526
1620
  'export type ApplicationAuthenticationSurface = (typeof authenticationSurfaces)[keyof typeof authenticationSurfaces];',
1527
1621
  'export type ApplicationAuthenticationSurfaceCode = ApplicationAuthenticationSurface["routeCode"];',
1528
1622
  'export type AnonymousPublicAccess = typeof anonymousPublicAccess;',
1623
+ 'export type AdminAccess = typeof adminAccess;',
1529
1624
  'export type RouteManifest = typeof routeManifest;',
1530
1625
  'export type AdminPage = (typeof adminPages)[number];',
1531
1626
  'export type AdminNavigationGroup = (typeof adminNavigation)[number];',
@@ -1611,6 +1706,7 @@ function normalizeResourceSurface(surface) {
1611
1706
  ? { requiredHint: field.requiredHint }
1612
1707
  : {}),
1613
1708
  ...(field.system !== undefined ? { system: field.system } : {}),
1709
+ ...(field.hidden !== undefined ? { hidden: field.hidden } : {}),
1614
1710
  readCapabilities: uniqueSorted(field.readCapabilities),
1615
1711
  createCapabilities: uniqueSorted(field.createCapabilities),
1616
1712
  updateCapabilities: uniqueSorted(field.updateCapabilities),
@@ -1642,6 +1738,7 @@ function normalizeResourceSurface(surface) {
1642
1738
  },
1643
1739
  ]));
1644
1740
  return {
1741
+ ...(surface.views?.length ? { views: sorted(surface.views, view => view.code) } : {}),
1645
1742
  ...(surface.mutationOwner !== undefined
1646
1743
  ? { mutationOwner: surface.mutationOwner }
1647
1744
  : {}),
@@ -1652,6 +1749,7 @@ function normalizeResourceSurface(surface) {
1652
1749
  ...(surface.list
1653
1750
  ? {
1654
1751
  list: {
1752
+ ...(surface.list.fieldOrder ? { fieldOrder: [...surface.list.fieldOrder] } : {}),
1655
1753
  ...(surface.list.defaultPageSize !== undefined
1656
1754
  ? { defaultPageSize: surface.list.defaultPageSize }
1657
1755
  : {}),
@@ -1686,7 +1784,7 @@ function defaultResourceSurface(resource) {
1686
1784
  label: field.code,
1687
1785
  type: field.type,
1688
1786
  widget: resolveDataFieldSurfaceWidget(field),
1689
- ...(field.nullable === false ? { requiredHint: true } : {}),
1787
+ ...(nativeFieldRequiresCreateInputV2(field) ? { requiredHint: true } : {}),
1690
1788
  ...(field.maxLength !== undefined ? { maxLength: field.maxLength } : {}),
1691
1789
  ...(field.precision !== undefined ? { precision: field.precision } : {}),
1692
1790
  ...(field.scale !== undefined ? { scale: field.scale } : {}),