Skip to content

Commit 042950d

Browse files
committed
feat: Add approval and feature flag schemas with validation logic
1 parent 9ee8c85 commit 042950d

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { z } from 'zod';
2+
import { SnakeCaseIdentifierSchema } from '../shared/identifiers.zod';
3+
4+
/**
5+
* Approval Step Approver Type
6+
*/
7+
export const ApproverType = z.enum([
8+
'user', // Specific user(s)
9+
'role', // Users with specific role
10+
'manager', // Submitter's manager
11+
'field', // User ID defined in a record field
12+
'queue' // Data ownership queue
13+
]);
14+
15+
/**
16+
* Approval Action Type
17+
* Actions to execute on transition
18+
*/
19+
export const ApprovalActionType = z.enum([
20+
'field_update',
21+
'email_alert',
22+
'webhook',
23+
'script'
24+
]);
25+
26+
/**
27+
* definition of an action to perform
28+
*/
29+
export const ApprovalActionSchema = z.object({
30+
type: ApprovalActionType,
31+
name: z.string().describe('Action name'),
32+
config: z.record(z.any()).describe('Action configuration')
33+
});
34+
35+
/**
36+
* Approval Process Step
37+
*/
38+
export const ApprovalStepSchema = z.object({
39+
name: SnakeCaseIdentifierSchema.describe('Step machine name'),
40+
label: z.string().describe('Step display label'),
41+
description: z.string().optional(),
42+
43+
/** Entry criteria for this step */
44+
entryCriteria: z.string().optional().describe('Formula expression to enter this step'),
45+
46+
/** Who can approve */
47+
approvers: z.array(z.object({
48+
type: ApproverType,
49+
value: z.string().describe('User ID, Role Name, or Field Name')
50+
})).min(1).describe('List of allowed approvers'),
51+
52+
/** Approval Logic */
53+
behavior: z.enum(['first_response', 'unanimous']).default('first_response')
54+
.describe('How to handle multiple approvers'),
55+
56+
/** Rejection behavior */
57+
rejectionBehavior: z.enum(['reject_process', 'back_to_previous'])
58+
.default('reject_process').describe('What happens if rejected'),
59+
60+
/** Actions */
61+
onApprove: z.array(ApprovalActionSchema).optional().describe('Actions on step approval'),
62+
onReject: z.array(ApprovalActionSchema).optional().describe('Actions on step rejection'),
63+
});
64+
65+
/**
66+
* Approval Process Protocol
67+
*
68+
* Defines a complex review and approval cycle for a record.
69+
* Manages state locking, notifications, and transition logic.
70+
*/
71+
export const ApprovalProcessSchema = z.object({
72+
name: SnakeCaseIdentifierSchema.describe('Unique process name'),
73+
label: z.string().describe('Human readable label'),
74+
object: z.string().describe('Target Object Name'),
75+
76+
active: z.boolean().default(false),
77+
description: z.string().optional(),
78+
79+
/** Entry Criteria for the entire process */
80+
entryCriteria: z.string().optional().describe('Formula to allow submission'),
81+
82+
/** Record Locking */
83+
lockRecord: z.boolean().default(true).describe('Lock record from editing during approval'),
84+
85+
/** Steps */
86+
steps: z.array(ApprovalStepSchema).min(1).describe('Sequence of approval steps'),
87+
88+
/** Global Actions */
89+
onSubmit: z.array(ApprovalActionSchema).optional().describe('Actions on initial submission'),
90+
onFinalApprove: z.array(ApprovalActionSchema).optional().describe('Actions on final approval'),
91+
onFinalReject: z.array(ApprovalActionSchema).optional().describe('Actions on final rejection'),
92+
onRecall: z.array(ApprovalActionSchema).optional().describe('Actions on recall'),
93+
});
94+
95+
export const ApprovalProcess = Object.assign(ApprovalProcessSchema, {
96+
create: <T extends z.input<typeof ApprovalProcessSchema>>(config: T) => config,
97+
});
98+
99+
export type ApprovalProcess = z.infer<typeof ApprovalProcessSchema>;
100+
export type ApprovalStep = z.infer<typeof ApprovalStepSchema>;

packages/spec/src/automation/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
export * from './workflow.zod';
33
export * from './flow.zod';
44
export * from './webhook.zod';
5+
export * from './approval.zod';
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { z } from 'zod';
2+
import { SnakeCaseIdentifierSchema } from '../shared/identifiers.zod';
3+
4+
/**
5+
* Feature Rollout Strategy
6+
*/
7+
export const FeatureStrategy = z.enum([
8+
'boolean', // Simple On/Off
9+
'percentage', // Gradual rollout (0-100%)
10+
'user_list', // Specific users
11+
'group', // Specific groups/roles
12+
'custom' // Custom constraint/script
13+
]);
14+
15+
/**
16+
* Feature Flag Protocol
17+
*
18+
* Manages feature toggles and gradual rollouts.
19+
* Used for CI/CD, A/B Testing, and Trunk-Based Development.
20+
*/
21+
export const FeatureFlagSchema = z.object({
22+
name: SnakeCaseIdentifierSchema.describe('Feature key (snake_case)'),
23+
label: z.string().optional().describe('Display label'),
24+
description: z.string().optional(),
25+
26+
/** Default state */
27+
enabled: z.boolean().default(false).describe('Is globally enabled'),
28+
29+
/** Rollout Strategy */
30+
strategy: FeatureStrategy.default('boolean'),
31+
32+
/** Strategy Configuration */
33+
conditions: z.object({
34+
percentage: z.number().min(0).max(100).optional(),
35+
users: z.array(z.string()).optional(),
36+
groups: z.array(z.string()).optional(),
37+
expression: z.string().optional().describe('Custom formula expression')
38+
}).optional(),
39+
40+
/** Integration */
41+
environment: z.enum(['dev', 'staging', 'prod', 'all']).default('all')
42+
.describe('Environment validity'),
43+
44+
/** Expiration */
45+
expiresAt: z.string().datetime().optional().describe('Feature flag expiration date'),
46+
});
47+
48+
export const FeatureFlag = Object.assign(FeatureFlagSchema, {
49+
create: <T extends z.input<typeof FeatureFlagSchema>>(config: T) => config,
50+
});
51+
52+
export type FeatureFlag = z.infer<typeof FeatureFlagSchema>;

packages/spec/src/system/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export * from './audit.zod';
1111
export * from './translation.zod';
1212
export * from './events.zod';
1313
export * from './job.zod';
14+
export * from './feature.zod';
1415
export * from './types';
1516

1617
// Re-export Core System Definitions

0 commit comments

Comments
 (0)