nhb-toolbox 4.20.30 → 4.20.40

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/CHANGELOG.md CHANGED
@@ -6,6 +6,16 @@ All notable changes to the package will be documented here.
6
6
 
7
7
  ---
8
8
 
9
+ ## [4.20.40] - 2025-09-18
10
+
11
+ - **Added** new **utility types**: `DeepPartialAll`, `Join`,`Split` along with `ValidArray`, `List` and _more_.
12
+
13
+ ## [4.20.32] - 2025-09-17
14
+
15
+ - **Renamed** `isPastParticiple()` method to `isParticiple()` in `Verbalizer/verbalizer`.
16
+ - **Optimized** _internal logic_ for `toPast()` and `toParticiple()` methods in `Verbalizer/verbalizer`.
17
+ - **Updated** all the _rules_ for `Verbalizer/verbalizer`.
18
+
9
19
  ## [4.20.30] - 2025-09-17
10
20
 
11
21
  - **Reduced** _unpacked size_ by **removing** _tsdoc comments_ from js (both `cjs` and `esm`) outputs.
@@ -52,6 +52,9 @@ class Verbalizer {
52
52
  }
53
53
  return result;
54
54
  }
55
+ #applyBaseRule(verb) {
56
+ return this.#applyRules(verb, this.#baseRules);
57
+ }
55
58
  #applyRules(verb, rules) {
56
59
  if (!(0, primitives_1.isNonEmptyString)(verb))
57
60
  return '';
@@ -96,7 +99,7 @@ class Verbalizer {
96
99
  if (irregularEntry) {
97
100
  return this.#restoreCase(verb, irregularEntry.past);
98
101
  }
99
- return this.#restoreCase(verb, this.#applyRules(this.toBase(lower), this.#pastRules));
102
+ return this.#restoreCase(verb, this.#applyRules(this.#applyBaseRule(lower), this.#pastRules));
100
103
  }
101
104
  toParticiple(verb) {
102
105
  if (!(0, primitives_1.isNonEmptyString)(verb))
@@ -106,7 +109,7 @@ class Verbalizer {
106
109
  if (irregularEntry) {
107
110
  return this.#restoreCase(verb, irregularEntry.participle);
108
111
  }
109
- return this.#restoreCase(verb, this.#applyRules(this.toBase(lower), this.#participleRules));
112
+ return this.#restoreCase(verb, this.#applyRules(this.#applyBaseRule(lower), this.#participleRules));
110
113
  }
111
114
  toBase(verb) {
112
115
  if (!(0, primitives_1.isNonEmptyString)(verb))
@@ -116,7 +119,7 @@ class Verbalizer {
116
119
  if (irregularEntry) {
117
120
  return this.#restoreCase(verb, irregularEntry.base);
118
121
  }
119
- return this.#restoreCase(verb, this.#applyRules(lower, this.#baseRules));
122
+ return this.#restoreCase(verb, this.#applyBaseRule(lower));
120
123
  }
121
124
  isPast(verb) {
122
125
  if (!(0, primitives_1.isNonEmptyString)(verb))
@@ -124,7 +127,7 @@ class Verbalizer {
124
127
  const lower = verb?.trim()?.toLowerCase();
125
128
  return this.toPast(lower) === lower;
126
129
  }
127
- isPastParticiple(verb) {
130
+ isParticiple(verb) {
128
131
  if (!(0, primitives_1.isNonEmptyString)(verb))
129
132
  return false;
130
133
  const lower = verb?.trim()?.toLowerCase();
@@ -79,7 +79,6 @@ exports.irregularVerbs = Object.freeze([
79
79
  ['leave', 'left', 'left'],
80
80
  ['lend', 'lent', 'lent'],
81
81
  ['lie', 'lay', 'lain'],
82
- ['lie', 'lied', 'lied'],
83
82
  ['light', 'lit', 'lit'],
84
83
  ['lose', 'lost', 'lost'],
85
84
  ['make', 'made', 'made'],
@@ -150,6 +149,7 @@ exports.pastRules = Object.freeze([
150
149
  [/e$/i, 'ed'],
151
150
  [/([aeiou])lf$/i, '$1lved'],
152
151
  [/([^aeiou])y$/i, '$1ied'],
152
+ [/([^aeiou])ic$/i, '$1icked'],
153
153
  [/([^aeiou])([aeiou])([^aeiou])$/i, '$1$2$3$3ed'],
154
154
  [/$/i, 'ed'],
155
155
  ]);
@@ -158,13 +158,20 @@ exports.pastParticipleRules = Object.freeze([
158
158
  [/e$/i, 'ed'],
159
159
  [/([aeiou])lf$/i, '$1lved'],
160
160
  [/([^aeiou])y$/i, '$1ied'],
161
+ [/([^aeiou])ic$/i, '$1icked'],
161
162
  [/([^aeiou])([aeiou])([^aeiou])$/i, '$1$2$3$3ed'],
162
163
  [/$/i, 'ed'],
163
164
  ]);
164
165
  exports.baseRules = Object.freeze([
165
166
  [/([aeiou])yed$/i, '$1y'],
167
+ [/^([^aeiouwy])ied$/i, '$1ie'],
166
168
  [/ied$/i, 'y'],
167
169
  [/([aeiou])lved$/i, '$1lf'],
170
+ [/([^aeiou])icked$/i, '$1ic'],
168
171
  [/([bcdfghjklmnpqrstvwxyz])\1ed$/i, '$1'],
172
+ [/([aeiou])ked$/i, '$1ke'],
173
+ [/ined$/i, 'ine'],
174
+ [/eted$/i, 'ete'],
175
+ [/gued$/i, 'gue'],
169
176
  [/ed$/i, ''],
170
177
  ]);
@@ -38,17 +38,17 @@ export type NonNullishPrimitiveKey<T> = {
38
38
  /** Falsy primitive type */
39
39
  export type FalsyPrimitive = false | 0 | '' | null | undefined;
40
40
  /** A generic class constructor */
41
- export type Constructor = new (...args: any[]) => unknown;
41
+ export type Constructor = new (...args: any) => any;
42
42
  /** Generic function type */
43
- export type GenericFn = (...args: unknown[]) => unknown;
43
+ export type GenericFn = (...args: any) => any;
44
44
  /** Generic function type that returns `void` */
45
- export type VoidFunction = (...args: any[]) => void;
45
+ export type VoidFunction = (...args: any) => void;
46
46
  /** Debounced function type after certain delay */
47
47
  export type DelayedFn<T extends VoidFunction> = (...args: Parameters<T>) => void;
48
48
  /** Throttled function type after specific delay */
49
49
  export type ThrottledFn<T extends VoidFunction> = (...args: Parameters<T>) => void;
50
50
  /** Asynchronous function type */
51
- export type AsyncFunction<T> = (...args: unknown[]) => Promise<T>;
51
+ export type AsyncFunction<T> = (...args: any) => Promise<T>;
52
52
  /** Advanced types to exclude from counting as object key */
53
53
  export type AdvancedTypes = Array<unknown> | File | FileList | Chronos | DateLike | Blob | Date | RegExp | WeakMap<WeakKey, unknown> | WeakSet<WeakKey> | Map<unknown, unknown> | Set<unknown> | Function | GenericFn | VoidFunction | AsyncFunction<unknown> | Promise<unknown> | Error | EvalError | RangeError | ReferenceError | SyntaxError | TypeError | URIError | bigint | symbol;
54
54
  /** Helper to detect if a type has methods */
@@ -76,4 +76,61 @@ export interface ClassDetails {
76
76
  }
77
77
  /** Literal type for `partial` and `required` */
78
78
  export type PartialOrRequired = 'partial' | 'required';
79
+ /**
80
+ * - Utility type to assert that a given type condition evaluates to `true`.
81
+ *
82
+ * @remarks
83
+ * - This type is mainly used in **type-level tests** to enforce that a condition (usually produced by {@link Equal}) is satisfied.
84
+ * - If the condition is not `true`, TypeScript will raise an error at compile time.
85
+ *
86
+ * @example
87
+ * // Passes ✅
88
+ * type Test1 = Expect<true>;
89
+ *
90
+ * // Fails ❌ - will cause a type error
91
+ * type Test2 = Expect<false>;
92
+ */
93
+ export type Expect<T extends true> = T;
94
+ /**
95
+ * * Utility type that checks whether two types `X` and `Y` are strictly equal.
96
+ *
97
+ * @remarks
98
+ * - This type uses conditional types and generic inference tricks to compare whether two types are identical.
99
+ * - It resolves to `true` if `X` and `Y` are the same type, otherwise `false`.
100
+ *
101
+ * _Typically used together with {@link Expect} for type-level assertions in tests._
102
+ *
103
+ * @example
104
+ * type Test1 = Equal<string, string>; // true
105
+ * type Test2 = Equal<string, number>; // false
106
+ *
107
+ * // Example with Expect
108
+ * type Check = Expect<Equal<'a', 'a'>>; // ✅ Compiles
109
+ * type Fail = Expect<Equal<'a', 'b'>>; // ❌ Type error
110
+ */
111
+ export type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
112
+ /**
113
+ * * Ensures that an array has **at least one element**.
114
+ *
115
+ * @remarks
116
+ * - This type enforces non-empty arrays by requiring the first element `T`, followed by zero or more additional `T`s.
117
+ *
118
+ * @example
119
+ * type NonEmpty = ValidArray<number>; // [number, ...number[]]
120
+ * const arr1: NonEmpty = [1]; // ✅ OK
121
+ * const arr2: NonEmpty = []; // ❌ Error (empty array not allowed)
122
+ */
123
+ export type ValidArray<T> = [T, ...Array<T>];
124
+ /**
125
+ * * A readonly array of elements of type `T`.
126
+ *
127
+ * @remarks
128
+ * - Shorthand for `ReadonlyArray<T>`. Used to represent immutable lists.
129
+ *
130
+ * @example
131
+ * type Numbers = List<number>; // readonly number[]
132
+ * const arr: Numbers = [1, 2, 3]; // ✅ OK
133
+ * arr.push(4); // ❌ Error (readonly)
134
+ */
135
+ export type List<T = any> = ReadonlyArray<T>;
79
136
  export {};
@@ -1,5 +1,5 @@
1
1
  import type { GenericObject, NestedPrimitiveKey } from '../object/types';
2
- import type { AdvancedTypes, Numeric } from '../types/index';
2
+ import type { AdvancedTypes, List, NormalPrimitive, Numeric } from '../types/index';
3
3
  /** Options to initialize Paginator */
4
4
  export interface PaginatorOptions {
5
5
  /** The total number of items. */
@@ -72,7 +72,11 @@ export type ValueOf<T> = T[keyof T];
72
72
  */
73
73
  export type KeysOfUnion<T> = T extends T ? keyof T : never;
74
74
  /**
75
- * * Recursively makes all properties in an object type optional.
75
+ * * Recursively makes all potential standard js object properties optional.
76
+ *
77
+ * @remarks
78
+ * - It excludes complex types like `Array`, `Map`, `File`, `Date`, `Chronos` etc. from being recursively partial.
79
+ * - Please, refer to {@link AdvancedTypes} to learn more about these complex types.
76
80
  *
77
81
  * @example
78
82
  * type Config = { a: string; nested: { b: number } };
@@ -82,6 +86,17 @@ export type KeysOfUnion<T> = T extends T ? keyof T : never;
82
86
  export type DeepPartial<T> = {
83
87
  [K in keyof T]?: T[K] extends AdvancedTypes ? T[K] : T[K] extends object ? DeepPartial<T[K]> : T[K];
84
88
  };
89
+ /**
90
+ * * Recursively makes all properties in any object or array type optional.
91
+ *
92
+ * @example
93
+ * type Config = { a: string; nested: { b: number } };
94
+ * type PartialConfig = DeepPartial<Config>;
95
+ * // { a?: string; nested?: { b?: number } }
96
+ */
97
+ export type DeepPartialAll<T> = T extends Array<infer El> ? Array<DeepPartialAll<El>> : {
98
+ [K in keyof T]?: DeepPartialAll<T[K]>;
99
+ };
85
100
  /**
86
101
  * * Removes `readonly` modifiers from all properties of an object type.
87
102
  *
@@ -444,4 +459,67 @@ export type RequireExactly<T extends GenericObject, N extends number> = {
444
459
  * const s4: OneOrTwo = {}; // ❌ (0 keys)
445
460
  */
446
461
  export type RequireBetween<T extends GenericObject, Min extends number, Max extends number, C extends unknown[] = BuildTuple<Max>, Acc extends unknown[] = BuildTuple<Min>> = RequireExactly<T, Acc['length']> | (Acc['length'] extends Max ? never : RequireBetween<T, Min, Max, C, [...Acc, unknown]>);
462
+ /**
463
+ * * Cast one type to another while preserving compatibility.
464
+ *
465
+ * @remarks
466
+ * - Ensures that `A1` extends `A2`. If not, falls back to `A2`.
467
+ * - Useful for enforcing constraints on generics or parameters.
468
+ *
469
+ * @param A1 - Type to check.
470
+ * @param A2 - Type to cast to.
471
+ * @returns `A1` if it extends `A2`, otherwise `A2`.
472
+ *
473
+ * @example
474
+ * type T0 = Cast<'42', string>; // '42'
475
+ * type T1 = Cast<'42', number>; // number
476
+ * type T2 = Cast<42, number>; // 42
477
+ */
478
+ export type Cast<A1, A2> = A1 extends A2 ? A1 : A2;
479
+ /**
480
+ * * Remove the last element of a list (array).
481
+ *
482
+ * @remarks Produces a new tuple/list type with the last element removed.
483
+ *
484
+ * @example
485
+ * type T0 = Pop<[1, 2, 3]>; // [1, 2]
486
+ * type T1 = Pop<[]>; // []
487
+ * type T2 = Pop<['a']>; // []
488
+ */
489
+ export type Pop<L extends List> = L extends readonly [...infer El, any] | readonly [...infer El, any?] ? El : L;
490
+ type __Split<S extends string, D extends string, T extends string[] = []> = S extends `${infer BS}${D}${infer AS}` ? __Split<AS, D, [...T, BS]> : [...T, S];
491
+ type _Split<S extends string, D extends string = ''> = D extends '' ? Pop<__Split<S, D>> : __Split<S, D>;
492
+ /**
493
+ * ✂️ Split a string literal by a given delimiter into a list of strings.
494
+ *
495
+ * @remarks
496
+ * Produces a tuple of substrings by splitting `S` at each occurrence of `D`.
497
+ *
498
+ * @param S - String literal to split.
499
+ * @param D - Delimiter to split on (default: empty string, i.e., character split).
500
+ * @returns A list of string literals.
501
+ *
502
+ * @example
503
+ * type T0 = Split<'a,b,c', ','>; // ['a', 'b', 'c']
504
+ * type T1 = Split<'hello', ''>; // ['h', 'e', 'l', 'l', 'o']
505
+ * type T2 = Split<'foo-bar', '-'>; // ['foo', 'bar']
506
+ */
507
+ export type Split<S extends string, D extends string = ''> = _Split<S, D> extends infer X ? Cast<X, string[]> : never;
508
+ type _Join<T extends List, D extends string> = T extends [] ? '' : T extends [NormalPrimitive] ? `${T[0]}` : T extends [NormalPrimitive, ...infer R] ? `${T[0]}${D}${_Join<R, D>}` : string;
509
+ /**
510
+ * * Join a list of string/number/boolean literals into a single string.
511
+ *
512
+ * @remarks
513
+ * Concatenates elements of `T` into a single string, separated by delimiter `D`.
514
+ *
515
+ * @param T - List of string/number/boolean literals.
516
+ * @param D - Delimiter to insert between elements (default: space `" "`).
517
+ * @returns A concatenated string literal.
518
+ *
519
+ * @example
520
+ * type T0 = Join<['a', 'b', 'c'], ','>; // "a,b,c"
521
+ * type T1 = Join<['2025', '09', '18'], '-'>; // "2025-09-18"
522
+ * type T2 = Join<['hello', 'world']>; // "hello world"
523
+ */
524
+ export type Join<T extends List<NormalPrimitive>, D extends string = ' '> = _Join<T, D> extends infer X ? Cast<X, string> : never;
447
525
  export {};
@@ -12,10 +12,10 @@
12
12
  * For ready to use instance, please refer to {@link https://toolbox.nazmul-nhb.dev/docs/utilities/string/verbalizer verbalizer} instead.
13
13
  *
14
14
  * @example
15
- * const verbalizer = new Verbalizer();
16
- * verbalizer.toPast('run'); // "ran"
17
- * verbalizer.toParticiple('go'); // "gone"
18
- * verbalizer.toBase('went'); // "go"
15
+ * const myVerbalizer = new Verbalizer();
16
+ * myVerbalizer.toPast('run'); // "ran"
17
+ * myVerbalizer.toParticiple('go'); // "gone"
18
+ * myVerbalizer.toBase('went'); // "go"
19
19
  */
20
20
  export declare class Verbalizer {
21
21
  #private;
@@ -98,10 +98,10 @@ export declare class Verbalizer {
98
98
  * @param verb Verb to check.
99
99
  * @returns True if the verb is in past participle form, otherwise false.
100
100
  * @example
101
- * verbalizer.isPastParticiple('gone'); // true
102
- * verbalizer.isPastParticiple('go'); // false
101
+ * verbalizer.isParticiple('gone'); // true
102
+ * verbalizer.isParticiple('go'); // false
103
103
  */
104
- isPastParticiple(verb: string): boolean;
104
+ isParticiple(verb: string): boolean;
105
105
  /**
106
106
  * * Check if a given verb is in its base form.
107
107
  * @param verb Verb to check.
@@ -49,6 +49,9 @@ export class Verbalizer {
49
49
  }
50
50
  return result;
51
51
  }
52
+ #applyBaseRule(verb) {
53
+ return this.#applyRules(verb, this.#baseRules);
54
+ }
52
55
  #applyRules(verb, rules) {
53
56
  if (!isNonEmptyString(verb))
54
57
  return '';
@@ -93,7 +96,7 @@ export class Verbalizer {
93
96
  if (irregularEntry) {
94
97
  return this.#restoreCase(verb, irregularEntry.past);
95
98
  }
96
- return this.#restoreCase(verb, this.#applyRules(this.toBase(lower), this.#pastRules));
99
+ return this.#restoreCase(verb, this.#applyRules(this.#applyBaseRule(lower), this.#pastRules));
97
100
  }
98
101
  toParticiple(verb) {
99
102
  if (!isNonEmptyString(verb))
@@ -103,7 +106,7 @@ export class Verbalizer {
103
106
  if (irregularEntry) {
104
107
  return this.#restoreCase(verb, irregularEntry.participle);
105
108
  }
106
- return this.#restoreCase(verb, this.#applyRules(this.toBase(lower), this.#participleRules));
109
+ return this.#restoreCase(verb, this.#applyRules(this.#applyBaseRule(lower), this.#participleRules));
107
110
  }
108
111
  toBase(verb) {
109
112
  if (!isNonEmptyString(verb))
@@ -113,7 +116,7 @@ export class Verbalizer {
113
116
  if (irregularEntry) {
114
117
  return this.#restoreCase(verb, irregularEntry.base);
115
118
  }
116
- return this.#restoreCase(verb, this.#applyRules(lower, this.#baseRules));
119
+ return this.#restoreCase(verb, this.#applyBaseRule(lower));
117
120
  }
118
121
  isPast(verb) {
119
122
  if (!isNonEmptyString(verb))
@@ -121,7 +124,7 @@ export class Verbalizer {
121
124
  const lower = verb?.trim()?.toLowerCase();
122
125
  return this.toPast(lower) === lower;
123
126
  }
124
- isPastParticiple(verb) {
127
+ isParticiple(verb) {
125
128
  if (!isNonEmptyString(verb))
126
129
  return false;
127
130
  const lower = verb?.trim()?.toLowerCase();
@@ -76,7 +76,6 @@ export const irregularVerbs = Object.freeze([
76
76
  ['leave', 'left', 'left'],
77
77
  ['lend', 'lent', 'lent'],
78
78
  ['lie', 'lay', 'lain'],
79
- ['lie', 'lied', 'lied'],
80
79
  ['light', 'lit', 'lit'],
81
80
  ['lose', 'lost', 'lost'],
82
81
  ['make', 'made', 'made'],
@@ -147,6 +146,7 @@ export const pastRules = Object.freeze([
147
146
  [/e$/i, 'ed'],
148
147
  [/([aeiou])lf$/i, '$1lved'],
149
148
  [/([^aeiou])y$/i, '$1ied'],
149
+ [/([^aeiou])ic$/i, '$1icked'],
150
150
  [/([^aeiou])([aeiou])([^aeiou])$/i, '$1$2$3$3ed'],
151
151
  [/$/i, 'ed'],
152
152
  ]);
@@ -155,13 +155,20 @@ export const pastParticipleRules = Object.freeze([
155
155
  [/e$/i, 'ed'],
156
156
  [/([aeiou])lf$/i, '$1lved'],
157
157
  [/([^aeiou])y$/i, '$1ied'],
158
+ [/([^aeiou])ic$/i, '$1icked'],
158
159
  [/([^aeiou])([aeiou])([^aeiou])$/i, '$1$2$3$3ed'],
159
160
  [/$/i, 'ed'],
160
161
  ]);
161
162
  export const baseRules = Object.freeze([
162
163
  [/([aeiou])yed$/i, '$1y'],
164
+ [/^([^aeiouwy])ied$/i, '$1ie'],
163
165
  [/ied$/i, 'y'],
164
166
  [/([aeiou])lved$/i, '$1lf'],
167
+ [/([^aeiou])icked$/i, '$1ic'],
165
168
  [/([bcdfghjklmnpqrstvwxyz])\1ed$/i, '$1'],
169
+ [/([aeiou])ked$/i, '$1ke'],
170
+ [/ined$/i, 'ine'],
171
+ [/eted$/i, 'ete'],
172
+ [/gued$/i, 'gue'],
166
173
  [/ed$/i, ''],
167
174
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nhb-toolbox",
3
- "version": "4.20.30",
3
+ "version": "4.20.40",
4
4
  "description": "A versatile collection of smart, efficient, and reusable utility functions and classes for everyday development needs.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",