@cxtms/cx-schema 1.9.8 → 1.9.9

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.8",
3
+ "version": "1.9.9",
4
4
  "description": "Schema validation package for CargoXplorer YAML modules",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -145,6 +145,28 @@ transitions:
145
145
  - **auto**: Automatic evaluation based on conditions; sorted by priority descending
146
146
  - **event**: External event-driven; requires `eventName`
147
147
 
148
+ ### Auto-Transitions and Cross-Entity Support
149
+
150
+ Auto-transitions can fire on related entities. For example, an Order status change can trigger a Commodity flow transition via the `OrderCommodities` join table.
151
+
152
+ ```yaml
153
+ states:
154
+ - name: Pending
155
+ autoTransitions:
156
+ - to: InTransit
157
+ trigger:
158
+ entityType: Order
159
+ event: StatusChanged
160
+ conditions:
161
+ - "order.orderStatusName == 'Shipped'"
162
+ priority: 1
163
+ ```
164
+
165
+ **Cross-entity resolution:**
166
+ | Triggering Entity | Flow Entity | Resolution |
167
+ |-------------------|-------------|------------|
168
+ | Order | Commodity | Via `OrderCommodities` join table |
169
+
148
170
  ### From States
149
171
  - Single state: `from: Draft`
150
172
  - Multiple states: `from: [Draft, Submitted]`
@@ -12,7 +12,8 @@
12
12
  | `Utilities/Template@1` | Handlebars template rendering |
13
13
  | `Utilities/Import@1` | Import data |
14
14
  | `Utilities/Export@1` | Export data |
15
- | `Utilities/CsvParse@1` | Parse CSV content |
15
+ | `Utilities/CsvParse@1` | Parse CSV content (supports columns, distinct) |
16
+ | `Utilities/GroupBy@1` | Group collection by fields |
16
17
  | `Utilities/MoveFile@1` | Move file |
17
18
  | `Utilities/ValidateReCaptcha` | Validate reCAPTCHA |
18
19
  | `Utilities/ValidateHMAC` | Validate HMAC signatures |
@@ -242,7 +243,19 @@ Renders a Handlebars template string with data.
242
243
 
243
244
  ## CsvParse@1
244
245
 
245
- Parses CSV/TSV data from a URL (file://, http://, https://). Headers are trimmed of whitespace, BOM, and special characters. Outputs: `records` (array of dicts), `count` (int), `hasRecords` (boolean).
246
+ Parses CSV/TSV data from a URL (file://, http://, https://). Headers are trimmed of whitespace, BOM, and special characters and converted to camelCase. Blank rows are skipped.
247
+
248
+ **Inputs:**
249
+
250
+ | Input | Type | Required | Description |
251
+ |-------|------|----------|-------------|
252
+ | `url` | string | yes | URL to CSV file |
253
+ | `hasHeader` | bool | no | `true` (default). Whether first row has headers |
254
+ | `delimiter` | string | no | `,` (default). Field delimiter. Use `\t` for tab |
255
+ | `columns` | string[] | no | Explicit column names; overrides file header |
256
+ | `distinct` | string[] | no | Deduplicate by these fields; output projected to only these fields |
257
+
258
+ **Outputs:** `records` (array of dicts), `count` (int), `hasRecords` (boolean).
246
259
 
247
260
  ```yaml
248
261
  - task: "Utilities/CsvParse@1"
@@ -253,36 +266,25 @@ Parses CSV/TSV data from a URL (file://, http://, https://). Headers are trimmed
253
266
  outputs:
254
267
  - name: rows
255
268
  mapping: "records?"
256
- - name: rowCount
257
- mapping: "count?"
258
- ```
259
-
260
- Tab-delimited with custom columns (e.g. GeoNames postal code files):
261
269
 
262
- ```yaml
263
270
  - task: "Utilities/CsvParse@1"
264
271
  name: ParsePostalCodes
265
272
  inputs:
266
273
  url: "{{ Data?.UnzipFiles?.filePath? }}"
267
274
  delimiter: "\t"
268
- columns:
269
- - CountryCode
270
- - Code
271
- - PlaceName
272
- - StateName
273
- - StateCode
274
- - AdminName2
275
- - AdminCode2
276
- - AdminName3
277
- - AdminCode3
278
- - Latitude
279
- - Longitude
280
- - Accuracy
275
+ columns: ["CountryCode", "Code", "PlaceName", "StateName", "StateCode"]
281
276
  outputs:
282
277
  - name: postalCodes
283
278
  mapping: "records?"
284
- - name: totalCount
285
- mapping: "count?"
279
+
280
+ - task: "Utilities/CsvParse@1"
281
+ name: ExtractDistinctStates
282
+ inputs:
283
+ url: "{{ fileUrl }}"
284
+ distinct: ["stateCode", "stateName", "countryCode"]
285
+ outputs:
286
+ - name: states
287
+ mapping: "records?"
286
288
  ```
287
289
 
288
290
  ## Export@1
@@ -328,3 +330,88 @@ Imports data from file content or URL. Supports `file://` URLs for local files (
328
330
  ```
329
331
 
330
332
  **`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://).
333
+
334
+ ---
335
+
336
+ ## GroupBy@1
337
+
338
+ Groups a collection of dictionaries by one or more fields. Produces `{ key, values }` groups for batch processing.
339
+
340
+ **Inputs:**
341
+
342
+ | Input | Type | Required | Description |
343
+ |-------|------|----------|-------------|
344
+ | `collection` | List<Dictionary> | yes | Records to group |
345
+ | `by` | string[] | yes | Field names to group by (case-insensitive) |
346
+
347
+ **Outputs:** `items` (array of `{ key: {field: value, ...}, values: [...] }`), `count` (int).
348
+
349
+ ```yaml
350
+ - task: "Utilities/GroupBy@1"
351
+ name: GroupByCustomer
352
+ inputs:
353
+ collection: "{{ Data?.ParseCsv?.records? }}"
354
+ by: ["customerId"]
355
+ outputs:
356
+ - name: groups
357
+ mapping: "items?"
358
+ - name: groupCount
359
+ mapping: "count?"
360
+ ```
361
+
362
+ ---
363
+
364
+ ## Import Tasks
365
+
366
+ Import tasks handle bulk data ingestion. All support `file://` URLs for chaining with UnzipFile.
367
+
368
+ ### Order/Import@1
369
+
370
+ Imports orders from CSV, JSON, or XLSX. Inputs: `organizationId` (auto-injected), `fileUrl`/`stream`/`orders` (one required), `fileType` (auto-detected), `options` (match-by, update behavior).
371
+
372
+ ### States/Import@1
373
+
374
+ Imports US states/provinces. Inputs: `organizationId`, `fileUrl`/`stream`/`states`, `matchByFields`, `updateIfExists`. Deduplicates by match key.
375
+
376
+ ### PostalCodes/Import@1
377
+
378
+ Imports postal/ZIP codes. Inputs: `organizationId`, `fileUrl`/`stream`/`postalCodes`, `matchByFields`. Match-key fields protected from overwrite.
379
+
380
+ ### TrackingEvent/Import@1
381
+
382
+ Imports tracking events for an order. Inputs: `orderId`, `events`, `matchByFields` (default: `["eventDefinitionName", "eventDate"]`), `skipIfExists`, `createEventDefinitions`. Auto-links commodities via `CommodityId`.
383
+
384
+ ```yaml
385
+ # Full ZIP-to-import pipeline
386
+ - task: "Utilities/HttpRequest@1"
387
+ name: Download
388
+ inputs:
389
+ url: "{{ downloadUrl }}"
390
+ method: GET
391
+ saveToFile: true
392
+
393
+ - task: "Utilities/UnzipFile@1"
394
+ name: Unzip
395
+ inputs:
396
+ filePath: "{{ Main?.Download?.result?.FilePath? }}"
397
+ filePattern: "*.csv"
398
+
399
+ - task: "Utilities/CsvParse@1"
400
+ name: Parse
401
+ inputs:
402
+ url: "file://{{ Main?.Unzip?.files?[0] }}"
403
+ distinct: ["stateCode", "stateName", "countryCode"]
404
+
405
+ - task: "States/Import@1"
406
+ name: ImportStates
407
+ inputs:
408
+ organizationId: "{{ organizationId }}"
409
+ states: "{{ Main?.Parse?.records? }}"
410
+
411
+ - task: "PostalCodes/Import@1"
412
+ name: ImportPostalCodes
413
+ inputs:
414
+ organizationId: "{{ organizationId }}"
415
+ fileUrl: "file://{{ Main?.Unzip?.files?[0] }}"
416
+ fileType: "csv"
417
+ ```