@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.
@@ -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
+ }