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.
- package/dist/ai.d.ts +1 -1
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +4 -3
- package/dist/ai.js.map +1 -1
- package/dist/browser.d.ts +6 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +6 -0
- package/dist/browser.js.map +1 -1
- package/dist/concurrency.d.ts +1 -1
- package/dist/concurrency.d.ts.map +1 -1
- package/dist/data-field-validation.js +7 -2
- package/dist/data-field-validation.js.map +1 -1
- package/dist/field-value-schemas.d.ts +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/native-compiler/actor-authority.d.ts +11 -0
- package/dist/native-compiler/actor-authority.d.ts.map +1 -0
- package/dist/native-compiler/actor-authority.js +56 -0
- package/dist/native-compiler/actor-authority.js.map +1 -0
- package/dist/native-compiler/artifact-capacity.d.ts +12 -0
- package/dist/native-compiler/artifact-capacity.d.ts.map +1 -0
- package/dist/native-compiler/artifact-capacity.js +34 -0
- package/dist/native-compiler/artifact-capacity.js.map +1 -0
- package/dist/native-compiler/authenticated-public-read.d.ts +18 -0
- package/dist/native-compiler/authenticated-public-read.d.ts.map +1 -0
- package/dist/native-compiler/authenticated-public-read.js +79 -0
- package/dist/native-compiler/authenticated-public-read.js.map +1 -0
- package/dist/native-compiler/compiler.d.ts +2 -21
- package/dist/native-compiler/compiler.d.ts.map +1 -1
- package/dist/native-compiler/compiler.js +213 -49
- package/dist/native-compiler/compiler.js.map +1 -1
- package/dist/native-compiler/data-business-command.d.ts +3 -1
- package/dist/native-compiler/data-business-command.d.ts.map +1 -1
- package/dist/native-compiler/data-business-command.js.map +1 -1
- package/dist/native-compiler/data-capacity.d.ts +25 -3
- package/dist/native-compiler/data-capacity.d.ts.map +1 -1
- package/dist/native-compiler/data-capacity.js +26 -3
- package/dist/native-compiler/data-capacity.js.map +1 -1
- package/dist/native-compiler/data-field.d.ts +22 -0
- package/dist/native-compiler/data-field.d.ts.map +1 -1
- package/dist/native-compiler/data-field.js +25 -4
- package/dist/native-compiler/data-field.js.map +1 -1
- package/dist/native-compiler/date-event-projection.d.ts +13 -0
- package/dist/native-compiler/date-event-projection.d.ts.map +1 -0
- package/dist/native-compiler/date-event-projection.js +71 -0
- package/dist/native-compiler/date-event-projection.js.map +1 -0
- package/dist/native-compiler/declaration-capacity.d.ts +60 -0
- package/dist/native-compiler/declaration-capacity.d.ts.map +1 -0
- package/dist/native-compiler/declaration-capacity.js +32 -0
- package/dist/native-compiler/declaration-capacity.js.map +1 -0
- package/dist/native-compiler/development-backend.d.ts +84 -0
- package/dist/native-compiler/development-backend.d.ts.map +1 -0
- package/dist/native-compiler/development-backend.js +77 -0
- package/dist/native-compiler/development-backend.js.map +1 -0
- package/dist/native-compiler/development-configuration.d.ts +65 -0
- package/dist/native-compiler/development-configuration.d.ts.map +1 -0
- package/dist/native-compiler/development-configuration.js +143 -0
- package/dist/native-compiler/development-configuration.js.map +1 -0
- package/dist/native-compiler/index.cjs +6 -6
- package/dist/native-compiler/index.d.ts +14 -0
- package/dist/native-compiler/index.d.ts.map +1 -1
- package/dist/native-compiler/index.js +6 -6
- package/dist/native-compiler/record-set-guard.d.ts +5 -0
- package/dist/native-compiler/record-set-guard.d.ts.map +1 -0
- package/dist/native-compiler/record-set-guard.js +27 -0
- package/dist/native-compiler/record-set-guard.js.map +1 -0
- package/dist/native-compiler/user-candidates.d.ts +9 -1
- package/dist/native-compiler/user-candidates.d.ts.map +1 -1
- package/dist/native-compiler/user-candidates.js +14 -1
- package/dist/native-compiler/user-candidates.js.map +1 -1
- package/dist/native-compiler/workflow-approved-delegation.d.ts +39 -0
- package/dist/native-compiler/workflow-approved-delegation.d.ts.map +1 -0
- package/dist/native-compiler/workflow-approved-delegation.js +94 -0
- package/dist/native-compiler/workflow-approved-delegation.js.map +1 -0
- package/dist/native-compiler/workflow-business-command.d.ts +2 -2
- package/dist/native-compiler/workflow-business-command.d.ts.map +1 -1
- package/dist/native-compiler/workflow-business-command.js +8 -4
- package/dist/native-compiler/workflow-business-command.js.map +1 -1
- package/dist/native-compiler/workflow-business-step.d.ts +4 -0
- package/dist/native-compiler/workflow-business-step.d.ts.map +1 -1
- package/dist/native-compiler/workflow-business-step.js +11 -6
- package/dist/native-compiler/workflow-business-step.js.map +1 -1
- package/dist/native-compiler/workflow-completion-deadline.d.ts +41 -0
- package/dist/native-compiler/workflow-completion-deadline.d.ts.map +1 -0
- package/dist/native-compiler/workflow-completion-deadline.js +40 -0
- package/dist/native-compiler/workflow-completion-deadline.js.map +1 -0
- package/dist/native-compiler/workflow-correction.d.ts +16 -0
- package/dist/native-compiler/workflow-correction.d.ts.map +1 -0
- package/dist/native-compiler/workflow-correction.js +42 -0
- package/dist/native-compiler/workflow-correction.js.map +1 -0
- package/dist/native-compiler/workflow-delegation-administration.d.ts +73 -0
- package/dist/native-compiler/workflow-delegation-administration.d.ts.map +1 -1
- package/dist/native-compiler/workflow-delegation-administration.js +6 -4
- package/dist/native-compiler/workflow-delegation-administration.js.map +1 -1
- package/dist/native-compiler/workflow-graph.d.ts +13 -2
- package/dist/native-compiler/workflow-graph.d.ts.map +1 -1
- package/dist/native-compiler/workflow-graph.js +20 -4
- package/dist/native-compiler/workflow-graph.js.map +1 -1
- package/dist/native-compiler/workflow-initiator-approval.d.ts +5 -1
- package/dist/native-compiler/workflow-initiator-approval.d.ts.map +1 -1
- package/dist/native-compiler/workflow-initiator-approval.js +18 -3
- package/dist/native-compiler/workflow-initiator-approval.js.map +1 -1
- package/dist/native-compiler/workflow-native-stage.d.ts +69 -0
- package/dist/native-compiler/workflow-native-stage.d.ts.map +1 -0
- package/dist/native-compiler/workflow-native-stage.js +42 -0
- package/dist/native-compiler/workflow-native-stage.js.map +1 -0
- package/dist/native-compiler/workflow-node-administration.d.ts +5 -1
- package/dist/native-compiler/workflow-node-administration.d.ts.map +1 -1
- package/dist/native-compiler/workflow-node-administration.js +17 -4
- package/dist/native-compiler/workflow-node-administration.js.map +1 -1
- package/dist/native-compiler/workflow-owned-subject.d.ts +60 -0
- package/dist/native-compiler/workflow-owned-subject.d.ts.map +1 -0
- package/dist/native-compiler/workflow-owned-subject.js +69 -0
- package/dist/native-compiler/workflow-owned-subject.js.map +1 -0
- package/dist/native-compiler/workflow-role-union.d.ts +31 -0
- package/dist/native-compiler/workflow-role-union.d.ts.map +1 -0
- package/dist/native-compiler/workflow-role-union.js +38 -0
- package/dist/native-compiler/workflow-role-union.js.map +1 -0
- package/dist/native-compiler/workflow-step-data.d.ts +28 -0
- package/dist/native-compiler/workflow-step-data.d.ts.map +1 -0
- package/dist/native-compiler/workflow-step-data.js +34 -0
- package/dist/native-compiler/workflow-step-data.js.map +1 -0
- package/dist/native-compiler/workflow-task-page.d.ts +11 -4
- package/dist/native-compiler/workflow-task-page.d.ts.map +1 -1
- package/dist/native-compiler/workflow-task-page.js +7 -5
- package/dist/native-compiler/workflow-task-page.js.map +1 -1
- package/dist/native-version.d.ts +22 -0
- package/dist/native-version.d.ts.map +1 -1
- package/dist/native-version.js +22 -0
- package/dist/native-version.js.map +1 -1
- package/dist/native.d.ts +9 -0
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/references.d.ts +2 -0
- package/dist/references.d.ts.map +1 -1
- package/dist/schemas.d.ts +1838 -80
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +153 -30
- package/dist/schemas.js.map +1 -1
- package/dist/surface.d.ts +1 -0
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js.map +1 -1
- package/dist/types.d.ts +89 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +30 -1
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
96
|
-
const MAX_CONTRACT_BYTES =
|
|
97
|
-
const MAX_DEPTH =
|
|
98
|
-
const
|
|
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
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
201
|
-
|
|
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',
|
|
858
|
-
boundedArray(authz.roles, '/config/authz/roles',
|
|
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',
|
|
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',
|
|
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',
|
|
898
|
-
boundedArray(workflows.bindings, '/config/workflows/bindings',
|
|
899
|
-
boundedArray(workflows.activations, '/config/workflows/activations',
|
|
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',
|
|
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`,
|
|
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',
|
|
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',
|
|
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',
|
|
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 >
|
|
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);
|