utilitish 0.0.7 → 0.0.9
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/README.md +60 -0
- package/dist/array/array-constructor.d.ts +1 -1
- package/dist/array/array-prototype.d.ts +5 -4
- package/dist/array/array-prototype.js +20 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/map/map-prototype.d.ts +1 -1
- package/dist/map/map-prototype.js +11 -2
- package/dist/object/object-prototype.d.ts +39 -1
- package/dist/object/object-prototype.js +34 -14
- package/dist/object/object-prototype.spec.js +74 -0
- package/dist/set/set-prototype.d.ts +1 -1
- package/dist/string/string-prototype.d.ts +44 -7
- package/dist/string/string-prototype.js +3 -6
- package/dist/string/string-prototype.spec.js +71 -1
- package/dist/utils/logic.utils.d.ts +0 -1
- package/dist/utils/logic.utils.js +0 -17
- package/dist/utils/slugify.config.d.ts +78 -0
- package/dist/utils/slugify.config.js +58 -0
- package/dist/utils/slugify.utils.d.ts +10 -0
- package/dist/utils/slugify.utils.js +59 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -3,3 +3,63 @@
|
|
|
3
3
|
Ce projet est distribué sous licence [GPL v3](https://www.gnu.org/licenses/gpl-3.0.html) © 2025 Donovan Ferreira.
|
|
4
4
|
|
|
5
5
|
Ceci est une petite librairie qui ajoute plusieurs méthodes au prototype JavaScript.
|
|
6
|
+
|
|
7
|
+
## Configuration Slugify
|
|
8
|
+
|
|
9
|
+
La méthode `slugify()` peut être personnalisée pour répondre à vos besoins spécifiques. Consultez la JSDoc dans votre IDE pour voir tous les exemples disponibles.
|
|
10
|
+
|
|
11
|
+
### Configuration globale
|
|
12
|
+
|
|
13
|
+
Vous pouvez définir une configuration globale qui s'appliquera à tous les appels `slugify()` :
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { setSlugifyConfig } from 'utilitish';
|
|
17
|
+
|
|
18
|
+
// Configuration pour remplacer les symboles de genre
|
|
19
|
+
setSlugifyConfig({
|
|
20
|
+
customReplacements: {
|
|
21
|
+
'♀': 'feminin',
|
|
22
|
+
'♂': 'masculin',
|
|
23
|
+
},
|
|
24
|
+
separator: '_',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
'Test ♀'.slugify(); // "test_feminin"
|
|
28
|
+
'User♂@domain.com'.slugify(); // "user_masculin_at_domain_com"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Configuration par appel
|
|
32
|
+
|
|
33
|
+
Vous pouvez aussi passer une configuration spécifique à chaque appel :
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Utilise la config globale
|
|
37
|
+
'Hello World'.slugify(); // "hello-world"
|
|
38
|
+
|
|
39
|
+
// Override la config pour cet appel
|
|
40
|
+
'Hello World'.slugify({ separator: '_' }); // "hello_world"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Options de configuration
|
|
44
|
+
|
|
45
|
+
- **`customReplacements`**: Remplacer des caractères spécifiques (ex: "♀" → "feminin")
|
|
46
|
+
- **`separator`**: Caractère de séparation (défaut: "-")
|
|
47
|
+
- **`lowercase`**: Convertir en minuscules (défaut: true)
|
|
48
|
+
- **`removeAccents`**: Supprimer les accents (défaut: true)
|
|
49
|
+
- **`allowedChars`**: Regex des caractères autorisés (défaut: /[a-zA-Z0-9]/)
|
|
50
|
+
- **`maxLength`**: Longueur maximale du slug
|
|
51
|
+
- **`transformers`**: Fonctions de transformation personnalisées
|
|
52
|
+
|
|
53
|
+
### Gestion de la configuration
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { getSlugifyConfig, resetSlugifyConfig } from 'utilitish';
|
|
57
|
+
|
|
58
|
+
// Obtenir la config actuelle
|
|
59
|
+
const currentConfig = getSlugifyConfig();
|
|
60
|
+
|
|
61
|
+
// Réinitialiser aux valeurs par défaut
|
|
62
|
+
resetSlugifyConfig();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Pour plus d'exemples et cas d'usage avancés, consultez les JSDoc intégrées dans votre IDE.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export {};
|
|
2
1
|
declare global {
|
|
3
2
|
interface ArrayConstructor {
|
|
4
3
|
/**
|
|
@@ -97,3 +96,4 @@ declare global {
|
|
|
97
96
|
}
|
|
98
97
|
type MultiDimensionalArray<T, Dims extends number[]> = Dims extends [number, ...infer Rest extends number[]] ? MultiDimensionalArray<T[], Rest> : T;
|
|
99
98
|
type WidenLiterals<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
|
|
99
|
+
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Selector } from '../utils/core.utils';
|
|
2
|
-
export {};
|
|
3
2
|
declare global {
|
|
4
3
|
interface Array<T> {
|
|
5
4
|
/**
|
|
@@ -48,7 +47,7 @@ declare global {
|
|
|
48
47
|
* - Returns 0 for empty arrays regardless of type
|
|
49
48
|
*/
|
|
50
49
|
sum(this: number[]): number;
|
|
51
|
-
sum(this: T[], selector
|
|
50
|
+
sum(this: T[], selector: Selector<T, number>): number;
|
|
52
51
|
/**
|
|
53
52
|
* Returns a new array with only unique elements based on strict equality (===).
|
|
54
53
|
* Preserves order of first occurrence.
|
|
@@ -188,7 +187,8 @@ declare global {
|
|
|
188
187
|
* - Returns new array; does not mutate original
|
|
189
188
|
* - Empty arrays return empty array
|
|
190
189
|
*/
|
|
191
|
-
sortAsc(this:
|
|
190
|
+
sortAsc(this: (number | string)[]): T[];
|
|
191
|
+
sortAsc(this: T[], selector: Selector<T, number | string>): T[];
|
|
192
192
|
/**
|
|
193
193
|
* Returns a new sorted copy of the array in descending order.
|
|
194
194
|
* Creates a new array without modifying the original.
|
|
@@ -211,7 +211,8 @@ declare global {
|
|
|
211
211
|
* - Returns new array; does not mutate original
|
|
212
212
|
* - Empty arrays return empty array
|
|
213
213
|
*/
|
|
214
|
-
sortDesc(this:
|
|
214
|
+
sortDesc(this: (number | string)[]): T[];
|
|
215
|
+
sortDesc(this: T[], selector: Selector<T, number | string>): T[];
|
|
215
216
|
/**
|
|
216
217
|
* Swaps the elements at two indices within the array.
|
|
217
218
|
* Modifies the array in place and returns the array itself (for chaining).
|
|
@@ -121,8 +121,26 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
121
121
|
return map;
|
|
122
122
|
});
|
|
123
123
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'toObject', function (keySelector, valueSelector) {
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
let entries;
|
|
125
|
+
if (!keySelector && this.length && this.every((item) => Array.isArray(item) && item.length === 2)) {
|
|
126
|
+
entries = this;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const getKey = (0, core_utils_1.resolveSelector)(keySelector, (_, index) => index);
|
|
130
|
+
const getValue = (0, core_utils_1.resolveSelector)(valueSelector, (item) => item);
|
|
131
|
+
entries = this.map((item, index) => [getKey(item, index), getValue(item)]);
|
|
132
|
+
}
|
|
133
|
+
// Validate keys
|
|
134
|
+
for (const [key] of entries) {
|
|
135
|
+
if (key === null || key === undefined) {
|
|
136
|
+
throw new TypeError(`Invalid key: key cannot be null or undefined. Key received: ${String(key)}`);
|
|
137
|
+
}
|
|
138
|
+
const keyType = typeof key;
|
|
139
|
+
if (keyType !== 'string' && keyType !== 'number' && keyType !== 'symbol') {
|
|
140
|
+
throw new TypeError(`Invalid key type: keys must be string, number, or symbol, received ${keyType}. Key value: ${String(key)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return Object.fromEntries(entries);
|
|
126
144
|
});
|
|
127
145
|
(0, core_utils_1.defineIfNotExists)(Array.prototype, 'toSet', function (selector) {
|
|
128
146
|
const getValue = (0, core_utils_1.resolveSelector)(selector, (item) => item);
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setSlugifyConfig = exports.resetSlugifyConfig = exports.getSlugifyConfig = void 0;
|
|
3
4
|
require("./array/array-constructor");
|
|
4
5
|
require("./array/array-prototype");
|
|
5
6
|
require("./map/map-prototype");
|
|
6
7
|
require("./object/object-prototype");
|
|
7
8
|
require("./set/set-prototype");
|
|
8
9
|
require("./string/string-prototype");
|
|
10
|
+
// Export slugify configuration functions
|
|
11
|
+
var slugify_config_1 = require("./utils/slugify.config");
|
|
12
|
+
Object.defineProperty(exports, "getSlugifyConfig", { enumerable: true, get: function () { return slugify_config_1.getSlugifyConfig; } });
|
|
13
|
+
Object.defineProperty(exports, "resetSlugifyConfig", { enumerable: true, get: function () { return slugify_config_1.resetSlugifyConfig; } });
|
|
14
|
+
Object.defineProperty(exports, "setSlugifyConfig", { enumerable: true, get: function () { return slugify_config_1.setSlugifyConfig; } });
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_utils_1 = require("../utils/core.utils");
|
|
4
|
-
const logic_utils_1 = require("../utils/logic.utils");
|
|
5
4
|
/**
|
|
6
5
|
* @see Map.prototype.toList
|
|
7
6
|
*/
|
|
@@ -34,7 +33,17 @@ const logic_utils_1 = require("../utils/logic.utils");
|
|
|
34
33
|
* @see Map.prototype.toObject
|
|
35
34
|
*/
|
|
36
35
|
(0, core_utils_1.defineIfNotExists)(Map.prototype, 'toObject', function () {
|
|
37
|
-
|
|
36
|
+
const entries = Array.from(this.entries());
|
|
37
|
+
for (const [key] of entries) {
|
|
38
|
+
if (key === null || key === undefined) {
|
|
39
|
+
throw new TypeError(`Invalid key: key cannot be null or undefined. Key received: ${String(key)}`);
|
|
40
|
+
}
|
|
41
|
+
const keyType = typeof key;
|
|
42
|
+
if (keyType !== 'string' && keyType !== 'number' && keyType !== 'symbol') {
|
|
43
|
+
throw new TypeError(`Invalid key type: keys must be string, number, or symbol, received ${keyType}. Key value: ${String(key)}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return Object.fromEntries(entries);
|
|
38
47
|
});
|
|
39
48
|
/**
|
|
40
49
|
* @see Map.prototype.ensureArray
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export {};
|
|
2
1
|
declare global {
|
|
3
2
|
interface Object {
|
|
4
3
|
/**
|
|
@@ -71,5 +70,44 @@ declare global {
|
|
|
71
70
|
* - null and undefined are handled correctly
|
|
72
71
|
*/
|
|
73
72
|
deepEquals(other: unknown): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Converts the object to a stable string representation with sorted keys.
|
|
75
|
+
* The resulting string is deterministic: the same object will always produce the same string.
|
|
76
|
+
*
|
|
77
|
+
* @returns {string} A stable string representation of the object
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const obj = { b: 2, a: 1 };
|
|
81
|
+
* obj.stableStringify(); // '{"a":1,"b":2}'
|
|
82
|
+
*
|
|
83
|
+
* const obj2 = { a: 1, b: 2 };
|
|
84
|
+
* obj2.stableStringify(); // '{"a":1,"b":2}' (same result, different key order)
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* - Object keys are sorted alphabetically to ensure stable output
|
|
88
|
+
* - Arrays maintain their element order
|
|
89
|
+
* - Null and primitives are handled using JSON.stringify
|
|
90
|
+
*/
|
|
91
|
+
stableStringify(): string;
|
|
92
|
+
/**
|
|
93
|
+
* Generates a stable hash of the object using FNV-1a algorithm.
|
|
94
|
+
* The hash is deterministic: the same object will always produce the same hash.
|
|
95
|
+
*
|
|
96
|
+
* @returns {string} A hexadecimal string representing the hash of the object
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const obj = { b: 2, a: 1 };
|
|
100
|
+
* obj.stableHash(); // '7a8c9f2b'
|
|
101
|
+
*
|
|
102
|
+
* const obj2 = { a: 1, b: 2 };
|
|
103
|
+
* obj2.stableHash(); // '7a8c9f2b' (same hash, different key order)
|
|
104
|
+
*
|
|
105
|
+
* @remarks
|
|
106
|
+
* - Uses FNV-1a (Fowler–Noll–Vo) hashing algorithm
|
|
107
|
+
* - The object is first converted to a stable string using stableStringify()
|
|
108
|
+
* - The hash is returned as a hexadecimal string
|
|
109
|
+
*/
|
|
110
|
+
stableHash(): string;
|
|
74
111
|
}
|
|
75
112
|
}
|
|
113
|
+
export {};
|
|
@@ -1,26 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
4
|
-
const descriptor = Object.getOwnPropertyDescriptor(Object.prototype, name);
|
|
5
|
-
if (!descriptor || descriptor.writable || descriptor.configurable) {
|
|
6
|
-
Object.defineProperty(Object.prototype, name, {
|
|
7
|
-
value: fn,
|
|
8
|
-
enumerable: false,
|
|
9
|
-
configurable: true,
|
|
10
|
-
writable: true,
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
};
|
|
3
|
+
const core_utils_1 = require("../utils/core.utils");
|
|
14
4
|
/**
|
|
15
5
|
* @see Object.prototype.deepClone
|
|
16
6
|
*/
|
|
17
|
-
defineIfNotExists('deepClone', function () {
|
|
7
|
+
(0, core_utils_1.defineIfNotExists)(Object.prototype, 'deepClone', function () {
|
|
18
8
|
return structuredClone(this);
|
|
19
9
|
});
|
|
20
10
|
/**
|
|
21
11
|
* @see Object.prototype.deepMerge
|
|
22
12
|
*/
|
|
23
|
-
defineIfNotExists('deepMerge', function (source) {
|
|
13
|
+
(0, core_utils_1.defineIfNotExists)(Object.prototype, 'deepMerge', function (source) {
|
|
24
14
|
if (typeof source !== 'object' || source === null) {
|
|
25
15
|
throw new TypeError('Source must be a non-null object');
|
|
26
16
|
}
|
|
@@ -43,7 +33,7 @@ defineIfNotExists('deepMerge', function (source) {
|
|
|
43
33
|
/**
|
|
44
34
|
* @see Object.prototype.deepEquals
|
|
45
35
|
*/
|
|
46
|
-
defineIfNotExists('deepEquals', function (other) {
|
|
36
|
+
(0, core_utils_1.defineIfNotExists)(Object.prototype, 'deepEquals', function (other) {
|
|
47
37
|
function eq(a, b) {
|
|
48
38
|
// Functions: always false (even if code is the same)
|
|
49
39
|
if (typeof a === 'function' || typeof b === 'function') {
|
|
@@ -95,3 +85,33 @@ defineIfNotExists('deepEquals', function (other) {
|
|
|
95
85
|
}
|
|
96
86
|
return eq(this, other);
|
|
97
87
|
});
|
|
88
|
+
/**
|
|
89
|
+
* @see Object.prototype.stableStringify
|
|
90
|
+
*/
|
|
91
|
+
(0, core_utils_1.defineIfNotExists)(Object.prototype, 'stableStringify', function () {
|
|
92
|
+
const stringify = (v) => {
|
|
93
|
+
if (v === null || typeof v !== 'object') {
|
|
94
|
+
return JSON.stringify(v);
|
|
95
|
+
}
|
|
96
|
+
if (Array.isArray(v)) {
|
|
97
|
+
return '[' + v.map(stringify).join(',') + ']';
|
|
98
|
+
}
|
|
99
|
+
const obj = v;
|
|
100
|
+
const keys = Object.keys(obj).sort();
|
|
101
|
+
const entries = keys.map((key) => JSON.stringify(key) + ':' + stringify(obj[key]));
|
|
102
|
+
return '{' + entries.join(',') + '}';
|
|
103
|
+
};
|
|
104
|
+
return stringify(this);
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* @see Object.prototype.stableHash
|
|
108
|
+
*/
|
|
109
|
+
(0, core_utils_1.defineIfNotExists)(Object.prototype, 'stableHash', function () {
|
|
110
|
+
const str = this.stableStringify();
|
|
111
|
+
let hash = 2166136261;
|
|
112
|
+
for (let i = 0; i < str.length; i++) {
|
|
113
|
+
hash ^= str.charCodeAt(i);
|
|
114
|
+
hash = Math.imul(hash, 16777619);
|
|
115
|
+
}
|
|
116
|
+
return (hash >>> 0).toString(16);
|
|
117
|
+
});
|
|
@@ -107,4 +107,78 @@ describe('Object.prototype', () => {
|
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
});
|
|
110
|
+
describe('stableStringify()', () => {
|
|
111
|
+
it('should stringify plain objects with sorted keys', () => {
|
|
112
|
+
const obj = { b: 2, a: 1 };
|
|
113
|
+
expect(obj.stableStringify()).toBe('{"a":1,"b":2}');
|
|
114
|
+
});
|
|
115
|
+
it('should produce same string regardless of key order', () => {
|
|
116
|
+
const obj1 = { b: 2, a: 1, c: 3 };
|
|
117
|
+
const obj2 = { c: 3, a: 1, b: 2 };
|
|
118
|
+
expect(obj1.stableStringify()).toBe(obj2.stableStringify());
|
|
119
|
+
});
|
|
120
|
+
it('should stringify arrays with element order preserved', () => {
|
|
121
|
+
const arr = [1, 2, 3];
|
|
122
|
+
expect(arr.stableStringify()).toBe('[1,2,3]');
|
|
123
|
+
});
|
|
124
|
+
it('should handle nested objects', () => {
|
|
125
|
+
const obj = { b: { d: 4, c: 3 }, a: 1 };
|
|
126
|
+
expect(obj.stableStringify()).toBe('{"a":1,"b":{"c":3,"d":4}}');
|
|
127
|
+
});
|
|
128
|
+
it('should handle arrays within objects', () => {
|
|
129
|
+
const obj = { a: [1, 2], b: 2 };
|
|
130
|
+
expect(obj.stableStringify()).toBe('{"a":[1,2],"b":2}');
|
|
131
|
+
});
|
|
132
|
+
it('should handle empty objects and arrays', () => {
|
|
133
|
+
expect({}.stableStringify()).toBe('{}');
|
|
134
|
+
expect([].stableStringify()).toBe('[]');
|
|
135
|
+
});
|
|
136
|
+
it('should handle undefined values in objects', () => {
|
|
137
|
+
const obj = { a: undefined, b: 1 };
|
|
138
|
+
expect(obj.stableStringify()).toBe('{"a":undefined,"b":1}');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
describe('stableHash()', () => {
|
|
142
|
+
it('should generate same hash for objects with different key order', () => {
|
|
143
|
+
const obj1 = { b: 2, a: 1 };
|
|
144
|
+
const obj2 = { a: 1, b: 2 };
|
|
145
|
+
expect(obj1.stableHash()).toBe(obj2.stableHash());
|
|
146
|
+
});
|
|
147
|
+
it('should generate different hashes for different objects', () => {
|
|
148
|
+
const obj1 = { a: 1, b: 2 };
|
|
149
|
+
const obj2 = { a: 1, b: 3 };
|
|
150
|
+
expect(obj1.stableHash()).not.toBe(obj2.stableHash());
|
|
151
|
+
});
|
|
152
|
+
it('should generate consistent hashes', () => {
|
|
153
|
+
const obj = { a: 1, b: 2 };
|
|
154
|
+
const hash1 = obj.stableHash();
|
|
155
|
+
const hash2 = obj.stableHash();
|
|
156
|
+
expect(hash1).toBe(hash2);
|
|
157
|
+
});
|
|
158
|
+
it('should generate hex string', () => {
|
|
159
|
+
const obj = { a: 1 };
|
|
160
|
+
const hash = obj.stableHash();
|
|
161
|
+
expect(/^[0-9a-f]+$/.test(hash)).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
it('should handle arrays', () => {
|
|
164
|
+
const arr1 = [1, 2, 3];
|
|
165
|
+
const arr2 = [1, 2, 3];
|
|
166
|
+
expect(arr1.stableHash()).toBe(arr2.stableHash());
|
|
167
|
+
});
|
|
168
|
+
it('should handle different arrays', () => {
|
|
169
|
+
const arr1 = [1, 2, 3];
|
|
170
|
+
const arr2 = [3, 2, 1];
|
|
171
|
+
expect(arr1.stableHash()).not.toBe(arr2.stableHash());
|
|
172
|
+
});
|
|
173
|
+
it('should handle nested structures', () => {
|
|
174
|
+
const obj1 = { a: [1, { b: 2 }], c: 3 };
|
|
175
|
+
const obj2 = { c: 3, a: [1, { b: 2 }] };
|
|
176
|
+
expect(obj1.stableHash()).toBe(obj2.stableHash());
|
|
177
|
+
});
|
|
178
|
+
it('should handle empty objects', () => {
|
|
179
|
+
const obj1 = {};
|
|
180
|
+
const obj2 = {};
|
|
181
|
+
expect(obj1.stableHash()).toBe(obj2.stableHash());
|
|
182
|
+
});
|
|
183
|
+
});
|
|
110
184
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { SlugifyConfig } from '../utils/slugify.config';
|
|
2
2
|
declare global {
|
|
3
3
|
interface String {
|
|
4
4
|
/**
|
|
@@ -138,24 +138,61 @@ declare global {
|
|
|
138
138
|
/**
|
|
139
139
|
* Converts the string into a URL-friendly slug format.
|
|
140
140
|
* Combines normalization, lowercasing, whitespace handling, and special character removal.
|
|
141
|
+
* Can be customized using global configuration or per-call options.
|
|
141
142
|
*
|
|
142
143
|
* @this {string} The string to slugify
|
|
144
|
+
* @param {SlugifyConfig} [config] - Optional configuration to override global settings
|
|
143
145
|
* @returns {string} A URL-safe slug version of the string
|
|
144
146
|
*
|
|
145
147
|
* @example
|
|
148
|
+
* // Basic usage (default config)
|
|
146
149
|
* 'Hello World'.slugify(); // 'hello-world'
|
|
147
150
|
* 'Héllo Wørld'.slugify(); // 'hello-world'
|
|
148
151
|
* 'Hello World!!!'.slugify(); // 'hello-world'
|
|
149
152
|
*
|
|
153
|
+
* @example
|
|
154
|
+
* // Custom replacements
|
|
155
|
+
* 'Test ♀'.slugify({ customReplacements: { "♀": "feminin" } }); // 'test-feminin'
|
|
156
|
+
* 'User♂@domain.com'.slugify({ customReplacements: { "♂": "masculin", "@": "at" } }); // 'usermasculinatdomain-com'
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* // Custom separator
|
|
160
|
+
* 'Hello World'.slugify({ separator: "_" }); // 'hello_world'
|
|
161
|
+
* 'Hello World'.slugify({ separator: "--" }); // 'hello--world'
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* // Preserve accents
|
|
165
|
+
* 'Éléphant'.slugify({ removeAccents: false }); // 'éléphant'
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* // Disable lowercasing
|
|
169
|
+
* 'Hello World'.slugify({ lowercase: false }); // 'Hello-World'
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* // Max length
|
|
173
|
+
* 'Very long string'.slugify({ maxLength: 8 }); // 'very-lon'
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* // Custom transformers
|
|
177
|
+
* 'hello world'.slugify({
|
|
178
|
+
* transformers: [(str) => str.replace(/world/g, 'universe')]
|
|
179
|
+
* }); // 'hello-universe'
|
|
180
|
+
*
|
|
150
181
|
* @remarks
|
|
151
|
-
* -
|
|
152
|
-
* -
|
|
153
|
-
* -
|
|
154
|
-
* -
|
|
155
|
-
* -
|
|
156
|
-
* -
|
|
182
|
+
* - Uses global configuration by default (see setSlugifyConfig)
|
|
183
|
+
* - Normalizes Unicode characters (NFD decomposition) by default
|
|
184
|
+
* - Removes accents and diacritical marks by default
|
|
185
|
+
* - Converts to lowercase by default
|
|
186
|
+
* - Replaces spaces and special characters with separator (hyphen by default)
|
|
187
|
+
* - Removes leading/trailing separators
|
|
188
|
+
* - Collapses multiple consecutive separators into one
|
|
189
|
+
*
|
|
190
|
+
* @see setSlugifyConfig
|
|
191
|
+
* @see getSlugifyConfig
|
|
192
|
+
* @see resetSlugifyConfig
|
|
157
193
|
*/
|
|
158
194
|
slugify(): string;
|
|
195
|
+
slugify(config: SlugifyConfig): string;
|
|
159
196
|
/**
|
|
160
197
|
* Replaces a substring between `start` and `end` indices with a given string.
|
|
161
198
|
* Provides precise control over which portion of the string to replace.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_utils_1 = require("../utils/core.utils");
|
|
4
|
+
const slugify_utils_1 = require("../utils/slugify.utils");
|
|
4
5
|
/**
|
|
5
6
|
* @see String.prototype.splitWords
|
|
6
7
|
*/
|
|
@@ -61,12 +62,8 @@ const core_utils_1 = require("../utils/core.utils");
|
|
|
61
62
|
/**
|
|
62
63
|
* @see String.prototype.slugify
|
|
63
64
|
*/
|
|
64
|
-
(0, core_utils_1.defineIfNotExists)(String.prototype, 'slugify', function () {
|
|
65
|
-
return
|
|
66
|
-
.replace(/[̀-ͯ]/g, '')
|
|
67
|
-
.replace(/[^a-zA-Z0-9]+/g, '-')
|
|
68
|
-
.replace(/^-+|-+$/g, '')
|
|
69
|
-
.toLowerCase();
|
|
65
|
+
(0, core_utils_1.defineIfNotExists)(String.prototype, 'slugify', function (config) {
|
|
66
|
+
return (0, slugify_utils_1.slugifyString)(this, config);
|
|
70
67
|
});
|
|
71
68
|
/**
|
|
72
69
|
* @see String.prototype.capitalize
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
require("../string/string-prototype");
|
|
4
|
+
const slugify_config_1 = require("../utils/slugify.config");
|
|
4
5
|
describe('String.prototype', () => {
|
|
5
6
|
describe('capitalize()', () => {
|
|
6
7
|
it('should capitalize the first character', () => {
|
|
@@ -67,11 +68,80 @@ describe('String.prototype', () => {
|
|
|
67
68
|
});
|
|
68
69
|
});
|
|
69
70
|
describe('slugify()', () => {
|
|
70
|
-
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
(0, slugify_config_1.resetSlugifyConfig)(); // Reset to defaults before each test
|
|
73
|
+
});
|
|
74
|
+
it('should slugify a string with default config', () => {
|
|
71
75
|
expect('Hello World!'.slugify()).toBe('hello-world');
|
|
72
76
|
expect("Éléphant à l'été".slugify()).toBe('elephant-a-l-ete');
|
|
73
77
|
expect(' --Hello__World-- '.slugify()).toBe('hello-world');
|
|
74
78
|
});
|
|
79
|
+
describe('custom replacements', () => {
|
|
80
|
+
it('should apply custom replacements per call', () => {
|
|
81
|
+
expect('Test ♀'.slugify({ customReplacements: { '♀': 'feminin' } })).toBe('test-feminin');
|
|
82
|
+
expect('User♂@domain.com'.slugify({ customReplacements: { '♂': 'masculin', '@': 'at' } })).toBe('usermasculinatdomain-com');
|
|
83
|
+
});
|
|
84
|
+
it('should handle special regex characters in replacements', () => {
|
|
85
|
+
expect('Test [bracket]'.slugify({ customReplacements: { '[': 'left', ']': 'right' } })).toBe('test-leftbracketright');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('separator customization', () => {
|
|
89
|
+
it('should use custom separator globally', () => {
|
|
90
|
+
(0, slugify_config_1.setSlugifyConfig)({ separator: '_' });
|
|
91
|
+
expect('Hello World'.slugify()).toBe('hello_world');
|
|
92
|
+
expect('Test String'.slugify()).toBe('test_string');
|
|
93
|
+
});
|
|
94
|
+
it('should use custom separator per call', () => {
|
|
95
|
+
expect('Hello World'.slugify({ separator: '_' })).toBe('hello_world');
|
|
96
|
+
expect('Test String'.slugify({ separator: '__' })).toBe('test__string');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('case transformation', () => {
|
|
100
|
+
it('should respect lowercase setting globally', () => {
|
|
101
|
+
(0, slugify_config_1.setSlugifyConfig)({ lowercase: false });
|
|
102
|
+
expect('Hello World'.slugify()).toBe('Hello-World');
|
|
103
|
+
});
|
|
104
|
+
it('should respect lowercase setting per call', () => {
|
|
105
|
+
expect('Hello World'.slugify({ lowercase: false })).toBe('Hello-World');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe('accent removal', () => {
|
|
109
|
+
it('should respect removeAccents setting per call', () => {
|
|
110
|
+
expect('Éléphant'.slugify({ removeAccents: false })).toBe('éléphant');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('allowed characters', () => {
|
|
114
|
+
it('should use custom allowed characters', () => {
|
|
115
|
+
expect('Hello123!@#'.slugify({ allowedChars: /[a-zA-Z0-9_]/ })).toBe('hello123');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('max length', () => {
|
|
119
|
+
it('should truncate to max length', () => {
|
|
120
|
+
expect('very-long-string-here'.slugify({ maxLength: 10 })).toBe('very-long');
|
|
121
|
+
expect('short'.slugify({ maxLength: 10 })).toBe('short');
|
|
122
|
+
});
|
|
123
|
+
it('should remove trailing separator after truncation', () => {
|
|
124
|
+
expect('hello-world-test'.slugify({ maxLength: 8 })).toBe('hello-wo');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe('custom transformers', () => {
|
|
128
|
+
it('should apply custom transformers', () => {
|
|
129
|
+
const config = {
|
|
130
|
+
transformers: [
|
|
131
|
+
(str) => str.replace(/test/g, 'example'),
|
|
132
|
+
(str) => str.toUpperCase(),
|
|
133
|
+
],
|
|
134
|
+
lowercase: false,
|
|
135
|
+
};
|
|
136
|
+
expect('this is a test'.slugify(config)).toBe('THIS-IS-A-EXAMPLE');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe('configuration merging', () => {
|
|
140
|
+
it('should merge global and local config correctly', () => {
|
|
141
|
+
// Local config should override global defaults
|
|
142
|
+
expect('Test ♀'.slugify({ separator: '_', customReplacements: { '♀': 'female' } })).toBe('test_female');
|
|
143
|
+
});
|
|
144
|
+
});
|
|
75
145
|
});
|
|
76
146
|
describe('replaceRange()', () => {
|
|
77
147
|
it('should replace a single character at the given index', () => {
|
|
@@ -1,24 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.mapToObject = mapToObject;
|
|
4
3
|
exports.sortBy = sortBy;
|
|
5
4
|
const core_utils_1 = require("./core.utils");
|
|
6
|
-
function mapToObject(map) {
|
|
7
|
-
const obj = {};
|
|
8
|
-
for (const [key, value] of map) {
|
|
9
|
-
// Validate that key is not null or undefined
|
|
10
|
-
if (key === null || key === undefined) {
|
|
11
|
-
throw new TypeError(`Invalid key: key cannot be null or undefined. Key received: ${String(key)}`);
|
|
12
|
-
}
|
|
13
|
-
const keyType = typeof key;
|
|
14
|
-
// Only allow string, number, or symbol
|
|
15
|
-
if (keyType !== 'string' && keyType !== 'number' && keyType !== 'symbol') {
|
|
16
|
-
throw new TypeError(`Invalid key type: keys must be string, number, or symbol, received ${keyType}. Key value: ${String(key)}`);
|
|
17
|
-
}
|
|
18
|
-
obj[key] = value;
|
|
19
|
-
}
|
|
20
|
-
return obj;
|
|
21
|
-
}
|
|
22
5
|
function sortBy(arr, direction, selector) {
|
|
23
6
|
if (arr.length === 0)
|
|
24
7
|
return arr.slice();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration types and interfaces for the slugify functionality.
|
|
3
|
+
* Provides comprehensive customization options for URL slug generation.
|
|
4
|
+
*/
|
|
5
|
+
export interface SlugifyConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Custom character replacements applied before other transformations.
|
|
8
|
+
* Useful for replacing special symbols with words or other characters.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* { "♀": "feminin", "♂": "masculin", "@": "at" }
|
|
12
|
+
*/
|
|
13
|
+
customReplacements?: Record<string, string>;
|
|
14
|
+
/**
|
|
15
|
+
* Character used to separate words in the slug.
|
|
16
|
+
* @default "-"
|
|
17
|
+
*/
|
|
18
|
+
separator?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Whether to convert the result to lowercase.
|
|
21
|
+
* @default true
|
|
22
|
+
*/
|
|
23
|
+
lowercase?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether to remove accents and diacritical marks.
|
|
26
|
+
* @default true
|
|
27
|
+
*/
|
|
28
|
+
removeAccents?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Regular expression pattern for characters to keep.
|
|
31
|
+
* Characters not matching this pattern will be replaced with the separator.
|
|
32
|
+
* @default "/[a-zA-Z0-9]/"
|
|
33
|
+
*/
|
|
34
|
+
allowedChars?: RegExp;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum length of the resulting slug (excluding separator trimming).
|
|
37
|
+
* If exceeded, the slug will be truncated.
|
|
38
|
+
* @default undefined (no limit)
|
|
39
|
+
*/
|
|
40
|
+
maxLength?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Custom transformation functions applied in order.
|
|
43
|
+
* Each function receives the current string state and should return the transformed string.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* [(str) => str.replace(/custom-pattern/g, 'replacement')]
|
|
47
|
+
*/
|
|
48
|
+
transformers?: Array<(str: string) => string>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Default configuration for slugify operations.
|
|
52
|
+
* Provides sensible defaults that can be overridden by users.
|
|
53
|
+
*/
|
|
54
|
+
export declare const defaultSlugifyConfig: SlugifyConfig;
|
|
55
|
+
/**
|
|
56
|
+
* Sets the global slugify configuration that will be used by default.
|
|
57
|
+
* Merges the provided config with the existing global config.
|
|
58
|
+
*
|
|
59
|
+
* @param config - Partial configuration object to merge with current global config
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* setSlugifyConfig({
|
|
63
|
+
* customReplacements: { "♀": "feminin", "♂": "masculin" },
|
|
64
|
+
* separator: "_"
|
|
65
|
+
* });
|
|
66
|
+
*/
|
|
67
|
+
export declare function setSlugifyConfig(config: SlugifyConfig): void;
|
|
68
|
+
/**
|
|
69
|
+
* Gets the current global slugify configuration.
|
|
70
|
+
* Returns a copy to prevent external mutations.
|
|
71
|
+
*
|
|
72
|
+
* @returns Current global configuration object
|
|
73
|
+
*/
|
|
74
|
+
export declare function getSlugifyConfig(): SlugifyConfig;
|
|
75
|
+
/**
|
|
76
|
+
* Resets the global slugify configuration to default values.
|
|
77
|
+
*/
|
|
78
|
+
export declare function resetSlugifyConfig(): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration types and interfaces for the slugify functionality.
|
|
4
|
+
* Provides comprehensive customization options for URL slug generation.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.defaultSlugifyConfig = void 0;
|
|
8
|
+
exports.setSlugifyConfig = setSlugifyConfig;
|
|
9
|
+
exports.getSlugifyConfig = getSlugifyConfig;
|
|
10
|
+
exports.resetSlugifyConfig = resetSlugifyConfig;
|
|
11
|
+
/**
|
|
12
|
+
* Default configuration for slugify operations.
|
|
13
|
+
* Provides sensible defaults that can be overridden by users.
|
|
14
|
+
*/
|
|
15
|
+
exports.defaultSlugifyConfig = {
|
|
16
|
+
customReplacements: {},
|
|
17
|
+
separator: '-',
|
|
18
|
+
lowercase: true,
|
|
19
|
+
removeAccents: true,
|
|
20
|
+
allowedChars: undefined, // Will be determined dynamically based on removeAccents
|
|
21
|
+
maxLength: undefined,
|
|
22
|
+
transformers: [],
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Global configuration instance that can be modified by users.
|
|
26
|
+
* Starts with default values but can be updated via setSlugifyConfig().
|
|
27
|
+
*/
|
|
28
|
+
let globalSlugifyConfig = { ...exports.defaultSlugifyConfig };
|
|
29
|
+
/**
|
|
30
|
+
* Sets the global slugify configuration that will be used by default.
|
|
31
|
+
* Merges the provided config with the existing global config.
|
|
32
|
+
*
|
|
33
|
+
* @param config - Partial configuration object to merge with current global config
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* setSlugifyConfig({
|
|
37
|
+
* customReplacements: { "♀": "feminin", "♂": "masculin" },
|
|
38
|
+
* separator: "_"
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
41
|
+
function setSlugifyConfig(config) {
|
|
42
|
+
globalSlugifyConfig = { ...globalSlugifyConfig, ...config };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Gets the current global slugify configuration.
|
|
46
|
+
* Returns a copy to prevent external mutations.
|
|
47
|
+
*
|
|
48
|
+
* @returns Current global configuration object
|
|
49
|
+
*/
|
|
50
|
+
function getSlugifyConfig() {
|
|
51
|
+
return { ...globalSlugifyConfig };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resets the global slugify configuration to default values.
|
|
55
|
+
*/
|
|
56
|
+
function resetSlugifyConfig() {
|
|
57
|
+
globalSlugifyConfig = { ...exports.defaultSlugifyConfig };
|
|
58
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SlugifyConfig } from './slugify.config';
|
|
2
|
+
/**
|
|
3
|
+
* Applies slugify transformations to a string based on the provided configuration.
|
|
4
|
+
* This is the core logic for converting strings to URL-friendly slugs.
|
|
5
|
+
*
|
|
6
|
+
* @param str - The input string to slugify
|
|
7
|
+
* @param config - Configuration object (uses global config if not provided)
|
|
8
|
+
* @returns The slugified string
|
|
9
|
+
*/
|
|
10
|
+
export declare function slugifyString(str: string, config?: SlugifyConfig): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.slugifyString = slugifyString;
|
|
4
|
+
const slugify_config_1 = require("./slugify.config");
|
|
5
|
+
/**
|
|
6
|
+
* Applies slugify transformations to a string based on the provided configuration.
|
|
7
|
+
* This is the core logic for converting strings to URL-friendly slugs.
|
|
8
|
+
*
|
|
9
|
+
* @param str - The input string to slugify
|
|
10
|
+
* @param config - Configuration object (uses global config if not provided)
|
|
11
|
+
* @returns The slugified string
|
|
12
|
+
*/
|
|
13
|
+
function slugifyString(str, config) {
|
|
14
|
+
// Merge provided config with global config
|
|
15
|
+
const finalConfig = { ...(0, slugify_config_1.getSlugifyConfig)(), ...config };
|
|
16
|
+
let result = str;
|
|
17
|
+
// 1. Apply custom replacements first
|
|
18
|
+
if (finalConfig.customReplacements && Object.keys(finalConfig.customReplacements).length > 0) {
|
|
19
|
+
for (const [pattern, replacement] of Object.entries(finalConfig.customReplacements)) {
|
|
20
|
+
// Escape special regex characters in the pattern
|
|
21
|
+
const escapedPattern = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22
|
+
result = result.replace(new RegExp(escapedPattern, 'g'), replacement);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// 2. Apply custom transformers
|
|
26
|
+
if (finalConfig.transformers && finalConfig.transformers.length > 0) {
|
|
27
|
+
for (const transformer of finalConfig.transformers) {
|
|
28
|
+
result = transformer(result);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// 3. Remove accents if configured
|
|
32
|
+
if (finalConfig.removeAccents) {
|
|
33
|
+
result = result.normalize('NFD').replace(/[̀-ͯ]/g, '');
|
|
34
|
+
}
|
|
35
|
+
// 4. Replace non-allowed characters with separator
|
|
36
|
+
const separator = finalConfig.separator || '-';
|
|
37
|
+
// Determine allowed characters based on removeAccents setting
|
|
38
|
+
const defaultAllowedChars = finalConfig.removeAccents ? /[a-zA-Z0-9]/ : /[a-zA-Z0-9\u00C0-\u017F]/; // Include accented Latin characters if accents are preserved
|
|
39
|
+
const allowedRegex = finalConfig.allowedChars || defaultAllowedChars;
|
|
40
|
+
const allowedPattern = allowedRegex.source;
|
|
41
|
+
// Extract the character class content (remove surrounding brackets if present)
|
|
42
|
+
const charClass = allowedPattern.startsWith('[') && allowedPattern.endsWith(']') ? allowedPattern.slice(1, -1) : allowedPattern;
|
|
43
|
+
result = result.replace(new RegExp(`[^${charClass}]+`, 'g'), separator);
|
|
44
|
+
// 5. Remove leading/trailing separators
|
|
45
|
+
result = result.replace(new RegExp(`^${separator}+|${separator}+$`, 'g'), '');
|
|
46
|
+
// 6. Convert to lowercase if configured
|
|
47
|
+
if (finalConfig.lowercase) {
|
|
48
|
+
result = result.toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
// 7. Apply max length if configured
|
|
51
|
+
if (finalConfig.maxLength && finalConfig.maxLength > 0) {
|
|
52
|
+
result = result.slice(0, finalConfig.maxLength);
|
|
53
|
+
// Remove trailing separator after truncation
|
|
54
|
+
result = result.replace(new RegExp(`${separator}$`), '');
|
|
55
|
+
}
|
|
56
|
+
// 8. Collapse multiple consecutive separators
|
|
57
|
+
result = result.replace(new RegExp(`${separator}+`, 'g'), separator);
|
|
58
|
+
return result;
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "utilitish",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/FDonovan12/utilitish.git"
|
|
7
|
+
},
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public",
|
|
10
|
+
"provenance": false
|
|
11
|
+
},
|
|
4
12
|
"description": "",
|
|
5
13
|
"main": "dist/index.js",
|
|
6
14
|
"types": "dist/index.d.ts",
|