@ls-stack/utils 3.36.0 → 3.38.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/dist/asyncQueue.cjs +2 -2
- package/dist/asyncQueue.js +2 -2
- package/dist/testUtils.cjs +44 -0
- package/dist/testUtils.d.cts +5 -1
- package/dist/testUtils.d.ts +5 -1
- package/dist/testUtils.js +44 -0
- package/dist/typeUtils.d.cts +8 -1
- package/dist/typeUtils.d.ts +8 -1
- package/dist/typedStrings.cjs +90 -0
- package/dist/typedStrings.d.cts +152 -0
- package/dist/typedStrings.d.ts +152 -0
- package/dist/typedStrings.js +57 -0
- package/dist/typingFnUtils.cjs +5 -0
- package/dist/typingFnUtils.d.cts +3 -1
- package/dist/typingFnUtils.d.ts +3 -1
- package/dist/typingFnUtils.js +4 -0
- package/docs/_media/modules.md +1 -0
- package/docs/modules.md +1 -0
- package/docs/testUtils.md +1 -1
- package/docs/typeUtils/README.md +18 -0
- package/docs/typedStrings.md +458 -0
- package/docs/typingFnUtils/-internal-.md +17 -1
- package/docs/typingFnUtils/README.md +35 -9
- package/package.json +7 -3
package/dist/asyncQueue.cjs
CHANGED
|
@@ -61,10 +61,10 @@ var AsyncQueue = class {
|
|
|
61
61
|
this.#signal = signal;
|
|
62
62
|
this.#taskTimeout = taskTimeout;
|
|
63
63
|
this.events.on("error", (e) => {
|
|
64
|
-
this.failures.push(e);
|
|
64
|
+
this.failures.push(e.payload);
|
|
65
65
|
});
|
|
66
66
|
this.events.on("complete", (e) => {
|
|
67
|
-
this.completions.push(e);
|
|
67
|
+
this.completions.push(e.payload);
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
#enqueue(task) {
|
package/dist/asyncQueue.js
CHANGED
|
@@ -32,10 +32,10 @@ var AsyncQueue = class {
|
|
|
32
32
|
this.#signal = signal;
|
|
33
33
|
this.#taskTimeout = taskTimeout;
|
|
34
34
|
this.events.on("error", (e) => {
|
|
35
|
-
this.failures.push(e);
|
|
35
|
+
this.failures.push(e.payload);
|
|
36
36
|
});
|
|
37
37
|
this.events.on("complete", (e) => {
|
|
38
|
-
this.completions.push(e);
|
|
38
|
+
this.completions.push(e.payload);
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
#enqueue(task) {
|
package/dist/testUtils.cjs
CHANGED
|
@@ -1347,6 +1347,7 @@ function compactSnapshot(value, {
|
|
|
1347
1347
|
maxLineLength = 100,
|
|
1348
1348
|
showUndefined = false,
|
|
1349
1349
|
showBooleansAs = true,
|
|
1350
|
+
replaceValues,
|
|
1350
1351
|
rejectKeys,
|
|
1351
1352
|
filterKeys,
|
|
1352
1353
|
sortKeys,
|
|
@@ -1364,6 +1365,9 @@ function compactSnapshot(value, {
|
|
|
1364
1365
|
});
|
|
1365
1366
|
}
|
|
1366
1367
|
}
|
|
1368
|
+
if (replaceValues) {
|
|
1369
|
+
processedValue = applyValueReplacements(processedValue, replaceValues);
|
|
1370
|
+
}
|
|
1367
1371
|
processedValue = showBooleansAs ? replaceBooleansWithEmoji(processedValue, showBooleansAs) : processedValue;
|
|
1368
1372
|
return `
|
|
1369
1373
|
${yamlStringify(processedValue, {
|
|
@@ -1373,6 +1377,46 @@ ${yamlStringify(processedValue, {
|
|
|
1373
1377
|
...options
|
|
1374
1378
|
})}`;
|
|
1375
1379
|
}
|
|
1380
|
+
function applyValueReplacements(value, replaceValues, visited = /* @__PURE__ */ new Set(), currentPath = "") {
|
|
1381
|
+
function processValue(val, path) {
|
|
1382
|
+
const replacement = replaceValues(val, path);
|
|
1383
|
+
if (replacement !== false) {
|
|
1384
|
+
return replacement.newValue;
|
|
1385
|
+
}
|
|
1386
|
+
if (Array.isArray(val)) {
|
|
1387
|
+
if (visited.has(val)) {
|
|
1388
|
+
throw new Error("Circular reference detected in array");
|
|
1389
|
+
}
|
|
1390
|
+
visited.add(val);
|
|
1391
|
+
try {
|
|
1392
|
+
return val.map((item, index) => {
|
|
1393
|
+
const itemPath = path ? `${path}[${index}]` : `[${index}]`;
|
|
1394
|
+
return processValue(item, itemPath);
|
|
1395
|
+
});
|
|
1396
|
+
} finally {
|
|
1397
|
+
visited.delete(val);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
if (isPlainObject(val)) {
|
|
1401
|
+
if (visited.has(val)) {
|
|
1402
|
+
throw new Error("Circular reference detected in object");
|
|
1403
|
+
}
|
|
1404
|
+
visited.add(val);
|
|
1405
|
+
try {
|
|
1406
|
+
const result = {};
|
|
1407
|
+
for (const [key, itemValue] of Object.entries(val)) {
|
|
1408
|
+
const itemPath = path ? `${path}.${key}` : key;
|
|
1409
|
+
result[key] = processValue(itemValue, itemPath);
|
|
1410
|
+
}
|
|
1411
|
+
return result;
|
|
1412
|
+
} finally {
|
|
1413
|
+
visited.delete(val);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return val;
|
|
1417
|
+
}
|
|
1418
|
+
return processValue(value, currentPath);
|
|
1419
|
+
}
|
|
1376
1420
|
function replaceBooleansWithEmoji(value, showBooleansAs, visited = /* @__PURE__ */ new Set()) {
|
|
1377
1421
|
if (showBooleansAs === false) {
|
|
1378
1422
|
return value;
|
package/dist/testUtils.d.cts
CHANGED
|
@@ -88,11 +88,12 @@ declare function waitController(): {
|
|
|
88
88
|
* @param options.rejectKeys - The keys to reject.
|
|
89
89
|
* @param options.filterKeys - The keys to filter.
|
|
90
90
|
* @param options.ignoreProps - The props to ignore.
|
|
91
|
+
* @param options.replaceValues - Function to replace values at specific paths. Returns `false` to keep original value or `{newValue}` to replace.
|
|
91
92
|
* @param options.sortKeys - Sort all keys by a specific order (default: `simpleValuesFirst`).
|
|
92
93
|
* @param options.sortPatterns - Sort specific keys by pattern. Use to control the order of specific properties. The same patterns as `filterKeys` are supported.
|
|
93
94
|
* @returns The compact snapshot of the value.
|
|
94
95
|
*/
|
|
95
|
-
declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLength, showUndefined, showBooleansAs, rejectKeys, filterKeys, sortKeys, sortPatterns, ...options }?: YamlStringifyOptions & {
|
|
96
|
+
declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLength, showUndefined, showBooleansAs, replaceValues, rejectKeys, filterKeys, sortKeys, sortPatterns, ...options }?: YamlStringifyOptions & {
|
|
96
97
|
showBooleansAs?: boolean | {
|
|
97
98
|
props?: Record<string, {
|
|
98
99
|
trueText?: string;
|
|
@@ -102,6 +103,9 @@ declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLengt
|
|
|
102
103
|
trueText?: string;
|
|
103
104
|
falseText?: string;
|
|
104
105
|
};
|
|
106
|
+
replaceValues?: (value: unknown, path: string) => false | {
|
|
107
|
+
newValue: unknown;
|
|
108
|
+
};
|
|
105
109
|
rejectKeys?: string[] | string;
|
|
106
110
|
filterKeys?: string[] | string;
|
|
107
111
|
sortKeys?: 'asc' | 'desc' | 'simpleValuesFirst' | false;
|
package/dist/testUtils.d.ts
CHANGED
|
@@ -88,11 +88,12 @@ declare function waitController(): {
|
|
|
88
88
|
* @param options.rejectKeys - The keys to reject.
|
|
89
89
|
* @param options.filterKeys - The keys to filter.
|
|
90
90
|
* @param options.ignoreProps - The props to ignore.
|
|
91
|
+
* @param options.replaceValues - Function to replace values at specific paths. Returns `false` to keep original value or `{newValue}` to replace.
|
|
91
92
|
* @param options.sortKeys - Sort all keys by a specific order (default: `simpleValuesFirst`).
|
|
92
93
|
* @param options.sortPatterns - Sort specific keys by pattern. Use to control the order of specific properties. The same patterns as `filterKeys` are supported.
|
|
93
94
|
* @returns The compact snapshot of the value.
|
|
94
95
|
*/
|
|
95
|
-
declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLength, showUndefined, showBooleansAs, rejectKeys, filterKeys, sortKeys, sortPatterns, ...options }?: YamlStringifyOptions & {
|
|
96
|
+
declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLength, showUndefined, showBooleansAs, replaceValues, rejectKeys, filterKeys, sortKeys, sortPatterns, ...options }?: YamlStringifyOptions & {
|
|
96
97
|
showBooleansAs?: boolean | {
|
|
97
98
|
props?: Record<string, {
|
|
98
99
|
trueText?: string;
|
|
@@ -102,6 +103,9 @@ declare function compactSnapshot(value: unknown, { collapseObjects, maxLineLengt
|
|
|
102
103
|
trueText?: string;
|
|
103
104
|
falseText?: string;
|
|
104
105
|
};
|
|
106
|
+
replaceValues?: (value: unknown, path: string) => false | {
|
|
107
|
+
newValue: unknown;
|
|
108
|
+
};
|
|
105
109
|
rejectKeys?: string[] | string;
|
|
106
110
|
filterKeys?: string[] | string;
|
|
107
111
|
sortKeys?: 'asc' | 'desc' | 'simpleValuesFirst' | false;
|
package/dist/testUtils.js
CHANGED
|
@@ -261,6 +261,7 @@ function compactSnapshot(value, {
|
|
|
261
261
|
maxLineLength = 100,
|
|
262
262
|
showUndefined = false,
|
|
263
263
|
showBooleansAs = true,
|
|
264
|
+
replaceValues,
|
|
264
265
|
rejectKeys,
|
|
265
266
|
filterKeys,
|
|
266
267
|
sortKeys,
|
|
@@ -278,6 +279,9 @@ function compactSnapshot(value, {
|
|
|
278
279
|
});
|
|
279
280
|
}
|
|
280
281
|
}
|
|
282
|
+
if (replaceValues) {
|
|
283
|
+
processedValue = applyValueReplacements(processedValue, replaceValues);
|
|
284
|
+
}
|
|
281
285
|
processedValue = showBooleansAs ? replaceBooleansWithEmoji(processedValue, showBooleansAs) : processedValue;
|
|
282
286
|
return `
|
|
283
287
|
${yamlStringify(processedValue, {
|
|
@@ -287,6 +291,46 @@ ${yamlStringify(processedValue, {
|
|
|
287
291
|
...options
|
|
288
292
|
})}`;
|
|
289
293
|
}
|
|
294
|
+
function applyValueReplacements(value, replaceValues, visited = /* @__PURE__ */ new Set(), currentPath = "") {
|
|
295
|
+
function processValue(val, path) {
|
|
296
|
+
const replacement = replaceValues(val, path);
|
|
297
|
+
if (replacement !== false) {
|
|
298
|
+
return replacement.newValue;
|
|
299
|
+
}
|
|
300
|
+
if (Array.isArray(val)) {
|
|
301
|
+
if (visited.has(val)) {
|
|
302
|
+
throw new Error("Circular reference detected in array");
|
|
303
|
+
}
|
|
304
|
+
visited.add(val);
|
|
305
|
+
try {
|
|
306
|
+
return val.map((item, index) => {
|
|
307
|
+
const itemPath = path ? `${path}[${index}]` : `[${index}]`;
|
|
308
|
+
return processValue(item, itemPath);
|
|
309
|
+
});
|
|
310
|
+
} finally {
|
|
311
|
+
visited.delete(val);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (isPlainObject(val)) {
|
|
315
|
+
if (visited.has(val)) {
|
|
316
|
+
throw new Error("Circular reference detected in object");
|
|
317
|
+
}
|
|
318
|
+
visited.add(val);
|
|
319
|
+
try {
|
|
320
|
+
const result = {};
|
|
321
|
+
for (const [key, itemValue] of Object.entries(val)) {
|
|
322
|
+
const itemPath = path ? `${path}.${key}` : key;
|
|
323
|
+
result[key] = processValue(itemValue, itemPath);
|
|
324
|
+
}
|
|
325
|
+
return result;
|
|
326
|
+
} finally {
|
|
327
|
+
visited.delete(val);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return val;
|
|
331
|
+
}
|
|
332
|
+
return processValue(value, currentPath);
|
|
333
|
+
}
|
|
290
334
|
function replaceBooleansWithEmoji(value, showBooleansAs, visited = /* @__PURE__ */ new Set()) {
|
|
291
335
|
if (showBooleansAs === false) {
|
|
292
336
|
return value;
|
package/dist/typeUtils.d.cts
CHANGED
|
@@ -35,5 +35,12 @@ type DeepReplaceValueImpl<T, ReplaceType, NewType, SkipPaths extends string | un
|
|
|
35
35
|
* @template SkipTypes - The types to skip in transverse and replace.
|
|
36
36
|
*/
|
|
37
37
|
type DeepReplaceValue<T, ReplaceType, NewType, SkipPaths extends string | undefined = undefined, SkipTypes = DefaultSkipTransverseDeepReplace> = DeepReplaceValueImpl<T, ReplaceType, NewType, SkipPaths, SkipTypes>;
|
|
38
|
+
type KeysWithUndefinedValues<T extends Record<string, unknown>> = {
|
|
39
|
+
[K in keyof T]: undefined extends T[K] ? K : never;
|
|
40
|
+
}[keyof T];
|
|
41
|
+
/**
|
|
42
|
+
* Marks all possible undefined values as partial at the root level of the object.
|
|
43
|
+
*/
|
|
44
|
+
type PartialPossiblyUndefinedValues<T extends Record<string, unknown>> = Prettify<Partial<Pick<T, KeysWithUndefinedValues<T>>> & Omit<T, KeysWithUndefinedValues<T>>>;
|
|
38
45
|
|
|
39
|
-
export type { DeepPrettify, DeepReplaceValue, DefaultSkipTransverseDeepReplace, IsAny, NonPartial, ObjKeysWithValuesOfType, PartialRecord, Prettify };
|
|
46
|
+
export type { DeepPrettify, DeepReplaceValue, DefaultSkipTransverseDeepReplace, IsAny, NonPartial, ObjKeysWithValuesOfType, PartialPossiblyUndefinedValues, PartialRecord, Prettify };
|
package/dist/typeUtils.d.ts
CHANGED
|
@@ -35,5 +35,12 @@ type DeepReplaceValueImpl<T, ReplaceType, NewType, SkipPaths extends string | un
|
|
|
35
35
|
* @template SkipTypes - The types to skip in transverse and replace.
|
|
36
36
|
*/
|
|
37
37
|
type DeepReplaceValue<T, ReplaceType, NewType, SkipPaths extends string | undefined = undefined, SkipTypes = DefaultSkipTransverseDeepReplace> = DeepReplaceValueImpl<T, ReplaceType, NewType, SkipPaths, SkipTypes>;
|
|
38
|
+
type KeysWithUndefinedValues<T extends Record<string, unknown>> = {
|
|
39
|
+
[K in keyof T]: undefined extends T[K] ? K : never;
|
|
40
|
+
}[keyof T];
|
|
41
|
+
/**
|
|
42
|
+
* Marks all possible undefined values as partial at the root level of the object.
|
|
43
|
+
*/
|
|
44
|
+
type PartialPossiblyUndefinedValues<T extends Record<string, unknown>> = Prettify<Partial<Pick<T, KeysWithUndefinedValues<T>>> & Omit<T, KeysWithUndefinedValues<T>>>;
|
|
38
45
|
|
|
39
|
-
export type { DeepPrettify, DeepReplaceValue, DefaultSkipTransverseDeepReplace, IsAny, NonPartial, ObjKeysWithValuesOfType, PartialRecord, Prettify };
|
|
46
|
+
export type { DeepPrettify, DeepReplaceValue, DefaultSkipTransverseDeepReplace, IsAny, NonPartial, ObjKeysWithValuesOfType, PartialPossiblyUndefinedValues, PartialRecord, Prettify };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/typedStrings.ts
|
|
21
|
+
var typedStrings_exports = {};
|
|
22
|
+
__export(typedStrings_exports, {
|
|
23
|
+
asNonEmptyStringOrNull: () => asNonEmptyStringOrNull,
|
|
24
|
+
asNonEmptyStringOrThrow: () => asNonEmptyStringOrThrow,
|
|
25
|
+
assertStringIsNonEmpty: () => assertStringIsNonEmpty,
|
|
26
|
+
isNonEmptyString: () => isNonEmptyString,
|
|
27
|
+
splitTypedString: () => splitTypedString,
|
|
28
|
+
splitTypedStringAt: () => splitTypedStringAt,
|
|
29
|
+
stringContains: () => stringContains,
|
|
30
|
+
stringEndsWith: () => stringEndsWith,
|
|
31
|
+
stringStartsWith: () => stringStartsWith
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(typedStrings_exports);
|
|
34
|
+
function stringContains(str, substring) {
|
|
35
|
+
return str.includes(substring);
|
|
36
|
+
}
|
|
37
|
+
function stringStartsWith(str, substring) {
|
|
38
|
+
return str.startsWith(substring);
|
|
39
|
+
}
|
|
40
|
+
function stringEndsWith(str, substring) {
|
|
41
|
+
return str.endsWith(substring);
|
|
42
|
+
}
|
|
43
|
+
function splitTypedString(str, separator) {
|
|
44
|
+
return str.split(separator);
|
|
45
|
+
}
|
|
46
|
+
function splitTypedStringAt(str, separator, splitAtNSeparatorPos = 1) {
|
|
47
|
+
const parts = str.split(separator);
|
|
48
|
+
let leftPart = parts[0];
|
|
49
|
+
let rightPart = parts.slice(1).join(separator);
|
|
50
|
+
if (leftPart === void 0) {
|
|
51
|
+
throw new Error("String does not contain the separator");
|
|
52
|
+
}
|
|
53
|
+
if (splitAtNSeparatorPos > 1) {
|
|
54
|
+
leftPart = parts.slice(0, splitAtNSeparatorPos).join(separator);
|
|
55
|
+
rightPart = parts.slice(splitAtNSeparatorPos).join(separator);
|
|
56
|
+
}
|
|
57
|
+
return [leftPart, rightPart];
|
|
58
|
+
}
|
|
59
|
+
function isNonEmptyString(str) {
|
|
60
|
+
return str.length > 0;
|
|
61
|
+
}
|
|
62
|
+
function asNonEmptyStringOrThrow(str) {
|
|
63
|
+
if (isNonEmptyString(str)) {
|
|
64
|
+
return str;
|
|
65
|
+
}
|
|
66
|
+
throw new Error("String is empty");
|
|
67
|
+
}
|
|
68
|
+
function asNonEmptyStringOrNull(str) {
|
|
69
|
+
if (isNonEmptyString(str)) {
|
|
70
|
+
return str;
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function assertStringIsNonEmpty(str) {
|
|
75
|
+
if (!isNonEmptyString(str)) {
|
|
76
|
+
throw new Error("String is empty");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
80
|
+
0 && (module.exports = {
|
|
81
|
+
asNonEmptyStringOrNull,
|
|
82
|
+
asNonEmptyStringOrThrow,
|
|
83
|
+
assertStringIsNonEmpty,
|
|
84
|
+
isNonEmptyString,
|
|
85
|
+
splitTypedString,
|
|
86
|
+
splitTypedStringAt,
|
|
87
|
+
stringContains,
|
|
88
|
+
stringEndsWith,
|
|
89
|
+
stringStartsWith
|
|
90
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A type representing a string that contains a specific substring.
|
|
3
|
+
* Uses template literal types to ensure type safety at compile time.
|
|
4
|
+
*
|
|
5
|
+
* @template T - The substring that must be contained within the string
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* type EmailString = StringContaining<'@'>; // string that contains '@'
|
|
9
|
+
* const email: EmailString = 'user@example.com'; // ✓ valid
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
type StringContaining<T extends string> = string extends T ? never : `${string}${T}${string}`;
|
|
13
|
+
/**
|
|
14
|
+
* A type representing a string that starts with a specific substring.
|
|
15
|
+
* Uses template literal types to ensure the string begins with the specified prefix.
|
|
16
|
+
*
|
|
17
|
+
* @template T - The substring that the string must start with
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* type HttpUrl = StringStartingWith<'http'>; // string starting with 'http'
|
|
21
|
+
* const url: HttpUrl = 'https://example.com'; // ✓ valid
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
type StringStartingWith<T extends string> = string extends T ? never : `${T}${string}`;
|
|
25
|
+
/**
|
|
26
|
+
* A type representing a string that ends with a specific substring.
|
|
27
|
+
* Uses template literal types to ensure the string ends with the specified suffix.
|
|
28
|
+
*
|
|
29
|
+
* @template T - The substring that the string must end with
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* type JavaFile = StringEndingWith<'.java'>; // string ending with '.java'
|
|
33
|
+
* const filename: JavaFile = 'HelloWorld.java'; // ✓ valid
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
type StringEndingWith<T extends string> = string extends T ? never : `${string}${T}`;
|
|
37
|
+
/**
|
|
38
|
+
* Type guard function that checks if a string contains a specific substring.
|
|
39
|
+
* Narrows the type to `StringContaining<T>` when the check passes.
|
|
40
|
+
*
|
|
41
|
+
* @param str - The string to check
|
|
42
|
+
* @param substring - The substring to search for
|
|
43
|
+
* @returns `true` if the string contains the substring, `false` otherwise
|
|
44
|
+
*/
|
|
45
|
+
declare function stringContains<T extends string>(str: string, substring: T): str is StringContaining<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Type guard function that checks if a string starts with a specific substring.
|
|
48
|
+
* Narrows the type to `StringStartingWith<T>` when the check passes.
|
|
49
|
+
*
|
|
50
|
+
* @param str - The string to check
|
|
51
|
+
* @param substring - The substring to check for at the beginning
|
|
52
|
+
* @returns `true` if the string starts with the substring, `false` otherwise
|
|
53
|
+
*/
|
|
54
|
+
declare function stringStartsWith<T extends string>(str: string, substring: T): str is StringStartingWith<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Type guard function that checks if a string ends with a specific substring.
|
|
57
|
+
* Narrows the type to `StringEndingWith<T>` when the check passes.
|
|
58
|
+
*
|
|
59
|
+
* @param str - The string to check
|
|
60
|
+
* @param substring - The substring to check for at the end
|
|
61
|
+
* @returns `true` if the string ends with the substring, `false` otherwise
|
|
62
|
+
*/
|
|
63
|
+
declare function stringEndsWith<T extends string>(str: string, substring: T): str is StringEndingWith<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Splits a typed string by a separator that is guaranteed to exist in the string.
|
|
66
|
+
* Returns an array with at least two elements: the parts before and after the first separator,
|
|
67
|
+
* plus any additional parts if there are multiple separators.
|
|
68
|
+
*
|
|
69
|
+
* @param str - A string that contains, starts with, or ends with the separator
|
|
70
|
+
* @param separator - The separator to split by
|
|
71
|
+
* @returns An array with at least two string elements
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const path: StringContaining<'/'> = 'src/utils/types.ts';
|
|
75
|
+
* const [first, second, ...rest] = splitTypedString(path, '/');
|
|
76
|
+
* // first: 'src', second: 'utils', rest: ['types.ts']
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
declare function splitTypedString<T extends string>(str: StringContaining<NoInfer<T>> | StringStartingWith<NoInfer<T>> | StringEndingWith<NoInfer<T>>, separator: T): [string, string, ...string[]];
|
|
80
|
+
/**
|
|
81
|
+
* Splits a typed string at a specific occurrence of the separator.
|
|
82
|
+
* Unlike `splitTypedString`, this returns exactly two parts: everything before
|
|
83
|
+
* the nth separator and everything after it.
|
|
84
|
+
*
|
|
85
|
+
* @param str - A string that contains, starts with, or ends with the separator
|
|
86
|
+
* @param separator - The separator to split by
|
|
87
|
+
* @param splitAtNSeparatorPos - The position of the separator to split at (1-based)
|
|
88
|
+
* @returns A tuple with exactly two string elements
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* const path: StringContaining<'.'> = 'file.name.ext';
|
|
92
|
+
* const [name, ext] = splitTypedStringAt(path, '.', 2);
|
|
93
|
+
* // name: 'file.name', ext: 'ext'
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
declare function splitTypedStringAt<T extends string>(str: StringContaining<NoInfer<T>> | StringStartingWith<NoInfer<T>> | StringEndingWith<NoInfer<T>>, separator: T,
|
|
97
|
+
/**
|
|
98
|
+
* The position of the separator to split at.
|
|
99
|
+
* @default 1 - split at the first separator
|
|
100
|
+
*/
|
|
101
|
+
splitAtNSeparatorPos?: number): [string, string];
|
|
102
|
+
/**
|
|
103
|
+
* A branded type representing a string that is guaranteed to be non-empty (length > 0).
|
|
104
|
+
* This type provides compile-time safety by preventing empty strings from being
|
|
105
|
+
* assigned without proper validation.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* function processName(name: NonEmptyString) {
|
|
110
|
+
* // name is guaranteed to be non-empty
|
|
111
|
+
* return name.toUpperCase();
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
type NonEmptyString = string & {
|
|
116
|
+
__nonEmptyString: true;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Type guard function that checks if a string is non-empty.
|
|
120
|
+
* Narrows the type to `NonEmptyString` when the check passes.
|
|
121
|
+
*
|
|
122
|
+
* @param str - The string to check
|
|
123
|
+
* @returns `true` if the string has length > 0, `false` otherwise
|
|
124
|
+
*/
|
|
125
|
+
declare function isNonEmptyString(str: string): str is NonEmptyString;
|
|
126
|
+
/**
|
|
127
|
+
* Converts a string to `NonEmptyString` or throws an error if the string is empty.
|
|
128
|
+
* Use this when you need to ensure a string is non-empty and want to fail fast.
|
|
129
|
+
*
|
|
130
|
+
* @param str - The string to convert
|
|
131
|
+
* @returns The string as `NonEmptyString`
|
|
132
|
+
* @throws Error if the string is empty
|
|
133
|
+
*/
|
|
134
|
+
declare function asNonEmptyStringOrThrow(str: string): NonEmptyString;
|
|
135
|
+
/**
|
|
136
|
+
* Converts a string to `NonEmptyString` or returns `null` if the string is empty.
|
|
137
|
+
* Use this when empty strings should be handled gracefully rather than throwing errors.
|
|
138
|
+
*
|
|
139
|
+
* @param str - The string to convert
|
|
140
|
+
* @returns The string as `NonEmptyString` or `null` if empty
|
|
141
|
+
*/
|
|
142
|
+
declare function asNonEmptyStringOrNull(str: string): NonEmptyString | null;
|
|
143
|
+
/**
|
|
144
|
+
* Assertion function that ensures a string is non-empty.
|
|
145
|
+
* Throws an error if the string is empty, otherwise narrows the type to `NonEmptyString`.
|
|
146
|
+
*
|
|
147
|
+
* @param str - The string to assert as non-empty
|
|
148
|
+
* @throws Error if the string is empty
|
|
149
|
+
*/
|
|
150
|
+
declare function assertStringIsNonEmpty(str: string): asserts str is NonEmptyString;
|
|
151
|
+
|
|
152
|
+
export { type NonEmptyString, type StringContaining, type StringEndingWith, type StringStartingWith, asNonEmptyStringOrNull, asNonEmptyStringOrThrow, assertStringIsNonEmpty, isNonEmptyString, splitTypedString, splitTypedStringAt, stringContains, stringEndsWith, stringStartsWith };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A type representing a string that contains a specific substring.
|
|
3
|
+
* Uses template literal types to ensure type safety at compile time.
|
|
4
|
+
*
|
|
5
|
+
* @template T - The substring that must be contained within the string
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* type EmailString = StringContaining<'@'>; // string that contains '@'
|
|
9
|
+
* const email: EmailString = 'user@example.com'; // ✓ valid
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
type StringContaining<T extends string> = string extends T ? never : `${string}${T}${string}`;
|
|
13
|
+
/**
|
|
14
|
+
* A type representing a string that starts with a specific substring.
|
|
15
|
+
* Uses template literal types to ensure the string begins with the specified prefix.
|
|
16
|
+
*
|
|
17
|
+
* @template T - The substring that the string must start with
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* type HttpUrl = StringStartingWith<'http'>; // string starting with 'http'
|
|
21
|
+
* const url: HttpUrl = 'https://example.com'; // ✓ valid
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
type StringStartingWith<T extends string> = string extends T ? never : `${T}${string}`;
|
|
25
|
+
/**
|
|
26
|
+
* A type representing a string that ends with a specific substring.
|
|
27
|
+
* Uses template literal types to ensure the string ends with the specified suffix.
|
|
28
|
+
*
|
|
29
|
+
* @template T - The substring that the string must end with
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* type JavaFile = StringEndingWith<'.java'>; // string ending with '.java'
|
|
33
|
+
* const filename: JavaFile = 'HelloWorld.java'; // ✓ valid
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
type StringEndingWith<T extends string> = string extends T ? never : `${string}${T}`;
|
|
37
|
+
/**
|
|
38
|
+
* Type guard function that checks if a string contains a specific substring.
|
|
39
|
+
* Narrows the type to `StringContaining<T>` when the check passes.
|
|
40
|
+
*
|
|
41
|
+
* @param str - The string to check
|
|
42
|
+
* @param substring - The substring to search for
|
|
43
|
+
* @returns `true` if the string contains the substring, `false` otherwise
|
|
44
|
+
*/
|
|
45
|
+
declare function stringContains<T extends string>(str: string, substring: T): str is StringContaining<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Type guard function that checks if a string starts with a specific substring.
|
|
48
|
+
* Narrows the type to `StringStartingWith<T>` when the check passes.
|
|
49
|
+
*
|
|
50
|
+
* @param str - The string to check
|
|
51
|
+
* @param substring - The substring to check for at the beginning
|
|
52
|
+
* @returns `true` if the string starts with the substring, `false` otherwise
|
|
53
|
+
*/
|
|
54
|
+
declare function stringStartsWith<T extends string>(str: string, substring: T): str is StringStartingWith<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Type guard function that checks if a string ends with a specific substring.
|
|
57
|
+
* Narrows the type to `StringEndingWith<T>` when the check passes.
|
|
58
|
+
*
|
|
59
|
+
* @param str - The string to check
|
|
60
|
+
* @param substring - The substring to check for at the end
|
|
61
|
+
* @returns `true` if the string ends with the substring, `false` otherwise
|
|
62
|
+
*/
|
|
63
|
+
declare function stringEndsWith<T extends string>(str: string, substring: T): str is StringEndingWith<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Splits a typed string by a separator that is guaranteed to exist in the string.
|
|
66
|
+
* Returns an array with at least two elements: the parts before and after the first separator,
|
|
67
|
+
* plus any additional parts if there are multiple separators.
|
|
68
|
+
*
|
|
69
|
+
* @param str - A string that contains, starts with, or ends with the separator
|
|
70
|
+
* @param separator - The separator to split by
|
|
71
|
+
* @returns An array with at least two string elements
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const path: StringContaining<'/'> = 'src/utils/types.ts';
|
|
75
|
+
* const [first, second, ...rest] = splitTypedString(path, '/');
|
|
76
|
+
* // first: 'src', second: 'utils', rest: ['types.ts']
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
declare function splitTypedString<T extends string>(str: StringContaining<NoInfer<T>> | StringStartingWith<NoInfer<T>> | StringEndingWith<NoInfer<T>>, separator: T): [string, string, ...string[]];
|
|
80
|
+
/**
|
|
81
|
+
* Splits a typed string at a specific occurrence of the separator.
|
|
82
|
+
* Unlike `splitTypedString`, this returns exactly two parts: everything before
|
|
83
|
+
* the nth separator and everything after it.
|
|
84
|
+
*
|
|
85
|
+
* @param str - A string that contains, starts with, or ends with the separator
|
|
86
|
+
* @param separator - The separator to split by
|
|
87
|
+
* @param splitAtNSeparatorPos - The position of the separator to split at (1-based)
|
|
88
|
+
* @returns A tuple with exactly two string elements
|
|
89
|
+
* @example
|
|
90
|
+
* ```ts
|
|
91
|
+
* const path: StringContaining<'.'> = 'file.name.ext';
|
|
92
|
+
* const [name, ext] = splitTypedStringAt(path, '.', 2);
|
|
93
|
+
* // name: 'file.name', ext: 'ext'
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
declare function splitTypedStringAt<T extends string>(str: StringContaining<NoInfer<T>> | StringStartingWith<NoInfer<T>> | StringEndingWith<NoInfer<T>>, separator: T,
|
|
97
|
+
/**
|
|
98
|
+
* The position of the separator to split at.
|
|
99
|
+
* @default 1 - split at the first separator
|
|
100
|
+
*/
|
|
101
|
+
splitAtNSeparatorPos?: number): [string, string];
|
|
102
|
+
/**
|
|
103
|
+
* A branded type representing a string that is guaranteed to be non-empty (length > 0).
|
|
104
|
+
* This type provides compile-time safety by preventing empty strings from being
|
|
105
|
+
* assigned without proper validation.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```ts
|
|
109
|
+
* function processName(name: NonEmptyString) {
|
|
110
|
+
* // name is guaranteed to be non-empty
|
|
111
|
+
* return name.toUpperCase();
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
type NonEmptyString = string & {
|
|
116
|
+
__nonEmptyString: true;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Type guard function that checks if a string is non-empty.
|
|
120
|
+
* Narrows the type to `NonEmptyString` when the check passes.
|
|
121
|
+
*
|
|
122
|
+
* @param str - The string to check
|
|
123
|
+
* @returns `true` if the string has length > 0, `false` otherwise
|
|
124
|
+
*/
|
|
125
|
+
declare function isNonEmptyString(str: string): str is NonEmptyString;
|
|
126
|
+
/**
|
|
127
|
+
* Converts a string to `NonEmptyString` or throws an error if the string is empty.
|
|
128
|
+
* Use this when you need to ensure a string is non-empty and want to fail fast.
|
|
129
|
+
*
|
|
130
|
+
* @param str - The string to convert
|
|
131
|
+
* @returns The string as `NonEmptyString`
|
|
132
|
+
* @throws Error if the string is empty
|
|
133
|
+
*/
|
|
134
|
+
declare function asNonEmptyStringOrThrow(str: string): NonEmptyString;
|
|
135
|
+
/**
|
|
136
|
+
* Converts a string to `NonEmptyString` or returns `null` if the string is empty.
|
|
137
|
+
* Use this when empty strings should be handled gracefully rather than throwing errors.
|
|
138
|
+
*
|
|
139
|
+
* @param str - The string to convert
|
|
140
|
+
* @returns The string as `NonEmptyString` or `null` if empty
|
|
141
|
+
*/
|
|
142
|
+
declare function asNonEmptyStringOrNull(str: string): NonEmptyString | null;
|
|
143
|
+
/**
|
|
144
|
+
* Assertion function that ensures a string is non-empty.
|
|
145
|
+
* Throws an error if the string is empty, otherwise narrows the type to `NonEmptyString`.
|
|
146
|
+
*
|
|
147
|
+
* @param str - The string to assert as non-empty
|
|
148
|
+
* @throws Error if the string is empty
|
|
149
|
+
*/
|
|
150
|
+
declare function assertStringIsNonEmpty(str: string): asserts str is NonEmptyString;
|
|
151
|
+
|
|
152
|
+
export { type NonEmptyString, type StringContaining, type StringEndingWith, type StringStartingWith, asNonEmptyStringOrNull, asNonEmptyStringOrThrow, assertStringIsNonEmpty, isNonEmptyString, splitTypedString, splitTypedStringAt, stringContains, stringEndsWith, stringStartsWith };
|