@cxtms/cx-schema 1.9.21 → 1.9.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cxtms/cx-schema",
3
- "version": "1.9.21",
3
+ "version": "1.9.23",
4
4
  "description": "Schema validation package for CXTMS YAML modules",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,6 +17,10 @@
17
17
 
18
18
  Full-featured data table with views, filtering, sorting, pagination, and row actions.
19
19
 
20
+ **Responsive layout:**
21
+ - **Toolbar**: ViewSelector and search input stack full-width on mobile (`xs`), then collapse to auto-width on `sm` and above. Search input enforces a `20ch` minimum width on `sm+`.
22
+ - **Filters**: Filter inputs use a responsive grid — 1 per row on `xs`/`sm`, 2 per row on `md`, 3 per row on `lg`, 4 per row on `xl`.
23
+
20
24
  **Props:**
21
25
  | Prop | Type | Description |
22
26
  |------|------|-------------|
@@ -4,8 +4,8 @@
4
4
  - NCalc expression syntax `[variable]` (in conditions and expression directives)
5
5
  - Operators (comparison, logical, arithmetic, ternary, membership)
6
6
  - Iterator variables (`[each.*]` and `[item.*]`)
7
- - Collection functions (any, all, count, sum, first, last, distinct, groupBy, join, etc.)
8
- - String functions (isNullOrEmpty, length, lower, upper, replace, format, base64, etc.)
7
+ - Collection functions (any, all, count, sum, first, last, distinct, select, zip, groupBy, join, etc.)
8
+ - String functions (isNullOrEmpty, length, lower, upper, replace, format, base64, coalesce, etc.)
9
9
  - Date functions (now, parseDate, addDays, formatDate, dateFromUnix, etc.)
10
10
  - Math functions (Abs, Ceiling, Floor, Round, Min, Max, etc.)
11
11
  - Domain functions (convertWeight, convertDimension)
@@ -28,6 +28,7 @@ conditions:
28
28
  - Numeric strings are auto-converted to `decimal` when needed (e.g., `[price] > 100` works even if price is the string `"150"`)
29
29
  - Dot paths resolve deep: `[Activity.Step.output.nested.field]`
30
30
  - Optional suffix `?` prevents errors: `[order.customer?.name?]`
31
+ - Wildcard traversal continues through POCOs, dictionaries, and `JObject` values, for example `[items[*].customValues.chapter_en?]`
31
32
 
32
33
  ### Operators
33
34
 
@@ -42,14 +43,14 @@ conditions:
42
43
  ### Iterator Variables
43
44
 
44
45
  Functions use two iterator variable names:
45
- - **`[each.*]`** -- used by: `any`, `all`, `sum`, `join` (3-arg)
46
+ - **`[each.*]`** -- used by: `any`, `all`, `sum`, `select`, `join` (3-arg), and projections over `zip(...)` output
46
47
  - **`[item.*]`** -- used by: `first`, `last`, `groupBy`
47
48
 
48
49
  ### Collection Functions
49
50
 
50
51
  | Function | Description |
51
52
  |----------|-------------|
52
- | `any([items], [each.prop] = 'val')` | True if any item matches expression. Without expression: checks if collection contains the value |
53
+ | `any([items], [each.prop] = 'val')` | True if any item matches expression. Without expression: checks if collection contains the value. Returns `false` for empty collections |
53
54
  | `all([items], [each.prop] > 0)` | True if all items match. Returns `false` for null/empty collections |
54
55
  | `count([items])` | Count items in list or JToken. Returns `0` for non-collections |
55
56
  | `sum([items], [each.amount])` | Sum values as `decimal`. Optional `[each.*]` accessor. Skips nulls |
@@ -63,6 +64,8 @@ Functions use two iterator variable names:
63
64
  | `groupBy([items], [item.cat])` | Group by one or more key expressions. Returns `[{key, items}]`. Multi-key: keys joined with `\|` |
64
65
  | `join([items], [each.name], ',')` | Join collection with `[each.*]` accessor and separator (3-arg) |
65
66
  | `join([items], ',')` | Join collection directly with separator (2-arg) |
67
+ | `select([items], [each.field])` | Project each item via `[each.*]` accessor. Returns flat `List<object>` of projected values. Empty input → empty list |
68
+ | `zip([a], [b])` | Pair elements from two or more lists into `[{item1, item2}, ...]`. Custom keys: `zip([a], [b], 'name', 'code')`. Variadic: accepts N lists. Truncates to shortest list. Returns empty list if any input is empty/null. Falls back to `item1`, `item2`, ... when custom key count does not match list count |
66
69
  | `split([str], ' ')` | Split string by first character of separator. Returns `List<string>` |
67
70
  | `elementAt([items], 0)` | Get element at index (zero-based) from list |
68
71
 
@@ -82,6 +85,8 @@ Functions use two iterator variable names:
82
85
  | `bool([value])` | Convert to boolean: null->`false`, empty string->`false`, "true"/"false"->parsed, non-zero number->`true`, any object->`true` |
83
86
  | `transliterate([value])` | Unicode to ASCII (Unidecode). Returns `""` for null |
84
87
  | `transliterateUa([value])` | Ukrainian-specific transliteration. Returns `""` for null |
88
+ | `coalesce([a], [b], 'default')` | First non-null, non-empty/whitespace argument. Variadic. `0` and `false` are kept (not skipped). Returns `null` if all args are null/empty |
89
+ | `prop([obj], 'path.to.field')` | Drill into an object by a runtime-computed string path. Supports dotted paths and `?` optional suffix, same as `[obj.path]` but the path is a string argument |
85
90
  | `parseAddress([address])` | Parse address -> `{StreetNumber, StreetName}`. Handles US and EU formats |
86
91
 
87
92
  ### Date Functions
@@ -84,6 +84,7 @@ commodities:
84
84
  mapping: # dict -> List<dict>, string -> List<object>
85
85
  name: "{{ item.name }}"
86
86
  quantity: "{{ item.qty }}"
87
+ "{{ item.langKey }}": "{{ item.value }}" # dynamic key (template-substituted)
87
88
  ```
88
89
 
89
90
  **`switch`** (value context) -- Value-based switch (case-insensitive match):
@@ -104,6 +105,7 @@ orderData:
104
105
  mapping: # dict: merge overrides. array: append items
105
106
  status: "Updated"
106
107
  notes: "{{ newNotes }}"
108
+ "{{ dynamicField }}": "{{ value }}" # dynamic key (template-substituted)
107
109
  ```
108
110
 
109
111
  **`resolve`** -- Entity ID lookup by querying a GraphQL collection:
@@ -137,6 +139,25 @@ apiKey:
137
139
  initializationVector: "{{ iv }}" # optional Base64 IV
138
140
  ```
139
141
 
142
+ ### Template-Substituted Dictionary Keys
143
+
144
+ Dictionary **keys** (not just values) support `{{ path }}` template expressions. The engine resolves each key through the same template parser before inserting it into the result dictionary. This works in:
145
+
146
+ - **Generic dictionaries** (plain object mappings in step inputs)
147
+ - **`foreach` complex mapping** keys
148
+ - **`extends` mapping** keys
149
+
150
+ ```yaml
151
+ # Build a dict whose keys depend on a variable
152
+ inputs:
153
+ customValues:
154
+ "{{ fieldName }}": "{{ fieldValue }}" # single variable key
155
+ "{{ prefix }}_{{ lang }}": "translated text" # composite key
156
+ staticKey: "literal value" # plain keys pass through unchanged
157
+ ```
158
+
159
+ **Fallback**: If a templated key resolves to null or empty string, the engine keeps the original literal key (e.g., `{{ missingVar }}`) to avoid silently dropping entries. An `InvalidOperationException` during resolution also falls back to the literal key.
160
+
140
161
  ---
141
162
 
142
163
  ## Property Path Syntax (in collection, mapping, variable paths)
@@ -152,6 +173,7 @@ Used in `collection:` (foreach), `mapping:` (outputs), and variable resolution.
152
173
  | `list[0]` | Array index | `items[0]` |
153
174
  | `list[^1]` | Index from end (last item) | `items[^1]` |
154
175
  | `list[*]` | Flatten/wildcard (all items) | `containers[*].commodities` |
176
+ | `list[*].dictKey` | Wildcard traversal into Dictionary/JObject keys | `items[*].customValues.chapter_en` |
155
177
  | `list[**]` | Recursive flatten (all depths) | `containerCommodities[**]` |
156
178
  | `list[-1]` | Depth filter (leaves only) | `tree[**][-1]` |
157
179
  | `list[condition]` | Filter by condition | `items[status=Active]` |
@@ -159,3 +181,7 @@ Used in `collection:` (foreach), `mapping:` (outputs), and variable resolution.
159
181
  | `list[*].{f1 f2}` | Field selector (projection) | `items[*].{name description}` |
160
182
  | `list[*].{alias:source}` | Field selector with alias | `items[*].{id:commodityId}` |
161
183
  | `list[*].{alias:_.parent}` | Field selector referencing parent | `items[*].{parentId:_.orderId}` |
184
+
185
+ **Wildcard traversal into Dictionary/JObject**: After `[*]`, subsequent path segments drill into Dictionary keys and JObject properties on each item. Dictionary-like values are preserved intact (not flattened) so multi-hop paths work: `items[*].customValues.chapter_en` extracts the `chapter_en` key from each item's `customValues` dictionary. This also works with nested dictionaries (`items[*].meta.locale.name`) and JObject items from JSON payloads.
186
+
187
+ **JArray primitive unwrapping**: When `GetPropertyValue` encounters a JArray where every element is a JValue (primitive), it automatically unwraps the array into a `List<object>` of plain .NET values. This ensures downstream iteration (e.g., `select()`, `zip()`, `foreach`) works with primitives rather than JValue wrappers.