openxiangda-contracts 2.63.0 → 2.69.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 (144) hide show
  1. package/dist/ai.d.ts +1 -1
  2. package/dist/ai.d.ts.map +1 -1
  3. package/dist/ai.js +4 -3
  4. package/dist/ai.js.map +1 -1
  5. package/dist/browser.d.ts +6 -0
  6. package/dist/browser.d.ts.map +1 -1
  7. package/dist/browser.js +6 -0
  8. package/dist/browser.js.map +1 -1
  9. package/dist/concurrency.d.ts +1 -1
  10. package/dist/concurrency.d.ts.map +1 -1
  11. package/dist/data-field-validation.js +7 -2
  12. package/dist/data-field-validation.js.map +1 -1
  13. package/dist/field-value-schemas.d.ts +1 -1
  14. package/dist/index.d.ts +10 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +10 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/native-compiler/actor-authority.d.ts +11 -0
  19. package/dist/native-compiler/actor-authority.d.ts.map +1 -0
  20. package/dist/native-compiler/actor-authority.js +56 -0
  21. package/dist/native-compiler/actor-authority.js.map +1 -0
  22. package/dist/native-compiler/artifact-capacity.d.ts +12 -0
  23. package/dist/native-compiler/artifact-capacity.d.ts.map +1 -0
  24. package/dist/native-compiler/artifact-capacity.js +34 -0
  25. package/dist/native-compiler/artifact-capacity.js.map +1 -0
  26. package/dist/native-compiler/authenticated-public-read.d.ts +18 -0
  27. package/dist/native-compiler/authenticated-public-read.d.ts.map +1 -0
  28. package/dist/native-compiler/authenticated-public-read.js +79 -0
  29. package/dist/native-compiler/authenticated-public-read.js.map +1 -0
  30. package/dist/native-compiler/compiler.d.ts +2 -21
  31. package/dist/native-compiler/compiler.d.ts.map +1 -1
  32. package/dist/native-compiler/compiler.js +203 -46
  33. package/dist/native-compiler/compiler.js.map +1 -1
  34. package/dist/native-compiler/data-business-command.d.ts +3 -1
  35. package/dist/native-compiler/data-business-command.d.ts.map +1 -1
  36. package/dist/native-compiler/data-business-command.js.map +1 -1
  37. package/dist/native-compiler/data-capacity.d.ts +14 -3
  38. package/dist/native-compiler/data-capacity.d.ts.map +1 -1
  39. package/dist/native-compiler/data-capacity.js +11 -3
  40. package/dist/native-compiler/data-capacity.js.map +1 -1
  41. package/dist/native-compiler/data-field.d.ts +22 -0
  42. package/dist/native-compiler/data-field.d.ts.map +1 -1
  43. package/dist/native-compiler/data-field.js +19 -2
  44. package/dist/native-compiler/data-field.js.map +1 -1
  45. package/dist/native-compiler/date-event-projection.d.ts +13 -0
  46. package/dist/native-compiler/date-event-projection.d.ts.map +1 -0
  47. package/dist/native-compiler/date-event-projection.js +71 -0
  48. package/dist/native-compiler/date-event-projection.js.map +1 -0
  49. package/dist/native-compiler/declaration-capacity.d.ts +60 -0
  50. package/dist/native-compiler/declaration-capacity.d.ts.map +1 -0
  51. package/dist/native-compiler/declaration-capacity.js +32 -0
  52. package/dist/native-compiler/declaration-capacity.js.map +1 -0
  53. package/dist/native-compiler/development-backend.d.ts +84 -0
  54. package/dist/native-compiler/development-backend.d.ts.map +1 -0
  55. package/dist/native-compiler/development-backend.js +77 -0
  56. package/dist/native-compiler/development-backend.js.map +1 -0
  57. package/dist/native-compiler/development-configuration.d.ts +65 -0
  58. package/dist/native-compiler/development-configuration.d.ts.map +1 -0
  59. package/dist/native-compiler/development-configuration.js +143 -0
  60. package/dist/native-compiler/development-configuration.js.map +1 -0
  61. package/dist/native-compiler/index.cjs +6 -6
  62. package/dist/native-compiler/index.d.ts +13 -0
  63. package/dist/native-compiler/index.d.ts.map +1 -1
  64. package/dist/native-compiler/index.js +6 -6
  65. package/dist/native-compiler/record-set-guard.d.ts +5 -0
  66. package/dist/native-compiler/record-set-guard.d.ts.map +1 -0
  67. package/dist/native-compiler/record-set-guard.js +27 -0
  68. package/dist/native-compiler/record-set-guard.js.map +1 -0
  69. package/dist/native-compiler/user-candidates.d.ts +9 -1
  70. package/dist/native-compiler/user-candidates.d.ts.map +1 -1
  71. package/dist/native-compiler/user-candidates.js +14 -1
  72. package/dist/native-compiler/user-candidates.js.map +1 -1
  73. package/dist/native-compiler/workflow-approved-delegation.d.ts +39 -0
  74. package/dist/native-compiler/workflow-approved-delegation.d.ts.map +1 -0
  75. package/dist/native-compiler/workflow-approved-delegation.js +94 -0
  76. package/dist/native-compiler/workflow-approved-delegation.js.map +1 -0
  77. package/dist/native-compiler/workflow-business-command.d.ts +2 -2
  78. package/dist/native-compiler/workflow-business-command.d.ts.map +1 -1
  79. package/dist/native-compiler/workflow-business-command.js +8 -4
  80. package/dist/native-compiler/workflow-business-command.js.map +1 -1
  81. package/dist/native-compiler/workflow-completion-deadline.d.ts +41 -0
  82. package/dist/native-compiler/workflow-completion-deadline.d.ts.map +1 -0
  83. package/dist/native-compiler/workflow-completion-deadline.js +40 -0
  84. package/dist/native-compiler/workflow-completion-deadline.js.map +1 -0
  85. package/dist/native-compiler/workflow-correction.d.ts +16 -0
  86. package/dist/native-compiler/workflow-correction.d.ts.map +1 -0
  87. package/dist/native-compiler/workflow-correction.js +42 -0
  88. package/dist/native-compiler/workflow-correction.js.map +1 -0
  89. package/dist/native-compiler/workflow-delegation-administration.d.ts +73 -0
  90. package/dist/native-compiler/workflow-delegation-administration.d.ts.map +1 -1
  91. package/dist/native-compiler/workflow-delegation-administration.js +6 -4
  92. package/dist/native-compiler/workflow-delegation-administration.js.map +1 -1
  93. package/dist/native-compiler/workflow-graph.d.ts +13 -2
  94. package/dist/native-compiler/workflow-graph.d.ts.map +1 -1
  95. package/dist/native-compiler/workflow-graph.js +20 -4
  96. package/dist/native-compiler/workflow-graph.js.map +1 -1
  97. package/dist/native-compiler/workflow-initiator-approval.d.ts +5 -1
  98. package/dist/native-compiler/workflow-initiator-approval.d.ts.map +1 -1
  99. package/dist/native-compiler/workflow-initiator-approval.js +18 -3
  100. package/dist/native-compiler/workflow-initiator-approval.js.map +1 -1
  101. package/dist/native-compiler/workflow-native-stage.d.ts +69 -0
  102. package/dist/native-compiler/workflow-native-stage.d.ts.map +1 -0
  103. package/dist/native-compiler/workflow-native-stage.js +42 -0
  104. package/dist/native-compiler/workflow-native-stage.js.map +1 -0
  105. package/dist/native-compiler/workflow-node-administration.d.ts +5 -1
  106. package/dist/native-compiler/workflow-node-administration.d.ts.map +1 -1
  107. package/dist/native-compiler/workflow-node-administration.js +17 -4
  108. package/dist/native-compiler/workflow-node-administration.js.map +1 -1
  109. package/dist/native-compiler/workflow-owned-subject.d.ts +60 -0
  110. package/dist/native-compiler/workflow-owned-subject.d.ts.map +1 -0
  111. package/dist/native-compiler/workflow-owned-subject.js +69 -0
  112. package/dist/native-compiler/workflow-owned-subject.js.map +1 -0
  113. package/dist/native-compiler/workflow-role-union.d.ts +31 -0
  114. package/dist/native-compiler/workflow-role-union.d.ts.map +1 -0
  115. package/dist/native-compiler/workflow-role-union.js +38 -0
  116. package/dist/native-compiler/workflow-role-union.js.map +1 -0
  117. package/dist/native-compiler/workflow-task-page.d.ts +10 -3
  118. package/dist/native-compiler/workflow-task-page.d.ts.map +1 -1
  119. package/dist/native-compiler/workflow-task-page.js +5 -3
  120. package/dist/native-compiler/workflow-task-page.js.map +1 -1
  121. package/dist/native-version.d.ts +20 -0
  122. package/dist/native-version.d.ts.map +1 -1
  123. package/dist/native-version.js +20 -0
  124. package/dist/native-version.js.map +1 -1
  125. package/dist/native.d.ts +9 -0
  126. package/dist/native.d.ts.map +1 -1
  127. package/dist/native.js.map +1 -1
  128. package/dist/references.d.ts +2 -0
  129. package/dist/references.d.ts.map +1 -1
  130. package/dist/schemas.d.ts +1708 -80
  131. package/dist/schemas.d.ts.map +1 -1
  132. package/dist/schemas.js +147 -27
  133. package/dist/schemas.js.map +1 -1
  134. package/dist/surface.d.ts +1 -0
  135. package/dist/surface.d.ts.map +1 -1
  136. package/dist/surface.js.map +1 -1
  137. package/dist/types.d.ts +87 -7
  138. package/dist/types.d.ts.map +1 -1
  139. package/dist/types.js +2 -2
  140. package/dist/types.js.map +1 -1
  141. package/dist/validation.d.ts.map +1 -1
  142. package/dist/validation.js +26 -0
  143. package/dist/validation.js.map +1 -1
  144. package/package.json +1 -1
@@ -1,4 +1,6 @@
1
- import { validateUserCandidateReferences, validateWorkflowUserCandidateBindings, UserCandidateContractError } from './user-candidates.js';
1
+ import { assertWorkflowNativeStagePolicy } from './workflow-native-stage.js';
2
+ import { validateAuthenticatedPublicRead, AuthenticatedPublicReadContractError } from './authenticated-public-read.js';
3
+ import { validateUserCandidateReferences, validateWorkflowUserCandidateBindings, UserCandidateContractError, requiresUserCandidateLaunchScope } from './user-candidates.js';
2
4
  import { NativeUniqueKeyContractError, parseNativeUniqueKeys, nativeUniqueKeysContractVersion } from './unique-keys.js';
3
5
  import { validateManagedConcurrency, ManagedConcurrencyContractError } from './managed-concurrency.js';
4
6
  import { normalizeDecimalReservationEventDeclaration, decimalReservationEventContext } from './decimal-reservation.js';
@@ -12,16 +14,23 @@ import { validateWorkflowAdministration } from './workflow-node-administration.j
12
14
  import { validateWorkflowAutomaticCc } from './workflow-automatic-cc.js';
13
15
  import { validateWorkflowApprovalEmptyPolicy, workflowBindingAllowsEmptyUsers } from './workflow-approval-empty.js';
14
16
  import { validateWorkflowInitiatorApprovalPolicy } from './workflow-initiator-approval.js';
17
+ import { validateWorkflowCompletionDeadlines } from './workflow-completion-deadline.js';
15
18
  import { validateWorkflowRejectionNotification } from './workflow-rejection-notification.js';
16
19
  import { validateWorkflowCommandHandlers } from './workflow-business-command.js';
17
20
  import { validateWorkflowLaunchPreflight } from './workflow-launch-preflight.js';
21
+ import { validateWorkflowApprovedDelegation } from './workflow-approved-delegation.js';
22
+ import { validateWorkflowCorrections } from './workflow-correction.js';
23
+ import { dateEventProjectionFieldsV2 } from './date-event-projection.js';
18
24
  import { validateWorkflowTaskPages } from './workflow-task-page.js';
25
+ import { requiresExtendedOwnedSubtableCapacity } from './data-capacity.js';
19
26
  import { validateWorkflowBusinessSteps, compileWorkflowBusinessStepHandlers, validateWorkflowBusinessStepSubscriptions, WORKFLOW_BUSINESS_STEP_EVENT, WORKFLOW_BUSINESS_STEP_EVENTS } from './workflow-business-step.js';
20
27
  import { validateWorkflowAssignmentRoutingBindings, validateWorkflowAssignmentRoutingPolicy } from './workflow-assignment-routing.js';
28
+ import { requiresWorkflowRoleUnion, validateWorkflowRoleUnion } from './workflow-role-union.js';
29
+ import { assertWorkflowOwnedSubjectCreate, assertWorkflowOwnedSubjectCreateResources } from './workflow-owned-subject.js';
21
30
  import * as crypto from 'crypto';
22
31
  import { OPENXIANGDA_COMPILER_CONTRACT_VERSION as OPENXIANGDA_V2_COMPILER_CONTRACT_VERSION, PLATFORM_CAPABILITY_CONTRACT_VERSIONS as OPENXIANGDA_V2_PLATFORM_CAPABILITY_CONTRACT_VERSIONS, } from '../native-version.js';
23
32
  import { canonicalJson, sha256Digest as sha256Canonical } from '../canonical.js';
24
- import { NativeDataFieldContractV2Error, nativeFieldRequiresCreateInputV2, parseNativeDataFieldsV2, parseNativeDataResourceInvariantsV2, validateNativeDataResourceReferencesV2, } from './data-field.js';
33
+ import { NativeDataFieldContractV2Error, nativeFieldRequiresCreateInputV2, requiresDateTimeMinutePrecision, requiresOwnedSubtableMinimumRows, parseNativeDataFieldsV2, parseNativeDataResourceInvariantsV2, validateNativeDataResourceReferencesV2, } from './data-field.js';
25
34
  import { validateNativeDataResourceSurfaceV2 } from './data-surface.js';
26
35
  import { nativeFieldSupportsSortV2 } from './field-query-plan.js';
27
36
  import { nativeScopeSourceGrantPathSupportedV2, nativeScopeSourceSubjectPathSupportedV2, resolveNativeScopeSourceFieldPathV2, } from './scope-source-field-path.js';
@@ -92,11 +101,10 @@ const WORKFLOW_OPERATIONS = new Set([
92
101
  'admin_override',
93
102
  'retry_resolution',
94
103
  ]);
95
- const MAX_CONFIG_BYTES = 4 * 1024 * 1024;
96
- const MAX_CONTRACT_BYTES = 8 * 1024 * 1024;
97
- const MAX_DEPTH = 40;
98
- const MAX_NODES = 100000;
99
- const MAX_STRING_BYTES = 1024 * 1024;
104
+ const MAX_CONFIG_BYTES = NATIVE_ARTIFACT_CAPACITY_V2.configBytes;
105
+ const MAX_CONTRACT_BYTES = NATIVE_ARTIFACT_CAPACITY_V2.contractBytes;
106
+ const MAX_DEPTH = NATIVE_ARTIFACT_CAPACITY_V2.depth;
107
+ const MAX_STRING_BYTES = NATIVE_ARTIFACT_CAPACITY_V2.stringBytes;
100
108
  const EVENT_HANDLER_MANIFEST_SCHEMA = 'openxiangda.event-handler-manifest/v2';
101
109
  const ROUTE_MANIFEST_SCHEMA = 'openxiangda.application-route-manifest/v3';
102
110
  const EVENT_CAPTURE_FIELD_LIMIT = 64;
@@ -108,22 +116,10 @@ const EVENT_AUTHORIZATION_SYSTEM_FIELDS = new Set([
108
116
  'created_at',
109
117
  'updated_at',
110
118
  ]);
111
- export const NATIVE_CONTRACT_CAPACITY_V2 = Object.freeze({
112
- perspectives: 100,
113
- resources: 100,
114
- subjectReadSurfaces: 50,
115
- capabilities: 2000,
116
- operations: 500,
117
- eventConsumers: 100,
118
- eventProducers: 1000,
119
- eventSchemas: 100,
120
- eventTypes: 500,
121
- workflows: 100,
122
- routes: 500,
123
- adminPages: 500,
124
- adminNavigationGroups: 100,
125
- adminNavigationItems: 500,
126
- });
119
+ export { NATIVE_CONTRACT_CAPACITY_V2, requiresExtendedDeclarationCapacity, generatedCrudCapabilityOperations } from './declaration-capacity.js';
120
+ import { NATIVE_CONTRACT_CAPACITY_V2, requiresExtendedDeclarationCapacity } from './declaration-capacity.js';
121
+ export { NATIVE_ARTIFACT_CAPACITY_V2, requiresExtendedArtifactCapacity } from './artifact-capacity.js';
122
+ import { NATIVE_ARTIFACT_CAPACITY_V2, requiresExtendedArtifactCapacity } from './artifact-capacity.js';
127
123
  export const DATA_EVENT_TYPES_V2 = [
128
124
  'openxiangda.data.record.created.v2',
129
125
  'openxiangda.data.record.updated.v2',
@@ -197,8 +193,13 @@ export function compileNativeApplicationConfiguration(input) {
197
193
  verifyArtifactDigest(contractSource, expectedContractDigest, '/contractBytes', 'NATIVE_CONTRACT_DIGEST_MISMATCH');
198
194
  const parsedConfig = parseCanonicalArtifact(configSource, '/configBytes', 'NATIVE_CONFIG_JSON_INVALID', 'NATIVE_CONFIG_NOT_CANONICAL');
199
195
  const parsedContract = parseCanonicalArtifact(contractSource, '/contractBytes', 'NATIVE_CONTRACT_JSON_INVALID', 'NATIVE_CONTRACT_NOT_CANONICAL');
200
- inspectJsonBudget(parsedConfig, '/config');
201
- inspectJsonBudget(parsedContract, '/contracts');
196
+ // First enforce all global safety bounds before deriving an extension from
197
+ // untrusted input. The sealed configuration alone selects the larger budget.
198
+ inspectJsonBudget(parsedConfig, '/config', NATIVE_ARTIFACT_CAPACITY_V2.extendedNodes);
199
+ inspectJsonBudget(parsedContract, '/contracts', NATIVE_ARTIFACT_CAPACITY_V2.extendedNodes);
200
+ if (!requiresExtendedArtifactCapacity(parsedConfig)) {
201
+ inspectJsonBudget(parsedContract, '/contracts', NATIVE_ARTIFACT_CAPACITY_V2.legacyNodes);
202
+ }
202
203
  const config = parsedConfig;
203
204
  const contract = parsedContract;
204
205
  validateConfigurationEnvelope(config, appCode);
@@ -287,6 +288,12 @@ export function compileNativeApplicationConfiguration(input) {
287
288
  * Derives the platform capability usage closure from the normalized, sealed
288
289
  * ConfigurationBundleV3. Manifest declarations are deliberately not an input.
289
290
  */
291
+ function workflowUsesOwnedInitialFacts(definition, resources) {
292
+ const subject = definition.subject;
293
+ const resource = resources.find(value => value.code === subject?.resourceCode);
294
+ const projected = new Set(Object.values(subject?.factProjection || {}));
295
+ return Boolean(resource?.schema?.fields?.some((field) => field.type === 'subtable' && projected.has(field.code)));
296
+ }
290
297
  export function compileRequiredPlatformCapabilitiesV3(config) {
291
298
  const roles = config.authz.roles;
292
299
  const resources = config.data.resources;
@@ -374,6 +381,17 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
374
381
  },
375
382
  };
376
383
  const usages = [
384
+ ...(requiresExtendedArtifactCapacity(config)
385
+ ? [{ code: 'application.extended-artifact-capacity',
386
+ declaration: NATIVE_ARTIFACT_CAPACITY_V2 }] : []),
387
+ ...(requiresExtendedDeclarationCapacity(config)
388
+ ? [{ code: 'application.extended-declaration-capacity', declaration: {
389
+ resources: config.data.resources.length, policies: config.authz.dataPolicies.length,
390
+ roles: config.authz.roles.length, capabilities: config.authz.capabilities.length,
391
+ definitions: config.workflows.definitions.length, bindings: config.workflows.bindings.length,
392
+ activations: config.workflows.activations.length, routes: config.frontend.routes.length,
393
+ navigationItems: config.frontend.admin.navigation.reduce((count, group) => count + group.items.length, 0),
394
+ } }] : []),
377
395
  ...(resources.some(resource => resource.uniqueKeys?.length)
378
396
  ? [{ code: 'data.unique-keys', declaration: resources.filter(resource => resource.uniqueKeys?.length)
379
397
  .map(resource => ({ code: resource.code, uniqueKeys: resource.uniqueKeys })) }] : []),
@@ -387,6 +405,17 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
387
405
  ...(operations.some(operation => operation.platformAccess?.dataCommands)
388
406
  ? [{ code: 'data.business-commands', declaration: operations.filter(operation => operation.platformAccess?.dataCommands) }]
389
407
  : []),
408
+ ...(config.authz.dataPolicies.some((policy) => policy.publicRead)
409
+ ? [{ code: 'data.authenticated-public-projection',
410
+ declaration: config.authz.dataPolicies.filter((policy) => policy.publicRead) }]
411
+ : []),
412
+ ...(operations.some(operation => operation.platformAccess?.roleAssertions?.actorAuthority)
413
+ ? [{ code: 'data.transaction-actor-authority',
414
+ declaration: operations.filter(operation => operation.platformAccess?.roleAssertions?.actorAuthority) }]
415
+ : []),
416
+ ...(operations.some(operation => operation.platformAccess?.workflowStage)
417
+ ? [{ code: 'workflow.native-stage-guard', declaration: operations.filter(operation => operation.platformAccess?.workflowStage) }]
418
+ : []),
390
419
  ...(operations.some(operation => operation.platformAccess?.recordEdit)
391
420
  ? [{ code: 'data.record-edit', declaration: operations.filter(operation => operation.platformAccess?.recordEdit) }]
392
421
  : []),
@@ -514,6 +543,9 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
514
543
  ...(config.events.capturePolicies?.length
515
544
  ? [{ code: 'events.capture-policy', declaration: config.events.capturePolicies }]
516
545
  : []),
546
+ ...(config.events.dateTriggers.some((trigger) => dateEventProjectionFieldsV2(trigger.resourceCode, config.events.subscriptions.filter((subscription) => subscription.eventTypes.includes(trigger.eventType)), config.data.resources.find((resource) => resource.code === trigger.resourceCode)).length > 0)
547
+ ? [{ code: 'events.date-source-projection', declaration: { triggers: config.events.dateTriggers, subscriptions: config.events.subscriptions } }]
548
+ : []),
517
549
  ...(usesEvents
518
550
  ? [
519
551
  { code: 'events-v2', declaration: config.events },
@@ -523,9 +555,23 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
523
555
  },
524
556
  ]
525
557
  : []),
558
+ ...(requiresExtendedOwnedSubtableCapacity(config.data.resources)
559
+ ? [{ code: 'data.extended-owned-subtable-capacity', declaration: config.data.resources.filter((resource) => requiresExtendedOwnedSubtableCapacity([resource])) }] : []),
526
560
  ...(config.data.resources.some((resource) => resource.schema.fields.some((field) => field.userCandidates !== undefined))
527
561
  ? [{ code: 'data.user-candidates', declaration: config.data.resources.filter((resource) => resource.schema.fields.some((field) => field.userCandidates !== undefined)) }]
528
562
  : []),
563
+ ...(requiresWorkflowRoleUnion(config.workflows.bindings)
564
+ ? [{ code: 'workflow.role-union', declaration: config.workflows.bindings.filter((item) => Object.values(item.binding.bindings).some((entry) => entry.roleCodes !== undefined)) }] : []),
565
+ ...(config.backend.operations.some((item) => item.platformAccess?.ownedSubject)
566
+ ? [{ code: 'workflow.named-owned-create', declaration: config.backend.operations.filter((item) => item.platformAccess?.ownedSubject) }] : []),
567
+ ...(requiresUserCandidateLaunchScope(config.data.resources)
568
+ ? [{ code: 'data.user-candidate-launch-scope', declaration: config.data.resources.filter((resource) => requiresUserCandidateLaunchScope([resource])) }]
569
+ : []),
570
+ ...(requiresOwnedSubtableMinimumRows(config.data.resources)
571
+ ? [{ code: 'data.subtable-minimum-rows', declaration: config.data.resources.filter((resource) => requiresOwnedSubtableMinimumRows([resource])) }] : []),
572
+ ...(requiresDateTimeMinutePrecision(config.data.resources)
573
+ ? [{ code: 'data.datetime-minute-precision', declaration: config.data.resources.filter((resource) => requiresDateTimeMinutePrecision([resource])) }]
574
+ : []),
529
575
  ...(config.workflows.activations.length
530
576
  ? [
531
577
  {
@@ -559,10 +605,23 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
559
605
  definitions: config.workflows.definitions.filter((item) => item.definition.commandHandlers !== undefined),
560
606
  operations: operations.filter(operation => operation.platformAccess?.workflow?.businessCommands),
561
607
  } }] : []),
608
+ ...(config.workflows.definitions.some((item) => item.definition.commandHandlers?.resubmit) || operations.some(operation => operation.platformAccess?.workflow?.businessCommands?.includes('resubmit'))
609
+ ? [{ code: 'workflow.correction-business-command', declaration: {
610
+ definitions: config.workflows.definitions.filter((item) => item.definition.commandHandlers?.resubmit),
611
+ operations: operations.filter(operation => operation.platformAccess?.workflow?.businessCommands?.includes('resubmit')),
612
+ } }] : []),
562
613
  ...(config.workflows.definitions.some((item) => item.definition.launchPreflight !== undefined)
563
614
  ? [{ code: 'workflow.launch-preflight', declaration: config.workflows.definitions.filter((item) => item.definition.launchPreflight !== undefined) }] : []),
615
+ ...(config.workflows.definitions.some((item) => item.definition.approvedDelegation !== undefined)
616
+ ? [{ code: 'workflow.approved-delegation', declaration: config.workflows.definitions.filter((item) => item.definition.approvedDelegation !== undefined) }] : []),
617
+ ...(config.workflows.definitions.some((item) => workflowUsesOwnedInitialFacts(item.definition, config.data.resources))
618
+ ? [{ code: 'workflow.owned-initial-facts', declaration: config.workflows.definitions.filter((item) => workflowUsesOwnedInitialFacts(item.definition, config.data.resources)) }] : []),
619
+ ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'correction'))
620
+ ? [{ code: 'workflow.initiator-correction', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'correction')) }] : []),
564
621
  ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.operationPolicy?.reject?.commentRequired === false))
565
622
  ? [{ code: 'workflow.optional-rejection-comment', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.operationPolicy?.reject?.commentRequired === false)) }] : []),
623
+ ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => Object.values(node.operationPolicy || {}).some((policy) => policy.reasonRequired === false)))
624
+ ? [{ code: 'workflow.optional-operation-reason', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => Object.values(node.operationPolicy || {}).some((policy) => policy.reasonRequired === false))) }] : []),
566
625
  ...(namedInputSourceDefinitions.length
567
626
  ? [{ code: 'workflow.named-input-sources', declaration: namedInputSourceDefinitions }]
568
627
  : []),
@@ -572,6 +631,9 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
572
631
  ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'approval' && node.emptyPolicy !== undefined))
573
632
  ? [{ code: 'workflow.approval-empty-policy', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'approval' && node.emptyPolicy !== undefined)) }]
574
633
  : []),
634
+ ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.completionDeadline !== undefined))
635
+ ? [{ code: 'workflow.completion-deadline', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.completionDeadline !== undefined)) }]
636
+ : []),
575
637
  ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.initiatorApprovalPolicy !== undefined))
576
638
  ? [{ code: 'workflow.initiator-approval-policy', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.initiatorApprovalPolicy !== undefined)) }]
577
639
  : []),
@@ -854,13 +916,13 @@ function validateConfigurationEnvelope(config, appCode) {
854
916
  'dataPolicies',
855
917
  'authorizationTransitions',
856
918
  ], '/config/authz', ['authenticatedUserRoleCode']);
857
- boundedArray(authz.capabilities, '/config/authz/capabilities', 2000);
858
- boundedArray(authz.roles, '/config/authz/roles', 100);
919
+ boundedArray(authz.capabilities, '/config/authz/capabilities', NATIVE_CONTRACT_CAPACITY_V2.capabilities);
920
+ boundedArray(authz.roles, '/config/authz/roles', NATIVE_CONTRACT_CAPACITY_V2.roles);
859
921
  boundedArray(authz.scopeDimensions, '/config/authz/scopeDimensions', 100);
860
922
  boundedArray(authz.scopeSources, '/config/authz/scopeSources', 100);
861
923
  boundedArray(authz.roleMembershipSources, '/config/authz/roleMembershipSources', 100);
862
924
  boundedArray(authz.relationshipGrantSources, '/config/authz/relationshipGrantSources', 100);
863
- boundedArray(authz.dataPolicies, '/config/authz/dataPolicies', 100);
925
+ boundedArray(authz.dataPolicies, '/config/authz/dataPolicies', NATIVE_CONTRACT_CAPACITY_V2.dataPolicies);
864
926
  boundedArray(authz.authorizationTransitions, '/config/authz/authorizationTransitions', 100);
865
927
  const backend = object(config.backend, '/config/backend');
866
928
  exactKeys(backend, ['secrets', 'operations'], '/config/backend');
@@ -873,7 +935,7 @@ function validateConfigurationEnvelope(config, appCode) {
873
935
  'subjectReadSurfaces',
874
936
  'concurrency',
875
937
  ]);
876
- boundedArray(data.resources, '/config/data/resources', 100);
938
+ boundedArray(data.resources, '/config/data/resources', NATIVE_CONTRACT_CAPACITY_V2.resources);
877
939
  if (data.subjectReadSurfaces !== undefined) {
878
940
  boundedArray(data.subjectReadSurfaces, '/config/data/subjectReadSurfaces', NATIVE_CONTRACT_CAPACITY_V2.subjectReadSurfaces);
879
941
  }
@@ -894,14 +956,14 @@ function validateConfigurationEnvelope(config, appCode) {
894
956
  'providers',
895
957
  'editableParameters',
896
958
  ], '/config/workflows');
897
- boundedArray(workflows.definitions, '/config/workflows/definitions', 100);
898
- boundedArray(workflows.bindings, '/config/workflows/bindings', 100);
899
- boundedArray(workflows.activations, '/config/workflows/activations', 100);
959
+ boundedArray(workflows.definitions, '/config/workflows/definitions', NATIVE_CONTRACT_CAPACITY_V2.workflows);
960
+ boundedArray(workflows.bindings, '/config/workflows/bindings', NATIVE_CONTRACT_CAPACITY_V2.workflows);
961
+ boundedArray(workflows.activations, '/config/workflows/activations', NATIVE_CONTRACT_CAPACITY_V2.workflows);
900
962
  boundedArray(workflows.providers, '/config/workflows/providers', 100);
901
963
  boundedArray(workflows.editableParameters, '/config/workflows/editableParameters', 100);
902
964
  const frontend = object(config.frontend, '/config/frontend');
903
965
  exactKeys(frontend, ['routes', 'user', 'admin', 'devicePolicy'], '/config/frontend', ['authentication', 'publicAccess']);
904
- boundedArray(frontend.routes, '/config/frontend/routes', 500);
966
+ boundedArray(frontend.routes, '/config/frontend/routes', NATIVE_CONTRACT_CAPACITY_V2.routes);
905
967
  const user = object(frontend.user, '/config/frontend/user');
906
968
  exactKeys(user, ['applicationTodoCenter'], '/config/frontend/user');
907
969
  boolean(user.applicationTodoCenter, '/config/frontend/user/applicationTodoCenter');
@@ -1609,6 +1671,12 @@ function compileOperations(config) {
1609
1671
  const code = stableCode(operation.code, `${pointer}/code`);
1610
1672
  validateBackendOperationAi(operation.ai, pointer, code, method, declaredResourceCodes);
1611
1673
  const platformAccess = validateOperationPlatformAccess(operation.platformAccess, `${pointer}/platformAccess`, declaredResources, declaredResourceFields, declaredWorkflowCodes, new Set(config.authz.roles.map((role) => String(role.code))));
1674
+ if (platformAccess?.workflowStage) {
1675
+ const stage = platformAccess.workflowStage;
1676
+ const workflow = config.workflows.definitions.find((entry) => entry.definition.code === stage.workflowCode);
1677
+ if (!workflow || workflow.definition.subject?.resourceCode !== stage.resourceCode || stage.runningNodeIds.some((id) => !workflow.definition.nodes[id]))
1678
+ fail('NATIVE_WORKFLOW_STAGE_NODE_INVALID', `${pointer}/platformAccess/workflowStage/runningNodeIds`);
1679
+ }
1612
1680
  if (platformAccess?.recordEdit) {
1613
1681
  const owner = config.data.resources.find((item) => item.code === platformAccess.recordEdit.resourceCode);
1614
1682
  if (owner?.surface?.mutationOwner !== 'action')
@@ -1814,7 +1882,7 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1814
1882
  if (value === undefined)
1815
1883
  return null;
1816
1884
  const access = object(value, pointer);
1817
- exactKeys(access, ['directory', 'managedFiles', 'managedFileCopies', 'notification', 'workflow', 'roleAssertions', 'decimalReservation', 'dataCommands', 'recordEdit'], pointer, true);
1885
+ exactKeys(access, ['directory', 'managedFiles', 'managedFileCopies', 'notification', 'workflow', 'roleAssertions', 'decimalReservation', 'dataCommands', 'recordEdit', 'ownedSubject', 'workflowStage'], pointer, true);
1818
1886
  if (Object.keys(access).length === 0) {
1819
1887
  fail('NATIVE_OPERATION_PLATFORM_ACCESS_EMPTY', pointer);
1820
1888
  }
@@ -1825,6 +1893,19 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1825
1893
  equal(commands.mode, 'recoverable-native', `${pointer}/dataCommands/mode`);
1826
1894
  result.dataCommands = { mode: 'recoverable-native' };
1827
1895
  }
1896
+ if (access.workflowStage !== undefined) {
1897
+ try {
1898
+ assertWorkflowNativeStagePolicy(access.workflowStage);
1899
+ }
1900
+ catch {
1901
+ fail('NATIVE_WORKFLOW_STAGE_DECLARATION_INVALID', `${pointer}/workflowStage`);
1902
+ }
1903
+ const stage = access.workflowStage;
1904
+ if (!result.dataCommands || access.decimalReservation !== undefined || !declaredResources.has(stage.resourceCode) ||
1905
+ !declaredWorkflowCodes.has(stage.workflowCode) || !access.workflow?.codes?.includes(stage.workflowCode))
1906
+ fail('NATIVE_WORKFLOW_STAGE_DECLARATION_INVALID', `${pointer}/workflowStage`);
1907
+ result.workflowStage = { ...stage, runningNodeIds: uniqueSorted([...stage.runningNodeIds]), allowedStatuses: uniqueSorted([...stage.allowedStatuses]) };
1908
+ }
1828
1909
  if (access.recordEdit !== undefined) {
1829
1910
  const entryPointer = `${pointer}/recordEdit`;
1830
1911
  const entry = object(access.recordEdit, entryPointer);
@@ -1843,7 +1924,9 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1843
1924
  }
1844
1925
  if (access.roleAssertions !== undefined) {
1845
1926
  const assertions = object(access.roleAssertions, `${pointer}/roleAssertions`);
1846
- exactKeys(assertions, ['roleCodes'], `${pointer}/roleAssertions`);
1927
+ exactKeys(assertions, ['roleCodes', 'actorAuthority'], `${pointer}/roleAssertions`, true);
1928
+ if (assertions.actorAuthority !== undefined)
1929
+ equal(assertions.actorAuthority, true, `${pointer}/roleAssertions/actorAuthority`);
1847
1930
  const codes = uniqueStrings(assertions.roleCodes, `${pointer}/roleAssertions/roleCodes`, 20);
1848
1931
  if (!codes.length)
1849
1932
  fail('NATIVE_OPERATION_ROLE_ASSERTIONS_REQUIRED', `${pointer}/roleAssertions/roleCodes`);
@@ -1851,7 +1934,8 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1851
1934
  if (!declaredRoleCodes.has(code))
1852
1935
  fail('NATIVE_ROLE_REFERENCE_MISSING', `${pointer}/roleAssertions/roleCodes/${index}`);
1853
1936
  });
1854
- result.roleAssertions = { roleCodes: uniqueSorted(codes) };
1937
+ result.roleAssertions = { roleCodes: uniqueSorted(codes),
1938
+ ...(assertions.actorAuthority === true ? { actorAuthority: true } : {}) };
1855
1939
  }
1856
1940
  if (access.directory !== undefined) {
1857
1941
  const directory = object(access.directory, `${pointer}/directory`);
@@ -1964,12 +2048,33 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1964
2048
  if (!codes.length || codes.some(code => !declaredWorkflowCodes.has(code))) {
1965
2049
  fail('NATIVE_OPERATION_WORKFLOW_CODE_INVALID', `${pointer}/workflow/codes`);
1966
2050
  }
1967
- const businessCommands = workflow.businessCommands === undefined ? undefined : uniqueStrings(workflow.businessCommands, `${pointer}/workflow/businessCommands`, 3);
1968
- if (businessCommands && (!businessCommands.length || businessCommands.some(command => !['approve', 'reject', 'withdraw'].includes(command)))) {
2051
+ const businessCommands = workflow.businessCommands === undefined ? undefined : uniqueStrings(workflow.businessCommands, `${pointer}/workflow/businessCommands`, 4);
2052
+ if (businessCommands && (!businessCommands.length || businessCommands.some(command => !['approve', 'reject', 'withdraw', 'resubmit'].includes(command)))) {
1969
2053
  fail('NATIVE_OPERATION_WORKFLOW_BUSINESS_COMMAND_INVALID', `${pointer}/workflow/businessCommands`);
1970
2054
  }
1971
2055
  result.workflow = { codes: uniqueSorted(codes), ...(businessCommands ? { businessCommands: uniqueSorted(businessCommands) } : {}) };
1972
2056
  }
2057
+ if (access.ownedSubject !== undefined) {
2058
+ const entryPointer = `${pointer}/ownedSubject`;
2059
+ const grant = access.ownedSubject;
2060
+ try {
2061
+ assertWorkflowOwnedSubjectCreate(grant);
2062
+ }
2063
+ catch {
2064
+ fail('NATIVE_WORKFLOW_OWNED_SUBJECT_INVALID', entryPointer);
2065
+ }
2066
+ if (!result.workflow || access.decimalReservation !== undefined)
2067
+ fail('NATIVE_WORKFLOW_OWNED_SUBJECT_INVALID', entryPointer);
2068
+ try {
2069
+ assertWorkflowOwnedSubjectCreateResources(grant, declaredResourceFields, result.managedFiles || []);
2070
+ }
2071
+ catch (error) {
2072
+ fail(`NATIVE_${error.message}`, entryPointer);
2073
+ }
2074
+ result.ownedSubject = { mode: 'create', resourceCode: grant.resourceCode, subtables: sorted(grant.subtables.map(table => ({
2075
+ fieldCode: table.fieldCode, fieldCodes: uniqueSorted([...table.fieldCodes]),
2076
+ })), table => table.fieldCode) };
2077
+ }
1973
2078
  if (access.decimalReservation !== undefined) {
1974
2079
  const reservationPointer = `${pointer}/decimalReservation`;
1975
2080
  const reservation = object(access.decimalReservation, reservationPointer);
@@ -2933,6 +3038,8 @@ function compileAdminNavigation(config) {
2933
3038
  }
2934
3039
  function validateAdminNavigationDeclaration(value, pointer) {
2935
3040
  const groups = boundedArray(value, pointer, NATIVE_CONTRACT_CAPACITY_V2.adminNavigationGroups);
3041
+ if (groups.reduce((count, group) => count + (Array.isArray(group?.items) ? group.items.length : 0), 0) > NATIVE_CONTRACT_CAPACITY_V2.adminNavigationItems)
3042
+ fail('NATIVE_ARRAY_LIMIT_EXCEEDED', pointer);
2936
3043
  let itemCount = 0;
2937
3044
  groups.forEach((rawGroup, groupIndex) => {
2938
3045
  const groupPointer = `${pointer}/${groupIndex}`;
@@ -3073,6 +3180,8 @@ function validateAdminPageContracts(value, pointer) {
3073
3180
  }
3074
3181
  function validateAdminNavigationContracts(value, pointer) {
3075
3182
  const groups = boundedArray(value, pointer, NATIVE_CONTRACT_CAPACITY_V2.adminNavigationGroups);
3183
+ if (groups.reduce((count, group) => count + (Array.isArray(group?.items) ? group.items.length : 0), 0) > NATIVE_CONTRACT_CAPACITY_V2.adminNavigationItems)
3184
+ fail('NATIVE_ARRAY_LIMIT_EXCEEDED', pointer);
3076
3185
  let itemCount = 0;
3077
3186
  groups.forEach((rawGroup, groupIndex) => {
3078
3187
  const groupPointer = `${pointer}/${groupIndex}`;
@@ -3594,6 +3703,9 @@ function compileWorkflowLaunchContract(config, definition, value, pointer, opera
3594
3703
  const fieldCodes = new Set();
3595
3704
  const sourceCounts = new Map();
3596
3705
  const normalizedInputs = {};
3706
+ const ownedSubject = operationContract.platformAccess?.ownedSubject;
3707
+ if (ownedSubject && (intentKind !== 'create' || ownedSubject.resourceCode !== subjectResourceCode))
3708
+ fail('NATIVE_WORKFLOW_OWNED_SUBJECT_LAUNCH_MISMATCH', intentPointer);
3597
3709
  for (const [inputCode, rawBinding] of inputEntries) {
3598
3710
  if (!PROPERTY_CODE_PATTERN.test(inputCode) ||
3599
3711
  !Object.prototype.hasOwnProperty.call(requestProperties, inputCode)) {
@@ -3620,6 +3732,9 @@ function compileWorkflowLaunchContract(config, definition, value, pointer, opera
3620
3732
  fail('NATIVE_WORKFLOW_NAMED_OPERATION_INPUT_INVALID', `${bindingPointer}/fieldCode`);
3621
3733
  }
3622
3734
  fieldCodes.add(fieldCode);
3735
+ const field = subjectResource.schema.fields.find((item) => item.code === fieldCode);
3736
+ if (field?.type === 'subtable' && (!ownedSubject || !ownedSubject.subtables.some((table) => table.fieldCode === fieldCode)))
3737
+ fail('NATIVE_WORKFLOW_OWNED_SUBJECT_LAUNCH_REQUIRED', bindingPointer);
3623
3738
  normalizedInputs[inputCode] = { source, fieldCode };
3624
3739
  }
3625
3740
  else {
@@ -4075,6 +4190,7 @@ function validateDataPolicyReferences(config) {
4075
4190
  'name',
4076
4191
  'resourceCode',
4077
4192
  'unrestrictedRoleCodes',
4193
+ 'publicRead',
4078
4194
  'operations',
4079
4195
  'matchMode',
4080
4196
  'rules',
@@ -4106,6 +4222,26 @@ function validateDataPolicyReferences(config) {
4106
4222
  if (hasReadExpression && !policyFields) {
4107
4223
  fail('NATIVE_DATA_POLICY_RESOURCE_MISSING', `${pointer}/resourceCode`);
4108
4224
  }
4225
+ if (policy.publicRead !== undefined) {
4226
+ try {
4227
+ const baseline = config.authz.roles.find((role) => role.code === authenticatedUserRoleCode);
4228
+ validateAuthenticatedPublicRead({ policy,
4229
+ resource: config.data.resources.find((resource) => resource.code === policyResourceCode),
4230
+ baselineRoleCode: authenticatedUserRoleCode,
4231
+ baselineCapabilities: baseline?.capabilities || [],
4232
+ baselineDeniedCapabilities: baseline?.deniedCapabilities || [], pointer });
4233
+ }
4234
+ catch (error) {
4235
+ if (!(error instanceof AuthenticatedPublicReadContractError))
4236
+ throw error;
4237
+ fail(error.code, error.pointer);
4238
+ }
4239
+ if (!(policy.unrestrictedRoleCodes || []).includes(authenticatedUserRoleCode))
4240
+ fail('AUTHENTICATED_PUBLIC_READ_CANONICAL_BASELINE_MISSING', `${pointer}/unrestrictedRoleCodes`);
4241
+ }
4242
+ else if (authenticatedUserRoleCode && (policy.unrestrictedRoleCodes || []).includes(authenticatedUserRoleCode)) {
4243
+ fail('NATIVE_DATA_POLICY_BASELINE_UNRESTRICTED', `${pointer}/unrestrictedRoleCodes`);
4244
+ }
4109
4245
  const unrestrictedRoleCodes = uniqueStrings(policy.unrestrictedRoleCodes || [], `${pointer}/unrestrictedRoleCodes`, 100);
4110
4246
  if (policy.unrestrictedRoleCodes !== undefined &&
4111
4247
  unrestrictedRoleCodes.length === 0) {
@@ -4482,6 +4618,9 @@ function validateWorkflowReferences(config) {
4482
4618
  const preflightErrors = validateWorkflowLaunchPreflight(definition);
4483
4619
  if (preflightErrors.length)
4484
4620
  fail(preflightErrors[0], `${pointer}/definition/launchPreflight`);
4621
+ const delegationErrors = validateWorkflowApprovedDelegation(definition);
4622
+ if (delegationErrors.length)
4623
+ fail(delegationErrors[0], `${pointer}/definition/approvedDelegation`);
4485
4624
  const businessCommandErrors = validateWorkflowCommandHandlers(definition, config.backend.operations);
4486
4625
  if (businessCommandErrors.length)
4487
4626
  fail(businessCommandErrors[0], `${pointer}/definition/commandHandlers`);
@@ -4723,7 +4862,7 @@ function validateWorkflowDefinition(definition, pointer) {
4723
4862
  'startAt',
4724
4863
  'inputSchema',
4725
4864
  'nodes',
4726
- ], pointer, ['organizationContext', 'instanceCommands', 'readability', 'taskPages', 'rejectionNotification', 'commandHandlers', 'launchPreflight']);
4865
+ ], pointer, ['organizationContext', 'instanceCommands', 'readability', 'taskPages', 'rejectionNotification', 'commandHandlers', 'launchPreflight', 'approvedDelegation']);
4727
4866
  equal(definition.schemaVersion, 'openxiangda.workflow-definition/v2', `${pointer}/schemaVersion`);
4728
4867
  workflowCodeValue(definition.code, `${pointer}/code`);
4729
4868
  requiredString(definition.title, `${pointer}/title`, 255);
@@ -4748,6 +4887,9 @@ function validateWorkflowDefinition(definition, pointer) {
4748
4887
  const initiatorPolicyErrors = validateWorkflowInitiatorApprovalPolicy(definition);
4749
4888
  if (initiatorPolicyErrors.length)
4750
4889
  fail(initiatorPolicyErrors[0], `${pointer}/nodes`);
4890
+ const deadlineErrors = validateWorkflowCompletionDeadlines(definition);
4891
+ if (deadlineErrors.length)
4892
+ fail(deadlineErrors[0], `${pointer}/nodes`);
4751
4893
  const rejectionNotificationErrors = validateWorkflowRejectionNotification(definition);
4752
4894
  if (rejectionNotificationErrors.length)
4753
4895
  fail(rejectionNotificationErrors[0], `${pointer}/rejectionNotification`);
@@ -4767,7 +4909,7 @@ function validateWorkflowDefinition(definition, pointer) {
4767
4909
  const nodePointer = `${pointer}/nodes/${nodeId}`;
4768
4910
  const node = object(rawNode, nodePointer);
4769
4911
  equal(node.id, nodeId, `${nodePointer}/id`);
4770
- if (!['approval', 'condition', 'end', 'cc', 'action'].includes(node.kind)) {
4912
+ if (!['approval', 'condition', 'end', 'cc', 'action', 'correction'].includes(node.kind)) {
4771
4913
  fail('NATIVE_WORKFLOW_NODE_KIND_INVALID', `${nodePointer}/kind`);
4772
4914
  }
4773
4915
  const targets = [];
@@ -4789,6 +4931,11 @@ function validateWorkflowDefinition(definition, pointer) {
4789
4931
  }
4790
4932
  }
4791
4933
  }
4934
+ else if (node.kind === 'correction') {
4935
+ const errors = validateWorkflowCorrections(definition);
4936
+ if (errors.length)
4937
+ fail(errors[0], nodePointer);
4938
+ }
4792
4939
  else if (node.kind === 'action') {
4793
4940
  targets.push(requiredString(node.next, `${nodePointer}/next`, 128));
4794
4941
  }
@@ -4833,6 +4980,13 @@ function validateWorkflowDefinition(definition, pointer) {
4833
4980
  visiting.delete(nodeId);
4834
4981
  };
4835
4982
  walk(startAt);
4983
+ for (const id of [...reachable]) {
4984
+ const current = nodes[id];
4985
+ if (current?.kind === 'approval')
4986
+ for (const target of current.returnTargets || [])
4987
+ if (nodes[target]?.kind === 'correction')
4988
+ reachable.add(target);
4989
+ }
4836
4990
  if (reachable.size !== nodeIds.length) {
4837
4991
  fail('NATIVE_WORKFLOW_NODE_UNREACHABLE', `${pointer}/nodes`);
4838
4992
  }
@@ -4879,6 +5033,9 @@ function validateWorkflowBinding(binding, pointer) {
4879
5033
  for (const [code, raw] of Object.entries(entries)) {
4880
5034
  workflowBindingCode(code, `${pointer}/bindings/${code}`);
4881
5035
  const entry = object(raw, `${pointer}/bindings/${code}`);
5036
+ const unionErrors = validateWorkflowRoleUnion(entry);
5037
+ if (unionErrors.length)
5038
+ fail(unionErrors[0], `${pointer}/bindings/${code}/roleCodes`);
4882
5039
  const provider = requiredString(entry.provider, `${pointer}/bindings/${code}/provider`, 64);
4883
5040
  if (![
4884
5041
  'fixed_users',
@@ -4903,7 +5060,7 @@ function validateWorkflowBinding(binding, pointer) {
4903
5060
  if (['input_users', 'form_field_users'].includes(provider)) {
4904
5061
  requiredString(entry.inputPath, `${pointer}/bindings/${code}/inputPath`, 255);
4905
5062
  }
4906
- if (['app_role', 'app_role_in_scope', 'initiator_select'].includes(provider)) {
5063
+ if (['app_role', 'app_role_in_scope', 'initiator_select'].includes(provider) && entry.roleCodes === undefined) {
4907
5064
  stableCode(entry.roleCode, `${pointer}/bindings/${code}/roleCode`);
4908
5065
  }
4909
5066
  if (provider === 'department_supervisor') {
@@ -4952,13 +5109,13 @@ function mergeUserSurfaceRoutes(expectedContract, contract, config) {
4952
5109
  const manifest = object(contract.routeManifest, '/contracts/routeManifest');
4953
5110
  const expectedManifest = object(expectedContract.routeManifest, '/contracts/routeManifest');
4954
5111
  const resources = new Map();
4955
- for (const raw of boundedArray(config.data?.resources, '/config/data/resources', 100)) {
5112
+ for (const raw of boundedArray(config.data?.resources, '/config/data/resources', NATIVE_CONTRACT_CAPACITY_V2.resources)) {
4956
5113
  const resource = object(raw, '/config/data/resources');
4957
5114
  resources.set(String(resource.code || ''), resource);
4958
5115
  }
4959
- const existingCodes = new Set(boundedArray(expectedManifest.routes, '/contracts/routeManifest/routes', 1000).map(entry => String(object(entry, '/contracts/routeManifest/routes').code || '')));
5116
+ const existingCodes = new Set(boundedArray(expectedManifest.routes, '/contracts/routeManifest/routes', NATIVE_CONTRACT_CAPACITY_V2.routes).map(entry => String(object(entry, '/contracts/routeManifest/routes').code || '')));
4960
5117
  const accepted = [];
4961
- for (const rawEntry of boundedArray(manifest.routes, '/contracts/routeManifest/routes', 1000)) {
5118
+ for (const rawEntry of boundedArray(manifest.routes, '/contracts/routeManifest/routes', NATIVE_CONTRACT_CAPACITY_V2.routes)) {
4962
5119
  const entry = object(rawEntry, '/contracts/routeManifest/routes');
4963
5120
  const kind = String(entry.kind || '');
4964
5121
  if (kind !== 'resource-records' && kind !== 'resource-submit')
@@ -5160,11 +5317,11 @@ function parseCanonicalArtifact(source, pointer, invalidCode, canonicalCode) {
5160
5317
  fail(canonicalCode, pointer);
5161
5318
  return result;
5162
5319
  }
5163
- function inspectJsonBudget(value, pointer) {
5320
+ function inspectJsonBudget(value, pointer, maxNodes) {
5164
5321
  let nodes = 0;
5165
5322
  const visit = (current, path, depth) => {
5166
5323
  nodes += 1;
5167
- if (nodes > MAX_NODES)
5324
+ if (nodes > maxNodes)
5168
5325
  fail('NATIVE_ARTIFACT_NODE_LIMIT_EXCEEDED', path);
5169
5326
  if (depth > MAX_DEPTH)
5170
5327
  fail('NATIVE_ARTIFACT_DEPTH_LIMIT_EXCEEDED', path);