openxiangda-contracts 2.63.0 → 2.70.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 (152) 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 +11 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +11 -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 +213 -49
  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 +25 -3
  38. package/dist/native-compiler/data-capacity.d.ts.map +1 -1
  39. package/dist/native-compiler/data-capacity.js +26 -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 +25 -4
  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 +14 -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-business-step.d.ts +4 -0
  82. package/dist/native-compiler/workflow-business-step.d.ts.map +1 -1
  83. package/dist/native-compiler/workflow-business-step.js +11 -6
  84. package/dist/native-compiler/workflow-business-step.js.map +1 -1
  85. package/dist/native-compiler/workflow-completion-deadline.d.ts +41 -0
  86. package/dist/native-compiler/workflow-completion-deadline.d.ts.map +1 -0
  87. package/dist/native-compiler/workflow-completion-deadline.js +40 -0
  88. package/dist/native-compiler/workflow-completion-deadline.js.map +1 -0
  89. package/dist/native-compiler/workflow-correction.d.ts +16 -0
  90. package/dist/native-compiler/workflow-correction.d.ts.map +1 -0
  91. package/dist/native-compiler/workflow-correction.js +42 -0
  92. package/dist/native-compiler/workflow-correction.js.map +1 -0
  93. package/dist/native-compiler/workflow-delegation-administration.d.ts +73 -0
  94. package/dist/native-compiler/workflow-delegation-administration.d.ts.map +1 -1
  95. package/dist/native-compiler/workflow-delegation-administration.js +6 -4
  96. package/dist/native-compiler/workflow-delegation-administration.js.map +1 -1
  97. package/dist/native-compiler/workflow-graph.d.ts +13 -2
  98. package/dist/native-compiler/workflow-graph.d.ts.map +1 -1
  99. package/dist/native-compiler/workflow-graph.js +20 -4
  100. package/dist/native-compiler/workflow-graph.js.map +1 -1
  101. package/dist/native-compiler/workflow-initiator-approval.d.ts +5 -1
  102. package/dist/native-compiler/workflow-initiator-approval.d.ts.map +1 -1
  103. package/dist/native-compiler/workflow-initiator-approval.js +18 -3
  104. package/dist/native-compiler/workflow-initiator-approval.js.map +1 -1
  105. package/dist/native-compiler/workflow-native-stage.d.ts +69 -0
  106. package/dist/native-compiler/workflow-native-stage.d.ts.map +1 -0
  107. package/dist/native-compiler/workflow-native-stage.js +42 -0
  108. package/dist/native-compiler/workflow-native-stage.js.map +1 -0
  109. package/dist/native-compiler/workflow-node-administration.d.ts +5 -1
  110. package/dist/native-compiler/workflow-node-administration.d.ts.map +1 -1
  111. package/dist/native-compiler/workflow-node-administration.js +17 -4
  112. package/dist/native-compiler/workflow-node-administration.js.map +1 -1
  113. package/dist/native-compiler/workflow-owned-subject.d.ts +60 -0
  114. package/dist/native-compiler/workflow-owned-subject.d.ts.map +1 -0
  115. package/dist/native-compiler/workflow-owned-subject.js +69 -0
  116. package/dist/native-compiler/workflow-owned-subject.js.map +1 -0
  117. package/dist/native-compiler/workflow-role-union.d.ts +31 -0
  118. package/dist/native-compiler/workflow-role-union.d.ts.map +1 -0
  119. package/dist/native-compiler/workflow-role-union.js +38 -0
  120. package/dist/native-compiler/workflow-role-union.js.map +1 -0
  121. package/dist/native-compiler/workflow-step-data.d.ts +28 -0
  122. package/dist/native-compiler/workflow-step-data.d.ts.map +1 -0
  123. package/dist/native-compiler/workflow-step-data.js +34 -0
  124. package/dist/native-compiler/workflow-step-data.js.map +1 -0
  125. package/dist/native-compiler/workflow-task-page.d.ts +11 -4
  126. package/dist/native-compiler/workflow-task-page.d.ts.map +1 -1
  127. package/dist/native-compiler/workflow-task-page.js +7 -5
  128. package/dist/native-compiler/workflow-task-page.js.map +1 -1
  129. package/dist/native-version.d.ts +22 -0
  130. package/dist/native-version.d.ts.map +1 -1
  131. package/dist/native-version.js +22 -0
  132. package/dist/native-version.js.map +1 -1
  133. package/dist/native.d.ts +9 -0
  134. package/dist/native.d.ts.map +1 -1
  135. package/dist/native.js.map +1 -1
  136. package/dist/references.d.ts +2 -0
  137. package/dist/references.d.ts.map +1 -1
  138. package/dist/schemas.d.ts +1838 -80
  139. package/dist/schemas.d.ts.map +1 -1
  140. package/dist/schemas.js +153 -30
  141. package/dist/schemas.js.map +1 -1
  142. package/dist/surface.d.ts +1 -0
  143. package/dist/surface.d.ts.map +1 -1
  144. package/dist/surface.js.map +1 -1
  145. package/dist/types.d.ts +89 -7
  146. package/dist/types.d.ts.map +1 -1
  147. package/dist/types.js +2 -2
  148. package/dist/types.js.map +1 -1
  149. package/dist/validation.d.ts.map +1 -1
  150. package/dist/validation.js +30 -1
  151. package/dist/validation.js.map +1 -1
  152. 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, requiresAggregateOwnedSubtableCapacity, dataOwnedRowLimit, isDataOwnedRowLimit } 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,25 @@ 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])) }] : []),
560
+ ...(requiresAggregateOwnedSubtableCapacity(config.data.resources)
561
+ ? [{ code: 'data.aggregate-owned-subtable-capacity', declaration: config.data.resources.filter((resource) => requiresAggregateOwnedSubtableCapacity([resource])) }] : []),
526
562
  ...(config.data.resources.some((resource) => resource.schema.fields.some((field) => field.userCandidates !== undefined))
527
563
  ? [{ code: 'data.user-candidates', declaration: config.data.resources.filter((resource) => resource.schema.fields.some((field) => field.userCandidates !== undefined)) }]
528
564
  : []),
565
+ ...(requiresWorkflowRoleUnion(config.workflows.bindings)
566
+ ? [{ code: 'workflow.role-union', declaration: config.workflows.bindings.filter((item) => Object.values(item.binding.bindings).some((entry) => entry.roleCodes !== undefined)) }] : []),
567
+ ...(config.backend.operations.some((item) => item.platformAccess?.ownedSubject)
568
+ ? [{ code: 'workflow.named-owned-create', declaration: config.backend.operations.filter((item) => item.platformAccess?.ownedSubject) }] : []),
569
+ ...(requiresUserCandidateLaunchScope(config.data.resources)
570
+ ? [{ code: 'data.user-candidate-launch-scope', declaration: config.data.resources.filter((resource) => requiresUserCandidateLaunchScope([resource])) }]
571
+ : []),
572
+ ...(requiresOwnedSubtableMinimumRows(config.data.resources)
573
+ ? [{ code: 'data.subtable-minimum-rows', declaration: config.data.resources.filter((resource) => requiresOwnedSubtableMinimumRows([resource])) }] : []),
574
+ ...(requiresDateTimeMinutePrecision(config.data.resources)
575
+ ? [{ code: 'data.datetime-minute-precision', declaration: config.data.resources.filter((resource) => requiresDateTimeMinutePrecision([resource])) }]
576
+ : []),
529
577
  ...(config.workflows.activations.length
530
578
  ? [
531
579
  {
@@ -559,10 +607,23 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
559
607
  definitions: config.workflows.definitions.filter((item) => item.definition.commandHandlers !== undefined),
560
608
  operations: operations.filter(operation => operation.platformAccess?.workflow?.businessCommands),
561
609
  } }] : []),
610
+ ...(config.workflows.definitions.some((item) => item.definition.commandHandlers?.resubmit) || operations.some(operation => operation.platformAccess?.workflow?.businessCommands?.includes('resubmit'))
611
+ ? [{ code: 'workflow.correction-business-command', declaration: {
612
+ definitions: config.workflows.definitions.filter((item) => item.definition.commandHandlers?.resubmit),
613
+ operations: operations.filter(operation => operation.platformAccess?.workflow?.businessCommands?.includes('resubmit')),
614
+ } }] : []),
562
615
  ...(config.workflows.definitions.some((item) => item.definition.launchPreflight !== undefined)
563
616
  ? [{ code: 'workflow.launch-preflight', declaration: config.workflows.definitions.filter((item) => item.definition.launchPreflight !== undefined) }] : []),
617
+ ...(config.workflows.definitions.some((item) => item.definition.approvedDelegation !== undefined)
618
+ ? [{ code: 'workflow.approved-delegation', declaration: config.workflows.definitions.filter((item) => item.definition.approvedDelegation !== undefined) }] : []),
619
+ ...(config.workflows.definitions.some((item) => workflowUsesOwnedInitialFacts(item.definition, config.data.resources))
620
+ ? [{ code: 'workflow.owned-initial-facts', declaration: config.workflows.definitions.filter((item) => workflowUsesOwnedInitialFacts(item.definition, config.data.resources)) }] : []),
621
+ ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'correction'))
622
+ ? [{ code: 'workflow.initiator-correction', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'correction')) }] : []),
564
623
  ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.operationPolicy?.reject?.commentRequired === false))
565
624
  ? [{ code: 'workflow.optional-rejection-comment', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.operationPolicy?.reject?.commentRequired === false)) }] : []),
625
+ ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => Object.values(node.operationPolicy || {}).some((policy) => policy.reasonRequired === false)))
626
+ ? [{ 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
627
  ...(namedInputSourceDefinitions.length
567
628
  ? [{ code: 'workflow.named-input-sources', declaration: namedInputSourceDefinitions }]
568
629
  : []),
@@ -572,6 +633,9 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
572
633
  ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'approval' && node.emptyPolicy !== undefined))
573
634
  ? [{ 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
635
  : []),
636
+ ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.completionDeadline !== undefined))
637
+ ? [{ code: 'workflow.completion-deadline', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.completionDeadline !== undefined)) }]
638
+ : []),
575
639
  ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.initiatorApprovalPolicy !== undefined))
576
640
  ? [{ code: 'workflow.initiator-approval-policy', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.initiatorApprovalPolicy !== undefined)) }]
577
641
  : []),
@@ -592,6 +656,9 @@ export function compileRequiredPlatformCapabilitiesV3(config) {
592
656
  ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'action'))
593
657
  ? [{ code: 'workflow.durable-business-step', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'action')) }]
594
658
  : []),
659
+ ...(config.workflows.definitions.some((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'action' && node.handler.dataTransaction))
660
+ ? [{ code: 'workflow.step-data-transaction', declaration: config.workflows.definitions.filter((item) => Object.values(item.definition.nodes).some((node) => node.kind === 'action' && node.handler.dataTransaction)) }]
661
+ : []),
595
662
  ...(config.workflows.bindings.some((item) => Object.values(item.binding.bindings).some((entry) => entry.routing !== undefined))
596
663
  ? [{ code: 'workflow.assignment-routing', declaration: config.workflows.bindings.filter((item) => Object.values(item.binding.bindings).some((entry) => entry.routing !== undefined)) }]
597
664
  : []),
@@ -854,13 +921,13 @@ function validateConfigurationEnvelope(config, appCode) {
854
921
  'dataPolicies',
855
922
  'authorizationTransitions',
856
923
  ], '/config/authz', ['authenticatedUserRoleCode']);
857
- boundedArray(authz.capabilities, '/config/authz/capabilities', 2000);
858
- boundedArray(authz.roles, '/config/authz/roles', 100);
924
+ boundedArray(authz.capabilities, '/config/authz/capabilities', NATIVE_CONTRACT_CAPACITY_V2.capabilities);
925
+ boundedArray(authz.roles, '/config/authz/roles', NATIVE_CONTRACT_CAPACITY_V2.roles);
859
926
  boundedArray(authz.scopeDimensions, '/config/authz/scopeDimensions', 100);
860
927
  boundedArray(authz.scopeSources, '/config/authz/scopeSources', 100);
861
928
  boundedArray(authz.roleMembershipSources, '/config/authz/roleMembershipSources', 100);
862
929
  boundedArray(authz.relationshipGrantSources, '/config/authz/relationshipGrantSources', 100);
863
- boundedArray(authz.dataPolicies, '/config/authz/dataPolicies', 100);
930
+ boundedArray(authz.dataPolicies, '/config/authz/dataPolicies', NATIVE_CONTRACT_CAPACITY_V2.dataPolicies);
864
931
  boundedArray(authz.authorizationTransitions, '/config/authz/authorizationTransitions', 100);
865
932
  const backend = object(config.backend, '/config/backend');
866
933
  exactKeys(backend, ['secrets', 'operations'], '/config/backend');
@@ -873,7 +940,7 @@ function validateConfigurationEnvelope(config, appCode) {
873
940
  'subjectReadSurfaces',
874
941
  'concurrency',
875
942
  ]);
876
- boundedArray(data.resources, '/config/data/resources', 100);
943
+ boundedArray(data.resources, '/config/data/resources', NATIVE_CONTRACT_CAPACITY_V2.resources);
877
944
  if (data.subjectReadSurfaces !== undefined) {
878
945
  boundedArray(data.subjectReadSurfaces, '/config/data/subjectReadSurfaces', NATIVE_CONTRACT_CAPACITY_V2.subjectReadSurfaces);
879
946
  }
@@ -894,14 +961,14 @@ function validateConfigurationEnvelope(config, appCode) {
894
961
  'providers',
895
962
  'editableParameters',
896
963
  ], '/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);
964
+ boundedArray(workflows.definitions, '/config/workflows/definitions', NATIVE_CONTRACT_CAPACITY_V2.workflows);
965
+ boundedArray(workflows.bindings, '/config/workflows/bindings', NATIVE_CONTRACT_CAPACITY_V2.workflows);
966
+ boundedArray(workflows.activations, '/config/workflows/activations', NATIVE_CONTRACT_CAPACITY_V2.workflows);
900
967
  boundedArray(workflows.providers, '/config/workflows/providers', 100);
901
968
  boundedArray(workflows.editableParameters, '/config/workflows/editableParameters', 100);
902
969
  const frontend = object(config.frontend, '/config/frontend');
903
970
  exactKeys(frontend, ['routes', 'user', 'admin', 'devicePolicy'], '/config/frontend', ['authentication', 'publicAccess']);
904
- boundedArray(frontend.routes, '/config/frontend/routes', 500);
971
+ boundedArray(frontend.routes, '/config/frontend/routes', NATIVE_CONTRACT_CAPACITY_V2.routes);
905
972
  const user = object(frontend.user, '/config/frontend/user');
906
973
  exactKeys(user, ['applicationTodoCenter'], '/config/frontend/user');
907
974
  boolean(user.applicationTodoCenter, '/config/frontend/user/applicationTodoCenter');
@@ -1609,6 +1676,12 @@ function compileOperations(config) {
1609
1676
  const code = stableCode(operation.code, `${pointer}/code`);
1610
1677
  validateBackendOperationAi(operation.ai, pointer, code, method, declaredResourceCodes);
1611
1678
  const platformAccess = validateOperationPlatformAccess(operation.platformAccess, `${pointer}/platformAccess`, declaredResources, declaredResourceFields, declaredWorkflowCodes, new Set(config.authz.roles.map((role) => String(role.code))));
1679
+ if (platformAccess?.workflowStage) {
1680
+ const stage = platformAccess.workflowStage;
1681
+ const workflow = config.workflows.definitions.find((entry) => entry.definition.code === stage.workflowCode);
1682
+ if (!workflow || workflow.definition.subject?.resourceCode !== stage.resourceCode || stage.runningNodeIds.some((id) => !workflow.definition.nodes[id]))
1683
+ fail('NATIVE_WORKFLOW_STAGE_NODE_INVALID', `${pointer}/platformAccess/workflowStage/runningNodeIds`);
1684
+ }
1612
1685
  if (platformAccess?.recordEdit) {
1613
1686
  const owner = config.data.resources.find((item) => item.code === platformAccess.recordEdit.resourceCode);
1614
1687
  if (owner?.surface?.mutationOwner !== 'action')
@@ -1814,7 +1887,7 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1814
1887
  if (value === undefined)
1815
1888
  return null;
1816
1889
  const access = object(value, pointer);
1817
- exactKeys(access, ['directory', 'managedFiles', 'managedFileCopies', 'notification', 'workflow', 'roleAssertions', 'decimalReservation', 'dataCommands', 'recordEdit'], pointer, true);
1890
+ exactKeys(access, ['directory', 'managedFiles', 'managedFileCopies', 'notification', 'workflow', 'roleAssertions', 'decimalReservation', 'dataCommands', 'recordEdit', 'ownedSubject', 'workflowStage'], pointer, true);
1818
1891
  if (Object.keys(access).length === 0) {
1819
1892
  fail('NATIVE_OPERATION_PLATFORM_ACCESS_EMPTY', pointer);
1820
1893
  }
@@ -1825,6 +1898,19 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1825
1898
  equal(commands.mode, 'recoverable-native', `${pointer}/dataCommands/mode`);
1826
1899
  result.dataCommands = { mode: 'recoverable-native' };
1827
1900
  }
1901
+ if (access.workflowStage !== undefined) {
1902
+ try {
1903
+ assertWorkflowNativeStagePolicy(access.workflowStage);
1904
+ }
1905
+ catch {
1906
+ fail('NATIVE_WORKFLOW_STAGE_DECLARATION_INVALID', `${pointer}/workflowStage`);
1907
+ }
1908
+ const stage = access.workflowStage;
1909
+ if (!result.dataCommands || access.decimalReservation !== undefined || !declaredResources.has(stage.resourceCode) ||
1910
+ !declaredWorkflowCodes.has(stage.workflowCode) || !access.workflow?.codes?.includes(stage.workflowCode))
1911
+ fail('NATIVE_WORKFLOW_STAGE_DECLARATION_INVALID', `${pointer}/workflowStage`);
1912
+ result.workflowStage = { ...stage, runningNodeIds: uniqueSorted([...stage.runningNodeIds]), allowedStatuses: uniqueSorted([...stage.allowedStatuses]) };
1913
+ }
1828
1914
  if (access.recordEdit !== undefined) {
1829
1915
  const entryPointer = `${pointer}/recordEdit`;
1830
1916
  const entry = object(access.recordEdit, entryPointer);
@@ -1843,7 +1929,9 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1843
1929
  }
1844
1930
  if (access.roleAssertions !== undefined) {
1845
1931
  const assertions = object(access.roleAssertions, `${pointer}/roleAssertions`);
1846
- exactKeys(assertions, ['roleCodes'], `${pointer}/roleAssertions`);
1932
+ exactKeys(assertions, ['roleCodes', 'actorAuthority'], `${pointer}/roleAssertions`, true);
1933
+ if (assertions.actorAuthority !== undefined)
1934
+ equal(assertions.actorAuthority, true, `${pointer}/roleAssertions/actorAuthority`);
1847
1935
  const codes = uniqueStrings(assertions.roleCodes, `${pointer}/roleAssertions/roleCodes`, 20);
1848
1936
  if (!codes.length)
1849
1937
  fail('NATIVE_OPERATION_ROLE_ASSERTIONS_REQUIRED', `${pointer}/roleAssertions/roleCodes`);
@@ -1851,7 +1939,8 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1851
1939
  if (!declaredRoleCodes.has(code))
1852
1940
  fail('NATIVE_ROLE_REFERENCE_MISSING', `${pointer}/roleAssertions/roleCodes/${index}`);
1853
1941
  });
1854
- result.roleAssertions = { roleCodes: uniqueSorted(codes) };
1942
+ result.roleAssertions = { roleCodes: uniqueSorted(codes),
1943
+ ...(assertions.actorAuthority === true ? { actorAuthority: true } : {}) };
1855
1944
  }
1856
1945
  if (access.directory !== undefined) {
1857
1946
  const directory = object(access.directory, `${pointer}/directory`);
@@ -1964,12 +2053,33 @@ function validateOperationPlatformAccess(value, pointer, declaredResources, decl
1964
2053
  if (!codes.length || codes.some(code => !declaredWorkflowCodes.has(code))) {
1965
2054
  fail('NATIVE_OPERATION_WORKFLOW_CODE_INVALID', `${pointer}/workflow/codes`);
1966
2055
  }
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)))) {
2056
+ const businessCommands = workflow.businessCommands === undefined ? undefined : uniqueStrings(workflow.businessCommands, `${pointer}/workflow/businessCommands`, 4);
2057
+ if (businessCommands && (!businessCommands.length || businessCommands.some(command => !['approve', 'reject', 'withdraw', 'resubmit'].includes(command)))) {
1969
2058
  fail('NATIVE_OPERATION_WORKFLOW_BUSINESS_COMMAND_INVALID', `${pointer}/workflow/businessCommands`);
1970
2059
  }
1971
2060
  result.workflow = { codes: uniqueSorted(codes), ...(businessCommands ? { businessCommands: uniqueSorted(businessCommands) } : {}) };
1972
2061
  }
2062
+ if (access.ownedSubject !== undefined) {
2063
+ const entryPointer = `${pointer}/ownedSubject`;
2064
+ const grant = access.ownedSubject;
2065
+ try {
2066
+ assertWorkflowOwnedSubjectCreate(grant);
2067
+ }
2068
+ catch {
2069
+ fail('NATIVE_WORKFLOW_OWNED_SUBJECT_INVALID', entryPointer);
2070
+ }
2071
+ if (!result.workflow || access.decimalReservation !== undefined)
2072
+ fail('NATIVE_WORKFLOW_OWNED_SUBJECT_INVALID', entryPointer);
2073
+ try {
2074
+ assertWorkflowOwnedSubjectCreateResources(grant, declaredResourceFields, result.managedFiles || []);
2075
+ }
2076
+ catch (error) {
2077
+ fail(`NATIVE_${error.message}`, entryPointer);
2078
+ }
2079
+ result.ownedSubject = { mode: 'create', resourceCode: grant.resourceCode, subtables: sorted(grant.subtables.map(table => ({
2080
+ fieldCode: table.fieldCode, fieldCodes: uniqueSorted([...table.fieldCodes]),
2081
+ })), table => table.fieldCode) };
2082
+ }
1973
2083
  if (access.decimalReservation !== undefined) {
1974
2084
  const reservationPointer = `${pointer}/decimalReservation`;
1975
2085
  const reservation = object(access.decimalReservation, reservationPointer);
@@ -2933,6 +3043,8 @@ function compileAdminNavigation(config) {
2933
3043
  }
2934
3044
  function validateAdminNavigationDeclaration(value, pointer) {
2935
3045
  const groups = boundedArray(value, pointer, NATIVE_CONTRACT_CAPACITY_V2.adminNavigationGroups);
3046
+ if (groups.reduce((count, group) => count + (Array.isArray(group?.items) ? group.items.length : 0), 0) > NATIVE_CONTRACT_CAPACITY_V2.adminNavigationItems)
3047
+ fail('NATIVE_ARRAY_LIMIT_EXCEEDED', pointer);
2936
3048
  let itemCount = 0;
2937
3049
  groups.forEach((rawGroup, groupIndex) => {
2938
3050
  const groupPointer = `${pointer}/${groupIndex}`;
@@ -3073,6 +3185,8 @@ function validateAdminPageContracts(value, pointer) {
3073
3185
  }
3074
3186
  function validateAdminNavigationContracts(value, pointer) {
3075
3187
  const groups = boundedArray(value, pointer, NATIVE_CONTRACT_CAPACITY_V2.adminNavigationGroups);
3188
+ if (groups.reduce((count, group) => count + (Array.isArray(group?.items) ? group.items.length : 0), 0) > NATIVE_CONTRACT_CAPACITY_V2.adminNavigationItems)
3189
+ fail('NATIVE_ARRAY_LIMIT_EXCEEDED', pointer);
3076
3190
  let itemCount = 0;
3077
3191
  groups.forEach((rawGroup, groupIndex) => {
3078
3192
  const groupPointer = `${pointer}/${groupIndex}`;
@@ -3594,6 +3708,9 @@ function compileWorkflowLaunchContract(config, definition, value, pointer, opera
3594
3708
  const fieldCodes = new Set();
3595
3709
  const sourceCounts = new Map();
3596
3710
  const normalizedInputs = {};
3711
+ const ownedSubject = operationContract.platformAccess?.ownedSubject;
3712
+ if (ownedSubject && (intentKind !== 'create' || ownedSubject.resourceCode !== subjectResourceCode))
3713
+ fail('NATIVE_WORKFLOW_OWNED_SUBJECT_LAUNCH_MISMATCH', intentPointer);
3597
3714
  for (const [inputCode, rawBinding] of inputEntries) {
3598
3715
  if (!PROPERTY_CODE_PATTERN.test(inputCode) ||
3599
3716
  !Object.prototype.hasOwnProperty.call(requestProperties, inputCode)) {
@@ -3620,6 +3737,9 @@ function compileWorkflowLaunchContract(config, definition, value, pointer, opera
3620
3737
  fail('NATIVE_WORKFLOW_NAMED_OPERATION_INPUT_INVALID', `${bindingPointer}/fieldCode`);
3621
3738
  }
3622
3739
  fieldCodes.add(fieldCode);
3740
+ const field = subjectResource.schema.fields.find((item) => item.code === fieldCode);
3741
+ if (field?.type === 'subtable' && (!ownedSubject || !ownedSubject.subtables.some((table) => table.fieldCode === fieldCode)))
3742
+ fail('NATIVE_WORKFLOW_OWNED_SUBJECT_LAUNCH_REQUIRED', bindingPointer);
3623
3743
  normalizedInputs[inputCode] = { source, fieldCode };
3624
3744
  }
3625
3745
  else {
@@ -4075,6 +4195,7 @@ function validateDataPolicyReferences(config) {
4075
4195
  'name',
4076
4196
  'resourceCode',
4077
4197
  'unrestrictedRoleCodes',
4198
+ 'publicRead',
4078
4199
  'operations',
4079
4200
  'matchMode',
4080
4201
  'rules',
@@ -4106,6 +4227,26 @@ function validateDataPolicyReferences(config) {
4106
4227
  if (hasReadExpression && !policyFields) {
4107
4228
  fail('NATIVE_DATA_POLICY_RESOURCE_MISSING', `${pointer}/resourceCode`);
4108
4229
  }
4230
+ if (policy.publicRead !== undefined) {
4231
+ try {
4232
+ const baseline = config.authz.roles.find((role) => role.code === authenticatedUserRoleCode);
4233
+ validateAuthenticatedPublicRead({ policy,
4234
+ resource: config.data.resources.find((resource) => resource.code === policyResourceCode),
4235
+ baselineRoleCode: authenticatedUserRoleCode,
4236
+ baselineCapabilities: baseline?.capabilities || [],
4237
+ baselineDeniedCapabilities: baseline?.deniedCapabilities || [], pointer });
4238
+ }
4239
+ catch (error) {
4240
+ if (!(error instanceof AuthenticatedPublicReadContractError))
4241
+ throw error;
4242
+ fail(error.code, error.pointer);
4243
+ }
4244
+ if (!(policy.unrestrictedRoleCodes || []).includes(authenticatedUserRoleCode))
4245
+ fail('AUTHENTICATED_PUBLIC_READ_CANONICAL_BASELINE_MISSING', `${pointer}/unrestrictedRoleCodes`);
4246
+ }
4247
+ else if (authenticatedUserRoleCode && (policy.unrestrictedRoleCodes || []).includes(authenticatedUserRoleCode)) {
4248
+ fail('NATIVE_DATA_POLICY_BASELINE_UNRESTRICTED', `${pointer}/unrestrictedRoleCodes`);
4249
+ }
4109
4250
  const unrestrictedRoleCodes = uniqueStrings(policy.unrestrictedRoleCodes || [], `${pointer}/unrestrictedRoleCodes`, 100);
4110
4251
  if (policy.unrestrictedRoleCodes !== undefined &&
4111
4252
  unrestrictedRoleCodes.length === 0) {
@@ -4482,12 +4623,15 @@ function validateWorkflowReferences(config) {
4482
4623
  const preflightErrors = validateWorkflowLaunchPreflight(definition);
4483
4624
  if (preflightErrors.length)
4484
4625
  fail(preflightErrors[0], `${pointer}/definition/launchPreflight`);
4626
+ const delegationErrors = validateWorkflowApprovedDelegation(definition);
4627
+ if (delegationErrors.length)
4628
+ fail(delegationErrors[0], `${pointer}/definition/approvedDelegation`);
4485
4629
  const businessCommandErrors = validateWorkflowCommandHandlers(definition, config.backend.operations);
4486
4630
  if (businessCommandErrors.length)
4487
4631
  fail(businessCommandErrors[0], `${pointer}/definition/commandHandlers`);
4488
4632
  validateWorkflowSubject(definition.subject, resources, `${pointer}/definition/subject`);
4489
4633
  const taskPageErrors = validateWorkflowTaskPages(definition, new Map((resources.get(definition.subject.resourceCode)?.schema.fields || [])
4490
- .map((field) => [field.code, { ...resources.get(definition.subject.resourceCode)?.surface?.fields?.[field.code], ...field, system: ['id', 'revision', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'].includes(field.code) }])), new Map([...resources].map(([code, resource]) => [code, new Map(resource.schema.fields.map((field) => [field.code, { ...resource.surface?.fields?.[field.code], ...field, system: ['id', 'revision', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'].includes(field.code) }]))])));
4634
+ .map((field) => [field.code, { ...resources.get(definition.subject.resourceCode)?.surface?.fields?.[field.code], ...field, system: ['id', 'revision', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'].includes(field.code) }])), new Map([...resources].map(([code, resource]) => [code, new Map(resource.schema.fields.map((field) => [field.code, { ...resource.surface?.fields?.[field.code], ...field, system: ['id', 'revision', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'].includes(field.code) }]))])), dataOwnedRowLimit(resources.get(definition.subject.resourceCode)?.schema || {}));
4491
4635
  if (taskPageErrors.length)
4492
4636
  fail(taskPageErrors[0], `${pointer}/definition/taskPages`);
4493
4637
  const policyErrors = validateWorkflowInstanceCommandPolicies(definition, {
@@ -4723,7 +4867,7 @@ function validateWorkflowDefinition(definition, pointer) {
4723
4867
  'startAt',
4724
4868
  'inputSchema',
4725
4869
  'nodes',
4726
- ], pointer, ['organizationContext', 'instanceCommands', 'readability', 'taskPages', 'rejectionNotification', 'commandHandlers', 'launchPreflight']);
4870
+ ], pointer, ['organizationContext', 'instanceCommands', 'readability', 'taskPages', 'rejectionNotification', 'commandHandlers', 'launchPreflight', 'approvedDelegation']);
4727
4871
  equal(definition.schemaVersion, 'openxiangda.workflow-definition/v2', `${pointer}/schemaVersion`);
4728
4872
  workflowCodeValue(definition.code, `${pointer}/code`);
4729
4873
  requiredString(definition.title, `${pointer}/title`, 255);
@@ -4748,6 +4892,9 @@ function validateWorkflowDefinition(definition, pointer) {
4748
4892
  const initiatorPolicyErrors = validateWorkflowInitiatorApprovalPolicy(definition);
4749
4893
  if (initiatorPolicyErrors.length)
4750
4894
  fail(initiatorPolicyErrors[0], `${pointer}/nodes`);
4895
+ const deadlineErrors = validateWorkflowCompletionDeadlines(definition);
4896
+ if (deadlineErrors.length)
4897
+ fail(deadlineErrors[0], `${pointer}/nodes`);
4751
4898
  const rejectionNotificationErrors = validateWorkflowRejectionNotification(definition);
4752
4899
  if (rejectionNotificationErrors.length)
4753
4900
  fail(rejectionNotificationErrors[0], `${pointer}/rejectionNotification`);
@@ -4767,7 +4914,7 @@ function validateWorkflowDefinition(definition, pointer) {
4767
4914
  const nodePointer = `${pointer}/nodes/${nodeId}`;
4768
4915
  const node = object(rawNode, nodePointer);
4769
4916
  equal(node.id, nodeId, `${nodePointer}/id`);
4770
- if (!['approval', 'condition', 'end', 'cc', 'action'].includes(node.kind)) {
4917
+ if (!['approval', 'condition', 'end', 'cc', 'action', 'correction'].includes(node.kind)) {
4771
4918
  fail('NATIVE_WORKFLOW_NODE_KIND_INVALID', `${nodePointer}/kind`);
4772
4919
  }
4773
4920
  const targets = [];
@@ -4789,6 +4936,11 @@ function validateWorkflowDefinition(definition, pointer) {
4789
4936
  }
4790
4937
  }
4791
4938
  }
4939
+ else if (node.kind === 'correction') {
4940
+ const errors = validateWorkflowCorrections(definition);
4941
+ if (errors.length)
4942
+ fail(errors[0], nodePointer);
4943
+ }
4792
4944
  else if (node.kind === 'action') {
4793
4945
  targets.push(requiredString(node.next, `${nodePointer}/next`, 128));
4794
4946
  }
@@ -4833,6 +4985,13 @@ function validateWorkflowDefinition(definition, pointer) {
4833
4985
  visiting.delete(nodeId);
4834
4986
  };
4835
4987
  walk(startAt);
4988
+ for (const id of [...reachable]) {
4989
+ const current = nodes[id];
4990
+ if (current?.kind === 'approval')
4991
+ for (const target of current.returnTargets || [])
4992
+ if (nodes[target]?.kind === 'correction')
4993
+ reachable.add(target);
4994
+ }
4836
4995
  if (reachable.size !== nodeIds.length) {
4837
4996
  fail('NATIVE_WORKFLOW_NODE_UNREACHABLE', `${pointer}/nodes`);
4838
4997
  }
@@ -4879,6 +5038,9 @@ function validateWorkflowBinding(binding, pointer) {
4879
5038
  for (const [code, raw] of Object.entries(entries)) {
4880
5039
  workflowBindingCode(code, `${pointer}/bindings/${code}`);
4881
5040
  const entry = object(raw, `${pointer}/bindings/${code}`);
5041
+ const unionErrors = validateWorkflowRoleUnion(entry);
5042
+ if (unionErrors.length)
5043
+ fail(unionErrors[0], `${pointer}/bindings/${code}/roleCodes`);
4882
5044
  const provider = requiredString(entry.provider, `${pointer}/bindings/${code}/provider`, 64);
4883
5045
  if (![
4884
5046
  'fixed_users',
@@ -4903,7 +5065,7 @@ function validateWorkflowBinding(binding, pointer) {
4903
5065
  if (['input_users', 'form_field_users'].includes(provider)) {
4904
5066
  requiredString(entry.inputPath, `${pointer}/bindings/${code}/inputPath`, 255);
4905
5067
  }
4906
- if (['app_role', 'app_role_in_scope', 'initiator_select'].includes(provider)) {
5068
+ if (['app_role', 'app_role_in_scope', 'initiator_select'].includes(provider) && entry.roleCodes === undefined) {
4907
5069
  stableCode(entry.roleCode, `${pointer}/bindings/${code}/roleCode`);
4908
5070
  }
4909
5071
  if (provider === 'department_supervisor') {
@@ -4952,13 +5114,13 @@ function mergeUserSurfaceRoutes(expectedContract, contract, config) {
4952
5114
  const manifest = object(contract.routeManifest, '/contracts/routeManifest');
4953
5115
  const expectedManifest = object(expectedContract.routeManifest, '/contracts/routeManifest');
4954
5116
  const resources = new Map();
4955
- for (const raw of boundedArray(config.data?.resources, '/config/data/resources', 100)) {
5117
+ for (const raw of boundedArray(config.data?.resources, '/config/data/resources', NATIVE_CONTRACT_CAPACITY_V2.resources)) {
4956
5118
  const resource = object(raw, '/config/data/resources');
4957
5119
  resources.set(String(resource.code || ''), resource);
4958
5120
  }
4959
- const existingCodes = new Set(boundedArray(expectedManifest.routes, '/contracts/routeManifest/routes', 1000).map(entry => String(object(entry, '/contracts/routeManifest/routes').code || '')));
5121
+ 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
5122
  const accepted = [];
4961
- for (const rawEntry of boundedArray(manifest.routes, '/contracts/routeManifest/routes', 1000)) {
5123
+ for (const rawEntry of boundedArray(manifest.routes, '/contracts/routeManifest/routes', NATIVE_CONTRACT_CAPACITY_V2.routes)) {
4962
5124
  const entry = object(rawEntry, '/contracts/routeManifest/routes');
4963
5125
  const kind = String(entry.kind || '');
4964
5126
  if (kind !== 'resource-records' && kind !== 'resource-submit')
@@ -5036,7 +5198,9 @@ function validateResource(raw, pointer, appCode) {
5036
5198
  const code = resourceCode(resource.code, `${pointer}/code`);
5037
5199
  const name = requiredString(resource.name, `${pointer}/name`, 255);
5038
5200
  const schema = object(resource.schema, `${pointer}/schema`);
5039
- exactKeys(schema, ['fields'], `${pointer}/schema`);
5201
+ exactKeys(schema, ['fields', 'ownedRowLimit'], `${pointer}/schema`, true);
5202
+ if (schema.ownedRowLimit !== undefined && !isDataOwnedRowLimit(schema.ownedRowLimit))
5203
+ fail('NATIVE_DATA_OWNED_ROW_LIMIT_INVALID', `${pointer}/schema/ownedRowLimit`);
5040
5204
  let fields;
5041
5205
  let invariants;
5042
5206
  let decimalReservationLifecycle;
@@ -5115,7 +5279,7 @@ function validateResource(raw, pointer, appCode) {
5115
5279
  ...resource,
5116
5280
  code,
5117
5281
  name,
5118
- schema: { fields },
5282
+ schema: { fields, ...(schema.ownedRowLimit !== undefined ? { ownedRowLimit: schema.ownedRowLimit } : {}) },
5119
5283
  ...(resource.invariants === undefined ? {} : { invariants }),
5120
5284
  ...(decimalReservationLifecycle ? { decimalReservationLifecycle } : {}),
5121
5285
  ...(uniqueKeys !== undefined ? { uniqueKeys } : {}),
@@ -5160,11 +5324,11 @@ function parseCanonicalArtifact(source, pointer, invalidCode, canonicalCode) {
5160
5324
  fail(canonicalCode, pointer);
5161
5325
  return result;
5162
5326
  }
5163
- function inspectJsonBudget(value, pointer) {
5327
+ function inspectJsonBudget(value, pointer, maxNodes) {
5164
5328
  let nodes = 0;
5165
5329
  const visit = (current, path, depth) => {
5166
5330
  nodes += 1;
5167
- if (nodes > MAX_NODES)
5331
+ if (nodes > maxNodes)
5168
5332
  fail('NATIVE_ARTIFACT_NODE_LIMIT_EXCEEDED', path);
5169
5333
  if (depth > MAX_DEPTH)
5170
5334
  fail('NATIVE_ARTIFACT_DEPTH_LIMIT_EXCEEDED', path);