@koloseum/utils 0.2.28 → 0.3.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.
@@ -0,0 +1,125 @@
1
+ import type { IdentityType, PronounsItem, Sex, SocialMediaPlatform, SocialMediaPlatformObject } from "@koloseum/types/public-auth";
2
+ import type { MobilePhoneLocale } from "validator";
3
+ export declare const Transform: {
4
+ /**
5
+ * Capitalises a given word.
6
+ * @param {string} word - The word to be capitalised
7
+ * @returns The capitalised word
8
+ */
9
+ capitalise: (word: string) => string;
10
+ /**
11
+ * Cleans a URL by removing the protocol and trailing slashes.
12
+ * @param {string} url - The URL to clean
13
+ * @returns {string} The cleaned URL
14
+ */
15
+ cleanUrl: (url: string) => string;
16
+ /**
17
+ * Formats a date in the format DD/MM/YYYY (by default) or YYYY-MM-DD (for client).
18
+ * @param {string} date - Date to be formatted
19
+ * @param {boolean} forClient - Specify whether the data is for displaying on the client; defaults to `false`
20
+ * @returns The formatted date string, or `null` if invalid input or in case of an error
21
+ */
22
+ formatDate: (date: string, forClient?: boolean) => string | null;
23
+ /**
24
+ * Formats a social media platform for display.
25
+ * @param {SocialMediaPlatform} platform - The social media platform to be formatted
26
+ * @returns The formatted social media platform string
27
+ */
28
+ formatSocialMediaPlatform: (platform: SocialMediaPlatform) => string;
29
+ /**
30
+ * Returns an age in years given a birth date.
31
+ * @param {Date | string | number | null} dateOfBirth - The date of birth
32
+ * @returns The age in years, or `NaN` if the input is invalid
33
+ */
34
+ getAge: (dateOfBirth: Date | string | number | null) => number;
35
+ /**
36
+ * Formats a date as a locale-specific string.
37
+ * @param {Date | string | number | null} date - Date to be formatted
38
+ * @param {boolean} withTime - Specify whether the date should include time; defaults to `false`
39
+ * @param {string} timeZone - The time zone to use for formatting; defaults to `Africa/Nairobi`
40
+ * @returns The formatted date string, or an empty string if invalid input
41
+ */
42
+ getDateString: (date: Date | string | number | null, withTime?: boolean, timeZone?: string) => string;
43
+ /**
44
+ * Formats an identity type for display.
45
+ * @param {IdentityType} identityType - The identity type to be formatted
46
+ * @returns The formatted identity type string
47
+ */
48
+ getIdentityTypeString: (identityType: IdentityType) => string;
49
+ /**
50
+ * Formats a date to an ISO string in Kenyan time, i.e. `Africa/Nairobi` (UTC+3).
51
+ * @param {Date | string | number | null} date - The date to format
52
+ * @returns {string} The formatted date in ISO string format, or an empty string if invalid input
53
+ */
54
+ getKenyanISOString: (date: Date | string | number | null) => string;
55
+ /**
56
+ * Returns a pronoun given pronouns and a type.
57
+ * @param {PronounsItem | null} pronouns - The pronouns to be formatted
58
+ * @param {"subject" | "object" | "possessive" | "reflexive"} type - The type of pronoun to be returned
59
+ * @param {Sex} sex - The user's sex; defaults to `undefined`
60
+ * @returns The formatted pronoun, or `null` if the input is invalid
61
+ */
62
+ getPronoun: (pronouns: PronounsItem | null, type: "subject" | "object" | "possessive" | "reflexive", sex?: Sex) => string | null;
63
+ /**
64
+ * Paginates an array.
65
+ * @template T - The type of array items; defaults to `any`
66
+ * @param {T[]} array - The array to paginate
67
+ * @param {number} page - The page number
68
+ * @param {number} pageSize - The number of items per page; defaults to 10
69
+ * @returns {Object} The paginated array and total number of pages
70
+ */
71
+ paginateArray: <T = any>(array: T[], page: number, pageSize?: number) => {
72
+ paginatedItems: T[];
73
+ totalPages: number;
74
+ };
75
+ /**
76
+ * Sanitises any potential HTML injected into user input.
77
+ * @param {string} input - The input to be sanitised
78
+ * @returns A sanitised string, or an empty string if the input is invalid
79
+ */
80
+ sanitiseHtml: (input: string) => string;
81
+ };
82
+ export declare const Validate: {
83
+ /**
84
+ * A regular expression for E.164 phone numbers.
85
+ *
86
+ * - Source: https://www.twilio.com/docs/glossary/what-e164
87
+ */
88
+ e164Regex: RegExp;
89
+ /**
90
+ * The minimum birth date (from today's date) for a user who is a minor, i.e. `YYYY-MM-DD`
91
+ */
92
+ minimumMinorBirthDate: string;
93
+ /**
94
+ * A regular expression for user passwords to match during authentication. It covers the following rules:
95
+ * - at least 8 characters long
96
+ * - at least one digit
97
+ * - at least one lowercase letter
98
+ * - at least one uppercase letter
99
+ * - at least one symbol
100
+ */
101
+ passwordRegex: RegExp;
102
+ /**
103
+ * A regular expression for social media handles, without a leading slash or @ character.
104
+ *
105
+ * - Source: https://stackoverflow.com/a/74579651
106
+ */
107
+ socialMediaHandleRegex: RegExp;
108
+ /**
109
+ * An array of values representing social media platforms recognised in the database.
110
+ */
111
+ socialMediaPlatforms: SocialMediaPlatformObject[];
112
+ /**
113
+ * Validates an E.164 phone number.
114
+ * @param {string} phoneNumber - The phone number to be validated
115
+ * @param {"any" | MobilePhoneLocale | MobilePhoneLocale[]} [locale="any"] - The locale(s) to validate the phone number against
116
+ * @returns `true` if the phone number is valid; `false` otherwise
117
+ */
118
+ validateE164: (phoneNumber: string, locale?: "any" | MobilePhoneLocale | MobilePhoneLocale[]) => boolean;
119
+ /**
120
+ * Validates a social media handle.
121
+ * @param {string} handle - The social media handle to be validated
122
+ * @returns `true` if the handle is valid; `false` otherwise
123
+ */
124
+ validateSocialMediaHandle: (handle: string) => boolean;
125
+ };
@@ -0,0 +1,369 @@
1
+ import validator from "validator";
2
+ /* HELPERS */
3
+ const sanitize = (await import("sanitize-html")).default;
4
+ const { isMobilePhone, isURL } = validator;
5
+ /* DATA TRANSFORMATION HELPERS */
6
+ export const Transform = {
7
+ /**
8
+ * Capitalises a given word.
9
+ * @param {string} word - The word to be capitalised
10
+ * @returns The capitalised word
11
+ */
12
+ capitalise: (word) => word.charAt(0).toUpperCase() +
13
+ word
14
+ .slice(1)
15
+ .split("")
16
+ .map((c) => c.toLowerCase())
17
+ .join(""),
18
+ /**
19
+ * Cleans a URL by removing the protocol and trailing slashes.
20
+ * @param {string} url - The URL to clean
21
+ * @returns {string} The cleaned URL
22
+ */
23
+ cleanUrl: (url) => url.replace(/^https?:\/\//, "").replace(/\/+$/, ""),
24
+ /**
25
+ * Formats a date in the format DD/MM/YYYY (by default) or YYYY-MM-DD (for client).
26
+ * @param {string} date - Date to be formatted
27
+ * @param {boolean} forClient - Specify whether the data is for displaying on the client; defaults to `false`
28
+ * @returns The formatted date string, or `null` if invalid input or in case of an error
29
+ */
30
+ formatDate: (date, forClient = false) => {
31
+ // Return null if no date is provided
32
+ if (date === "" || typeof date !== "string")
33
+ return null;
34
+ // Handle input
35
+ try {
36
+ // Parse date string
37
+ const parsedDate = Date.parse(date);
38
+ // Return null if date string is not parseable
39
+ if (isNaN(parsedDate))
40
+ return null;
41
+ // Use the Intl.DateTimeFormat with explicit options to ensure correct formatting
42
+ const formattedDate = new Intl.DateTimeFormat(forClient ? "fr-CA" : "en-KE", {
43
+ day: "2-digit",
44
+ month: "2-digit",
45
+ year: "numeric",
46
+ timeZone: "Africa/Nairobi"
47
+ }).format(parsedDate);
48
+ // Return formatted date string
49
+ return formattedDate;
50
+ }
51
+ catch (error) {
52
+ console.error(error);
53
+ return null;
54
+ }
55
+ },
56
+ /**
57
+ * Formats a social media platform for display.
58
+ * @param {SocialMediaPlatform} platform - The social media platform to be formatted
59
+ * @returns The formatted social media platform string
60
+ */
61
+ formatSocialMediaPlatform: (platform) => {
62
+ const platforms = Validate.socialMediaPlatforms.map(({ value }) => value);
63
+ if (!platforms.includes(platform))
64
+ return "";
65
+ if (platform === "tiktok")
66
+ return "TikTok";
67
+ if (platform === "twitter")
68
+ return "X (formerly Twitter)";
69
+ if (platform === "youtube")
70
+ return "YouTube";
71
+ return Transform.capitalise(platform);
72
+ },
73
+ /**
74
+ * Returns an age in years given a birth date.
75
+ * @param {Date | string | number | null} dateOfBirth - The date of birth
76
+ * @returns The age in years, or `NaN` if the input is invalid
77
+ */
78
+ getAge: (dateOfBirth) => {
79
+ // Return NaN if no date is provided
80
+ if (!dateOfBirth)
81
+ return NaN;
82
+ // Validate and process date of birth
83
+ let birthDate;
84
+ if (dateOfBirth instanceof Date) {
85
+ if (isNaN(dateOfBirth.getTime()))
86
+ return NaN;
87
+ birthDate = dateOfBirth;
88
+ }
89
+ else if (typeof dateOfBirth === "string") {
90
+ if (dateOfBirth === "" || isNaN(Date.parse(dateOfBirth)))
91
+ return NaN;
92
+ // Format date of birth if it's a string
93
+ const formattedDate = Transform.formatDate(dateOfBirth, true);
94
+ if (!formattedDate)
95
+ return NaN;
96
+ birthDate = new Date(formattedDate);
97
+ }
98
+ else if (typeof dateOfBirth === "number") {
99
+ if (isNaN(dateOfBirth))
100
+ return NaN;
101
+ birthDate = new Date(dateOfBirth);
102
+ }
103
+ else {
104
+ return NaN;
105
+ }
106
+ // Validate the final date object
107
+ if (isNaN(birthDate.getTime()))
108
+ return NaN;
109
+ // Calculate age
110
+ const currentDate = new Date();
111
+ const monthDiff = currentDate.getMonth() - birthDate.getMonth();
112
+ let age = currentDate.getFullYear() - birthDate.getFullYear();
113
+ // Return age
114
+ return monthDiff < 0 || (monthDiff === 0 && currentDate.getDate() < birthDate.getDate()) ? --age : age;
115
+ },
116
+ /**
117
+ * Formats a date as a locale-specific string.
118
+ * @param {Date | string | number | null} date - Date to be formatted
119
+ * @param {boolean} withTime - Specify whether the date should include time; defaults to `false`
120
+ * @param {string} timeZone - The time zone to use for formatting; defaults to `Africa/Nairobi`
121
+ * @returns The formatted date string, or an empty string if invalid input
122
+ */
123
+ getDateString: (date, withTime = false, timeZone = "Africa/Nairobi") => {
124
+ // Return empty string if no date is provided
125
+ if (!date)
126
+ return "";
127
+ // Validate date to format
128
+ let dateToFormat;
129
+ if (date instanceof Date) {
130
+ if (isNaN(date.getTime()))
131
+ return "";
132
+ dateToFormat = date;
133
+ }
134
+ else if (typeof date === "string" || typeof date === "number") {
135
+ if (typeof date === "string" && (date === "" || isNaN(Date.parse(date))))
136
+ return "";
137
+ if (typeof date === "number" && isNaN(date))
138
+ return "";
139
+ dateToFormat = new Date(date);
140
+ }
141
+ else
142
+ return "";
143
+ // Validate the final date object
144
+ if (isNaN(dateToFormat.getTime()))
145
+ return "";
146
+ // Format date
147
+ return dateToFormat.toLocaleString("en-KE", {
148
+ timeZone,
149
+ year: "numeric",
150
+ month: "long",
151
+ day: "numeric",
152
+ hour: withTime ? "numeric" : undefined,
153
+ minute: withTime ? "2-digit" : undefined,
154
+ hour12: withTime ? true : undefined
155
+ });
156
+ },
157
+ /**
158
+ * Formats an identity type for display.
159
+ * @param {IdentityType} identityType - The identity type to be formatted
160
+ * @returns The formatted identity type string
161
+ */
162
+ getIdentityTypeString: (identityType) => {
163
+ if (identityType === "national")
164
+ return "National ID";
165
+ if (identityType === "alien")
166
+ return "Alien ID";
167
+ if (identityType === "passport")
168
+ return "Passport";
169
+ if (identityType === "driver_licence")
170
+ return "Driver's licence";
171
+ return "";
172
+ },
173
+ /**
174
+ * Formats a date to an ISO string in Kenyan time, i.e. `Africa/Nairobi` (UTC+3).
175
+ * @param {Date | string | number | null} date - The date to format
176
+ * @returns {string} The formatted date in ISO string format, or an empty string if invalid input
177
+ */
178
+ getKenyanISOString: (date) => {
179
+ // Return empty string if no date is provided or input is invalid
180
+ if (!date || (typeof date !== "string" && typeof date !== "number" && !(date instanceof Date)))
181
+ return "";
182
+ // Get locale string to format
183
+ const dateObj = date instanceof Date ? date : new Date(date);
184
+ const localeString = dateObj.toLocaleString("sv-SE", {
185
+ timeZone: "Africa/Nairobi"
186
+ });
187
+ // Return formatted string
188
+ return localeString.replace(" ", "T") + "+03:00";
189
+ },
190
+ /**
191
+ * Returns a pronoun given pronouns and a type.
192
+ * @param {PronounsItem | null} pronouns - The pronouns to be formatted
193
+ * @param {"subject" | "object" | "possessive" | "reflexive"} type - The type of pronoun to be returned
194
+ * @param {Sex} sex - The user's sex; defaults to `undefined`
195
+ * @returns The formatted pronoun, or `null` if the input is invalid
196
+ */
197
+ getPronoun: (pronouns, type, sex) => {
198
+ // Get pronoun from pronouns item
199
+ if (pronouns) {
200
+ // Return subject pronoun
201
+ if (type === "subject") {
202
+ if (pronouns === "he/him/his/himself")
203
+ return "he";
204
+ if (pronouns === "she/her/hers/herself")
205
+ return "she";
206
+ if (pronouns === "they/them/their/themself")
207
+ return "they";
208
+ if (pronouns === "name_only")
209
+ return "";
210
+ }
211
+ // Return object pronoun
212
+ else if (type === "object") {
213
+ if (pronouns === "he/him/his/himself")
214
+ return "him";
215
+ if (pronouns === "she/her/hers/herself")
216
+ return "her";
217
+ if (pronouns === "they/them/their/themself")
218
+ return "them";
219
+ if (pronouns === "name_only")
220
+ return "";
221
+ }
222
+ // Return possessive pronoun
223
+ else if (type === "possessive") {
224
+ if (pronouns === "he/him/his/himself")
225
+ return "his";
226
+ if (pronouns === "she/her/hers/herself")
227
+ return "her";
228
+ if (pronouns === "they/them/their/themself")
229
+ return "their";
230
+ if (pronouns === "name_only")
231
+ return "";
232
+ }
233
+ // Return reflexive pronoun
234
+ else if (type === "reflexive") {
235
+ if (pronouns === "he/him/his/himself")
236
+ return "himself";
237
+ if (pronouns === "she/her/hers/herself")
238
+ return "herself";
239
+ if (pronouns === "they/them/their/themself")
240
+ return "themself";
241
+ if (pronouns === "name_only")
242
+ return "";
243
+ }
244
+ }
245
+ // Assume pronoun for sex if no pronouns are provided
246
+ else if (sex) {
247
+ // Return subject pronoun
248
+ if (type === "subject") {
249
+ if (sex === "male" || sex === "intersex_man")
250
+ return "he";
251
+ if (sex === "female" || sex === "intersex_woman")
252
+ return "she";
253
+ return "they";
254
+ }
255
+ // Return object pronoun
256
+ else if (type === "object") {
257
+ if (sex === "male" || sex === "intersex_man")
258
+ return "him";
259
+ if (sex === "female" || sex === "intersex_woman")
260
+ return "her";
261
+ return "them";
262
+ }
263
+ // Return possessive pronoun
264
+ else if (type === "possessive") {
265
+ if (sex === "male" || sex === "intersex_man")
266
+ return "his";
267
+ if (sex === "female" || sex === "intersex_woman")
268
+ return "her";
269
+ return "their";
270
+ }
271
+ // Return reflexive pronoun
272
+ else if (type === "reflexive") {
273
+ if (sex === "male" || sex === "intersex_man")
274
+ return "himself";
275
+ if (sex === "female" || sex === "intersex_woman")
276
+ return "herself";
277
+ return "themself";
278
+ }
279
+ }
280
+ // Return null
281
+ return null;
282
+ },
283
+ /**
284
+ * Paginates an array.
285
+ * @template T - The type of array items; defaults to `any`
286
+ * @param {T[]} array - The array to paginate
287
+ * @param {number} page - The page number
288
+ * @param {number} pageSize - The number of items per page; defaults to 10
289
+ * @returns {Object} The paginated array and total number of pages
290
+ */
291
+ paginateArray: (array, page, pageSize = 10) => {
292
+ // Define variables
293
+ const startIndex = (page - 1) * pageSize;
294
+ const endIndex = startIndex + pageSize;
295
+ const totalPages = Math.ceil(array.length / pageSize);
296
+ // Paginate items
297
+ const paginatedItems = array.slice(startIndex, endIndex);
298
+ // Return data
299
+ return { paginatedItems, totalPages };
300
+ },
301
+ /**
302
+ * Sanitises any potential HTML injected into user input.
303
+ * @param {string} input - The input to be sanitised
304
+ * @returns A sanitised string, or an empty string if the input is invalid
305
+ */
306
+ sanitiseHtml: (input) => typeof input !== "string" ? "" : sanitize(input, { allowedTags: [], allowedAttributes: {} })
307
+ };
308
+ /* DATA VALIDATION HELPERS */
309
+ export const Validate = {
310
+ /**
311
+ * A regular expression for E.164 phone numbers.
312
+ *
313
+ * - Source: https://www.twilio.com/docs/glossary/what-e164
314
+ */
315
+ e164Regex: /^\+?[1-9]\d{1,14}$/,
316
+ /**
317
+ * The minimum birth date (from today's date) for a user who is a minor, i.e. `YYYY-MM-DD`
318
+ */
319
+ minimumMinorBirthDate: (() => {
320
+ const today = new Date();
321
+ const tomorrow = new Date();
322
+ tomorrow.setUTCDate(today.getDate() + 1);
323
+ const tomorrow18YearsAgo = tomorrow.getFullYear() - 18;
324
+ tomorrow.setFullYear(tomorrow18YearsAgo);
325
+ return tomorrow.toISOString().split("T")[0];
326
+ })(),
327
+ /**
328
+ * A regular expression for user passwords to match during authentication. It covers the following rules:
329
+ * - at least 8 characters long
330
+ * - at least one digit
331
+ * - at least one lowercase letter
332
+ * - at least one uppercase letter
333
+ * - at least one symbol
334
+ */
335
+ passwordRegex: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()\-_+=|{}\[\]:;'<>,.?/~]).{8,}$/,
336
+ /**
337
+ * A regular expression for social media handles, without a leading slash or @ character.
338
+ *
339
+ * - Source: https://stackoverflow.com/a/74579651
340
+ */
341
+ socialMediaHandleRegex: /^([A-Za-z0-9_.]{3,25})$/gm,
342
+ /**
343
+ * An array of values representing social media platforms recognised in the database.
344
+ */
345
+ socialMediaPlatforms: [
346
+ { value: "facebook", linkPrefix: "https://www.facebook.com/" },
347
+ { value: "instagram", linkPrefix: "https://www.instagram.com/" },
348
+ { value: "kick", linkPrefix: "https://kick.com/" },
349
+ { value: "tiktok", linkPrefix: "https://www.tiktok.com/@" },
350
+ { value: "twitch", linkPrefix: "https://www.twitch.tv/" },
351
+ { value: "twitter", linkPrefix: "https://x.com/" },
352
+ { value: "youtube", linkPrefix: "https://www.youtube.com/@" }
353
+ ],
354
+ /**
355
+ * Validates an E.164 phone number.
356
+ * @param {string} phoneNumber - The phone number to be validated
357
+ * @param {"any" | MobilePhoneLocale | MobilePhoneLocale[]} [locale="any"] - The locale(s) to validate the phone number against
358
+ * @returns `true` if the phone number is valid; `false` otherwise
359
+ */
360
+ validateE164: (phoneNumber, locale = "en-KE") => isMobilePhone(phoneNumber, locale) && Boolean(phoneNumber.match(Validate.e164Regex)),
361
+ /**
362
+ * Validates a social media handle.
363
+ * @param {string} handle - The social media handle to be validated
364
+ * @returns `true` if the handle is valid; `false` otherwise
365
+ */
366
+ validateSocialMediaHandle: (handle) => !isURL(handle, { require_protocol: false }) &&
367
+ !handle.startsWith("@") &&
368
+ Boolean(handle.match(Validate.socialMediaHandleRegex))
369
+ };
@@ -0,0 +1,54 @@
1
+ export declare const Status: {
2
+ /**
3
+ * A generic error message.
4
+ */
5
+ ERROR: string;
6
+ /**
7
+ * A generic loading message.
8
+ */
9
+ LOADING: string;
10
+ /**
11
+ * A generic password reset request message.
12
+ */
13
+ PASSWORD_RESET_REQUESTED: string;
14
+ };
15
+ export declare const Cache: {
16
+ /**
17
+ * Delete cached data from Valkey.
18
+ * @param valkey - The Valkey client instance
19
+ * @param cachePrefix - The cache prefix
20
+ * @param key - The cache key
21
+ */
22
+ deleteData: (valkey: any, cachePrefix: string, key: string) => Promise<void>;
23
+ /**
24
+ * Generate cache key with consistent formatting
25
+ * @param prefix - The key prefix
26
+ * @param params - Additional parameters to include in the key
27
+ * @returns The formatted cache key
28
+ */
29
+ generateDataKey: (prefix: string, ...params: (string | number)[]) => string;
30
+ /**
31
+ * Get cached data from Valkey.
32
+ * @param valkey - The Valkey client instance
33
+ * @param cachePrefix - The cache prefix
34
+ * @param key - The cache key
35
+ * @returns The cached data, or `null` if not found
36
+ */
37
+ getData: <T>(valkey: any, cachePrefix: string, key: string) => Promise<T | null>;
38
+ /**
39
+ * Invalidate cache by pattern
40
+ * @param valkey - The Valkey client instance
41
+ * @param cachePrefix - The cache prefix
42
+ * @param pattern - The pattern to match keys against (e.g., "app-config*")
43
+ */
44
+ invalidateDataByPattern: (valkey: any, cachePrefix: string, pattern: string) => Promise<void>;
45
+ /**
46
+ * Set data in Valkey cache.
47
+ * @param valkey - The Valkey client instance
48
+ * @param cachePrefix - The cache prefix
49
+ * @param key - The cache key
50
+ * @param data - The data to cache
51
+ * @param ttl - The time to live in seconds; defaults to 1 hour
52
+ */
53
+ setData: <T>(valkey: any, cachePrefix: string, key: string, data: T, ttl?: number) => Promise<void>;
54
+ };
@@ -0,0 +1,88 @@
1
+ /* STATUS MESSAGES */
2
+ export const Status = {
3
+ /**
4
+ * A generic error message.
5
+ */
6
+ ERROR: "An unexpected error occurred. Kindly try again.",
7
+ /**
8
+ * A generic loading message.
9
+ */
10
+ LOADING: "Please wait…",
11
+ /**
12
+ * A generic password reset request message.
13
+ */
14
+ PASSWORD_RESET_REQUESTED: "If the provided email address is registered, you will receive a password reset link shortly."
15
+ };
16
+ /* CACHE HELPERS */
17
+ export const Cache = {
18
+ /**
19
+ * Delete cached data from Valkey.
20
+ * @param valkey - The Valkey client instance
21
+ * @param cachePrefix - The cache prefix
22
+ * @param key - The cache key
23
+ */
24
+ deleteData: async (valkey, cachePrefix, key) => {
25
+ try {
26
+ await valkey.del(`${cachePrefix}${key}`);
27
+ }
28
+ catch (error) {
29
+ console.error("Cache delete error:", error);
30
+ }
31
+ },
32
+ /**
33
+ * Generate cache key with consistent formatting
34
+ * @param prefix - The key prefix
35
+ * @param params - Additional parameters to include in the key
36
+ * @returns The formatted cache key
37
+ */
38
+ generateDataKey: (prefix, ...params) => `${prefix}:${params.join(":")}`,
39
+ /**
40
+ * Get cached data from Valkey.
41
+ * @param valkey - The Valkey client instance
42
+ * @param cachePrefix - The cache prefix
43
+ * @param key - The cache key
44
+ * @returns The cached data, or `null` if not found
45
+ */
46
+ getData: async (valkey, cachePrefix, key) => {
47
+ try {
48
+ const cached = await valkey.get(`${cachePrefix}${key}`);
49
+ return cached ? JSON.parse(cached) : null;
50
+ }
51
+ catch (error) {
52
+ console.error("Cache get error:", error);
53
+ return null;
54
+ }
55
+ },
56
+ /**
57
+ * Invalidate cache by pattern
58
+ * @param valkey - The Valkey client instance
59
+ * @param cachePrefix - The cache prefix
60
+ * @param pattern - The pattern to match keys against (e.g., "app-config*")
61
+ */
62
+ invalidateDataByPattern: async (valkey, cachePrefix, pattern) => {
63
+ try {
64
+ const keys = await valkey.keys(`${cachePrefix}${pattern}`);
65
+ if (keys.length > 0)
66
+ await valkey.del(...keys);
67
+ }
68
+ catch (error) {
69
+ console.error("Cache pattern invalidation error:", error);
70
+ }
71
+ },
72
+ /**
73
+ * Set data in Valkey cache.
74
+ * @param valkey - The Valkey client instance
75
+ * @param cachePrefix - The cache prefix
76
+ * @param key - The cache key
77
+ * @param data - The data to cache
78
+ * @param ttl - The time to live in seconds; defaults to 1 hour
79
+ */
80
+ setData: async (valkey, cachePrefix, key, data, ttl = 3600) => {
81
+ try {
82
+ await valkey.setex(`${cachePrefix}${key}`, ttl, JSON.stringify(data));
83
+ }
84
+ catch (error) {
85
+ console.error("Cache set error:", error);
86
+ }
87
+ }
88
+ };