@controlium/utils 0.0.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.
- package/LICENSE +661 -0
- package/README.md +10 -0
- package/dist/cjs/detokeniser/detokeniser.js +1135 -0
- package/dist/cjs/index.js +14 -0
- package/dist/cjs/jsonUtils/jsonUtils.js +460 -0
- package/dist/cjs/logger/logger.js +863 -0
- package/dist/cjs/logger/logger.spec.js +875 -0
- package/dist/cjs/logger/types.js +2 -0
- package/dist/cjs/stringUtils/stringUtils.js +294 -0
- package/dist/cjs/utils/utils.js +1050 -0
- package/dist/esm/detokeniser/detokeniser.js +1131 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/jsonUtils/jsonUtils.js +420 -0
- package/dist/esm/logger/logger.js +859 -0
- package/dist/esm/logger/logger.spec.js +873 -0
- package/dist/esm/logger/types.js +1 -0
- package/dist/esm/stringUtils/stringUtils.js +290 -0
- package/dist/esm/utils/utils.js +1043 -0
- package/dist/types/detokeniser/detokeniser.d.ts +402 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/jsonUtils/jsonUtils.d.ts +196 -0
- package/dist/types/logger/logger.d.ts +388 -0
- package/dist/types/logger/logger.spec.d.ts +1 -0
- package/dist/types/logger/types.d.ts +235 -0
- package/dist/types/stringUtils/stringUtils.d.ts +129 -0
- package/dist/types/utils/utils.d.ts +450 -0
- package/package.json +110 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Processes strings containing tokens in the format `[[tokenType|expression|format]]`, replacing each
|
|
3
|
+
* token with its resolved value. Tokens can be nested — the innermost is always resolved first.
|
|
4
|
+
*
|
|
5
|
+
* ## Token syntax
|
|
6
|
+
* ```
|
|
7
|
+
* [[tokenType|expression|format]]
|
|
8
|
+
* ```
|
|
9
|
+
* - **tokenType** — identifies the built-in handler or is matched against registered callbacks
|
|
10
|
+
* - **expression** — what to compute (type-specific)
|
|
11
|
+
* - **format** — how to format the result (type-specific, often optional)
|
|
12
|
+
*
|
|
13
|
+
* The delimiter `|` and the `[[` / `]]` endstops are configurable via {@link Detokeniser.delimiter} and
|
|
14
|
+
* {@link Detokeniser.tokenStartEndChars}, but the defaults cover the vast majority of use cases.
|
|
15
|
+
*
|
|
16
|
+
* ---
|
|
17
|
+
* ## Built-in token types
|
|
18
|
+
*
|
|
19
|
+
* ### `random` — random data
|
|
20
|
+
* | Expression | Format | Example | Sample output |
|
|
21
|
+
* |---|---|---|---|
|
|
22
|
+
* | `digits` | count | `[[random\|digits\|6]]` | `482910` |
|
|
23
|
+
* | `letters` | count | `[[random\|letters\|4]]` | `xKpQ` |
|
|
24
|
+
* | `lowercaseletters` | count | `[[random\|lowercaseletters\|4]]` | `xkpq` |
|
|
25
|
+
* | `uppercaseletters` | count | `[[random\|uppercaseletters\|4]]` | `XKPQ` |
|
|
26
|
+
* | `alphanumerics` | count | `[[random\|alphanumerics\|8]]` | `a3Kx92Zp` |
|
|
27
|
+
* | `from(<chars>)` | count | `[[random\|from(aeiou)\|3]]` | `ioa` |
|
|
28
|
+
* | `float(min,max)` | decimal places | `[[random\|float(1.5,3.7)\|2]]` | `2.83` |
|
|
29
|
+
* | `date(fromEpoch,toEpoch)` | date format | `[[random\|date(0,1700000000000)\|yyyy-MM-dd]]` | `1994-07-12` |
|
|
30
|
+
*
|
|
31
|
+
* ### `date` / `date(<state>)` — date and time
|
|
32
|
+
* Format is a [date-fns format string](https://date-fns.org/docs/format), or the special values
|
|
33
|
+
* `epoch` (milliseconds since 1970-01-01) or `second-epoch`.
|
|
34
|
+
*
|
|
35
|
+
* | Expression | Example | Sample output |
|
|
36
|
+
* |---|---|---|
|
|
37
|
+
* | `today` / `now` | `[[date\|today\|dd-MM-yyyy]]` | `01-04-2026` |
|
|
38
|
+
* | `yesterday` | `[[date\|yesterday\|dd-MM-yyyy]]` | `31-03-2026` |
|
|
39
|
+
* | `tomorrow` | `[[date\|tomorrow\|dd-MM-yyyy]]` | `02-04-2026` |
|
|
40
|
+
* | `addYears(n)` | `[[date\|addYears(-1)\|yyyy]]` | `2025` |
|
|
41
|
+
* | `addMonths(n)` | `[[date\|addMonths(3)\|MMM yyyy]]` | `Jul 2026` |
|
|
42
|
+
* | `addDays(n)` | `[[date\|addDays(5)\|dd-MM-yyyy]]` | `06-04-2026` |
|
|
43
|
+
* | `addHours(n)` | `[[date\|addHours(2)\|HH:mm]]` | `14:30` |
|
|
44
|
+
* | `addMinutes(n)` | `[[date\|addMinutes(30)\|HH:mm]]` | `13:00` |
|
|
45
|
+
* | `random(fromEpoch,toEpoch)` | date format | `[[date\|random(0,1700000000000)\|yyyy-MM-dd]]` | random date |
|
|
46
|
+
* | `followingDay(epoch,dayName)` | date format | `[[date\|followingDay(1711929600000,wednesday)\|dd-MM-yyyy]]` | next Wednesday |
|
|
47
|
+
* | `yyyy-MM-dd` _(fixed date)_ | date format | `[[date\|2026-06-15\|EEEE]]` | `Monday` |
|
|
48
|
+
* | `timezoneOffset` _(with region)_ | _(none)_ | `[[date(us-east)\|timezoneOffset]]` | `-0500` |
|
|
49
|
+
*
|
|
50
|
+
* A region qualifier routes date formatting through the region's IANA timezone (resolved by the
|
|
51
|
+
* consumer-provided `PublicHolidays` module):
|
|
52
|
+
* ```
|
|
53
|
+
* [[date(eu-london)|today|HH:mm]] ← formatted in London time
|
|
54
|
+
* [[date(us-east)|addDays(1)|dd-MM-yyyy]]
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* The following expressions are **async-only** — use {@link Detokeniser.doAsync} with a region qualifier:
|
|
58
|
+
* - `addWorkingDays(n)` — adds business days, skipping weekends and public holidays
|
|
59
|
+
* - `followingWorkingDay(epoch,dayName)` — as `followingDay` but skips weekends and public holidays
|
|
60
|
+
* - `nextPublicHoliday(fromEpoch,maxDays)` — next public holiday within a search window
|
|
61
|
+
*
|
|
62
|
+
* ```typescript
|
|
63
|
+
* await Detokeniser.doAsync('[[date(us-east)|addWorkingDays(5)|dd-MM-yyyy]]');
|
|
64
|
+
* await Detokeniser.doAsync('[[date(eu-london)|nextPublicHoliday([[date|today|epoch]],90)|dd-MM-yyyy]]');
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* ### `setting` — retrieve a configured value
|
|
68
|
+
* Parameters are given as JSON key/value pairs after the delimiter. Resolution order:
|
|
69
|
+
* process env → npm config → context parameters → default value.
|
|
70
|
+
* ```
|
|
71
|
+
* [[setting|processEnvName: "MY_VAR"]]
|
|
72
|
+
* [[setting|processEnvName: "MY_VAR", defaultValue: "fallback"]]
|
|
73
|
+
* [[setting|npmPackageConfigName: "myconfig"]]
|
|
74
|
+
* [[setting|profileParameterName: "myParam", defaultValue: "none"]]
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* ### `base64` — encode or decode
|
|
78
|
+
* ```
|
|
79
|
+
* [[base64|encode|Hello World]] → SGVsbG8gV29ybGQ=
|
|
80
|
+
* [[base64|decode|SGVsbG8gV29ybGQ=]] → Hello World
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* ### `jwt` — generate a signed JWT
|
|
84
|
+
* ```
|
|
85
|
+
* [[jwt|{"sub":"1234","name":"Test"}|MySecret]]
|
|
86
|
+
* [[jwt|{"sub":"1234"}|MySecret|{"algorithm":"HS256"}]]
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* ### `mockintercepts` — harvest a value from intercepted mock requests
|
|
90
|
+
* ```
|
|
91
|
+
* [[mockintercepts|$.requests[0].body.userId]]
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* ---
|
|
95
|
+
* ## Nesting
|
|
96
|
+
* Tokens are resolved innermost-first, so nested tokens compose naturally:
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // Date N random days from now, where N is itself a random digit
|
|
99
|
+
* Detokeniser.do('[[date|addDays([[random|digits|1]])|dd-MM-yyyy]]');
|
|
100
|
+
*
|
|
101
|
+
* // Next NSW public holiday from today (async — needs doAsync)
|
|
102
|
+
* await Detokeniser.doAsync('[[date(eu-london)|nextPublicHoliday([[date|today|epoch]],90)|dd-MM-yyyy]]');
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* ---
|
|
106
|
+
* ## Escaping
|
|
107
|
+
* The escape character is `/` (configurable via `EscapeChar`). Escaping applies both inside and
|
|
108
|
+
* outside tokens. A double escape `//` produces a literal `/`.
|
|
109
|
+
*
|
|
110
|
+
* | Input | Output | Notes |
|
|
111
|
+
* |---|---|---|
|
|
112
|
+
* | `/[[` | `[[` | Literal `[[` — not treated as token start |
|
|
113
|
+
* | `/]]` | `]]` | Literal `]]` — not treated as token end |
|
|
114
|
+
* | `//` | `/` | Literal escape char |
|
|
115
|
+
* | `[[random\|from(xyz/[[)\|3]]` | 3 chars from `xyz[[` | `/[[` inside `from()` = `/[` (→`[`) + `[` = two `[` (higher weight) |
|
|
116
|
+
* | `[[random\|from(abc/))\|2]]` | 2 chars from `abc)` | `/)` inside `from()` = literal `)` |
|
|
117
|
+
*
|
|
118
|
+
* ---
|
|
119
|
+
* ## Extending with callbacks
|
|
120
|
+
* Custom token types are registered via {@link Detokeniser.addCallbackSync} (for sync processing) or
|
|
121
|
+
* {@link Detokeniser.addCallbackAsync} (for async processing). Callbacks are tried in registration
|
|
122
|
+
* order; return `undefined` to pass to the next callback. If all callbacks return `undefined` and no
|
|
123
|
+
* built-in handler matched, an error is thrown.
|
|
124
|
+
*
|
|
125
|
+
* @see {@link Detokeniser.addCallbackSync}
|
|
126
|
+
* @see {@link Detokeniser.addCallbackAsync}
|
|
127
|
+
* @see {@link Detokeniser.do}
|
|
128
|
+
* @see {@link Detokeniser.doAsync}
|
|
129
|
+
*/
|
|
130
|
+
export declare class Detokeniser {
|
|
131
|
+
private static _endTokenChar;
|
|
132
|
+
private static _startTokenChar;
|
|
133
|
+
private static EscapeChar;
|
|
134
|
+
private static _delimiter;
|
|
135
|
+
private static _asyncCallbacks;
|
|
136
|
+
private static _syncCallbacks;
|
|
137
|
+
/**
|
|
138
|
+
* Gets the current single-character delimiter used to separate token parts.
|
|
139
|
+
* Default: `|`
|
|
140
|
+
*/
|
|
141
|
+
static get delimiter(): string;
|
|
142
|
+
/**
|
|
143
|
+
* Sets the single-character delimiter used to separate token parts.
|
|
144
|
+
* The new value applies to all subsequent {@link Detokeniser.do} / {@link Detokeniser.doAsync} calls.
|
|
145
|
+
* @param newDelimiter - Exactly one character
|
|
146
|
+
* @throws If `newDelimiter` is not exactly one character
|
|
147
|
+
* @example
|
|
148
|
+
* Detokeniser.delimiter = ':';
|
|
149
|
+
* Detokeniser.do('[[random:digits:6]]'); // → e.g. '482910'
|
|
150
|
+
* Detokeniser.reset(); // restore default '|'
|
|
151
|
+
*/
|
|
152
|
+
static set delimiter(newDelimiter: string);
|
|
153
|
+
/**
|
|
154
|
+
* Sets the start and end token sequences.
|
|
155
|
+
* @param startEndChars - An even-length string whose first half is the start sequence and second half the end sequence.
|
|
156
|
+
* @example Detokeniser.tokenStartEndChars = "[[]]"; // start = "[[", end = "]]"
|
|
157
|
+
* @remarks Start and end sequences must differ. Minimum total length is 2 (one char each).
|
|
158
|
+
*/
|
|
159
|
+
static set tokenStartEndChars(startEndChars: string);
|
|
160
|
+
/**
|
|
161
|
+
* Gets the current token start/end sequences concatenated (e.g. `"[[]]"`).
|
|
162
|
+
*/
|
|
163
|
+
static get tokenStartEndChars(): string;
|
|
164
|
+
/**
|
|
165
|
+
* Resets the Detokeniser to factory defaults:
|
|
166
|
+
* - Token endstops restored to `[[` / `]]`
|
|
167
|
+
* - Delimiter restored to `|`
|
|
168
|
+
* - Escape char restored to `/`
|
|
169
|
+
* - All registered sync and async callbacks cleared
|
|
170
|
+
*
|
|
171
|
+
* Call this in test teardown to guarantee a clean state between scenarios.
|
|
172
|
+
* @example
|
|
173
|
+
* afterEach(() => Detokeniser.reset());
|
|
174
|
+
*/
|
|
175
|
+
static reset(): void;
|
|
176
|
+
/**
|
|
177
|
+
* Registers a synchronous custom token handler.
|
|
178
|
+
*
|
|
179
|
+
* When {@link Detokeniser.do} encounters a token not handled by the built-in set, it invokes each
|
|
180
|
+
* registered sync callback in registration order. The first to return a non-`undefined` string wins.
|
|
181
|
+
* Return `undefined` to pass to the next callback. If all callbacks return `undefined`, an error is thrown.
|
|
182
|
+
*
|
|
183
|
+
* @param callback - `(delimiter: string, token: string) => string | undefined`
|
|
184
|
+
* - `delimiter` — current delimiter character (default `|`)
|
|
185
|
+
* - `token` — full token body without `[[` / `]]`, e.g. `"mytype|arg1|arg2"`
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* // Handle [[env|VAR_NAME]] tokens
|
|
189
|
+
* Detokeniser.addCallbackSync((delimiter, token) => {
|
|
190
|
+
* const [type, name] = token.split(delimiter);
|
|
191
|
+
* if (type.toLowerCase() !== 'env') return undefined;
|
|
192
|
+
* return process.env[name] ?? '';
|
|
193
|
+
* });
|
|
194
|
+
* Detokeniser.do('Path: [[env|HOME]]'); // → 'Path: /home/user'
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* // Multiple callbacks — each handles one type, passes on the rest
|
|
198
|
+
* Detokeniser.addCallbackSync((delimiter, token) => {
|
|
199
|
+
* const [type, value] = token.split(delimiter);
|
|
200
|
+
* if (type === 'upper') return value.toUpperCase();
|
|
201
|
+
* return undefined;
|
|
202
|
+
* });
|
|
203
|
+
* Detokeniser.addCallbackSync((delimiter, token) => {
|
|
204
|
+
* const [type, value] = token.split(delimiter);
|
|
205
|
+
* if (type === 'lower') return value.toLowerCase();
|
|
206
|
+
* return undefined;
|
|
207
|
+
* });
|
|
208
|
+
* Detokeniser.do('[[upper|hello]] [[lower|WORLD]]'); // → 'HELLO world'
|
|
209
|
+
*
|
|
210
|
+
* @see {@link Detokeniser.resetSyncCallbacks} to remove all sync callbacks
|
|
211
|
+
* @see {@link Detokeniser.addCallbackAsync} for async token handlers
|
|
212
|
+
*/
|
|
213
|
+
static addCallbackSync(callback: Detokeniser.Callback_Sync): void;
|
|
214
|
+
/**
|
|
215
|
+
* Registers an asynchronous custom token handler.
|
|
216
|
+
*
|
|
217
|
+
* Works identically to {@link Detokeniser.addCallbackSync} but is invoked by {@link Detokeniser.doAsync}.
|
|
218
|
+
* Async callbacks are tried in registration order; the first to return a non-`undefined` value wins.
|
|
219
|
+
* Return `undefined` to pass to the next callback.
|
|
220
|
+
*
|
|
221
|
+
* @param asyncCallback - `(delimiter: string, token: string) => Promise<string | undefined>`
|
|
222
|
+
* - `delimiter` — current delimiter character (default `|`)
|
|
223
|
+
* - `token` — full token body without `[[` / `]]`, e.g. `"mytype|arg1|arg2"`
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* // Handle [[db|table|column|whereClause]] tokens
|
|
227
|
+
* Detokeniser.addCallbackAsync(async (delimiter, token) => {
|
|
228
|
+
* const [type, table, column, where] = token.split(delimiter);
|
|
229
|
+
* if (type.toLowerCase() !== 'db') return undefined;
|
|
230
|
+
* const row = await db.query(`SELECT ${column} FROM ${table} WHERE ${where} LIMIT 1`);
|
|
231
|
+
* return String(row[column]);
|
|
232
|
+
* });
|
|
233
|
+
* const result = await Detokeniser.doAsync('ID: [[db|users|id|active=1]]');
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* // Combine with nested tokens — inner tokens resolve before the callback is called
|
|
237
|
+
* Detokeniser.addCallbackAsync(async (delimiter, token) => {
|
|
238
|
+
* const [type, key] = token.split(delimiter);
|
|
239
|
+
* if (type !== 'cache') return undefined;
|
|
240
|
+
* return await redis.get(key);
|
|
241
|
+
* });
|
|
242
|
+
* // [[random|digits|8]] resolves first, then [[cache|...]] receives the result
|
|
243
|
+
* await Detokeniser.doAsync('Val: [[cache|prefix-[[random|digits|8]]]]');
|
|
244
|
+
*
|
|
245
|
+
* @see {@link Detokeniser.resetAsyncCallbacks} to remove all async callbacks
|
|
246
|
+
* @see {@link Detokeniser.addCallbackSync} for synchronous token handlers
|
|
247
|
+
*/
|
|
248
|
+
static addCallbackAsync(asyncCallback: Detokeniser.Callback_Async): void;
|
|
249
|
+
/**
|
|
250
|
+
* Removes all registered sync callbacks. Built-in token handlers are unaffected.
|
|
251
|
+
* Use between tests or scenarios to ensure callback isolation.
|
|
252
|
+
* @see {@link Detokeniser.reset} to also clear async callbacks and restore all defaults
|
|
253
|
+
*/
|
|
254
|
+
static resetSyncCallbacks(): void;
|
|
255
|
+
/**
|
|
256
|
+
* Removes all registered async callbacks. Built-in token handlers are unaffected.
|
|
257
|
+
* Use between tests or scenarios to ensure callback isolation.
|
|
258
|
+
* @see {@link Detokeniser.reset} to also clear sync callbacks and restore all defaults
|
|
259
|
+
*/
|
|
260
|
+
static resetAsyncCallbacks(): void;
|
|
261
|
+
/**
|
|
262
|
+
* Synchronously resolves all tokens in the given string and returns the result.
|
|
263
|
+
*
|
|
264
|
+
* Tokens are resolved innermost-first. Registered sync callbacks are invoked for token types not
|
|
265
|
+
* handled by the built-in set. Use {@link Detokeniser.doAsync} if you need async callbacks or any
|
|
266
|
+
* of the async-only date expressions (`addWorkingDays`, `followingWorkingDay`, `nextPublicHoliday`).
|
|
267
|
+
*
|
|
268
|
+
* @param tokenisedString - String potentially containing `[[...]]` tokens
|
|
269
|
+
* @param options - Optional processing options
|
|
270
|
+
* @returns The input string with all tokens replaced by their resolved values
|
|
271
|
+
* @throws If any token is malformed, unsupported, or a callback throws
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* Detokeniser.do('Ref-[[random|digits|6]]'); // → e.g. 'Ref-482910'
|
|
275
|
+
* Detokeniser.do('Expires [[date|addDays(30)|dd/MM/yyyy]]'); // → e.g. 'Expires 01/05/2026'
|
|
276
|
+
* Detokeniser.do('[[random|uppercaseletters|3]]-[[random|digits|4]]'); // → e.g. 'XKP-7391'
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* // Nested tokens — innermost resolved first
|
|
280
|
+
* Detokeniser.do('[[date|addDays([[random|digits|1]])|dd-MM-yyyy]]');
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // Context parameters for [[setting|...]] tokens
|
|
284
|
+
* Detokeniser.do('Hello [[setting|profileParameterName: "username"]]', {
|
|
285
|
+
* contextParameters: { username: 'Alice' }
|
|
286
|
+
* }); // → 'Hello Alice'
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* // Escaping — produce literal [[ / ]] in output
|
|
290
|
+
* Detokeniser.do('Press /[[Enter/]] to continue'); // → 'Press [[Enter]] to continue'
|
|
291
|
+
*/
|
|
292
|
+
static do(tokenisedString: string, options?: Detokeniser.DoOptions): string;
|
|
293
|
+
/**
|
|
294
|
+
* Asynchronously resolves all tokens in the given string and returns a Promise of the result.
|
|
295
|
+
*
|
|
296
|
+
* Functionally equivalent to {@link Detokeniser.do} but additionally supports:
|
|
297
|
+
* - Async callbacks registered via {@link Detokeniser.addCallbackAsync}
|
|
298
|
+
* - Async-only date expressions: `addWorkingDays`, `followingWorkingDay`, `nextPublicHoliday`
|
|
299
|
+
*
|
|
300
|
+
* Note: sync callbacks registered via {@link Detokeniser.addCallbackSync} are **not** invoked
|
|
301
|
+
* during async processing — re-register them with {@link Detokeniser.addCallbackAsync} if needed.
|
|
302
|
+
*
|
|
303
|
+
* @param tokenisedString - String potentially containing `[[...]]` tokens
|
|
304
|
+
* @returns Promise resolving to the input string with all tokens replaced
|
|
305
|
+
* @throws If any token is malformed, unsupported, or a callback throws
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* // Async-only date expressions require doAsync and a region qualifier
|
|
309
|
+
* await Detokeniser.doAsync('[[date(us-east)|addWorkingDays(5)|dd-MM-yyyy]]');
|
|
310
|
+
* await Detokeniser.doAsync('[[date(eu-london)|nextPublicHoliday([[date|today|epoch]],90)|dd-MM-yyyy]]');
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
* // Async callback for database-driven tokens
|
|
314
|
+
* Detokeniser.addCallbackAsync(async (delim, token) => {
|
|
315
|
+
* const [type, key] = token.split(delim);
|
|
316
|
+
* if (type !== 'db') return undefined;
|
|
317
|
+
* return await fetchFromDatabase(key);
|
|
318
|
+
* });
|
|
319
|
+
* await Detokeniser.doAsync('User: [[db|users.name.first]]');
|
|
320
|
+
*/
|
|
321
|
+
static doAsync(tokenisedString: string): Promise<string>;
|
|
322
|
+
private static doDeEscapesIfRequired;
|
|
323
|
+
private static doPreamble;
|
|
324
|
+
private static doTokenMain;
|
|
325
|
+
private static doToken;
|
|
326
|
+
private static asyncDoToken;
|
|
327
|
+
private static doSettingToken;
|
|
328
|
+
/**
|
|
329
|
+
* Base64-encodes or decodes a string.
|
|
330
|
+
*
|
|
331
|
+
* This is the underlying handler for `[[base64|encode|...]]` and `[[base64|decode|...]]` tokens
|
|
332
|
+
* but is also exposed for direct use.
|
|
333
|
+
*
|
|
334
|
+
* @param original - The string to encode or decode
|
|
335
|
+
* @param direction - `"encode"` to base64-encode; `"decode"` to base64-decode
|
|
336
|
+
* @returns The encoded or decoded string
|
|
337
|
+
* @throws If the conversion fails (e.g. invalid base64 input for decode)
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* Detokeniser.doBase64('Hello World', 'encode'); // → 'SGVsbG8gV29ybGQ='
|
|
341
|
+
* Detokeniser.doBase64('SGVsbG8gV29ybGQ=', 'decode'); // → 'Hello World'
|
|
342
|
+
*/
|
|
343
|
+
static doBase64(original: string, direction: "encode" | "decode"): string;
|
|
344
|
+
private static doJWTToken;
|
|
345
|
+
private static doMockRequests;
|
|
346
|
+
/**
|
|
347
|
+
* Extracts the content inside the first set of parentheses in `input`, respecting the escape char.
|
|
348
|
+
* An escaped `)` (i.e. `/)`) is treated as a literal `)` and does not end the content.
|
|
349
|
+
* @example parseParenContent("from(abc/))") → "abc)"
|
|
350
|
+
* @example parseParenContent("from(xyz/[[)") → "xyz[[" (with default escape char `/`)
|
|
351
|
+
*/
|
|
352
|
+
private static parseParenContent;
|
|
353
|
+
private static doRandomToken;
|
|
354
|
+
private static doDateTokenPreamble;
|
|
355
|
+
private static doDateTokenPostamble;
|
|
356
|
+
private static doDateTokenNonAsyncVerbs;
|
|
357
|
+
private static doDateToken;
|
|
358
|
+
private static asyncDoDateToken;
|
|
359
|
+
private static getNextPublicHoliday;
|
|
360
|
+
private static getFollowingDay;
|
|
361
|
+
private static getParsedDateOffset;
|
|
362
|
+
private static doRandomDate;
|
|
363
|
+
private static getOffset;
|
|
364
|
+
private static numberOfDays;
|
|
365
|
+
private static doRandomFloat;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Signature for an asynchronous custom token handler registered via {@link Detokeniser.addCallbackAsync}.
|
|
369
|
+
*
|
|
370
|
+
* @param delimiter - Current delimiter character (default `|`)
|
|
371
|
+
* @param token - Full token body without `[[` / `]]` endstops, e.g. `"mytype|arg1|arg2"`
|
|
372
|
+
* @returns Promise resolving to the replacement string, or `undefined` to pass to the next handler
|
|
373
|
+
*/
|
|
374
|
+
export interface Detokeniser_Callback_Async {
|
|
375
|
+
(delimiter: string, token: string): Promise<string | undefined>;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Signature for a synchronous custom token handler registered via {@link Detokeniser.addCallbackSync}.
|
|
379
|
+
*
|
|
380
|
+
* @param delimiter - Current delimiter character (default `|`)
|
|
381
|
+
* @param token - Full token body without `[[` / `]]` endstops, e.g. `"mytype|arg1|arg2"`
|
|
382
|
+
* @returns The replacement string, or `undefined` to pass to the next handler
|
|
383
|
+
*/
|
|
384
|
+
export interface Detokeniser_Callback_Sync {
|
|
385
|
+
(delimiter: string, token: string): string | undefined;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Options passed to {@link Detokeniser.do} and {@link Detokeniser.doAsync}.
|
|
389
|
+
*/
|
|
390
|
+
export interface Detokeniser_DoOptions {
|
|
391
|
+
/**
|
|
392
|
+
* Key/value pairs made available to `[[setting|profileParameterName: "key"]]` tokens.
|
|
393
|
+
* Typically populated from Cucumber world parameters or a test profile configuration object.
|
|
394
|
+
* @example { username: 'alice', environment: 'staging' }
|
|
395
|
+
*/
|
|
396
|
+
contextParameters?: Record<string, unknown>;
|
|
397
|
+
}
|
|
398
|
+
export declare namespace Detokeniser {
|
|
399
|
+
type Callback_Async = Detokeniser_Callback_Async;
|
|
400
|
+
type Callback_Sync = Detokeniser_Callback_Sync;
|
|
401
|
+
type DoOptions = Detokeniser_DoOptions;
|
|
402
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Logger } from "./logger/logger";
|
|
2
|
+
export { Logger, Logger as Log };
|
|
3
|
+
export declare const LogLevels: {
|
|
4
|
+
readonly Maximum: number;
|
|
5
|
+
readonly Verbose: number;
|
|
6
|
+
readonly FrameworkDebug: 6;
|
|
7
|
+
readonly FrameworkInformation: 5;
|
|
8
|
+
readonly TestDebug: 4;
|
|
9
|
+
readonly TestInformation: 3;
|
|
10
|
+
readonly Warning: 2;
|
|
11
|
+
readonly Error: 1;
|
|
12
|
+
readonly NoOutput: 0;
|
|
13
|
+
};
|
|
14
|
+
export type { LogLevel, VideoOptions, WriteLineOptions, LogOutputCallbackSignature, } from "./logger/types";
|
|
15
|
+
export { JsonUtils } from "./jsonUtils/jsonUtils";
|
|
16
|
+
export { StringUtils } from "./stringUtils/stringUtils";
|
|
17
|
+
export { Utils, ExistingFileWriteActions } from "./utils/utils";
|
|
18
|
+
export type { AssertTypeMap, ActionAndParams } from "./utils/utils";
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON utility methods for querying, manipulating, parsing and validating JSON objects.
|
|
3
|
+
* All methods are static — no instantiation required.
|
|
4
|
+
*/
|
|
5
|
+
export declare class JsonUtils {
|
|
6
|
+
/**
|
|
7
|
+
* Returns the JSONPath path to the parent of the node addressed by the given path.
|
|
8
|
+
*
|
|
9
|
+
* @param pathToChild - A valid JSONPath path to a JSON node.
|
|
10
|
+
* @returns The JSONPath path to the parent of the given node.
|
|
11
|
+
* @throws {Error} If the node has no parent (i.e. it is a top-level node).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* JsonUtils.getParentPath("$.a.b.c"); // returns "$.a.b"
|
|
15
|
+
*/
|
|
16
|
+
static getParentPath(pathToChild: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Returns a deep copy of the given object with the property identified by the
|
|
19
|
+
* given JSONPath renamed. The original object is not mutated.
|
|
20
|
+
*
|
|
21
|
+
* The JSONPath must match exactly one property — zero or multiple matches will throw.
|
|
22
|
+
*
|
|
23
|
+
* @param jsonObject - The source object containing the property to rename.
|
|
24
|
+
* @param pathToPropertyToRename - A valid JSONPath identifying the property to rename.
|
|
25
|
+
* @param newName - The new name for the property.
|
|
26
|
+
* @returns A new deep-copied object with the property renamed.
|
|
27
|
+
* @throws {Error} If the JSONPath matches zero or more than one property.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const result = JsonUtils.withRenamedProperty({ a: { b: 1 } }, "$.a.b", "c");
|
|
31
|
+
* // result => { a: { c: 1 } }
|
|
32
|
+
*/
|
|
33
|
+
static withRenamedProperty(jsonObject: object, pathToPropertyToRename: string, newName: string): object;
|
|
34
|
+
/**
|
|
35
|
+
* Shallow-merges the given object into the property identified by the given JSONPath
|
|
36
|
+
* within the source object. Properties in `objectToMerge` overwrite same-named
|
|
37
|
+
* properties in the target. The JSONPath must match exactly one property.
|
|
38
|
+
*
|
|
39
|
+
* Use `"$"` as the path to merge directly into the root object.
|
|
40
|
+
*
|
|
41
|
+
* @param jsonObject - The source object containing the property to merge into.
|
|
42
|
+
* @param pathToPropertyToMergeInto - A valid JSONPath identifying the target property.
|
|
43
|
+
* @param objectToMerge - The object whose properties will be merged into the target.
|
|
44
|
+
* @returns The updated source object with the merge applied.
|
|
45
|
+
* @throws {Error} If the JSONPath matches zero or more than one property.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* const result = JsonUtils.mergeObjectIntoProperty({ a: { x: 1 } }, "$.a", { y: 2 });
|
|
49
|
+
* // result => { a: { x: 1, y: 2 } }
|
|
50
|
+
*/
|
|
51
|
+
static mergeObjectIntoProperty(jsonObject: object, pathToPropertyToMergeInto: string, objectToMerge: object): object;
|
|
52
|
+
/**
|
|
53
|
+
* Reads a file from disk and parses its contents as JSON, returning the result as an object.
|
|
54
|
+
* Returns an empty object `{}` if the file contents are not valid JSON.
|
|
55
|
+
*
|
|
56
|
+
* @param pathAndFilename - Path to the JSON file to read.
|
|
57
|
+
* @param options - Optional settings:
|
|
58
|
+
* - `encoding` — File encoding to use when reading (default: `"utf-8"`).
|
|
59
|
+
* - `detokeniseFileContents` — When `true`, passes file contents through the
|
|
60
|
+
* detokeniser before parsing (default: `false`).
|
|
61
|
+
* @returns The parsed JSON as an object, or `{}` if the file contains invalid JSON.
|
|
62
|
+
* @throws {Error} If the file cannot be read.
|
|
63
|
+
*/
|
|
64
|
+
static getObjectFromFile(pathAndFilename: string, options?: {
|
|
65
|
+
encoding?: BufferEncoding;
|
|
66
|
+
detokeniseFileContents?: boolean;
|
|
67
|
+
}): object;
|
|
68
|
+
/**
|
|
69
|
+
* Parses a JSON (or optionally JSON5) string into an object.
|
|
70
|
+
* If `item` is `null`, an empty object `{}` is returned.
|
|
71
|
+
*
|
|
72
|
+
* @param item - The JSON string to parse, or `null` (returns `{}`).
|
|
73
|
+
* @param useJson5 - When `true`, parses using JSON5 rules, allowing comments,
|
|
74
|
+
* trailing commas, unquoted keys, etc. Defaults to `false` (strict JSON).
|
|
75
|
+
* @returns The parsed object.
|
|
76
|
+
* @throws {Error} If `item` is not a string or null, or if parsing fails.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* JsonUtils.parse('{"a":1}'); // => { a: 1 }
|
|
80
|
+
* JsonUtils.parse(null); // => {}
|
|
81
|
+
* JsonUtils.parse("{a:1}", true); // => { a: 1 } (JSON5)
|
|
82
|
+
*/
|
|
83
|
+
static parse(item: string | null, useJson5?: boolean): any;
|
|
84
|
+
/**
|
|
85
|
+
* Checks whether the given item is valid JSON.
|
|
86
|
+
*
|
|
87
|
+
* Returns `true` if:
|
|
88
|
+
* - `item` is a string that parses as a JSON object (not a primitive), or
|
|
89
|
+
* - `item` is an object that can be `JSON.stringify`-ed and parsed back as an object.
|
|
90
|
+
*
|
|
91
|
+
* Returns `false` for `undefined`, unparseable strings, or values that parse
|
|
92
|
+
* to a primitive (e.g. `"true"`, `"42"`).
|
|
93
|
+
*
|
|
94
|
+
* @param item - The value to check.
|
|
95
|
+
* @param allowJson5 - When `true`, accepts JSON5 syntax (comments, trailing commas,
|
|
96
|
+
* unquoted keys, etc.). Defaults to `false` (strict JSON only).
|
|
97
|
+
* @returns `true` if the item represents a valid JSON object, `false` otherwise.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* JsonUtils.isJson('{"a":1}'); // true
|
|
101
|
+
* JsonUtils.isJson('42'); // false (primitive)
|
|
102
|
+
* JsonUtils.isJson(undefined); // false
|
|
103
|
+
* JsonUtils.isJson("{a:1}", true); // true (JSON5)
|
|
104
|
+
*/
|
|
105
|
+
static isJson(item: string | object | undefined | null, allowJson5?: boolean): boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Returns all properties within a JSON object that match the given JSONPath expression.
|
|
108
|
+
* Each result includes the matched value, its JSON Pointer path, and its parent object.
|
|
109
|
+
*
|
|
110
|
+
* @param jsonObject - The object (or JSON string) to query.
|
|
111
|
+
* @param pathToJSONProperties - A valid JSONPath expression identifying the properties to retrieve.
|
|
112
|
+
* @returns An array of matches, each with `value`, `pointer` (JSON Pointer string), and `parent`.
|
|
113
|
+
* Returns an empty array if no properties match.
|
|
114
|
+
* @throws {Error} If `jsonObject` is not a valid JSON object, or if the JSONPath query fails.
|
|
115
|
+
*
|
|
116
|
+
* @see https://jsonpath-plus.github.io/JSONPath/docs/ts/
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* JsonUtils.getPropertiesMatchingPath({ a: { b: 1 } }, "$.a.b");
|
|
120
|
+
* // => [{ value: 1, pointer: "/a/b", parent: { b: 1 } }]
|
|
121
|
+
*/
|
|
122
|
+
static getPropertiesMatchingPath(jsonObject: object | string, pathToJSONProperties: string): Array<{
|
|
123
|
+
value: unknown;
|
|
124
|
+
pointer: string;
|
|
125
|
+
parent: object;
|
|
126
|
+
}>;
|
|
127
|
+
/**
|
|
128
|
+
* Sets, adds, or deletes a property within a JSON object identified by a JSONPath expression.
|
|
129
|
+
* Operates on a deep copy of `currentObject` — the original is not mutated.
|
|
130
|
+
*
|
|
131
|
+
* - If the property **exists**, its value is updated.
|
|
132
|
+
* - If the property **does not exist**, it is added with the given value.
|
|
133
|
+
* - To **delete** a property, pass the string `"_undefined"` as the value.
|
|
134
|
+
* Note: deleting a property that does not exist will throw.
|
|
135
|
+
*
|
|
136
|
+
* The JSONPath must match zero or one property — multiple matches will throw.
|
|
137
|
+
*
|
|
138
|
+
* When `Log.loggingLevel` is below `LogLevels.TestInformation`, a detailed error
|
|
139
|
+
* including the full object and value is written to the log before throwing.
|
|
140
|
+
*
|
|
141
|
+
* @param currentObject - The source object to update (not mutated).
|
|
142
|
+
* @param pathString - A valid JSONPath expression identifying the property to set/add/delete.
|
|
143
|
+
* @param value - The value to set. Pass the string `"_undefined"` to delete the property.
|
|
144
|
+
* @returns A new deep-copied object with the change applied.
|
|
145
|
+
* @throws {Error} If `currentObject` is not a valid JSON object, if the JSONPath matches
|
|
146
|
+
* more than one property, if deletion is attempted on a non-existent property, or if
|
|
147
|
+
* the value could not be verified after being set.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* JsonUtils.updateJSONObject({ a: 1 }, "$.a", 2); // => { a: 2 }
|
|
151
|
+
* JsonUtils.updateJSONObject({ a: 1 }, "$.b", "hello"); // => { a: 1, b: "hello" }
|
|
152
|
+
* JsonUtils.updateJSONObject({ a: 1 }, "$.a", "_undefined"); // => {}
|
|
153
|
+
*/
|
|
154
|
+
static updateJSONObject(currentObject: object, pathString: string, value: string | object | boolean | number | null): object;
|
|
155
|
+
/**
|
|
156
|
+
* Returns the number of properties within a JSON object that match the given JSONPath expression.
|
|
157
|
+
*
|
|
158
|
+
* @param jsonObject - The object to query.
|
|
159
|
+
* @param pathString - A valid JSONPath expression identifying the properties to count.
|
|
160
|
+
* @returns The number of matching properties, or `0` if none match.
|
|
161
|
+
* @throws {Error} If `jsonObject` is not a valid JSON object.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* JsonUtils.getMatchingJSONPropertyCount({ a: 1, b: 2 }, "$.a"); // => 1
|
|
165
|
+
* JsonUtils.getMatchingJSONPropertyCount({ a: 1, b: 2 }, "$.*"); // => 2
|
|
166
|
+
*/
|
|
167
|
+
static getMatchingJSONPropertyCount(jsonObject: object, pathString: string): number;
|
|
168
|
+
/**
|
|
169
|
+
* Removes all properties matching the given key names from a JSON object, including
|
|
170
|
+
* those nested within child objects or arrays. Operates directly on the passed object
|
|
171
|
+
* (mutates in place).
|
|
172
|
+
*
|
|
173
|
+
* @param jsonObject - The object (or array of objects) to remove properties from.
|
|
174
|
+
* @param removeKeys - One or more property names to remove at any depth.
|
|
175
|
+
* @param throwError - When `true`, any error encountered during removal is re-thrown.
|
|
176
|
+
* When `false` (default), errors are logged and silently swallowed.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* const obj = { a: 1, b: { a: 2, c: 3 } };
|
|
180
|
+
* JsonUtils.removeJsonPropertyByKey(obj, ["a"]);
|
|
181
|
+
* // obj => { b: { c: 3 } }
|
|
182
|
+
*/
|
|
183
|
+
static removeJsonPropertyByKey(jsonObject: object | Array<object>, removeKeys: string[], throwError?: boolean): void;
|
|
184
|
+
/**
|
|
185
|
+
* Escapes all RegExp special characters in a string so it can be safely used
|
|
186
|
+
* as a literal pattern in a `RegExp` constructor.
|
|
187
|
+
*
|
|
188
|
+
* @param toBeEscaped - The string to escape.
|
|
189
|
+
* @returns The input string with all RegExp special characters escaped.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* JsonUtils.escapeRegExp("a.b+c"); // => "a\\.b\\+c"
|
|
193
|
+
* new RegExp(JsonUtils.escapeRegExp("1+1=2")); // matches literal "1+1=2"
|
|
194
|
+
*/
|
|
195
|
+
static escapeRegExp(toBeEscaped: string): string;
|
|
196
|
+
}
|