@cxtms/cx-schema 1.0.0 → 1.1.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 (38) hide show
  1. package/README.md +34 -34
  2. package/dist/cli.js +545 -35
  3. package/dist/cli.js.map +1 -1
  4. package/dist/types.d.ts +17 -1
  5. package/dist/types.d.ts.map +1 -1
  6. package/dist/workflowValidator.d.ts +31 -0
  7. package/dist/workflowValidator.d.ts.map +1 -1
  8. package/dist/workflowValidator.js +299 -9
  9. package/dist/workflowValidator.js.map +1 -1
  10. package/package.json +2 -2
  11. package/schemas/schema.graphql +2077 -321
  12. package/schemas/workflows/flow/aggregation.json +44 -0
  13. package/schemas/workflows/flow/entity.json +110 -0
  14. package/schemas/workflows/flow/state.json +105 -0
  15. package/schemas/workflows/flow/transition.json +143 -0
  16. package/schemas/workflows/input.json +53 -7
  17. package/schemas/workflows/output.json +20 -0
  18. package/schemas/workflows/trigger.json +4 -0
  19. package/schemas/workflows/variable.json +2 -6
  20. package/schemas/workflows/workflow.json +179 -21
  21. package/scripts/postinstall.js +30 -1
  22. package/templates/module-configuration.yaml +110 -0
  23. package/templates/module-form.yaml +152 -0
  24. package/templates/module-grid.yaml +229 -0
  25. package/templates/module-select.yaml +139 -0
  26. package/templates/module.yaml +3 -3
  27. package/templates/workflow-api-tracking.yaml +189 -0
  28. package/templates/workflow-basic.yaml +76 -0
  29. package/templates/workflow-document.yaml +155 -0
  30. package/templates/workflow-entity-trigger.yaml +90 -0
  31. package/templates/workflow-ftp-edi.yaml +158 -0
  32. package/templates/workflow-ftp-tracking.yaml +161 -0
  33. package/templates/workflow-mcp-tool.yaml +112 -0
  34. package/templates/workflow-public-api.yaml +135 -0
  35. package/templates/workflow-scheduled.yaml +125 -0
  36. package/templates/workflow-utility.yaml +96 -0
  37. package/templates/workflow-webhook.yaml +128 -0
  38. package/templates/workflow.yaml +52 -12
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "title": "CargoXplorer Workflow",
4
- "description": "Schema for CargoXplorer workflow YAML files",
4
+ "description": "Schema for CargoXplorer workflow YAML files. Supports standard task-based workflows and Flow (state machine) workflows.",
5
5
  "type": "object",
6
6
  "properties": {
7
7
  "workflow": {
@@ -50,11 +50,13 @@
50
50
  },
51
51
  "enableActionEvents": {
52
52
  "type": "boolean",
53
- "description": "Enable action events"
53
+ "description": "Enable action events (deprecated)"
54
54
  },
55
55
  "priority": {
56
- "type": "number",
57
- "description": "Workflow priority (higher = higher priority)"
56
+ "type": "integer",
57
+ "minimum": 0,
58
+ "maximum": 100,
59
+ "description": "Workflow priority (0-100, higher = higher priority, default: 50)"
58
60
  },
59
61
  "tags": {
60
62
  "type": "array",
@@ -65,30 +67,44 @@
65
67
  },
66
68
  "workflowType": {
67
69
  "type": "string",
68
- "enum": ["Document", "Quote", "EmailTemplate"],
69
- "description": "Workflow type classification"
70
+ "enum": ["Document", "Quote", "Flow", "Webhook", "PublicApi"],
71
+ "description": "Workflow type: Document (PDF/Excel generation), Quote, Flow (declarative state machine), Webhook (HTTP endpoint), or PublicApi (REST API endpoint). Omit for standard process workflows."
70
72
  },
71
73
  "runAs": {
72
74
  "type": "string",
73
- "description": "User context for workflow execution"
75
+ "description": "Username or user ID for workflow execution context"
74
76
  },
75
77
  "concurrency": {
76
78
  "type": "object",
77
79
  "properties": {
80
+ "enabled": {
81
+ "type": "boolean",
82
+ "default": true,
83
+ "description": "Whether concurrency control is active"
84
+ },
78
85
  "group": {
79
86
  "type": "string",
80
87
  "description": "Concurrency group identifier"
81
88
  },
82
89
  "waitTime": {
83
90
  "type": "number",
84
- "description": "Seconds to wait for lock"
91
+ "default": 15,
92
+ "description": "Seconds to wait for lock (default: 15)"
85
93
  }
86
94
  },
87
95
  "additionalProperties": true
88
96
  },
97
+ "filePath": {
98
+ "type": "string",
99
+ "description": "Repository file path under workflows/ or features/{name}/workflows/"
100
+ },
101
+ "agentInstruction": {
102
+ "type": "string",
103
+ "description": "AI agent instruction describing how to use this workflow, expected inputs, and returned outputs"
104
+ },
89
105
  "fileName": {
90
106
  "type": "string",
91
- "description": "Path to the workflow YAML file"
107
+ "description": "Path to the workflow YAML file (deprecated, use filePath)"
92
108
  },
93
109
  "actionEvent": {
94
110
  "type": "object",
@@ -127,7 +143,7 @@
127
143
  },
128
144
  "activities": {
129
145
  "type": "array",
130
- "description": "Workflow activities containing steps",
146
+ "description": "Workflow activities containing steps (required for standard workflows, not used in Flow workflows)",
131
147
  "items": {
132
148
  "$ref": "activity.json"
133
149
  },
@@ -148,25 +164,167 @@
148
164
  }
149
165
  },
150
166
  "events": {
167
+ "oneOf": [
168
+ {
169
+ "type": "object",
170
+ "description": "Workflow-level event handlers as object",
171
+ "properties": {
172
+ "onWorkflowStarted": {
173
+ "type": "array",
174
+ "description": "Steps to execute when workflow starts"
175
+ },
176
+ "onWorkflowExecuted": {
177
+ "type": "array",
178
+ "description": "Steps to execute when workflow completes"
179
+ },
180
+ "onWorkflowFailed": {
181
+ "type": "array",
182
+ "description": "Steps to execute when workflow fails"
183
+ }
184
+ },
185
+ "additionalProperties": true
186
+ },
187
+ {
188
+ "type": "array",
189
+ "description": "Workflow-level event handlers as array",
190
+ "items": {
191
+ "type": "object",
192
+ "properties": {
193
+ "type": {
194
+ "type": "string",
195
+ "description": "Event type"
196
+ },
197
+ "steps": {
198
+ "type": "array",
199
+ "description": "Steps to execute for this event"
200
+ }
201
+ },
202
+ "additionalProperties": true
203
+ }
204
+ }
205
+ ]
206
+ },
207
+ "api": {
151
208
  "type": "object",
152
- "description": "Workflow-level event handlers",
209
+ "description": "Public API endpoint configuration (required for PublicApi workflows)",
153
210
  "properties": {
154
- "onWorkflowStarted": {
155
- "type": "array",
156
- "description": "Steps to execute when workflow starts"
211
+ "path": {
212
+ "type": "string",
213
+ "description": "API route path with parameter placeholders (e.g., '/orders/{orderId}')"
157
214
  },
158
- "onWorkflowExecuted": {
159
- "type": "array",
160
- "description": "Steps to execute when workflow completes"
215
+ "method": {
216
+ "type": "string",
217
+ "enum": ["GET", "POST", "PUT", "PATCH", "DELETE"],
218
+ "description": "HTTP method"
161
219
  },
162
- "onWorkflowFailed": {
163
- "type": "array",
164
- "description": "Steps to execute when workflow fails"
220
+ "summary": {
221
+ "type": "string",
222
+ "description": "Short summary for Swagger/OpenAPI documentation"
223
+ },
224
+ "description": {
225
+ "type": "string",
226
+ "description": "Detailed description for Swagger/OpenAPI documentation"
227
+ },
228
+ "operationId": {
229
+ "type": "string",
230
+ "description": "Unique operation ID for OpenAPI (auto-generated if omitted)"
231
+ },
232
+ "document": {
233
+ "type": "string",
234
+ "description": "Swagger document name (default: 'public')"
235
+ },
236
+ "category": {
237
+ "type": "string",
238
+ "description": "Swagger tag/group for organizing endpoints"
239
+ },
240
+ "authentication": {
241
+ "type": "string",
242
+ "enum": ["none", "bearer", "apiKey"],
243
+ "description": "Authentication method: none (anonymous), bearer (JWT token), or apiKey"
244
+ },
245
+ "rateLimit": {
246
+ "type": "object",
247
+ "description": "Rate limiting configuration",
248
+ "properties": {
249
+ "perSecond": {
250
+ "type": "integer",
251
+ "description": "Maximum requests per second"
252
+ },
253
+ "perMinute": {
254
+ "type": "integer",
255
+ "description": "Maximum requests per minute"
256
+ }
257
+ },
258
+ "additionalProperties": false
165
259
  }
166
260
  },
261
+ "required": ["path", "method"],
167
262
  "additionalProperties": true
263
+ },
264
+ "entity": {
265
+ "$ref": "flow/entity.json",
266
+ "description": "Entity configuration for Flow workflows"
267
+ },
268
+ "states": {
269
+ "type": "array",
270
+ "description": "State definitions for Flow workflows",
271
+ "items": {
272
+ "$ref": "flow/state.json"
273
+ }
274
+ },
275
+ "transitions": {
276
+ "type": "array",
277
+ "description": "Transition definitions for Flow workflows",
278
+ "items": {
279
+ "$ref": "flow/transition.json"
280
+ }
281
+ },
282
+ "aggregations": {
283
+ "type": "array",
284
+ "description": "Aggregation definitions for Flow workflows",
285
+ "items": {
286
+ "$ref": "flow/aggregation.json"
287
+ }
168
288
  }
169
289
  },
170
- "required": ["workflow", "activities"],
290
+ "required": ["workflow"],
291
+ "allOf": [
292
+ {
293
+ "if": {
294
+ "properties": {
295
+ "workflow": {
296
+ "properties": {
297
+ "workflowType": { "const": "Flow" }
298
+ },
299
+ "required": ["workflowType"]
300
+ }
301
+ }
302
+ },
303
+ "then": {
304
+ "required": ["workflow", "entity"],
305
+ "properties": {
306
+ "activities": false
307
+ }
308
+ },
309
+ "else": {
310
+ "required": ["workflow", "activities"]
311
+ }
312
+ },
313
+ {
314
+ "if": {
315
+ "properties": {
316
+ "workflow": {
317
+ "properties": {
318
+ "workflowType": { "const": "PublicApi" }
319
+ },
320
+ "required": ["workflowType"]
321
+ }
322
+ }
323
+ },
324
+ "then": {
325
+ "required": ["workflow", "activities", "api"]
326
+ }
327
+ }
328
+ ],
171
329
  "additionalProperties": true
172
330
  }
@@ -149,10 +149,39 @@ function main() {
149
149
  console.log('Creating validation script...');
150
150
  createValidationScript(projectRoot);
151
151
 
152
+ // Copy Claude Code skills
153
+ const skillNames = ['cx-module', 'cx-workflow'];
154
+ for (const skillName of skillNames) {
155
+ const skillSource = path.join(__dirname, '..', '.claude', 'skills', skillName);
156
+ if (fs.existsSync(skillSource)) {
157
+ const skillDest = path.join(projectRoot, '.claude', 'skills', skillName);
158
+ console.log(`Installing ${skillName} skill...`);
159
+ try {
160
+ copyDirectory(skillSource, skillDest);
161
+ console.log(`${skillName} skill installed successfully!`);
162
+ } catch (error) {
163
+ console.warn(`Warning: Could not install ${skillName} skill:`, error.message);
164
+ }
165
+ }
166
+ }
167
+
168
+ // Remove old cx-build skill if it exists
169
+ const oldSkillDest = path.join(projectRoot, '.claude', 'skills', 'cx-build');
170
+ if (fs.existsSync(oldSkillDest)) {
171
+ try {
172
+ fs.rmSync(oldSkillDest, { recursive: true });
173
+ console.log('Removed deprecated cx-build skill.');
174
+ } catch (error) {
175
+ // Ignore cleanup errors
176
+ }
177
+ }
178
+
152
179
  console.log('✓ CX Schema Validator installed successfully!');
153
180
  console.log('\nUsage:');
154
- console.log(' npx cx-validate modules/your-module.yaml');
181
+ console.log(' npx cx-cli modules/your-module.yaml');
155
182
  console.log(' node .cx-schema/validate.js modules/your-module.yaml');
183
+ console.log(' /cx-module <description> (Claude Code skill - UI modules)');
184
+ console.log(' /cx-workflow <description> (Claude Code skill - workflows)');
156
185
  }
157
186
 
158
187
  // Only run if this is not being installed as a dependency of cx-schema-validator itself
@@ -0,0 +1,110 @@
1
+ # {{displayName}} Configuration Module
2
+ # Generated by cx-cli create module --template configuration
3
+
4
+ module:
5
+ name: "{{displayNameNoSpaces}}"
6
+ appModuleId: "{{uuid}}"
7
+ displayName:
8
+ en-US: "{{displayName}}"
9
+ description:
10
+ en-US: "{{displayName}} configuration module"
11
+ application: CargoXplorer
12
+ fileName: "{{fileName}}"
13
+
14
+ components:
15
+ - name: {{displayNameNoSpaces}}/Configuration
16
+ displayName:
17
+ en-US: "{{displayName}}"
18
+ layout:
19
+ component: form
20
+ name: {{name}}Form
21
+ props:
22
+ title:
23
+ en-US: "{{displayName}}"
24
+ queries:
25
+ - name: getConfiguration
26
+ query:
27
+ command: |
28
+ query GetConfiguration($organizationId: Int!) {
29
+ configuration(organizationId: $organizationId) {
30
+ settingName
31
+ maxRetries
32
+ enableNotifications
33
+ }
34
+ }
35
+ variables:
36
+ organizationId: "\{{organizationId}}"
37
+ initialValues:
38
+ fromQuery:
39
+ name: getConfiguration
40
+ path: configuration
41
+ append:
42
+ settingName: "Default Setting"
43
+ maxRetries: 3
44
+ enableNotifications: true
45
+ validationSchema:
46
+ settingName:
47
+ type: string
48
+ required: true
49
+ maxRetries:
50
+ type: number
51
+ required: true
52
+ enableNotifications:
53
+ type: boolean
54
+ onSubmit:
55
+ - type: mutation
56
+ props:
57
+ command: |
58
+ mutation SaveConfiguration($organizationId: Int!, $input: ConfigurationInput!) {
59
+ saveConfiguration(organizationId: $organizationId, input: $input) {
60
+ success
61
+ }
62
+ }
63
+ variables:
64
+ organizationId: "\{{organizationId}}"
65
+ input: "\{{formValues}}"
66
+ - type: notification
67
+ props:
68
+ message:
69
+ en-US: "{{displayName}} saved successfully"
70
+ severity: success
71
+ children:
72
+ - component: field
73
+ name: settingName
74
+ props:
75
+ label:
76
+ en-US: "Setting Name"
77
+ type: text
78
+ required: true
79
+ - component: field
80
+ name: maxRetries
81
+ props:
82
+ label:
83
+ en-US: "Max Retries"
84
+ type: number
85
+ - component: field
86
+ name: enableNotifications
87
+ props:
88
+ label:
89
+ en-US: "Enable Notifications"
90
+ type: checkbox
91
+
92
+ routes:
93
+ - name: configuration
94
+ path: /{{name}}
95
+ component: {{displayNameNoSpaces}}/Configuration
96
+ props:
97
+ title:
98
+ en-US: "{{displayName}}"
99
+
100
+ permissions:
101
+ - name: "{{displayNameNoSpaces}}/View"
102
+ displayName:
103
+ en-US: "View {{displayName}}"
104
+ roles:
105
+ - Admin
106
+ - name: "{{displayNameNoSpaces}}/Edit"
107
+ displayName:
108
+ en-US: "Edit {{displayName}}"
109
+ roles:
110
+ - Admin
@@ -0,0 +1,152 @@
1
+ # {{displayName}} Form Module
2
+ # Generated by cx-cli create module --template form
3
+
4
+ module:
5
+ name: "{{displayNameNoSpaces}}"
6
+ appModuleId: "{{uuid}}"
7
+ displayName:
8
+ en-US: "{{displayName}}"
9
+ description:
10
+ en-US: "{{displayName}} module"
11
+ application: CargoXplorer
12
+ fileName: "{{fileName}}"
13
+
14
+ components:
15
+ - name: {{displayNameNoSpaces}}/Detail
16
+ displayName:
17
+ en-US: "{{displayName}} Detail"
18
+ platforms:
19
+ - web
20
+ layout:
21
+ component: form
22
+ name: {{name}}Form
23
+ props:
24
+ title:
25
+ en-US: "{{displayName}}"
26
+ toolbar:
27
+ - component: button
28
+ name: saveBtn
29
+ props:
30
+ label:
31
+ en-US: "Save"
32
+ icon: check
33
+ options:
34
+ type: submit
35
+ variant: primary
36
+ - component: button
37
+ name: cancelBtn
38
+ props:
39
+ label:
40
+ en-US: "Cancel"
41
+ icon: x
42
+ options:
43
+ variant: secondary
44
+ onClick:
45
+ - navigateBack:
46
+ fallback: "/{{name}}"
47
+ dirtyGuard:
48
+ enabled: true
49
+ title:
50
+ en-US: "Unsaved Changes"
51
+ message:
52
+ en-US: "You have unsaved changes. Are you sure you want to leave?"
53
+ confirmLabel:
54
+ en-US: "Leave"
55
+ cancelLabel:
56
+ en-US: "Stay"
57
+ queries:
58
+ - name: get{{displayNameNoSpaces}}
59
+ query:
60
+ command: |
61
+ query Get{{displayNameNoSpaces}}($organizationId: Int!, $id: Int!) {
62
+ {{name}}(organizationId: $organizationId, id: $id) {
63
+ id
64
+ name
65
+ description
66
+ }
67
+ }
68
+ variables:
69
+ organizationId: '\{{number organizationId}}'
70
+ id: '\{{number id}}'
71
+ initialValues:
72
+ fromQuery:
73
+ name: get{{displayNameNoSpaces}}
74
+ path: {{name}}
75
+ validationSchema:
76
+ name:
77
+ type: string
78
+ required: true
79
+ description:
80
+ type: string
81
+ onSubmit:
82
+ - mutation:
83
+ command: |
84
+ mutation Save{{displayNameNoSpaces}}($organizationId: Int!, $input: {{displayNameNoSpaces}}Input!) {
85
+ save{{displayNameNoSpaces}}(organizationId: $organizationId, input: $input) {
86
+ id
87
+ }
88
+ }
89
+ variables:
90
+ organizationId: '\{{number organizationId}}'
91
+ input: '\{{form}}'
92
+ onSuccess:
93
+ - notification:
94
+ message:
95
+ en-US: "{{displayName}} saved successfully"
96
+ type: success
97
+ - navigateBack:
98
+ fallback: "/{{name}}"
99
+ onError:
100
+ - notification:
101
+ message:
102
+ en-US: "Failed to save {{displayName}}"
103
+ type: error
104
+ children:
105
+ - component: field
106
+ name: name
107
+ props:
108
+ label:
109
+ en-US: "Name"
110
+ type: text
111
+ required: true
112
+ - component: field
113
+ name: description
114
+ props:
115
+ label:
116
+ en-US: "Description"
117
+ type: textarea
118
+
119
+ routes:
120
+ - name: detail
121
+ path: /{{name}}/:id
122
+ component: {{displayNameNoSpaces}}/Detail
123
+ props:
124
+ title:
125
+ en-US: "{{displayName}}"
126
+
127
+ entities:
128
+ - name: {{displayNameNoSpaces}}
129
+ entityKind: Other
130
+ displayName:
131
+ en-US: "{{displayName}}"
132
+ fields:
133
+ - name: name
134
+ displayName:
135
+ en-US: "Name"
136
+ fieldType: text
137
+ - name: description
138
+ displayName:
139
+ en-US: "Description"
140
+ fieldType: text
141
+
142
+ permissions:
143
+ - name: "{{displayNameNoSpaces}}/Read"
144
+ displayName:
145
+ en-US: "Read {{displayName}}"
146
+ roles:
147
+ - Admin
148
+ - name: "{{displayNameNoSpaces}}/Update"
149
+ displayName:
150
+ en-US: "Update {{displayName}}"
151
+ roles:
152
+ - Admin