@controlium/utils 1.0.1 → 1.0.2-alpha.2
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/dist/cjs/detokeniser/detokeniser.js +50 -170
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/logger/logger.js +5 -3
- package/dist/cjs/mock/mock.js +509 -0
- package/dist/esm/detokeniser/detokeniser.js +50 -170
- package/dist/esm/index.js +1 -0
- package/dist/esm/logger/logger.js +5 -3
- package/dist/esm/mock/mock.js +505 -0
- package/dist/types/detokeniser/detokeniser.d.ts +46 -128
- package/dist/types/index.d.ts +1 -0
- package/dist/types/logger/types.d.ts +3 -1
- package/dist/types/mock/mock.d.ts +391 -0
- package/package.json +17 -12
- package/dist/cjs/logger/logger.spec.js +0 -875
- package/dist/esm/logger/logger.spec.js +0 -873
- package/dist/types/logger/logger.spec.d.ts +0 -1
|
@@ -34,9 +34,6 @@ const MockUtils = {
|
|
|
34
34
|
* - **expression** — what to compute (type-specific)
|
|
35
35
|
* - **format** — how to format the result (type-specific, often optional)
|
|
36
36
|
*
|
|
37
|
-
* The delimiter `|` and the `[[` / `]]` endstops are configurable via {@link Detokeniser.delimiter} and
|
|
38
|
-
* {@link Detokeniser.tokenStartEndChars}, but the defaults cover the vast majority of use cases.
|
|
39
|
-
*
|
|
40
37
|
* ---
|
|
41
38
|
* ## Built-in token types
|
|
42
39
|
*
|
|
@@ -141,77 +138,21 @@ const MockUtils = {
|
|
|
141
138
|
*
|
|
142
139
|
* ---
|
|
143
140
|
* ## Extending with callbacks
|
|
144
|
-
* Custom token types are registered via {@link Detokeniser.
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
141
|
+
* Custom token types are registered via {@link Detokeniser.addCallback}. Both sync and async handlers
|
|
142
|
+
* share the same registration method and callback list. Callbacks are tried in registration order;
|
|
143
|
+
* return `undefined` to pass to the next callback. If all callbacks return `undefined` and no built-in
|
|
144
|
+
* handler matched, an error is thrown.
|
|
145
|
+
*
|
|
146
|
+
* Async callbacks are silently skipped when {@link Detokeniser.do} (sync) is used — use
|
|
147
|
+
* {@link Detokeniser.doAsync} if your callback returns a Promise.
|
|
148
148
|
*
|
|
149
|
-
* @see {@link Detokeniser.
|
|
150
|
-
* @see {@link Detokeniser.addCallbackAsync}
|
|
149
|
+
* @see {@link Detokeniser.addCallback}
|
|
151
150
|
* @see {@link Detokeniser.do}
|
|
152
151
|
* @see {@link Detokeniser.doAsync}
|
|
153
152
|
*/
|
|
154
153
|
class Detokeniser {
|
|
155
|
-
/**
|
|
156
|
-
* Gets the current single-character delimiter used to separate token parts.
|
|
157
|
-
* Default: `|`
|
|
158
|
-
*/
|
|
159
|
-
static get delimiter() {
|
|
160
|
-
return this._delimiter;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Sets the single-character delimiter used to separate token parts.
|
|
164
|
-
* The new value applies to all subsequent {@link Detokeniser.do} / {@link Detokeniser.doAsync} calls.
|
|
165
|
-
* @param newDelimiter - Exactly one character
|
|
166
|
-
* @throws If `newDelimiter` is not exactly one character
|
|
167
|
-
* @example
|
|
168
|
-
* Detokeniser.delimiter = ':';
|
|
169
|
-
* Detokeniser.do('[[random:digits:6]]'); // → e.g. '482910'
|
|
170
|
-
* Detokeniser.reset(); // restore default '|'
|
|
171
|
-
*/
|
|
172
|
-
static set delimiter(newDelimiter) {
|
|
173
|
-
if (newDelimiter.length !== 1) {
|
|
174
|
-
const errTxt = `Invalid delimiter [${newDelimiter}]. Must be exactly 1 character!`;
|
|
175
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errTxt);
|
|
176
|
-
throw new Error(errTxt);
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
this._delimiter = newDelimiter;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Sets the start and end token sequences.
|
|
184
|
-
* @param startEndChars - An even-length string whose first half is the start sequence and second half the end sequence.
|
|
185
|
-
* @example Detokeniser.tokenStartEndChars = "[[]]"; // start = "[[", end = "]]"
|
|
186
|
-
* @remarks Start and end sequences must differ. Minimum total length is 2 (one char each).
|
|
187
|
-
*/
|
|
188
|
-
static set tokenStartEndChars(startEndChars) {
|
|
189
|
-
if (startEndChars.length < 2 || startEndChars.length % 2 !== 0) {
|
|
190
|
-
const errTxt = `Invalid start/end chars [${startEndChars}]. Must be an even number of characters (minimum 2) — first half as start sequence, second half as end sequence!`;
|
|
191
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errTxt);
|
|
192
|
-
throw new Error(errTxt);
|
|
193
|
-
}
|
|
194
|
-
const half = startEndChars.length / 2;
|
|
195
|
-
const start = startEndChars.substring(0, half);
|
|
196
|
-
const end = startEndChars.substring(half);
|
|
197
|
-
if (start === end) {
|
|
198
|
-
const errTxt = `Invalid start/end chars — start sequence [${start}] must differ from end sequence [${end}]!`;
|
|
199
|
-
index_1.Log.writeLine(index_1.LogLevels.Error, errTxt);
|
|
200
|
-
throw new Error(errTxt);
|
|
201
|
-
}
|
|
202
|
-
this._startTokenChar = start;
|
|
203
|
-
this._endTokenChar = end;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Gets the current token start/end sequences concatenated (e.g. `"[[]]"`).
|
|
207
|
-
*/
|
|
208
|
-
static get tokenStartEndChars() {
|
|
209
|
-
return this._startTokenChar + this._endTokenChar;
|
|
210
|
-
}
|
|
211
154
|
/**
|
|
212
155
|
* Resets the Detokeniser to factory defaults:
|
|
213
|
-
* - Token endstops restored to `[[` / `]]`
|
|
214
|
-
* - Delimiter restored to `|`
|
|
215
156
|
* - Escape char restored to `/`
|
|
216
157
|
* - All registered sync and async callbacks cleared
|
|
217
158
|
*
|
|
@@ -220,121 +161,58 @@ class Detokeniser {
|
|
|
220
161
|
* afterEach(() => Detokeniser.reset());
|
|
221
162
|
*/
|
|
222
163
|
static reset() {
|
|
223
|
-
this._endTokenChar = "]]";
|
|
224
|
-
this._startTokenChar = "[[";
|
|
225
164
|
this.EscapeChar = "/";
|
|
226
|
-
this.
|
|
227
|
-
if (this._syncCallbacks) {
|
|
228
|
-
this._syncCallbacks = [];
|
|
229
|
-
}
|
|
230
|
-
if (this._asyncCallbacks) {
|
|
231
|
-
this._asyncCallbacks = [];
|
|
232
|
-
}
|
|
233
|
-
this._asyncCallbacks = undefined;
|
|
234
|
-
this._syncCallbacks = undefined;
|
|
165
|
+
this._callbacks = undefined;
|
|
235
166
|
}
|
|
236
167
|
/**
|
|
237
|
-
* Registers a
|
|
168
|
+
* Registers a custom token handler. Both sync and async handlers are registered via this method
|
|
169
|
+
* and share the same callback list.
|
|
238
170
|
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
171
|
+
* Callbacks are tried in registration order. The first to return a non-`undefined` value wins.
|
|
172
|
+
* Return `undefined` to pass to the next callback. If all callbacks return `undefined` and no
|
|
173
|
+
* built-in handler matched, an error is thrown.
|
|
242
174
|
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
175
|
+
* Async callbacks (those returning a `Promise`) are silently skipped when {@link Detokeniser.do}
|
|
176
|
+
* is used — register an async callback only if you intend to call {@link Detokeniser.doAsync}.
|
|
177
|
+
*
|
|
178
|
+
* @param callback - `(token: string) => string | undefined | Promise<string | undefined>`
|
|
179
|
+
* - `token` — full token body without `[[` / `]]`, e.g. `"mytype|arg1|arg2"` (delimiter is always `|`)
|
|
246
180
|
*
|
|
247
181
|
* @example
|
|
248
|
-
* //
|
|
249
|
-
* Detokeniser.
|
|
250
|
-
* const [type, name] = token.split(
|
|
251
|
-
* if (type
|
|
182
|
+
* // Sync handler for [[env|VAR_NAME]] tokens
|
|
183
|
+
* Detokeniser.addCallback((token) => {
|
|
184
|
+
* const [type, name] = token.split('|');
|
|
185
|
+
* if (type !== 'env') return undefined;
|
|
252
186
|
* return process.env[name] ?? '';
|
|
253
187
|
* });
|
|
254
188
|
* Detokeniser.do('Path: [[env|HOME]]'); // → 'Path: /home/user'
|
|
255
189
|
*
|
|
256
190
|
* @example
|
|
257
|
-
* //
|
|
258
|
-
* Detokeniser.
|
|
259
|
-
* const [type,
|
|
260
|
-
* if (type
|
|
261
|
-
* return undefined;
|
|
262
|
-
* });
|
|
263
|
-
* Detokeniser.addCallbackSync((delimiter, token) => {
|
|
264
|
-
* const [type, value] = token.split(delimiter);
|
|
265
|
-
* if (type === 'lower') return value.toLowerCase();
|
|
266
|
-
* return undefined;
|
|
267
|
-
* });
|
|
268
|
-
* Detokeniser.do('[[upper|hello]] [[lower|WORLD]]'); // → 'HELLO world'
|
|
269
|
-
*
|
|
270
|
-
* @see {@link Detokeniser.resetSyncCallbacks} to remove all sync callbacks
|
|
271
|
-
* @see {@link Detokeniser.addCallbackAsync} for async token handlers
|
|
272
|
-
*/
|
|
273
|
-
static addCallbackSync(callback) {
|
|
274
|
-
if (!this._syncCallbacks) {
|
|
275
|
-
this._syncCallbacks = new Array();
|
|
276
|
-
}
|
|
277
|
-
this._syncCallbacks.push(callback);
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Registers an asynchronous custom token handler.
|
|
281
|
-
*
|
|
282
|
-
* Works identically to {@link Detokeniser.addCallbackSync} but is invoked by {@link Detokeniser.doAsync}.
|
|
283
|
-
* Async callbacks are tried in registration order; the first to return a non-`undefined` value wins.
|
|
284
|
-
* Return `undefined` to pass to the next callback.
|
|
285
|
-
*
|
|
286
|
-
* @param asyncCallback - `(delimiter: string, token: string) => Promise<string | undefined>`
|
|
287
|
-
* - `delimiter` — current delimiter character (default `|`)
|
|
288
|
-
* - `token` — full token body without `[[` / `]]`, e.g. `"mytype|arg1|arg2"`
|
|
289
|
-
*
|
|
290
|
-
* @example
|
|
291
|
-
* // Handle [[db|table|column|whereClause]] tokens
|
|
292
|
-
* Detokeniser.addCallbackAsync(async (delimiter, token) => {
|
|
293
|
-
* const [type, table, column, where] = token.split(delimiter);
|
|
294
|
-
* if (type.toLowerCase() !== 'db') return undefined;
|
|
191
|
+
* // Async handler for [[db|table|column|where]] tokens
|
|
192
|
+
* Detokeniser.addCallback(async (token) => {
|
|
193
|
+
* const [type, table, column, where] = token.split('|');
|
|
194
|
+
* if (type !== 'db') return undefined;
|
|
295
195
|
* const row = await db.query(`SELECT ${column} FROM ${table} WHERE ${where} LIMIT 1`);
|
|
296
196
|
* return String(row[column]);
|
|
297
197
|
* });
|
|
298
198
|
* const result = await Detokeniser.doAsync('ID: [[db|users|id|active=1]]');
|
|
299
199
|
*
|
|
300
|
-
* @
|
|
301
|
-
*
|
|
302
|
-
* Detokeniser.addCallbackAsync(async (delimiter, token) => {
|
|
303
|
-
* const [type, key] = token.split(delimiter);
|
|
304
|
-
* if (type !== 'cache') return undefined;
|
|
305
|
-
* return await redis.get(key);
|
|
306
|
-
* });
|
|
307
|
-
* // [[random|digits|8]] resolves first, then [[cache|...]] receives the result
|
|
308
|
-
* await Detokeniser.doAsync('Val: [[cache|prefix-[[random|digits|8]]]]');
|
|
309
|
-
*
|
|
310
|
-
* @see {@link Detokeniser.resetAsyncCallbacks} to remove all async callbacks
|
|
311
|
-
* @see {@link Detokeniser.addCallbackSync} for synchronous token handlers
|
|
200
|
+
* @see {@link Detokeniser.resetCallbacks} to remove all registered callbacks
|
|
201
|
+
* @see {@link Detokeniser.doAsync} for async token resolution
|
|
312
202
|
*/
|
|
313
|
-
static
|
|
314
|
-
if (!this.
|
|
315
|
-
this.
|
|
203
|
+
static addCallback(callback) {
|
|
204
|
+
if (!this._callbacks) {
|
|
205
|
+
this._callbacks = new Array();
|
|
316
206
|
}
|
|
317
|
-
this.
|
|
207
|
+
this._callbacks.push(callback);
|
|
318
208
|
}
|
|
319
209
|
/**
|
|
320
|
-
* Removes all registered
|
|
210
|
+
* Removes all registered callbacks. Built-in token handlers are unaffected.
|
|
321
211
|
* Use between tests or scenarios to ensure callback isolation.
|
|
322
|
-
* @see {@link Detokeniser.reset} to also
|
|
212
|
+
* @see {@link Detokeniser.reset} to also restore all defaults
|
|
323
213
|
*/
|
|
324
|
-
static
|
|
325
|
-
|
|
326
|
-
this._syncCallbacks = [];
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Removes all registered async callbacks. Built-in token handlers are unaffected.
|
|
331
|
-
* Use between tests or scenarios to ensure callback isolation.
|
|
332
|
-
* @see {@link Detokeniser.reset} to also clear sync callbacks and restore all defaults
|
|
333
|
-
*/
|
|
334
|
-
static resetAsyncCallbacks() {
|
|
335
|
-
if (this._asyncCallbacks) {
|
|
336
|
-
this._asyncCallbacks = [];
|
|
337
|
-
}
|
|
214
|
+
static resetCallbacks() {
|
|
215
|
+
this._callbacks = undefined;
|
|
338
216
|
}
|
|
339
217
|
/**
|
|
340
218
|
* Synchronously resolves all tokens in the given string and returns the result.
|
|
@@ -421,8 +299,8 @@ class Detokeniser {
|
|
|
421
299
|
*
|
|
422
300
|
* @example
|
|
423
301
|
* // Async callback for database-driven tokens
|
|
424
|
-
* Detokeniser.addCallbackAsync(async (
|
|
425
|
-
* const [type, key] = token.split(
|
|
302
|
+
* Detokeniser.addCallbackAsync(async (token) => {
|
|
303
|
+
* const [type, key] = token.split('|');
|
|
426
304
|
* if (type !== 'db') return undefined;
|
|
427
305
|
* return await fetchFromDatabase(key);
|
|
428
306
|
* });
|
|
@@ -556,9 +434,12 @@ class Detokeniser {
|
|
|
556
434
|
//
|
|
557
435
|
try {
|
|
558
436
|
if (index_1.Utils.isNullOrUndefined(processedToken)) {
|
|
559
|
-
if (typeof this.
|
|
560
|
-
this.
|
|
561
|
-
|
|
437
|
+
if (typeof this._callbacks != "undefined") {
|
|
438
|
+
this._callbacks.every((callback) => {
|
|
439
|
+
const result = callback(token);
|
|
440
|
+
if (result instanceof Promise)
|
|
441
|
+
return true; // skip async callbacks in sync context
|
|
442
|
+
processedToken = result;
|
|
562
443
|
return index_1.Utils.isNullOrUndefined(processedToken);
|
|
563
444
|
});
|
|
564
445
|
}
|
|
@@ -589,9 +470,9 @@ class Detokeniser {
|
|
|
589
470
|
//
|
|
590
471
|
try {
|
|
591
472
|
if (index_1.Utils.isNullOrUndefined(processedToken)) {
|
|
592
|
-
if (typeof this.
|
|
593
|
-
for (
|
|
594
|
-
processedToken = await
|
|
473
|
+
if (typeof this._callbacks != "undefined") {
|
|
474
|
+
for (const callback of this._callbacks) {
|
|
475
|
+
processedToken = await Promise.resolve(callback(token));
|
|
595
476
|
if (!index_1.Utils.isNullOrUndefined(processedToken))
|
|
596
477
|
break;
|
|
597
478
|
}
|
|
@@ -837,7 +718,7 @@ class Detokeniser {
|
|
|
837
718
|
case "addhours":
|
|
838
719
|
returnDate = (0, date_fns_1.addHours)(date, this.getParsedDateOffset(dateTokenPrep.params, dateTokenPrep.errParseDateOffset)).getTime();
|
|
839
720
|
break;
|
|
840
|
-
case "
|
|
721
|
+
case "addminutes":
|
|
841
722
|
returnDate = (0, date_fns_1.addMinutes)(date, this.getParsedDateOffset(dateTokenPrep.params, dateTokenPrep.errParseDateOffset)).getTime();
|
|
842
723
|
break;
|
|
843
724
|
case "followingday": {
|
|
@@ -1064,8 +945,7 @@ Detokeniser._endTokenChar = "]]";
|
|
|
1064
945
|
Detokeniser._startTokenChar = "[[";
|
|
1065
946
|
Detokeniser.EscapeChar = "/";
|
|
1066
947
|
Detokeniser._delimiter = "|";
|
|
1067
|
-
Detokeniser.
|
|
1068
|
-
Detokeniser._syncCallbacks = undefined;
|
|
948
|
+
Detokeniser._callbacks = undefined;
|
|
1069
949
|
class InnermostToken {
|
|
1070
950
|
constructor(inputString, StartTokenChar, EndTokenChar, EscapeChar) {
|
|
1071
951
|
let startIndex = -1;
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ExistingFileWriteActions = exports.Utils = exports.StringUtils = exports.JsonUtils = exports.LogLevels = exports.Log = exports.Logger = void 0;
|
|
3
|
+
exports.Mock = exports.ExistingFileWriteActions = exports.Utils = exports.StringUtils = exports.JsonUtils = exports.LogLevels = exports.Log = exports.Logger = void 0;
|
|
4
4
|
const logger_1 = require("./logger/logger");
|
|
5
5
|
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logger_1.Logger; } });
|
|
6
6
|
Object.defineProperty(exports, "Log", { enumerable: true, get: function () { return logger_1.Logger; } });
|
|
@@ -12,3 +12,5 @@ Object.defineProperty(exports, "StringUtils", { enumerable: true, get: function
|
|
|
12
12
|
var utils_1 = require("./utils/utils");
|
|
13
13
|
Object.defineProperty(exports, "Utils", { enumerable: true, get: function () { return utils_1.Utils; } });
|
|
14
14
|
Object.defineProperty(exports, "ExistingFileWriteActions", { enumerable: true, get: function () { return utils_1.ExistingFileWriteActions; } });
|
|
15
|
+
var mock_1 = require("./mock/mock");
|
|
16
|
+
Object.defineProperty(exports, "Mock", { enumerable: true, get: function () { return mock_1.Mock; } });
|
|
@@ -488,7 +488,7 @@ class Logger {
|
|
|
488
488
|
const stackObj = {};
|
|
489
489
|
Error.captureStackTrace(stackObj, this.writeLine);
|
|
490
490
|
const stack = stackObj?.stack ?? "[Unknown]";
|
|
491
|
-
const callingMethodDetails = this.callingMethodDetails(stack);
|
|
491
|
+
const callingMethodDetails = this.callingMethodDetails(stack, options?.stackOffset ?? 0);
|
|
492
492
|
const maxLines = options?.maxLines ?? this.options.writeLine.maxLines;
|
|
493
493
|
const suppressAllPreamble = options?.suppressAllPreamble ??
|
|
494
494
|
this.options.writeLine.suppressAllPreamble;
|
|
@@ -708,7 +708,7 @@ class Logger {
|
|
|
708
708
|
return this.pad(levelOfWrite, WRITE_TYPE_PAD_WIDTH);
|
|
709
709
|
}
|
|
710
710
|
}
|
|
711
|
-
static callingMethodDetails(methodBase) {
|
|
711
|
+
static callingMethodDetails(methodBase, stackOffset = 0) {
|
|
712
712
|
let methodName = "<Unknown>";
|
|
713
713
|
let typeName = "";
|
|
714
714
|
if (methodBase) {
|
|
@@ -720,7 +720,9 @@ class Logger {
|
|
|
720
720
|
.findIndex((item) => !item.includes(LOGGER_STACK_FRAME_MARKER));
|
|
721
721
|
indexOfFirstNonLogLine =
|
|
722
722
|
indexOfFirstNonLogLine === -1 ? 1 : indexOfFirstNonLogLine + 1;
|
|
723
|
-
|
|
723
|
+
const safeOffset = Math.max(0, stackOffset);
|
|
724
|
+
const targetIndex = Math.min(indexOfFirstNonLogLine + safeOffset, methodBaseLines.length - 1);
|
|
725
|
+
methodName = methodBaseLines[targetIndex]
|
|
724
726
|
.replace(/\s\s+/g, " ")
|
|
725
727
|
.trim();
|
|
726
728
|
if (methodName.startsWith("at ")) {
|