UNPKG

@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
/*! * ==================================================== * 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 };