@kumix/utils 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as NODE_ENV, c as isProduction, i as LOG_LEVEL, l as isStaging, n as API_PORT, o as PORT, r as DEBUG, s as isDevelopment, t as logger, u as getEnv } from "./logger-BOB35dQN.js";
1
+ import { a as NODE_ENV, c as isProduction, d as getEnv, i as LOG_LEVEL, l as isStaging, n as API_PORT, o as PORT, r as DEBUG, s as isDevelopment, t as logger, u as isTest } from "./logger-qDWnDXis.js";
2
2
  import slugify, { default as slugify$1 } from "@sindresorhus/slugify";
3
3
  import baseX from "base-x";
4
4
  import { createId, init } from "@paralleldrive/cuid2";
@@ -551,31 +551,30 @@ const EU_COUNTRY_CODES = [
551
551
  "AT",
552
552
  "BE",
553
553
  "BG",
554
+ "HR",
554
555
  "CY",
555
556
  "CZ",
556
- "DE",
557
557
  "DK",
558
558
  "EE",
559
- "ES",
560
559
  "FI",
561
560
  "FR",
562
- "GB",
561
+ "DE",
563
562
  "GR",
564
- "HR",
565
563
  "HU",
566
564
  "IE",
567
565
  "IT",
566
+ "LV",
568
567
  "LT",
569
568
  "LU",
570
- "LV",
571
569
  "MT",
572
570
  "NL",
573
571
  "PL",
574
572
  "PT",
575
573
  "RO",
576
- "SE",
574
+ "SK",
577
575
  "SI",
578
- "SK"
576
+ "ES",
577
+ "SE"
579
578
  ];
580
579
  //#endregion
581
580
  //#region src/constants/country/country-phone-codes.ts
@@ -1149,7 +1148,7 @@ const REGIONS = {
1149
1148
  "BY-VI": "Vitsyebskaya voblasts'",
1150
1149
  "BE-BRU": "Brussels Hoofdstedelijk Gewest",
1151
1150
  "BE-VLG": "Vlaams Gewest",
1152
- "BE-WAL": "Waals Gewest[note 2]",
1151
+ "BE-WAL": "Waals Gewest",
1153
1152
  "BZ-BZ": "Belize",
1154
1153
  "BZ-CY": "Cayo",
1155
1154
  "BZ-CZL": "Corozal",
@@ -1981,7 +1980,7 @@ const REGIONS = {
1981
1980
  "IN-BR": "Bihār",
1982
1981
  "IN-CH": "Chandīgarh",
1983
1982
  "IN-CG": "Chhattīsgarh",
1984
- "IN-DH": "Dādra and Nagar Haveli and Damān and Diu[1]",
1983
+ "IN-DH": "Dādra and Nagar Haveli and Damān and Diu",
1985
1984
  "IN-DL": "Delhi",
1986
1985
  "IN-GA": "Goa",
1987
1986
  "IN-GJ": "Gujarāt",
@@ -2005,7 +2004,7 @@ const REGIONS = {
2005
2004
  "IN-RJ": "Rājasthān",
2006
2005
  "IN-SK": "Sikkim",
2007
2006
  "IN-TN": "Tamil Nādu",
2008
- "IN-TS": "Telangāna[2]",
2007
+ "IN-TS": "Telangāna",
2009
2008
  "IN-TR": "Tripura",
2010
2009
  "IN-UP": "Uttar Pradesh",
2011
2010
  "IN-UK": "Uttarākhand",
@@ -2070,7 +2069,7 @@ const REGIONS = {
2070
2069
  "IE-C": "Connaught",
2071
2070
  "IE-L": "Leinster",
2072
2071
  "IE-M": "Munster",
2073
- "IE-U": "Ulster[a]",
2072
+ "IE-U": "Ulster",
2074
2073
  "IL-D": "HaDarom",
2075
2074
  "IL-M": "HaMerkaz",
2076
2075
  "IL-Z": "HaTsafon",
@@ -3084,7 +3083,7 @@ const REGIONS = {
3084
3083
  "PE-TAC": "Tacna",
3085
3084
  "PE-TUM": "Tumbes",
3086
3085
  "PE-UCA": "Ucayali",
3087
- "PH-14": "Autonomous Region in Muslim Mindanao[b]",
3086
+ "PH-14": "Autonomous Region in Muslim Mindanao",
3088
3087
  "PH-05": "Bicol",
3089
3088
  "PH-02": "Cagayan Valley",
3090
3089
  "PH-40": "Calabarzon",
@@ -4366,7 +4365,7 @@ const REGIONS = {
4366
4365
  "ME-20": "Ulcinj",
4367
4366
  "ME-21": "Žabljak",
4368
4367
  "ME-25": "Zeta",
4369
- "RS-KM": "Kosovo-Metohija[1]",
4368
+ "RS-KM": "Kosovo-Metohija",
4370
4369
  "RS-VO": "Vojvodina",
4371
4370
  "SS-EC": "Central Equatoria",
4372
4371
  "SS-EE": "Eastern Equatoria",
@@ -4387,8 +4386,12 @@ const REGION_CODES = Object.keys(REGIONS);
4387
4386
  * Provides a comprehensive set of all country-specific domain extensions
4388
4387
  */
4389
4388
  /**
4390
- * A Set containing all country code top-level domains (ccTLDs)
4391
- * Used for domain validation, parsing, and identification
4389
+ * A Set containing ISO 3166-1 alpha-2 country code top-level domains (ccTLDs),
4390
+ * plus a small set of commonly-recognized regional TLDs (`eu`, `eus`, `gal`,
4391
+ * `cat`, `asia`). De-duplicated and pruned of retired/withdrawn codes:
4392
+ * - `an` (Netherlands Antilles, retired 2010)
4393
+ * - `tp` (Timor-Leste, retired 2015 — use `tl`)
4394
+ * `gb` is included alongside `uk` per IANA reservation.
4392
4395
  *
4393
4396
  * @example
4394
4397
  * ```ts
@@ -4401,286 +4404,261 @@ const REGION_CODES = Object.keys(REGIONS);
4401
4404
  * const domain = 'example.co.uk';
4402
4405
  * const tld = domain.split('.').pop();
4403
4406
  * const isCountryDomain = tld ? ccTLDs.has(tld) : false; // true
4404
- *
4405
- * // Filter domains by ccTLD
4406
- * const domains = ['example.com', 'example.co.uk', 'example.org'];
4407
- * const countryDomains = domains.filter(domain => {
4408
- * const tld = domain.split('.').pop();
4409
- * return tld ? ccTLDs.has(tld) : false;
4410
- * }); // ['example.co.uk']
4411
4407
  * ```
4412
4408
  */
4413
4409
  const ccTLDs = /* @__PURE__ */ new Set([
4410
+ "ac",
4411
+ "ad",
4412
+ "ae",
4414
4413
  "af",
4415
- "ax",
4414
+ "ag",
4415
+ "ai",
4416
4416
  "al",
4417
- "dz",
4418
- "as",
4419
- "ad",
4417
+ "am",
4420
4418
  "ao",
4421
- "ai",
4422
4419
  "aq",
4423
- "ag",
4424
4420
  "ar",
4425
- "am",
4426
- "aw",
4427
- "ac",
4428
- "au",
4421
+ "as",
4429
4422
  "at",
4423
+ "au",
4424
+ "aw",
4425
+ "ax",
4430
4426
  "az",
4431
- "bs",
4432
- "bh",
4433
- "bd",
4427
+ "ba",
4434
4428
  "bb",
4435
- "eus",
4436
- "by",
4429
+ "bd",
4437
4430
  "be",
4438
- "bz",
4431
+ "bf",
4432
+ "bg",
4433
+ "bh",
4434
+ "bi",
4439
4435
  "bj",
4436
+ "bl",
4440
4437
  "bm",
4441
- "bt",
4438
+ "bn",
4442
4439
  "bo",
4443
4440
  "bq",
4444
- "an",
4445
- "nl",
4446
- "ba",
4447
- "bw",
4448
- "bv",
4449
4441
  "br",
4450
- "io",
4451
- "vg",
4452
- "bn",
4453
- "bg",
4454
- "bf",
4455
- "mm",
4456
- "bi",
4457
- "kh",
4458
- "cm",
4442
+ "bs",
4443
+ "bt",
4444
+ "bv",
4445
+ "bw",
4446
+ "by",
4447
+ "bz",
4459
4448
  "ca",
4460
- "cv",
4461
4449
  "cat",
4462
- "ky",
4463
- "cf",
4464
- "td",
4465
- "cl",
4466
- "cn",
4467
- "cx",
4468
4450
  "cc",
4469
- "co",
4470
- "km",
4471
4451
  "cd",
4452
+ "cf",
4472
4453
  "cg",
4454
+ "ch",
4455
+ "ci",
4473
4456
  "ck",
4457
+ "cl",
4458
+ "cm",
4459
+ "cn",
4460
+ "co",
4474
4461
  "cr",
4475
- "ci",
4476
- "hr",
4477
4462
  "cu",
4463
+ "cv",
4478
4464
  "cw",
4465
+ "cx",
4479
4466
  "cy",
4480
4467
  "cz",
4481
- "dk",
4468
+ "de",
4482
4469
  "dj",
4470
+ "dk",
4483
4471
  "dm",
4484
4472
  "do",
4485
- "tl",
4486
- "tp",
4473
+ "dz",
4487
4474
  "ec",
4475
+ "ee",
4488
4476
  "eg",
4489
- "sv",
4490
- "gq",
4477
+ "eh",
4491
4478
  "er",
4492
- "ee",
4479
+ "es",
4493
4480
  "et",
4494
4481
  "eu",
4482
+ "eus",
4483
+ "fi",
4484
+ "fj",
4495
4485
  "fk",
4496
- "fo",
4497
4486
  "fm",
4498
- "fj",
4499
- "fi",
4487
+ "fo",
4500
4488
  "fr",
4501
- "gf",
4502
- "pf",
4503
- "tf",
4504
4489
  "ga",
4505
4490
  "gal",
4506
- "gm",
4507
- "ps",
4491
+ "gb",
4492
+ "gd",
4508
4493
  "ge",
4509
- "de",
4494
+ "gf",
4495
+ "gg",
4510
4496
  "gh",
4511
4497
  "gi",
4512
- "gr",
4513
4498
  "gl",
4514
- "gd",
4499
+ "gm",
4500
+ "gn",
4515
4501
  "gp",
4516
- "gu",
4502
+ "gq",
4503
+ "gr",
4504
+ "gs",
4517
4505
  "gt",
4518
- "gg",
4519
- "gn",
4506
+ "gu",
4520
4507
  "gw",
4521
4508
  "gy",
4522
- "ht",
4509
+ "hk",
4523
4510
  "hm",
4524
4511
  "hn",
4525
- "hk",
4512
+ "hr",
4513
+ "ht",
4526
4514
  "hu",
4527
- "is",
4528
- "in",
4529
4515
  "id",
4530
- "ir",
4531
- "iq",
4532
4516
  "ie",
4533
- "im",
4534
4517
  "il",
4518
+ "im",
4519
+ "in",
4520
+ "io",
4521
+ "iq",
4522
+ "ir",
4523
+ "is",
4535
4524
  "it",
4536
- "jm",
4537
- "jp",
4538
4525
  "je",
4526
+ "jm",
4539
4527
  "jo",
4540
- "kz",
4528
+ "jp",
4541
4529
  "ke",
4530
+ "kg",
4531
+ "kh",
4542
4532
  "ki",
4533
+ "km",
4534
+ "kn",
4535
+ "kp",
4536
+ "kr",
4543
4537
  "kw",
4544
- "kg",
4538
+ "ky",
4539
+ "kz",
4545
4540
  "la",
4546
- "lv",
4547
4541
  "lb",
4548
- "ls",
4549
- "lr",
4550
- "ly",
4542
+ "lc",
4551
4543
  "li",
4544
+ "lk",
4545
+ "lr",
4546
+ "ls",
4552
4547
  "lt",
4553
4548
  "lu",
4554
- "mo",
4555
- "mk",
4549
+ "lv",
4550
+ "ly",
4551
+ "ma",
4552
+ "mc",
4553
+ "md",
4554
+ "me",
4555
+ "mf",
4556
4556
  "mg",
4557
- "mw",
4558
- "my",
4559
- "mv",
4560
- "ml",
4561
- "mt",
4562
4557
  "mh",
4558
+ "mk",
4559
+ "ml",
4560
+ "mm",
4561
+ "mn",
4562
+ "mo",
4563
+ "mp",
4563
4564
  "mq",
4564
4565
  "mr",
4566
+ "ms",
4567
+ "mt",
4565
4568
  "mu",
4566
- "yt",
4569
+ "mv",
4570
+ "mw",
4567
4571
  "mx",
4568
- "md",
4569
- "mc",
4570
- "mn",
4571
- "me",
4572
- "ms",
4573
- "ma",
4572
+ "my",
4574
4573
  "mz",
4575
- "mm",
4576
4574
  "na",
4577
- "nr",
4578
- "np",
4579
- "nl",
4580
4575
  "nc",
4581
- "nz",
4582
- "ni",
4583
4576
  "ne",
4584
- "ng",
4585
- "nu",
4586
4577
  "nf",
4587
- "nc",
4588
- "tr",
4589
- "kp",
4590
- "mp",
4578
+ "ng",
4579
+ "ni",
4580
+ "nl",
4591
4581
  "no",
4582
+ "np",
4583
+ "nr",
4584
+ "nu",
4585
+ "nz",
4592
4586
  "om",
4593
- "pk",
4594
- "pw",
4595
- "ps",
4596
4587
  "pa",
4597
- "pg",
4598
- "py",
4599
4588
  "pe",
4589
+ "pf",
4590
+ "pg",
4600
4591
  "ph",
4601
- "pn",
4592
+ "pk",
4602
4593
  "pl",
4603
- "pt",
4594
+ "pm",
4595
+ "pn",
4604
4596
  "pr",
4597
+ "ps",
4598
+ "pt",
4599
+ "pw",
4600
+ "py",
4605
4601
  "qa",
4602
+ "re",
4606
4603
  "ro",
4604
+ "rs",
4607
4605
  "ru",
4608
4606
  "rw",
4609
- "re",
4610
- "bq",
4611
- "an",
4612
- "bl",
4613
- "gp",
4614
- "fr",
4615
- "sh",
4616
- "kn",
4617
- "lc",
4618
- "mf",
4619
- "gp",
4620
- "fr",
4621
- "pm",
4622
- "vc",
4623
- "ws",
4624
- "sm",
4625
- "st",
4626
4607
  "sa",
4627
- "sn",
4628
- "rs",
4608
+ "sb",
4629
4609
  "sc",
4630
- "sl",
4610
+ "sd",
4611
+ "se",
4631
4612
  "sg",
4632
- "bq",
4633
- "an",
4634
- "nl",
4635
- "sx",
4636
- "an",
4637
- "sk",
4613
+ "sh",
4638
4614
  "si",
4639
- "sb",
4640
- "so",
4615
+ "sj",
4616
+ "sk",
4617
+ "sl",
4618
+ "sm",
4619
+ "sn",
4641
4620
  "so",
4642
- "za",
4643
- "gs",
4644
- "kr",
4645
- "ss",
4646
- "es",
4647
- "lk",
4648
- "sd",
4649
4621
  "sr",
4650
- "sj",
4651
- "sz",
4652
- "se",
4653
- "ch",
4622
+ "ss",
4623
+ "st",
4624
+ "sv",
4625
+ "sx",
4654
4626
  "sy",
4655
- "tw",
4656
- "tj",
4657
- "tz",
4658
- "th",
4627
+ "sz",
4628
+ "tc",
4629
+ "td",
4630
+ "tf",
4659
4631
  "tg",
4632
+ "th",
4633
+ "tj",
4660
4634
  "tk",
4661
- "to",
4662
- "tt",
4635
+ "tl",
4636
+ "tm",
4663
4637
  "tn",
4638
+ "to",
4664
4639
  "tr",
4665
- "tm",
4666
- "tc",
4640
+ "tt",
4667
4641
  "tv",
4668
- "ug",
4642
+ "tw",
4643
+ "tz",
4669
4644
  "ua",
4670
- "ae",
4645
+ "ug",
4671
4646
  "uk",
4647
+ "um",
4672
4648
  "us",
4673
- "vi",
4674
4649
  "uy",
4675
4650
  "uz",
4676
- "vu",
4677
4651
  "va",
4652
+ "vc",
4678
4653
  "ve",
4654
+ "vg",
4655
+ "vi",
4679
4656
  "vn",
4657
+ "vu",
4680
4658
  "wf",
4681
- "eh",
4682
- "ma",
4659
+ "ws",
4683
4660
  "ye",
4661
+ "za",
4684
4662
  "zm",
4685
4663
  "zw"
4686
4664
  ]);
@@ -4827,6 +4805,15 @@ const DEFAULT_THEME_MODE = THEME_MODES.SYSTEM;
4827
4805
  */
4828
4806
  const NEXT_PUBLIC_RECAPTCHA_SITE_KEY = getEnv("NEXT_PUBLIC_RECAPTCHA_SITE_KEY");
4829
4807
  /**
4808
+ * Recaptcha secret key for server-side token verification.
4809
+ * Used by verifyRecaptchaToken to call Google's siteverify endpoint.
4810
+ * Must NOT be exposed to the client (no NEXT_PUBLIC prefix).
4811
+ *
4812
+ * @example
4813
+ * const isValid = await verifyRecaptchaToken(token);
4814
+ */
4815
+ const RECAPTCHA_SECRET_KEY = getEnv("RECAPTCHA_SECRET_KEY");
4816
+ /**
4830
4817
  * Slack webhook URLs for different log types.
4831
4818
  * Used for sending log messages to specific Slack channels based on log category.
4832
4819
  *
@@ -4853,7 +4840,7 @@ const SLACK_WEBHOOKS = {
4853
4840
  * @example
4854
4841
  * <img src={OG_AVATAR_URL + userId} />
4855
4842
  */
4856
- const OG_AVATAR_URL = "https://api.kumix.com/og/avatar/";
4843
+ const OG_AVATAR_URL = "https://api.kumix.io/og/avatar/";
4857
4844
  /**
4858
4845
  * Default pagination limit for API responses and UI lists.
4859
4846
  *
@@ -5063,7 +5050,7 @@ const LOCALHOST_GEO_DATA = {
5063
5050
  * }
5064
5051
  * ```
5065
5052
  */
5066
- const LOCALHOST_IP = "63.141.57.109";
5053
+ const LOCALHOST_IP = "198.51.100.1";
5067
5054
  //#endregion
5068
5055
  //#region src/constants/reserved-slugs.ts
5069
5056
  /**
@@ -5239,15 +5226,18 @@ const googleTrackEvent = ({ name, properties }) => {
5239
5226
  }
5240
5227
  };
5241
5228
  /**
5242
- * Helper function to safely get component name from path.
5229
+ * Helper function to safely get the first path segment (component category)
5230
+ * from a component path. For "ui/buttons/Button" this returns "ui".
5243
5231
  *
5244
5232
  * @param componentPath - Component path string
5245
- * @returns Component name or original path if invalid
5233
+ * @returns First segment of the path, or "unknown" if invalid
5246
5234
  */
5247
5235
  const getComponentName = (componentPath) => {
5248
5236
  if (!componentPath || typeof componentPath !== "string") return "unknown";
5249
- const parts = componentPath.trim().split("/");
5250
- return parts.length > 1 ? parts[0] : componentPath.trim();
5237
+ const trimmed = componentPath.trim();
5238
+ if (!trimmed) return "unknown";
5239
+ const firstSlash = trimmed.indexOf("/");
5240
+ return firstSlash === -1 ? trimmed : trimmed.slice(0, firstSlash);
5251
5241
  };
5252
5242
  /**
5253
5243
  * Tracks code copy events with validation.
@@ -5544,6 +5534,7 @@ const googleTrackItemRestore = (module, itemType, itemId) => {
5544
5534
  * ```
5545
5535
  */
5546
5536
  const chunk = (array, chunk_size) => {
5537
+ if (chunk_size <= 0) return [];
5547
5538
  return array.reduce((resultArray, item, index) => {
5548
5539
  const chunkIndex = Math.floor(index / chunk_size);
5549
5540
  if (!resultArray[chunkIndex]) resultArray[chunkIndex] = [];
@@ -5648,15 +5639,17 @@ const stableSort = (arr, compare) => arr.map((item, index) => ({
5648
5639
  * Asset URL builder utilities
5649
5640
  * Provides helpers to construct absolute asset URLs against a configurable asset host.
5650
5641
  */
5651
- const DEFAULT_ASSET_HOST = getEnv("NEXT_PUBLIC_ASSETS_URL") || getEnv("VITE_ASSETS_URL") || "https://assets.kumix.com";
5642
+ const DEFAULT_ASSET_HOST = getEnv("NEXT_PUBLIC_ASSETS_URL") || getEnv("VITE_ASSETS_URL") || "https://assets.kumix.io";
5652
5643
  /**
5653
5644
  * Builds an absolute URL for a given asset path using the provided asset host (CDN) base.
5654
5645
  * Accepts relative paths (e.g., `logo/logo.png`, `/images/icon.svg`) and returns a fully-qualified URL.
5655
5646
  * If an absolute URL is provided as `path`, it will be returned as-is (host is ignored).
5656
5647
  *
5657
5648
  * @param path - Asset path or absolute URL to resolve
5658
- * @param host - Optional asset host (base URL). Defaults to `https://assets.kumix.com`
5659
- * @returns A fully-qualified asset URL string, or `null` if inputs are invalid
5649
+ * @param host - Optional asset host (base URL). Defaults to `https://assets.kumix.io`
5650
+ * @returns A fully-qualified asset URL string. On invalid input (empty path or
5651
+ * malformed base), falls back to the configured asset host rather than
5652
+ * throwing — call sites that need strict failure should validate inputs first.
5660
5653
  *
5661
5654
  * @example
5662
5655
  * ```ts
@@ -5664,7 +5657,7 @@ const DEFAULT_ASSET_HOST = getEnv("NEXT_PUBLIC_ASSETS_URL") || getEnv("VITE_ASSE
5664
5657
  *
5665
5658
  * // Default host
5666
5659
  * const u1 = assetsUrl('logo/logo.png');
5667
- * // 'https://assets.kumix.com/logo/logo.png'
5660
+ * // 'https://assets.kumix.io/logo/logo.png'
5668
5661
  *
5669
5662
  * // Custom host (full URL)
5670
5663
  * const u2 = assetsUrl('/images/icon.svg', 'https://cdn.example.com');
@@ -5678,12 +5671,12 @@ const DEFAULT_ASSET_HOST = getEnv("NEXT_PUBLIC_ASSETS_URL") || getEnv("VITE_ASSE
5678
5671
  * const u4 = assetsUrl('https://other.com/a.png', 'https://cdn.example.com');
5679
5672
  * // 'https://other.com/a.png'
5680
5673
  *
5681
- * // Edge cases
5674
+ * // Edge cases — falsy path or malformed base returns the configured asset host
5682
5675
  * const u5 = assetsUrl('');
5683
- * // null
5676
+ * // 'https://assets.kumix.io' (the configured host)
5684
5677
  *
5685
5678
  * const u6 = assetsUrl('logo.png', '://bad-host');
5686
- * // null (invalid base)
5679
+ * // '://bad-host' (the supplied host — invalid, returned as-is)
5687
5680
  * ```
5688
5681
  */
5689
5682
  function assetsUrl(path, host = DEFAULT_ASSET_HOST) {
@@ -5711,17 +5704,18 @@ function assetsUrl(path, host = DEFAULT_ASSET_HOST) {
5711
5704
  * import { getFlagUrl } from "@kumix/ui/utils/flag-url";
5712
5705
  *
5713
5706
  * const urlUs = getFlagUrl("US");
5714
- * // "https://assets.kumix.com/media/flags/us.svg"
5707
+ * // "https://assets.kumix.io/media/flags/us.svg"
5715
5708
  *
5716
5709
  * const urlId = getFlagUrl("id");
5717
- * // "https://assets.kumix.com/media/flags/id.svg"
5710
+ * // "https://assets.kumix.io/media/flags/id.svg"
5718
5711
  *
5719
5712
  * // Can be used directly in an <img /> or <Image /> component
5720
5713
  * // <img src={getFlagUrl("GB")} alt="United Kingdom flag" />
5721
5714
  * ```
5722
5715
  */
5723
5716
  function getFlagUrl(flag) {
5724
- const flagCode = flag.toLowerCase();
5717
+ if (typeof flag !== "string" || !flag.trim()) return `${assetsUrl("media/flags")}/unknown.svg`;
5718
+ const flagCode = flag.trim().toLowerCase();
5725
5719
  return `${assetsUrl("media/flags")}/${flagCode}.svg`;
5726
5720
  }
5727
5721
  //#endregion
@@ -5791,7 +5785,7 @@ function mapLocale(locale) {
5791
5785
  * Supports Open Graph, Twitter cards, icons, canonical URLs, robots, manifest, and multi-language alternates.
5792
5786
  *
5793
5787
  * @param params - Metadata construction options
5794
- * @param params.baseUrl - The base URL/domain for metadataBase (optional, e.g., 'https://kumix.com')
5788
+ * @param params.baseUrl - The base URL/domain for metadataBase (optional, e.g., 'https://kumix.io')
5795
5789
  * @param params.title - The base title for the page (used in Open Graph and Twitter)
5796
5790
  * @param params.fullTitle - The full title for the page (overrides title in the <title> tag)
5797
5791
  * @param params.description - Description of the page (used in meta, Open Graph, Twitter)
@@ -5835,8 +5829,8 @@ function mapLocale(locale) {
5835
5829
  * creator: '@lukmanaviccena',
5836
5830
  * },
5837
5831
  * alternateLanguages: {
5838
- * en: 'https://kumix.com/en',
5839
- * id: 'https://kumix.com/id',
5832
+ * en: 'https://kumix.io/en',
5833
+ * id: 'https://kumix.io/id',
5840
5834
  * },
5841
5835
  * icons: [
5842
5836
  * { rel: 'icon', url: '/favicon.ico' },
@@ -5890,8 +5884,14 @@ function constructMetadata({ baseUrl, title, fullTitle, description, icons = [
5890
5884
  type: "website",
5891
5885
  locale: "en_US"
5892
5886
  }, twitter = {}, canonicalUrl, alternateLanguages, noIndex = false, noArchive = false, noSnippet = false, manifest }) {
5887
+ let metadataBase;
5888
+ if (baseUrl) try {
5889
+ metadataBase = new URL(baseUrl);
5890
+ } catch {
5891
+ metadataBase = void 0;
5892
+ }
5893
5893
  return {
5894
- metadataBase: baseUrl ? new URL(baseUrl) : void 0,
5894
+ metadataBase,
5895
5895
  title: fullTitle || title,
5896
5896
  description,
5897
5897
  icons,
@@ -6507,7 +6507,6 @@ const resizeImage = (file, opts = {
6507
6507
  const reader = new FileReader();
6508
6508
  reader.onload = (e) => {
6509
6509
  const img = new Image();
6510
- img.src = e.target?.result;
6511
6510
  img.onload = () => {
6512
6511
  const targetWidth = opts.width;
6513
6512
  const targetHeight = opts.height;
@@ -6537,6 +6536,7 @@ const resizeImage = (file, opts = {
6537
6536
  resolve(canvas.toDataURL("image/jpeg", opts.quality));
6538
6537
  };
6539
6538
  img.onerror = (error) => reject(/* @__PURE__ */ new Error(`Image loading error: ${error}`));
6539
+ img.src = e.target?.result;
6540
6540
  };
6541
6541
  reader.onerror = (error) => reject(/* @__PURE__ */ new Error(`FileReader error: ${error}`));
6542
6542
  reader.readAsDataURL(file);
@@ -6630,11 +6630,11 @@ async function loadImage(file) {
6630
6630
  return new Promise((resolve, reject) => {
6631
6631
  const image = new Image();
6632
6632
  const reader = new FileReader();
6633
+ image.onload = () => resolve(image);
6634
+ image.onerror = (err) => reject(err);
6633
6635
  reader.onload = (e) => {
6634
6636
  image.src = e.target?.result;
6635
6637
  };
6636
- image.onload = () => resolve(image);
6637
- image.onerror = (err) => reject(err);
6638
6638
  reader.readAsDataURL(file);
6639
6639
  });
6640
6640
  }
@@ -6885,6 +6885,15 @@ const baseId = ({ prefix }) => baseIdCustom({ prefix });
6885
6885
  * CUID (Collision-resistant Unique IDentifier) generation utilities
6886
6886
  * Provides functions for generating unique identifiers using @paralleldrive/cuid2
6887
6887
  */
6888
+ const generatorCache = /* @__PURE__ */ new Map();
6889
+ function getGenerator(length) {
6890
+ let generator = generatorCache.get(length);
6891
+ if (!generator) {
6892
+ generator = init({ length });
6893
+ generatorCache.set(length, generator);
6894
+ }
6895
+ return generator;
6896
+ }
6888
6897
  /**
6889
6898
  * Generates a CUID (Collision-resistant Unique IDentifier)
6890
6899
  * CUIDs are designed to be globally unique, even in distributed systems.
@@ -6921,10 +6930,8 @@ function cuid() {
6921
6930
  return createId();
6922
6931
  }
6923
6932
  /**
6924
- * Generates a custom ID with an optional prefix and configurable length
6925
- * This function uses `@paralleldrive/cuid2` to create unique identifiers
6926
- * with an optional prefix. Useful for creating IDs with specific patterns
6927
- * like `user_abc123` or `order_987xyz`.
6933
+ * Generates a custom ID with an optional prefix and configurable length.
6934
+ * Uses `@paralleldrive/cuid2`'s cryptographically-secure RNG.
6928
6935
  *
6929
6936
  * @param prefix - Optional prefix to prepend to the ID
6930
6937
  * @param length - The length of the generated ID (default is 12)
@@ -6932,40 +6939,25 @@ function cuid() {
6932
6939
  *
6933
6940
  * @example
6934
6941
  * ```ts
6935
- * import { customeId } from '@/utils/functions';
6942
+ * import { customId } from '@/utils/functions';
6936
6943
  *
6937
6944
  * // Generate ID with prefix
6938
- * const userId = customeId('user_');
6945
+ * const userId = customId('user_');
6939
6946
  * // Returns: 'user_l5r8xn2gzqnb'
6940
6947
  *
6941
6948
  * // Generate ID with custom length
6942
- * const orderId = customeId('order_', 16);
6943
- * // Returns: 'order_l5r8xn2gzqnbf4r2'
6944
- *
6945
- * // Generate ID without prefix
6946
- * const randomId = customeId();
6947
- * // Returns: 'l5r8xn2gzqnb'
6948
- *
6949
- * // Use for different entity types
6950
- * const productId = customeId('prod_', 8);
6951
- * const invoiceId = customeId('inv_', 10);
6952
- * const sessionId = customeId('sess_', 20);
6953
- *
6954
- * // Use in API responses
6955
- * const apiResponse = {
6956
- * id: customeId('api_'),
6957
- * timestamp: Date.now(),
6958
- * data: {...}
6959
- * };
6949
+ * const orderId = customId('order_', 16);
6960
6950
  * ```
6961
6951
  */
6962
- function customeId(prefix, length = 12) {
6963
- const generateId = init({
6964
- random: Math.random,
6965
- length
6966
- });
6952
+ function customId(prefix, length = 12) {
6953
+ const generateId = getGenerator(length);
6967
6954
  return `${prefix || ""}${generateId()}`;
6968
6955
  }
6956
+ /**
6957
+ * @deprecated Use {@link customId} instead. This alias has a typo and is kept
6958
+ * only for backward compatibility; it will be removed in a future major.
6959
+ */
6960
+ const customeId = customId;
6969
6961
  //#endregion
6970
6962
  //#region src/functions/crypto/hash-string.ts
6971
6963
  /**
@@ -7011,6 +7003,8 @@ async function hashStringSHA256(str) {
7011
7003
  * Utility function for generating unique IDs
7012
7004
  * Provides a wrapper around nanoid for consistent ID generation
7013
7005
  */
7006
+ const DEFAULT_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
7007
+ const DEFAULT_SIZE = 7;
7014
7008
  /**
7015
7009
  * Generates a random alphanumeric ID of specified length
7016
7010
  * Uses a custom alphabet of numbers and letters (both cases)
@@ -7036,7 +7030,7 @@ async function hashStringSHA256(str) {
7036
7030
  * ```
7037
7031
  */
7038
7032
  const nanoid = (chars) => {
7039
- return customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", chars || 7)();
7033
+ return customAlphabet(DEFAULT_ALPHABET, chars || DEFAULT_SIZE)();
7040
7034
  };
7041
7035
  /**
7042
7036
  * Generates a unique identifier using the current timestamp and a random number
@@ -7216,8 +7210,9 @@ const getBillingStartDate = (billingCycleStart, referenceDate = /* @__PURE__ */
7216
7210
  * ```
7217
7211
  */
7218
7212
  const formatDate = (datetime, options, locale = "en-US") => {
7219
- if (datetime.toString() === "Invalid Date") return "";
7220
- return new Date(datetime).toLocaleDateString(locale, {
7213
+ const date = new Date(datetime);
7214
+ if (Number.isNaN(date.getTime())) return "";
7215
+ return date.toLocaleDateString(locale, {
7221
7216
  day: "numeric",
7222
7217
  month: "long",
7223
7218
  year: "numeric",
@@ -7265,11 +7260,12 @@ const formatDate = (datetime, options, locale = "en-US") => {
7265
7260
  */
7266
7261
  const formatDateTimeSmart = (datetime, options, locale = "en-US") => {
7267
7262
  const date = new Date(datetime);
7263
+ if (Number.isNaN(date.getTime())) return "Invalid Date";
7268
7264
  const now = /* @__PURE__ */ new Date();
7269
7265
  return new Date(datetime).toLocaleDateString(locale, {
7270
7266
  month: "short",
7271
7267
  day: "numeric",
7272
- ...date.getUTCFullYear() !== now.getUTCFullYear() ? { year: "numeric" } : {
7268
+ ...date.getFullYear() !== now.getFullYear() ? { year: "numeric" } : {
7273
7269
  hour: "numeric",
7274
7270
  minute: "numeric",
7275
7271
  hour12: true
@@ -7324,8 +7320,9 @@ const formatDateTimeSmart = (datetime, options, locale = "en-US") => {
7324
7320
  * ```
7325
7321
  */
7326
7322
  const formatDateTime = (datetime, options, locale = "en-US") => {
7327
- if (datetime.toString() === "Invalid Date") return "";
7328
- return new Date(datetime).toLocaleTimeString(locale, {
7323
+ const date = new Date(datetime);
7324
+ if (Number.isNaN(date.getTime())) return "";
7325
+ return date.toLocaleString(locale, {
7329
7326
  month: "short",
7330
7327
  day: "numeric",
7331
7328
  year: "numeric",
@@ -7561,18 +7558,26 @@ const getFirstAndLastDay = (day, referenceDate = /* @__PURE__ */ new Date()) =>
7561
7558
  const currentDay = today.getDate();
7562
7559
  const currentMonth = today.getMonth();
7563
7560
  const currentYear = today.getFullYear();
7564
- if (currentDay >= day) return {
7565
- firstDay: new Date(currentYear, currentMonth, day),
7566
- lastDay: new Date(currentYear, currentMonth + 1, day - 1)
7567
- };
7568
- else {
7569
- const lastYear = currentMonth === 0 ? currentYear - 1 : currentYear;
7570
- const lastMonth = currentMonth === 0 ? 11 : currentMonth - 1;
7561
+ const lastDayOfMonth = (year, month) => new Date(year, month + 1, 0).getDate();
7562
+ const clamp = (year, month, requested) => Math.min(requested, lastDayOfMonth(year, month));
7563
+ if (currentDay >= day) {
7564
+ const firstDayClamped = clamp(currentYear, currentMonth, day);
7565
+ const lastMonth = currentMonth + 1;
7566
+ const lastYear = currentYear + (currentMonth === 11 ? 1 : 0);
7567
+ const lastDayClamped = clamp(lastMonth === 12 ? lastYear : currentYear, lastMonth % 12, Math.max(1, day - 1));
7571
7568
  return {
7572
- firstDay: new Date(lastYear, lastMonth, day),
7573
- lastDay: new Date(currentYear, currentMonth, day - 1)
7569
+ firstDay: new Date(currentYear, currentMonth, firstDayClamped),
7570
+ lastDay: new Date(lastMonth === 12 ? lastYear : currentYear, lastMonth % 12, lastDayClamped)
7574
7571
  };
7575
7572
  }
7573
+ const lastYear = currentMonth === 0 ? currentYear - 1 : currentYear;
7574
+ const lastMonth = currentMonth === 0 ? 11 : currentMonth - 1;
7575
+ const firstDayClamped = clamp(lastYear, lastMonth, day);
7576
+ const lastDayClamped = clamp(currentYear, currentMonth, Math.max(1, day - 1));
7577
+ return {
7578
+ firstDay: new Date(lastYear, lastMonth, firstDayClamped),
7579
+ lastDay: new Date(currentYear, currentMonth, lastDayClamped)
7580
+ };
7576
7581
  };
7577
7582
  //#endregion
7578
7583
  //#region src/functions/datetime/get-year.ts
@@ -7746,7 +7751,29 @@ const timeAgo = (timestamp, { withAgo, neverText = "Never", justNowText = "Just
7746
7751
  * ```
7747
7752
  */
7748
7753
  const getTimeZones = () => {
7749
- return Intl.supportedValuesOf("timeZone").map((timezone) => {
7754
+ const safeSupported = () => {
7755
+ if (typeof Intl !== "undefined" && typeof Intl.supportedValuesOf === "function") try {
7756
+ return Intl.supportedValuesOf("timeZone");
7757
+ } catch {}
7758
+ return [
7759
+ "UTC",
7760
+ "America/New_York",
7761
+ "America/Los_Angeles",
7762
+ "Europe/London",
7763
+ "Europe/Paris",
7764
+ "Asia/Tokyo"
7765
+ ];
7766
+ };
7767
+ const timezones = safeSupported();
7768
+ const parseOffsetMinutes = (offset) => {
7769
+ const match = offset.match(/GMT([+-])(\d{1,2})(?::(\d{2}))?/);
7770
+ if (!match) return 0;
7771
+ const sign = match[1] === "-" ? -1 : 1;
7772
+ const hours = Number.parseInt(match[2] ?? "0", 10);
7773
+ const minutes = Number.parseInt(match[3] ?? "0", 10);
7774
+ return sign * (hours * 60 + minutes);
7775
+ };
7776
+ return timezones.map((timezone) => {
7750
7777
  const offset = new Intl.DateTimeFormat("en", {
7751
7778
  timeZone: timezone,
7752
7779
  timeZoneName: "shortOffset"
@@ -7755,7 +7782,7 @@ const getTimeZones = () => {
7755
7782
  return {
7756
7783
  value: timezone,
7757
7784
  label: `(${formattedOffset}) ${timezone.replace(/_/g, " ")}`,
7758
- numericOffset: parseInt(formattedOffset.replace("GMT", "").replace("+", "") || "0")
7785
+ numericOffset: parseOffsetMinutes(formattedOffset)
7759
7786
  };
7760
7787
  }).sort((a, b) => a.numericOffset - b.numericOffset);
7761
7788
  };
@@ -7806,6 +7833,7 @@ const getTimeZones = () => {
7806
7833
  */
7807
7834
  function formatFileSize(bytes, decimals = 2, base = 1024) {
7808
7835
  if (bytes === 0) return "0 Bytes";
7836
+ if (bytes < 0) return `-${formatFileSize(-bytes, decimals, base)}`;
7809
7837
  const k = base;
7810
7838
  const sizes = [
7811
7839
  "B",
@@ -7818,7 +7846,7 @@ function formatFileSize(bytes, decimals = 2, base = 1024) {
7818
7846
  "ZB",
7819
7847
  "YB"
7820
7848
  ];
7821
- const i = Math.floor(Math.log(bytes) / Math.log(k));
7849
+ const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
7822
7850
  return `${parseFloat((bytes / k ** i).toFixed(decimals))} ${sizes[i]}`;
7823
7851
  }
7824
7852
  //#endregion
@@ -7874,6 +7902,8 @@ function formatFileSize(bytes, decimals = 2, base = 1024) {
7874
7902
  async function fetchWithRetry(input, init, options = {}) {
7875
7903
  const { timeout = 5e3, maxRetries = 10, retryDelay = 1e3 } = options;
7876
7904
  let lastError = null;
7905
+ let httpError = null;
7906
+ let lastStatus = 0;
7877
7907
  for (let i = 0; i < maxRetries; i++) {
7878
7908
  const controller = new AbortController();
7879
7909
  const timeoutId = setTimeout(() => {
@@ -7886,21 +7916,25 @@ async function fetchWithRetry(input, init, options = {}) {
7886
7916
  });
7887
7917
  clearTimeout(timeoutId);
7888
7918
  if (response.ok) return response;
7919
+ lastStatus = response.status;
7889
7920
  if (response.status === 429 || response.status >= 500) {
7921
+ httpError = /* @__PURE__ */ new Error(`HTTP error ${response.status}`);
7890
7922
  const delay = retryDelay + i ** 2 * 50;
7891
7923
  await new Promise((resolve) => setTimeout(resolve, delay));
7892
7924
  continue;
7893
7925
  }
7894
- if (response.status === 403) throw new Error("Unauthorized");
7895
- if (!response.ok) {
7896
- let errorMessage;
7897
- try {
7898
- errorMessage = (await response.json()).error || `HTTP error ${response.status}`;
7899
- } catch {
7900
- errorMessage = `HTTP error ${response.status}`;
7901
- }
7902
- throw new Error(errorMessage);
7926
+ if (response.status === 401 || response.status === 403) {
7927
+ httpError = /* @__PURE__ */ new Error(response.status === 401 ? "Unauthorized" : "Forbidden");
7928
+ break;
7903
7929
  }
7930
+ let errorMessage;
7931
+ try {
7932
+ errorMessage = (await response.json())?.error || `HTTP error ${response.status}`;
7933
+ } catch {
7934
+ errorMessage = `HTTP error ${response.status}`;
7935
+ }
7936
+ httpError = new Error(errorMessage);
7937
+ break;
7904
7938
  } catch (error) {
7905
7939
  clearTimeout(timeoutId);
7906
7940
  lastError = error instanceof Error ? error : new Error(String(error));
@@ -7909,7 +7943,8 @@ async function fetchWithRetry(input, init, options = {}) {
7909
7943
  await new Promise((resolve) => setTimeout(resolve, delay));
7910
7944
  }
7911
7945
  }
7912
- throw new Error(`Failed after ${maxRetries} retries`);
7946
+ if (httpError) throw httpError;
7947
+ throw new Error(`Failed after ${maxRetries} retries${lastStatus ? ` (last status: ${lastStatus})` : ""}`);
7913
7948
  }
7914
7949
  //#endregion
7915
7950
  //#region src/functions/http/fetch-with-timeout.ts
@@ -8069,13 +8104,25 @@ async function fetcher(endpoint, method = "GET", body, options) {
8069
8104
  if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
8070
8105
  payload = JSON.stringify(body);
8071
8106
  }
8072
- const data = await (await fetch(endpoint, {
8107
+ const res = await fetch(endpoint, {
8073
8108
  method,
8074
8109
  headers,
8075
8110
  body: payload
8076
- })).json();
8111
+ });
8112
+ let data;
8113
+ try {
8114
+ data = await res.json();
8115
+ } catch {
8116
+ data = void 0;
8117
+ }
8077
8118
  if (options?.log && isDevelopment) console.log(`[request] ${endpoint}`, data);
8078
- return data?.data;
8119
+ if (!res.ok) {
8120
+ const message = (typeof data === "object" && data !== null && "error" in data ? String(data.error) : `HTTP error ${res.status}`) || `HTTP error ${res.status}`;
8121
+ const error = new Error(message);
8122
+ error.status = res.status;
8123
+ throw error;
8124
+ }
8125
+ return typeof data === "object" && data !== null && "data" in data ? data.data : data;
8079
8126
  }
8080
8127
  //#endregion
8081
8128
  //#region src/functions/http/ip.ts
@@ -8386,7 +8433,7 @@ function normalizePath(input) {
8386
8433
  * export function middleware(req: NextRequest) {
8387
8434
  * const { domain, path, fullPath, key } = parse(req);
8388
8435
  *
8389
- * console.log(domain); // e.g., "api.kumix.com"
8436
+ * console.log(domain); // e.g., "api.kumix.io"
8390
8437
  * console.log(path); // e.g., "/v1/users"
8391
8438
  * console.log(fullPath); // e.g., "/v1/users?page=1"
8392
8439
  * console.log(key); // e.g., "v1"
@@ -8410,22 +8457,26 @@ function normalizePath(input) {
8410
8457
  * ```
8411
8458
  */
8412
8459
  const parse = (req) => {
8413
- let domain = req.headers.get("host");
8414
- domain = domain.replace(/^www\./, "").toLowerCase();
8460
+ const domain = (req.headers.get("host") || "").replace(/^www\./, "").toLowerCase();
8415
8461
  const path = req.nextUrl.pathname;
8416
8462
  const searchParams = req.nextUrl.searchParams.toString();
8417
8463
  const searchParamsObj = Object.fromEntries(req.nextUrl.searchParams);
8418
8464
  const searchParamsString = searchParams.length > 0 ? `?${searchParams}` : "";
8419
8465
  const fullPath = `${path}${searchParamsString}`;
8466
+ const safeDecode = (value) => {
8467
+ try {
8468
+ return decodeURIComponent(value);
8469
+ } catch {
8470
+ return value;
8471
+ }
8472
+ };
8420
8473
  const pathSegments = path.split("/").filter(Boolean);
8421
- const key = pathSegments.length > 0 ? decodeURIComponent(pathSegments[0]) : "";
8422
- const fullKey = path.length > 1 ? decodeURIComponent(path.slice(1)) : "";
8423
8474
  return {
8424
8475
  domain,
8425
8476
  path,
8426
8477
  fullPath,
8427
- key,
8428
- fullKey,
8478
+ key: pathSegments.length > 0 ? safeDecode(pathSegments[0]) : "",
8479
+ fullKey: path.length > 1 ? safeDecode(path.slice(1)) : "",
8429
8480
  searchParamsObj,
8430
8481
  searchParamsString
8431
8482
  };
@@ -8458,9 +8509,9 @@ const parse = (req) => {
8458
8509
  */
8459
8510
  async function verifyRecaptchaToken(token) {
8460
8511
  try {
8461
- const secretKey = NEXT_PUBLIC_RECAPTCHA_SITE_KEY;
8512
+ const secretKey = RECAPTCHA_SECRET_KEY;
8462
8513
  if (!secretKey) {
8463
- logger.error("NEXT_PUBLIC_RECAPTCHA_SITE_KEY is not set");
8514
+ logger.error("RECAPTCHA_SECRET_KEY is not set");
8464
8515
  return false;
8465
8516
  }
8466
8517
  const data = await (await fetch("https://www.google.com/recaptcha/api/siteverify", {
@@ -8531,10 +8582,13 @@ const logTypeToEnv = {
8531
8582
  * // In development, logs to console instead of Slack
8532
8583
  * ```
8533
8584
  */
8534
- const SlackLog = async ({ message, type, mention = false }) => {
8535
- if (isDevelopment || !SLACK_WEBHOOKS.ALERTS || !SLACK_WEBHOOKS.CRON || !SLACK_WEBHOOKS.LINKS || !SLACK_WEBHOOKS.SUBSCRIBERS || !SLACK_WEBHOOKS.ERRORS) logger.log(message);
8585
+ const SlackLog = async ({ message, type, mention = false, mentionUserId }) => {
8536
8586
  const HOOK = logTypeToEnv[type];
8537
- if (!HOOK) return;
8587
+ if (isDevelopment || !HOOK) {
8588
+ logger.log(message);
8589
+ return;
8590
+ }
8591
+ const resolvedMentionId = mentionUserId || (typeof process !== "undefined" ? process.env.SLACK_MENTION_USER_ID : void 0);
8538
8592
  try {
8539
8593
  return await fetch(HOOK, {
8540
8594
  method: "POST",
@@ -8543,7 +8597,7 @@ const SlackLog = async ({ message, type, mention = false }) => {
8543
8597
  type: "section",
8544
8598
  text: {
8545
8599
  type: "mrkdwn",
8546
- text: `${mention ? "<@U0404G6J3NJ> " : ""}${type === "alerts" || type === "errors" ? ":alert: " : ""}${message}`
8600
+ text: `${mention && resolvedMentionId ? `<@${resolvedMentionId}> ` : ""}${type === "alerts" || type === "errors" ? ":alert: " : ""}${message}`
8547
8601
  }
8548
8602
  }] })
8549
8603
  });
@@ -8696,37 +8750,30 @@ function getUserName(user) {
8696
8750
  //#region src/functions/string/camel-case.ts
8697
8751
  /**
8698
8752
  * Utility function for string case conversion
8699
- * Provides conversion from snake_case to camelCase format
8753
+ * Provides conversion from various cases (snake, kebab, space, Pascal) to camelCase
8700
8754
  */
8701
8755
  /**
8702
- * Converts a string from snake_case to camelCase format
8703
- * If the string is already in camelCase, it will be returned unchanged
8756
+ * Converts a string to camelCase.
8757
+ * Handles snake_case, kebab-case, CONST_CASE, PascalCase, and space-separated words.
8704
8758
  *
8705
8759
  * @param str - The string to convert to camelCase
8706
8760
  * @returns The camelCase version of the input string
8707
8761
  *
8708
8762
  * @example
8709
8763
  * ```ts
8710
- * // Convert snake_case to camelCase
8711
- * toCamelCase('user_name')
8712
- * // Returns "userName"
8713
- *
8714
- * // Already camelCase strings remain unchanged
8715
- * toCamelCase('userName')
8716
- * // Returns "userName"
8717
- *
8718
- * // Multi-word snake_case strings
8719
- * toCamelCase('first_name_last_name')
8720
- * // Returns "firstNameLastName"
8721
- *
8722
- * // Handles single-word strings
8723
- * toCamelCase('user')
8724
- * // Returns "user"
8764
+ * toCamelCase('user_name') // "userName"
8765
+ * toCamelCase('user-name') // "userName"
8766
+ * toCamelCase('first name') // "firstName"
8767
+ * toCamelCase('PascalCase') // "pascalCase"
8768
+ * toCamelCase('first_name_last') // "firstNameLastName"
8725
8769
  * ```
8726
8770
  */
8727
8771
  const toCamelCase = (str) => {
8728
- if (/^[a-z][a-zA-Z0-9]*$/.test(str)) return str;
8729
- return str.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
8772
+ if (!str) return "";
8773
+ const words = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/).filter(Boolean);
8774
+ if (words.length === 0) return "";
8775
+ const [first, ...rest] = words;
8776
+ return `${first.toLowerCase()}${rest.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("")}`;
8730
8777
  };
8731
8778
  //#endregion
8732
8779
  //#region src/functions/string/capitalize.ts
@@ -8868,29 +8915,28 @@ const combineWords = (words) => {
8868
8915
  * ```ts
8869
8916
  * // Basic usage with defaults (USD)
8870
8917
  * currencyFormatter(1000)
8871
- * // Returns "$1,000"
8918
+ * // Returns "$1,000.00"
8872
8919
  *
8873
8920
  * // With custom currency
8874
8921
  * currencyFormatter(1000, {}, 'en-US', 'EUR')
8875
- * // Returns "€1,000"
8922
+ * // Returns "€1,000.00"
8876
8923
  *
8877
8924
  * // With custom locale
8878
8925
  * currencyFormatter(1000, {}, 'de-DE', 'EUR')
8879
- * // Returns "1.000 €"
8926
+ * // Returns "1.000,00 €"
8880
8927
  *
8881
- * // With custom options
8882
- * currencyFormatter(1234.56, { maximumFractionDigits: 2 })
8883
- * // Returns "$1,234.56"
8928
+ * // With custom options (no decimals)
8929
+ * currencyFormatter(1234.56, { maximumFractionDigits: 0 })
8930
+ * // Returns "$1,235"
8884
8931
  *
8885
8932
  * // Combining custom options, locale and currency
8886
- * currencyFormatter(1234.56, { maximumFractionDigits: 2 }, 'ja-JP', 'JPY')
8887
- * // Returns "¥1,235" (rounded to 0 decimal placeas by default for JPY)
8933
+ * currencyFormatter(1234.56, { maximumFractionDigits: 0 }, 'ja-JP', 'JPY')
8934
+ * // Returns "¥1,235"
8888
8935
  * ```
8889
8936
  */
8890
8937
  const currencyFormatter = (value, options, locale = "en-US", currencyCode = "USD") => Intl.NumberFormat(locale, {
8891
8938
  style: "currency",
8892
8939
  currency: currencyCode,
8893
- maximumFractionDigits: 0,
8894
8940
  ...options
8895
8941
  }).format(value);
8896
8942
  //#endregion
@@ -9002,7 +9048,10 @@ function nFormatter(num, opts = { digits: 1 }, locale = "en-US") {
9002
9048
  if (!num) return "0";
9003
9049
  if (opts.full) return Intl.NumberFormat(locale).format(num);
9004
9050
  const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
9005
- if (num < 1) return num.toFixed(opts.digits).replace(rx, "$1");
9051
+ const digits = opts.digits ?? 1;
9052
+ const sign = num < 0 ? "-" : "";
9053
+ const abs = Math.abs(num);
9054
+ if (abs < 1) return `${sign}${abs.toFixed(digits).replace(rx, "$1")}`;
9006
9055
  const item = [
9007
9056
  {
9008
9057
  value: 1,
@@ -9032,8 +9081,8 @@ function nFormatter(num, opts = { digits: 1 }, locale = "en-US") {
9032
9081
  value: 0xde0b6b3a7640000,
9033
9082
  symbol: "E"
9034
9083
  }
9035
- ].slice().reverse().find((item) => num >= item.value);
9036
- return item ? (num / item.value).toFixed(opts.digits).replace(rx, "$1") + item.symbol : "0";
9084
+ ].slice().reverse().find((item) => abs >= item.value);
9085
+ return item ? `${sign}${(abs / item.value).toFixed(digits).replace(rx, "$1")}${item.symbol}` : "0";
9037
9086
  }
9038
9087
  //#endregion
9039
9088
  //#region src/functions/string/normalize-string.ts
@@ -9076,14 +9125,11 @@ function nFormatter(num, opts = { digits: 1 }, locale = "en-US") {
9076
9125
  const normalizeString = (key) => {
9077
9126
  if (!key) return "";
9078
9127
  const original = key;
9079
- const normalized = key.replace(/^\uFEFF/, "").replace(/^\uFFFE/, "").replace(/^\uEFBBBF/, "").replace(/^[\u0000\uFEFF]{2}/, "").replace(/^[\uFFFE\u0000]{2}/, "").replace(/^\u2028/, "").replace(/^\u2029/, "").replace(/./g, (char) => {
9128
+ const normalized = key.replace(/^\uFEFF/, "").replace(/^\uFFFE/, "").replace(/^[\u00EF\u00BB\u00BF]+/, "").replace(/^[\u0000\uFEFF]{2}/, "").replace(/^[\uFFFE\u0000]{2}/, "").replace(/^\u2028/, "").replace(/^\u2029/, "").replace(/./g, (char) => {
9080
9129
  const code = char.charCodeAt(0);
9081
9130
  return code <= 31 || code >= 127 && code <= 159 ? "" : char;
9082
9131
  }).replace(/\s+/g, " ").trim().toLowerCase();
9083
- if (isDevelopment && original !== normalized) {
9084
- logger.log(`Normalized key: "${original}" -> "${normalized}"`);
9085
- logger.log("Original char codes:", Array.from(original).map((c) => c.charCodeAt(0)));
9086
- }
9132
+ if (isDevelopment && process.env.KUMIX_DEBUG_NORMALIZE && original !== normalized) logger.log(`normalizeString: input changed (length ${original.length} -> ${normalized.length})`);
9087
9133
  return normalized;
9088
9134
  };
9089
9135
  //#endregion
@@ -9212,8 +9258,11 @@ function regexEscape(str) {
9212
9258
  * ```
9213
9259
  */
9214
9260
  const truncate = (str, length) => {
9215
- if (!str || str.length <= length) return str ?? null;
9216
- return `${str.slice(0, length - 3)}...`;
9261
+ if (!str) return null;
9262
+ if (str.length <= length) return str;
9263
+ const ELLIPSIS = "...";
9264
+ if (length <= 3) return str.slice(0, length);
9265
+ return `${str.slice(0, length - 3)}${ELLIPSIS}`;
9217
9266
  };
9218
9267
  //#endregion
9219
9268
  //#region src/functions/string/smart-truncate.ts
@@ -9270,9 +9319,16 @@ const truncateDomain = (domain, maxLength) => {
9270
9319
  * ```
9271
9320
  */
9272
9321
  const smartTruncate = (link, maxLength) => {
9322
+ if (!link) return "";
9323
+ if (maxLength <= 0) return "";
9273
9324
  if (link.length <= maxLength) return link;
9274
9325
  const [domain, ...pathParts] = link.split("/");
9275
- const truncatedPath = truncate(pathParts.join("/"), maxLength - 8);
9326
+ const path = pathParts.join("/");
9327
+ const minDomainLength = 8;
9328
+ if (maxLength < minDomainLength) return link.slice(0, maxLength);
9329
+ const maxPathLength = maxLength - minDomainLength;
9330
+ const truncatedPath = path ? truncate(path, maxPathLength) : null;
9331
+ if (!truncatedPath) return truncateDomain(domain, maxLength);
9276
9332
  return `${truncateDomain(domain, maxLength - truncatedPath.length)}/${truncatedPath}`;
9277
9333
  };
9278
9334
  //#endregion
@@ -9319,10 +9375,17 @@ const trim = (u) => typeof u === "string" ? u.trim() : u;
9319
9375
  * Provides functions for validating URL strings and checking URL validity
9320
9376
  */
9321
9377
  /**
9322
- * Checks if a string is a valid URL
9323
- * Uses the URL constructor for validation
9378
+ * Checks if a string is a valid URL.
9379
+ *
9380
+ * By default rejects non-fetchable schemes (`javascript:`, `data:`, `file:`,
9381
+ * `vbscript:`) to avoid XSS via redirect/href validation. Set `allowAnyScheme`
9382
+ * to accept any scheme the URL parser accepts.
9383
+ *
9384
+ * Uses the URL constructor for validation.
9324
9385
  *
9325
9386
  * @param url - The string to check
9387
+ * @param options - Validation options
9388
+ * @param options.allowAnyScheme - When true, accept any scheme (defaults to false)
9326
9389
  * @returns True if the string is a valid URL, false otherwise
9327
9390
  *
9328
9391
  * @example
@@ -9334,11 +9397,9 @@ const trim = (u) => typeof u === "string" ? u.trim() : u;
9334
9397
  * isValidUrl('http://localhost:3000'); // true
9335
9398
  * isValidUrl('ftp://files.example.com'); // true
9336
9399
  *
9337
- * // Invalid URLs
9400
+ * // Invalid / unsafe URLs (rejected by default)
9401
+ * isValidUrl('javascript:alert(1)'); // false
9338
9402
  * isValidUrl('example.com'); // false (missing protocol)
9339
- * isValidUrl('not a url'); // false
9340
- * isValidUrl(''); // false
9341
- * isValidUrl('https://'); // false (incomplete)
9342
9403
  *
9343
9404
  * // Use in form validation
9344
9405
  * function validateUrlInput(input: string) {
@@ -9347,14 +9408,21 @@ const trim = (u) => typeof u === "string" ? u.trim() : u;
9347
9408
  * }
9348
9409
  * return null;
9349
9410
  * }
9350
- *
9351
- * // Use in data processing
9352
- * const validUrls = urls.filter(isValidUrl);
9353
9411
  * ```
9354
9412
  */
9355
- const isValidUrl = (url) => {
9413
+ const isValidUrl = (url, options = {}) => {
9414
+ if (!url || typeof url !== "string") return false;
9356
9415
  try {
9357
- new URL(url);
9416
+ const parsed = new URL(url);
9417
+ if (options.allowAnyScheme) return true;
9418
+ const blocked = [
9419
+ "javascript:",
9420
+ "data:",
9421
+ "vbscript:",
9422
+ "file:"
9423
+ ];
9424
+ const scheme = parsed.protocol.toLowerCase();
9425
+ if (blocked.some((b) => scheme === b)) return false;
9358
9426
  return true;
9359
9427
  } catch (_) {
9360
9428
  return false;
@@ -9386,9 +9454,11 @@ const isValidUrl = (url) => {
9386
9454
  * generateDomainFromName('My Cool Project', 'com')
9387
9455
  * // Returns "mycoolproject.com"
9388
9456
  *
9389
- * // With country code TLD if possible
9457
+ * // ccTLD fallback only applies when the trailing 2 chars form a known ccTLD;
9458
+ * // for "Canada" the trailing 2 chars are "da" (not a ccTLD), so the default
9459
+ * // extension is used.
9390
9460
  * generateDomainFromName('Canada')
9391
- * // Returns "canad.ca"
9461
+ * // Returns "canada.link"
9392
9462
  *
9393
9463
  * // With vowels removed if needed
9394
9464
  * generateDomainFromName('Digital', 'io')
@@ -9676,9 +9746,11 @@ function linkConstructorSimple({ domain, key }) {
9676
9746
  */
9677
9747
  const getSearchParams = (url) => {
9678
9748
  const params = {};
9679
- new URL(url).searchParams.forEach((val, key) => {
9680
- params[key] = val;
9681
- });
9749
+ try {
9750
+ new URL(url).searchParams.forEach((val, key) => {
9751
+ params[key] = val;
9752
+ });
9753
+ } catch {}
9682
9754
  return params;
9683
9755
  };
9684
9756
  /**
@@ -9720,13 +9792,15 @@ const getSearchParams = (url) => {
9720
9792
  */
9721
9793
  const getSearchParamsWithArray = (url) => {
9722
9794
  const params = {};
9723
- new URL(url).searchParams.forEach((val, key) => {
9724
- if (key in params) {
9725
- const param = params[key];
9726
- if (Array.isArray(param)) param.push(val);
9727
- else params[key] = [param, val];
9728
- } else params[key] = val;
9729
- });
9795
+ try {
9796
+ new URL(url).searchParams.forEach((val, key) => {
9797
+ if (key in params) {
9798
+ const param = params[key];
9799
+ if (Array.isArray(param)) param.push(val);
9800
+ else params[key] = [param, val];
9801
+ } else params[key] = val;
9802
+ });
9803
+ } catch {}
9730
9804
  return params;
9731
9805
  };
9732
9806
  /**
@@ -9964,7 +10038,7 @@ const getUrlFromStringIfValid = (str) => {
9964
10038
  */
9965
10039
  const getPrettyUrl = (url) => {
9966
10040
  if (!url) return "";
9967
- return url.replace(/(^\w+:|^)\/\//, "").replace("www.", "").replace(/\/$/, "");
10041
+ return url.replace(/(^\w+:|^)\/\//, "").replace(/^www\./, "").replace(/\/$/, "");
9968
10042
  };
9969
10043
  /**
9970
10044
  * Generates a URL-friendly slug from a given title
@@ -10166,7 +10240,7 @@ const constructURLFromUTMParams = (url, utmParams) => {
10166
10240
  try {
10167
10241
  const newURL = new URL(url);
10168
10242
  for (const [key, value] of Object.entries(utmParams)) if (value === "") newURL.searchParams.delete(key);
10169
- else newURL.searchParams.set(key, value.replace("+", " "));
10243
+ else newURL.searchParams.set(key, value.replace(/\+/g, " "));
10170
10244
  return newURL.toString();
10171
10245
  } catch (_) {
10172
10246
  return "";
@@ -10211,9 +10285,7 @@ const constructURLFromUTMParams = (url, utmParams) => {
10211
10285
  const getUrlWithoutUTMParams = (url) => {
10212
10286
  try {
10213
10287
  const newURL = new URL(url);
10214
- paramsMetadata.forEach((param) => {
10215
- newURL.searchParams.delete(param.key);
10216
- });
10288
+ for (const key of UTMTags) newURL.searchParams.delete(key);
10217
10289
  return newURL.toString();
10218
10290
  } catch (_) {
10219
10291
  return url;
@@ -10321,6 +10393,34 @@ const createHref = (href, domain, utmParams) => {
10321
10393
  const deepEqual = (obj1, obj2) => {
10322
10394
  if (obj1 === obj2) return true;
10323
10395
  if (typeof obj1 !== "object" || typeof obj2 !== "object" || obj1 === null || obj2 === null) return false;
10396
+ if (Object.prototype.toString.call(obj1) !== Object.prototype.toString.call(obj2)) return false;
10397
+ if (obj1 instanceof Date && obj2 instanceof Date) return obj1.getTime() === obj2.getTime();
10398
+ if (obj1 instanceof RegExp && obj2 instanceof RegExp) return obj1.source === obj2.source && obj1.flags === obj2.flags;
10399
+ if (Array.isArray(obj1) && Array.isArray(obj2)) {
10400
+ if (obj1.length !== obj2.length) return false;
10401
+ for (let i = 0; i < obj1.length; i++) if (!deepEqual(obj1[i], obj2[i])) return false;
10402
+ return true;
10403
+ }
10404
+ if (obj1 instanceof Map && obj2 instanceof Map) {
10405
+ if (obj1.size !== obj2.size) return false;
10406
+ for (const [key, value] of obj1) {
10407
+ if (!obj2.has(key)) return false;
10408
+ if (!deepEqual(value, obj2.get(key))) return false;
10409
+ }
10410
+ return true;
10411
+ }
10412
+ if (obj1 instanceof Set && obj2 instanceof Set) {
10413
+ if (obj1.size !== obj2.size) return false;
10414
+ for (const value of obj1) {
10415
+ let found = false;
10416
+ for (const other of obj2) if (deepEqual(value, other)) {
10417
+ found = true;
10418
+ break;
10419
+ }
10420
+ if (!found) return false;
10421
+ }
10422
+ return true;
10423
+ }
10324
10424
  const keys1 = Object.keys(obj1);
10325
10425
  const keys2 = Object.keys(obj2);
10326
10426
  if (keys1.length !== keys2.length) return false;
@@ -10617,64 +10717,64 @@ function getEmailDomain(email) {
10617
10717
  * Checks if a URL can be embedded in an iframe based on security headers
10618
10718
  */
10619
10719
  /**
10620
- * Determines if a URL can be displayed in an iframe by checking security headers
10621
- * Examines Content-Security-Policy and X-Frame-Options headers to verify embedding permissions
10720
+ * Determines if a URL can be displayed in an iframe by checking security headers.
10721
+ * Examines Content-Security-Policy `frame-ancestors` and `X-Frame-Options` headers.
10722
+ *
10723
+ * A URL is considered embeddable only when:
10724
+ * - `frame-ancestors` is absent OR explicitly allows `*` / `self` (same origin) /
10725
+ * a wildcard/origin that matches `requestDomain`, AND
10726
+ * - `X-Frame-Options` is not `DENY` or `SAMEORIGIN` (when origin differs).
10622
10727
  *
10623
10728
  * @param options - The options for the check
10624
10729
  * @param options.url - The URL to check for iframe embedding capability
10625
- * @param options.requestDomain - The domain that will be embedding the iframe
10626
- * @returns A Promise that resolves to true if the URL can be embedded, false otherwise
10730
+ * @param options.requestDomain - The origin that will be embedding the iframe
10731
+ * (e.g. `https://myapp.com`). Compared against CSP source expressions.
10732
+ * @returns A Promise that resolves to true if the URL can be embedded, false otherwise.
10627
10733
  *
10628
10734
  * @example
10629
10735
  * ```ts
10630
10736
  * import { isIframeable } from '@/utils/functions';
10631
10737
  *
10632
- * // Check if a URL can be embedded in an iframe
10633
10738
  * const canEmbed = await isIframeable({
10634
10739
  * url: 'https://example.com',
10635
10740
  * requestDomain: 'https://myapp.com'
10636
10741
  * });
10637
- *
10638
- * if (canEmbed) {
10639
- * console.log('URL can be embedded in an iframe');
10640
- * } else {
10641
- * console.log('URL cannot be embedded due to security restrictions');
10642
- * }
10643
- *
10644
- * // In a React component
10645
- * function EmbedPreview({ url }) {
10646
- * const [canEmbed, setCanEmbed] = useState<boolean | null>(null);
10647
- *
10648
- * useEffect(() => {
10649
- * async function checkEmbedding() {
10650
- * const result = await isIframeable({
10651
- * url,
10652
- * requestDomain: window.location.origin
10653
- * });
10654
- * setCanEmbed(result);
10655
- * }
10656
- * checkEmbedding();
10657
- * }, [url]);
10658
- *
10659
- * if (canEmbed === null) return <div>Checking...</div>;
10660
- * if (canEmbed === false) return <div>Cannot embed this URL</div>;
10661
- *
10662
- * return <iframe src={url} />;
10663
- * }
10664
10742
  * ```
10665
10743
  */
10666
10744
  const isIframeable = async ({ url, requestDomain }) => {
10667
10745
  const res = await fetch(url);
10746
+ const normalizeOrigin = (value) => value.trim().replace(/\/$/, "").toLowerCase();
10747
+ const requestedOrigin = normalizeOrigin(requestDomain);
10668
10748
  const cspHeader = res.headers.get("content-security-policy");
10669
10749
  if (cspHeader) {
10670
10750
  const frameAncestorsMatch = cspHeader.match(/frame-ancestors\s+([\s\S]+?)(?=;|$)/i);
10671
- if (frameAncestorsMatch) {
10672
- if (frameAncestorsMatch[1] === "*") return true;
10673
- if (frameAncestorsMatch[1].split(/\s+/).includes(requestDomain)) return true;
10674
- }
10751
+ if (frameAncestorsMatch) return frameAncestorsMatch[1].trim().split(/\s+/).filter(Boolean).some((source) => {
10752
+ const s = source.toLowerCase();
10753
+ if (s === "'none'") return false;
10754
+ if (s === "*" || s === "https:*" || s === "http:*") return true;
10755
+ if (s === "'self'") try {
10756
+ return normalizeOrigin(new URL(res.url).origin) === requestedOrigin;
10757
+ } catch {
10758
+ return false;
10759
+ }
10760
+ if (s.includes("*")) {
10761
+ const pattern = s.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
10762
+ return new RegExp(`^${pattern}$`).test(requestedOrigin);
10763
+ }
10764
+ return normalizeOrigin(s) === requestedOrigin;
10765
+ });
10675
10766
  }
10676
10767
  const xFrameOptions = res.headers.get("X-Frame-Options");
10677
- if (xFrameOptions === "DENY" || xFrameOptions === "SAMEORIGIN") return false;
10768
+ if (xFrameOptions) {
10769
+ const value = xFrameOptions.trim().toUpperCase();
10770
+ if (value === "DENY") return false;
10771
+ if (value === "SAMEORIGIN") try {
10772
+ return normalizeOrigin(new URL(res.url).origin) === requestedOrigin;
10773
+ } catch {
10774
+ return false;
10775
+ }
10776
+ if (value.startsWith("ALLOW-FROM")) return false;
10777
+ }
10678
10778
  return true;
10679
10779
  };
10680
10780
  //#endregion
@@ -10758,6 +10858,6 @@ const isReservedKeyGlobal = (key) => {
10758
10858
  ].includes(key);
10759
10859
  };
10760
10860
  //#endregion
10761
- export { API_PORT, CONNECT_SUPPORTED_COUNTRIES, CONTINENTS, CONTINENT_CODES, COUNTRIES, COUNTRY_CODES, COUNTRY_PHONE_CODES, DEBUG, DEFAULT_THEME_MODE, EU_COUNTRY_CODES, FRAMER_MOTION_LIST_ITEM_VARIANTS, INFINITY_NUMBER, LAST_VISITED_WORKSPACE, LOCALHOST_GEO_DATA, LOCALHOST_IP, LOG_LEVEL, META_THEME_COLORS, NEXT_PUBLIC_RECAPTCHA_SITE_KEY, NODE_ENV, OG_AVATAR_URL, PAGINATION_LIMIT, PORT, REDIRECT_QUERY_PARAM, REGIONS, REGION_CODES, RESERVED_SLUGS, SECOND_LEVEL_DOMAINS, SLACK_WEBHOOKS, SPECIAL_APEX_DOMAINS, STAGGER_CHILD_VARIANTS, SlackLog, TAB_ITEM_ANIMATION_SETTINGS, THEME_MODES, THE_BEGINNING_OF_TIME, TWO_WEEKS_IN_SECONDS, UTMTags, assetsUrl, baseId, baseIdCustom, capitalize, ccTLDs, chunk, clearAllCookies, clearStorage, cn, combineWords, constructMetadata, constructURLFromUTMParams, createHref, createId, cuid, currencyFormatter, customeId, debounce, deepEqual, fetchWithRetry, fetchWithTimeout, fetcher, fetcherSWR, fileToBase64, formatDate, formatDateTime, formatDateTimeSmart, formatFileSize, formatPeriod, formatTime, generateDomainFromName, getAdjustedBillingCycleStart, getAllCookies, getApexDomain, getBillingStartDate, getClientIP, getClientIPFromHeaders, getCookie, getCurrentYear, getDateTimeLocal, getDaysDifference, getDomainWithoutWWW, getEmailDomain, getEnv, getFirstAndLastDay, getFlagUrl, getHeight, getIPAddress, getInitials, getLastDayOfMonth, getParamsFromURL, getPrettyUrl, getRequestOrigin, getSearchParams, getSearchParamsWithArray, getSizeAvatar, getSlug, getStorageItem, getSubdomain, getTimeZones, getUrlFromString, getUrlFromStringIfValid, getUrlObjFromString, getUrlWithoutUTMParams, getUserAgent, getUserAgentFromHeaders, getUserName, googleTrackCodeCopy, googleTrackDirectionChange, googleTrackEvent, googleTrackItemCreate, googleTrackItemDelete, googleTrackItemRestore, googleTrackItemUpdate, googleTrackItemView, googleTrackThemeChange, googleTrackViewChange, hasCookie, hasStorageItem, hashStringSHA256, init as initCuid, isBusinessEmail, isDevelopment, isDisposableEmail, isIframeable, isProduction, isReservedKeyGlobal, isStaging, isUnsupportedKey, isValidIP, isValidUrl, linkConstructor, linkConstructorSimple, loadImage, logger, mapLocale, nFormatter, nanoid, normalizeEmail, normalizePath, normalizeString, paramsMetadata, parse, pluralize, randomValue, regexEscape, removeCookie, removeStorageItem, resizeAndCropImage, resizeImage, setCookie, setStorageItem, slugify, smartTruncate, stableSort, throttle, timeAgo, toCamelCase, trim, truncate, uid, validDomainRegex, validKeyRegex, validSlugRegex, validateEmail, verifyRecaptchaToken };
10861
+ export { API_PORT, CONNECT_SUPPORTED_COUNTRIES, CONTINENTS, CONTINENT_CODES, COUNTRIES, COUNTRY_CODES, COUNTRY_PHONE_CODES, DEBUG, DEFAULT_THEME_MODE, EU_COUNTRY_CODES, FRAMER_MOTION_LIST_ITEM_VARIANTS, INFINITY_NUMBER, LAST_VISITED_WORKSPACE, LOCALHOST_GEO_DATA, LOCALHOST_IP, LOG_LEVEL, META_THEME_COLORS, NEXT_PUBLIC_RECAPTCHA_SITE_KEY, NODE_ENV, OG_AVATAR_URL, PAGINATION_LIMIT, PORT, RECAPTCHA_SECRET_KEY, REDIRECT_QUERY_PARAM, REGIONS, REGION_CODES, RESERVED_SLUGS, SECOND_LEVEL_DOMAINS, SLACK_WEBHOOKS, SPECIAL_APEX_DOMAINS, STAGGER_CHILD_VARIANTS, SlackLog, TAB_ITEM_ANIMATION_SETTINGS, THEME_MODES, THE_BEGINNING_OF_TIME, TWO_WEEKS_IN_SECONDS, UTMTags, assetsUrl, baseId, baseIdCustom, capitalize, ccTLDs, chunk, clearAllCookies, clearStorage, cn, combineWords, constructMetadata, constructURLFromUTMParams, createHref, createId, cuid, currencyFormatter, customId, customeId, debounce, deepEqual, fetchWithRetry, fetchWithTimeout, fetcher, fetcherSWR, fileToBase64, formatDate, formatDateTime, formatDateTimeSmart, formatFileSize, formatPeriod, formatTime, generateDomainFromName, getAdjustedBillingCycleStart, getAllCookies, getApexDomain, getBillingStartDate, getClientIP, getClientIPFromHeaders, getCookie, getCurrentYear, getDateTimeLocal, getDaysDifference, getDomainWithoutWWW, getEmailDomain, getEnv, getFirstAndLastDay, getFlagUrl, getHeight, getIPAddress, getInitials, getLastDayOfMonth, getParamsFromURL, getPrettyUrl, getRequestOrigin, getSearchParams, getSearchParamsWithArray, getSizeAvatar, getSlug, getStorageItem, getSubdomain, getTimeZones, getUrlFromString, getUrlFromStringIfValid, getUrlObjFromString, getUrlWithoutUTMParams, getUserAgent, getUserAgentFromHeaders, getUserName, googleTrackCodeCopy, googleTrackDirectionChange, googleTrackEvent, googleTrackItemCreate, googleTrackItemDelete, googleTrackItemRestore, googleTrackItemUpdate, googleTrackItemView, googleTrackThemeChange, googleTrackViewChange, hasCookie, hasStorageItem, hashStringSHA256, init as initCuid, isBusinessEmail, isDevelopment, isDisposableEmail, isIframeable, isProduction, isReservedKeyGlobal, isStaging, isTest, isUnsupportedKey, isValidIP, isValidUrl, linkConstructor, linkConstructorSimple, loadImage, logger, mapLocale, nFormatter, nanoid, normalizeEmail, normalizePath, normalizeString, paramsMetadata, parse, pluralize, randomValue, regexEscape, removeCookie, removeStorageItem, resizeAndCropImage, resizeImage, setCookie, setStorageItem, slugify, smartTruncate, stableSort, throttle, timeAgo, toCamelCase, trim, truncate, uid, validDomainRegex, validKeyRegex, validSlugRegex, validateEmail, verifyRecaptchaToken };
10762
10862
 
10763
10863
  //# sourceMappingURL=index.js.map