esi-cap 1.7.32 → 1.7.33

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.
@@ -0,0 +1,483 @@
1
+ # esi-cap Utilities API Reference
2
+
3
+ > **Module:** `src/lib/utils/index.js`
4
+ > **Purpose:** Core utility belt for the esi-cap framework — date validation, XML parsing, JSON manipulation, array operations, and UUID generation/parsing.
5
+
6
+ ---
7
+
8
+ ## Table of Contents
9
+
10
+ - [Dependencies](#dependencies)
11
+ - [Exports](#exports)
12
+ - [Namespace: `date`](#namespace-date)
13
+ - [date.isValid(sDate)](#dateisvalidsdate)
14
+ - [Namespace: `xml`](#namespace-xml)
15
+ - [xml.isValid(oXML)](#xmlisvalidoxml)
16
+ - [Namespace: `json`](#namespace-json)
17
+ - [json.isValid(oJson)](#jsonisvalidojson)
18
+ - [json.copy(oJson)](#jsoncopyojson)
19
+ - [json.replace(oJson, sOldValue, sNewValue, oEvaluate?)](#jsonreplaceojson-soldvalue-snewvalue-oevaluate)
20
+ - [json.getValue(oJson, sPropertyPath)](#jsongetvalueojson-spropertypath)
21
+ - [json.unique(oNestedArray)](#jsonuniqueonnestedarray)
22
+ - [json.order(oJson, oKeyOrder)](#jsonorderojson-okeyorder)
23
+ - [json.projection(oJson, oColumns, bHasAssociattion?)](#jsonprojectionojson-ocolumns-bhasassociattion)
24
+ - [json.map(oJson, oMap)](#jsonmapojson-omap)
25
+ - [json.merge(oJson1, oJson2)](#jsonmergeojson1-ojson2)
26
+ - [json.flat(oJson, sFlattenedProperty)](#jsonflatojson-sflattenedproperty)
27
+ - [json.stripUndefined(oSourceJson)](#jsonstripundefinedosourcejson)
28
+ - [Namespace: `array`](#namespace-array)
29
+ - [array.add(oArray, oItem)](#arrayaddoarray-oitem)
30
+ - [array.topN(oSortedArray, iTop)](#arraytopnosortedarray-itop)
31
+ - [array.flat(oArray, sFlattenedProperty)](#arrayflatoarray-sflattenedproperty)
32
+ - [array.unique(oArray)](#arrayuniqueoarray)
33
+ - [array.order(oArray, oOrder)](#arrayorderoarray-oorder)
34
+ - [array.projection(oArray, oColumns, bHasAssociattion?)](#arrayprojectionoarray-ocolumns-bhasassociattion)
35
+ - [array.map(oArray, oMap)](#arraymapoarray-omap)
36
+ - [array.sort(oArray, oOrderBy)](#arraysortoarray-oorderby)
37
+ - [array.filter(oArray, oFilterCondition)](#arrayfilteroarray-ofiltercondition)
38
+ - [array.hasElement(oArray, oElement)](#arrayhaselementoarray-oelement)
39
+ - [array.toArray(oArray)](#arraytoarrayoarray)
40
+ - [array.toDisArray(oArray)](#arraytodisarrayoarray)
41
+ - [array.toInterleavedArray(oArray, oElement)](#arraytointerleavedarray-oarray-oelement)
42
+ - [array.toArrayProjection(oArray, sProjectionFieldName)](#arraytoarrayprojectionoarray-sprojectionfieldname)
43
+ - [array.findProperty(oArray, sProjectionFieldName)](#arrayfindpropertyoarray-sprojectionfieldname)
44
+ - [array.toGroupByPropertyName(oArray, sPropertyName)](#arraytogroupbypropertynameoarray-spropertyname)
45
+ - [array.toGroupByPropertyList(oArray, oPropertyList)](#arraytogroupbypropertylistoarray-opropertylist)
46
+ - [Class: `UUID`](#class-uuid)
47
+ - [constructor()](#constructor)
48
+ - [converse(oJsonData)](#converseoJsondata)
49
+ - [inverse(sUUID)](#inversesuuid)
50
+
51
+ ---
52
+
53
+ ## Dependencies
54
+
55
+ | Package | Purpose |
56
+ |----------|-------------------------------------------------|
57
+ | `xml2js` | XML string parsing and validation |
58
+ | `uuid` | UUID format validation |
59
+ | `lodash` | Deep merge, orderBy, uniqWith, and isEqual |
60
+ | `../_interface` | Internal logger and `_LOG` constant |
61
+
62
+ ---
63
+
64
+ ## Exports
65
+
66
+ ```js
67
+ module.exports = { date, xml, json, array, UUID, _LOG };
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Namespace: `date`
73
+
74
+ Date-related validation helpers.
75
+
76
+ ### `date.isValid(sDate)`
77
+
78
+ | Parameter | Type | Description |
79
+ |-----------|----------|--------------------------------------|
80
+ | `sDate` | `string` | Date string to validate |
81
+ | **Returns** | `Promise<boolean>` | `true` if valid date |
82
+
83
+ **Behavior:** Parses the string via `new Date()` and verifies the result is a real Date (not `NaN`). Logs errors via the framework logger.
84
+
85
+ ```js
86
+ await date.isValid("2026-04-20"); // true
87
+ await date.isValid("not-a-date"); // false
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Namespace: `xml`
93
+
94
+ XML parsing and validation helpers.
95
+
96
+ ### `xml.isValid(oXML)`
97
+
98
+ | Parameter | Type | Description |
99
+ |-----------|------------------|-----------------------------|
100
+ | `oXML` | `string\|object` | XML content to validate |
101
+ | **Returns** | `Promise<boolean>` | `true` if well-formed XML |
102
+
103
+ **Behavior:** Uses `xml2js.Parser.parseStringPromise` to attempt parsing. Logs the parsed result on success and errors on failure.
104
+
105
+ ```js
106
+ await xml.isValid("<root><item>val</item></root>"); // true
107
+ await xml.isValid("not xml"); // false
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Namespace: `json`
113
+
114
+ Comprehensive JSON/object manipulation utilities.
115
+
116
+ ### `json.isValid(oJson)`
117
+
118
+ Checks if the input can be parsed as JSON via `JSON.parse`.
119
+
120
+ | Parameter | Type | Returns |
121
+ |-----------|------------------|------------|
122
+ | `oJson` | `string\|object` | `boolean` |
123
+
124
+ ---
125
+
126
+ ### `json.copy(oJson)`
127
+
128
+ Creates a deep clone using `structuredClone`. Falls back to returning the original reference if the input isn't valid JSON.
129
+
130
+ | Parameter | Type | Returns |
131
+ |-----------|----------|----------|
132
+ | `oJson` | `object` | `object` |
133
+
134
+ ---
135
+
136
+ ### `json.replace(oJson, sOldValue, sNewValue, oEvaluate?)`
137
+
138
+ Recursively walks objects and arrays, replacing all string occurrences of `sOldValue` with `sNewValue`. **Mutates the input in place.**
139
+
140
+ | Parameter | Type | Default | Description |
141
+ |-------------|----------|-------------|----------------------------------------------------------|
142
+ | `oJson` | `object\|Array` | — | Target structure |
143
+ | `sOldValue` | `string` | — | Substring to find |
144
+ | `sNewValue` | `string` | — | Replacement substring |
145
+ | `oEvaluate` | `object` | `undefined` | Optional context; when set, resolved string is evaluated as a property path via `json.getValue` |
146
+
147
+ ```js
148
+ const data = { msg: "Hello {{name}}", nested: { msg: "Hi {{name}}" } };
149
+ json.replace(data, "{{name}}", "World");
150
+ // data → { msg: "Hello World", nested: { msg: "Hi World" } }
151
+ ```
152
+
153
+ ---
154
+
155
+ ### `json.getValue(oJson, sPropertyPath)`
156
+
157
+ Resolves a dot-separated property path to its value.
158
+
159
+ | Parameter | Type | Returns |
160
+ |-----------------|----------|---------|
161
+ | `oJson` | `object` | `*` |
162
+ | `sPropertyPath` | `string` | Value or `undefined` |
163
+
164
+ ```js
165
+ json.getValue({ a: { b: { c: 42 } } }, "a.b.c"); // 42
166
+ json.getValue({ a: 1 }, "x.y"); // undefined
167
+ ```
168
+
169
+ ---
170
+
171
+ ### `json.unique(oNestedArray)`
172
+
173
+ Flattens a nested array and removes duplicates using JSON serialization for deep equality.
174
+
175
+ | Parameter | Type | Returns |
176
+ |----------------|---------|---------|
177
+ | `oNestedArray` | `any[]` | `any[]` |
178
+
179
+ ---
180
+
181
+ ### `json.order(oJson, oKeyOrder)`
182
+
183
+ Returns a new object with keys reordered: specified keys first, then the rest.
184
+
185
+ | Parameter | Type | Returns |
186
+ |------------|------------|----------|
187
+ | `oJson` | `object` | `object` |
188
+ | `oKeyOrder` | `string[]` | — |
189
+
190
+ ```js
191
+ json.order({ c: 3, a: 1, b: 2 }, ["a", "b"]); // { a: 1, b: 2, c: 3 }
192
+ ```
193
+
194
+ ---
195
+
196
+ ### `json.projection(oJson, oColumns, bHasAssociattion?)`
197
+
198
+ Filters an object to include only the specified properties.
199
+
200
+ | Parameter | Type | Default | Description |
201
+ |-------------------|------------|---------|----------------------------------------------|
202
+ | `oJson` | `object` | — | Source object |
203
+ | `oColumns` | `string[]` | — | Property names to keep |
204
+ | `bHasAssociattion` | `boolean` | `false` | Also include keys prefixed with column names |
205
+
206
+ ---
207
+
208
+ ### `json.map(oJson, oMap)`
209
+
210
+ Creates a new object by mapping source properties via a definition object.
211
+
212
+ | Parameter | Type | Description |
213
+ |-----------|----------|-------------------------------------------------------|
214
+ | `oJson` | `object` | Source data |
215
+ | `oMap` | `object` | `{ targetKey: "source.path" }` mapping definition |
216
+
217
+ ```js
218
+ json.map({ user: { name: "John" } }, { fullName: "user.name" }); // { fullName: "John" }
219
+ ```
220
+
221
+ ---
222
+
223
+ ### `json.merge(oJson1, oJson2)`
224
+
225
+ Deep-merges two objects using Lodash `_.merge`. Returns `{}` if either argument is not an object.
226
+
227
+ ---
228
+
229
+ ### `json.flat(oJson, sFlattenedProperty)`
230
+
231
+ Promotes a nested property's contents to the top level of the object.
232
+
233
+ ```js
234
+ json.flat({ id: 1, details: { name: "A", desc: "B" } }, "details");
235
+ // { id: 1, name: "A", desc: "B" }
236
+ ```
237
+
238
+ ---
239
+
240
+ ### `json.stripUndefined(oSourceJson)`
241
+
242
+ Removes properties where the value is `undefined`, `null`, or an empty string.
243
+
244
+ ---
245
+
246
+ ## Namespace: `array`
247
+
248
+ Array operations with automatic fallback to `json` namespace for single-object inputs.
249
+
250
+ ### `array.add(oArray, oItem)`
251
+
252
+ Appends item(s) to an array. Accepts single values or arrays.
253
+
254
+ ---
255
+
256
+ ### `array.topN(oSortedArray, iTop)`
257
+
258
+ Returns the first `iTop` elements via `Array.slice`.
259
+
260
+ ---
261
+
262
+ ### `array.flat(oArray, sFlattenedProperty)`
263
+
264
+ Flattens a nested property for each element. Delegates to `json.flat` for single objects.
265
+
266
+ ---
267
+
268
+ ### `array.unique(oArray)`
269
+
270
+ Deduplicates using Lodash `_.uniqWith` with `_.isEqual` for deep comparison.
271
+
272
+ ---
273
+
274
+ ### `array.order(oArray, oOrder)`
275
+
276
+ Reorders keys for every object in the array per the specified key order.
277
+
278
+ ---
279
+
280
+ ### `array.projection(oArray, oColumns, bHasAssociattion?)`
281
+
282
+ Projects each element to only contain the listed columns.
283
+
284
+ ---
285
+
286
+ ### `array.map(oArray, oMap)`
287
+
288
+ Applies a property mapping to every element in the array.
289
+
290
+ ---
291
+
292
+ ### `array.sort(oArray, oOrderBy)`
293
+
294
+ Multi-column sort with type coercion support.
295
+
296
+ | `oOrderBy` entry | Property | Type | Description |
297
+ |---|---|---|---|
298
+ | `ref` | `string[]` | Required | Column path |
299
+ | `sort` | `'asc'\|'desc'` | Optional (default `'asc'`) | Sort direction |
300
+ | `function` | `'Date'\|'parseFloat'` | Optional | Value coercion |
301
+
302
+ ```js
303
+ array.sort(items, [
304
+ { ref: ["date"], sort: "desc", function: "Date" },
305
+ { ref: ["price"], sort: "asc", function: "parseFloat" }
306
+ ]);
307
+ ```
308
+
309
+ ---
310
+
311
+ ### `array.filter(oArray, oFilterCondition)`
312
+
313
+ Filters array elements based on a list of filter conditions (AND logic).
314
+
315
+ **FilterCondition:**
316
+ | Property | Type | Description |
317
+ |---|---|---|
318
+ | `name` | `string` | Property name to filter by |
319
+ | `op` | `'='\|'!='\|'>'\|'<'\|'>='\|'<='` | Comparison operator |
320
+ | `value` | `string\|number\|boolean` | Value to compare against |
321
+
322
+ ```js
323
+ array.filter(employees, [
324
+ { name: "age", op: ">=", value: 30 },
325
+ { name: "dept", op: "=", value: "Engineering" }
326
+ ]);
327
+ ```
328
+
329
+ ---
330
+
331
+ ### `array.hasElement(oArray, oElement)`
332
+
333
+ Returns `true` if the element is found (strict `===` via `Array.includes`).
334
+
335
+ ---
336
+
337
+ ### `array.toArray(value)`
338
+
339
+ Normalizes any value into an array: `undefined` → `[]`, non-array → `[value]`, array → as-is.
340
+
341
+ ---
342
+
343
+ ### `array.toDisArray(oArray)`
344
+
345
+ Unwraps a single-element array to its value. Multi-element arrays pass through.
346
+
347
+ ---
348
+
349
+ ### `array.toInterleavedArray(oArray, oElement)`
350
+
351
+ Inserts a separator element between consecutive items.
352
+
353
+ ```js
354
+ array.toInterleavedArray(["a", "b", "c"], "|"); // ["a", ["|"], "b", ["|"], "c"]
355
+ ```
356
+
357
+ ---
358
+
359
+ ### `array.toArrayProjection(oArray, sProjectionFieldName)`
360
+
361
+ Extracts a single field from each element into a flat array.
362
+
363
+ ```js
364
+ array.toArrayProjection([{ id: 1 }, { id: 2 }], "id"); // [1, 2]
365
+ ```
366
+
367
+ ---
368
+
369
+ ### `array.findProperty(oArray, sProjectionFieldName)`
370
+
371
+ Recursively searches nested structures for all values of a named property. Returns a deduplicated array.
372
+
373
+ ---
374
+
375
+ ### `array.toGroupByPropertyName(oArray, sPropertyName)`
376
+
377
+ Groups elements into an object keyed by a single property's value.
378
+
379
+ ```js
380
+ array.toGroupByPropertyName(
381
+ [{ type: "A", v: 1 }, { type: "B", v: 2 }, { type: "A", v: 3 }],
382
+ "type"
383
+ );
384
+ // { A: [{...}, {...}], B: [{...}] }
385
+ ```
386
+
387
+ ---
388
+
389
+ ### `array.toGroupByPropertyList(oArray, oPropertyList)`
390
+
391
+ Groups elements by a composite key derived from multiple properties (joined with `##`). Supports dot-notation paths and recursive deep property lookup.
392
+
393
+ ```js
394
+ array.toGroupByPropertyList(data, ["region", "details.category"]);
395
+ // { "US##Electronics": [...], "EU##Clothing": [...] }
396
+ ```
397
+
398
+ ---
399
+
400
+ ## Class: `UUID`
401
+
402
+ Singleton class for deterministic UUID generation and parsing based on a fixed field configuration. Encodes/decodes structured business data (CompanyCode, PersonWorkAgreementExternalID, TimeSheetRecord) into RFC-4122-compliant UUIDs.
403
+
404
+ ### Constructor
405
+
406
+ ```js
407
+ const uuidHelper = new UUID();
408
+ ```
409
+
410
+ Creates a singleton instance. Subsequent `new UUID()` calls return the same instance.
411
+
412
+ **Internal field configuration:**
413
+
414
+ | Field | Order | Format (Regex) | Description |
415
+ |---|---|---|---|
416
+ | `CompanyCode` | 1 | `/^[A-Za-z]{2}\d{2}$/` | 2 letters + 2 digits |
417
+ | `PersonWorkAgreementExternalID` | 2 | `/^[Ii]\d{7}$\|^\d{1,9}$/` | I-number or numeric ID |
418
+ | `TimeSheetRecord` | 3 | `/^\d{12}$/` | 12-digit record number |
419
+
420
+ ---
421
+
422
+ ### `converse(oJsonData)`
423
+
424
+ Encodes structured JSON data into a deterministic UUID.
425
+
426
+ | Parameter | Type | Description |
427
+ |------------|----------|-------------------------------------------|
428
+ | `oJsonData` | `object` | Object with `CompanyCode`, `PersonWorkAgreementExternalID`, `TimeSheetRecord` |
429
+ | **Returns** | `string` | A valid UUID string |
430
+ | **Throws** | `Error` | If any field has an invalid format or the generated UUID fails validation |
431
+
432
+ ```js
433
+ const uid = new UUID();
434
+ uid.converse({
435
+ CompanyCode: "AB12",
436
+ PersonWorkAgreementExternalID: "I1234567",
437
+ TimeSheetRecord: "000000012345"
438
+ });
439
+ // Returns a valid UUID string e.g., "c0656612-2e12-4345-8670-000000012345"
440
+ ```
441
+
442
+ ---
443
+
444
+ ### `inverse(sUUID)`
445
+
446
+ Decodes a previously generated UUID back into its original structured data.
447
+
448
+ | Parameter | Type | Description |
449
+ |-----------|----------|------------------------------------|
450
+ | `sUUID` | `string` | A UUID previously created by `converse` |
451
+ | **Returns** | `object` | `{ CompanyCode, PersonWorkAgreementExternalID, TimeSheetRecord }` or `{}` if invalid |
452
+
453
+ ```js
454
+ const uid = new UUID();
455
+ uid.inverse("c0656612-2e12-4345-8670-000000012345");
456
+ // { CompanyCode: "AB12", PersonWorkAgreementExternalID: "I1234567", TimeSheetRecord: "000000012345" }
457
+ ```
458
+
459
+ ---
460
+
461
+ ## Quick Usage Examples
462
+
463
+ ```js
464
+ const { date, xml, json, array, UUID } = require('./src/lib/utils');
465
+
466
+ // Validate & transform
467
+ const valid = await date.isValid("2026-04-20");
468
+ const items = [{ id: 1, name: "A" }, { id: 2, name: "B" }];
469
+
470
+ // Project, sort, filter
471
+ const names = array.toArrayProjection(items, "name"); // ["A", "B"]
472
+ const sorted = array.sort(items, [{ ref: ["id"], sort: "desc" }]);
473
+ const filtered = array.filter(items, [{ name: "id", op: ">", value: 1 }]);
474
+
475
+ // Group & merge
476
+ const grouped = array.toGroupByPropertyName(items, "name");
477
+ const merged = json.merge({ a: 1 }, { b: 2 }); // { a: 1, b: 2 }
478
+
479
+ // UUID round-trip
480
+ const uid = new UUID();
481
+ const uuidStr = uid.converse({ CompanyCode: "AB12", PersonWorkAgreementExternalID: "I1234567", TimeSheetRecord: "000000012345" });
482
+ const original = uid.inverse(uuidStr);
483
+ ```