@digicroz/js-kit 1.0.6 → 1.0.10

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 +1 @@
1
- {"version":3,"sources":["../../src/sleep/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["type TSleepParams ={\r\n milliseconds?: number;\r\n seconds?: number;\r\n minutes?: number;\r\n until?: {\r\n unixTimestamp?: number;\r\n };\r\n random?:{\r\n milliseconds?: {\r\n min: number;\r\n max: number;\r\n }\r\n seconds?: {\r\n min: number;\r\n max: number;\r\n }\r\n minutes?: {\r\n min: number;\r\n max: number;\r\n }\r\n }\r\n}\r\n\r\ntype TSleepReturn = Promise<void>;\r\n\r\n/**\r\n * Sleep function that supports various delay options\r\n * @param params - Sleep parameters including fixed delays, random delays, or until timestamp\r\n * @returns Promise that resolves after the specified delay\r\n * @throws Error if invalid parameters are provided\r\n */\r\nexport const sleep = (params: TSleepParams): TSleepReturn => {\r\n return new Promise((resolve, reject) => {\r\n let delayMs = 0;\r\n\r\n try {\r\n // Validate input parameters\r\n if (!params || Object.keys(params).length === 0) {\r\n throw new Error('Sleep parameters cannot be empty');\r\n }\r\n\r\n // Handle sleeping until a specific timestamp\r\n if (params.until?.unixTimestamp) {\r\n const now = Date.now();\r\n const targetTime = params.until.unixTimestamp * 1000; // Convert to milliseconds\r\n delayMs = Math.max(0, targetTime - now);\r\n \r\n // If the timestamp is in the past, resolve immediately\r\n if (delayMs === 0) {\r\n resolve();\r\n return;\r\n }\r\n }\r\n // Handle random delays\r\n else if (params.random) {\r\n if (params.random.milliseconds) {\r\n const { min, max } = params.random.milliseconds;\r\n if (min < 0 || max < 0 || min > max) {\r\n throw new Error('Invalid random milliseconds range');\r\n }\r\n delayMs = Math.random() * (max - min) + min;\r\n } else if (params.random.seconds) {\r\n const { min, max } = params.random.seconds;\r\n if (min < 0 || max < 0 || min > max) {\r\n throw new Error('Invalid random seconds range');\r\n }\r\n delayMs = (Math.random() * (max - min) + min) * 1000;\r\n } else if (params.random.minutes) {\r\n const { min, max } = params.random.minutes;\r\n if (min < 0 || max < 0 || min > max) {\r\n throw new Error('Invalid random minutes range');\r\n }\r\n delayMs = (Math.random() * (max - min) + min) * 60 * 1000;\r\n } else {\r\n throw new Error('Random delay type must be specified (milliseconds, seconds, or minutes)');\r\n }\r\n }\r\n // Handle fixed delays\r\n else {\r\n if (params.milliseconds !== undefined) {\r\n if (params.milliseconds < 0) {\r\n throw new Error('Milliseconds cannot be negative');\r\n }\r\n delayMs += params.milliseconds;\r\n }\r\n if (params.seconds !== undefined) {\r\n if (params.seconds < 0) {\r\n throw new Error('Seconds cannot be negative');\r\n }\r\n delayMs += params.seconds * 1000;\r\n }\r\n if (params.minutes !== undefined) {\r\n if (params.minutes < 0) {\r\n throw new Error('Minutes cannot be negative');\r\n }\r\n delayMs += params.minutes * 60 * 1000;\r\n }\r\n\r\n // If no valid delay was specified\r\n if (delayMs === 0 && !params.milliseconds && !params.seconds && !params.minutes) {\r\n throw new Error('At least one delay parameter must be specified');\r\n }\r\n }\r\n\r\n // Ensure delay is not too large (prevent potential issues)\r\n const MAX_DELAY = 2147483647; // Maximum value for setTimeout\r\n if (delayMs > MAX_DELAY) {\r\n throw new Error(`Delay too large. Maximum delay is ${MAX_DELAY}ms`);\r\n }\r\n\r\n setTimeout(resolve, Math.floor(delayMs));\r\n } catch (error) {\r\n reject(error);\r\n }\r\n });\r\n};\r\n\r\n/**\r\n * Convenience function to sleep for a specific number of milliseconds\r\n * @param ms - Number of milliseconds to sleep\r\n * @returns Promise that resolves after the specified delay\r\n */\r\nexport const sleepMs = (ms: number): TSleepReturn => {\r\n return sleep({ milliseconds: ms });\r\n};\r\n\r\n/**\r\n * Convenience function to sleep for a specific number of seconds\r\n * @param seconds - Number of seconds to sleep\r\n * @returns Promise that resolves after the specified delay\r\n */\r\nexport const sleepSeconds = (seconds: number): TSleepReturn => {\r\n return sleep({ seconds });\r\n};\r\n\r\n/**\r\n * Convenience function to sleep for a specific number of minutes\r\n * @param minutes - Number of minutes to sleep\r\n * @returns Promise that resolves after the specified delay\r\n */\r\nexport const sleepMinutes = (minutes: number): TSleepReturn => {\r\n return sleep({ minutes });\r\n};\r\n\r\n/**\r\n * Convenience function to sleep until a specific Unix timestamp\r\n * @param unixTimestamp - Unix timestamp (in seconds) to sleep until\r\n * @returns Promise that resolves at the specified timestamp\r\n */\r\nexport const sleepUntil = (unixTimestamp: number): TSleepReturn => {\r\n return sleep({ until: { unixTimestamp } });\r\n};\r\n\r\n// Export the type definitions for consumers\r\nexport type { TSleepParams, TSleepReturn };"]}
1
+ {"version":3,"sources":["../../src/sleep/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["type TSleepParams ={\r\n milliseconds?: number;\r\n seconds?: number;\r\n minutes?: number;\r\n until?: {\r\n unixTimestamp?: number;\r\n };\r\n random?:{\r\n milliseconds?: {\r\n min: number;\r\n max: number;\r\n }\r\n seconds?: {\r\n min: number;\r\n max: number;\r\n }\r\n minutes?: {\r\n min: number;\r\n max: number;\r\n }\r\n }\r\n}\r\n\r\ntype TSleepReturn = Promise<void>;\r\n\r\n/**\r\n * Sleep function that supports various delay options\r\n * @param params - Sleep parameters including fixed delays, random delays, or until timestamp\r\n * @returns Promise that resolves after the specified delay\r\n * @throws Error if invalid parameters are provided\r\n */\r\nexport const sleep = (params: TSleepParams): TSleepReturn => {\r\n return new Promise((resolve, reject) => {\r\n let delayMs = 0;\r\n\r\n try {\r\n // Validate input parameters\r\n if (!params || Object.keys(params).length === 0) {\r\n throw new Error('Sleep parameters cannot be empty');\r\n }\r\n\r\n // Handle sleeping until a specific timestamp\r\n if (params.until?.unixTimestamp) {\r\n const now = Date.now();\r\n const targetTime = params.until.unixTimestamp * 1000; // Convert to milliseconds\r\n delayMs = Math.max(0, targetTime - now);\r\n \r\n // If the timestamp is in the past, resolve immediately\r\n if (delayMs === 0) {\r\n resolve();\r\n return;\r\n }\r\n }\r\n // Handle random delays\r\n else if (params.random) {\r\n if (params.random.milliseconds) {\r\n const { min, max } = params.random.milliseconds;\r\n if (min < 0 || max < 0 || min > max) {\r\n throw new Error('Invalid random milliseconds range');\r\n }\r\n delayMs = Math.random() * (max - min) + min;\r\n } else if (params.random.seconds) {\r\n const { min, max } = params.random.seconds;\r\n if (min < 0 || max < 0 || min > max) {\r\n throw new Error('Invalid random seconds range');\r\n }\r\n delayMs = (Math.random() * (max - min) + min) * 1000;\r\n } else if (params.random.minutes) {\r\n const { min, max } = params.random.minutes;\r\n if (min < 0 || max < 0 || min > max) {\r\n throw new Error('Invalid random minutes range');\r\n }\r\n delayMs = (Math.random() * (max - min) + min) * 60 * 1000;\r\n } else {\r\n throw new Error('Random delay type must be specified (milliseconds, seconds, or minutes)');\r\n }\r\n }\r\n // Handle fixed delays\r\n else {\r\n if (params.milliseconds !== undefined) {\r\n if (params.milliseconds < 0) {\r\n throw new Error('Milliseconds cannot be negative');\r\n }\r\n delayMs += params.milliseconds;\r\n }\r\n if (params.seconds !== undefined) {\r\n if (params.seconds < 0) {\r\n throw new Error('Seconds cannot be negative');\r\n }\r\n delayMs += params.seconds * 1000;\r\n }\r\n if (params.minutes !== undefined) {\r\n if (params.minutes < 0) {\r\n throw new Error('Minutes cannot be negative');\r\n }\r\n delayMs += params.minutes * 60 * 1000;\r\n }\r\n\r\n // If no valid delay was specified\r\n if (delayMs === 0 && params.milliseconds === undefined && params.seconds === undefined && params.minutes === undefined) {\r\n throw new Error('At least one delay parameter must be specified');\r\n }\r\n }\r\n\r\n // Ensure delay is not too large (prevent potential issues)\r\n const MAX_DELAY = 2147483647; // Maximum value for setTimeout\r\n if (delayMs > MAX_DELAY) {\r\n throw new Error(`Delay too large. Maximum delay is ${MAX_DELAY}ms`);\r\n }\r\n\r\n if (delayMs === 0) {\r\n resolve();\r\n return;\r\n }\r\n\r\n setTimeout(resolve, Math.floor(delayMs));\r\n } catch (error) {\r\n reject(error);\r\n }\r\n });\r\n};\r\n\r\n/**\r\n * Convenience function to sleep for a specific number of milliseconds\r\n * @param ms - Number of milliseconds to sleep\r\n * @returns Promise that resolves after the specified delay\r\n */\r\nexport const sleepMs = (ms: number): TSleepReturn => {\r\n return sleep({ milliseconds: ms });\r\n};\r\n\r\n/**\r\n * Convenience function to sleep for a specific number of seconds\r\n * @param seconds - Number of seconds to sleep\r\n * @returns Promise that resolves after the specified delay\r\n */\r\nexport const sleepSeconds = (seconds: number): TSleepReturn => {\r\n return sleep({ seconds });\r\n};\r\n\r\n/**\r\n * Convenience function to sleep for a specific number of minutes\r\n * @param minutes - Number of minutes to sleep\r\n * @returns Promise that resolves after the specified delay\r\n */\r\nexport const sleepMinutes = (minutes: number): TSleepReturn => {\r\n return sleep({ minutes });\r\n};\r\n\r\n/**\r\n * Convenience function to sleep until a specific Unix timestamp\r\n * @param unixTimestamp - Unix timestamp (in seconds) to sleep until\r\n * @returns Promise that resolves at the specified timestamp\r\n */\r\nexport const sleepUntil = (unixTimestamp: number): TSleepReturn => {\r\n return sleep({ until: { unixTimestamp } });\r\n};\r\n\r\n// Export the type definitions for consumers\r\nexport type { TSleepParams, TSleepReturn };"]}
@@ -1,19 +1,38 @@
1
1
  'use strict';
2
2
 
3
3
  // src/slug/index.ts
4
- function isValidSlug(slug) {
4
+ function isValidSlug(slug, options = {}) {
5
5
  if (!slug || typeof slug !== "string") {
6
6
  return false;
7
7
  }
8
+ const { allowDots = false } = options;
9
+ if (allowDots) {
10
+ const slugWithDotsPattern = /^[a-z0-9]+(?:[-.][a-z0-9]+)*$/;
11
+ return slugWithDotsPattern.test(slug);
12
+ }
8
13
  const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
9
14
  return slugPattern.test(slug);
10
15
  }
11
16
  function convertToSlug(text, options = {}) {
12
- const { separator = "-" } = options;
17
+ const { separator = "-", allowDots = false } = options;
13
18
  if (!text || typeof text !== "string") {
14
19
  return "";
15
20
  }
16
- return text.toString().toLowerCase().trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[\s_]+/g, separator).replace(new RegExp(`[^a-z0-9${separator}]`, "g"), "").replace(new RegExp(`${separator}+`, "g"), separator).replace(new RegExp(`^${separator}+|${separator}+$`, "g"), "");
21
+ const escapedSeparator = separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22
+ let slug = text.toString().toLowerCase().trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
23
+ if (!allowDots) {
24
+ slug = slug.replace(/\./g, separator);
25
+ }
26
+ slug = slug.replace(/[\s_]+/g, separator).replace(new RegExp(`[^a-z0-9${escapedSeparator}${allowDots ? "\\." : ""}]`, "g"), "");
27
+ if (allowDots) {
28
+ slug = slug.replace(new RegExp(`[${escapedSeparator}.]+`, "g"), (match) => {
29
+ return match.includes(".") ? "." : separator;
30
+ });
31
+ } else {
32
+ slug = slug.replace(new RegExp(`${escapedSeparator}+`, "g"), separator);
33
+ }
34
+ const trimRegex = new RegExp(`^[${escapedSeparator}${allowDots ? "\\." : ""}]+|[${escapedSeparator}${allowDots ? "\\." : ""}]+$`, "g");
35
+ return slug.replace(trimRegex, "");
17
36
  }
18
37
  function generateUniqueSlug(baseSlug, existingSlugs, options = {}) {
19
38
  const { separator = "-" } = options;
@@ -28,8 +47,9 @@ function generateUniqueSlug(baseSlug, existingSlugs, options = {}) {
28
47
  }
29
48
  return uniqueSlug;
30
49
  }
31
- function zodSlugValidation(message) {
32
- return (val) => isValidSlug(val);
50
+ function zodSlugValidation(optionsOrMessage) {
51
+ const options = typeof optionsOrMessage === "string" ? { } : optionsOrMessage || {};
52
+ return (val) => isValidSlug(val, { allowDots: options.allowDots });
33
53
  }
34
54
  function zodSlugTransform(options) {
35
55
  return (val) => convertToSlug(val, options);
@@ -39,11 +59,13 @@ var slugSchema = {
39
59
  * Get a Zod string schema that validates slug format
40
60
  * Requires zod to be installed: npm install zod
41
61
  */
42
- create: (customMessage) => {
62
+ create: (customMessageOrOptions) => {
63
+ const options = typeof customMessageOrOptions === "string" ? { message: customMessageOrOptions } : customMessageOrOptions || {};
64
+ const message = options.message || "Must be a valid slug (lowercase, alphanumeric, and hyphens only, no consecutive hyphens)";
43
65
  return {
44
66
  _type: "slug-validator",
45
- validate: zodSlugValidation(),
46
- message: customMessage || "Must be a valid slug (lowercase, alphanumeric, and hyphens only, no consecutive hyphens)"
67
+ validate: zodSlugValidation(options),
68
+ message
47
69
  };
48
70
  }
49
71
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/slug/index.ts"],"names":[],"mappings":";;;AAiBO,SAAS,YAAY,IAAA,EAAuB;AACjD,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,IAAA,OAAO,KAAA;AAAA;AAOT,EAAA,MAAM,WAAA,GAAc,4BAAA;AAEpB,EAAA,OAAO,WAAA,CAAY,KAAK,IAAI,CAAA;AAC9B;AA0BO,SAAS,aAAA,CACd,IAAA,EACA,OAAA,GAAkC,EAAC,EAC3B;AACR,EAAA,MAAM,EAAE,SAAA,GAAY,GAAA,EAAI,GAAI,OAAA;AAE5B,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,IAAA,OAAO,EAAA;AAAA;AAGT,EAAA,OAAO,IAAA,CACJ,UAAS,CACT,WAAA,GACA,IAAA,EAAK,CAEL,SAAA,CAAU,KAAK,CAAA,CACf,OAAA,CAAQ,oBAAoB,EAAE,CAAA,CAE9B,QAAQ,SAAA,EAAW,SAAS,EAE5B,OAAA,CAAQ,IAAI,MAAA,CAAO,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAA,EAAK,GAAG,CAAA,EAAG,EAAE,EAEpD,OAAA,CAAQ,IAAI,OAAO,CAAA,EAAG,SAAS,CAAA,CAAA,CAAA,EAAK,GAAG,CAAA,EAAG,SAAS,EAEnD,OAAA,CAAQ,IAAI,OAAO,CAAA,CAAA,EAAI,SAAS,KAAK,SAAS,CAAA,EAAA,CAAA,EAAM,GAAG,CAAA,EAAG,EAAE,CAAA;AACjE;AAkBO,SAAS,kBAAA,CACd,QAAA,EACA,aAAA,EACA,OAAA,GAAkC,EAAC,EAC3B;AACR,EAAA,MAAM,EAAE,SAAA,GAAY,GAAA,EAAI,GAAI,OAAA;AAE5B,EAAA,IAAI,CAAC,aAAA,CAAc,QAAA,CAAS,QAAQ,CAAA,EAAG;AACrC,IAAA,OAAO,QAAA;AAAA;AAGT,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,aAAa,CAAA,EAAG,QAAQ,CAAA,EAAG,SAAS,GAAG,OAAO,CAAA,CAAA;AAElD,EAAA,OAAO,aAAA,CAAc,QAAA,CAAS,UAAU,CAAA,EAAG;AACzC,IAAA,OAAA,EAAA;AACA,IAAA,UAAA,GAAa,CAAA,EAAG,QAAQ,CAAA,EAAG,SAAS,GAAG,OAAO,CAAA,CAAA;AAAA;AAGhD,EAAA,OAAO,UAAA;AACT;AAqBO,SAAS,kBAAkB,OAAA,EAAkB;AAClD,EAAA,OAAO,CAAC,GAAA,KAAgB,WAAA,CAAY,GAAG,CAAA;AACzC;AAwBO,SAAS,iBAAiB,OAAA,EAAkC;AACjE,EAAA,OAAO,CAAC,GAAA,KAAgB,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACpD;AAmBO,IAAM,UAAA,GAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxB,MAAA,EAAQ,CAAC,aAAA,KAA2B;AAElC,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,gBAAA;AAAA,MACP,QAAA,EAAU,kBAA+B,CAAA;AAAA,MACzC,SAAS,aAAA,IAAiB;AAAA,KAC5B;AAAA;AAEJ;AAoBO,IAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA,EAI5B,SAAA,EAAW,CAAC,OAAA,KAAqC,gBAAA,CAAiB,OAAO;AAC3E","file":"index.cjs","sourcesContent":["/**\r\n * Checks if a string is a valid slug\r\n * A valid slug contains only lowercase alphanumeric characters and hyphens,\r\n * with no consecutive hyphens and no leading/trailing hyphens\r\n * \r\n * @param slug - The string to validate\r\n * @returns True if the string is a valid slug, false otherwise\r\n * \r\n * @example\r\n * ```ts\r\n * isValidSlug('hello-world') // true\r\n * isValidSlug('hello--world') // false (consecutive hyphens)\r\n * isValidSlug('Hello-World') // false (uppercase)\r\n * isValidSlug('-hello-world') // false (leading hyphen)\r\n * isValidSlug('hello_world') // false (underscore not allowed)\r\n * ```\r\n */\r\nexport function isValidSlug(slug: string): boolean {\r\n if (!slug || typeof slug !== 'string') {\r\n return false;\r\n }\r\n\r\n // Check if slug matches the pattern:\r\n // - starts with alphanumeric\r\n // - ends with alphanumeric\r\n // - contains only lowercase alphanumeric and single hyphens\r\n const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\r\n \r\n return slugPattern.test(slug);\r\n}\r\n\r\n/**\r\n * Converts a string to a URL-safe slug\r\n * - Converts to lowercase\r\n * - Removes special characters\r\n * - Replaces spaces and underscores with hyphens\r\n * - Removes consecutive hyphens\r\n * - Trims leading and trailing hyphens\r\n * \r\n * @param text - The string to convert to a slug\r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use as separator (default: '-')\r\n * @returns A URL-safe slug\r\n * \r\n * @example\r\n * ```ts\r\n * convertToSlug('Hello World') // 'hello-world'\r\n * convertToSlug('Hello World!!!') // 'hello-world'\r\n * convertToSlug('Hello_World') // 'hello-world'\r\n * convertToSlug(' Hello World ') // 'hello-world'\r\n * convertToSlug('Hello---World') // 'hello-world'\r\n * convertToSlug('Café & Restaurant') // 'cafe-restaurant'\r\n * convertToSlug('Product #123') // 'product-123'\r\n * ```\r\n */\r\nexport function convertToSlug(\r\n text: string,\r\n options: { separator?: string } = {}\r\n): string {\r\n const { separator = '-' } = options;\r\n\r\n if (!text || typeof text !== 'string') {\r\n return '';\r\n }\r\n\r\n return text\r\n .toString()\r\n .toLowerCase()\r\n .trim()\r\n // Remove accents and diacritics\r\n .normalize('NFD')\r\n .replace(/[\\u0300-\\u036f]/g, '')\r\n // Replace spaces, underscores, and other common separators with separator\r\n .replace(/[\\s_]+/g, separator)\r\n // Remove all non-alphanumeric characters except the separator\r\n .replace(new RegExp(`[^a-z0-9${separator}]`, 'g'), '')\r\n // Replace multiple consecutive separators with single separator\r\n .replace(new RegExp(`${separator}+`, 'g'), separator)\r\n // Remove leading and trailing separators\r\n .replace(new RegExp(`^${separator}+|${separator}+$`, 'g'), '');\r\n}\r\n\r\n/**\r\n * Generates a unique slug by appending a number if the slug already exists\r\n * \r\n * @param baseSlug - The base slug to make unique\r\n * @param existingSlugs - Array of existing slugs to check against\r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use before the number (default: '-')\r\n * @returns A unique slug\r\n * \r\n * @example\r\n * ```ts\r\n * generateUniqueSlug('hello-world', ['hello-world']) // 'hello-world-1'\r\n * generateUniqueSlug('hello-world', ['hello-world', 'hello-world-1']) // 'hello-world-2'\r\n * generateUniqueSlug('hello-world', []) // 'hello-world'\r\n * ```\r\n */\r\nexport function generateUniqueSlug(\r\n baseSlug: string,\r\n existingSlugs: string[],\r\n options: { separator?: string } = {}\r\n): string {\r\n const { separator = '-' } = options;\r\n\r\n if (!existingSlugs.includes(baseSlug)) {\r\n return baseSlug;\r\n }\r\n\r\n let counter = 1;\r\n let uniqueSlug = `${baseSlug}${separator}${counter}`;\r\n\r\n while (existingSlugs.includes(uniqueSlug)) {\r\n counter++;\r\n uniqueSlug = `${baseSlug}${separator}${counter}`;\r\n }\r\n\r\n return uniqueSlug;\r\n}\r\n\r\n/**\r\n * Creates a Zod refinement function for slug validation\r\n * Use with z.string().refine() or z.string().superRefine()\r\n * \r\n * @param message - Custom error message (optional)\r\n * @returns Refinement function for Zod\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { zodSlugValidation } from '@digicroz/js-kit';\r\n * \r\n * const schema = z.object({\r\n * slug: z.string().refine(zodSlugValidation(), {\r\n * message: 'Invalid slug format'\r\n * })\r\n * });\r\n * ```\r\n */\r\nexport function zodSlugValidation(message?: string) {\r\n return (val: string) => isValidSlug(val);\r\n}\r\n\r\n/**\r\n * Creates a Zod transform function that converts strings to slugs\r\n * Use with z.string().transform()\r\n * \r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use as separator (default: '-')\r\n * @returns Transform function for Zod\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { zodSlugTransform } from '@digicroz/js-kit';\r\n * \r\n * const schema = z.object({\r\n * title: z.string(),\r\n * slug: z.string().transform(zodSlugTransform())\r\n * });\r\n * \r\n * schema.parse({ title: 'Hello', slug: 'Hello World!!!' })\r\n * // { title: 'Hello', slug: 'hello-world' }\r\n * ```\r\n */\r\nexport function zodSlugTransform(options?: { separator?: string }) {\r\n return (val: string) => convertToSlug(val, options);\r\n}\r\n\r\n/**\r\n * Pre-configured Zod schema for slug validation\r\n * Validates that the string is a valid slug format\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { slugSchema } from '@digicroz/js-kit';\r\n * \r\n * const postSchema = z.object({\r\n * slug: slugSchema\r\n * });\r\n * \r\n * postSchema.parse({ slug: 'hello-world' }); // ✓ Valid\r\n * postSchema.parse({ slug: 'Hello World' }); // ✗ Invalid\r\n * ```\r\n */\r\nexport const slugSchema = {\r\n /**\r\n * Get a Zod string schema that validates slug format\r\n * Requires zod to be installed: npm install zod\r\n */\r\n create: (customMessage?: string) => {\r\n // Dynamic import to avoid making zod a required dependency\r\n return {\r\n _type: 'slug-validator' as const,\r\n validate: zodSlugValidation(customMessage),\r\n message: customMessage || 'Must be a valid slug (lowercase, alphanumeric, and hyphens only, no consecutive hyphens)'\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * Pre-configured Zod schema that auto-converts strings to slugs\r\n * Automatically transforms any string input into a valid slug\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { autoSlugSchema } from '@digicroz/js-kit';\r\n * \r\n * const postSchema = z.object({\r\n * title: z.string(),\r\n * slug: z.string().transform(autoSlugSchema.transform())\r\n * });\r\n * \r\n * postSchema.parse({ title: 'My Post', slug: 'Hello World!!!' })\r\n * // { title: 'My Post', slug: 'hello-world' }\r\n * ```\r\n */\r\nexport const autoSlugSchema = {\r\n /**\r\n * Get a transform function for Zod\r\n */\r\n transform: (options?: { separator?: string }) => zodSlugTransform(options)\r\n};\r\n"]}
1
+ {"version":3,"sources":["../../src/slug/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Checks if a string is a valid slug\r\n * A valid slug contains only lowercase alphanumeric characters and hyphens,\r\n * with no consecutive hyphens and no leading/trailing hyphens\r\n * \r\n * @param slug - The string to validate\r\n * @returns True if the string is a valid slug, false otherwise\r\n * \r\n * @example\r\n * ```ts\r\n * isValidSlug('hello-world') // true\r\n * isValidSlug('hello--world') // false (consecutive hyphens)\r\n * isValidSlug('Hello-World') // false (uppercase)\r\n * isValidSlug('-hello-world') // false (leading hyphen)\r\n * isValidSlug('hello_world') // false (underscore not allowed)\r\n * ```\r\n */\r\nexport function isValidSlug(\r\n slug: string,\r\n options: { allowDots?: boolean } = {}\r\n): boolean {\r\n if (!slug || typeof slug !== 'string') {\r\n return false;\r\n }\r\n\r\n const { allowDots = false } = options;\r\n\r\n // Check if slug matches the pattern:\r\n // - starts with alphanumeric\r\n // - ends with alphanumeric\r\n // - contains only lowercase alphanumeric and single hyphens (and dots if allowed)\r\n // - no consecutive separators (hyphens or dots)\r\n \r\n if (allowDots) {\r\n // pattern allowing dots:\r\n // starts with alphanumeric\r\n // middle: alphanumeric or single hyphen/dot followed by alphanumeric\r\n // ends with alphanumeric\r\n const slugWithDotsPattern = /^[a-z0-9]+(?:[-.][a-z0-9]+)*$/;\r\n return slugWithDotsPattern.test(slug);\r\n }\r\n\r\n const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\r\n \r\n return slugPattern.test(slug);\r\n}\r\n\r\n/**\r\n * Converts a string to a URL-safe slug\r\n * - Converts to lowercase\r\n * - Removes special characters\r\n * - Replaces spaces and underscores with hyphens\r\n * - Removes consecutive hyphens\r\n * - Trims leading and trailing hyphens\r\n * \r\n * @param text - The string to convert to a slug\r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use as separator (default: '-')\r\n * @returns A URL-safe slug\r\n * \r\n * @example\r\n * ```ts\r\n * convertToSlug('Hello World') // 'hello-world'\r\n * convertToSlug('Hello World!!!') // 'hello-world'\r\n * convertToSlug('Hello_World') // 'hello-world'\r\n * convertToSlug(' Hello World ') // 'hello-world'\r\n * convertToSlug('Hello---World') // 'hello-world'\r\n * convertToSlug('Café & Restaurant') // 'cafe-restaurant'\r\n * convertToSlug('Product #123') // 'product-123'\r\n * ```\r\n */\r\nexport function convertToSlug(\r\n text: string,\r\n options: { separator?: string; allowDots?: boolean } = {}\r\n): string {\r\n const { separator = '-', allowDots = false } = options;\r\n\r\n if (!text || typeof text !== 'string') {\r\n return '';\r\n }\r\n\r\n // Escape separator for use in regex char class\r\n const escapedSeparator = separator.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n\r\n let slug = text\r\n .toString()\r\n .toLowerCase()\r\n .trim()\r\n // Remove accents and diacritics\r\n .normalize('NFD')\r\n .replace(/[\\u0300-\\u036f]/g, '');\r\n\r\n if (!allowDots) {\r\n // Replace dots with separator\r\n slug = slug.replace(/\\./g, separator);\r\n }\r\n\r\n slug = slug\r\n // Replace spaces, underscores with separator\r\n .replace(/[\\s_]+/g, separator)\r\n // Remove all non-alphanumeric characters except the separator (and dot if allowed)\r\n .replace(new RegExp(`[^a-z0-9${escapedSeparator}${allowDots ? '\\\\.' : ''}]`, 'g'), '');\r\n\r\n if (allowDots) {\r\n // Collapse consecutive separators/dots\r\n // If sequence contains a dot, we generally want to keep it as a dot (e.g. file.-extension -> file.extension)\r\n slug = slug.replace(new RegExp(`[${escapedSeparator}.]+`, 'g'), (match) => {\r\n return match.includes('.') ? '.' : separator;\r\n });\r\n } else {\r\n // Replace multiple consecutive separators with single separator\r\n slug = slug.replace(new RegExp(`${escapedSeparator}+`, 'g'), separator);\r\n }\r\n \r\n // Remove leading and trailing separators (and dots)\r\n const trimRegex = new RegExp(`^[${escapedSeparator}${allowDots ? '\\\\.' : ''}]+|[${escapedSeparator}${allowDots ? '\\\\.' : ''}]+$`, 'g');\r\n return slug.replace(trimRegex, '');\r\n}\r\n\r\n/**\r\n * Generates a unique slug by appending a number if the slug already exists\r\n * \r\n * @param baseSlug - The base slug to make unique\r\n * @param existingSlugs - Array of existing slugs to check against\r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use before the number (default: '-')\r\n * @returns A unique slug\r\n * \r\n * @example\r\n * ```ts\r\n * generateUniqueSlug('hello-world', ['hello-world']) // 'hello-world-1'\r\n * generateUniqueSlug('hello-world', ['hello-world', 'hello-world-1']) // 'hello-world-2'\r\n * generateUniqueSlug('hello-world', []) // 'hello-world'\r\n * ```\r\n */\r\nexport function generateUniqueSlug(\r\n baseSlug: string,\r\n existingSlugs: string[],\r\n options: { separator?: string } = {}\r\n): string {\r\n const { separator = '-' } = options;\r\n\r\n if (!existingSlugs.includes(baseSlug)) {\r\n return baseSlug;\r\n }\r\n\r\n let counter = 1;\r\n let uniqueSlug = `${baseSlug}${separator}${counter}`;\r\n\r\n while (existingSlugs.includes(uniqueSlug)) {\r\n counter++;\r\n uniqueSlug = `${baseSlug}${separator}${counter}`;\r\n }\r\n\r\n return uniqueSlug;\r\n}\r\n\r\n/**\r\n * Creates a Zod refinement function for slug validation\r\n * Use with z.string().refine() or z.string().superRefine()\r\n * \r\n * @param optionsOrMessage - Custom error message or options object\r\n * @returns Refinement function for Zod\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { zodSlugValidation } from '@digicroz/js-kit';\r\n * \r\n * // Basic usage\r\n * const schema = z.object({\r\n * slug: z.string().refine(zodSlugValidation(), {\r\n * message: 'Invalid slug format'\r\n * })\r\n * });\r\n * \r\n * // Allow dots (e.g. for filenames)\r\n * const fileSchema = z.object({\r\n * filename: z.string().refine(zodSlugValidation({ allowDots: true }), {\r\n * message: 'Invalid filename format'\r\n * })\r\n * });\r\n * ```\r\n */\r\nexport function zodSlugValidation(\r\n optionsOrMessage?: string | { message?: string; allowDots?: boolean }\r\n) {\r\n const options =\r\n typeof optionsOrMessage === 'string'\r\n ? { message: optionsOrMessage }\r\n : optionsOrMessage || {};\r\n \r\n return (val: string) => isValidSlug(val, { allowDots: options.allowDots });\r\n}\r\n\r\n/**\r\n * Creates a Zod transform function that converts strings to slugs\r\n * Use with z.string().transform()\r\n * \r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use as separator (default: '-')\r\n * @param options.allowDots - Whether to allow dots in the slug (default: false)\r\n * @returns Transform function for Zod\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { zodSlugTransform } from '@digicroz/js-kit';\r\n * \r\n * const schema = z.object({\r\n * title: z.string(),\r\n * slug: z.string().transform(zodSlugTransform())\r\n * });\r\n * \r\n * schema.parse({ title: 'Hello', slug: 'Hello World!!!' })\r\n * // { title: 'Hello', slug: 'hello-world' }\r\n * ```\r\n */\r\nexport function zodSlugTransform(options?: { separator?: string; allowDots?: boolean }) {\r\n return (val: string) => convertToSlug(val, options);\r\n}\r\n\r\n/**\r\n * Pre-configured Zod schema for slug validation\r\n * Validates that the string is a valid slug format\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { slugSchema } from '@digicroz/js-kit';\r\n * \r\n * const postSchema = z.object({\r\n * slug: slugSchema\r\n * });\r\n * \r\n * postSchema.parse({ slug: 'hello-world' }); // ✓ Valid\r\n * postSchema.parse({ slug: 'Hello World' }); // ✗ Invalid\r\n * ```\r\n */\r\nexport const slugSchema = {\r\n /**\r\n * Get a Zod string schema that validates slug format\r\n * Requires zod to be installed: npm install zod\r\n */\r\n create: (\r\n customMessageOrOptions?: string | { message?: string; allowDots?: boolean }\r\n ) => {\r\n // Dynamic import to avoid making zod a required dependency\r\n const options =\r\n typeof customMessageOrOptions === 'string'\r\n ? { message: customMessageOrOptions }\r\n : customMessageOrOptions || {};\r\n \r\n const message =\r\n options.message ||\r\n 'Must be a valid slug (lowercase, alphanumeric, and hyphens only, no consecutive hyphens)';\r\n\r\n return {\r\n _type: 'slug-validator' as const,\r\n validate: zodSlugValidation(options),\r\n message\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * Pre-configured Zod schema that auto-converts strings to slugs\r\n * Automatically transforms any string input into a valid slug\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { autoSlugSchema } from '@digicroz/js-kit';\r\n * \r\n * const postSchema = z.object({\r\n * title: z.string(),\r\n * slug: z.string().transform(autoSlugSchema.transform())\r\n * });\r\n * \r\n * postSchema.parse({ title: 'My Post', slug: 'Hello World!!!' })\r\n * // { title: 'My Post', slug: 'hello-world' }\r\n * ```\r\n */\r\nexport const autoSlugSchema = {\r\n /**\r\n * Get a transform function for Zod\r\n */\r\n transform: (options?: { separator?: string }) => zodSlugTransform(options)\r\n};\r\n"]}
@@ -15,7 +15,9 @@
15
15
  * isValidSlug('hello_world') // false (underscore not allowed)
16
16
  * ```
17
17
  */
18
- declare function isValidSlug(slug: string): boolean;
18
+ declare function isValidSlug(slug: string, options?: {
19
+ allowDots?: boolean;
20
+ }): boolean;
19
21
  /**
20
22
  * Converts a string to a URL-safe slug
21
23
  * - Converts to lowercase
@@ -42,6 +44,7 @@ declare function isValidSlug(slug: string): boolean;
42
44
  */
43
45
  declare function convertToSlug(text: string, options?: {
44
46
  separator?: string;
47
+ allowDots?: boolean;
45
48
  }): string;
46
49
  /**
47
50
  * Generates a unique slug by appending a number if the slug already exists
@@ -66,7 +69,7 @@ declare function generateUniqueSlug(baseSlug: string, existingSlugs: string[], o
66
69
  * Creates a Zod refinement function for slug validation
67
70
  * Use with z.string().refine() or z.string().superRefine()
68
71
  *
69
- * @param message - Custom error message (optional)
72
+ * @param optionsOrMessage - Custom error message or options object
70
73
  * @returns Refinement function for Zod
71
74
  *
72
75
  * @example
@@ -74,20 +77,32 @@ declare function generateUniqueSlug(baseSlug: string, existingSlugs: string[], o
74
77
  * import { z } from 'zod';
75
78
  * import { zodSlugValidation } from '@digicroz/js-kit';
76
79
  *
80
+ * // Basic usage
77
81
  * const schema = z.object({
78
82
  * slug: z.string().refine(zodSlugValidation(), {
79
83
  * message: 'Invalid slug format'
80
84
  * })
81
85
  * });
86
+ *
87
+ * // Allow dots (e.g. for filenames)
88
+ * const fileSchema = z.object({
89
+ * filename: z.string().refine(zodSlugValidation({ allowDots: true }), {
90
+ * message: 'Invalid filename format'
91
+ * })
92
+ * });
82
93
  * ```
83
94
  */
84
- declare function zodSlugValidation(message?: string): (val: string) => boolean;
95
+ declare function zodSlugValidation(optionsOrMessage?: string | {
96
+ message?: string;
97
+ allowDots?: boolean;
98
+ }): (val: string) => boolean;
85
99
  /**
86
100
  * Creates a Zod transform function that converts strings to slugs
87
101
  * Use with z.string().transform()
88
102
  *
89
103
  * @param options - Optional configuration
90
104
  * @param options.separator - Character to use as separator (default: '-')
105
+ * @param options.allowDots - Whether to allow dots in the slug (default: false)
91
106
  * @returns Transform function for Zod
92
107
  *
93
108
  * @example
@@ -106,6 +121,7 @@ declare function zodSlugValidation(message?: string): (val: string) => boolean;
106
121
  */
107
122
  declare function zodSlugTransform(options?: {
108
123
  separator?: string;
124
+ allowDots?: boolean;
109
125
  }): (val: string) => string;
110
126
  /**
111
127
  * Pre-configured Zod schema for slug validation
@@ -129,7 +145,10 @@ declare const slugSchema: {
129
145
  * Get a Zod string schema that validates slug format
130
146
  * Requires zod to be installed: npm install zod
131
147
  */
132
- create: (customMessage?: string) => {
148
+ create: (customMessageOrOptions?: string | {
149
+ message?: string;
150
+ allowDots?: boolean;
151
+ }) => {
133
152
  _type: "slug-validator";
134
153
  validate: (val: string) => boolean;
135
154
  message: string;
@@ -15,7 +15,9 @@
15
15
  * isValidSlug('hello_world') // false (underscore not allowed)
16
16
  * ```
17
17
  */
18
- declare function isValidSlug(slug: string): boolean;
18
+ declare function isValidSlug(slug: string, options?: {
19
+ allowDots?: boolean;
20
+ }): boolean;
19
21
  /**
20
22
  * Converts a string to a URL-safe slug
21
23
  * - Converts to lowercase
@@ -42,6 +44,7 @@ declare function isValidSlug(slug: string): boolean;
42
44
  */
43
45
  declare function convertToSlug(text: string, options?: {
44
46
  separator?: string;
47
+ allowDots?: boolean;
45
48
  }): string;
46
49
  /**
47
50
  * Generates a unique slug by appending a number if the slug already exists
@@ -66,7 +69,7 @@ declare function generateUniqueSlug(baseSlug: string, existingSlugs: string[], o
66
69
  * Creates a Zod refinement function for slug validation
67
70
  * Use with z.string().refine() or z.string().superRefine()
68
71
  *
69
- * @param message - Custom error message (optional)
72
+ * @param optionsOrMessage - Custom error message or options object
70
73
  * @returns Refinement function for Zod
71
74
  *
72
75
  * @example
@@ -74,20 +77,32 @@ declare function generateUniqueSlug(baseSlug: string, existingSlugs: string[], o
74
77
  * import { z } from 'zod';
75
78
  * import { zodSlugValidation } from '@digicroz/js-kit';
76
79
  *
80
+ * // Basic usage
77
81
  * const schema = z.object({
78
82
  * slug: z.string().refine(zodSlugValidation(), {
79
83
  * message: 'Invalid slug format'
80
84
  * })
81
85
  * });
86
+ *
87
+ * // Allow dots (e.g. for filenames)
88
+ * const fileSchema = z.object({
89
+ * filename: z.string().refine(zodSlugValidation({ allowDots: true }), {
90
+ * message: 'Invalid filename format'
91
+ * })
92
+ * });
82
93
  * ```
83
94
  */
84
- declare function zodSlugValidation(message?: string): (val: string) => boolean;
95
+ declare function zodSlugValidation(optionsOrMessage?: string | {
96
+ message?: string;
97
+ allowDots?: boolean;
98
+ }): (val: string) => boolean;
85
99
  /**
86
100
  * Creates a Zod transform function that converts strings to slugs
87
101
  * Use with z.string().transform()
88
102
  *
89
103
  * @param options - Optional configuration
90
104
  * @param options.separator - Character to use as separator (default: '-')
105
+ * @param options.allowDots - Whether to allow dots in the slug (default: false)
91
106
  * @returns Transform function for Zod
92
107
  *
93
108
  * @example
@@ -106,6 +121,7 @@ declare function zodSlugValidation(message?: string): (val: string) => boolean;
106
121
  */
107
122
  declare function zodSlugTransform(options?: {
108
123
  separator?: string;
124
+ allowDots?: boolean;
109
125
  }): (val: string) => string;
110
126
  /**
111
127
  * Pre-configured Zod schema for slug validation
@@ -129,7 +145,10 @@ declare const slugSchema: {
129
145
  * Get a Zod string schema that validates slug format
130
146
  * Requires zod to be installed: npm install zod
131
147
  */
132
- create: (customMessage?: string) => {
148
+ create: (customMessageOrOptions?: string | {
149
+ message?: string;
150
+ allowDots?: boolean;
151
+ }) => {
133
152
  _type: "slug-validator";
134
153
  validate: (val: string) => boolean;
135
154
  message: string;
@@ -1,17 +1,36 @@
1
1
  // src/slug/index.ts
2
- function isValidSlug(slug) {
2
+ function isValidSlug(slug, options = {}) {
3
3
  if (!slug || typeof slug !== "string") {
4
4
  return false;
5
5
  }
6
+ const { allowDots = false } = options;
7
+ if (allowDots) {
8
+ const slugWithDotsPattern = /^[a-z0-9]+(?:[-.][a-z0-9]+)*$/;
9
+ return slugWithDotsPattern.test(slug);
10
+ }
6
11
  const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
7
12
  return slugPattern.test(slug);
8
13
  }
9
14
  function convertToSlug(text, options = {}) {
10
- const { separator = "-" } = options;
15
+ const { separator = "-", allowDots = false } = options;
11
16
  if (!text || typeof text !== "string") {
12
17
  return "";
13
18
  }
14
- return text.toString().toLowerCase().trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[\s_]+/g, separator).replace(new RegExp(`[^a-z0-9${separator}]`, "g"), "").replace(new RegExp(`${separator}+`, "g"), separator).replace(new RegExp(`^${separator}+|${separator}+$`, "g"), "");
19
+ const escapedSeparator = separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20
+ let slug = text.toString().toLowerCase().trim().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
21
+ if (!allowDots) {
22
+ slug = slug.replace(/\./g, separator);
23
+ }
24
+ slug = slug.replace(/[\s_]+/g, separator).replace(new RegExp(`[^a-z0-9${escapedSeparator}${allowDots ? "\\." : ""}]`, "g"), "");
25
+ if (allowDots) {
26
+ slug = slug.replace(new RegExp(`[${escapedSeparator}.]+`, "g"), (match) => {
27
+ return match.includes(".") ? "." : separator;
28
+ });
29
+ } else {
30
+ slug = slug.replace(new RegExp(`${escapedSeparator}+`, "g"), separator);
31
+ }
32
+ const trimRegex = new RegExp(`^[${escapedSeparator}${allowDots ? "\\." : ""}]+|[${escapedSeparator}${allowDots ? "\\." : ""}]+$`, "g");
33
+ return slug.replace(trimRegex, "");
15
34
  }
16
35
  function generateUniqueSlug(baseSlug, existingSlugs, options = {}) {
17
36
  const { separator = "-" } = options;
@@ -26,8 +45,9 @@ function generateUniqueSlug(baseSlug, existingSlugs, options = {}) {
26
45
  }
27
46
  return uniqueSlug;
28
47
  }
29
- function zodSlugValidation(message) {
30
- return (val) => isValidSlug(val);
48
+ function zodSlugValidation(optionsOrMessage) {
49
+ const options = typeof optionsOrMessage === "string" ? { } : optionsOrMessage || {};
50
+ return (val) => isValidSlug(val, { allowDots: options.allowDots });
31
51
  }
32
52
  function zodSlugTransform(options) {
33
53
  return (val) => convertToSlug(val, options);
@@ -37,11 +57,13 @@ var slugSchema = {
37
57
  * Get a Zod string schema that validates slug format
38
58
  * Requires zod to be installed: npm install zod
39
59
  */
40
- create: (customMessage) => {
60
+ create: (customMessageOrOptions) => {
61
+ const options = typeof customMessageOrOptions === "string" ? { message: customMessageOrOptions } : customMessageOrOptions || {};
62
+ const message = options.message || "Must be a valid slug (lowercase, alphanumeric, and hyphens only, no consecutive hyphens)";
41
63
  return {
42
64
  _type: "slug-validator",
43
- validate: zodSlugValidation(),
44
- message: customMessage || "Must be a valid slug (lowercase, alphanumeric, and hyphens only, no consecutive hyphens)"
65
+ validate: zodSlugValidation(options),
66
+ message
45
67
  };
46
68
  }
47
69
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/slug/index.ts"],"names":[],"mappings":";AAiBO,SAAS,YAAY,IAAA,EAAuB;AACjD,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,IAAA,OAAO,KAAA;AAAA;AAOT,EAAA,MAAM,WAAA,GAAc,4BAAA;AAEpB,EAAA,OAAO,WAAA,CAAY,KAAK,IAAI,CAAA;AAC9B;AA0BO,SAAS,aAAA,CACd,IAAA,EACA,OAAA,GAAkC,EAAC,EAC3B;AACR,EAAA,MAAM,EAAE,SAAA,GAAY,GAAA,EAAI,GAAI,OAAA;AAE5B,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,IAAA,OAAO,EAAA;AAAA;AAGT,EAAA,OAAO,IAAA,CACJ,UAAS,CACT,WAAA,GACA,IAAA,EAAK,CAEL,SAAA,CAAU,KAAK,CAAA,CACf,OAAA,CAAQ,oBAAoB,EAAE,CAAA,CAE9B,QAAQ,SAAA,EAAW,SAAS,EAE5B,OAAA,CAAQ,IAAI,MAAA,CAAO,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAA,EAAK,GAAG,CAAA,EAAG,EAAE,EAEpD,OAAA,CAAQ,IAAI,OAAO,CAAA,EAAG,SAAS,CAAA,CAAA,CAAA,EAAK,GAAG,CAAA,EAAG,SAAS,EAEnD,OAAA,CAAQ,IAAI,OAAO,CAAA,CAAA,EAAI,SAAS,KAAK,SAAS,CAAA,EAAA,CAAA,EAAM,GAAG,CAAA,EAAG,EAAE,CAAA;AACjE;AAkBO,SAAS,kBAAA,CACd,QAAA,EACA,aAAA,EACA,OAAA,GAAkC,EAAC,EAC3B;AACR,EAAA,MAAM,EAAE,SAAA,GAAY,GAAA,EAAI,GAAI,OAAA;AAE5B,EAAA,IAAI,CAAC,aAAA,CAAc,QAAA,CAAS,QAAQ,CAAA,EAAG;AACrC,IAAA,OAAO,QAAA;AAAA;AAGT,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,aAAa,CAAA,EAAG,QAAQ,CAAA,EAAG,SAAS,GAAG,OAAO,CAAA,CAAA;AAElD,EAAA,OAAO,aAAA,CAAc,QAAA,CAAS,UAAU,CAAA,EAAG;AACzC,IAAA,OAAA,EAAA;AACA,IAAA,UAAA,GAAa,CAAA,EAAG,QAAQ,CAAA,EAAG,SAAS,GAAG,OAAO,CAAA,CAAA;AAAA;AAGhD,EAAA,OAAO,UAAA;AACT;AAqBO,SAAS,kBAAkB,OAAA,EAAkB;AAClD,EAAA,OAAO,CAAC,GAAA,KAAgB,WAAA,CAAY,GAAG,CAAA;AACzC;AAwBO,SAAS,iBAAiB,OAAA,EAAkC;AACjE,EAAA,OAAO,CAAC,GAAA,KAAgB,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACpD;AAmBO,IAAM,UAAA,GAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxB,MAAA,EAAQ,CAAC,aAAA,KAA2B;AAElC,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,gBAAA;AAAA,MACP,QAAA,EAAU,kBAA+B,CAAA;AAAA,MACzC,SAAS,aAAA,IAAiB;AAAA,KAC5B;AAAA;AAEJ;AAoBO,IAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA,EAI5B,SAAA,EAAW,CAAC,OAAA,KAAqC,gBAAA,CAAiB,OAAO;AAC3E","file":"index.js","sourcesContent":["/**\r\n * Checks if a string is a valid slug\r\n * A valid slug contains only lowercase alphanumeric characters and hyphens,\r\n * with no consecutive hyphens and no leading/trailing hyphens\r\n * \r\n * @param slug - The string to validate\r\n * @returns True if the string is a valid slug, false otherwise\r\n * \r\n * @example\r\n * ```ts\r\n * isValidSlug('hello-world') // true\r\n * isValidSlug('hello--world') // false (consecutive hyphens)\r\n * isValidSlug('Hello-World') // false (uppercase)\r\n * isValidSlug('-hello-world') // false (leading hyphen)\r\n * isValidSlug('hello_world') // false (underscore not allowed)\r\n * ```\r\n */\r\nexport function isValidSlug(slug: string): boolean {\r\n if (!slug || typeof slug !== 'string') {\r\n return false;\r\n }\r\n\r\n // Check if slug matches the pattern:\r\n // - starts with alphanumeric\r\n // - ends with alphanumeric\r\n // - contains only lowercase alphanumeric and single hyphens\r\n const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\r\n \r\n return slugPattern.test(slug);\r\n}\r\n\r\n/**\r\n * Converts a string to a URL-safe slug\r\n * - Converts to lowercase\r\n * - Removes special characters\r\n * - Replaces spaces and underscores with hyphens\r\n * - Removes consecutive hyphens\r\n * - Trims leading and trailing hyphens\r\n * \r\n * @param text - The string to convert to a slug\r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use as separator (default: '-')\r\n * @returns A URL-safe slug\r\n * \r\n * @example\r\n * ```ts\r\n * convertToSlug('Hello World') // 'hello-world'\r\n * convertToSlug('Hello World!!!') // 'hello-world'\r\n * convertToSlug('Hello_World') // 'hello-world'\r\n * convertToSlug(' Hello World ') // 'hello-world'\r\n * convertToSlug('Hello---World') // 'hello-world'\r\n * convertToSlug('Café & Restaurant') // 'cafe-restaurant'\r\n * convertToSlug('Product #123') // 'product-123'\r\n * ```\r\n */\r\nexport function convertToSlug(\r\n text: string,\r\n options: { separator?: string } = {}\r\n): string {\r\n const { separator = '-' } = options;\r\n\r\n if (!text || typeof text !== 'string') {\r\n return '';\r\n }\r\n\r\n return text\r\n .toString()\r\n .toLowerCase()\r\n .trim()\r\n // Remove accents and diacritics\r\n .normalize('NFD')\r\n .replace(/[\\u0300-\\u036f]/g, '')\r\n // Replace spaces, underscores, and other common separators with separator\r\n .replace(/[\\s_]+/g, separator)\r\n // Remove all non-alphanumeric characters except the separator\r\n .replace(new RegExp(`[^a-z0-9${separator}]`, 'g'), '')\r\n // Replace multiple consecutive separators with single separator\r\n .replace(new RegExp(`${separator}+`, 'g'), separator)\r\n // Remove leading and trailing separators\r\n .replace(new RegExp(`^${separator}+|${separator}+$`, 'g'), '');\r\n}\r\n\r\n/**\r\n * Generates a unique slug by appending a number if the slug already exists\r\n * \r\n * @param baseSlug - The base slug to make unique\r\n * @param existingSlugs - Array of existing slugs to check against\r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use before the number (default: '-')\r\n * @returns A unique slug\r\n * \r\n * @example\r\n * ```ts\r\n * generateUniqueSlug('hello-world', ['hello-world']) // 'hello-world-1'\r\n * generateUniqueSlug('hello-world', ['hello-world', 'hello-world-1']) // 'hello-world-2'\r\n * generateUniqueSlug('hello-world', []) // 'hello-world'\r\n * ```\r\n */\r\nexport function generateUniqueSlug(\r\n baseSlug: string,\r\n existingSlugs: string[],\r\n options: { separator?: string } = {}\r\n): string {\r\n const { separator = '-' } = options;\r\n\r\n if (!existingSlugs.includes(baseSlug)) {\r\n return baseSlug;\r\n }\r\n\r\n let counter = 1;\r\n let uniqueSlug = `${baseSlug}${separator}${counter}`;\r\n\r\n while (existingSlugs.includes(uniqueSlug)) {\r\n counter++;\r\n uniqueSlug = `${baseSlug}${separator}${counter}`;\r\n }\r\n\r\n return uniqueSlug;\r\n}\r\n\r\n/**\r\n * Creates a Zod refinement function for slug validation\r\n * Use with z.string().refine() or z.string().superRefine()\r\n * \r\n * @param message - Custom error message (optional)\r\n * @returns Refinement function for Zod\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { zodSlugValidation } from '@digicroz/js-kit';\r\n * \r\n * const schema = z.object({\r\n * slug: z.string().refine(zodSlugValidation(), {\r\n * message: 'Invalid slug format'\r\n * })\r\n * });\r\n * ```\r\n */\r\nexport function zodSlugValidation(message?: string) {\r\n return (val: string) => isValidSlug(val);\r\n}\r\n\r\n/**\r\n * Creates a Zod transform function that converts strings to slugs\r\n * Use with z.string().transform()\r\n * \r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use as separator (default: '-')\r\n * @returns Transform function for Zod\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { zodSlugTransform } from '@digicroz/js-kit';\r\n * \r\n * const schema = z.object({\r\n * title: z.string(),\r\n * slug: z.string().transform(zodSlugTransform())\r\n * });\r\n * \r\n * schema.parse({ title: 'Hello', slug: 'Hello World!!!' })\r\n * // { title: 'Hello', slug: 'hello-world' }\r\n * ```\r\n */\r\nexport function zodSlugTransform(options?: { separator?: string }) {\r\n return (val: string) => convertToSlug(val, options);\r\n}\r\n\r\n/**\r\n * Pre-configured Zod schema for slug validation\r\n * Validates that the string is a valid slug format\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { slugSchema } from '@digicroz/js-kit';\r\n * \r\n * const postSchema = z.object({\r\n * slug: slugSchema\r\n * });\r\n * \r\n * postSchema.parse({ slug: 'hello-world' }); // ✓ Valid\r\n * postSchema.parse({ slug: 'Hello World' }); // ✗ Invalid\r\n * ```\r\n */\r\nexport const slugSchema = {\r\n /**\r\n * Get a Zod string schema that validates slug format\r\n * Requires zod to be installed: npm install zod\r\n */\r\n create: (customMessage?: string) => {\r\n // Dynamic import to avoid making zod a required dependency\r\n return {\r\n _type: 'slug-validator' as const,\r\n validate: zodSlugValidation(customMessage),\r\n message: customMessage || 'Must be a valid slug (lowercase, alphanumeric, and hyphens only, no consecutive hyphens)'\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * Pre-configured Zod schema that auto-converts strings to slugs\r\n * Automatically transforms any string input into a valid slug\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { autoSlugSchema } from '@digicroz/js-kit';\r\n * \r\n * const postSchema = z.object({\r\n * title: z.string(),\r\n * slug: z.string().transform(autoSlugSchema.transform())\r\n * });\r\n * \r\n * postSchema.parse({ title: 'My Post', slug: 'Hello World!!!' })\r\n * // { title: 'My Post', slug: 'hello-world' }\r\n * ```\r\n */\r\nexport const autoSlugSchema = {\r\n /**\r\n * Get a transform function for Zod\r\n */\r\n transform: (options?: { separator?: string }) => zodSlugTransform(options)\r\n};\r\n"]}
1
+ {"version":3,"sources":["../../src/slug/index.ts"],"names":[],"mappings":";AAiBO,SAAS,WAAA,CACd,IAAA,EACA,OAAA,GAAmC,EAAC,EAC3B;AACT,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,IAAA,OAAO,KAAA;AAAA;AAGT,EAAA,MAAM,EAAE,SAAA,GAAY,KAAA,EAAM,GAAI,OAAA;AAQ9B,EAAA,IAAI,SAAA,EAAW;AAKb,IAAA,MAAM,mBAAA,GAAsB,+BAAA;AAC5B,IAAA,OAAO,mBAAA,CAAoB,KAAK,IAAI,CAAA;AAAA;AAGtC,EAAA,MAAM,WAAA,GAAc,4BAAA;AAEpB,EAAA,OAAO,WAAA,CAAY,KAAK,IAAI,CAAA;AAC9B;AA0BO,SAAS,aAAA,CACd,IAAA,EACA,OAAA,GAAuD,EAAC,EAChD;AACR,EAAA,MAAM,EAAE,SAAA,GAAY,GAAA,EAAK,SAAA,GAAY,OAAM,GAAI,OAAA;AAE/C,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,IAAA,OAAO,EAAA;AAAA;AAIT,EAAA,MAAM,gBAAA,GAAmB,SAAA,CAAU,OAAA,CAAQ,qBAAA,EAAuB,MAAM,CAAA;AAExE,EAAA,IAAI,IAAA,GAAO,IAAA,CACR,QAAA,EAAS,CACT,WAAA,EAAY,CACZ,IAAA,EAAK,CAEL,SAAA,CAAU,KAAK,CAAA,CACf,OAAA,CAAQ,oBAAoB,EAAE,CAAA;AAEjC,EAAA,IAAI,CAAC,SAAA,EAAW;AAEd,IAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,SAAS,CAAA;AAAA;AAGtC,EAAA,IAAA,GAAO,KAEJ,OAAA,CAAQ,SAAA,EAAW,SAAS,CAAA,CAE5B,QAAQ,IAAI,MAAA,CAAO,CAAA,QAAA,EAAW,gBAAgB,GAAG,SAAA,GAAY,KAAA,GAAQ,EAAE,CAAA,CAAA,CAAA,EAAK,GAAG,GAAG,EAAE,CAAA;AAEvF,EAAA,IAAI,SAAA,EAAW;AAGb,IAAA,IAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,IAAI,MAAA,CAAO,CAAA,CAAA,EAAI,gBAAgB,CAAA,GAAA,CAAA,EAAO,GAAG,CAAA,EAAG,CAAC,KAAA,KAAU;AACzE,MAAA,OAAO,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA,GAAI,GAAA,GAAM,SAAA;AAAA,KACpC,CAAA;AAAA,GACH,MAAO;AAEL,IAAA,IAAA,GAAO,IAAA,CAAK,QAAQ,IAAI,MAAA,CAAO,GAAG,gBAAgB,CAAA,CAAA,CAAA,EAAK,GAAG,CAAA,EAAG,SAAS,CAAA;AAAA;AAIxE,EAAA,MAAM,YAAY,IAAI,MAAA,CAAO,CAAA,EAAA,EAAK,gBAAgB,GAAG,SAAA,GAAY,KAAA,GAAQ,EAAE,CAAA,IAAA,EAAO,gBAAgB,CAAA,EAAG,SAAA,GAAY,KAAA,GAAQ,EAAE,OAAO,GAAG,CAAA;AACrI,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,SAAA,EAAW,EAAE,CAAA;AACnC;AAkBO,SAAS,kBAAA,CACd,QAAA,EACA,aAAA,EACA,OAAA,GAAkC,EAAC,EAC3B;AACR,EAAA,MAAM,EAAE,SAAA,GAAY,GAAA,EAAI,GAAI,OAAA;AAE5B,EAAA,IAAI,CAAC,aAAA,CAAc,QAAA,CAAS,QAAQ,CAAA,EAAG;AACrC,IAAA,OAAO,QAAA;AAAA;AAGT,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,aAAa,CAAA,EAAG,QAAQ,CAAA,EAAG,SAAS,GAAG,OAAO,CAAA,CAAA;AAElD,EAAA,OAAO,aAAA,CAAc,QAAA,CAAS,UAAU,CAAA,EAAG;AACzC,IAAA,OAAA,EAAA;AACA,IAAA,UAAA,GAAa,CAAA,EAAG,QAAQ,CAAA,EAAG,SAAS,GAAG,OAAO,CAAA,CAAA;AAAA;AAGhD,EAAA,OAAO,UAAA;AACT;AA6BO,SAAS,kBACd,gBAAA,EACA;AACA,EAAA,MAAM,OAAA,GACJ,OAAO,gBAAA,KAAqB,QAAA,GACxB,EAA4B,CAAA,GAC5B,gBAAA,IAAoB,EAAC;AAE3B,EAAA,OAAO,CAAC,QAAgB,WAAA,CAAY,GAAA,EAAK,EAAE,SAAA,EAAW,OAAA,CAAQ,WAAW,CAAA;AAC3E;AAyBO,SAAS,iBAAiB,OAAA,EAAuD;AACtF,EAAA,OAAO,CAAC,GAAA,KAAgB,aAAA,CAAc,GAAA,EAAK,OAAO,CAAA;AACpD;AAmBO,IAAM,UAAA,GAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKxB,MAAA,EAAQ,CACN,sBAAA,KACG;AAEH,IAAA,MAAM,OAAA,GACJ,OAAO,sBAAA,KAA2B,QAAA,GAC9B,EAAE,OAAA,EAAS,sBAAA,EAAuB,GAClC,sBAAA,IAA0B,EAAC;AAEjC,IAAA,MAAM,OAAA,GACJ,QAAQ,OAAA,IACR,0FAAA;AAEF,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,gBAAA;AAAA,MACP,QAAA,EAAU,kBAAkB,OAAO,CAAA;AAAA,MACnC;AAAA,KACF;AAAA;AAEJ;AAoBO,IAAM,cAAA,GAAiB;AAAA;AAAA;AAAA;AAAA,EAI5B,SAAA,EAAW,CAAC,OAAA,KAAqC,gBAAA,CAAiB,OAAO;AAC3E","file":"index.js","sourcesContent":["/**\r\n * Checks if a string is a valid slug\r\n * A valid slug contains only lowercase alphanumeric characters and hyphens,\r\n * with no consecutive hyphens and no leading/trailing hyphens\r\n * \r\n * @param slug - The string to validate\r\n * @returns True if the string is a valid slug, false otherwise\r\n * \r\n * @example\r\n * ```ts\r\n * isValidSlug('hello-world') // true\r\n * isValidSlug('hello--world') // false (consecutive hyphens)\r\n * isValidSlug('Hello-World') // false (uppercase)\r\n * isValidSlug('-hello-world') // false (leading hyphen)\r\n * isValidSlug('hello_world') // false (underscore not allowed)\r\n * ```\r\n */\r\nexport function isValidSlug(\r\n slug: string,\r\n options: { allowDots?: boolean } = {}\r\n): boolean {\r\n if (!slug || typeof slug !== 'string') {\r\n return false;\r\n }\r\n\r\n const { allowDots = false } = options;\r\n\r\n // Check if slug matches the pattern:\r\n // - starts with alphanumeric\r\n // - ends with alphanumeric\r\n // - contains only lowercase alphanumeric and single hyphens (and dots if allowed)\r\n // - no consecutive separators (hyphens or dots)\r\n \r\n if (allowDots) {\r\n // pattern allowing dots:\r\n // starts with alphanumeric\r\n // middle: alphanumeric or single hyphen/dot followed by alphanumeric\r\n // ends with alphanumeric\r\n const slugWithDotsPattern = /^[a-z0-9]+(?:[-.][a-z0-9]+)*$/;\r\n return slugWithDotsPattern.test(slug);\r\n }\r\n\r\n const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\r\n \r\n return slugPattern.test(slug);\r\n}\r\n\r\n/**\r\n * Converts a string to a URL-safe slug\r\n * - Converts to lowercase\r\n * - Removes special characters\r\n * - Replaces spaces and underscores with hyphens\r\n * - Removes consecutive hyphens\r\n * - Trims leading and trailing hyphens\r\n * \r\n * @param text - The string to convert to a slug\r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use as separator (default: '-')\r\n * @returns A URL-safe slug\r\n * \r\n * @example\r\n * ```ts\r\n * convertToSlug('Hello World') // 'hello-world'\r\n * convertToSlug('Hello World!!!') // 'hello-world'\r\n * convertToSlug('Hello_World') // 'hello-world'\r\n * convertToSlug(' Hello World ') // 'hello-world'\r\n * convertToSlug('Hello---World') // 'hello-world'\r\n * convertToSlug('Café & Restaurant') // 'cafe-restaurant'\r\n * convertToSlug('Product #123') // 'product-123'\r\n * ```\r\n */\r\nexport function convertToSlug(\r\n text: string,\r\n options: { separator?: string; allowDots?: boolean } = {}\r\n): string {\r\n const { separator = '-', allowDots = false } = options;\r\n\r\n if (!text || typeof text !== 'string') {\r\n return '';\r\n }\r\n\r\n // Escape separator for use in regex char class\r\n const escapedSeparator = separator.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n\r\n let slug = text\r\n .toString()\r\n .toLowerCase()\r\n .trim()\r\n // Remove accents and diacritics\r\n .normalize('NFD')\r\n .replace(/[\\u0300-\\u036f]/g, '');\r\n\r\n if (!allowDots) {\r\n // Replace dots with separator\r\n slug = slug.replace(/\\./g, separator);\r\n }\r\n\r\n slug = slug\r\n // Replace spaces, underscores with separator\r\n .replace(/[\\s_]+/g, separator)\r\n // Remove all non-alphanumeric characters except the separator (and dot if allowed)\r\n .replace(new RegExp(`[^a-z0-9${escapedSeparator}${allowDots ? '\\\\.' : ''}]`, 'g'), '');\r\n\r\n if (allowDots) {\r\n // Collapse consecutive separators/dots\r\n // If sequence contains a dot, we generally want to keep it as a dot (e.g. file.-extension -> file.extension)\r\n slug = slug.replace(new RegExp(`[${escapedSeparator}.]+`, 'g'), (match) => {\r\n return match.includes('.') ? '.' : separator;\r\n });\r\n } else {\r\n // Replace multiple consecutive separators with single separator\r\n slug = slug.replace(new RegExp(`${escapedSeparator}+`, 'g'), separator);\r\n }\r\n \r\n // Remove leading and trailing separators (and dots)\r\n const trimRegex = new RegExp(`^[${escapedSeparator}${allowDots ? '\\\\.' : ''}]+|[${escapedSeparator}${allowDots ? '\\\\.' : ''}]+$`, 'g');\r\n return slug.replace(trimRegex, '');\r\n}\r\n\r\n/**\r\n * Generates a unique slug by appending a number if the slug already exists\r\n * \r\n * @param baseSlug - The base slug to make unique\r\n * @param existingSlugs - Array of existing slugs to check against\r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use before the number (default: '-')\r\n * @returns A unique slug\r\n * \r\n * @example\r\n * ```ts\r\n * generateUniqueSlug('hello-world', ['hello-world']) // 'hello-world-1'\r\n * generateUniqueSlug('hello-world', ['hello-world', 'hello-world-1']) // 'hello-world-2'\r\n * generateUniqueSlug('hello-world', []) // 'hello-world'\r\n * ```\r\n */\r\nexport function generateUniqueSlug(\r\n baseSlug: string,\r\n existingSlugs: string[],\r\n options: { separator?: string } = {}\r\n): string {\r\n const { separator = '-' } = options;\r\n\r\n if (!existingSlugs.includes(baseSlug)) {\r\n return baseSlug;\r\n }\r\n\r\n let counter = 1;\r\n let uniqueSlug = `${baseSlug}${separator}${counter}`;\r\n\r\n while (existingSlugs.includes(uniqueSlug)) {\r\n counter++;\r\n uniqueSlug = `${baseSlug}${separator}${counter}`;\r\n }\r\n\r\n return uniqueSlug;\r\n}\r\n\r\n/**\r\n * Creates a Zod refinement function for slug validation\r\n * Use with z.string().refine() or z.string().superRefine()\r\n * \r\n * @param optionsOrMessage - Custom error message or options object\r\n * @returns Refinement function for Zod\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { zodSlugValidation } from '@digicroz/js-kit';\r\n * \r\n * // Basic usage\r\n * const schema = z.object({\r\n * slug: z.string().refine(zodSlugValidation(), {\r\n * message: 'Invalid slug format'\r\n * })\r\n * });\r\n * \r\n * // Allow dots (e.g. for filenames)\r\n * const fileSchema = z.object({\r\n * filename: z.string().refine(zodSlugValidation({ allowDots: true }), {\r\n * message: 'Invalid filename format'\r\n * })\r\n * });\r\n * ```\r\n */\r\nexport function zodSlugValidation(\r\n optionsOrMessage?: string | { message?: string; allowDots?: boolean }\r\n) {\r\n const options =\r\n typeof optionsOrMessage === 'string'\r\n ? { message: optionsOrMessage }\r\n : optionsOrMessage || {};\r\n \r\n return (val: string) => isValidSlug(val, { allowDots: options.allowDots });\r\n}\r\n\r\n/**\r\n * Creates a Zod transform function that converts strings to slugs\r\n * Use with z.string().transform()\r\n * \r\n * @param options - Optional configuration\r\n * @param options.separator - Character to use as separator (default: '-')\r\n * @param options.allowDots - Whether to allow dots in the slug (default: false)\r\n * @returns Transform function for Zod\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { zodSlugTransform } from '@digicroz/js-kit';\r\n * \r\n * const schema = z.object({\r\n * title: z.string(),\r\n * slug: z.string().transform(zodSlugTransform())\r\n * });\r\n * \r\n * schema.parse({ title: 'Hello', slug: 'Hello World!!!' })\r\n * // { title: 'Hello', slug: 'hello-world' }\r\n * ```\r\n */\r\nexport function zodSlugTransform(options?: { separator?: string; allowDots?: boolean }) {\r\n return (val: string) => convertToSlug(val, options);\r\n}\r\n\r\n/**\r\n * Pre-configured Zod schema for slug validation\r\n * Validates that the string is a valid slug format\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { slugSchema } from '@digicroz/js-kit';\r\n * \r\n * const postSchema = z.object({\r\n * slug: slugSchema\r\n * });\r\n * \r\n * postSchema.parse({ slug: 'hello-world' }); // ✓ Valid\r\n * postSchema.parse({ slug: 'Hello World' }); // ✗ Invalid\r\n * ```\r\n */\r\nexport const slugSchema = {\r\n /**\r\n * Get a Zod string schema that validates slug format\r\n * Requires zod to be installed: npm install zod\r\n */\r\n create: (\r\n customMessageOrOptions?: string | { message?: string; allowDots?: boolean }\r\n ) => {\r\n // Dynamic import to avoid making zod a required dependency\r\n const options =\r\n typeof customMessageOrOptions === 'string'\r\n ? { message: customMessageOrOptions }\r\n : customMessageOrOptions || {};\r\n \r\n const message =\r\n options.message ||\r\n 'Must be a valid slug (lowercase, alphanumeric, and hyphens only, no consecutive hyphens)';\r\n\r\n return {\r\n _type: 'slug-validator' as const,\r\n validate: zodSlugValidation(options),\r\n message\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * Pre-configured Zod schema that auto-converts strings to slugs\r\n * Automatically transforms any string input into a valid slug\r\n * \r\n * @example\r\n * ```ts\r\n * import { z } from 'zod';\r\n * import { autoSlugSchema } from '@digicroz/js-kit';\r\n * \r\n * const postSchema = z.object({\r\n * title: z.string(),\r\n * slug: z.string().transform(autoSlugSchema.transform())\r\n * });\r\n * \r\n * postSchema.parse({ title: 'My Post', slug: 'Hello World!!!' })\r\n * // { title: 'My Post', slug: 'hello-world' }\r\n * ```\r\n */\r\nexport const autoSlugSchema = {\r\n /**\r\n * Get a transform function for Zod\r\n */\r\n transform: (options?: { separator?: string }) => zodSlugTransform(options)\r\n};\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digicroz/js-kit",
3
- "version": "1.0.6",
3
+ "version": "1.0.10",
4
4
  "description": "Modern TypeScript utility library with tree-shaking support - Array, String, Number, Sleep, and Time utilities for JavaScript and TypeScript projects",
5
5
  "keywords": [
6
6
  "typescript",