@rzl-zone/utils-js
Version:
A modern, lightweight set of JavaScript utility functions with TypeScript support for everyday development, crafted to enhance code readability and maintainability.
218 lines (212 loc) • 7.75 kB
JavaScript
/*!
* ====================================================
* Rzl Utils-JS.
* ----------------------------------------------------
* Version: 3.11.0.
* Author: Rizalvin Dwiky.
* Repository: https://github.com/rzl-zone/utils-js.
* ====================================================
*/
import { normalizePathname, formatEnvPort } from '../chunk-SZUNAEMR.js';
import '../chunk-5WIEDF2J.js';
import '../chunk-6YGBRENU.js';
import { removeSpaces } from '../chunk-HNBRGN4R.js';
import { isEmptyString } from '../chunk-ULQPCIA2.js';
import { assertIsString } from '../chunk-3T6VSWYX.js';
import { safeStableStringify } from '../chunk-AXDYWO67.js';
import '../chunk-QNKGP5DY.js';
import { isString, getPreciseType, assertIsPlainObject, isNil, isNonEmptyArray, isNonEmptyString, isPlainObject, isUndefined, assertIsBoolean, isError, hasOwnProp } from '../chunk-MSUW5VHZ.js';
function generateRoute(route, params) {
if (!isString(route) || isEmptyString(route)) {
throw new TypeError(
`\u274C 'generateRoute' Failed:
- Invalid 'route' value.
- Must be of type \`string\` and non-empty string, but received: "${getPreciseType(
route
)}": \`${safeStableStringify(route, {
keepUndefined: true
})}\`.`
);
}
if (!/[\\[\]]/.test(route)) {
return route;
}
assertIsPlainObject(params, {
message: ({ validType }) => `\u274C 'generateRoute' Failed cause in route "${route}":
- Missing or invalid parameters \`${validType}\` for route: "${route}", must be of type \`${validType}\` mapping parameters.`
});
if (isNil(params)) {
throw new TypeError(
`\u274C 'generateRoute' Failed cause in route "${route}":
- Missing parameters \`plain-object\` for route: "${route}".`
);
}
const invalidChars = [
"?",
"&",
"#",
"=",
"/",
// "`",
// " ",
// ".",
"'",
'"',
"(",
")",
"+",
";",
"%",
"@",
":"
];
const errors = [];
const requiredKeys = Array.from(route.matchAll(/\[(\w+)\]/g)).map((m) => m[1]);
for (const key of requiredKeys) {
const value = params[key];
if (!isString(value)) {
errors.push(
`- Invalid parameter: "${key}" must be of type \`string\`, but received: \`${getPreciseType(
value
)}\`.`
);
continue;
}
if (isEmptyString(value)) {
errors.push(`- Parameter "${key}" cannot be empty string.`);
continue;
}
const foundInvalidChars = invalidChars.filter((char) => value.includes(char));
if (/\s/.test(value)) {
foundInvalidChars.push("white-space(s)");
}
if (foundInvalidChars.length > 0) {
const formattedChars = foundInvalidChars.map(
(c) => c === "`" ? "backtick - (`)" : `\`${c}\``
);
if (!invalidChars.includes("white-space(s)")) invalidChars.push("white-space(s)");
const formattedInvalidChars = invalidChars.map(
(c) => c === "`" ? "backtick - (`)" : `\`${c}\``
);
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(
", "
)}).`
);
}
}
if (isNonEmptyArray(errors)) {
throw new Error(
`\u274C 'generateRoute' Failed cause in route "${route}":
${errors.join("\n")}.`
);
}
return route.replace(/\[(\w+)\]/g, (_, key) => {
const paramKey = isNonEmptyString(params[key]) ? params[key] : "";
return paramKey.trim().replace(/^\/+|\/+$/g, "");
}).replace(/\/+/g, "/");
}
var createBeApiUrl = (pathname, options = {}) => {
try {
let joinPath2 = function(a, b) {
return `${a.replace(/\/+$/, "")}/${b.replace(/^\/+/, "")}`;
};
var joinPath = joinPath2;
assertIsString(isNil(pathname) ? "" : pathname, {
message({ currentType, validType }) {
return `First parameter (\`pathname\`) must be of type \`${validType}\`, but received: \`${currentType}\`.`;
}
});
if (!isPlainObject(options)) {
options = {};
}
let {
prefix = "/api",
withOrigin = true
} = options;
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
)}\`.`
);
}
assertIsBoolean(withOrigin, {
message: ({ currentType, validType }) => `Parameter \`withOrigin\` property of the \`options\` (second parameter) must be of type \`${validType}\`, but received: \`${currentType}\`.`
});
pathname = normalizePathname(pathname);
prefix = normalizePathname(prefix);
const normalizedPrefix = prefix.endsWith("/") ? prefix : prefix + "/";
if (pathname === prefix || pathname === prefix + "/" || pathname.startsWith(normalizedPrefix)) {
pathname = pathname.slice(prefix.length);
pathname = normalizePathname(pathname);
}
const baseApiUrl = getBeApiUrl({ suffix: prefix });
const fullPath = withOrigin ? joinPath2(baseApiUrl, pathname) : joinPath2(new URL(baseApiUrl).pathname, pathname);
return fullPath.replace(/\/+$/, "");
} catch (err) {
if (isError(err)) {
throw err;
} else
throw new Error(
"Failed to generate backend API URL in `createBeApiUrl()`, Error: " + new Error(String(err)).message.trim()
);
}
};
var getBeApiUrl = (options = {}) => {
if (!isPlainObject(options)) options = {};
let suffix = hasOwnProp(options, "suffix") ? options.suffix : "/";
assertIsString(suffix, {
message({ currentType, validType }) {
return `Parameter \`suffix\` property of the first parameter must be of type \`${validType}\`, but received: \`${currentType}\`.`;
}
});
try {
let rawBaseUrl = process.env.NEXT_PUBLIC_BACKEND_API_URL?.trim();
if (rawBaseUrl) {
rawBaseUrl = removeSpaces(rawBaseUrl);
const urlObj = new URL(rawBaseUrl);
const hasPort = !!urlObj.port;
if (!hasPort && process.env.NEXT_PUBLIC_PORT_BE) {
rawBaseUrl = urlObj.origin + formatEnvPort(process.env.NEXT_PUBLIC_PORT_BE, {
prefixColon: true
});
}
} else {
rawBaseUrl = "http://localhost" + formatEnvPort(process.env.NEXT_PUBLIC_PORT_BE || "8000", {
prefixColon: true
});
}
suffix = removeSpaces(suffix).length ? removeSpaces(suffix) : "/";
const baseApiUrl = new URL(rawBaseUrl.replace(/\/+$/, "")).origin;
const finalSuffix = suffix === "/" ? "/" : `${suffix.startsWith("/") ? "" : "/"}${suffix.replace(/\/+$/, "")}`;
return `${baseApiUrl}${finalSuffix}`;
} catch (error) {
throw new Error(
"Invalid `NEXT_PUBLIC_BACKEND_API_URL`, failed to generate from `getBeApiUrl()`, Error:" + error
);
}
};
var getBaseUrl = () => {
try {
const baseEnv = process.env.NEXT_PUBLIC_BASE_URL?.trim();
const portEnv = process.env.NEXT_PUBLIC_PORT_FE?.trim();
let baseUrl = baseEnv || "http://localhost";
baseUrl = removeSpaces(baseUrl).replace(/\/+$/, "");
const hasPort = /:\/\/[^/]+:\d+/.test(baseUrl);
if (!hasPort && portEnv) {
baseUrl += formatEnvPort(portEnv, { prefixColon: true });
} else if (!hasPort && !baseEnv) {
baseUrl += ":3000";
}
const url = new URL(baseUrl);
return `${url.protocol}//${url.hostname}${url.port ? `:${url.port}` : ""}`;
} catch (error) {
throw new Error(
"Invalid `NEXT_PUBLIC_BASE_URL`, failed to generate from `getBaseUrl()`, Error:" + error
);
}
};
export { createBeApiUrl, generateRoute, getBaseUrl, getBeApiUrl };