@rzl-zone/utils-js 3.12.1-beta.1 → 3.13.0-beta.3
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/README.md +12 -32
- package/dist/.references/index.d.cts +1 -3
- package/dist/.references/index.d.ts +1 -3
- package/dist/{assertIsArray-bTA3XLjq.js → assertIsArray-C2rkhR9M.js} +3 -3
- package/dist/{assertIsArray-bTA3XLjq.js.map → assertIsArray-C2rkhR9M.js.map} +1 -1
- package/dist/{assertIsArray-DJXkjHZs.cjs → assertIsArray-DWsgvahU.cjs} +3 -3
- package/dist/{assertIsArray-DJXkjHZs.cjs.map → assertIsArray-DWsgvahU.cjs.map} +1 -1
- package/dist/{assertIsBoolean-C8WEXVr2.cjs → assertIsBoolean-BKcvET7Z.cjs} +2 -2
- package/dist/{assertIsBoolean-C8WEXVr2.cjs.map → assertIsBoolean-BKcvET7Z.cjs.map} +1 -1
- package/dist/{assertIsBoolean-DR1SaXPD.js → assertIsBoolean-Clc7Vw4v.js} +2 -2
- package/dist/{assertIsBoolean-DR1SaXPD.js.map → assertIsBoolean-Clc7Vw4v.js.map} +1 -1
- package/dist/{assertIsString-CEB07_83.js → assertIsString-Ddz4y81W.js} +3 -3
- package/dist/{assertIsString-CEB07_83.js.map → assertIsString-Ddz4y81W.js.map} +1 -1
- package/dist/{assertIsString-BiHQSrB2.cjs → assertIsString-DxIqUfF1.cjs} +3 -3
- package/dist/{assertIsString-BiHQSrB2.cjs.map → assertIsString-DxIqUfF1.cjs.map} +1 -1
- package/dist/assertions/index.cjs +5 -5
- package/dist/assertions/index.d.cts +2 -2
- package/dist/assertions/index.d.ts +2 -2
- package/dist/assertions/index.js +5 -5
- package/dist/conversions/index.cjs +6 -6
- package/dist/conversions/index.d.cts +2 -2
- package/dist/conversions/index.d.ts +2 -2
- package/dist/conversions/index.js +6 -6
- package/dist/{conversions-D_Kh0a_C.cjs → conversions-Bu5c_RZ9.cjs} +12 -12
- package/dist/{conversions-D_Kh0a_C.cjs.map → conversions-Bu5c_RZ9.cjs.map} +1 -1
- package/dist/{conversions-BNIh_tCH.js → conversions-DjfhrQE9.js} +12 -12
- package/dist/{conversions-BNIh_tCH.js.map → conversions-DjfhrQE9.js.map} +1 -1
- package/dist/events/index.cjs +4 -4
- package/dist/events/index.d.cts +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +4 -4
- package/dist/formatters/index.cjs +2 -2
- package/dist/formatters/index.d.cts +2 -2
- package/dist/formatters/index.d.ts +2 -2
- package/dist/formatters/index.js +2 -2
- package/dist/{formatters--1m_vpE8.js → formatters-CidRTj87.js} +10 -10
- package/dist/{formatters--1m_vpE8.js.map → formatters-CidRTj87.js.map} +1 -1
- package/dist/{formatters-Cbij0XLU.cjs → formatters-DF4887ia.cjs} +10 -10
- package/dist/{formatters-Cbij0XLU.cjs.map → formatters-DF4887ia.cjs.map} +1 -1
- package/dist/generators/index.cjs +5 -5
- package/dist/generators/index.d.cts +2 -2
- package/dist/generators/index.d.ts +2 -2
- package/dist/generators/index.js +5 -5
- package/dist/{index-UPp94Agr.d.ts → index-BK86Fnvd.d.ts} +2 -2
- package/dist/{index-B6tawc8L.d.cts → index-BZMwAuLf.d.ts} +1 -1
- package/dist/{index-CeBC2Vvl.d.cts → index-BbCX319b.d.cts} +2 -2
- package/dist/{index-CgRDTI6f.d.ts → index-C02XatNH.d.ts} +2 -2
- package/dist/{index-59zbLcPr.d.ts → index-CHwf6vUh.d.ts} +1 -1
- package/dist/{index-gBA_8SuF.d.cts → index-CIBhF2hQ.d.cts} +1 -1
- package/dist/{index-jyDqzicx.d.cts → index-CWgiyaU3.d.cts} +2 -2
- package/dist/{index-D4fcasfZ.d.cts → index-CX8gCrW8.d.ts} +241 -14
- package/dist/{index-Hg1qJkjl.d.ts → index-CyZz4BHD.d.cts} +1 -1
- package/dist/{index-CoiUBVmr.d.ts → index-Cyu3Mh3N.d.cts} +241 -14
- package/dist/{index-DDrSQKIc.d.ts → index-DC7eXTo_.d.cts} +1 -1
- package/dist/{index-DsGxO31H.d.cts → index-DPBypj6v.d.ts} +1 -1
- package/dist/{index-C267akkJ.d.ts → index-Dq5vUFfH.d.ts} +1 -1
- package/dist/{index-Qm3iFwd0.d.cts → index-YeyLvCAr.d.cts} +1 -1
- package/dist/{isBigInt-C4krUeAw.cjs → isBigInt-BePsmNsm.cjs} +2 -2
- package/dist/{isBigInt-C4krUeAw.cjs.map → isBigInt-BePsmNsm.cjs.map} +1 -1
- package/dist/{isBigInt-DKe0M6hp.js → isBigInt-Cx4j_cju.js} +2 -2
- package/dist/{isBigInt-DKe0M6hp.js.map → isBigInt-Cx4j_cju.js.map} +1 -1
- package/dist/{isEmptyObject-ZkSwRC_D.cjs → isEmptyObject-DYypq92-.cjs} +3 -3
- package/dist/{isEmptyObject-ZkSwRC_D.cjs.map → isEmptyObject-DYypq92-.cjs.map} +1 -1
- package/dist/{isEmptyObject-DCipFwxJ.js → isEmptyObject-Dp4pCGTa.js} +3 -3
- package/dist/{isEmptyObject-DCipFwxJ.js.map → isEmptyObject-Dp4pCGTa.js.map} +1 -1
- package/dist/{isEmptyString-BXzKAC2j.js → isEmptyString-BXgKFQxw.js} +3 -3
- package/dist/{isEmptyString-BXzKAC2j.js.map → isEmptyString-BXgKFQxw.js.map} +1 -1
- package/dist/{isEmptyString-UiiUsSQj.cjs → isEmptyString-CzY-9G-x.cjs} +3 -3
- package/dist/{isEmptyString-UiiUsSQj.cjs.map → isEmptyString-CzY-9G-x.cjs.map} +1 -1
- package/dist/{isEmptyValue-jqOr7OHD.js → isEmptyValue-B7Zd9DI3.js} +5 -5
- package/dist/{isEmptyValue-jqOr7OHD.js.map → isEmptyValue-B7Zd9DI3.js.map} +1 -1
- package/dist/{isEmptyValue-BQzcjVaL.cjs → isEmptyValue-Ci3kVU4E.cjs} +5 -5
- package/dist/{isEmptyValue-BQzcjVaL.cjs.map → isEmptyValue-Ci3kVU4E.cjs.map} +1 -1
- package/dist/{isEqual-BvumA3RA.cjs → isEqual-Bd7mClU4.cjs} +4 -4
- package/dist/{isEqual-BvumA3RA.cjs.map → isEqual-Bd7mClU4.cjs.map} +1 -1
- package/dist/{isEqual-BX49cF9m.js → isEqual-CylDBGUr.js} +4 -4
- package/dist/{isEqual-BX49cF9m.js.map → isEqual-CylDBGUr.js.map} +1 -1
- package/dist/{isFinite-BCnaDpod.js → isFinite-CZ-Aiy4T.js} +3 -3
- package/dist/{isFinite-BCnaDpod.js.map → isFinite-CZ-Aiy4T.js.map} +1 -1
- package/dist/{isFinite-D24ZaE6c.cjs → isFinite-D8jhlqPf.cjs} +3 -3
- package/dist/{isFinite-D24ZaE6c.cjs.map → isFinite-D8jhlqPf.cjs.map} +1 -1
- package/dist/{isInteger-Caeuz0rB.cjs → isInteger-CCJhQ1Mx.cjs} +2 -2
- package/dist/{isInteger-Caeuz0rB.cjs.map → isInteger-CCJhQ1Mx.cjs.map} +1 -1
- package/dist/{isInteger-naMbJsxJ.js → isInteger-DK_0nvNU.js} +2 -2
- package/dist/{isInteger-naMbJsxJ.js.map → isInteger-DK_0nvNU.js.map} +1 -1
- package/dist/{isPlainObject-BF-2-phb.d.cts → isPlainObject-BGg8uHeL.d.cts} +1 -1
- package/dist/{isPlainObject-DxNDL8XU.d.ts → isPlainObject-DtduwtUp.d.ts} +1 -1
- package/dist/{isServer-Da3o3XSs.js → isServer-C2YtJlD1.js} +2 -2
- package/dist/{isServer-Da3o3XSs.js.map → isServer-C2YtJlD1.js.map} +1 -1
- package/dist/{isServer-BJHVnixd.cjs → isServer-CT9EkFKG.cjs} +2 -2
- package/dist/{isServer-BJHVnixd.cjs.map → isServer-CT9EkFKG.cjs.map} +1 -1
- package/dist/{isTypedArray-TJptiw2b.cjs → isTypedArray-BT7YP1S9.cjs} +3 -3
- package/dist/{isTypedArray-TJptiw2b.cjs.map → isTypedArray-BT7YP1S9.cjs.map} +1 -1
- package/dist/{isTypedArray-DuNA8tK6.js → isTypedArray-hcngU0pK.js} +3 -3
- package/dist/{isTypedArray-DuNA8tK6.js.map → isTypedArray-hcngU0pK.js.map} +1 -1
- package/dist/{isValidDomain-DoE98yhJ.js → isValidDomain-BCN5-tj9.js} +9 -6
- package/dist/isValidDomain-BCN5-tj9.js.map +1 -0
- package/dist/{isValidDomain-BB9IGhJs.cjs → isValidDomain-CXWNcRo6.cjs} +14 -5
- package/dist/isValidDomain-CXWNcRo6.cjs.map +1 -0
- package/dist/{noop-ubqAIbHD.js → noop-BDfC9Zq4.js} +2 -2
- package/dist/{noop-ubqAIbHD.js.map → noop-BDfC9Zq4.js.map} +1 -1
- package/dist/{noop-B13_ii35.cjs → noop-D4tT7tWN.cjs} +2 -2
- package/dist/{noop-B13_ii35.cjs.map → noop-D4tT7tWN.cjs.map} +1 -1
- package/dist/{normalizeSpaces-Bg2IZW7W.js → normalizeSpaces-B289eKyK.js} +3 -3
- package/dist/{normalizeSpaces-Bg2IZW7W.js.map → normalizeSpaces-B289eKyK.js.map} +1 -1
- package/dist/{normalizeSpaces-ZXnR4Qzp.cjs → normalizeSpaces-BTXDP8vZ.cjs} +3 -3
- package/dist/{normalizeSpaces-ZXnR4Qzp.cjs.map → normalizeSpaces-BTXDP8vZ.cjs.map} +1 -1
- package/dist/operations/index.cjs +7 -7
- package/dist/operations/index.d.cts +1 -1
- package/dist/operations/index.d.ts +1 -1
- package/dist/operations/index.js +7 -7
- package/dist/parsers/index.cjs +2 -2
- package/dist/parsers/index.d.cts +1 -1
- package/dist/parsers/index.d.ts +1 -1
- package/dist/parsers/index.js +2 -2
- package/dist/{parsers-OqDeffqc.cjs → parsers-5G1Lface.cjs} +4 -4
- package/dist/{parsers-OqDeffqc.cjs.map → parsers-5G1Lface.cjs.map} +1 -1
- package/dist/{parsers-BSBPgvsq.js → parsers-CNag9LsX.js} +4 -4
- package/dist/{parsers-BSBPgvsq.js.map → parsers-CNag9LsX.js.map} +1 -1
- package/dist/{parsing-DOGSCH6N.cjs → parsing-B6vxp2h8.cjs} +3 -3
- package/dist/{parsing-DOGSCH6N.cjs.map → parsing-B6vxp2h8.cjs.map} +1 -1
- package/dist/{parsing-Cao8b358.js → parsing-CobzHooQ.js} +3 -3
- package/dist/{parsing-Cao8b358.js.map → parsing-CobzHooQ.js.map} +1 -1
- package/dist/predicates/index.cjs +15 -16
- package/dist/predicates/index.d.cts +3 -3
- package/dist/predicates/index.d.ts +3 -3
- package/dist/predicates/index.js +14 -15
- package/dist/{predicates-D0ubqgqy.cjs → predicates-DlID4Pks.cjs} +13 -13
- package/dist/{predicates-D0ubqgqy.cjs.map → predicates-DlID4Pks.cjs.map} +1 -1
- package/dist/{predicates-Bj6meyXV.js → predicates-DvLryz_b.js} +11 -11
- package/dist/{predicates-Bj6meyXV.js.map → predicates-DvLryz_b.js.map} +1 -1
- package/dist/promises/index.cjs +4 -4
- package/dist/promises/index.d.cts +1 -1
- package/dist/promises/index.d.ts +1 -1
- package/dist/promises/index.js +4 -4
- package/dist/{punyCode-D-Qu6nj6.cjs → punyCode-DaBFrfga.cjs} +4 -4
- package/dist/{punyCode-D-Qu6nj6.cjs.map → punyCode-DaBFrfga.cjs.map} +1 -1
- package/dist/{punyCode-8SrbMWfM.js → punyCode-GakZYPCT.js} +4 -4
- package/dist/{punyCode-8SrbMWfM.js.map → punyCode-GakZYPCT.js.map} +1 -1
- package/dist/{removeSpaces-Bmc5DX4F.js → removeSpaces-BJ-zNnJq.js} +7 -4
- package/dist/removeSpaces-BJ-zNnJq.js.map +1 -0
- package/dist/{removeSpaces-CWIvhZHg.cjs → removeSpaces-DhJ1haaH.cjs} +12 -3
- package/dist/removeSpaces-DhJ1haaH.cjs.map +1 -0
- package/dist/rzl-utils.global.js +1 -1
- package/dist/{safeJsonParse-Sms2CJf4.cjs → safeJsonParse-DiWBPfaE.cjs} +6 -6
- package/dist/{safeJsonParse-Sms2CJf4.cjs.map → safeJsonParse-DiWBPfaE.cjs.map} +1 -1
- package/dist/{safeJsonParse-BP38mwlj.js → safeJsonParse-bcBYPjeb.js} +6 -6
- package/dist/{safeJsonParse-BP38mwlj.js.map → safeJsonParse-bcBYPjeb.js.map} +1 -1
- package/dist/{safeStableStringify-CXOZ9Ub8.js → safeStableStringify-C1SA52w7.js} +4 -4
- package/dist/{safeStableStringify-CXOZ9Ub8.js.map → safeStableStringify-C1SA52w7.js.map} +1 -1
- package/dist/{safeStableStringify-CJtP89qn.cjs → safeStableStringify-DSwcrPll.cjs} +4 -4
- package/dist/{safeStableStringify-CJtP89qn.cjs.map → safeStableStringify-DSwcrPll.cjs.map} +1 -1
- package/dist/strings/index.cjs +6 -7
- package/dist/strings/index.cjs.map +1 -1
- package/dist/strings/index.d.cts +2 -2
- package/dist/strings/index.d.ts +2 -2
- package/dist/strings/index.js +5 -6
- package/dist/strings/index.js.map +1 -1
- package/dist/tailwind/index.cjs +6 -2
- package/dist/tailwind/index.d.cts +3 -3
- package/dist/tailwind/index.d.ts +3 -3
- package/dist/tailwind/index.js +3 -3
- package/dist/{tailwind-B2ssevxq.js → tailwind-D62asDBR.js} +18 -8
- package/dist/tailwind-D62asDBR.js.map +1 -0
- package/dist/{tailwind-CHIx9uxu.cjs → tailwind-DMPTwJ0X.cjs} +39 -5
- package/dist/tailwind-DMPTwJ0X.cjs.map +1 -0
- package/dist/{toStringArrayUnRecursive-DJGtPsFb.js → toStringArrayUnRecursive-DBicbWv8.js} +6 -6
- package/dist/{toStringArrayUnRecursive-DJGtPsFb.js.map → toStringArrayUnRecursive-DBicbWv8.js.map} +1 -1
- package/dist/{toStringArrayUnRecursive-C4zYCja7.cjs → toStringArrayUnRecursive-DJ8JBO3N.cjs} +6 -6
- package/dist/{toStringArrayUnRecursive-C4zYCja7.cjs.map → toStringArrayUnRecursive-DJ8JBO3N.cjs.map} +1 -1
- package/dist/urls/index.cjs +9 -120
- package/dist/urls/index.d.cts +1 -1
- package/dist/urls/index.d.ts +1 -1
- package/dist/urls/index.js +3 -114
- package/dist/urls-DCjjZNTH.cjs +299 -0
- package/dist/urls-DCjjZNTH.cjs.map +1 -0
- package/dist/urls-DTndjYDD.js +263 -0
- package/dist/urls-DTndjYDD.js.map +1 -0
- package/package.json +3 -24
- package/dist/formatEnvPort-B3OLxQk9.cjs +0 -171
- package/dist/formatEnvPort-B3OLxQk9.cjs.map +0 -1
- package/dist/formatEnvPort-ByFVLjSV.js +0 -159
- package/dist/formatEnvPort-ByFVLjSV.js.map +0 -1
- package/dist/isURL-C-kSk6KJ.js +0 -14
- package/dist/isURL-C-kSk6KJ.js.map +0 -1
- package/dist/isURL-DeUPO_oR.cjs +0 -20
- package/dist/isURL-DeUPO_oR.cjs.map +0 -1
- package/dist/isValidDomain-BB9IGhJs.cjs.map +0 -1
- package/dist/isValidDomain-DoE98yhJ.js.map +0 -1
- package/dist/next/index.cjs +0 -129
- package/dist/next/index.cjs.map +0 -1
- package/dist/next/index.d.cts +0 -220
- package/dist/next/index.d.ts +0 -220
- package/dist/next/index.js +0 -124
- package/dist/next/index.js.map +0 -1
- package/dist/next/server/index.cjs +0 -28
- package/dist/next/server/index.cjs.map +0 -1
- package/dist/next/server/index.d.cts +0 -39
- package/dist/next/server/index.d.ts +0 -39
- package/dist/next/server/index.js +0 -26
- package/dist/next/server/index.js.map +0 -1
- package/dist/normalizeString-BDdkaXui.js +0 -15
- package/dist/normalizeString-BDdkaXui.js.map +0 -1
- package/dist/normalizeString-BE6ELqEb.cjs +0 -21
- package/dist/normalizeString-BE6ELqEb.cjs.map +0 -1
- package/dist/removeSpaces-Bmc5DX4F.js.map +0 -1
- package/dist/removeSpaces-CWIvhZHg.cjs.map +0 -1
- package/dist/tailwind-B2ssevxq.js.map +0 -1
- package/dist/tailwind-CHIx9uxu.cjs.map +0 -1
- package/dist/urls/index.cjs.map +0 -1
- package/dist/urls/index.js.map +0 -1
package/dist/next/index.js
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* ========================================================================
|
|
3
|
-
* @rzl-zone/utils-js
|
|
4
|
-
* ------------------------------------------------------------------------
|
|
5
|
-
* Version: `3.12.1-beta.1`
|
|
6
|
-
* Author: `Rizalvin Dwiky <rizalvindwiky@gmail.com>`
|
|
7
|
-
* Repository: `https://github.com/rzl-zone/rzl-zone/tree/main/packages/utils-js`
|
|
8
|
-
* ========================================================================
|
|
9
|
-
*/
|
|
10
|
-
import { A as isPlainObject, N as isNil, S as isUndefined, T as assertIsPlainObject, b as hasOwnProp, k as isString, p as isNonEmptyArray, r as getPreciseType, t as assertIsBoolean, v as isError, y as isNonEmptyString } from "../assertIsBoolean-DR1SaXPD.js";
|
|
11
|
-
import { t as assertIsString } from "../assertIsString-CEB07_83.js";
|
|
12
|
-
import { t as safeStableStringify } from "../safeStableStringify-CXOZ9Ub8.js";
|
|
13
|
-
import { t as isEmptyString } from "../isEmptyString-BXzKAC2j.js";
|
|
14
|
-
import { t as removeSpaces } from "../removeSpaces-Bmc5DX4F.js";
|
|
15
|
-
import { n as normalizePathname, t as formatEnvPort } from "../formatEnvPort-ByFVLjSV.js";
|
|
16
|
-
function generateRoute(route, params) {
|
|
17
|
-
if (!isString(route) || isEmptyString(route)) throw new TypeError(`❌ 'generateRoute' Failed:\n- Invalid 'route' value.\n- Must be of type \`string\` and non-empty string, but received: "${getPreciseType(route)}": \`${safeStableStringify(route, { keepUndefined: true })}\`.`);
|
|
18
|
-
if (!/[\\[\]]/.test(route)) return route;
|
|
19
|
-
assertIsPlainObject(params, { message: ({ validType }) => `❌ 'generateRoute' Failed cause in route "${route}":\n- Missing or invalid parameters \`${validType}\` for route: "${route}", must be of type \`${validType}\` mapping parameters.` });
|
|
20
|
-
if (isNil(params)) throw new TypeError(`❌ 'generateRoute' Failed cause in route "${route}":\n- Missing parameters \`plain-object\` for route: "${route}".`);
|
|
21
|
-
const invalidChars = [
|
|
22
|
-
"?",
|
|
23
|
-
"&",
|
|
24
|
-
"#",
|
|
25
|
-
"=",
|
|
26
|
-
"/",
|
|
27
|
-
"'",
|
|
28
|
-
"\"",
|
|
29
|
-
"(",
|
|
30
|
-
")",
|
|
31
|
-
"+",
|
|
32
|
-
";",
|
|
33
|
-
"%",
|
|
34
|
-
"@",
|
|
35
|
-
":"
|
|
36
|
-
];
|
|
37
|
-
const errors = [];
|
|
38
|
-
const requiredKeys = Array.from(route.matchAll(/\[(\w+)\]/g)).map((m) => m[1] || "");
|
|
39
|
-
for (const key of requiredKeys) {
|
|
40
|
-
const value = params[key];
|
|
41
|
-
if (!isString(value)) {
|
|
42
|
-
errors.push(`- Invalid parameter: "${key}" must be of type \`string\`, but received: \`${getPreciseType(value)}\`.`);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
if (isEmptyString(value)) {
|
|
46
|
-
errors.push(`- Parameter "${key}" cannot be empty string.`);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
const foundInvalidChars = invalidChars.filter((char) => value.includes(char));
|
|
50
|
-
if (/\s/.test(value)) foundInvalidChars.push("white-space(s)");
|
|
51
|
-
if (foundInvalidChars.length > 0) {
|
|
52
|
-
const formattedChars = foundInvalidChars.map((c) => c === "`" ? "backtick - (`)" : `\`${c}\``);
|
|
53
|
-
if (!invalidChars.includes("white-space(s)")) invalidChars.push("white-space(s)");
|
|
54
|
-
const formattedInvalidChars = invalidChars.map((c) => c === "`" ? "backtick - (`)" : `\`${c}\``);
|
|
55
|
-
errors.push(`- Parameter "${key}" contains invalid characters (${formattedChars.join(", ")}). These characters are not allowed because they could cause issues in URL structure. The following characters are forbidden in route parameters: (${formattedInvalidChars.join(", ")}).`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (isNonEmptyArray(errors)) throw new Error(`❌ 'generateRoute' Failed cause in route "${route}":\n${errors.join("\n")}.`);
|
|
59
|
-
return route.replace(/\[(\w+)\]/g, (_, key) => {
|
|
60
|
-
return (isNonEmptyString(params[key]) ? params[key] : "").trim().replace(/^\/+|\/+$/g, "");
|
|
61
|
-
}).replace(/\/+/g, "/");
|
|
62
|
-
}
|
|
63
|
-
const createBeApiUrl = (pathname, options = {}) => {
|
|
64
|
-
try {
|
|
65
|
-
assertIsString(isNil(pathname) ? "" : pathname, { message({ currentType, validType }) {
|
|
66
|
-
return `First parameter (\`pathname\`) must be of type \`${validType}\`, but received: \`${currentType}\`.`;
|
|
67
|
-
} });
|
|
68
|
-
if (!isPlainObject(options)) options = {};
|
|
69
|
-
let { prefix = "/api", withOrigin = true } = options;
|
|
70
|
-
if (!isUndefined(prefix) && !isString(prefix)) throw new TypeError(`Parameter \`prefix\` property of the \`options\` (second parameter) must be of type \`string\`, but received: \`${getPreciseType(prefix)}\`.`);
|
|
71
|
-
assertIsBoolean(withOrigin, { message: ({ currentType, validType }) => `Parameter \`withOrigin\` property of the \`options\` (second parameter) must be of type \`${validType}\`, but received: \`${currentType}\`.` });
|
|
72
|
-
pathname = normalizePathname(pathname);
|
|
73
|
-
prefix = normalizePathname(prefix);
|
|
74
|
-
const normalizedPrefix = prefix.endsWith("/") ? prefix : prefix + "/";
|
|
75
|
-
if (pathname === prefix || pathname === prefix + "/" || pathname.startsWith(normalizedPrefix)) {
|
|
76
|
-
pathname = pathname.slice(prefix.length);
|
|
77
|
-
pathname = normalizePathname(pathname);
|
|
78
|
-
}
|
|
79
|
-
const baseApiUrl = getBeApiUrl({ suffix: prefix });
|
|
80
|
-
function joinPath(a, b) {
|
|
81
|
-
return `${a.replace(/\/+$/, "")}/${b.replace(/^\/+/, "")}`;
|
|
82
|
-
}
|
|
83
|
-
return (withOrigin ? joinPath(baseApiUrl, pathname) : joinPath(new URL(baseApiUrl).pathname, pathname)).replace(/\/+$/, "");
|
|
84
|
-
} catch (err) {
|
|
85
|
-
if (isError(err)) throw err;
|
|
86
|
-
else throw new Error("Failed to generate backend API URL in `createBeApiUrl()`, Error: " + String(err).trim(), { cause: err });
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
const getBeApiUrl = (options = {}) => {
|
|
90
|
-
if (!isPlainObject(options)) options = {};
|
|
91
|
-
let suffix = hasOwnProp(options, "suffix") ? options.suffix : "/";
|
|
92
|
-
assertIsString(suffix, { message({ currentType, validType }) {
|
|
93
|
-
return `Parameter \`suffix\` property of the first parameter must be of type \`${validType}\`, but received: \`${currentType}\`.`;
|
|
94
|
-
} });
|
|
95
|
-
try {
|
|
96
|
-
let rawBaseUrl = process.env.NEXT_PUBLIC_BACKEND_API_URL?.trim();
|
|
97
|
-
if (rawBaseUrl) {
|
|
98
|
-
rawBaseUrl = removeSpaces(rawBaseUrl);
|
|
99
|
-
const urlObj = new URL(rawBaseUrl);
|
|
100
|
-
if (!!!urlObj.port && process.env.NEXT_PUBLIC_PORT_BE) rawBaseUrl = urlObj.origin + formatEnvPort(process.env.NEXT_PUBLIC_PORT_BE, { prefixColon: true });
|
|
101
|
-
} else rawBaseUrl = "http://localhost" + formatEnvPort(process.env.NEXT_PUBLIC_PORT_BE || "8000", { prefixColon: true });
|
|
102
|
-
suffix = removeSpaces(suffix).length ? removeSpaces(suffix) : "/";
|
|
103
|
-
return `${new URL(rawBaseUrl.replace(/\/+$/, "")).origin}${suffix === "/" ? "/" : `${suffix.startsWith("/") ? "" : "/"}${suffix.replace(/\/+$/, "")}`}`;
|
|
104
|
-
} catch (error) {
|
|
105
|
-
throw new Error("Invalid `NEXT_PUBLIC_BACKEND_API_URL`, failed to generate from `getBeApiUrl()`, Error:" + error, { cause: error });
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
const getBaseUrl = () => {
|
|
109
|
-
try {
|
|
110
|
-
const baseEnv = process.env.NEXT_PUBLIC_BASE_URL?.trim();
|
|
111
|
-
const portEnv = process.env.NEXT_PUBLIC_PORT_FE?.trim();
|
|
112
|
-
let baseUrl = baseEnv || "http://localhost";
|
|
113
|
-
baseUrl = removeSpaces(baseUrl).replace(/\/+$/, "");
|
|
114
|
-
const hasPort = /:\/\/[^/]+:\d+/.test(baseUrl);
|
|
115
|
-
if (!hasPort && portEnv) baseUrl += formatEnvPort(portEnv, { prefixColon: true });
|
|
116
|
-
else if (!hasPort && !baseEnv) baseUrl += ":3000";
|
|
117
|
-
const url = new URL(baseUrl);
|
|
118
|
-
return `${url.protocol}//${url.hostname}${url.port ? `:${url.port}` : ""}`;
|
|
119
|
-
} catch (error) {
|
|
120
|
-
throw new Error("Invalid `NEXT_PUBLIC_BASE_URL`, failed to generate from `getBaseUrl()`, Error:" + error, { cause: error });
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
export { createBeApiUrl, generateRoute, getBaseUrl, getBeApiUrl };
|
|
124
|
-
//# sourceMappingURL=index.js.map
|
package/dist/next/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/next/generateRoute.ts","../../src/next/createBeApiUrl.ts","../../src/next/getBeApiUrl.ts","../../src/next/getBaseUrl.ts"],"sourcesContent":["import type { IsAny } from \"@rzl-zone/ts-types-plus\";\n\nimport { isNil } from \"@/predicates/is/isNil\";\nimport { isString } from \"@/predicates/is/isString\";\nimport { isEmptyString } from \"@/predicates/is/isEmptyString\";\nimport { isNonEmptyArray } from \"@/predicates/is/isNonEmptyArray\";\nimport { getPreciseType } from \"@/predicates/type/getPreciseType\";\nimport { isNonEmptyString } from \"@/predicates/is/isNonEmptyString\";\nimport { assertIsPlainObject } from \"@/assertions/objects/assertIsPlainObject\";\nimport { safeStableStringify } from \"@/conversions/stringify/safeStableStringify\";\n\n/** ---------------------------------------------------------\n * * ***Extracts dynamic route parameters from a given route string.***\n * ---------------------------------------------------------\n * **This utility type recursively searches for dynamic segments within a route,\n * extracting each parameter and constructing an object where each key represents\n * a dynamic segment and its value is of type `string`.**\n * - ***⚠️ Warning:***\n * - ***This types only support when using ***[`NextJS`](https://nextjs.org/)***.***\n * @template T - The route string containing potential dynamic segments.\n * @example\n * ```ts\n * type Params1 = ExtractRouteParams<\"/user/[id]\">;\n * // ➔ { id: string }\n * type Params2 = ExtractRouteParams<\"/post/[slug]/comment/[commentId]\">;\n * // ➔ { slug: string; commentId: string }\n * type Params3 = ExtractRouteParams<\"/dashboard\">;\n * // ➔ {} (no dynamic parameters)\n * ```\n */\nexport type ExtractRouteParams<T> = T extends string\n ? HasDynamicSegments<T> extends true\n ? T extends `${infer _Start}[${infer Param}]${infer Rest}`\n ? { [K in Param | keyof ExtractRouteParams<Rest>]: string }\n : unknown\n : unknown\n : unknown; // Ensures an empty object if no dynamic segments are found.\n\n/** ---------------------------------------------------------\n * * ***Determines whether a given route contains dynamic segments.***\n * ---------------------------------------------------------\n * **This type checks if the route includes at least one `[param]` pattern.\n * If it does, the result is `true`, otherwise `false`.**\n * - ***⚠️ Warning:***\n * - ***This types only support when using ***[`NextJS`](https://nextjs.org/)***.***\n * @template T - The route string to be evaluated.\n * @example\n * ```ts\n * type HasParams1 = HasDynamicSegments<\"/user/[id]\">;\n * // ➔ true\n * type HasParams2 = HasDynamicSegments<\"/settings/profile\">;\n * // ➔ false\n * type HasParams3 = HasDynamicSegments<\"/blog/[category]/[slug]\">;\n * // ➔ true\n * ```\n */\nexport type HasDynamicSegments<T> = T extends `${string}[${string}]${string}`\n ? true\n : false;\n\ntype GenerateRouteResult<T> =\n true extends IsAny<T> ? unknown : T extends string ? string : unknown;\n\n/** ---------------------------------\n * * ***Utility for NextJS: `generateRoute`.***\n * ---------------------------------\n * **Generates a URL by replacing dynamic route parameters with provided values.**\n * - ***⚠️ Warning:***\n * - ***This function only support when using ***[`NextJS`](https://nextjs.org/)***.***\n * @template T - The route string containing dynamic segments in the format `[param]`.\n * @param {T} route - The route string containing dynamic segments.\n * @param {ExtractRouteParams<T>} [params] - An object containing key-value pairs that match the dynamic segments in the route.\n * @returns {string} The formatted URL with all dynamic segments replaced.\n * @throws **{@link Error | `Error`}** if the route contains dynamic segments but no parameters object is provided.\n * @throws **{@link Error | `Error`}** if a required parameter is missing from the `params` object.\n * @throws **{@link Error | `Error`}** if a parameter value is an empty string.\n * @throws **{@link Error | `Error`}** if any parameter contains invalid characters like `?`, `&`, `=`, `#`, `/`, spaces, `'`, `\"`, `(`, `)`, `+`, `;`, `%`, `@`, or `:`, which can cause URL issues.\n * @example\n * // Basic usage\n * generateRoute(\"/user/[id]\", { id: \"123\" });\n * // ➔ \"/user/123\"\n *\n * // No dynamic segments, returns as-is\n * generateRoute(\"/dashboard\");\n * // ➔ \"/dashboard\"\n *\n * // Throws an error due to missing parameters object\n * generateRoute(\"/profile/[username]\");\n * // ➔ ❌ Error: ❌ Missing parameters object for route: \"/profile/[username]\"\n *\n * // Throws an error due to an empty parameter value\n * generateRoute(\"/post/[category]/[slug]\", { category: \"tech\", slug: \"\" });\n * // ➔ ❌ Error: ❌ Parameter \"slug\" cannot be empty in route: \"/post/[category]/[slug]\"\n *\n * // Throws an error due to parameter containing invalid characters\n * generateRoute(\"/search/[query]\", { query: \"how to?learn\" });\n * // ➔ ❌ Error: ❌ Parameter \"query\" contains invalid character \"?\" in route: \"/search/[query]\"\n *\n * // Handles leading/trailing slashes correctly\n * generateRoute(\"/blog/[category]/[slug]\", { category: \"/news/\", slug: \"/latest-update/\" });\n * // ➔ ❌ Error: ❌ Parameter \"category\" and \"slug\" contains slashes \"/\" which is not allowed.\n */\nexport function generateRoute<T extends string>(\n route: T extends string\n ? HasDynamicSegments<T> extends true\n ? T\n : never\n : never,\n params: T extends string ? ExtractRouteParams<T> : undefined\n): GenerateRouteResult<T>;\nexport function generateRoute<T extends string>(\n route: T extends string ? T : never,\n params?: Extract<ExtractRouteParams<T>, Record<string, unknown>>\n): GenerateRouteResult<T>;\nexport function generateRoute<T = unknown>(\n route: T extends string\n ? HasDynamicSegments<T> extends true\n ? T\n : unknown\n : unknown,\n params?: T extends string ? ExtractRouteParams<T> : undefined\n): unknown;\nexport function generateRoute<T>(\n route: T,\n params?: ExtractRouteParams<T>\n): string | unknown {\n //todo: Validate the route string\n if (!isString(route) || isEmptyString(route)) {\n throw new TypeError(\n `❌ 'generateRoute' Failed:\\n- Invalid 'route' value.\\n- Must be of type \\`string\\` and non-empty string, but received: \"${getPreciseType(\n route\n )}\": \\`${safeStableStringify(route, {\n keepUndefined: true\n })}\\`.`\n );\n }\n\n //todo: If no dynamic segments exist, return the route as-is immediately\n if (!/[\\\\[\\]]/.test(route)) {\n return route;\n }\n\n //todo: Validate that params is a plain object\n assertIsPlainObject(params, {\n message: ({ validType }) =>\n `❌ 'generateRoute' Failed cause in route \"${route}\":\\n- Missing or invalid parameters \\`${validType}\\` for route: \"${route}\", must be of type \\`${validType}\\` mapping parameters.`\n });\n\n //todo: Ensure parameters are provided for dynamic routes.\n if (isNil(params)) {\n throw new TypeError(\n `❌ 'generateRoute' Failed cause in route \"${route}\":\\n- Missing parameters \\`plain-object\\` for route: \"${route}\".`\n );\n }\n\n //todo: Check for invalid characters that can break the URL format\n const invalidChars = [\n \"?\",\n \"&\",\n \"#\",\n \"=\",\n \"/\",\n \"'\",\n // eslint-disable-next-line quotes\n '\"',\n \"(\",\n \")\",\n \"+\",\n \";\",\n \"%\",\n \"@\",\n \":\"\n ];\n\n const errors: string[] = [];\n\n const requiredKeys = Array.from(route.matchAll(/\\[(\\w+)\\]/g)).map(\n (m) => m[1] || \"\"\n );\n\n for (const key of requiredKeys) {\n const value = params[key];\n\n if (!isString(value)) {\n errors.push(\n `- Invalid parameter: \"${key}\" must be of type \\`string\\`, but received: \\`${getPreciseType(\n value\n )}\\`.`\n );\n continue;\n }\n\n if (isEmptyString(value)) {\n errors.push(`- Parameter \"${key}\" cannot be empty string.`);\n continue;\n }\n\n const foundInvalidChars = invalidChars.filter((char) =>\n value.includes(char)\n );\n\n if (/\\s/.test(value)) {\n foundInvalidChars.push(\"white-space(s)\");\n }\n\n if (foundInvalidChars.length > 0) {\n const formattedChars = foundInvalidChars.map((c) =>\n c === \"`\" ? \"backtick - (`)\" : `\\`${c}\\``\n );\n\n if (!invalidChars.includes(\"white-space(s)\"))\n invalidChars.push(\"white-space(s)\");\n\n const formattedInvalidChars = invalidChars.map((c) =>\n c === \"`\" ? \"backtick - (`)\" : `\\`${c}\\``\n );\n\n errors.push(\n `- Parameter \"${key}\" contains invalid characters (${formattedChars.join(\n \", \"\n )}). These characters are not allowed because they could cause issues in URL structure. The following characters are forbidden in route parameters: (${formattedInvalidChars.join(\n \", \"\n )}).`\n );\n }\n }\n\n if (isNonEmptyArray(errors)) {\n throw new Error(\n `❌ 'generateRoute' Failed cause in route \"${route}\":\\n${errors.join(\"\\n\")}.`\n );\n }\n\n return route\n .replace(/\\[(\\w+)\\]/g, (_, key) => {\n const paramKey = isNonEmptyString(params[key]) ? params[key] : \"\";\n\n return paramKey.trim().replace(/^\\/+|\\/+$/g, \"\");\n })\n .replace(/\\/+/g, \"/\");\n}\n","import { getBeApiUrl } from \"@/next\";\nimport { normalizePathname } from \"@/urls/pathname/normalizePathname\";\n\nimport { isNil } from \"@/predicates/is/isNil\";\nimport { isError } from \"@/predicates/is/isError\";\nimport { isString } from \"@/predicates/is/isString\";\nimport { isUndefined } from \"@/predicates/is/isUndefined\";\nimport { isPlainObject } from \"@/predicates/is/isPlainObject\";\nimport { getPreciseType } from \"@/predicates/type/getPreciseType\";\nimport { assertIsString } from \"@/assertions/strings/assertIsString\";\nimport { assertIsBoolean } from \"@/assertions/booleans/assertIsBoolean\";\n\ntype OptionsCreateBeApiUrl = {\n /** * The prefix pathname api url, e.g:`\"http://localhost.com/your-target-prefix-entri-point-api-is-here\"`, default: `\"/api\"`.\n *\n * @default \"/api\" */\n prefix?: string;\n /** * Option to getting `prefix` and `pathname` of api url only `(removing origin base api url)`, default: `true`.\n *\n * @default true */\n withOrigin?: boolean;\n};\n\n/** ---------------------------------\n * * ***Utility for NextJS: `createBeApiUrl`.***\n * ---------------------------------\n * **Constructs a backend API URL by appending a given pathname to the base API URL.**\n * - **ℹ️ Note:**\n * - This function builds on top of `getBeApiUrl()`.\n * - **Determines the base API URL from:**\n * - `NEXT_PUBLIC_BACKEND_API_URL` environment variable (or defaults to `\"http://localhost:8000\"`).\n * - Automatically appends `NEXT_PUBLIC_PORT_BE` if the base URL does not already include a port.\n * - **Features of this function:**\n * - Allows customizing the API path with an optional `prefix` (defaults to `\"/api\"`).\n * - Can include or exclude the origin (protocol + host) via `withOrigin`.\n * - Normalizes paths to avoid duplicate slashes.\n * - ***⚠️ Warning:***\n * - ***This function only support when using ***[`NextJS`](https://nextjs.org/)***.***\n * @param {string|null|undefined} pathname - The API endpoint path (e.g., `/users` or `/v1/posts`), defaultValue: `\"\"`.\n * @param {OptionsCreateBeApiUrl} [options] - Configuration options.\n * @param {OptionsCreateBeApiUrl[\"prefix\"]} [options.prefix=\"/api\"] - The prefix for the API path (default is `\"/api\"`).\n * @param {OptionsCreateBeApiUrl[\"withOrigin\"]} [options.withOrigin=true] - Whether to include the full base URL or return only the API path.\n * @returns {string} The formatted API URL.\n * @throws **{@link TypeError | `TypeError`}** if `withOrigin` is not a boolean.\n * @throws **{@link TypeError | `TypeError`}** if `prefix` and `pathname` is not a string.\n * @throws **{@link Error | `Error`}** if constructing the API URL fails due to an invalid base URL.\n * @example\n * createBeApiUrl(\"/users\")\n * // ➔ \"http://localhost:8000/api/users\"\n * createBeApiUrl(\"/api/users\")\n * // ➔ \"http://localhost:8000/api/users\"\n * createBeApiUrl(\"/v1\", { prefix: \"/v1\" })\n * // ➔ \"http://localhost:8000/v1\"\n * createBeApiUrl(\"/v1/users\")\n * // ➔ \"http://localhost:8000/api/v1/users\"\n * createBeApiUrl(\"/v1/users\", { prefix: \"/v1\" })\n * // ➔ \"http://localhost:8000/v1/users\"\n * createBeApiUrl(\"/users\", { withOrigin: false })\n * // ➔ \"/api/users\"\n * createBeApiUrl(null, { withOrigin: false })\n * // ➔ \"/api\"\n * createBeApiUrl(undefined, { withOrigin: false })\n * // ➔ \"/api\"\n */\nexport const createBeApiUrl = (\n /** * The pathname api url, e.g:`\"http://localhost.com/your-target-prefix-entri-point-api-is-here/your-target-pathname-is-here\"`.\n *\n * @default \"\"\n */\n pathname: string | null | undefined,\n options: OptionsCreateBeApiUrl = {}\n): string => {\n try {\n // ✅ Type checks\n assertIsString(isNil(pathname) ? \"\" : pathname, {\n message({ currentType, validType }) {\n return `First parameter (\\`pathname\\`) must be of type \\`${validType}\\`, but received: \\`${currentType}\\`.`;\n }\n });\n\n if (!isPlainObject(options)) {\n options = {};\n }\n\n let { prefix = \"/api\", withOrigin = true } = options;\n\n if (!isUndefined(prefix) && !isString(prefix)) {\n throw new TypeError(\n `Parameter \\`prefix\\` property of the \\`options\\` (second parameter) must be of type \\`string\\`, but received: \\`${getPreciseType(\n prefix\n )}\\`.`\n );\n }\n\n assertIsBoolean(withOrigin, {\n message: ({ currentType, validType }) =>\n `Parameter \\`withOrigin\\` property of the \\`options\\` (second parameter) must be of type \\`${validType}\\`, but received: \\`${currentType}\\`.`\n });\n\n // Normalize pathname\n pathname = normalizePathname(pathname);\n // Normalize prefix\n prefix = normalizePathname(prefix);\n\n const normalizedPrefix = prefix.endsWith(\"/\") ? prefix : prefix + \"/\";\n\n // Remove duplicate prefix in pathname\n if (\n pathname === prefix ||\n pathname === prefix + \"/\" ||\n pathname.startsWith(normalizedPrefix)\n ) {\n pathname = pathname.slice(prefix.length);\n pathname = normalizePathname(pathname);\n }\n\n // Get the base API URL\n const baseApiUrl = getBeApiUrl({ suffix: prefix });\n\n function joinPath(a: string, b: string) {\n return `${a.replace(/\\/+$/, \"\")}/${b.replace(/^\\/+/, \"\")}`;\n }\n\n const fullPath = withOrigin\n ? joinPath(baseApiUrl, pathname)\n : joinPath(new URL(baseApiUrl).pathname, pathname);\n\n return fullPath.replace(/\\/+$/, \"\");\n } catch (err) {\n if (isError(err)) {\n throw err;\n } else {\n throw new Error(\n \"Failed to generate backend API URL in `createBeApiUrl()`, Error: \" +\n String(err).trim(),\n { cause: err }\n );\n }\n }\n};\n","import { formatEnvPort } from \"@/urls/utils/formatEnvPort\";\nimport { hasOwnProp } from \"@/predicates/has/hasOwnProp\";\nimport { isPlainObject } from \"@/predicates/is/isPlainObject\";\nimport { removeSpaces } from \"@/strings/sanitizations/removeSpaces\";\nimport { assertIsString } from \"@/assertions/strings/assertIsString\";\n\ntype OptionsGetBeApiUrl = {\n /** * ***The Suffix origin base api url, e.g:`http://localhost.com/api`, default: `\"/\"`.***\n *\n * @default \"/\" */\n suffix?: string;\n};\n\n/** ---------------------------------------------------\n * * ***Utility for NextJS: `getBeApiUrl`.***\n * ---------------------------------------------------\n * **This function determines the backend API base URL from the `NEXT_PUBLIC_BACKEND_API_URL` environment variable (retrieves the base API URL of the backend).**\n * - **Behavior:**\n * - If the variable is not set, it defaults to `\"http://localhost:8000\"`.\n * - It also allows adding an optional suffix to the returned URL.\n * - ***⚠️ Warning:***\n * - ***This function only support when using ***[`NextJS`](https://nextjs.org/)***.***\n * @description\n * This function determines the backend API base URL from the `NEXT_PUBLIC_BACKEND_API_URL` environment variable.\n * - If `NEXT_PUBLIC_BACKEND_API_URL` is not set, it defaults to `\"http://localhost:8000\"`.\n * - If `NEXT_PUBLIC_BACKEND_API_URL` does **not** contain a port, it appends one from `NEXT_PUBLIC_PORT_BE` if available.\n * - Supports appending optional suffix (like `\"/api\"`).\n * @param {OptionsGetBeApiUrl|undefined} options - Configuration options.\n * @param {OptionsGetBeApiUrl[\"suffix\"]} [options.suffix=\"/\"] - The suffix to append to the base API URL.\n * @returns {string} The formatted backend API base URL.\n * @throws **{@link TypeError | `TypeError`}** if `suffix` is not a `string`.\n * @throws **{@link Error | `Error`}** if `NEXT_PUBLIC_BACKEND_API_URL` is invalid.\n * @example\n * // With NEXT_PUBLIC_BACKEND_API_URL set at `*.env` file\n * NEXT_PUBLIC_BACKEND_API_URL = \"https://api.example.com\";\n * getBeApiUrl();\n * // ➔ \"https://api.example.com/\"\n *\n * // With NEXT_PUBLIC_BACKEND_API_URL but no port, using NEXT_PUBLIC_PORT_BE at `*.env` file\n * NEXT_PUBLIC_BACKEND_API_URL = \"http://localhost\";\n * NEXT_PUBLIC_PORT_BE = \"5000\";\n * getBeApiUrl({ suffix: \"/api\" });\n * // ➔ \"http://localhost:5000/api\"\n *\n * // Without NEXT_PUBLIC_BACKEND_API_URL at `*.env` file (defaults to localhost:8000)\n * delete NEXT_PUBLIC_BACKEND_API_URL;\n * getBeApiUrl({ suffix: \"/v1\" });\n * // ➔ \"http://localhost:8000/v1\"\n */\nexport const getBeApiUrl = (options: OptionsGetBeApiUrl = {}): string => {\n if (!isPlainObject(options)) options = {};\n\n let suffix = hasOwnProp(options, \"suffix\") ? options.suffix : \"/\";\n\n // Ensure suffix is a string\n assertIsString(suffix, {\n message({ currentType, validType }) {\n return `Parameter \\`suffix\\` property of the first parameter must be of type \\`${validType}\\`, but received: \\`${currentType}\\`.`;\n }\n });\n\n try {\n let rawBaseUrl = process.env.NEXT_PUBLIC_BACKEND_API_URL?.trim();\n\n if (rawBaseUrl) {\n rawBaseUrl = removeSpaces(rawBaseUrl);\n\n const urlObj = new URL(rawBaseUrl);\n const hasPort = !!urlObj.port;\n\n if (!hasPort && process.env.NEXT_PUBLIC_PORT_BE) {\n rawBaseUrl =\n urlObj.origin +\n formatEnvPort(process.env.NEXT_PUBLIC_PORT_BE, {\n prefixColon: true\n });\n }\n } else {\n // fallback\n rawBaseUrl =\n \"http://localhost\" +\n formatEnvPort(process.env.NEXT_PUBLIC_PORT_BE || \"8000\", {\n prefixColon: true\n });\n }\n\n suffix = removeSpaces(suffix).length ? removeSpaces(suffix) : \"/\";\n const baseApiUrl = new URL(rawBaseUrl.replace(/\\/+$/, \"\")).origin;\n\n const finalSuffix =\n suffix === \"/\"\n ? \"/\"\n : `${suffix.startsWith(\"/\") ? \"\" : \"/\"}${suffix.replace(/\\/+$/, \"\")}`;\n\n return `${baseApiUrl}${finalSuffix}`;\n } catch (error) {\n throw new Error(\n \"Invalid `NEXT_PUBLIC_BACKEND_API_URL`, failed to generate from `getBeApiUrl()`, Error:\" +\n error,\n { cause: error }\n );\n }\n};\n","import { formatEnvPort } from \"@/urls/utils/formatEnvPort\";\nimport { removeSpaces } from \"@/strings/sanitizations/removeSpaces\";\n\n/** ---------------------------------------------------\n * * ***Utility for NextJS: `getBaseUrl`.***\n * ---------------------------------------------------\n * **Retrieves the base URL of the application.**\n * - **Behavior:**\n * - It determines the base URL from the `NEXT_PUBLIC_BASE_URL` environment variable.\n * - If `NEXT_PUBLIC_BASE_URL` is not set, it defaults to `\"http://localhost:3000\"`.\n * - If `NEXT_PUBLIC_BASE_URL` does **not** contain a port, it appends one from `NEXT_PUBLIC_PORT_FE` if available.\n * - Ensures the final URL is valid and normalized (no trailing slashes).\n * - ***⚠️ Warning:***\n * - ***This function only support when using ***[`NextJS`](https://nextjs.org/)***.***\n * @returns {string} The resolved base URL of the application.\n * @throws **{@link Error | `Error`}** if the constructed URL is invalid or malformed.\n * @example\n * // With environment variable set at `*.env` file\n * NEXT_PUBLIC_BASE_URL = \"https://example.com\";\n * getBaseUrl();\n * // ➔ \"https://example.com\"\n *\n * // With custom port via NEXT_PUBLIC_PORT_FE at `*.env` file\n * NEXT_PUBLIC_BASE_URL = \"http://localhost\";\n * NEXT_PUBLIC_PORT_FE = \"4000\";\n * getBaseUrl();\n * // ➔ \"http://localhost:4000\"\n *\n * // Without environment variable at `*.env` file\n * delete NEXT_PUBLIC_BASE_URL;\n * getBaseUrl();\n * // ➔ \"http://localhost:3000\"\n */\nexport const getBaseUrl = (): string => {\n try {\n const baseEnv = process.env.NEXT_PUBLIC_BASE_URL?.trim();\n const portEnv = process.env.NEXT_PUBLIC_PORT_FE?.trim();\n\n let baseUrl = baseEnv || \"http://localhost\";\n\n // Always clean trailing slashes first\n baseUrl = removeSpaces(baseUrl).replace(/\\/+$/, \"\");\n\n // Check if already contains port\n const hasPort = /:\\/\\/[^/]+:\\d+/.test(baseUrl);\n\n if (!hasPort && portEnv) {\n baseUrl += formatEnvPort(portEnv, { prefixColon: true });\n } else if (!hasPort && !baseEnv) {\n baseUrl += \":3000\";\n }\n\n const url = new URL(baseUrl);\n return `${url.protocol}//${url.hostname}${url.port ? `:${url.port}` : \"\"}`;\n } catch (error) {\n throw new Error(\n \"Invalid `NEXT_PUBLIC_BASE_URL`, failed to generate from `getBaseUrl()`, Error:\" +\n error,\n { cause: error }\n );\n }\n};\n"],"mappings":"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"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* ========================================================================
|
|
3
|
-
* @rzl-zone/utils-js
|
|
4
|
-
* ------------------------------------------------------------------------
|
|
5
|
-
* Version: `3.12.1-beta.1`
|
|
6
|
-
* Author: `Rizalvin Dwiky <rizalvindwiky@gmail.com>`
|
|
7
|
-
* Repository: `https://github.com/rzl-zone/rzl-zone/tree/main/packages/utils-js`
|
|
8
|
-
* ========================================================================
|
|
9
|
-
*/
|
|
10
|
-
"use strict";
|
|
11
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
12
|
-
const require_assertIsBoolean = require('../../assertIsBoolean-C8WEXVr2.cjs');
|
|
13
|
-
require("@rzl-zone/node-only");
|
|
14
|
-
let next_server = require("next/server");
|
|
15
|
-
const getClientIpOrUrl = (request, includeFullUrl = true) => {
|
|
16
|
-
if (!require_assertIsBoolean.isFunction(next_server.NextRequest)) throw new Error("Function `getClientIpOrUrl` is designed to be used in a `NextJS` environment.");
|
|
17
|
-
if (!(request instanceof next_server.NextRequest)) throw new TypeError("First parameter (`request`) must be an `instance of NextRequest` from `NextJS`.");
|
|
18
|
-
require_assertIsBoolean.assertIsBoolean(includeFullUrl, { message: ({ currentType, validType }) => `Second parameter (\`includeFullUrl\`) must be of type \`${validType}\`, but received: \`${currentType}\`.` });
|
|
19
|
-
const forwardedIps = (request.headers.get("x-forwarded-for") ?? "127.0.0.1").trim().split(",");
|
|
20
|
-
if (forwardedIps[0] === "::ffff:127.0.0.1" || forwardedIps[0] === "::1") forwardedIps[0] = "127.0.0.1";
|
|
21
|
-
const clientIp = forwardedIps.length > 1 ? forwardedIps[forwardedIps.length - 1]?.trim() || "" : forwardedIps[0] || "";
|
|
22
|
-
if (!includeFullUrl) return clientIp;
|
|
23
|
-
const protocol = request.headers.get("x-forwarded-proto") || "http";
|
|
24
|
-
const port = request.headers.get("x-forwarded-port") || "3000";
|
|
25
|
-
return `${process.env.NODE_ENV === "production" ? protocol : "http"}://${clientIp}:${port}`;
|
|
26
|
-
};
|
|
27
|
-
exports.getClientIpOrUrl = getClientIpOrUrl;
|
|
28
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["isFunction","NextRequest"],"sources":["../../../src/next/server/getClientIpOrUrl.ts"],"sourcesContent":["import \"@rzl-zone/node-only\";\n\nimport { NextRequest } from \"next/server\";\n\nimport { isFunction } from \"@/predicates/is/isFunction\";\nimport { assertIsBoolean } from \"@/assertions/booleans/assertIsBoolean\";\n\n/** ---------------------------------\n * * ***Utility for NextJS Server: `getClientIpOrUrl`.***\n * ---------------------------------\n * **Retrieves the real client IP address and constructs the full URL using headers like `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-port`.**\n * - **ℹ️ Note:**\n * - Only supported in **Next.js** environments (specifically in `server-only` contexts).\n * - Should be used in **middleware**, **route-handler** or **server actions** that have access to ***[`NextRequest - NextJS`](https://nextjs.org/docs/app/api-reference/functions/next-request)***.\n * @param {NextRequest} request - The incoming ***`NextJS`*** request object, must be instanceof `NextRequest` from `next/server`.\n * @param {boolean|undefined} [includeFullUrl=true] - Whether to return the full URL (`protocol`, `IP`, and `port` like `protocol://ip:port`) or just the IP address, defaultValue: `true`.\n * @returns {string} The extracted client IP address or the full constructed URL.\n * @throws **{@link Error | `Error`}** if the function is used outside a Next.js server environment.\n * @throws **{@link TypeError | `TypeError`}** if the arguments do not match the expected types.\n * @example\n * // Basic usage in Next.js middleware\n * import { NextRequest } from \"next/server\";\n * import { getClientIpOrUrl } from \"@rzl-zone/utils-js/next/server\";\n *\n * export function middleware(request: NextRequest) {\n * const clientIp = getClientIpOrUrl(request, false);\n * console.log(\"Client IP:\", clientIp);\n * }\n *\n * // Get full URL\n * const url = getClientIpOrUrl(request);\n * console.log(\"Client full URL:\", url);\n */\nexport const getClientIpOrUrl = (\n request: NextRequest,\n includeFullUrl: boolean = true\n): string => {\n // Ensure we're in a Next.js edge/server environment\n if (!isFunction(NextRequest)) {\n throw new Error(\n \"Function `getClientIpOrUrl` is designed to be used in a `NextJS` environment.\"\n );\n }\n\n if (!(request instanceof NextRequest)) {\n throw new TypeError(\n \"First parameter (`request`) must be an `instance of NextRequest` from `NextJS`.\"\n );\n }\n\n assertIsBoolean(includeFullUrl, {\n message: ({ currentType, validType }) =>\n `Second parameter (\\`includeFullUrl\\`) must be of type \\`${validType}\\`, but received: \\`${currentType}\\`.`\n });\n\n const forwardedIps = (request.headers.get(\"x-forwarded-for\") ?? \"127.0.0.1\")\n .trim()\n .split(\",\");\n\n // Normalize IPv6 loopback addresses\n if (forwardedIps[0] === \"::ffff:127.0.0.1\" || forwardedIps[0] === \"::1\") {\n forwardedIps[0] = \"127.0.0.1\";\n }\n\n // Get the last non-empty IP from the list (more reliable for real client IP)\n const clientIp: string =\n forwardedIps.length > 1\n ? forwardedIps[forwardedIps.length - 1]?.trim() || \"\"\n : forwardedIps[0] || \"\";\n\n if (!includeFullUrl) {\n return clientIp;\n }\n\n // Construct full URL using protocol, IP, and port\n const protocol = request.headers.get(\"x-forwarded-proto\") || \"http\";\n // const protocol = \"http\";\n const port = request.headers.get(\"x-forwarded-port\") || \"3000\";\n\n return `${\n process.env.NODE_ENV === \"production\" ? protocol : \"http\"\n }://${clientIp}:${port}`;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,oBACX,SACA,iBAA0B,SACf;CAEX,IAAI,CAACA,mCAAWC,wBAAY,EAC1B,MAAM,IAAI,MACR,gFACD;CAGH,IAAI,EAAE,mBAAmBA,0BACvB,MAAM,IAAI,UACR,kFACD;CAGH,wCAAgB,gBAAgB,EAC9B,UAAU,EAAE,aAAa,gBACvB,2DAA2D,UAAU,sBAAsB,YAAY,MAC1G,CAAC;CAEF,MAAM,gBAAgB,QAAQ,QAAQ,IAAI,kBAAkB,IAAI,aAC7D,MAAM,CACN,MAAM,IAAI;CAGb,IAAI,aAAa,OAAO,sBAAsB,aAAa,OAAO,OAChE,aAAa,KAAK;CAIpB,MAAM,WACJ,aAAa,SAAS,IAClB,aAAa,aAAa,SAAS,IAAI,MAAM,IAAI,KACjD,aAAa,MAAM;CAEzB,IAAI,CAAC,gBACH,OAAO;CAIT,MAAM,WAAW,QAAQ,QAAQ,IAAI,oBAAoB,IAAI;CAE7D,MAAM,OAAO,QAAQ,QAAQ,IAAI,mBAAmB,IAAI;CAExD,OAAO,GACL,QAAQ,IAAI,aAAa,eAAe,WAAW,OACpD,KAAK,SAAS,GAAG"}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* ========================================================================
|
|
3
|
-
* @rzl-zone/utils-js
|
|
4
|
-
* ------------------------------------------------------------------------
|
|
5
|
-
* Version: `3.12.1-beta.1`
|
|
6
|
-
* Author: `Rizalvin Dwiky <rizalvindwiky@gmail.com>`
|
|
7
|
-
* Repository: `https://github.com/rzl-zone/rzl-zone/tree/main/packages/utils-js`
|
|
8
|
-
* ========================================================================
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { NextRequest } from "next/server";
|
|
12
|
-
/** ---------------------------------
|
|
13
|
-
* * ***Utility for NextJS Server: `getClientIpOrUrl`.***
|
|
14
|
-
* ---------------------------------
|
|
15
|
-
* **Retrieves the real client IP address and constructs the full URL using headers like `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-port`.**
|
|
16
|
-
* - **ℹ️ Note:**
|
|
17
|
-
* - Only supported in **Next.js** environments (specifically in `server-only` contexts).
|
|
18
|
-
* - Should be used in **middleware**, **route-handler** or **server actions** that have access to ***[`NextRequest - NextJS`](https://nextjs.org/docs/app/api-reference/functions/next-request)***.
|
|
19
|
-
* @param {NextRequest} request - The incoming ***`NextJS`*** request object, must be instanceof `NextRequest` from `next/server`.
|
|
20
|
-
* @param {boolean|undefined} [includeFullUrl=true] - Whether to return the full URL (`protocol`, `IP`, and `port` like `protocol://ip:port`) or just the IP address, defaultValue: `true`.
|
|
21
|
-
* @returns {string} The extracted client IP address or the full constructed URL.
|
|
22
|
-
* @throws **{@link Error | `Error`}** if the function is used outside a Next.js server environment.
|
|
23
|
-
* @throws **{@link TypeError | `TypeError`}** if the arguments do not match the expected types.
|
|
24
|
-
* @example
|
|
25
|
-
* // Basic usage in Next.js middleware
|
|
26
|
-
* import { NextRequest } from "next/server";
|
|
27
|
-
* import { getClientIpOrUrl } from "@rzl-zone/utils-js/next/server";
|
|
28
|
-
*
|
|
29
|
-
* export function middleware(request: NextRequest) {
|
|
30
|
-
* const clientIp = getClientIpOrUrl(request, false);
|
|
31
|
-
* console.log("Client IP:", clientIp);
|
|
32
|
-
* }
|
|
33
|
-
*
|
|
34
|
-
* // Get full URL
|
|
35
|
-
* const url = getClientIpOrUrl(request);
|
|
36
|
-
* console.log("Client full URL:", url);
|
|
37
|
-
*/
|
|
38
|
-
declare const getClientIpOrUrl: (request: NextRequest, includeFullUrl?: boolean) => string;
|
|
39
|
-
export { getClientIpOrUrl };
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* ========================================================================
|
|
3
|
-
* @rzl-zone/utils-js
|
|
4
|
-
* ------------------------------------------------------------------------
|
|
5
|
-
* Version: `3.12.1-beta.1`
|
|
6
|
-
* Author: `Rizalvin Dwiky <rizalvindwiky@gmail.com>`
|
|
7
|
-
* Repository: `https://github.com/rzl-zone/rzl-zone/tree/main/packages/utils-js`
|
|
8
|
-
* ========================================================================
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { NextRequest } from "next/server";
|
|
12
|
-
/** ---------------------------------
|
|
13
|
-
* * ***Utility for NextJS Server: `getClientIpOrUrl`.***
|
|
14
|
-
* ---------------------------------
|
|
15
|
-
* **Retrieves the real client IP address and constructs the full URL using headers like `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-port`.**
|
|
16
|
-
* - **ℹ️ Note:**
|
|
17
|
-
* - Only supported in **Next.js** environments (specifically in `server-only` contexts).
|
|
18
|
-
* - Should be used in **middleware**, **route-handler** or **server actions** that have access to ***[`NextRequest - NextJS`](https://nextjs.org/docs/app/api-reference/functions/next-request)***.
|
|
19
|
-
* @param {NextRequest} request - The incoming ***`NextJS`*** request object, must be instanceof `NextRequest` from `next/server`.
|
|
20
|
-
* @param {boolean|undefined} [includeFullUrl=true] - Whether to return the full URL (`protocol`, `IP`, and `port` like `protocol://ip:port`) or just the IP address, defaultValue: `true`.
|
|
21
|
-
* @returns {string} The extracted client IP address or the full constructed URL.
|
|
22
|
-
* @throws **{@link Error | `Error`}** if the function is used outside a Next.js server environment.
|
|
23
|
-
* @throws **{@link TypeError | `TypeError`}** if the arguments do not match the expected types.
|
|
24
|
-
* @example
|
|
25
|
-
* // Basic usage in Next.js middleware
|
|
26
|
-
* import { NextRequest } from "next/server";
|
|
27
|
-
* import { getClientIpOrUrl } from "@rzl-zone/utils-js/next/server";
|
|
28
|
-
*
|
|
29
|
-
* export function middleware(request: NextRequest) {
|
|
30
|
-
* const clientIp = getClientIpOrUrl(request, false);
|
|
31
|
-
* console.log("Client IP:", clientIp);
|
|
32
|
-
* }
|
|
33
|
-
*
|
|
34
|
-
* // Get full URL
|
|
35
|
-
* const url = getClientIpOrUrl(request);
|
|
36
|
-
* console.log("Client full URL:", url);
|
|
37
|
-
*/
|
|
38
|
-
declare const getClientIpOrUrl: (request: NextRequest, includeFullUrl?: boolean) => string;
|
|
39
|
-
export { getClientIpOrUrl };
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* ========================================================================
|
|
3
|
-
* @rzl-zone/utils-js
|
|
4
|
-
* ------------------------------------------------------------------------
|
|
5
|
-
* Version: `3.12.1-beta.1`
|
|
6
|
-
* Author: `Rizalvin Dwiky <rizalvindwiky@gmail.com>`
|
|
7
|
-
* Repository: `https://github.com/rzl-zone/rzl-zone/tree/main/packages/utils-js`
|
|
8
|
-
* ========================================================================
|
|
9
|
-
*/
|
|
10
|
-
import { P as isFunction, t as assertIsBoolean } from "../../assertIsBoolean-DR1SaXPD.js";
|
|
11
|
-
import "@rzl-zone/node-only";
|
|
12
|
-
import { NextRequest } from "next/server";
|
|
13
|
-
const getClientIpOrUrl = (request, includeFullUrl = true) => {
|
|
14
|
-
if (!isFunction(NextRequest)) throw new Error("Function `getClientIpOrUrl` is designed to be used in a `NextJS` environment.");
|
|
15
|
-
if (!(request instanceof NextRequest)) throw new TypeError("First parameter (`request`) must be an `instance of NextRequest` from `NextJS`.");
|
|
16
|
-
assertIsBoolean(includeFullUrl, { message: ({ currentType, validType }) => `Second parameter (\`includeFullUrl\`) must be of type \`${validType}\`, but received: \`${currentType}\`.` });
|
|
17
|
-
const forwardedIps = (request.headers.get("x-forwarded-for") ?? "127.0.0.1").trim().split(",");
|
|
18
|
-
if (forwardedIps[0] === "::ffff:127.0.0.1" || forwardedIps[0] === "::1") forwardedIps[0] = "127.0.0.1";
|
|
19
|
-
const clientIp = forwardedIps.length > 1 ? forwardedIps[forwardedIps.length - 1]?.trim() || "" : forwardedIps[0] || "";
|
|
20
|
-
if (!includeFullUrl) return clientIp;
|
|
21
|
-
const protocol = request.headers.get("x-forwarded-proto") || "http";
|
|
22
|
-
const port = request.headers.get("x-forwarded-port") || "3000";
|
|
23
|
-
return `${process.env.NODE_ENV === "production" ? protocol : "http"}://${clientIp}:${port}`;
|
|
24
|
-
};
|
|
25
|
-
export { getClientIpOrUrl };
|
|
26
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/next/server/getClientIpOrUrl.ts"],"sourcesContent":["import \"@rzl-zone/node-only\";\n\nimport { NextRequest } from \"next/server\";\n\nimport { isFunction } from \"@/predicates/is/isFunction\";\nimport { assertIsBoolean } from \"@/assertions/booleans/assertIsBoolean\";\n\n/** ---------------------------------\n * * ***Utility for NextJS Server: `getClientIpOrUrl`.***\n * ---------------------------------\n * **Retrieves the real client IP address and constructs the full URL using headers like `x-forwarded-for`, `x-forwarded-proto`, and `x-forwarded-port`.**\n * - **ℹ️ Note:**\n * - Only supported in **Next.js** environments (specifically in `server-only` contexts).\n * - Should be used in **middleware**, **route-handler** or **server actions** that have access to ***[`NextRequest - NextJS`](https://nextjs.org/docs/app/api-reference/functions/next-request)***.\n * @param {NextRequest} request - The incoming ***`NextJS`*** request object, must be instanceof `NextRequest` from `next/server`.\n * @param {boolean|undefined} [includeFullUrl=true] - Whether to return the full URL (`protocol`, `IP`, and `port` like `protocol://ip:port`) or just the IP address, defaultValue: `true`.\n * @returns {string} The extracted client IP address or the full constructed URL.\n * @throws **{@link Error | `Error`}** if the function is used outside a Next.js server environment.\n * @throws **{@link TypeError | `TypeError`}** if the arguments do not match the expected types.\n * @example\n * // Basic usage in Next.js middleware\n * import { NextRequest } from \"next/server\";\n * import { getClientIpOrUrl } from \"@rzl-zone/utils-js/next/server\";\n *\n * export function middleware(request: NextRequest) {\n * const clientIp = getClientIpOrUrl(request, false);\n * console.log(\"Client IP:\", clientIp);\n * }\n *\n * // Get full URL\n * const url = getClientIpOrUrl(request);\n * console.log(\"Client full URL:\", url);\n */\nexport const getClientIpOrUrl = (\n request: NextRequest,\n includeFullUrl: boolean = true\n): string => {\n // Ensure we're in a Next.js edge/server environment\n if (!isFunction(NextRequest)) {\n throw new Error(\n \"Function `getClientIpOrUrl` is designed to be used in a `NextJS` environment.\"\n );\n }\n\n if (!(request instanceof NextRequest)) {\n throw new TypeError(\n \"First parameter (`request`) must be an `instance of NextRequest` from `NextJS`.\"\n );\n }\n\n assertIsBoolean(includeFullUrl, {\n message: ({ currentType, validType }) =>\n `Second parameter (\\`includeFullUrl\\`) must be of type \\`${validType}\\`, but received: \\`${currentType}\\`.`\n });\n\n const forwardedIps = (request.headers.get(\"x-forwarded-for\") ?? \"127.0.0.1\")\n .trim()\n .split(\",\");\n\n // Normalize IPv6 loopback addresses\n if (forwardedIps[0] === \"::ffff:127.0.0.1\" || forwardedIps[0] === \"::1\") {\n forwardedIps[0] = \"127.0.0.1\";\n }\n\n // Get the last non-empty IP from the list (more reliable for real client IP)\n const clientIp: string =\n forwardedIps.length > 1\n ? forwardedIps[forwardedIps.length - 1]?.trim() || \"\"\n : forwardedIps[0] || \"\";\n\n if (!includeFullUrl) {\n return clientIp;\n }\n\n // Construct full URL using protocol, IP, and port\n const protocol = request.headers.get(\"x-forwarded-proto\") || \"http\";\n // const protocol = \"http\";\n const port = request.headers.get(\"x-forwarded-port\") || \"3000\";\n\n return `${\n process.env.NODE_ENV === \"production\" ? protocol : \"http\"\n }://${clientIp}:${port}`;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,oBACX,SACA,iBAA0B,SACf;CAEX,IAAI,CAAC,WAAW,YAAY,EAC1B,MAAM,IAAI,MACR,gFACD;CAGH,IAAI,EAAE,mBAAmB,cACvB,MAAM,IAAI,UACR,kFACD;CAGH,gBAAgB,gBAAgB,EAC9B,UAAU,EAAE,aAAa,gBACvB,2DAA2D,UAAU,sBAAsB,YAAY,MAC1G,CAAC;CAEF,MAAM,gBAAgB,QAAQ,QAAQ,IAAI,kBAAkB,IAAI,aAC7D,MAAM,CACN,MAAM,IAAI;CAGb,IAAI,aAAa,OAAO,sBAAsB,aAAa,OAAO,OAChE,aAAa,KAAK;CAIpB,MAAM,WACJ,aAAa,SAAS,IAClB,aAAa,aAAa,SAAS,IAAI,MAAM,IAAI,KACjD,aAAa,MAAM;CAEzB,IAAI,CAAC,gBACH,OAAO;CAIT,MAAM,WAAW,QAAQ,QAAQ,IAAI,oBAAoB,IAAI;CAE7D,MAAM,OAAO,QAAQ,QAAQ,IAAI,mBAAmB,IAAI;CAExD,OAAO,GACL,QAAQ,IAAI,aAAa,eAAe,WAAW,OACpD,KAAK,SAAS,GAAG"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* ========================================================================
|
|
3
|
-
* @rzl-zone/utils-js
|
|
4
|
-
* ------------------------------------------------------------------------
|
|
5
|
-
* Version: `3.12.1-beta.1`
|
|
6
|
-
* Author: `Rizalvin Dwiky <rizalvindwiky@gmail.com>`
|
|
7
|
-
* Repository: `https://github.com/rzl-zone/rzl-zone/tree/main/packages/utils-js`
|
|
8
|
-
* ========================================================================
|
|
9
|
-
*/
|
|
10
|
-
import { y as isNonEmptyString } from "./assertIsBoolean-DR1SaXPD.js";
|
|
11
|
-
const normalizeString = (input) => {
|
|
12
|
-
return isNonEmptyString(input) ? input.trim() : "";
|
|
13
|
-
};
|
|
14
|
-
export { normalizeString as t };
|
|
15
|
-
//# sourceMappingURL=normalizeString-BDdkaXui.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"normalizeString-BDdkaXui.js","names":[],"sources":["../src/strings/sanitizations/normalizeString.ts"],"sourcesContent":["import { isNonEmptyString } from \"@/predicates/is/isNonEmptyString\";\n\n/** ----------------------------------------------------------\n * * ***Utility: `normalizeString`.***\n * ----------------------------------------------------------\n * **Normalizes a string by ensuring it is a valid string and trimming whitespace.**\n * - **Behavior:**\n * - If the input is `undefined`, `null`, or an `empty string` after trimming,\n * it returns an empty string `(\"\")`.\n * @param {string | undefined | null} input - The input string to be normalize. If `null` or `undefined`, returns an empty string.\n * @returns {string} A trimmed string or an empty string if the input is invalid.\n * @example\n * normalizeString(\" Hello World \");\n * // ➔ \"Hello World\"\n * normalizeString(\" Hello World \");\n * // ➔ \"Hello World\"\n * normalizeString(\"\");\n * // ➔ \"\"\n * normalizeString(null);\n * // ➔ \"\"\n * normalizeString(undefined);\n * // ➔ \"\"\n */\nexport const normalizeString = (input: string | null | undefined): string => {\n return isNonEmptyString(input) ? input.trim() : \"\";\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,mBAAmB,UAA6C;CAC3E,OAAO,iBAAiB,MAAM,GAAG,MAAM,MAAM,GAAG"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* ========================================================================
|
|
3
|
-
* @rzl-zone/utils-js
|
|
4
|
-
* ------------------------------------------------------------------------
|
|
5
|
-
* Version: `3.12.1-beta.1`
|
|
6
|
-
* Author: `Rizalvin Dwiky <rizalvindwiky@gmail.com>`
|
|
7
|
-
* Repository: `https://github.com/rzl-zone/rzl-zone/tree/main/packages/utils-js`
|
|
8
|
-
* ========================================================================
|
|
9
|
-
*/
|
|
10
|
-
"use strict";
|
|
11
|
-
const require_assertIsBoolean = require('./assertIsBoolean-C8WEXVr2.cjs');
|
|
12
|
-
const normalizeString = (input) => {
|
|
13
|
-
return require_assertIsBoolean.isNonEmptyString(input) ? input.trim() : "";
|
|
14
|
-
};
|
|
15
|
-
Object.defineProperty(exports, 'normalizeString', {
|
|
16
|
-
enumerable: true,
|
|
17
|
-
get: function () {
|
|
18
|
-
return normalizeString;
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
//# sourceMappingURL=normalizeString-BE6ELqEb.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"normalizeString-BE6ELqEb.cjs","names":["isNonEmptyString"],"sources":["../src/strings/sanitizations/normalizeString.ts"],"sourcesContent":["import { isNonEmptyString } from \"@/predicates/is/isNonEmptyString\";\n\n/** ----------------------------------------------------------\n * * ***Utility: `normalizeString`.***\n * ----------------------------------------------------------\n * **Normalizes a string by ensuring it is a valid string and trimming whitespace.**\n * - **Behavior:**\n * - If the input is `undefined`, `null`, or an `empty string` after trimming,\n * it returns an empty string `(\"\")`.\n * @param {string | undefined | null} input - The input string to be normalize. If `null` or `undefined`, returns an empty string.\n * @returns {string} A trimmed string or an empty string if the input is invalid.\n * @example\n * normalizeString(\" Hello World \");\n * // ➔ \"Hello World\"\n * normalizeString(\" Hello World \");\n * // ➔ \"Hello World\"\n * normalizeString(\"\");\n * // ➔ \"\"\n * normalizeString(null);\n * // ➔ \"\"\n * normalizeString(undefined);\n * // ➔ \"\"\n */\nexport const normalizeString = (input: string | null | undefined): string => {\n return isNonEmptyString(input) ? input.trim() : \"\";\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,mBAAmB,UAA6C;CAC3E,OAAOA,yCAAiB,MAAM,GAAG,MAAM,MAAM,GAAG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"removeSpaces-Bmc5DX4F.js","names":[],"sources":["../src/strings/sanitizations/removeSpaces.ts"],"sourcesContent":["import { isPlainObject } from \"@/predicates/is/isPlainObject\";\nimport { isNonEmptyString } from \"@/predicates/is/isNonEmptyString\";\n\ntype RemoveSpacesOptions = {\n /** If `true`, only trims the string, defaultValue: `false`.\n *\n * @default false\n */\n trimOnly?: boolean;\n};\n\n/** ----------------------------------------------------------\n * * ***Utility: `removeSpaces`.***\n * ----------------------------------------------------------\n * **Removes all spaces from a string or trims only, based on the options provided.**\n * - **Behavior:**\n * - If `trimOnly` is `true`, the string is simply trimmed.\n * - Otherwise, removes **all spaces**, tabs, newlines, etc.\n * - If the input is `null` or `undefined`, returns an empty string `(\"\")`.\n * @param {string | null | undefined} value - The input string to be processed. If `null` or `undefined`, returns an empty string.\n * @param {RemoveSpacesOptions} [options] - The options object.\n * @param {RemoveSpacesOptions[\"trimOnly\"]} [options.trimOnly=false] - If `true`, only trims the string without removing spaces inside.\n * @returns {string} The processed string.\n * @example\n * removeSpaces(\" Hello World \");\n * // ➔ \"HelloWorld\"\n * removeSpaces(\" Hello World \", { trimOnly: true });\n * // ➔ \"Hello World\"\n * removeSpaces(null);\n * // ➔ \"\"\n */\nexport const removeSpaces = (\n value: string | null | undefined,\n options: RemoveSpacesOptions = {\n trimOnly: false\n }\n): string => {\n if (!isNonEmptyString(value)) return \"\";\n\n if (!isPlainObject(options)) {\n options = {};\n }\n\n const { trimOnly = false } = options;\n\n if (trimOnly) return value.trim();\n\n // Remove all spaces (including tabs, newlines, etc.)\n return value.replace(/\\s+/g, \"\");\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,gBACX,OACA,UAA+B,EAC7B,UAAU,OACX,KACU;CACX,IAAI,CAAC,iBAAiB,MAAM,EAAE,OAAO;CAErC,IAAI,CAAC,cAAc,QAAQ,EACzB,UAAU,EAAE;CAGd,MAAM,EAAE,WAAW,UAAU;CAE7B,IAAI,UAAU,OAAO,MAAM,MAAM;CAGjC,OAAO,MAAM,QAAQ,QAAQ,GAAG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"removeSpaces-CWIvhZHg.cjs","names":["isNonEmptyString","isPlainObject"],"sources":["../src/strings/sanitizations/removeSpaces.ts"],"sourcesContent":["import { isPlainObject } from \"@/predicates/is/isPlainObject\";\nimport { isNonEmptyString } from \"@/predicates/is/isNonEmptyString\";\n\ntype RemoveSpacesOptions = {\n /** If `true`, only trims the string, defaultValue: `false`.\n *\n * @default false\n */\n trimOnly?: boolean;\n};\n\n/** ----------------------------------------------------------\n * * ***Utility: `removeSpaces`.***\n * ----------------------------------------------------------\n * **Removes all spaces from a string or trims only, based on the options provided.**\n * - **Behavior:**\n * - If `trimOnly` is `true`, the string is simply trimmed.\n * - Otherwise, removes **all spaces**, tabs, newlines, etc.\n * - If the input is `null` or `undefined`, returns an empty string `(\"\")`.\n * @param {string | null | undefined} value - The input string to be processed. If `null` or `undefined`, returns an empty string.\n * @param {RemoveSpacesOptions} [options] - The options object.\n * @param {RemoveSpacesOptions[\"trimOnly\"]} [options.trimOnly=false] - If `true`, only trims the string without removing spaces inside.\n * @returns {string} The processed string.\n * @example\n * removeSpaces(\" Hello World \");\n * // ➔ \"HelloWorld\"\n * removeSpaces(\" Hello World \", { trimOnly: true });\n * // ➔ \"Hello World\"\n * removeSpaces(null);\n * // ➔ \"\"\n */\nexport const removeSpaces = (\n value: string | null | undefined,\n options: RemoveSpacesOptions = {\n trimOnly: false\n }\n): string => {\n if (!isNonEmptyString(value)) return \"\";\n\n if (!isPlainObject(options)) {\n options = {};\n }\n\n const { trimOnly = false } = options;\n\n if (trimOnly) return value.trim();\n\n // Remove all spaces (including tabs, newlines, etc.)\n return value.replace(/\\s+/g, \"\");\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,MAAa,gBACX,OACA,UAA+B,EAC7B,UAAU,OACX,KACU;CACX,IAAI,CAACA,yCAAiB,MAAM,EAAE,OAAO;CAErC,IAAI,CAACC,sCAAc,QAAQ,EACzB,UAAU,EAAE;CAGd,MAAM,EAAE,WAAW,UAAU;CAE7B,IAAI,UAAU,OAAO,MAAM,MAAM;CAGjC,OAAO,MAAM,QAAQ,QAAQ,GAAG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tailwind-B2ssevxq.js","names":["defaultConfig","getDefaultConfig","extendTailwindMerge"],"sources":["../src/tailwind/class-names/cx.ts","../src/tailwind/tw-merge/_private/validate-props.ts","../src/tailwind/tw-merge/v3/twMergeDefault.ts","../src/tailwind/tw-merge/v4/twMergeDefault.ts","../src/tailwind/class-names/customCn.ts","../src/tailwind/class-names/cn.ts","../src/tailwind/tw-macro/shouldForwardProp.ts"],"sourcesContent":["import { isArray } from \"@/predicates/is/isArray\";\nimport { isNumber } from \"@/predicates/is/isNumber\";\nimport { isStringObject } from \"@/predicates/is/isStringObject\";\nimport { isNumberObject } from \"@/predicates/is/isNumberObject\";\nimport { isBooleanObject } from \"@/predicates/is/isBooleanObject\";\nimport { isObjectOrArray } from \"@/predicates/is/isObjectOrArray\";\nimport { isNonEmptyString } from \"@/predicates/is/isNonEmptyString\";\n\n/** ----------------------------------------------------------\n * * ***Type-Utility: `ClassValue`.***\n * ----------------------------------------------------------\n * **Represents a single valid value that can be converted to a CSS class string.**\n * @description\n * - Supports the following types:\n * - `string` → literal class names (non-empty only)\n * - `number | bigint` → numeric class names\n * - `boolean` → only `true` is considered in objects/arrays\n * - `null | undefined` → ignored\n * - `ClassObject` → objects where **keys with truthy values** are included\n * - `ClassValues` → arrays recursively flattened\n * - Used internally by ***`cx` utility function*** to process mixed input values.\n * @example\n * ```ts\n * const val1: ClassValue = \"button\"; // ➔ string\n * const val2: ClassValue = 0; // ➔ number\n * const val3: ClassValue = [\"a\", { b: true }]; // ➔ array with object\n * const val4: ClassValue = { d: true, e: false }; // ➔ object\n * const val5: ClassValue = new String(\"foo\"); // ➔ boxed string\n * const val6: ClassValue = new Number(\"123\"); // ➔ boxed number\n * const val7: ClassValue = new Boolean(\"true\"); // ➔ boxed boolean\n * ```\n */\nexport type ClassValue =\n | ClassValues\n | ClassObject\n | string\n | number\n | bigint\n | null\n | boolean\n | undefined;\n\n/** ----------------------------------------------------------\n * * ***Type-Utility: `ClassObject`.***\n * ----------------------------------------------------------\n * **Represents an object whose keys with truthy values are included in the final class string.**\n * @example\n * ```ts\n * const obj: ClassObject = { \"text-red\": true, \"hidden\": false };\n * // ➔ \"text-red\" when processed by cx\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type ClassObject = Record<string, any>;\n\n/** ----------------------------------------------------------\n * * ***Type-Utility: `ClassValues`.***\n * ----------------------------------------------------------\n * **Represents an array of {@link ClassValue | `ClassValue's`}, potentially nested.**\n * @example\n * ```ts\n * const arr: ClassValues = [\n * \"a\",\n * 1,\n * [\"b\", { c: true, d: false }],\n * { e: 2 }\n * ];\n * ```\n */\nexport type ClassValues = ClassValue[];\n\n/** ----------------------------------------------------------\n * * ***Utility: `toStringValue`.***\n * ----------------------------------------------------------\n * **Converts a `ClassValue` into a single space-separated string suitable for CSS class usage.**\n * @param {ClassValue} value The mixed value to process.\n * @returns {string} A string containing all valid class names.\n * @description\n * - Arrays are recursively flattened.\n * - Objects include only keys with truthy values.\n * - Skips `null`, `undefined`, and empty strings.\n * - Numbers, strings, and bigint are included as-is.\n * - Boolean `false` is ignored in objects/arrays.\n * - Boxed primitives (`new String()`, `new Number()`, `new Boolean()`) are supported.\n * - Preserves inherited object keys.\n * @example\n * ```ts\n * toStringValue(\"btn\"); // ➔ \"btn\"\n * toStringValue([\"a\", 0, \"b\"]); // ➔ \"a b\"\n * toStringValue({ a: true, b: false, c: 1 }); // ➔ \"a c\"\n * toStringValue([\"a\", [\"b\", { c: true, d: false }]]); // ➔ \"a b c\"\n * toStringValue(new String(\"foo\")); // ➔ \"foo\"\n * toStringValue(new Number(42)); // ➔ \"42\"\n * toStringValue(new Boolean(true)); // ➔ \"true\"\n * toStringValue(new Boolean(false));// ➔ \"\"\n * ```\n */\nfunction toStringValue(value: ClassValue): string {\n let str = \"\";\n\n if (isNonEmptyString(value) || isNumber(value)) {\n str += value;\n } else if (isObjectOrArray(value)) {\n if (\n isStringObject(value) ||\n isNumberObject(value) ||\n isBooleanObject(value)\n ) {\n const val = value.valueOf();\n if (val) str += val; // skip falsy\n } else if (isArray(value)) {\n for (const item of value) {\n if (!item) continue; // skip falsy\n const y = toStringValue(item);\n if (!y) continue;\n if (str) str += \" \";\n str += y;\n }\n } else {\n for (const key in value) {\n if (!value[key]) continue; // skip falsy\n if (str) str += \" \";\n str += key;\n }\n }\n }\n\n return str;\n}\n\n/** ----------------------------------------------------------\n * * ***Utility: `cx`.***\n * ----------------------------------------------------------\n * **Merge multiple class values into a single, space-separated string suitable for CSS usage.**\n * @description\n * - Supports **nested combinations** of arrays and objects, recursively.\n * - **Falsy values** are skipped:\n * - `false`, `null`, `undefined`, empty strings `\"\"` are ignored anywhere.\n * - Numbers `0` are ignored inside nested arrays/objects.\n * - **Boxed primitives** are correctly unwrapped to their primitive value.\n * - **Inherited object keys** are included in the final class string.\n * - Optimized for **CSS class merging** where conditional inclusion is common.\n * @param {ClassValues} values\n * A list of mixed class values, which can include:\n * - **Strings** → literal class names.\n * - **Numbers** → numeric class names.\n * - **BigInt** → numeric class names larger than JS safe integer limit.\n * - **Arrays** → recursively flattened, can contain nested arrays or objects.\n * - **Objects** → include keys whose values are truthy. Inherited keys are also included.\n * - **Boxed primitives** (`new String()`, `new Number()`, `new Boolean()`) → automatically unwrapped.\n * - **Falsy values** (`false`, `null`, `undefined`, `\"\"`, `0`) are ignored according to original behavior.\n * @returns {string}\n * A single space-separated string containing all valid class names.\n * @example\n * ```ts\n * // Basic string merge\n * cx(\"btn\", \"btn-primary\");\n * // ➔ \"btn btn-primary\"\n *\n * // Mixed arrays and objects\n * cx(\"a\", [\"b\", { c: true, d: false }], { e: 1, f: 0 }, null, 2);\n * // ➔ \"a b c e 2\"\n *\n * // Empty and falsy values are ignored\n * cx(\"\", null, undefined, false, 0);\n * // ➔ \"\"\n *\n * // Nested arrays with objects\n * cx([\"a\", [\"b\", { c: true, d: false }]]);\n * // ➔ \"a b c\"\n *\n * // Boxed primitives are unwrapped\n * cx(new String(\"foo\"), new Number(42), new Boolean(true), new Number(0), new Boolean(false));\n * // ➔ \"foo 42 true\"\n *\n * // Inherited keys from objects are included\n * const proto = { inherited: true };\n * const obj = Object.create(proto);\n * obj.own = true;\n * cx(obj);\n * // ➔ \"own inherited\"\n * ```\n */\nexport function cx(...values: ClassValues): string {\n let str = \"\";\n\n for (const value of values) {\n if (!value) continue; // skip falsy arguments\n const x = toStringValue(value);\n if (!x) continue;\n if (str) str += \" \";\n str += x;\n }\n\n return str;\n}\n","import type { Config as TwConfig } from \"tailwindcss\";\nimport type { ConfigExtension } from \"tailwind-merge-v4\";\n\nimport type { OptionsMergeTwClsV4 } from \"../v4/_private/types\";\n\nimport { hasOwnProp } from \"@/predicates/has/hasOwnProp\";\n\nimport { isNumber } from \"@/predicates/is/isNumber\";\nimport { isFunction } from \"@/predicates/is/isFunction\";\nimport { isPlainObject } from \"@/predicates/is/isPlainObject\";\nimport { isNonEmptyArray } from \"@/predicates/is/isNonEmptyArray\";\nimport { isNonEmptyString } from \"@/predicates/is/isNonEmptyString\";\n\nexport type TailwindConfig = TwConfig;\n\n/**\n * @internal\n */\ntype TwMergeConfigExt = ConfigExtension<string, string>;\n\n/**\n * @internal\n */\ntype ValidatorTwMergeReturn = {\n config: TwConfig;\n override: TwMergeConfigExt[\"override\"];\n cacheSize: TwMergeConfigExt[\"cacheSize\"];\n prefix: TwMergeConfigExt[\"prefix\"];\n experimentalParseClassName?: TwMergeConfigExt[\"experimentalParseClassName\"];\n theme: NonNullable<NonNullable<TwMergeConfigExt[\"extend\"]>[\"theme\"]>;\n classGroups: NonNullable<\n NonNullable<TwMergeConfigExt[\"extend\"]>[\"classGroups\"]\n >;\n conflictingClassGroupModifiers: NonNullable<\n NonNullable<TwMergeConfigExt[\"extend\"]>[\"conflictingClassGroupModifiers\"]\n >;\n conflictingClassGroups: NonNullable<\n NonNullable<TwMergeConfigExt[\"extend\"]>[\"conflictingClassGroups\"]\n >;\n orderSensitiveModifiers: NonNullable<\n NonNullable<TwMergeConfigExt[\"extend\"]>[\"orderSensitiveModifiers\"]\n >;\n};\n\n/**\n * @internal\n */\nexport const validatorPropsTwMerge = (\n options: OptionsMergeTwClsV4\n): ValidatorTwMergeReturn => {\n if (!isPlainObject(options)) options = {};\n let {\n config,\n prefix,\n extend,\n override,\n cacheSize,\n experimentalParseClassName\n } = options;\n\n if (!isPlainObject(config)) config = {};\n if (!isPlainObject(extend)) extend = {};\n if (!isPlainObject(override)) override = {};\n if (!isNumber(cacheSize)) cacheSize = undefined;\n if (!isNonEmptyString(prefix)) prefix = undefined;\n if (!isFunction(experimentalParseClassName))\n experimentalParseClassName = undefined;\n\n const theme = hasOwnProp(extend, \"theme\") ? extend.theme : {};\n const classGroups = hasOwnProp(extend, \"classGroups\")\n ? extend.classGroups\n : {};\n const conflictingClassGroupModifiers = hasOwnProp(\n extend,\n \"conflictingClassGroupModifiers\"\n )\n ? extend.conflictingClassGroupModifiers\n : {};\n\n const conflictingClassGroups = hasOwnProp(extend, \"conflictingClassGroups\")\n ? extend.conflictingClassGroups\n : {};\n const orderSensitiveModifiers =\n hasOwnProp(extend, \"orderSensitiveModifiers\") &&\n isNonEmptyArray(extend.orderSensitiveModifiers)\n ? extend.orderSensitiveModifiers\n : [];\n\n return {\n config,\n override,\n cacheSize,\n prefix,\n experimentalParseClassName,\n theme,\n classGroups,\n conflictingClassGroupModifiers,\n conflictingClassGroups,\n orderSensitiveModifiers\n };\n};\n","import type { OptionsMergeTwClsV3, TwMergeDefaultFnV3 } from \"./_private/types\";\n\nimport { extendTailwindMerge, getDefaultConfig } from \"tailwind-merge-v3\";\n\nimport { isPlainObject } from \"@/predicates/is/isPlainObject\";\nimport { validatorPropsTwMerge } from \"../_private/validate-props\";\n\n// Get default Tailwind merge configuration\nconst defaultConfig = getDefaultConfig();\n\n/** -------------------------------------------------------------\n * * ***Customized Tailwind class merger Version 3 with extended rules.***\n * -------------------------------------------------------------\n * **Wraps ***`extendTailwindMerge` from tailwind-merge-v3*** with Tailwind’s default\n * config (_*`getDefaultConfig()` from tailwind-merge-v3*_) to create a **project-ready `twMerge`**.**\n * - 🔑 **When to use it?**\n * - Your project uses **Tailwind v3**.\n * - Extend **class groups** (e.g. add `text-shadow`).\n * - Respect your own **`tailwind.config.ts`** (colors, spacing, fontSize, etc).\n * - Override or fine-tune **merge behavior**.\n * - Create a **project-wide `cn` helper** that replaces raw `twMerge`.\n * @param {OptionsMergeTwClsV3} [options={}]\n * ***Merge options:***\n * - `config` – Your Tailwind config (from `tailwind.config.ts`).\n * - `prefix` - Utility prefix (e.g. `tw-` or `tw`).\n * - `extend` – Extra merge rules (classGroups, theme, etc).\n * - `override` – Fully replace rules.\n * - `cacheSize` – Parsed class cache size.\n * - `experimentalParseClassName` – Custom classname parser.\n * @returns {TwMergeDefaultFnV3}\n * Customized Tailwind class merge function version 3 (same signature as `twMerge`).\n * @example\n * #### Example 1: ***Default behavior (same as tailwind-merge).***\n * ```ts\n * import { twMergeDefaultV3 } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const twMerge = twMergeDefaultV3();\n * twMerge(\"p-2 p-4\");\n * // ➔ \"p-4\"\n * ```\n * #### Example 2: ***Extend class groups.***\n * ```ts\n * import { twMergeDefaultV3 } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const twMerge2 = twMergeDefaultV3({\n * extend: {\n * classGroups: {\n * shadow: [\"shadow-soft\", \"shadow-hard\"],\n * },\n * },\n * });\n * twMerge2(\"shadow-soft shadow-hard\");\n * // ➔ \"shadow-hard\"\n * ```\n * #### Example 3: ***Respect your Tailwind config.***\n * ```ts\n * import config from \"../tailwind.config\";\n * import { twMergeDefaultV3 } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const twMerge3 = twMergeDefaultV3({ config });\n * twMerge3(\"text-base text-xxs\");\n * // ➔ \"text-xxs\" (resolved from config)\n * ```\n * #### Example 4: ***Project-wide helper (recommended).***\n * ```ts\n * import configTwCss from \"../tailwind.config\";\n * import { customCnV3, twMergeDefaultV3, type ClassValues } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const customTwMerge = twMergeDefaultV3({\n * config: configTwCss,\n * extend: {\n * classGroups: { shadow: [\"shadow-soft\", \"shadow-hard\"] },\n * },\n * });\n *\n * export const cnApp = (...classes: ClassValues) => {\n * return customCnV3(customTwMerge, ...classes);\n * };\n *\n * // ✅ Usage\n * cnApp(\"p-2 p-4\"); // ➔ \"p-4\"\n * cnApp(\"shadow-soft shadow-hard\"); // ➔ \"shadow-hard\"\n * cnApp(\"text-base text-xxs\"); // ➔ \"text-xxs\" (uses config)\n *\n * // ⚡ Difference with package-level `cn`\n * import { cnV3, cnV4 } from \"@rzl-zone/utils-js/tailwind\";\n *\n * cnV3(\"text-base text-xxs\");\n * // or\n * cnV4(\"text-base text-xxs\");\n * // ➔ \"text-base\" (❌ doesn't know about your config)\n *\n * cnApp(\"text-base text-xxs\");\n * // ➔ \"text-xxs\" (✅ respects config)\n * ```\n */\nexport const twMergeDefaultV3 = (\n options: OptionsMergeTwClsV3 = {}\n): TwMergeDefaultFnV3 => {\n if (!isPlainObject(options)) options = {};\n const {\n cacheSize,\n classGroups,\n config,\n conflictingClassGroupModifiers,\n conflictingClassGroups,\n override,\n prefix,\n theme,\n experimentalParseClassName\n } = validatorPropsTwMerge(options);\n\n return extendTailwindMerge<string, string>({\n prefix: prefix ?? config.prefix,\n cacheSize: cacheSize || defaultConfig.cacheSize,\n experimentalParseClassName,\n override,\n extend: {\n conflictingClassGroupModifiers: {\n ...defaultConfig.conflictingClassGroupModifiers,\n ...conflictingClassGroupModifiers\n },\n theme: { ...defaultConfig.theme, ...theme },\n conflictingClassGroups: {\n ...defaultConfig.conflictingClassGroups,\n ...conflictingClassGroups\n },\n classGroups: {\n ...defaultConfig.classGroups,\n ...classGroups,\n\n \"text-shadow\": [\n \"\",\n \"-sm\",\n \"-md\",\n \"-lg\",\n \"-xl\",\n \"-xxl\",\n \"-none\",\n \"-default\"\n ].map((size) => `text-shadow${size}`),\n \"font-size\": Object.keys({\n ...(config.theme?.fontSize || {}),\n ...(config.theme?.extend?.fontSize || {})\n }).map((size) => `text-${size}`)\n }\n }\n });\n};\n","import type { OptionsMergeTwClsV4, TwMergeDefaultFnV4 } from \"./_private/types\";\n\nimport { extendTailwindMerge, getDefaultConfig } from \"tailwind-merge-v4\";\n\nimport { isPlainObject } from \"@/predicates/is/isPlainObject\";\nimport { validatorPropsTwMerge } from \"../_private/validate-props\";\n\n// Get default Tailwind merge configuration\nconst defaultConfig = getDefaultConfig();\n\n/** -------------------------------------------------------------\n * * ***Customized Tailwind class merger Version 4 with extended rules.***\n * -------------------------------------------------------------\n * **Wraps ***`extendTailwindMerge` from tailwind-merge-v4*** with Tailwind’s default\n * config (_*`getDefaultConfig()` from tailwind-merge-v4*_) to create a **project-ready `twMerge`**.**\n * - 🔑 **When to use it?**\n * - Your project uses **Tailwind v4**.\n * - Extend **class groups** (e.g. add `text-shadow`).\n * - Respect your own **`tailwind.config.ts`** (colors, spacing, fontSize, etc).\n * - Override or fine-tune **merge behavior**.\n * - Create a **project-wide `cn` helper** that replaces raw `twMerge`.\n * @param {OptionsMergeTwClsV4} [options={}]\n * ***Merge options:***\n * - `config` – Your Tailwind config (from `tailwind.config.ts`).\n * - `prefix` - Utility prefix (e.g. `tw-` or `tw`).\n * - `extend` – Extra merge rules (classGroups, theme, etc).\n * - `override` – Fully replace rules.\n * - `cacheSize` – Parsed class cache size.\n * - `experimentalParseClassName` – Custom classname parser.\n * @returns {TwMergeDefaultFnV4}\n * Customized Tailwind class merge function version 4 (same signature as `twMerge`).\n * @example\n * #### Example 1: ***Default behavior (same as tailwind-merge).***\n * ```ts\n * import { twMergeDefaultV4 } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const twMerge = twMergeDefaultV4();\n * twMerge(\"p-2 p-4\");\n * // ➔ \"p-4\"\n * ```\n * #### Example 2: ***Extend class groups.***\n * ```ts\n * import { twMergeDefaultV4 } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const twMerge2 = twMergeDefaultV4({\n * extend: {\n * classGroups: {\n * shadow: [\"shadow-soft\", \"shadow-hard\"],\n * },\n * },\n * });\n * twMerge2(\"shadow-soft shadow-hard\");\n * // ➔ \"shadow-hard\"\n * ```\n * #### Example 3: ***Respect your Tailwind config.***\n * ```ts\n * import config from \"../tailwind.config\";\n * import { twMergeDefaultV4 } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const twMerge3 = twMergeDefaultV4({ config });\n * twMerge3(\"text-base text-xxs\");\n * // ➔ \"text-xxs\" (resolved from config)\n * ```\n * #### Example 4: ***Project-wide helper (recommended).***\n * ```ts\n * import configTwCss from \"../tailwind.config\";\n * import { customCnV4, twMergeDefaultV4, type ClassValues } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const customTwMerge = twMergeDefaultV4({\n * config: configTwCss,\n * extend: {\n * classGroups: { shadow: [\"shadow-soft\", \"shadow-hard\"] },\n * },\n * });\n *\n * export const cnApp = (...classes: ClassValues) => {\n * return customCnV4(customTwMerge, ...classes);\n * };\n *\n * // ✅ Usage\n * cnApp(\"p-2 p-4\"); // ➔ \"p-4\"\n * cnApp(\"shadow-soft shadow-hard\"); // ➔ \"shadow-hard\"\n * cnApp(\"text-base text-xxs\"); // ➔ \"text-xxs\" (uses config)\n *\n * // ⚡ Difference with package-level `cn`\n * import { cnV3, cnV4 } from \"@rzl-zone/utils-js/tailwind\";\n *\n * cnV3(\"text-base text-xxs\");\n * // or\n * cnV4(\"text-base text-xxs\");\n * // ➔ \"text-base\" (❌ doesn't know about your config)\n *\n * cnApp(\"text-base text-xxs\");\n * // ➔ \"text-xxs\" (✅ respects config)\n * ```\n */\nexport const twMergeDefaultV4 = (\n options: OptionsMergeTwClsV4 = {}\n): TwMergeDefaultFnV4 => {\n if (!isPlainObject(options)) options = {};\n const {\n cacheSize,\n classGroups,\n config,\n conflictingClassGroupModifiers,\n conflictingClassGroups,\n orderSensitiveModifiers,\n override,\n prefix,\n theme,\n experimentalParseClassName\n } = validatorPropsTwMerge(options);\n\n return extendTailwindMerge<string, string>({\n prefix: prefix || config.prefix,\n cacheSize: cacheSize || defaultConfig.cacheSize,\n experimentalParseClassName,\n override,\n extend: {\n conflictingClassGroupModifiers: {\n ...defaultConfig.conflictingClassGroupModifiers,\n ...conflictingClassGroupModifiers\n },\n theme: { ...defaultConfig.theme, ...theme },\n conflictingClassGroups: {\n ...defaultConfig.conflictingClassGroups,\n ...conflictingClassGroups\n },\n orderSensitiveModifiers: [\n ...defaultConfig.orderSensitiveModifiers,\n ...orderSensitiveModifiers\n ],\n classGroups: {\n ...defaultConfig.classGroups,\n ...classGroups,\n\n \"text-shadow\": [\n \"\",\n \"-sm\",\n \"-md\",\n \"-lg\",\n \"-xl\",\n \"-xxl\",\n \"-none\",\n \"-default\"\n ].map((size) => `text-shadow${size}`),\n \"font-size\": Object.keys({\n ...(config.theme?.fontSize || {}),\n ...(config.theme?.extend?.fontSize || {})\n }).map((size) => `text-${size}`)\n }\n }\n });\n};\n","import type { TwMergeDefaultFnV3 } from \"../tw-merge/v3/_private/types\";\nimport type { TwMergeDefaultFnV4 } from \"../tw-merge/v4/_private/types\";\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport { twMergeDefaultV3 } from \"../tw-merge/v3/twMergeDefault\";\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport { twMergeDefaultV4 } from \"../tw-merge/v4/twMergeDefault\";\n\nimport { isFunction } from \"@/predicates/is/isFunction\";\nimport { getPreciseType } from \"@/predicates/type/getPreciseType\";\nimport { cx, type ClassValues } from \"./cx\";\n\nexport type { ClassValues };\n\n/** -------------------------------------------------------------\n * * ***Factory utility for building a custom `cn` helper (Tailwind `v3`).***\n * -------------------------------------------------------------\n * **Wraps internally function to combines class-name values and applies the provided\n * Tailwind merge function (from {@link twMergeDefaultV3 | `twMergeDefaultV3`}).**\n * - 🔑 **When to use it?**\n * - Your project uses **Tailwind v3**.\n * - You extend Tailwind merge rules (`classGroups`, `tailwind.config`).\n * - You need multiple `cn*` variants across apps/packages.\n * @param {TwMergeDefaultFnV3} customTwMergeV3 - Merge function created via {@link twMergeDefaultV3 | `twMergeDefaultV3`}.\n * @param {ClassValues} classes - Class values (`string`, `array`, `object`, `etc`).\n * @returns {string} Merged Tailwind class string.\n * @example\n * ```ts\n * import tailwindConfig from \"../tailwind.config\";\n * import { twMergeDefaultV3, customCnV3, type ClassValues } from \"@rzl-zone/utils-js/tailwind\";\n *\n * // 1. Create a custom merge function\n * const myCustomTwMerge = twMergeDefaultV3({\n * config: tailwindConfig,\n * extend: {\n * classGroups: {\n * \"text-shadow\": [\"text-shadow\", \"text-shadow-sm\", \"text-shadow-md\"],\n * },\n * },\n * });\n *\n * // 2. Build your helper using `customCnV3`\n * export const cnApp = (...classes: ClassValues) => {\n * return customCnV3(myCustomTwMerge, ...classes);\n * };\n * // ✅ Usage\n * cnApp(\"p-2\", \"p-4\"); // ➔ \"p-4\"\n * cnApp(\"shadow-sm shadow-md\"); // ➔ \"shadow-md\"\n * cnApp(\"text-base text-xxs\"); // ➔ \"text-xxs\" (resolved from config)\n * ```\n */\nexport const customCnV3 = (\n customTwMergeV3: TwMergeDefaultFnV3,\n ...classes: ClassValues\n): string => {\n if (!isFunction(customTwMergeV3)) {\n throw new TypeError(\n `first Parameter (\\`customTwMergeV3\\`) must be of type \\`function\\`, but received: \\`${getPreciseType(\n customTwMergeV3\n )}\\`.`\n );\n }\n\n return customTwMergeV3(cx(...classes));\n};\n\n/** -------------------------------------------------------------\n * * ***Factory utility for building a custom `cn` helper (Tailwind `v4`).***\n * -------------------------------------------------------------\n * **Wraps internally function to combines class-name values and applies the provided\n * Tailwind merge function (from {@link twMergeDefaultV4 | `twMergeDefaultV4`}).**\n * - 🔑 **When to use it?**\n * - Your project uses **Tailwind v4**.\n * - You extend Tailwind merge rules (`classGroups`, `tailwind.config`).\n * - You need multiple `cn*` variants across apps/packages.\n * @param {TwMergeDefaultFnV4} customTwMergeV4 - Merge function created via {@link twMergeDefaultV4 | `twMergeDefaultV4`}.\n * @param {ClassValues} classes - Class values (`string`, `array`, `object`, `etc`).\n * @returns {string} Merged Tailwind class string.\n * @example\n * ```ts\n * import tailwindConfig from \"../tailwind.config\";\n * import { twMergeDefaultV4, customCnV4, type ClassValues } from \"@rzl-zone/utils-js/tailwind\";\n *\n * // 1. Create a custom merge function\n * const myCustomTwMerge = twMergeDefaultV4({\n * config: tailwindConfig,\n * extend: {\n * classGroups: {\n * \"text-shadow\": [\"text-shadow\", \"text-shadow-sm\", \"text-shadow-md\"],\n * },\n * },\n * });\n *\n * // 2. Build your helper using `customCnV4`\n * export const cnApp = (...classes: ClassValues) => {\n * return customCnV4(myCustomTwMerge, ...classes);\n * };\n *\n * // ✅ Usage\n * cnApp(\"p-2\", \"p-4\"); // ➔ \"p-4\"\n * cnApp(\"shadow-sm shadow-md\"); // ➔ \"shadow-md\"\n * cnApp(\"text-base text-xxs\"); // ➔ \"text-xxs\" (resolved from config)\n * ```\n */\nexport const customCnV4 = (\n customTwMergeV4: TwMergeDefaultFnV4,\n ...classes: ClassValues\n): string => {\n if (!isFunction(customTwMergeV4)) {\n throw new TypeError(\n `first Parameter (\\`customTwMergeV4\\`) must be of type \\`function\\`, but received: \\`${getPreciseType(\n customTwMergeV4\n )}\\`.`\n );\n }\n\n return customTwMergeV4(cx(...classes));\n};\n","import type { ClassValues } from \"./cx\";\n\nimport { cx } from \"./cx\";\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport { customCnV3, customCnV4 } from \"./customCn\";\nimport { twMergeDefaultV3 } from \"../tw-merge/v3/twMergeDefault\";\nimport { twMergeDefaultV4 } from \"../tw-merge/v4/twMergeDefault\";\n\nconst defaultTwMergeV3 = twMergeDefaultV3();\nconst defaultTwMergeV4 = twMergeDefaultV4();\n\n/** -------------------------------------------------------------\n * * ***Default `cnV3` utility (Tailwind v3).***\n * -------------------------------------------------------------\n * **Combines class-name values and then deduplicates/resolves\n * conflicts using {@link twMergeDefaultV3 | `twMergeDefaultV3`}\n * with **Tailwind v3 default config only**.**\n * - ✅ **Use this when:**\n * - Your project uses **Tailwind v3**.\n * - You need a simple `cn` that works out of the box without a custom config.\n * - ⚡ **Need custom rules?**\n * - Create a project-wide helper using\n * {@link twMergeDefaultV3 | `twMergeDefaultV3`} +\n * {@link customCnV3 | `customCnV3`} (see Example 2).\n * @param {ClassValues} classes - Class values (`string`, `array`, `object`, `etc`).\n * @returns {string} Merged Tailwind class string.\n * @example\n * #### Example 1: ✅ Default usage (Tailwind v3).\n * ```ts\n * cnV3(\"p-2\", \"p-4\");\n * // ➔ \"p-4\"\n *\n * cnV3(\"text-red-500\", { \"text-blue-500\": true });\n * // ➔ \"text-blue-500\"\n *\n * cnV3([\"m-2\", [\"m-4\"]], \"m-8\");\n * // ➔ \"m-8\"\n * ```\n * #### Example 2: ⚡ Custom project-wide usage with Tailwind config.\n * ```ts\n * import tailwindConfig from \"../tailwind.config\";\n * import { twMergeDefaultV3, customCnV3, type ClassValues } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const cnApp = (...classes: ClassValues) => {\n * return customCnV3(\n * twMergeDefaultV3({\n * config: tailwindConfig,\n * extend: {\n * classGroups: {\n * \"text-shadow\": [\n * \"text-shadow\",\n * \"text-shadow-sm\",\n * \"text-shadow-md\",\n * ],\n * },\n * },\n * }),\n * // ...other options classes,\n * );\n * };\n *\n * cnApp(\"p-2 p-4\"); // ➔ \"p-4\"\n * cnApp(\"shadow-sm shadow-md\"); // ➔ \"shadow-md\"\n * cnApp(\"text-base text-xxs\"); // ➔ \"text-xxs\" (resolved from config)\n * ```\n */\nexport const cnV3 = (...classes: ClassValues): string => {\n return defaultTwMergeV3(cx(...classes));\n};\n\n/** -------------------------------------------------------------\n * * ***Default `cnV4` utility (Tailwind v4).***\n * -------------------------------------------------------------\n * **Combines class-name values and then deduplicates/resolves\n * conflicts using {@link twMergeDefaultV4 | `twMergeDefaultV4`}\n * with **Tailwind v4 default config only**.**\n * - ✅ **Use this when:**\n * - Your project uses **Tailwind v4**.\n * - You need a simple `cn` that works out of the box without a custom config.\n * - ⚡ **Need custom rules?**\n * - Create a project-wide helper using\n * {@link twMergeDefaultV4 | `twMergeDefaultV4`} +\n * {@link customCnV4 | `customCnV4`} (see Example 2).\n * @param {ClassValues} classes - Class values (`string`, `array`, `object`, `etc`).\n * @returns {string} Merged Tailwind class string.\n * @example\n * #### Example 1: ✅ Default usage (Tailwind v4).\n * ```ts\n * cnV4(\"p-2\", \"p-4\");\n * // ➔ \"p-4\"\n *\n * cnV4(\"text-red-500\", { \"text-blue-500\": true });\n * // ➔ \"text-blue-500\"\n *\n * cnV4([\"m-2\", [\"m-4\"]], \"m-8\");\n * // ➔ \"m-8\"\n * ```\n * #### Example 2: ⚡ Custom project-wide usage with Tailwind config.\n * ```ts\n * import tailwindConfig from \"../tailwind.config\";\n * import { twMergeDefaultV4, customCnV4, type ClassValues } from \"@rzl-zone/utils-js/tailwind\";\n *\n * const cnApp = (...classes: ClassValues) => {\n * return customCnV4(\n * twMergeDefaultV4({\n * config: tailwindConfig,\n * extend: {\n * classGroups: {\n * \"text-shadow\": [\n * \"text-shadow\",\n * \"text-shadow-sm\",\n * \"text-shadow-md\",\n * ],\n * },\n * },\n * }),\n * // ...other options classes,\n * );\n * };\n *\n * cnApp(\"p-2 p-4\"); // ➔ \"p-4\"\n * cnApp(\"shadow-sm shadow-md\"); // ➔ \"shadow-md\"\n * cnApp(\"text-base text-xxs\"); // ➔ \"text-xxs\" (resolved from config)\n * ```\n */\nexport const cnV4 = (...classes: ClassValues): string => {\n return defaultTwMergeV4(cx(...classes));\n};\n","import type { Stringify } from \"@rzl-zone/ts-types-plus\";\n\nimport { isString } from \"@/predicates/is/isString\";\nimport { isEmptyString } from \"@/predicates/is/isEmptyString\";\nimport { getPreciseType } from \"@/predicates/type/getPreciseType\";\nimport { isNonEmptyArray } from \"@/predicates/is/isNonEmptyArray\";\nimport { assertIsArray } from \"@/assertions/objects/assertIsArray\";\n\n/** ----------------------------------------------------------\n * * ***Utility: `shouldForwardProp`.***\n * ----------------------------------------------------------\n * **Creates a helper for styled-components `shouldForwardProp`.**\n *\n * @description\n * 1. Returns a **predicate function** that determines whether a given prop\n * should be forwarded to the DOM.\n * 2. Useful for filtering out internal props (e.g., `$size`, `$active`)\n * so they don't become invalid HTML attributes.\n *\n * - **Behavior:**\n * - Accepts a strict tuple of **string keys** to exclude from forwarding.\n * - Every key is validated as a **non-empty string** at runtime.\n * - Throws a `TypeError` if:\n * - `props` is not an array, or\n * - any item is not a non-empty string.\n * - Automatically coerces the tested prop name to string for matching.\n *\n * @template CustomProps\n * The component props type to validate against.\n *\n * @param {readonly Stringify<keyof CustomProps>[]}\n * props\n * The list of prop names (keys of `CustomProps`) to exclude from forwarding.\n *\n * @returns {(propName: keyof CustomProps | ({} & string)) => boolean}\n * A function that receives a prop name and returns:\n * - `true` ➔ the prop **will** be forwarded to the DOM.\n * - `false` ➔ the prop **will not** be forwarded.\n *\n * @throws **{@link TypeError | `TypeError`}**\n * when:\n * - `props` is not an array, or\n * - any item is not a non-empty string.\n *\n * @example\n * // Basic usage\n * type Props = { $size: string; color: string; visible: boolean };\n * const filter = shouldForwardProp<Props>([\"$size\"]);\n *\n * filter(\"$size\"); // ➔ false (blocked).\n * filter(\"color\"); // ➔ true (forwarded).\n * filter(\"visible\"); // ➔ true (forwarded).\n *\n * @example\n * // With styled-components\n * type CustomProps = { $internal: boolean; public: string; another: boolean };\n *\n * styled.div.withConfig({\n * shouldForwardProp: shouldForwardProp<CustomProps>([\"$internal\"])\n * });\n */\nexport const shouldForwardProp = <CustomProps extends Record<string, unknown>>(\n props: readonly Stringify<keyof CustomProps>[]\n // props: Partial<UnionToTupleStrict<keyof CustomProps>>\n): ((propName: keyof CustomProps | ({} & string)) => boolean) => {\n assertIsArray(props, {\n message: ({ currentType, validType }) =>\n `First parameter (\\`props\\`) must be of type \\`${validType}\\`, but received: \\`${currentType}\\`.`\n });\n\n const invalidItems: { index: number; reason: string }[] = [];\n\n props.forEach((p, idx) => {\n if (!isString(p)) {\n invalidItems.push({\n index: idx,\n reason: `Expected a non-empty string, but received ${getPreciseType(p, {\n formatCase: \"toPascalCaseSpace\"\n })}.`\n });\n } else if (isEmptyString(p)) {\n invalidItems.push({\n index: idx,\n reason: \"Expected a non-empty string, but received EmptyString.\"\n });\n }\n });\n\n if (isNonEmptyArray(invalidItems)) {\n const maxWidth = Math.max(\n ...invalidItems.map((item) => String(item.index).length)\n );\n\n const details = invalidItems\n .map(\n (item) =>\n `• [Index ${String(item.index).padStart(maxWidth, \"0\")}] ${item.reason}`\n )\n .join(\"\\n\");\n\n throw new TypeError(\n `First parameter (\\`props\\`) contains invalid entries:\\n${details}`\n );\n }\n\n return (propName): boolean => {\n return !props.map(String).includes(String(propName));\n };\n};\n"],"mappings":"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"}
|