@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.d.ts +110 -172
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +488 -388
- package/dist/index.js.map +1 -1
- package/dist/{logger-BOB35dQN.js → logger-qDWnDXis.js} +17 -14
- package/dist/logger-qDWnDXis.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +5950 -26
- package/dist/server.js.map +1 -1
- package/package.json +7 -7
- package/dist/logger-BOB35dQN.js.map +0 -1
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
|
|
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
|
-
"
|
|
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
|
-
"
|
|
574
|
+
"SK",
|
|
577
575
|
"SI",
|
|
578
|
-
"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4391
|
-
*
|
|
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
|
-
"
|
|
4414
|
+
"ag",
|
|
4415
|
+
"ai",
|
|
4416
4416
|
"al",
|
|
4417
|
-
"
|
|
4418
|
-
"as",
|
|
4419
|
-
"ad",
|
|
4417
|
+
"am",
|
|
4420
4418
|
"ao",
|
|
4421
|
-
"ai",
|
|
4422
4419
|
"aq",
|
|
4423
|
-
"ag",
|
|
4424
4420
|
"ar",
|
|
4425
|
-
"
|
|
4426
|
-
"aw",
|
|
4427
|
-
"ac",
|
|
4428
|
-
"au",
|
|
4421
|
+
"as",
|
|
4429
4422
|
"at",
|
|
4423
|
+
"au",
|
|
4424
|
+
"aw",
|
|
4425
|
+
"ax",
|
|
4430
4426
|
"az",
|
|
4431
|
-
"
|
|
4432
|
-
"bh",
|
|
4433
|
-
"bd",
|
|
4427
|
+
"ba",
|
|
4434
4428
|
"bb",
|
|
4435
|
-
"
|
|
4436
|
-
"by",
|
|
4429
|
+
"bd",
|
|
4437
4430
|
"be",
|
|
4438
|
-
"
|
|
4431
|
+
"bf",
|
|
4432
|
+
"bg",
|
|
4433
|
+
"bh",
|
|
4434
|
+
"bi",
|
|
4439
4435
|
"bj",
|
|
4436
|
+
"bl",
|
|
4440
4437
|
"bm",
|
|
4441
|
-
"
|
|
4438
|
+
"bn",
|
|
4442
4439
|
"bo",
|
|
4443
4440
|
"bq",
|
|
4444
|
-
"an",
|
|
4445
|
-
"nl",
|
|
4446
|
-
"ba",
|
|
4447
|
-
"bw",
|
|
4448
|
-
"bv",
|
|
4449
4441
|
"br",
|
|
4450
|
-
"
|
|
4451
|
-
"
|
|
4452
|
-
"
|
|
4453
|
-
"
|
|
4454
|
-
"
|
|
4455
|
-
"
|
|
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
|
-
"
|
|
4468
|
+
"de",
|
|
4482
4469
|
"dj",
|
|
4470
|
+
"dk",
|
|
4483
4471
|
"dm",
|
|
4484
4472
|
"do",
|
|
4485
|
-
"
|
|
4486
|
-
"tp",
|
|
4473
|
+
"dz",
|
|
4487
4474
|
"ec",
|
|
4475
|
+
"ee",
|
|
4488
4476
|
"eg",
|
|
4489
|
-
"
|
|
4490
|
-
"gq",
|
|
4477
|
+
"eh",
|
|
4491
4478
|
"er",
|
|
4492
|
-
"
|
|
4479
|
+
"es",
|
|
4493
4480
|
"et",
|
|
4494
4481
|
"eu",
|
|
4482
|
+
"eus",
|
|
4483
|
+
"fi",
|
|
4484
|
+
"fj",
|
|
4495
4485
|
"fk",
|
|
4496
|
-
"fo",
|
|
4497
4486
|
"fm",
|
|
4498
|
-
"
|
|
4499
|
-
"fi",
|
|
4487
|
+
"fo",
|
|
4500
4488
|
"fr",
|
|
4501
|
-
"gf",
|
|
4502
|
-
"pf",
|
|
4503
|
-
"tf",
|
|
4504
4489
|
"ga",
|
|
4505
4490
|
"gal",
|
|
4506
|
-
"
|
|
4507
|
-
"
|
|
4491
|
+
"gb",
|
|
4492
|
+
"gd",
|
|
4508
4493
|
"ge",
|
|
4509
|
-
"
|
|
4494
|
+
"gf",
|
|
4495
|
+
"gg",
|
|
4510
4496
|
"gh",
|
|
4511
4497
|
"gi",
|
|
4512
|
-
"gr",
|
|
4513
4498
|
"gl",
|
|
4514
|
-
"
|
|
4499
|
+
"gm",
|
|
4500
|
+
"gn",
|
|
4515
4501
|
"gp",
|
|
4516
|
-
"
|
|
4502
|
+
"gq",
|
|
4503
|
+
"gr",
|
|
4504
|
+
"gs",
|
|
4517
4505
|
"gt",
|
|
4518
|
-
"
|
|
4519
|
-
"gn",
|
|
4506
|
+
"gu",
|
|
4520
4507
|
"gw",
|
|
4521
4508
|
"gy",
|
|
4522
|
-
"
|
|
4509
|
+
"hk",
|
|
4523
4510
|
"hm",
|
|
4524
4511
|
"hn",
|
|
4525
|
-
"
|
|
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
|
-
"
|
|
4528
|
+
"jp",
|
|
4541
4529
|
"ke",
|
|
4530
|
+
"kg",
|
|
4531
|
+
"kh",
|
|
4542
4532
|
"ki",
|
|
4533
|
+
"km",
|
|
4534
|
+
"kn",
|
|
4535
|
+
"kp",
|
|
4536
|
+
"kr",
|
|
4543
4537
|
"kw",
|
|
4544
|
-
"
|
|
4538
|
+
"ky",
|
|
4539
|
+
"kz",
|
|
4545
4540
|
"la",
|
|
4546
|
-
"lv",
|
|
4547
4541
|
"lb",
|
|
4548
|
-
"
|
|
4549
|
-
"lr",
|
|
4550
|
-
"ly",
|
|
4542
|
+
"lc",
|
|
4551
4543
|
"li",
|
|
4544
|
+
"lk",
|
|
4545
|
+
"lr",
|
|
4546
|
+
"ls",
|
|
4552
4547
|
"lt",
|
|
4553
4548
|
"lu",
|
|
4554
|
-
"
|
|
4555
|
-
"
|
|
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
|
-
"
|
|
4569
|
+
"mv",
|
|
4570
|
+
"mw",
|
|
4567
4571
|
"mx",
|
|
4568
|
-
"
|
|
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
|
-
"
|
|
4588
|
-
"
|
|
4589
|
-
"
|
|
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
|
-
"
|
|
4592
|
+
"pk",
|
|
4602
4593
|
"pl",
|
|
4603
|
-
"
|
|
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
|
-
"
|
|
4628
|
-
"rs",
|
|
4608
|
+
"sb",
|
|
4629
4609
|
"sc",
|
|
4630
|
-
"
|
|
4610
|
+
"sd",
|
|
4611
|
+
"se",
|
|
4631
4612
|
"sg",
|
|
4632
|
-
"
|
|
4633
|
-
"an",
|
|
4634
|
-
"nl",
|
|
4635
|
-
"sx",
|
|
4636
|
-
"an",
|
|
4637
|
-
"sk",
|
|
4613
|
+
"sh",
|
|
4638
4614
|
"si",
|
|
4639
|
-
"
|
|
4640
|
-
"
|
|
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
|
-
"
|
|
4651
|
-
"
|
|
4652
|
-
"
|
|
4653
|
-
"
|
|
4622
|
+
"ss",
|
|
4623
|
+
"st",
|
|
4624
|
+
"sv",
|
|
4625
|
+
"sx",
|
|
4654
4626
|
"sy",
|
|
4655
|
-
"
|
|
4656
|
-
"
|
|
4657
|
-
"
|
|
4658
|
-
"
|
|
4627
|
+
"sz",
|
|
4628
|
+
"tc",
|
|
4629
|
+
"td",
|
|
4630
|
+
"tf",
|
|
4659
4631
|
"tg",
|
|
4632
|
+
"th",
|
|
4633
|
+
"tj",
|
|
4660
4634
|
"tk",
|
|
4661
|
-
"
|
|
4662
|
-
"
|
|
4635
|
+
"tl",
|
|
4636
|
+
"tm",
|
|
4663
4637
|
"tn",
|
|
4638
|
+
"to",
|
|
4664
4639
|
"tr",
|
|
4665
|
-
"
|
|
4666
|
-
"tc",
|
|
4640
|
+
"tt",
|
|
4667
4641
|
"tv",
|
|
4668
|
-
"
|
|
4642
|
+
"tw",
|
|
4643
|
+
"tz",
|
|
4669
4644
|
"ua",
|
|
4670
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
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 = "
|
|
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
|
|
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
|
|
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
|
|
5250
|
-
|
|
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.
|
|
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.
|
|
5659
|
-
* @returns A fully-qualified asset URL string
|
|
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.
|
|
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
|
-
* //
|
|
5676
|
+
* // 'https://assets.kumix.io' (the configured host)
|
|
5684
5677
|
*
|
|
5685
5678
|
* const u6 = assetsUrl('logo.png', '://bad-host');
|
|
5686
|
-
* //
|
|
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.
|
|
5707
|
+
* // "https://assets.kumix.io/media/flags/us.svg"
|
|
5715
5708
|
*
|
|
5716
5709
|
* const urlId = getFlagUrl("id");
|
|
5717
|
-
* // "https://assets.kumix.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
5839
|
-
* id: 'https://kumix.
|
|
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
|
|
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
|
-
*
|
|
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 {
|
|
6942
|
+
* import { customId } from '@/utils/functions';
|
|
6936
6943
|
*
|
|
6937
6944
|
* // Generate ID with prefix
|
|
6938
|
-
* const userId =
|
|
6945
|
+
* const userId = customId('user_');
|
|
6939
6946
|
* // Returns: 'user_l5r8xn2gzqnb'
|
|
6940
6947
|
*
|
|
6941
6948
|
* // Generate ID with custom length
|
|
6942
|
-
* const orderId =
|
|
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
|
|
6963
|
-
const generateId =
|
|
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(
|
|
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
|
-
|
|
7220
|
-
|
|
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.
|
|
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
|
-
|
|
7328
|
-
|
|
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
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
const lastYear = currentMonth ===
|
|
7570
|
-
const
|
|
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(
|
|
7573
|
-
lastDay: new Date(currentYear,
|
|
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
|
-
|
|
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:
|
|
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 ===
|
|
7895
|
-
|
|
7896
|
-
|
|
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
|
-
|
|
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
|
|
8107
|
+
const res = await fetch(endpoint, {
|
|
8073
8108
|
method,
|
|
8074
8109
|
headers,
|
|
8075
8110
|
body: payload
|
|
8076
|
-
})
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
8512
|
+
const secretKey = RECAPTCHA_SECRET_KEY;
|
|
8462
8513
|
if (!secretKey) {
|
|
8463
|
-
logger.error("
|
|
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)
|
|
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 ?
|
|
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
|
|
8753
|
+
* Provides conversion from various cases (snake, kebab, space, Pascal) to camelCase
|
|
8700
8754
|
*/
|
|
8701
8755
|
/**
|
|
8702
|
-
* Converts a string
|
|
8703
|
-
*
|
|
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
|
-
* //
|
|
8711
|
-
* toCamelCase('
|
|
8712
|
-
* //
|
|
8713
|
-
*
|
|
8714
|
-
* //
|
|
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 (
|
|
8729
|
-
|
|
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:
|
|
8883
|
-
* // Returns "$1,
|
|
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:
|
|
8887
|
-
* // Returns "¥1,235"
|
|
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
|
-
|
|
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) =>
|
|
9036
|
-
return item ? (
|
|
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(
|
|
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
|
|
9216
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
-
* //
|
|
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 "
|
|
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
|
-
|
|
9680
|
-
|
|
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
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
10626
|
-
*
|
|
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
|
-
|
|
10673
|
-
if (
|
|
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
|
|
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
|