@cxtms/cx-schema 1.7.17 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/.claude/skills/cx-core/SKILL.md +38 -20
  2. package/.claude/skills/cx-core/ref-entity-accounting.md +0 -7
  3. package/.claude/skills/cx-core/ref-entity-commodity.md +0 -12
  4. package/.claude/skills/cx-core/ref-entity-contact.md +0 -10
  5. package/.claude/skills/cx-core/ref-entity-geography.md +0 -9
  6. package/.claude/skills/cx-core/ref-entity-notification.md +85 -0
  7. package/.claude/skills/cx-core/ref-entity-order-sub.md +7 -7
  8. package/.claude/skills/cx-core/ref-entity-order.md +1 -14
  9. package/.claude/skills/cx-core/ref-entity-rate.md +0 -8
  10. package/.claude/skills/cx-core/ref-entity-shared.md +20 -9
  11. package/.claude/skills/cx-core/ref-entity-warehouse.md +0 -5
  12. package/.claude/skills/cx-module/SKILL.md +28 -103
  13. package/.claude/skills/cx-module/ref-components-data.md +0 -7
  14. package/.claude/skills/cx-module/ref-components-display.md +0 -13
  15. package/.claude/skills/cx-module/ref-components-forms.md +1 -6
  16. package/.claude/skills/cx-module/ref-components-interactive.md +0 -11
  17. package/.claude/skills/cx-module/ref-components-layout.md +0 -11
  18. package/.claude/skills/cx-module/ref-components-specialized.md +0 -50
  19. package/.claude/skills/cx-workflow/SKILL.md +21 -130
  20. package/.claude/skills/cx-workflow/ref-communication.md +0 -8
  21. package/.claude/skills/cx-workflow/ref-entity.md +62 -17
  22. package/.claude/skills/cx-workflow/ref-expressions.md +272 -0
  23. package/.claude/skills/cx-workflow/ref-flow.md +5 -11
  24. package/.claude/skills/cx-workflow/ref-other.md +33 -14
  25. package/.claude/skills/cx-workflow/ref-utilities.md +111 -14
  26. package/README.md +34 -34
  27. package/dist/cli.js +166 -2394
  28. package/dist/cli.js.map +1 -1
  29. package/dist/types.d.ts +0 -2
  30. package/dist/types.d.ts.map +1 -1
  31. package/dist/validator.d.ts +0 -8
  32. package/dist/validator.d.ts.map +1 -1
  33. package/dist/validator.js +2 -54
  34. package/dist/validator.js.map +1 -1
  35. package/dist/workflowValidator.d.ts +0 -4
  36. package/dist/workflowValidator.d.ts.map +1 -1
  37. package/dist/workflowValidator.js +2 -28
  38. package/dist/workflowValidator.js.map +1 -1
  39. package/package.json +2 -3
  40. package/schemas/actions/all.json +2 -1
  41. package/schemas/actions/clipboard.json +46 -0
  42. package/schemas/components/appComponent.json +0 -8
  43. package/schemas/components/module.json +2 -31
  44. package/schemas/components/timelineGrid.json +0 -4
  45. package/schemas/schemas.json +0 -12
  46. package/schemas/workflows/flow/entity.json +19 -0
  47. package/schemas/workflows/tasks/all.json +9 -0
  48. package/schemas/workflows/tasks/authentication.json +12 -26
  49. package/schemas/workflows/tasks/edi.json +93 -1
  50. package/schemas/workflows/tasks/httpRequest.json +4 -0
  51. package/schemas/workflows/tasks/order.json +45 -0
  52. package/schemas/workflows/tasks/resolve-timezone.json +65 -0
  53. package/schemas/workflows/tasks/transmission.json +185 -0
  54. package/schemas/workflows/tasks/unzip-file.json +68 -0
  55. package/schemas/workflows/variable.json +5 -1
  56. package/schemas/workflows/workflow.json +4 -0
  57. package/scripts/postinstall.js +1 -1
  58. package/templates/module-configuration.yaml +89 -23
  59. package/templates/module-form.yaml +3 -3
  60. package/templates/module-grid.yaml +3 -3
  61. package/templates/module-select.yaml +3 -3
  62. package/templates/module.yaml +2 -3
  63. package/templates/workflow-api-tracking.yaml +1 -1
  64. package/templates/workflow-basic.yaml +1 -1
  65. package/templates/workflow-document.yaml +1 -1
  66. package/templates/workflow-entity-trigger.yaml +1 -1
  67. package/templates/workflow-ftp-edi.yaml +1 -1
  68. package/templates/workflow-ftp-tracking.yaml +1 -1
  69. package/templates/workflow-mcp-tool.yaml +1 -1
  70. package/templates/workflow-public-api.yaml +1 -1
  71. package/templates/workflow-scheduled.yaml +1 -1
  72. package/templates/workflow-utility.yaml +1 -1
  73. package/templates/workflow-webhook.yaml +1 -1
  74. package/templates/workflow.yaml +1 -1
  75. package/.claude/skills/cx-core/ref-cli-auth.md +0 -120
  76. package/.claude/skills/cx-core/ref-graphql-query.md +0 -320
  77. package/.claude/skills/cx-workflow/ref-expressions-ncalc.md +0 -111
  78. package/.claude/skills/cx-workflow/ref-expressions-template.md +0 -148
@@ -0,0 +1,272 @@
1
+ # Variable References & Expressions
2
+
3
+ There are **two distinct syntaxes** for referencing variables, used in different contexts.
4
+
5
+ ## Template Expressions: `{{ path }}` (in step inputs)
6
+
7
+ Used in step `inputs` values. Resolves variable paths from scoped variables.
8
+
9
+ ```yaml
10
+ inputs:
11
+ orderId: "{{ inputs.orderId }}" # Simple reference
12
+ url: "{{ chopinConfig.baseUrl }}/api/v1" # String interpolation
13
+ order: "{{ Data.GetOrder.order }}" # Raw object (single {{ }})
14
+ name: "Order {{ Data.GetOrder.order.orderNumber }}" # String interpolation (multiple)
15
+ ```
16
+
17
+ **Key behavior**: A single `{{ path }}` returns the **raw object** (preserving type). Multiple `{{ }}` in a string returns string interpolation (each resolved value is `.ToString()`).
18
+
19
+ ### Type Converters (prefix in {{ }})
20
+
21
+ ```yaml
22
+ organizationId: "{{ int organizationId }}"
23
+ amount: "{{ decimal totalAmount }}"
24
+ isActive: "{{ bool isActive }}"
25
+ flag: "{{ boolOrFalse someFlag }}" # null -> false
26
+ flagOn: "{{ boolOrTrue someFlag }}" # null -> true
27
+ notes: "{{ emptyIfNull notes }}" # null -> ""
28
+ notes: "{{ nullIfEmpty notes }}" # "" or whitespace -> null
29
+ config: "{{ fromJson configJsonString }}" # JSON string -> dict/array
30
+ payload: "{{ toJson someObject }}" # object -> JSON string
31
+ name: "{{ trim value }}"
32
+ search: "{{ luceneString query }}" # escape & quote for Lucene
33
+ ```
34
+
35
+ | Converter | Returns | Null handling |
36
+ |-----------|---------|---------------|
37
+ | `string` | `string` | null. Reads `Stream` to string if value is Stream |
38
+ | `int` | `int` | Throws on null |
39
+ | `decimal` | `decimal` | Throws on null |
40
+ | `bool` | `bool` | Throws on null |
41
+ | `boolOrFalse` | `bool` | `false` if null |
42
+ | `boolOrTrue` | `bool` | `true` if null |
43
+ | `datetime` | `DateTime` | Throws on null |
44
+ | `emptyIfNull` | same type | `""` if null, `0` for int?, `0m` for decimal? |
45
+ | `nullIfEmpty` | same type | `null` if empty/whitespace string or empty collection |
46
+ | `luceneString` | `string` | null |
47
+ | `transliterate` | `string` | null (Unicode -> ASCII via Unidecode) |
48
+ | `transliterateUa` | `string` | null (Ukrainian-specific rules) |
49
+ | `fromJson` | `dict` or `array` | null. Empty string -> empty dict |
50
+ | `toJson` | `string` | `""` if null |
51
+ | `trim` | `string` | null |
52
+ | `toLocalTime` | `string` | null. Function-style: `{{ toLocalTime datePath 'timezoneId' 'format?' }}` |
53
+
54
+ ### Value Directives (in YAML input mappings)
55
+
56
+ **`expression`** -- Evaluate NCalc expression as a value:
57
+ ```yaml
58
+ amount:
59
+ expression: "[price] * [quantity]"
60
+ ```
61
+
62
+ **`coalesce`** -- First non-null value from a list:
63
+ ```yaml
64
+ displayName:
65
+ coalesce:
66
+ - "{{ customer.name? }}"
67
+ - "{{ customer.email? }}"
68
+ - "Unknown"
69
+ ```
70
+
71
+ **`foreach`** (value context) -- Transform collections inline:
72
+ ```yaml
73
+ commodities:
74
+ foreach: "sourceCommodities"
75
+ item: "item" # default: "item"
76
+ conditions: "[item.isActive] = true" # optional NCalc filter per item
77
+ continueOnError: false # optional, skip errors
78
+ mapping: # dict -> List<dict>, string -> List<object>
79
+ name: "{{ item.name }}"
80
+ quantity: "{{ item.qty }}"
81
+ ```
82
+
83
+ **`switch`** (value context) -- Value-based switch (case-insensitive match):
84
+ ```yaml
85
+ perLb:
86
+ switch: "{{ contact.commissionTier }}"
87
+ cases:
88
+ "tier1": "{{ rate.customValues.commission_per_lb_tier1 }}"
89
+ "tier2": "{{ rate.customValues.commission_per_lb_tier2 }}"
90
+ default: "0"
91
+ ```
92
+
93
+ **`extends`** -- Extend/merge an existing object or array:
94
+ ```yaml
95
+ orderData:
96
+ extends: "{{ existingOrder }}" # base object or array
97
+ defaultIfNull: {} # fallback if extends is null
98
+ mapping: # dict: merge overrides. array: append items
99
+ status: "Updated"
100
+ notes: "{{ newNotes }}"
101
+ ```
102
+
103
+ **`resolve`** -- Entity ID lookup by querying a GraphQL collection:
104
+ ```yaml
105
+ customerId:
106
+ resolve:
107
+ entity: "Contact" # Entity type (auto-pluralized for query)
108
+ filter: "name={{ customerName }}" # Lucene filter (template-parsed)
109
+ field: "contactId" # Field to return (default: <entity>Id)
110
+ ```
111
+ Results are batched and cached per unique `entity|filter|field` combination by `ResolvePreProcessor` before step execution. Cache misses return `null`. Useful inside `foreach` mappings where many items reference the same entity — only one query per unique filter value.
112
+
113
+ **`$raw`** -- Prevent template parsing (pass as-is):
114
+ ```yaml
115
+ template:
116
+ $raw: "This {{ won't }} be parsed"
117
+ ```
118
+
119
+ **`$eval`** -- Parse JSON string then evaluate as template:
120
+ ```yaml
121
+ dynamicConfig:
122
+ $eval: "{{ configJsonString }}"
123
+ ```
124
+
125
+ **`decrypt`** / **`encrypt`** -- AES-CBC encryption (optional key/IV, has defaults):
126
+ ```yaml
127
+ apiKey:
128
+ decrypt:
129
+ encryptedValue: "{{ encryptedApiKey }}"
130
+ key: "{{ encryptionKey }}" # optional Base64 AES key
131
+ initializationVector: "{{ iv }}" # optional Base64 IV
132
+ ```
133
+
134
+ ---
135
+
136
+ ## NCalc Expressions: `[variable]` (in conditions and expression directives)
137
+
138
+ Used in `conditions[].expression`, `switch` case `when`, and `expression:` value directives. Variables use **square bracket** `[name]` syntax.
139
+
140
+ ```yaml
141
+ conditions:
142
+ - expression: "[status] = 'Active' AND [amount] > 100"
143
+ - expression: "isNullOrEmpty([Data.GetOrder.order?]) = false"
144
+ - expression: "any([changes], [each.key] = 'Status') = true"
145
+ ```
146
+
147
+ **Parameter resolution rules**:
148
+ - Empty strings are converted to `null` (so `""` is treated as no value)
149
+ - Numeric strings are auto-converted to `decimal` when needed (e.g., `[price] > 100` works even if price is the string `"150"`)
150
+ - Dot paths resolve deep: `[Activity.Step.output.nested.field]`
151
+ - Optional suffix `?` prevents errors: `[order.customer?.name?]`
152
+
153
+ ### Operators
154
+
155
+ | Type | Operators |
156
+ |------|-----------|
157
+ | Comparison | `=`, `!=`, `<>`, `<`, `>`, `<=`, `>=` |
158
+ | Logical | `AND`, `OR`, `NOT` (also `&&`, `\|\|`, `!`) |
159
+ | Arithmetic | `+`, `-`, `*`, `/`, `%` |
160
+ | Ternary | `if(condition, trueVal, falseVal)` |
161
+ | Membership | `in(value, val1, val2, ...)` |
162
+
163
+ ### Iterator Variables
164
+
165
+ Functions use two iterator variable names:
166
+ - **`[each.*]`** -- used by: `any`, `all`, `sum`, `join` (3-arg)
167
+ - **`[item.*]`** -- used by: `first`, `last`, `groupBy`
168
+
169
+ ### Collection Functions
170
+
171
+ | Function | Description |
172
+ |----------|-------------|
173
+ | `any([items], [each.prop] = 'val')` | True if any item matches expression. Without expression: checks if collection contains the value |
174
+ | `all([items], [each.prop] > 0)` | True if all items match. Returns `false` for null/empty collections |
175
+ | `count([items])` | Count items in list or JToken. Returns `0` for non-collections |
176
+ | `sum([items], [each.amount])` | Sum values as `decimal`. Optional `[each.*]` accessor. Skips nulls |
177
+ | `first([items])` or `first([items], [item.name])` | First item or evaluate expression on first item. Returns `""` if empty |
178
+ | `last([items])` or `last([items], [item.name])` | Last item or evaluate expression on last item. Returns `""` if empty |
179
+ | `distinct([items])` | Remove duplicates. Uses deep comparison for dictionaries |
180
+ | `reverse([items])` | Reverse collection or string |
181
+ | `contains([source], 'needle')` | String contains, JArray contains, list contains, or dict key/value contains |
182
+ | `removeEmpty([items])` | Remove null and whitespace-only items |
183
+ | `concat([list1], [list2], ...)` | Concatenate multiple collections into flat list. Variadic args. Skips nulls |
184
+ | `groupBy([items], [item.cat])` | Group by one or more key expressions. Returns `[{key, items}]`. Multi-key: keys joined with `\|` |
185
+ | `join([items], [each.name], ',')` | Join collection with `[each.*]` accessor and separator (3-arg) |
186
+ | `join([items], ',')` | Join collection directly with separator (2-arg) |
187
+ | `split([str], ' ')` | Split string by first character of separator. Returns `List<string>` |
188
+ | `elementAt([items], 0)` | Get element at index (zero-based) from list |
189
+
190
+ ### String Functions
191
+
192
+ | Function | Description |
193
+ |----------|-------------|
194
+ | `isNullOrEmpty([var])` | True if null, empty string, or empty list |
195
+ | `length([var])` | String length or collection count. `0` for null strings and non-collections |
196
+ | `lower([name])` / `upper([code])` | Case conversion. Handles string, JToken, any `.ToString()` |
197
+ | `left([code], 3)` / `right([code], 3)` | Left/right N characters. Returns full string if shorter than N |
198
+ | `substring([str], 0, 5)` | Extract substring starting at position for given length |
199
+ | `replace([str], 'old', 'new')` | String replacement. Returns null if any arg is null |
200
+ | `trim([value])` | Trim whitespace. Returns `""` for null |
201
+ | `format('{0}-{1}', [prefix], [id])` | String.Format style. Variadic args. Returns null if format is null |
202
+ | `base64([value])` / `fromBase64([encoded])` | Base64 encode/decode. Handles string, byte[], JToken |
203
+ | `bool([value])` | Convert to boolean: null->`false`, empty string->`false`, "true"/"false"->parsed, non-zero number->`true`, any object->`true` |
204
+ | `transliterate([value])` | Unicode to ASCII (Unidecode). Returns `""` for null |
205
+ | `transliterateUa([value])` | Ukrainian-specific transliteration. Returns `""` for null |
206
+ | `parseAddress([address])` | Parse address -> `{StreetNumber, StreetName}`. Handles US and EU formats |
207
+
208
+ ### Date Functions
209
+
210
+ | Function | Description |
211
+ |----------|-------------|
212
+ | `parseDate([str])` | Parse date string to DateTime. Supports common formats (ISO, US, etc.) |
213
+ | `now()` | Current UTC `DateTime` |
214
+ | `now('yyyy-MM-dd', 'en-US')` | Formatted current time as string |
215
+ | `addDays([date], 30)` | Add days (decimal, can be negative). Accepts DateTime, DateTimeOffset, string |
216
+ | `addHours([date], 2)` | Add hours (decimal, can be negative). Same type handling |
217
+ | `formatDate([date], 'dd/MM/yyyy', 'en-US')` | Format date with culture. Accepts DateTime or string |
218
+ | `dateFromUnix([unixTime])` | Unix timestamp (seconds) -> `DateTimeOffset`. Accepts int, long, decimal, string |
219
+ | `dateToUtc([date])` or `dateToUtc([date], 'en-US')` | Convert to UTC. Optional culture for string parsing |
220
+ | `toLocalTime([date], 'America/Chicago')` | Convert UTC date to local time in IANA timezone. Returns `DateTimeOffset`. Null-safe |
221
+
222
+ ### Business Date Math (in Lucene filter expressions)
223
+
224
+ The filter engine (`FilterBy`) supports business-aware date math units in Lucene date expressions:
225
+
226
+ | Unit | Aliases | Description |
227
+ |------|---------|-------------|
228
+ | `BHOUR` | `BHOURS` | Add/subtract business hours (respects weekly schedule + holidays) |
229
+ | `BDAY` | `BDAYS` | Add/subtract business days (skips non-working days) |
230
+
231
+ **Usage**: These units are used in **Lucene filter strings** (not NCalc expressions). They require an `IBusinessDateMathResolver` and are resolved via the organization's business calendar.
232
+
233
+ ```
234
+ dueDate: [NOW TO NOW+3BDAYS]
235
+ pickupDate: [* TO NOW-8BHOURS]
236
+ ```
237
+
238
+ The resolver loads `CalendarBusinessHour` (weekly schedule) and `CalendarAvailabilityBlock` (holidays) for the organization's `business`-type calendar, then walks through working time segments to compute the target date.
239
+
240
+ ### Math Functions (NCalc built-in)
241
+
242
+ `Abs(x)`, `Ceiling(x)`, `Floor(x)`, `Round(x, decimals)`, `Min(x, y)`, `Max(x, y)`, `Pow(x, y)`, `Sqrt(x)`, `Truncate(x)`
243
+
244
+ Custom: `ceiling([value])` -- same as `Ceiling` but handles type conversion to double.
245
+
246
+ ### Domain Functions
247
+
248
+ | Function | Description |
249
+ |----------|-------------|
250
+ | `convertWeight([weight], 'Kg', 'Lb')` | Weight unit conversion. Returns `decimal` rounded to 5 places |
251
+ | `convertDimension([length], 'Cm', 'In')` | Dimension unit conversion. Returns `decimal` rounded to 3 places |
252
+
253
+ ---
254
+
255
+ ## Property Path Syntax (in collection, mapping, variable paths)
256
+
257
+ Used in `collection:` (foreach), `mapping:` (outputs), and variable resolution.
258
+
259
+ | Pattern | Description | Example |
260
+ |---------|-------------|---------|
261
+ | `a.b.c` | Dot-separated nested path | `order.customer.name` |
262
+ | `prop?` | Optional access (null if missing) | `order.customer?.name?` |
263
+ | `list[0]` | Array index | `items[0]` |
264
+ | `list[^1]` | Index from end (last item) | `items[^1]` |
265
+ | `list[*]` | Flatten/wildcard (all items) | `containers[*].commodities` |
266
+ | `list[**]` | Recursive flatten (all depths) | `containerCommodities[**]` |
267
+ | `list[-1]` | Depth filter (leaves only) | `tree[**][-1]` |
268
+ | `list[condition]` | Filter by condition | `items[status=Active]` |
269
+ | `dict['key']` | Dictionary key access | `customValues['myField']` |
270
+ | `list[*].{f1 f2}` | Field selector (projection) | `items[*].{name description}` |
271
+ | `list[*].{alias:source}` | Field selector with alias | `items[*].{id:commodityId}` |
272
+ | `list[*].{alias:_.parent}` | Field selector referencing parent | `items[*].{parentId:_.orderId}` |
@@ -1,12 +1,5 @@
1
1
  # Flow Workflow YAML Reference
2
2
 
3
- ## Contents
4
- - Flow top-level structure (workflowType, entity, states, transitions, aggregations)
5
- - Flow entity section (entity name, type, includes, query)
6
- - Flow states section (initial, final, parent hierarchy, onEnter/onExit steps)
7
- - Flow transitions section (manual, auto, event triggers; from/to states; conditions)
8
- - Flow aggregations section (reusable collection expressions: all, any, sum, count)
9
-
10
3
  Flow workflows are declarative state machines for entity lifecycle management. Use `workflowType: Flow` in the workflow section.
11
4
 
12
5
  ## Top-Level Structure
@@ -152,10 +145,11 @@ transitions:
152
145
 
153
146
  ### Execution Order
154
147
  1. Validate transition from current state
155
- 2. Execute `onExit` steps (from source state)
156
- 3. Execute transition `steps`
157
- 4. Update entity status
158
- 5. Execute `onEnter` steps (on target state)
148
+ 2. Evaluate conditions (all must pass)
149
+ 3. Execute `onExit` steps (from source state)
150
+ 4. Execute transition `steps` (step inputs merged + template expressions resolved)
151
+ 5. Update entity status
152
+ 6. Execute `onEnter` steps (on target state)
159
153
 
160
154
  ## Aggregations Section
161
155
 
@@ -1,14 +1,5 @@
1
1
  # Other Tasks Reference
2
2
 
3
- ## Contents
4
- - User & Auth tasks (User CRUD, verification codes, OAuth2 authentication)
5
- - Caching tasks (SetCache and GetCache for in-memory key-value storage)
6
- - EDI & Structured File Parsing tasks (EDI/Parse for X12/EDIFACT, StructuredFile/Parse)
7
- - Flow/Transition task (trigger state machine transitions programmatically)
8
- - Note tasks (Create, Update, Delete, NoteThread/Rename, bulk Import/Export)
9
- - AppModule tasks (Create, Update, Delete app modules)
10
- - ActionEvent/Create task (trigger UI notifications or webhooks)
11
-
12
3
  ## User & Auth
13
4
 
14
5
  | Task | Description |
@@ -64,20 +55,48 @@
64
55
 
65
56
  | Task | Description |
66
57
  |------|-------------|
67
- | `EDI/Parse` | Parse EDI documents (X12, EDIFACT) |
58
+ | `X12/Parse` | Parse X12 EDI documents (850, 856) |
59
+ | `EDIFACT/Parse` | Parse EDIFACT messages (IFTMIN) |
60
+ | `EDIFACT/Generate` | Generate EDIFACT messages (IFTMIN) |
61
+ | `EDI/Parse` | **Deprecated** — alias for X12/Parse, use `X12/Parse` instead |
68
62
  | `StructuredFile/Parse` | Parse structured files |
69
63
 
70
64
  ```yaml
71
- - task: "EDI/Parse@1"
72
- name: ParseEdi
65
+ - task: "X12/Parse@1"
66
+ name: ParseX12
73
67
  inputs:
74
- content: "{{ Transfer.Download.content }}"
75
- format: "X12"
68
+ ediData: "{{ Transfer.Download.content }}"
69
+ transactionSet: "850"
70
+ validateSchema: true
76
71
  outputs:
77
72
  - name: parsed
78
73
  mapping: "document"
79
74
  ```
80
75
 
76
+ ```yaml
77
+ - task: "EDIFACT/Parse@1"
78
+ name: ParseEdifact
79
+ inputs:
80
+ edifactData: "{{ Transfer.Download.content }}"
81
+ messageType: "IFTMIN"
82
+ outputs:
83
+ - name: parsed
84
+ mapping: "document"
85
+ ```
86
+
87
+ ```yaml
88
+ - task: "EDIFACT/Generate@1"
89
+ name: GenerateEdifact
90
+ inputs:
91
+ messageType: "IFTMIN"
92
+ data: "{{ shipmentData }}"
93
+ outputs:
94
+ - name: edifactData
95
+ mapping: "edifactData"
96
+ - name: messageType
97
+ mapping: "messageType"
98
+ ```
99
+
81
100
  ## Flow
82
101
 
83
102
  | Task | Description |
@@ -1,17 +1,5 @@
1
1
  # Utilities Tasks Reference
2
2
 
3
- ## Contents
4
- - Available utility tasks (summary table of all Utilities/* tasks)
5
- - SetVariable task (set variables in activity and global scope)
6
- - Log task (log variables and messages to workflow logger)
7
- - Error task (throw workflow error to halt execution)
8
- - HttpRequest task (HTTP calls to external APIs with action events)
9
- - Map task (extract and reshape data into new variables)
10
- - Template task (Handlebars template rendering)
11
- - CsvParse task (parse CSV content into structured data)
12
- - Export task (export data to file format)
13
- - Import task (import data from file content)
14
-
15
3
  ## Available Tasks
16
4
 
17
5
  | Task | Description |
@@ -28,6 +16,86 @@
28
16
  | `Utilities/MoveFile@1` | Move file |
29
17
  | `Utilities/ValidateReCaptcha` | Validate reCAPTCHA |
30
18
  | `Utilities/ValidateHMAC` | Validate HMAC signatures |
19
+ | `Utilities/UnzipFile@1` | Extract files from ZIP archive (local path or URL) |
20
+ | `Utilities/ResolveTimezone@1` | Resolve IANA timezone and UTC offset from lat/lng coordinates |
21
+
22
+ ## UnzipFile@1
23
+
24
+ Extracts files from a ZIP archive. Accepts a local file path (from `saveToFile` or previous step) or a URL (`file://`, `http://`, `https://`). Files are extracted to a workflow-scoped temp directory with auto-cleanup.
25
+
26
+ ```yaml
27
+ - task: "Utilities/UnzipFile@1"
28
+ name: ExtractArchive
29
+ inputs:
30
+ filePath: "{{ Main?.DownloadArchive?.result?.FilePath? }}"
31
+ filePattern: "*.csv"
32
+ outputs:
33
+ - name: files
34
+ mapping: "Files?"
35
+ - name: count
36
+ mapping: "Count?"
37
+ ```
38
+
39
+ **Inputs:** `filePath` (string, local path) OR `fileUrl` (string, URL — `file://`, `http://`, `https://`). Optional: `filePattern` (glob pattern, e.g. `*.csv`, `data_*.json`).
40
+ **Outputs:** `Files` (string[] — full paths to extracted files), `Count` (int — number of matched files).
41
+ Provide one of `filePath` or `fileUrl`. Common pattern: HttpRequest with `saveToFile: true` → UnzipFile with `filePath`.
42
+
43
+ ```yaml
44
+ # Download + unzip + import pipeline
45
+ - task: "Utilities/HttpRequest@1"
46
+ name: Download
47
+ inputs:
48
+ url: "{{ downloadUrl }}"
49
+ method: GET
50
+ saveToFile: true
51
+ outputs:
52
+ - name: result
53
+ mapping: "response?"
54
+
55
+ - task: "Utilities/UnzipFile@1"
56
+ name: Unzip
57
+ inputs:
58
+ filePath: "{{ Main?.Download?.result?.FilePath? }}"
59
+ filePattern: "*.csv"
60
+ outputs:
61
+ - name: files
62
+ mapping: "Files?"
63
+
64
+ - task: foreach
65
+ name: ProcessFiles
66
+ collection: "Main?.Unzip?.files?"
67
+ steps:
68
+ - task: "Utilities/Import@1"
69
+ name: ImportFile
70
+ inputs:
71
+ fileUrl: "file://{{ item }}"
72
+ format: "csv"
73
+ ```
74
+
75
+ ---
76
+
77
+ ## ResolveTimezone@1
78
+
79
+ Resolves IANA timezone ID and current UTC offset from geographic coordinates using offline boundary lookup (GeoTimeZone).
80
+
81
+ ```yaml
82
+ - task: "Utilities/ResolveTimezone@1"
83
+ name: ResolveTimezone
84
+ inputs:
85
+ latitude: "{{ postalCode.location.y }}"
86
+ longitude: "{{ postalCode.location.x }}"
87
+ outputs:
88
+ - name: tz
89
+ mapping: "timezoneId?"
90
+ - name: offset
91
+ mapping: "utcOffset?"
92
+ ```
93
+
94
+ **Inputs:** `latitude` (double/string, required), `longitude` (double/string, required)
95
+ **Outputs:** `timezoneId` (string, e.g. `America/Chicago`), `utcOffset` (double, e.g. `-5`)
96
+ Throws `WorkflowRuntimeException` if lat/lng missing or unparseable.
97
+
98
+ ---
31
99
 
32
100
  ## SetVariable@1
33
101
 
@@ -103,6 +171,21 @@ Performs HTTP requests to external APIs.
103
171
 
104
172
  Response available at `ActivityName?.CallApi?.result?`.
105
173
 
174
+ **`saveToFile` mode**: When `saveToFile: true`, the response body is saved to a temp file instead of being returned in memory. The response object changes to `{ StatusCode, Headers, FilePath }`. Use this for large file downloads, then pass `FilePath` to downstream tasks like `UnzipFile` or `Import`.
175
+
176
+ ```yaml
177
+ - task: "Utilities/HttpRequest@1"
178
+ name: DownloadArchive
179
+ inputs:
180
+ url: "{{ downloadUrl }}"
181
+ method: GET
182
+ saveToFile: true
183
+ outputs:
184
+ - name: result
185
+ mapping: "response?"
186
+ # result.FilePath contains the temp file path
187
+ ```
188
+
106
189
  **Action events**: When an HTTP request operates on a specific entity (e.g., sending parcel info for an order), enable `actionEvents` in the inputs so the system can track and notify about the request. Include `eventDataExt` with the entity ID to link the event to the entity.
107
190
 
108
191
  ```yaml
@@ -159,7 +242,7 @@ Renders a Handlebars template string with data.
159
242
 
160
243
  ## CsvParse@1
161
244
 
162
- Parses CSV content into structured data.
245
+ Parses CSV content into structured data. CSV headers are automatically trimmed of whitespace, BOM characters, and other special characters to ensure reliable column matching.
163
246
 
164
247
  ```yaml
165
248
  - task: "Utilities/CsvParse@1"
@@ -190,7 +273,7 @@ Exports data to file format.
190
273
 
191
274
  ## Import@1
192
275
 
193
- Imports data from file content.
276
+ Imports data from file content or URL. Supports `file://` URLs for local files (e.g. from UnzipFile output).
194
277
 
195
278
  ```yaml
196
279
  - task: "Utilities/Import@1"
@@ -202,3 +285,17 @@ Imports data from file content.
202
285
  - name: data
203
286
  mapping: "data?"
204
287
  ```
288
+
289
+ ```yaml
290
+ # Import from local file (e.g. extracted from ZIP)
291
+ - task: "Utilities/Import@1"
292
+ name: ImportLocalFile
293
+ inputs:
294
+ fileUrl: "file://{{ localFilePath }}"
295
+ format: "csv"
296
+ outputs:
297
+ - name: data
298
+ mapping: "data?"
299
+ ```
300
+
301
+ **`file://` URL support**: Import, Order/Import, PostalCodes/Import, and Notes/Import all accept `file://` URLs via UrlStreamHelper. This enables pipeline patterns: HttpRequest (saveToFile) → UnzipFile → Import (file://).
package/README.md CHANGED
@@ -29,19 +29,19 @@ npm install @cxtms/cx-schema
29
29
 
30
30
  ```bash
31
31
  # Initialize a new project
32
- npx cxtms init
32
+ npx cx-cli init
33
33
 
34
34
  # Create a new module
35
- npx cxtms create module orders
35
+ npx cx-cli create module orders
36
36
 
37
37
  # Create a new workflow
38
- npx cxtms create workflow invoice-processor
38
+ npx cx-cli create workflow invoice-processor
39
39
 
40
40
  # Validate files
41
- npx cxtms modules/*.yaml workflows/*.yaml
41
+ npx cx-cli modules/*.yaml workflows/*.yaml
42
42
 
43
43
  # Generate validation report
44
- npx cxtms report modules/*.yaml --report report.html
44
+ npx cx-cli report modules/*.yaml --report report.html
45
45
  ```
46
46
 
47
47
  ## CLI Commands
@@ -51,14 +51,14 @@ npx cxtms report modules/*.yaml --report report.html
51
51
  Validate YAML file(s) against JSON Schema definitions.
52
52
 
53
53
  ```bash
54
- cxtms [files...]
55
- cxtms validate [files...]
54
+ cx-cli [files...]
55
+ cx-cli validate [files...]
56
56
 
57
57
  # Examples
58
- cxtms modules/orders-module.yaml
59
- cxtms modules/*.yaml workflows/*.yaml
60
- cxtms --verbose modules/orders-module.yaml
61
- cxtms --format json modules/orders-module.yaml
58
+ cx-cli modules/orders-module.yaml
59
+ cx-cli modules/*.yaml workflows/*.yaml
60
+ cx-cli --verbose modules/orders-module.yaml
61
+ cx-cli --format json modules/orders-module.yaml
62
62
  ```
63
63
 
64
64
  ### init
@@ -66,7 +66,7 @@ cxtms --format json modules/orders-module.yaml
66
66
  Initialize a new CX project with configuration files.
67
67
 
68
68
  ```bash
69
- cxtms init
69
+ cx-cli init
70
70
  ```
71
71
 
72
72
  Creates:
@@ -81,11 +81,11 @@ Creates:
81
81
  Create a new module or workflow from template.
82
82
 
83
83
  ```bash
84
- cxtms create <type> <name>
84
+ cx-cli create <type> <name>
85
85
 
86
86
  # Examples
87
- cxtms create module orders
88
- cxtms create workflow invoice-generator
87
+ cx-cli create module orders
88
+ cx-cli create workflow invoice-generator
89
89
  ```
90
90
 
91
91
  Generated files include:
@@ -98,12 +98,12 @@ Generated files include:
98
98
  Generate validation report for multiple files.
99
99
 
100
100
  ```bash
101
- cxtms report [files...] --report <output-file>
101
+ cx-cli report [files...] --report <output-file>
102
102
 
103
103
  # Examples
104
- cxtms report modules/*.yaml --report report.html
105
- cxtms report workflows/*.yaml --report report.md
106
- cxtms report modules/*.yaml workflows/*.yaml --report results.json
104
+ cx-cli report modules/*.yaml --report report.html
105
+ cx-cli report workflows/*.yaml --report report.md
106
+ cx-cli report modules/*.yaml workflows/*.yaml --report results.json
107
107
  ```
108
108
 
109
109
  Report formats (auto-detected from extension):
@@ -116,13 +116,13 @@ Report formats (auto-detected from extension):
116
116
  Display the JSON Schema definition for a component or task.
117
117
 
118
118
  ```bash
119
- cxtms schema <name>
119
+ cx-cli schema <name>
120
120
 
121
121
  # Examples
122
- cxtms schema form
123
- cxtms schema dataGrid
124
- cxtms schema workflow
125
- cxtms schema foreach
122
+ cx-cli schema form
123
+ cx-cli schema dataGrid
124
+ cx-cli schema workflow
125
+ cx-cli schema foreach
126
126
  ```
127
127
 
128
128
  ### example
@@ -130,11 +130,11 @@ cxtms schema foreach
130
130
  Show example YAML for a component or task.
131
131
 
132
132
  ```bash
133
- cxtms example <name>
133
+ cx-cli example <name>
134
134
 
135
135
  # Examples
136
- cxtms example form
137
- cxtms example workflow
136
+ cx-cli example form
137
+ cx-cli example workflow
138
138
  ```
139
139
 
140
140
  ### list
@@ -142,9 +142,9 @@ cxtms example workflow
142
142
  List all available schemas for validation.
143
143
 
144
144
  ```bash
145
- cxtms list
146
- cxtms list --type module
147
- cxtms list --type workflow
145
+ cx-cli list
146
+ cx-cli list --type module
147
+ cx-cli list --type workflow
148
148
  ```
149
149
 
150
150
  ## CLI Options
@@ -298,11 +298,11 @@ Generated workflows include:
298
298
  ```yaml
299
299
  - name: Validate YAML files
300
300
  run: |
301
- npx cxtms --format compact modules/*.yaml workflows/*.yaml
301
+ npx cx-cli --format compact modules/*.yaml workflows/*.yaml
302
302
 
303
303
  - name: Generate validation report
304
304
  run: |
305
- npx cxtms report modules/*.yaml workflows/*.yaml --report validation-report.html
305
+ npx cx-cli report modules/*.yaml workflows/*.yaml --report validation-report.html
306
306
 
307
307
  - name: Upload report
308
308
  uses: actions/upload-artifact@v3
@@ -316,7 +316,7 @@ Generated workflows include:
316
316
  ```yaml
317
317
  validate:
318
318
  script:
319
- - npx cxtms --format json modules/*.yaml > validation-results.json
319
+ - npx cx-cli --format json modules/*.yaml > validation-results.json
320
320
  artifacts:
321
321
  paths:
322
322
  - validation-results.json
@@ -331,7 +331,7 @@ validate:
331
331
  staged_files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(yaml|yml)$')
332
332
 
333
333
  if [ -n "$staged_files" ]; then
334
- npx cxtms --format compact $staged_files
334
+ npx cx-cli --format compact $staged_files
335
335
  if [ $? -ne 0 ]; then
336
336
  echo "Validation failed. Please fix errors before committing."
337
337
  exit 1