@kikiutils/shared 9.0.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/dist/consola.cjs +35 -0
  4. package/dist/consola.cjs.map +1 -0
  5. package/dist/consola.d.ts +24 -0
  6. package/dist/consola.d.ts.map +1 -0
  7. package/dist/consola.mjs +32 -0
  8. package/dist/consola.mjs.map +1 -0
  9. package/dist/crypto-hash.cjs +60 -0
  10. package/dist/crypto-hash.cjs.map +1 -0
  11. package/dist/crypto-hash.d.ts +26 -0
  12. package/dist/crypto-hash.d.ts.map +1 -0
  13. package/dist/crypto-hash.mjs +49 -0
  14. package/dist/crypto-hash.mjs.map +1 -0
  15. package/dist/datetime.cjs +131 -0
  16. package/dist/datetime.cjs.map +1 -0
  17. package/dist/datetime.d.ts +79 -0
  18. package/dist/datetime.d.ts.map +1 -0
  19. package/dist/datetime.mjs +127 -0
  20. package/dist/datetime.mjs.map +1 -0
  21. package/dist/enum.cjs +65 -0
  22. package/dist/enum.cjs.map +1 -0
  23. package/dist/enum.d.ts +43 -0
  24. package/dist/enum.d.ts.map +1 -0
  25. package/dist/enum.mjs +62 -0
  26. package/dist/enum.mjs.map +1 -0
  27. package/dist/env.cjs +54 -0
  28. package/dist/env.cjs.map +1 -0
  29. package/dist/env.d.ts +40 -0
  30. package/dist/env.d.ts.map +1 -0
  31. package/dist/env.mjs +51 -0
  32. package/dist/env.mjs.map +1 -0
  33. package/dist/general.cjs +8 -0
  34. package/dist/general.cjs.map +1 -0
  35. package/dist/general.d.ts +27 -0
  36. package/dist/general.d.ts.map +1 -0
  37. package/dist/general.mjs +6 -0
  38. package/dist/general.mjs.map +1 -0
  39. package/dist/hash.cjs +27 -0
  40. package/dist/hash.cjs.map +1 -0
  41. package/dist/hash.d.ts +17 -0
  42. package/dist/hash.d.ts.map +1 -0
  43. package/dist/hash.mjs +22 -0
  44. package/dist/hash.mjs.map +1 -0
  45. package/dist/math.cjs +37 -0
  46. package/dist/math.cjs.map +1 -0
  47. package/dist/math.d.ts +43 -0
  48. package/dist/math.d.ts.map +1 -0
  49. package/dist/math.mjs +35 -0
  50. package/dist/math.mjs.map +1 -0
  51. package/dist/number.cjs +31 -0
  52. package/dist/number.cjs.map +1 -0
  53. package/dist/number.d.ts +20 -0
  54. package/dist/number.d.ts.map +1 -0
  55. package/dist/number.mjs +29 -0
  56. package/dist/number.mjs.map +1 -0
  57. package/dist/pino.cjs +42 -0
  58. package/dist/pino.cjs.map +1 -0
  59. package/dist/pino.d.ts +24 -0
  60. package/dist/pino.d.ts.map +1 -0
  61. package/dist/pino.mjs +39 -0
  62. package/dist/pino.mjs.map +1 -0
  63. package/dist/random.cjs +30 -0
  64. package/dist/random.cjs.map +1 -0
  65. package/dist/random.d.ts +21 -0
  66. package/dist/random.d.ts.map +1 -0
  67. package/dist/random.mjs +28 -0
  68. package/dist/random.mjs.map +1 -0
  69. package/dist/string.cjs +44 -0
  70. package/dist/string.cjs.map +1 -0
  71. package/dist/string.d.ts +21 -0
  72. package/dist/string.d.ts.map +1 -0
  73. package/dist/string.mjs +42 -0
  74. package/dist/string.mjs.map +1 -0
  75. package/package.json +82 -0
  76. package/src/consola.ts +27 -0
  77. package/src/crypto-hash.ts +60 -0
  78. package/src/datetime.ts +154 -0
  79. package/src/enum.ts +60 -0
  80. package/src/env.ts +49 -0
  81. package/src/general.ts +29 -0
  82. package/src/hash.ts +25 -0
  83. package/src/math.ts +56 -0
  84. package/src/number.ts +29 -0
  85. package/src/pino.ts +37 -0
  86. package/src/random.ts +31 -0
  87. package/src/string.ts +49 -0
@@ -0,0 +1,42 @@
1
+ const DIGITS = '0123456789';
2
+ const LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';
3
+ const UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
4
+ const CHARSETS = {
5
+ 'alphabetic': LOWERCASE + UPPERCASE,
6
+ 'alphanumeric': DIGITS + LOWERCASE + UPPERCASE,
7
+ 'lowercase': LOWERCASE,
8
+ 'lowercase-numeric': DIGITS + LOWERCASE,
9
+ 'numeric': DIGITS,
10
+ 'uppercase': UPPERCASE,
11
+ 'uppercase-numeric': DIGITS + UPPERCASE,
12
+ };
13
+ /**
14
+ * Generates a random string of a given length using a specified character set.
15
+ *
16
+ * @param {number} length - The length of the string to generate. Must be a positive integer.
17
+ * @param {RandomStringMode} [mode] - The character set to use.
18
+ * @returns {string} The generated random string.
19
+ *
20
+ * @throws {Error} If the length is not a positive integer or the mode is unsupported.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { randomString } from '@kikiutils/shared/string';
25
+ *
26
+ * console.log(randomString(8)); // e.g. 'aZbXwTyQ' (alphabetic)
27
+ * console.log(randomString(6, 'numeric')); // e.g. '402398'
28
+ * console.log(randomString(10, 'alphanumeric')); // e.g. 'a9Z4pQ8xY2'
29
+ * ```
30
+ */
31
+ function randomString(length, mode = 'alphabetic') {
32
+ if (!Number.isInteger(length) || length <= 0) {
33
+ throw new Error(`Invalid length: ${length}. Must be a positive integer.`);
34
+ }
35
+ const charset = CHARSETS[mode];
36
+ if (!charset)
37
+ throw new Error(`Unsupported mode: ${mode}`);
38
+ return Array.from({ length }, () => charset[Math.floor(Math.random() * charset.length)]).join('');
39
+ }
40
+
41
+ export { randomString };
42
+ //# sourceMappingURL=string.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string.mjs","sources":["../src/string.ts"],"sourcesContent":["export type RandomStringMode =\n | 'alphabetic'\n | 'alphanumeric'\n | 'lowercase'\n | 'lowercase-numeric'\n | 'numeric'\n | 'uppercase'\n | 'uppercase-numeric';\n\nconst DIGITS = '0123456789';\nconst LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';\nconst UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\nconst CHARSETS: Record<RandomStringMode, string> = {\n 'alphabetic': LOWERCASE + UPPERCASE,\n 'alphanumeric': DIGITS + LOWERCASE + UPPERCASE,\n 'lowercase': LOWERCASE,\n 'lowercase-numeric': DIGITS + LOWERCASE,\n 'numeric': DIGITS,\n 'uppercase': UPPERCASE,\n 'uppercase-numeric': DIGITS + UPPERCASE,\n};\n\n/**\n * Generates a random string of a given length using a specified character set.\n *\n * @param {number} length - The length of the string to generate. Must be a positive integer.\n * @param {RandomStringMode} [mode] - The character set to use.\n * @returns {string} The generated random string.\n *\n * @throws {Error} If the length is not a positive integer or the mode is unsupported.\n *\n * @example\n * ```typescript\n * import { randomString } from '@kikiutils/shared/string';\n *\n * console.log(randomString(8)); // e.g. 'aZbXwTyQ' (alphabetic)\n * console.log(randomString(6, 'numeric')); // e.g. '402398'\n * console.log(randomString(10, 'alphanumeric')); // e.g. 'a9Z4pQ8xY2'\n * ```\n */\nexport function randomString(length: number, mode: RandomStringMode = 'alphabetic') {\n if (!Number.isInteger(length) || length <= 0) {\n throw new Error(`Invalid length: ${length}. Must be a positive integer.`);\n }\n\n const charset = CHARSETS[mode];\n if (!charset) throw new Error(`Unsupported mode: ${mode}`);\n return Array.from({ length }, () => charset[Math.floor(Math.random() * charset.length)]).join('');\n}\n"],"names":[],"mappings":"AASA,MAAM,MAAM,GAAG,YAAY;AAC3B,MAAM,SAAS,GAAG,4BAA4B;AAC9C,MAAM,SAAS,GAAG,4BAA4B;AAC9C,MAAM,QAAQ,GAAqC;IAC/C,YAAY,EAAE,SAAS,GAAG,SAAS;AACnC,IAAA,cAAc,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;AAC9C,IAAA,WAAW,EAAE,SAAS;IACtB,mBAAmB,EAAE,MAAM,GAAG,SAAS;AACvC,IAAA,SAAS,EAAE,MAAM;AACjB,IAAA,WAAW,EAAE,SAAS;IACtB,mBAAmB,EAAE,MAAM,GAAG,SAAS;CAC1C;AAED;;;;;;;;;;;;;;;;;AAiBG;SACa,YAAY,CAAC,MAAc,EAAE,OAAyB,YAAY,EAAA;AAC9E,IAAA,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE;AAC1C,QAAA,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,CAAA,6BAAA,CAA+B,CAAC;;AAG7E,IAAA,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC;AAC9B,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAA,CAAE,CAAC;AAC1D,IAAA,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;AACrG;;;;"}
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@kikiutils/shared",
3
+ "version": "9.0.0",
4
+ "description": "A lightweight modular utility library for JavaScript and TypeScript, offering secure hashing, flexible logging, date utilities, Vue/web helpers, and more.",
5
+ "author": "kiki-kanri",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/kikiutils/node-shared.git"
10
+ },
11
+ "keywords": [
12
+ "browser",
13
+ "consola",
14
+ "crypto",
15
+ "datetime",
16
+ "enum",
17
+ "env",
18
+ "hashing",
19
+ "javascript",
20
+ "logging",
21
+ "math",
22
+ "node",
23
+ "pino",
24
+ "string",
25
+ "typescript",
26
+ "url",
27
+ "utilities",
28
+ "utils",
29
+ "vue",
30
+ "web"
31
+ ],
32
+ "sideEffects": false,
33
+ "exports": {
34
+ "./*": {
35
+ "types": "./dist/*.d.ts",
36
+ "import": "./dist/*.mjs",
37
+ "require": "./dist/*.cjs"
38
+ }
39
+ },
40
+ "files": [
41
+ "./dist",
42
+ "./src"
43
+ ],
44
+ "engines": {
45
+ "node": ">=18.12.1"
46
+ },
47
+ "scripts": {
48
+ "build": "ts-project-builder './src/**/*.ts' --clean --sourcemaps",
49
+ "bumplog": "changelogen --bump --hideAuthorEmail",
50
+ "lint": "eslint",
51
+ "lint:fix": "eslint --fix",
52
+ "prepack": "pnpm run build",
53
+ "release": "pnpm run lint && pnpm run test && pnpm run build && changelogen --hideAuthorEmail --push --release && npm publish",
54
+ "test": "tsc -p ./tsconfig.test-check.json && cross-env TZ=UTC jest --coverage",
55
+ "typecheck": "tsc --noEmit"
56
+ },
57
+ "devDependencies": {
58
+ "@kikiutils/changelogen": "^0.8.0",
59
+ "@kikiutils/eslint-config": "^1.0.1",
60
+ "@kikiutils/tsconfigs": "^5.0.1",
61
+ "@noble/hashes": "^1.8.0",
62
+ "@types/jest": "^29.5.14",
63
+ "@types/node": "^22.15.3",
64
+ "consola": "^3.4.2",
65
+ "cross-env": "^7.0.3",
66
+ "date-fns": "^4.1.0",
67
+ "decimal.js": "^10.5.0",
68
+ "jest": "^29.7.0",
69
+ "millify": "^6.1.0",
70
+ "pino": "^9.6.0",
71
+ "pino-pretty": "^13.0.0",
72
+ "ts-jest": "^29.3.2",
73
+ "ts-project-builder": "5.0.1",
74
+ "typescript": "^5.8.3"
75
+ },
76
+ "pnpm": {
77
+ "onlyBuiltDependencies": [
78
+ "esbuild",
79
+ "unrs-resolver"
80
+ ]
81
+ }
82
+ }
package/src/consola.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { createConsola } from 'consola';
2
+
3
+ /**
4
+ * A consola logger instance.
5
+ *
6
+ * The logger's level is determined based on the `CONSOLA_LOGGER_LEVEL` and `NODE_ENV` environment variables.
7
+ * If `CONSOLA_LOGGER_LEVEL` is set, it will be used; otherwise, if `NODE_ENV` is `production`,
8
+ * the level will be set to `0`.
9
+ *
10
+ * To manually change the level, assign the desired level to `logger.level`.
11
+ *
12
+ * See available levels [here](https://github.com/unjs/consola?tab=readme-ov-file#log-level).
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import logger from '@kikiutils/shared/consola';
17
+ *
18
+ * logger.info('test'); // ℹ test 3:56:30 AM
19
+ *
20
+ * // Manually change the level
21
+ * logger.level = 3;
22
+ * ```
23
+ */
24
+ export const consolaLogger = createConsola();
25
+ export const logger = consolaLogger;
26
+ if (process.env.CONSOLA_LOGGER_LEVEL !== undefined) consolaLogger.level = +process.env.CONSOLA_LOGGER_LEVEL;
27
+ else consolaLogger.level = process.env.NODE_ENV === 'production' ? 0 : consolaLogger.level;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * This file provides a set of functions for creating hash digests using different algorithms and bit lengths.
3
+ * It includes functions for generating SHA-3 hash digests with bit lengths of 224, 256, 384, and 512,
4
+ * as well as a function for generating MD5 hash digests.
5
+ * These functions use the Node.js crypto module to generate the hashes.
6
+ * Can only be used in Node.js/Deno/Bun runtimes.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { cryptoSha3256 } from '@kikiutils/shared/crypto-hash';
11
+ *
12
+ * console.log(cryptoSha3256('test')); // 36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80
13
+ * ```
14
+ */
15
+
16
+ import { createHash } from 'node:crypto';
17
+ import type {
18
+ BinaryLike,
19
+ BinaryToTextEncoding,
20
+ } from 'node:crypto';
21
+
22
+ export function cryptoMd5(data: BinaryLike, outputEncoding: BinaryToTextEncoding = 'hex') {
23
+ return createHash('md5').update(data).digest(outputEncoding);
24
+ }
25
+
26
+ export function cryptoMd5ToBuffer(data: BinaryLike) {
27
+ return createHash('md5').update(data).digest();
28
+ }
29
+
30
+ export function cryptoSha3224(data: BinaryLike, outputEncoding: BinaryToTextEncoding = 'hex') {
31
+ return createHash('sha3-224').update(data).digest(outputEncoding);
32
+ }
33
+
34
+ export function cryptoSha3224ToBuffer(data: BinaryLike) {
35
+ return createHash('sha3-224').update(data).digest();
36
+ }
37
+
38
+ export function cryptoSha3256(data: BinaryLike, outputEncoding: BinaryToTextEncoding = 'hex') {
39
+ return createHash('sha3-256').update(data).digest(outputEncoding);
40
+ }
41
+
42
+ export function cryptoSha3256ToBuffer(data: BinaryLike) {
43
+ return createHash('sha3-256').update(data).digest();
44
+ }
45
+
46
+ export function cryptoSha3384(data: BinaryLike, outputEncoding: BinaryToTextEncoding = 'hex') {
47
+ return createHash('sha3-384').update(data).digest(outputEncoding);
48
+ }
49
+
50
+ export function cryptoSha3384ToBuffer(data: BinaryLike) {
51
+ return createHash('sha3-384').update(data).digest();
52
+ }
53
+
54
+ export function cryptoSha3512(data: BinaryLike, outputEncoding: BinaryToTextEncoding = 'hex') {
55
+ return createHash('sha3-512').update(data).digest(outputEncoding);
56
+ }
57
+
58
+ export function cryptoSha3512ToBuffer(data: BinaryLike) {
59
+ return createHash('sha3-512').update(data).digest();
60
+ }
@@ -0,0 +1,154 @@
1
+ import {
2
+ format as dateFnsFormat,
3
+ endOfDay,
4
+ endOfMonth,
5
+ endOfWeek,
6
+ startOfDay,
7
+ startOfMonth,
8
+ startOfWeek,
9
+ subDays,
10
+ subMonths,
11
+ subWeeks,
12
+ } from 'date-fns';
13
+ import type {
14
+ DateArg,
15
+ Day,
16
+ FormatOptions,
17
+ } from 'date-fns';
18
+
19
+ export type DateRangeType = 'lastMonth' | 'lastWeek' | 'thisMonth' | 'thisWeek' | 'today' | 'yesterday';
20
+
21
+ /**
22
+ * Formats a given date, timestamp, or date string into a specified format.
23
+ *
24
+ * This function is a wrapper around `date-fns/format`.
25
+ *
26
+ * @param {DateArg<Date>} date - The input date to format. Can be a Date object, a timestamp, or a string.
27
+ * @param {string} [format] - The target format string.
28
+ * @param {FormatOptions} [options] - Optional formatting options passed to `date-fns/format`.
29
+ * @returns {string} The formatted date string.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { formatDate } from '@kikiutils/shared/datetime';
34
+ *
35
+ * // Format a Date object
36
+ * console.log(formatDate(new Date(), 'yyyy-MM-dd')); // 2024-07-10
37
+ *
38
+ * // Format a timestamp
39
+ * console.log(formatDate(1657814400000, 'yyyy-MM-dd')); // 2022-07-15
40
+ *
41
+ * // Format a date string
42
+ * console.log(formatDate('2024-07-10T00:00:00Z', 'yyyy-MM-dd')); // 2024-07-10
43
+ * ```
44
+ *
45
+ * @see https://date-fns.org/docs/format
46
+ */
47
+ export function formatDate(date: DateArg<Date> & {}, format: string = 'yyyy-MM-dd HH:mm:ss', options?: FormatOptions) {
48
+ return dateFnsFormat(date, format, options);
49
+ }
50
+
51
+ /**
52
+ * Get the date range (start and end) based on a given date and range type.
53
+ *
54
+ * Supports common range types like 'lastMonth', 'lastWeek', 'thisMonth', 'thisWeek', 'today', and 'yesterday'.
55
+ *
56
+ * @param {Date} date - The reference date.
57
+ * @param {DateRangeType} type - The range type to compute.
58
+ * @param {object} [options] - Optional settings.
59
+ * @param {boolean} [options.setEndDateToNextDayStart] - If true, set `endDate` to 00:00:00.000 of the next day.
60
+ * @param {Day} [options.weekStartsOn] - The start day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday).
61
+ * @returns {{ startDate: Date, endDate: Date }} An object with `startDate` and `endDate`.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * import { getDateRangeFromDate } from '@kikiutils/shared/datetime';
66
+ *
67
+ * // Get the date range for last month
68
+ * const date = new Date('2023-07-01');
69
+ * console.log(getDateRangeFromDate(date, 'lastMonth'));
70
+ * // { startDate: 2023-06-01T00:00:00.000Z, endDate: 2023-06-30T23:59:59.999Z }
71
+ *
72
+ * // Get this week's range with Sunday as the first day
73
+ * console.log(getDateRangeFromDate(date, 'thisWeek', { weekStartsOn: 0 }));
74
+ * // { startDate: 2023-06-25T00:00:00.000Z, endDate: 2023-07-01T23:59:59.999Z }
75
+ * ```
76
+ */
77
+ export function getDateRangeFromDate(
78
+ date: Date,
79
+ type: DateRangeType,
80
+ options?: {
81
+ setEndDateToNextDayStart?: boolean;
82
+ weekStartsOn?: Day;
83
+ },
84
+ ) {
85
+ let endDate: Date;
86
+ let startDate: Date;
87
+ switch (type) {
88
+ case 'lastMonth':
89
+ {
90
+ const lastMonth = subMonths(date, 1);
91
+ endDate = endOfMonth(lastMonth);
92
+ startDate = startOfMonth(lastMonth);
93
+ }
94
+
95
+ break;
96
+ case 'lastWeek':
97
+ {
98
+ const lastWeek = subWeeks(date, 1);
99
+ endDate = endOfWeek(lastWeek, { weekStartsOn: options?.weekStartsOn ?? 1 });
100
+ startDate = startOfWeek(lastWeek, { weekStartsOn: options?.weekStartsOn ?? 1 });
101
+ }
102
+
103
+ break;
104
+ case 'thisMonth':
105
+ endDate = endOfMonth(date);
106
+ startDate = startOfMonth(date);
107
+ break;
108
+ case 'thisWeek':
109
+ endDate = endOfWeek(date, { weekStartsOn: options?.weekStartsOn ?? 1 });
110
+ startDate = startOfWeek(date, { weekStartsOn: options?.weekStartsOn ?? 1 });
111
+ break;
112
+ case 'today':
113
+ endDate = endOfDay(date);
114
+ startDate = startOfDay(date);
115
+ break;
116
+ case 'yesterday':
117
+ {
118
+ const yesterday = subDays(date, 1);
119
+ endDate = endOfDay(yesterday);
120
+ startDate = startOfDay(yesterday);
121
+ }
122
+
123
+ break;
124
+ default: throw new Error(`Unsupported date range type: ${type}.`);
125
+ }
126
+
127
+ if (options?.setEndDateToNextDayStart) endDate.setHours(24, 0, 0, 0);
128
+ return {
129
+ endDate,
130
+ startDate,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Returns a `Date` object set to midnight (00:00:00) of today, with an optional day offset.
136
+ *
137
+ * @param {number} [offsetDays] - Number of days to offset from today. Can be negative.
138
+ * @returns {Date} A `Date` object at 00:00:00 of the offset day.
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * import { getMidnightDateFromToday } from '@kikiutils/shared/datetime';
143
+ *
144
+ * console.log(getMidnightDateFromToday()); // today at 00:00:00
145
+ * console.log(getMidnightDateFromToday(3)); // 3 days from today at 00:00:00
146
+ * console.log(getMidnightDateFromToday(-1)); // yesterday at 00:00:00
147
+ * ```
148
+ */
149
+ export function getMidnightDateFromToday(offsetDays: number = 0) {
150
+ const date = new Date();
151
+ date.setDate(date.getDate() + offsetDays);
152
+ date.setHours(0, 0, 0, 0);
153
+ return date;
154
+ }
package/src/enum.ts ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Extracts the numeric values from an enumeration-like object.
3
+ *
4
+ * @param {Record<number | string, number | string>} data - The enumeration-like object to extract numeric values from.
5
+ * The keys can be numbers or strings, and the values can be numbers or strings.
6
+ * @returns {number[]} An array of numeric values extracted from the object.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { getEnumNumberValues } from '@kikiutils/shared/enum';
11
+ *
12
+ * enum RecordType {
13
+ * Receive = 0,
14
+ * Send = 1,
15
+ * Unknown = 'unknown'
16
+ * }
17
+ *
18
+ * console.log(getEnumNumberValues(RecordType)); // [0, 1]
19
+ * ```
20
+ */
21
+ export function getEnumNumberValues(data: Record<number | string, number | string>) {
22
+ return Object.values(data).filter((value) => typeof value === 'number');
23
+ }
24
+
25
+ /**
26
+ * Extracts the string values from an enumeration-like object.
27
+ *
28
+ * @param {Record<number | string, number | string>} data - The enumeration-like object to extract string values from.
29
+ * The keys can be numbers or strings, and the values can be numbers or strings.
30
+ * @returns {string[]} An array of string values extracted from the object.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { getEnumStringValues } from '@kikiutils/shared/enum';
35
+ *
36
+ * enum RecordType {
37
+ * Receive = 0,
38
+ * Send = 1,
39
+ * Unknown = 'unknown'
40
+ * }
41
+ *
42
+ * console.log(getEnumStringValues(RecordType)); // ['unknown']
43
+ * ```
44
+ */
45
+ export function getEnumStringValues(data: Record<number | string, number | string>) {
46
+ const keys: string[] = [];
47
+ const keysCount: Record<string, number> = {};
48
+ const values: any[] = [];
49
+ Object.entries(data).forEach(([key, value]) => {
50
+ keys.push(key);
51
+ values.push(value);
52
+ if (typeof value !== 'string') return;
53
+ keysCount[key] = (keysCount[key] ?? 0) + 1;
54
+ keysCount[value] = (keysCount[value] ?? 0) + 1;
55
+ });
56
+
57
+ return values.filter((value) => {
58
+ return typeof value === 'string' && (!keys.includes(value) || (keysCount[value] && keysCount[value] > 1));
59
+ });
60
+ }
package/src/env.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Custom error class for handling missing environment variables.
3
+ *
4
+ * Extends the built-in `Error` class and includes the missing key.
5
+ *
6
+ * @extends {Error}
7
+ */
8
+ export class EnvironmentNotFoundError extends Error {
9
+ readonly key: string;
10
+
11
+ /**
12
+ * Creates a new EnvironmentNotFoundError.
13
+ *
14
+ * @param {string} key - The missing environment variable key.
15
+ */
16
+ constructor(key: string) {
17
+ super(`Missing environment variable: ${key}`);
18
+ this.key = key;
19
+ this.name = this.constructor.name;
20
+ Error.captureStackTrace?.(this, this.constructor);
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Retrieves the value of an environment variable, or throws an error if not set.
26
+ *
27
+ * @param {string} key - The environment variable key to check.
28
+ * @returns {string} The value of the environment variable.
29
+ * @throws {EnvironmentNotFoundError} If the environment variable is not defined.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { checkAndGetEnvValue } from '@kikiutils/shared/env';
34
+ *
35
+ * // When the environment variable 'API_KEY' is set:
36
+ * console.log(checkAndGetEnvValue('API_KEY')); // value of API_KEY
37
+ *
38
+ * // When the environment variable 'API_KEY' is not set:
39
+ * try {
40
+ * const apiKey = checkAndGetEnvValue('API_KEY');
41
+ * } catch (error) {
42
+ * console.error(error); // Missing environment variable: API_KEY
43
+ * }
44
+ * ```
45
+ */
46
+ export function checkAndGetEnvValue(key: string): string {
47
+ if (!process.env[key]) throw new EnvironmentNotFoundError(key);
48
+ return process.env[key];
49
+ }
package/src/general.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Extracts the first value from an array or returns the value itself if it's not an array.
3
+ *
4
+ * - If `value` is an array, returns the first element.
5
+ * - If `value` is not an array, returns `value` directly.
6
+ * - If the result is `null` or `undefined`, and `defaultValue` is provided, returns `defaultValue` instead.
7
+ *
8
+ * @template T - The type of the input value(s).
9
+ * @template D - The type of the default value (if provided).
10
+ *
11
+ * @param {T | T[]} value - A single value or an array of values.
12
+ * @param {D} [defaultValue] - A fallback value if the result is `null` or `undefined`.
13
+ * @returns {T | D | undefined} The first value or the fallback.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { extractFirstValue } from '@kikiutils/shared/general';
18
+ *
19
+ * console.log(extractFirstValue([1, 2, 3])); // 1
20
+ * console.log(extractFirstValue('hello')); // hello
21
+ * console.log(extractFirstValue([], 'default')); // default
22
+ * console.log(extractFirstValue(undefined, 'fallback')); // fallback
23
+ * ```
24
+ */
25
+ export function extractFirstValue<T>(value: T | T[]): T | undefined;
26
+ export function extractFirstValue<T, D>(value: T | T[], defaultValue: D): D | NonNullable<T>;
27
+ export function extractFirstValue<T, D>(value: T | T[], defaultValue?: D) {
28
+ return (Array.isArray(value) ? value[0] : value) ?? defaultValue;
29
+ }
package/src/hash.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * This file provides a set of functions for creating SHA-3 hash digests using different bit lengths (224, 256, 384, 512).
3
+ * These functions use the [@noble/hashes](https://github.com/paulmillr/noble-hashes) library to generate the hashes.
4
+ * Can be used in the browser, mainly for Nuxt/Vue and other frameworks compiled and executed in the browser.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { sha3256 } from '@kikiutils/shared/hash';
9
+ *
10
+ * console.log(sha3256('test')); // 36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80
11
+ * ```
12
+ */
13
+
14
+ import {
15
+ sha3_224,
16
+ sha3_256,
17
+ sha3_384,
18
+ sha3_512,
19
+ } from '@noble/hashes/sha3';
20
+ import { bytesToHex } from '@noble/hashes/utils';
21
+
22
+ export const sha3224 = (data: string | Uint8Array) => bytesToHex(sha3_224(data));
23
+ export const sha3256 = (data: string | Uint8Array) => bytesToHex(sha3_256(data));
24
+ export const sha3384 = (data: string | Uint8Array) => bytesToHex(sha3_384(data));
25
+ export const sha3512 = (data: string | Uint8Array) => bytesToHex(sha3_512(data));
package/src/math.ts ADDED
@@ -0,0 +1,56 @@
1
+ import { Decimal } from 'decimal.js';
2
+
3
+ type CalculableValue = Decimal.Value | { toString: () => string };
4
+
5
+ /**
6
+ * Options for configuring the output of `toPercentageString`.
7
+ */
8
+ export interface ToPercentageStringOptions {
9
+ /**
10
+ * Number of decimal places to include in the result.
11
+ * @default 2
12
+ */
13
+ decimalPlaces?: number;
14
+
15
+ /**
16
+ * Whether to include the '%' symbol in the result.
17
+ * @default true
18
+ */
19
+ withSymbol?: boolean;
20
+ }
21
+
22
+ /**
23
+ * Converts a fraction (numerator / denominator) into a percentage string.
24
+ *
25
+ * - Uses `decimal.js` for precise decimal calculations.
26
+ * - Supports custom decimal places and optional percentage symbol.
27
+ * - Returns `'0.00%'` if result is `NaN` or division is invalid.
28
+ *
29
+ * @param {CalculableValue} molecular - The numerator of the fraction.
30
+ * @param {CalculableValue} denominator - The denominator of the fraction.
31
+ * @param {ToPercentageStringOptions} [options] - Optional output settings.
32
+ * @returns {string} Formatted percentage string.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * import { toPercentageString } from '@kikiutils/shared/math';
37
+ *
38
+ * console.log(toPercentageString(50, 200)); // 25.00%
39
+ * console.log(toPercentageString(50, 200, { withSymbol: false })); // 25.00
40
+ * console.log(toPercentageString(50, 200, { decimalPlaces: 1 })); // 25.0%
41
+ * ```
42
+ */
43
+ export function toPercentageString(
44
+ molecular: CalculableValue,
45
+ denominator: CalculableValue,
46
+ options?: ToPercentageStringOptions,
47
+ ) {
48
+ const molecularDecimal = new Decimal(molecular.toString());
49
+ const denominatorDecimal = new Decimal(denominator.toString());
50
+ const calculationResult = molecularDecimal.div(denominatorDecimal);
51
+ const result = calculationResult.isNaN()
52
+ ? '0.00'
53
+ : calculationResult.times(100).toFixed(options?.decimalPlaces ?? 2);
54
+
55
+ return options?.withSymbol ?? true ? `${result}%` : result;
56
+ }
package/src/number.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { millify } from 'millify';
2
+
3
+ /**
4
+ * Converts a large number into a compact, human-readable string using `millify`.
5
+ *
6
+ * Applies lowercase units (e.g. 'k', 'm') and default precision of 2, unless overridden.
7
+ *
8
+ * @param {number} value - The number to format.
9
+ * @param {Parameters<typeof millify>[1]} [options] - Optional configuration passed to `millify`.
10
+ * @returns {string} The compact number string.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { toCompactNumberString } from '@kikiutils/shared/number';
15
+ *
16
+ * console.log(toCompactNumberString(1234567)); // 1.23m
17
+ * console.log(toCompactNumberString(1234567, { precision: 3 })); // 1.235m
18
+ * ```
19
+ */
20
+ export function toCompactNumberString(value: number, options?: Parameters<typeof millify>[1]) {
21
+ return millify(
22
+ value,
23
+ {
24
+ lowercase: true,
25
+ precision: 2,
26
+ ...options,
27
+ },
28
+ );
29
+ }
package/src/pino.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { pino } from 'pino';
2
+ import { PinoPretty } from 'pino-pretty';
3
+
4
+ /**
5
+ * Configure pinoPretty to enhance the log output.
6
+ */
7
+ const stream = PinoPretty({
8
+ colorize: true, // Enable colored output for better readability
9
+ ignore: 'hostname,pid', // Exclude 'hostname' and 'pid' fields from the logs
10
+ translateTime: 'SYS:yyyy-mm-dd HH:MM:ss.l', // Format the timestamp in 'yyyy-mm-dd HH:MM:ss.l' format
11
+ });
12
+
13
+ /**
14
+ * A pino logger instance with the configured stream.
15
+ *
16
+ * The logger's level is determined based on the `PINO_LOGGER_LEVEL` and `NODE_ENV` environment variables.
17
+ * If `PINO_LOGGER_LEVEL` is set, it will be used; otherwise, if `NODE_ENV` is `production`,
18
+ * the level will be set to `error`.
19
+ *
20
+ * To manually change the level, assign the desired level to `logger.level`.
21
+ *
22
+ * See available levels [here](https://getpino.io/#/docs/api?id=level-string).
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import logger from '@kikiutils/shared/pino';
27
+ *
28
+ * logger.info('test'); // [2024-07-11 12:12:30.085] INFO: test
29
+ *
30
+ * // Manually change the level
31
+ * logger.level = 'info';
32
+ * ```
33
+ */
34
+ export const pinoLogger = pino({}, stream);
35
+ export const logger = pinoLogger;
36
+ // eslint-disable-next-line style/max-len
37
+ pinoLogger.level = process.env.PINO_LOGGER_LEVEL || (process.env.NODE_ENV === 'production' ? 'error' : pinoLogger.level);