@splendidlabz/utils 1.11.6 → 1.12.1

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.
@@ -1,5 +1,368 @@
1
- import pluralizeLib from "pluralize";
2
- const pluralize = pluralizeLib;
1
+ function sanitizeRule(rule) {
2
+ if (typeof rule === "string") return new RegExp("^" + rule + "$", "i");
3
+ return rule;
4
+ }
5
+ function restoreCase(word, token) {
6
+ if (word === token) return token;
7
+ if (word === word.toLowerCase()) return token.toLowerCase();
8
+ if (word === word.toUpperCase()) return token.toUpperCase();
9
+ if (word[0] === word[0].toUpperCase()) {
10
+ return token.charAt(0).toUpperCase() + token.substr(1).toLowerCase();
11
+ }
12
+ return token.toLowerCase();
13
+ }
14
+ function interpolate(str, args) {
15
+ return str.replace(/\$(\d{1,2})/g, (match, index) => args[index] || "");
16
+ }
17
+ function replace(word, rule) {
18
+ return word.replace(rule[0], function(match, index) {
19
+ const result = interpolate(rule[1], arguments);
20
+ if (match === "") return restoreCase(word[index - 1], result);
21
+ return restoreCase(match, result);
22
+ });
23
+ }
24
+ function sanitizeWord(token, word, rules, uncountables) {
25
+ if (!token.length || uncountables.hasOwnProperty(token)) return word;
26
+ let len = rules.length;
27
+ while (len--) {
28
+ const rule = rules[len];
29
+ if (rule[0].test(word)) return replace(word, rule);
30
+ }
31
+ return word;
32
+ }
33
+ function replaceWord(replaceMap, keepMap, rules, uncountables) {
34
+ return function(word) {
35
+ const token = word.toLowerCase();
36
+ if (keepMap.hasOwnProperty(token)) return restoreCase(word, token);
37
+ if (replaceMap.hasOwnProperty(token)) {
38
+ return restoreCase(word, replaceMap[token]);
39
+ }
40
+ return sanitizeWord(token, word, rules, uncountables);
41
+ };
42
+ }
43
+ function checkWord(replaceMap, keepMap, rules, uncountables) {
44
+ return function(word) {
45
+ const token = word.toLowerCase();
46
+ if (keepMap.hasOwnProperty(token)) return true;
47
+ if (replaceMap.hasOwnProperty(token)) return false;
48
+ return sanitizeWord(token, token, rules, uncountables) === token;
49
+ };
50
+ }
51
+ const BASE_IRREGULAR = [
52
+ // Pronouns.
53
+ ["I", "we"],
54
+ ["me", "us"],
55
+ ["he", "they"],
56
+ ["she", "they"],
57
+ ["them", "them"],
58
+ ["myself", "ourselves"],
59
+ ["yourself", "yourselves"],
60
+ ["itself", "themselves"],
61
+ ["herself", "themselves"],
62
+ ["himself", "themselves"],
63
+ ["themself", "themselves"],
64
+ ["is", "are"],
65
+ ["was", "were"],
66
+ ["has", "have"],
67
+ ["this", "these"],
68
+ ["that", "those"],
69
+ // Words ending with a consonant and `o`.
70
+ ["echo", "echoes"],
71
+ ["dingo", "dingoes"],
72
+ ["volcano", "volcanoes"],
73
+ ["tornado", "tornadoes"],
74
+ ["torpedo", "torpedoes"],
75
+ // Ends with `us`.
76
+ ["genus", "genera"],
77
+ ["viscus", "viscera"],
78
+ // Ends with `ma`.
79
+ ["stigma", "stigmata"],
80
+ ["stoma", "stomata"],
81
+ ["dogma", "dogmata"],
82
+ ["lemma", "lemmata"],
83
+ ["schema", "schemata"],
84
+ ["anathema", "anathemata"],
85
+ // Other irregular rules.
86
+ ["ox", "oxen"],
87
+ ["axe", "axes"],
88
+ ["die", "dice"],
89
+ ["yes", "yeses"],
90
+ ["foot", "feet"],
91
+ ["eave", "eaves"],
92
+ ["goose", "geese"],
93
+ ["tooth", "teeth"],
94
+ ["quiz", "quizzes"],
95
+ ["human", "humans"],
96
+ ["proof", "proofs"],
97
+ ["carve", "carves"],
98
+ ["valve", "valves"],
99
+ ["looey", "looies"],
100
+ ["thief", "thieves"],
101
+ ["groove", "grooves"],
102
+ ["pickaxe", "pickaxes"],
103
+ ["passerby", "passersby"]
104
+ ];
105
+ const BASE_PLURAL = [
106
+ [/s?$/i, "s"],
107
+ // eslint-disable-next-line no-control-regex
108
+ [/[^\u0000-\u007F]$/i, "$0"],
109
+ [/([^aeiou]ese)$/i, "$1"],
110
+ [/(ax|test)is$/i, "$1es"],
111
+ [/(alias|[^aou]us|t[lm]as|gas|ris)$/i, "$1es"],
112
+ [/(e[mn]u)s?$/i, "$1s"],
113
+ [/([^l]ias|[aeiou]las|[ejzr]as|[iu]am)$/i, "$1"],
114
+ [
115
+ /(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,
116
+ "$1i"
117
+ ],
118
+ [/(alumn|alg|vertebr)(?:a|ae)$/i, "$1ae"],
119
+ [/(seraph|cherub)(?:im)?$/i, "$1im"],
120
+ [/(her|at|gr)o$/i, "$1oes"],
121
+ [
122
+ /(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor)(?:a|um)$/i,
123
+ "$1a"
124
+ ],
125
+ [
126
+ /(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)(?:a|on)$/i,
127
+ "$1a"
128
+ ],
129
+ [/sis$/i, "ses"],
130
+ [/(?:(kni|wi|li)fe|(ar|l|ea|eo|oa|hoo)f)$/i, "$1$2ves"],
131
+ [/([^aeiouy]|qu)y$/i, "$1ies"],
132
+ [/([^ch][ieo][ln])ey$/i, "$1ies"],
133
+ [/(x|ch|ss|sh|zz)$/i, "$1es"],
134
+ [/(matr|cod|mur|sil|vert|ind|append)(?:ix|ex)$/i, "$1ices"],
135
+ [/\b((?:tit)?m|l)(?:ice|ouse)$/i, "$1ice"],
136
+ [/(pe)(?:rson|ople)$/i, "$1ople"],
137
+ [/(child)(?:ren)?$/i, "$1ren"],
138
+ [/eaux$/i, "$0"],
139
+ [/m[ae]n$/i, "men"],
140
+ ["thou", "you"]
141
+ ];
142
+ const BASE_SINGULAR = [
143
+ [/s$/i, ""],
144
+ [/(ss)$/i, "$1"],
145
+ [/(wi|kni|(?:after|half|high|low|mid|non|night|[^\w]|^)li)ves$/i, "$1fe"],
146
+ [/(ar|(?:wo|[ae])l|[eo][ao])ves$/i, "$1f"],
147
+ [/ies$/i, "y"],
148
+ [
149
+ /\b([pl]|zomb|(?:neck|cross)?t|coll|faer|food|gen|goon|group|lass|talk|goal|cut)ies$/i,
150
+ "$1ie"
151
+ ],
152
+ [/\b(mon|smil)ies$/i, "$1ey"],
153
+ [/\b((?:tit)?m|l)ice$/i, "$1ouse"],
154
+ [/(seraph|cherub)im$/i, "$1"],
155
+ [
156
+ /(x|ch|ss|sh|zz|tto|go|cho|alias|[^aou]us|t[lm]as|gas|(?:her|at|gr)o|[aeiou]ris)(?:es)?$/i,
157
+ "$1"
158
+ ],
159
+ [
160
+ /(analy|diagno|parenthe|progno|synop|the|empha|cri|ne)(?:sis|ses)$/i,
161
+ "$1sis"
162
+ ],
163
+ [/(movie|twelve|abuse|e[mn]u)s$/i, "$1"],
164
+ [/(test)(?:is|es)$/i, "$1is"],
165
+ [
166
+ /(alumn|syllab|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat)(?:us|i)$/i,
167
+ "$1us"
168
+ ],
169
+ [
170
+ /(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|quor)a$/i,
171
+ "$1um"
172
+ ],
173
+ [
174
+ /(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat)a$/i,
175
+ "$1on"
176
+ ],
177
+ [/(alumn|alg|vertebr)ae$/i, "$1a"],
178
+ [/(cod|mur|sil|vert|ind)ices$/i, "$1ex"],
179
+ [/(matr|append)ices$/i, "$1ix"],
180
+ [/(pe)(rson|ople)$/i, "$1rson"],
181
+ [/(child)ren$/i, "$1"],
182
+ [/(eau)x?$/i, "$1"],
183
+ [/men$/i, "man"]
184
+ ];
185
+ const BASE_UNCOUNTABLE = [
186
+ // Singular words with no plurals.
187
+ "adulthood",
188
+ "advice",
189
+ "agenda",
190
+ "aid",
191
+ "aircraft",
192
+ "alcohol",
193
+ "ammo",
194
+ "analytics",
195
+ "anime",
196
+ "athletics",
197
+ "audio",
198
+ "bison",
199
+ "blood",
200
+ "bream",
201
+ "buffalo",
202
+ "butter",
203
+ "carp",
204
+ "cash",
205
+ "chassis",
206
+ "chess",
207
+ "clothing",
208
+ "cod",
209
+ "commerce",
210
+ "cooperation",
211
+ "corps",
212
+ "debris",
213
+ "diabetes",
214
+ "digestion",
215
+ "elk",
216
+ "energy",
217
+ "equipment",
218
+ "excretion",
219
+ "expertise",
220
+ "firmware",
221
+ "flounder",
222
+ "fun",
223
+ "gallows",
224
+ "garbage",
225
+ "graffiti",
226
+ "hardware",
227
+ "headquarters",
228
+ "health",
229
+ "herpes",
230
+ "highjinks",
231
+ "homework",
232
+ "housework",
233
+ "information",
234
+ "jeans",
235
+ "justice",
236
+ "kudos",
237
+ "labour",
238
+ "literature",
239
+ "machinery",
240
+ "mackerel",
241
+ "mail",
242
+ "media",
243
+ "mews",
244
+ "moose",
245
+ "music",
246
+ "mud",
247
+ "manga",
248
+ "news",
249
+ "only",
250
+ "personnel",
251
+ "pike",
252
+ "plankton",
253
+ "pliers",
254
+ "police",
255
+ "pollution",
256
+ "premises",
257
+ "rain",
258
+ "research",
259
+ "rice",
260
+ "salmon",
261
+ "scissors",
262
+ "series",
263
+ "sewage",
264
+ "shambles",
265
+ "shrimp",
266
+ "software",
267
+ "species",
268
+ "staff",
269
+ "swine",
270
+ "tennis",
271
+ "traffic",
272
+ "transportation",
273
+ "trout",
274
+ "tuna",
275
+ "wealth",
276
+ "welfare",
277
+ "whiting",
278
+ "wildebeest",
279
+ "wildlife",
280
+ "you",
281
+ /pok[eé]mon$/i,
282
+ // Regexes.
283
+ /[^aeiou]ese$/i,
284
+ // "chinese", "japanese"
285
+ /deer$/i,
286
+ // "deer", "reindeer"
287
+ /fish$/i,
288
+ // "fish", "blowfish", "angelfish"
289
+ /measles$/i,
290
+ /o[iu]s$/i,
291
+ // "carnivorous"
292
+ /pox$/i,
293
+ // "chickpox", "smallpox"
294
+ /sheep$/i
295
+ ];
296
+ function createPluralize(config = {}) {
297
+ const pluralRules = [];
298
+ const singularRules = [];
299
+ const uncountables = {};
300
+ const irregularPlurals = {};
301
+ const irregularSingles = {};
302
+ for (const [single, plur] of [
303
+ ...BASE_IRREGULAR,
304
+ ...config.irregular || []
305
+ ]) {
306
+ irregularSingles[single.toLowerCase()] = plur.toLowerCase();
307
+ irregularPlurals[plur.toLowerCase()] = single.toLowerCase();
308
+ }
309
+ for (const [rule, replacement] of [...BASE_PLURAL, ...config.plural || []]) {
310
+ pluralRules.push([sanitizeRule(rule), replacement]);
311
+ }
312
+ for (const [rule, replacement] of [
313
+ ...BASE_SINGULAR,
314
+ ...config.singular || []
315
+ ]) {
316
+ singularRules.push([sanitizeRule(rule), replacement]);
317
+ }
318
+ for (const word of [...BASE_UNCOUNTABLE, ...config.uncountable || []]) {
319
+ if (typeof word === "string") {
320
+ uncountables[word.toLowerCase()] = true;
321
+ } else {
322
+ pluralRules.push([sanitizeRule(word), "$0"]);
323
+ singularRules.push([sanitizeRule(word), "$0"]);
324
+ }
325
+ }
326
+ const plural2 = replaceWord(
327
+ irregularSingles,
328
+ irregularPlurals,
329
+ pluralRules,
330
+ uncountables
331
+ );
332
+ const singular2 = replaceWord(
333
+ irregularPlurals,
334
+ irregularSingles,
335
+ singularRules,
336
+ uncountables
337
+ );
338
+ const isPlural2 = checkWord(
339
+ irregularSingles,
340
+ irregularPlurals,
341
+ pluralRules,
342
+ uncountables
343
+ );
344
+ const isSingular2 = checkWord(
345
+ irregularPlurals,
346
+ irregularSingles,
347
+ singularRules,
348
+ uncountables
349
+ );
350
+ function pluralize2(word, count, inclusive) {
351
+ const out = count === 1 ? singular2(word) : plural2(word);
352
+ return (inclusive ? count + " " : "") + out;
353
+ }
354
+ return Object.assign(pluralize2, { plural: plural2, singular: singular2, isPlural: isPlural2, isSingular: isSingular2 });
355
+ }
356
+ const pluralize = createPluralize();
357
+ const plural = pluralize.plural;
358
+ const singular = pluralize.singular;
359
+ const isPlural = pluralize.isPlural;
360
+ const isSingular = pluralize.isSingular;
3
361
  export {
4
- pluralize
362
+ createPluralize,
363
+ isPlural,
364
+ isSingular,
365
+ plural,
366
+ pluralize,
367
+ singular
5
368
  };
@@ -2,15 +2,38 @@ function toQueryString(object) {
2
2
  const searchParams = new URLSearchParams(object);
3
3
  return searchParams.toString();
4
4
  }
5
- function plural(count, noun, suffix = "s") {
6
- console.warn("plural is deprecated. Use pluralize instead");
7
- return `${count} ${noun}${count !== 1 ? suffix : ""}`;
5
+ function parseQueryString(string = "") {
6
+ const query = string.startsWith("?") ? string.slice(1) : string;
7
+ const result = {};
8
+ if (!query) return result;
9
+ const decode = (str) => decodeURIComponent(str.replace(/\+/g, " "));
10
+ const keyToPath = (key) => key.replace(/\]/g, "").split("[").map(decode);
11
+ for (const pair of query.split("&")) {
12
+ if (!pair) continue;
13
+ const eq = pair.indexOf("=");
14
+ const path = keyToPath(eq === -1 ? pair : pair.slice(0, eq));
15
+ const value = decode(eq === -1 ? "" : pair.slice(eq + 1));
16
+ let current = result;
17
+ path.forEach((key, i) => {
18
+ const index = Array.isArray(current) ? Number(key) : key;
19
+ if (i === path.length - 1) {
20
+ if (key === "") current.push(value);
21
+ else current[index] = value;
22
+ return;
23
+ }
24
+ const next = path[i + 1];
25
+ const childIsArray = next === "" || /^\d+$/.test(next);
26
+ if (current[index] == null) current[index] = childIsArray ? [] : {};
27
+ current = current[index];
28
+ });
29
+ }
30
+ return result;
8
31
  }
9
32
  function stripNumbers(string) {
10
33
  return string.replace(/\d*/, "");
11
34
  }
12
35
  export {
13
- plural,
36
+ parseQueryString,
14
37
  stripNumbers,
15
38
  toQueryString
16
39
  };
@@ -0,0 +1 @@
1
+ export { statusText, statusTexts } from './status-text.cjs';
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Reason phrase for an HTTP status code.
3
+ * @param {number} code - HTTP status code.
4
+ * @returns {string|undefined} The reason phrase, or undefined if unknown.
5
+ */
6
+ declare function statusText(code: number): string | undefined;
7
+ declare const statusTexts: {
8
+ 100: string;
9
+ 101: string;
10
+ 102: string;
11
+ 103: string;
12
+ 200: string;
13
+ 201: string;
14
+ 202: string;
15
+ 203: string;
16
+ 204: string;
17
+ 205: string;
18
+ 206: string;
19
+ 207: string;
20
+ 208: string;
21
+ 226: string;
22
+ 300: string;
23
+ 301: string;
24
+ 302: string;
25
+ 303: string;
26
+ 304: string;
27
+ 305: string;
28
+ 307: string;
29
+ 308: string;
30
+ 400: string;
31
+ 401: string;
32
+ 402: string;
33
+ 403: string;
34
+ 404: string;
35
+ 405: string;
36
+ 406: string;
37
+ 407: string;
38
+ 408: string;
39
+ 409: string;
40
+ 410: string;
41
+ 411: string;
42
+ 412: string;
43
+ 413: string;
44
+ 414: string;
45
+ 415: string;
46
+ 416: string;
47
+ 417: string;
48
+ 418: string;
49
+ 421: string;
50
+ 422: string;
51
+ 423: string;
52
+ 424: string;
53
+ 425: string;
54
+ 426: string;
55
+ 428: string;
56
+ 429: string;
57
+ 431: string;
58
+ 451: string;
59
+ 500: string;
60
+ 501: string;
61
+ 502: string;
62
+ 503: string;
63
+ 504: string;
64
+ 505: string;
65
+ 506: string;
66
+ 507: string;
67
+ 508: string;
68
+ 509: string;
69
+ 510: string;
70
+ 511: string;
71
+ };
72
+
73
+ export { statusText, statusTexts };
@@ -17,6 +17,7 @@ export { compose, composeAsync, curry, pipe, pipeAsync, times } from './function
17
17
  export { throttle } from './functions/throttle.cjs';
18
18
  export { delay, timeout, wait } from './functions/timeout.cjs';
19
19
  export { isHashedValue } from './hash.cjs';
20
+ export { statusText, statusTexts } from './http/status-text.cjs';
20
21
  export { splitUnit } from './numbers/index.cjs';
21
22
  export { camelCaseKeys } from './objects/camelcase-keys.cjs';
22
23
  export { isEmpty, notEmpty } from './objects/empty.cjs';
@@ -36,8 +37,8 @@ export { createSSE, parseSSE } from './sse.cjs';
36
37
  export { toCamel, toKebab, toLower, toPascal, toSentence, toSlug, toTitle, toUpper } from './strings/convert-case/convert-case.cjs';
37
38
  export { markdown, treatMarkdownWhitespace } from './strings/markdown.cjs';
38
39
  export { parseFullName } from './strings/name.cjs';
39
- export { pluralize } from './strings/pluralize.cjs';
40
- export { plural, stripNumbers, toQueryString } from './strings/query-string.cjs';
40
+ export { createPluralize, isPlural, isSingular, plural, pluralize, singular } from './strings/pluralize.cjs';
41
+ export { parseQueryString, stripNumbers, toQueryString } from './strings/query-string.cjs';
41
42
  export { toStyleString } from './style/index.cjs';
42
43
  export { getSymbol, getSymbolValue } from './symbols/symbols.cjs';
43
44
  export { toDegrees, toRadians } from './numbers/math.cjs';
@@ -1,5 +1,5 @@
1
1
  export { toCamel, toKebab, toLower, toPascal, toSentence, toSlug, toTitle, toUpper } from './convert-case/convert-case.cjs';
2
2
  export { markdown, treatMarkdownWhitespace } from './markdown.cjs';
3
3
  export { parseFullName } from './name.cjs';
4
- export { pluralize } from './pluralize.cjs';
5
- export { plural, stripNumbers, toQueryString } from './query-string.cjs';
4
+ export { createPluralize, isPlural, isSingular, plural, pluralize, singular } from './pluralize.cjs';
5
+ export { parseQueryString, stripNumbers, toQueryString } from './query-string.cjs';
@@ -1,3 +1,25 @@
1
- declare const pluralize: any;
1
+ /**
2
+ * Create a pluralizer with its own rule tables. Config rules are appended to
3
+ * the English base and take precedence (newest matching rule wins).
4
+ *
5
+ * @param {Object} [config]
6
+ * @param {Array} [config.plural] - `[rule, replacement]` pairs.
7
+ * @param {Array} [config.singular] - `[rule, replacement]` pairs.
8
+ * @param {Array} [config.irregular] - `[single, plural]` pairs.
9
+ * @param {Array} [config.uncountable] - words or regexes with no plural.
10
+ * @return {Function} A callable `pluralize` with `.plural`, `.singular`,
11
+ * `.isPlural`, `.isSingular` attached.
12
+ */
13
+ declare function createPluralize(config?: {
14
+ plural?: any[];
15
+ singular?: any[];
16
+ irregular?: any[];
17
+ uncountable?: any[];
18
+ }): Function;
19
+ declare const pluralize: Function;
20
+ declare const plural: any;
21
+ declare const singular: any;
22
+ declare const isPlural: any;
23
+ declare const isSingular: any;
2
24
 
3
- export { pluralize };
25
+ export { createPluralize, isPlural, isSingular, plural, pluralize, singular };
@@ -4,7 +4,15 @@
4
4
  * @returns
5
5
  */
6
6
  declare function toQueryString(object: any): string;
7
- declare function plural(count: any, noun: any, suffix?: string): string;
7
+ /**
8
+ * Parses a query string into an object, expanding bracket-notation keys into
9
+ * nested objects and arrays (e.g. `order[charges][0][reference]=23`). Decodes
10
+ * percent-encoding and treats `+` as a space. Mirrors the subset of `qs.parse`
11
+ * needed for form-urlencoded webhook payloads.
12
+ * @param {string} [string]
13
+ * @returns {Object}
14
+ */
15
+ declare function parseQueryString(string?: string): any;
8
16
  declare function stripNumbers(string: any): any;
9
17
 
10
- export { plural, stripNumbers, toQueryString };
18
+ export { parseQueryString, stripNumbers, toQueryString };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splendidlabz/utils",
3
- "version": "1.11.6",
3
+ "version": "1.12.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "homepage": "https://splendidlabz.com/docs/utils",
@@ -64,12 +64,10 @@
64
64
  "marked": "^18.0.2",
65
65
  "marked-gfm-heading-id": "^4.1.4",
66
66
  "marked-mangle": "^1.1.13",
67
- "pluralize": "^8.0.0",
68
- "sanitize-html": "^2.17.3",
69
- "statuses": "^2.0.2"
67
+ "sanitize-html": "^2.17.3"
70
68
  },
71
69
  "devDependencies": {
72
- "@splendidlabz/eslint-config": "2.1.4",
70
+ "@splendidlabz/eslint-config": "2.1.5",
73
71
  "jsdom": "^29.0.2",
74
72
  "np": "^11.1.0",
75
73
  "tsup": "^8.5.1",