diff --git a/CHANGELOG.md b/CHANGELOG.md index a514a74..12ebf3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # deverything +## 0.33.0 + +### Minor Changes + +- urls search params + ## 0.32.0 ### Minor Changes diff --git a/README.md b/README.md index a5736e5..4178763 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,11 @@ Contributions always welcome! - `cleanSpaces()` trims and turns double spaces into single space - `clamp()` clamp number in a range - `first()` get the first element of an array +- `firstKey()` +- `firstValue()` - `getKeys()` get the keys of an object, includes symbols +- `getUrlSearchParam()` +- `getUrlSearchParams()` - `last()` get the last element of an array - ⭐ `merge()` deep merge objects - `moveToFirst()` move array element to first @@ -80,6 +84,7 @@ Contributions always welcome! - `promiseWithTimeout()` takes a promise, a timeoutMs, and an option error as arguments. Returns a new Promise that either resolves with the value of the input promise or rejects with the provided error or a default error message if the input promise does not resolve or reject within the specified timeoutMs. - `scrambleText()` replace alpha chars with random chars - `sleep()` promise-based sleep +- `setUrlSearchParams()` - `shuffle()` shuffles elements in an array - `toggleArrayValue()` remove/add value in array - `truncate()` truncate text, does not break emojis diff --git a/package.json b/package.json index 1278c2b..4d7503a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "deverything", - "version": "0.32.0", + "version": "0.33.0", "description": "Everything you need for Dev", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/src/helpers/arrayIntersection.ts b/src/helpers/arrayIntersection.ts index f680364..25c00b0 100644 --- a/src/helpers/arrayIntersection.ts +++ b/src/helpers/arrayIntersection.ts @@ -1,5 +1,5 @@ import { uniqueValues } from "./uniqueValues"; -export const arrayIntersection = (arr1: any[], arr2: any[]) => { +export const arrayIntersection = <T>(arr1: T[], arr2: T[]): T[] => { return uniqueValues(arr1.filter((value) => arr2.includes(value))); }; diff --git a/src/helpers/getUrlSearchParam.test.ts b/src/helpers/getUrlSearchParam.test.ts index d4cb302..bedc864 100644 --- a/src/helpers/getUrlSearchParam.test.ts +++ b/src/helpers/getUrlSearchParam.test.ts @@ -27,7 +27,7 @@ describe("getUrlSearchParam", () => { "1" ); expect(getUrlSearchParam("http://pippo.com?param=2¶m=1", "param")).toBe( - "2" + "1" ); expect( getUrlSearchParam("http://localhost/?param=1¶m2=2", "param2") diff --git a/src/helpers/getUrlSearchParam.ts b/src/helpers/getUrlSearchParam.ts index bafef2b..3d2aa1e 100644 --- a/src/helpers/getUrlSearchParam.ts +++ b/src/helpers/getUrlSearchParam.ts @@ -1,12 +1,9 @@ import { Maybe } from "../types/Maybe"; +import { getUrlSearchParams } from "./getUrlSearchParams"; -export const getUrlSearchParam = (urlString: Maybe<string>, param: string) => { - if (!urlString) return undefined; - try { - const url = new URL(urlString); - const value = url.searchParams.get(param); - return value ?? undefined; - } catch (_e) { - return undefined; - } +export const getUrlSearchParam = ( + urlString: Maybe<string>, + param: string +): string | undefined => { + return getUrlSearchParams(urlString)[param]; }; diff --git a/src/helpers/getUrlSearchParams.test.ts b/src/helpers/getUrlSearchParams.test.ts new file mode 100644 index 0000000..389210b --- /dev/null +++ b/src/helpers/getUrlSearchParams.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, test } from "@jest/globals"; +import { getUrlSearchParams } from "./getUrlSearchParams"; + +describe("getUrlSearchParams", () => { + test("found", async () => { + expect(getUrlSearchParams("https")).toStrictEqual({}); + expect(getUrlSearchParams("https://www.ciao.com")).toStrictEqual({}); + expect(getUrlSearchParams("https://www.ciao.com/")).toStrictEqual({}); + expect(getUrlSearchParams("https://www.ciao.com/#")).toStrictEqual({}); + expect(getUrlSearchParams("https://www.ciao.com/?param=")).toStrictEqual({ + param: "", + }); + expect( + getUrlSearchParams("https://www.ciao.com/?param1=123¶m2=asd") + ).toStrictEqual({ + param1: "123", + param2: "asd", + }); + }); +}); diff --git a/src/helpers/getUrlSearchParams.ts b/src/helpers/getUrlSearchParams.ts new file mode 100644 index 0000000..c5a8f10 --- /dev/null +++ b/src/helpers/getUrlSearchParams.ts @@ -0,0 +1,13 @@ +import { Maybe } from "../types/Maybe"; + +export const getUrlSearchParams = ( + urlString: Maybe<string> +): Record<string, string> => { + if (!urlString) return {}; + try { + const url = new URL(urlString); + return Object.fromEntries(url.searchParams); + } catch (_e) { + return {}; + } +}; diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 950032d..ebc1e2e 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -9,6 +9,8 @@ export * from "./first"; export * from "./firstKey"; export * from "./firstValue"; export * from "./getKeys"; +export * from "./getUrlSearchParam"; +export * from "./getUrlSearchParams"; export * from "./last"; export * from "./merge"; export * from "./moveToFirst"; @@ -21,6 +23,7 @@ export * from "./pretty"; export * from "./promiseWithTimeout"; export * from "./scrambleText"; export * from "./serialize"; +export * from "./setUrlSearchParams"; export * from "./shuffle"; export * from "./sleep"; export * from "./toggleArrayValue"; diff --git a/src/helpers/setUrlSearchParams.test.ts b/src/helpers/setUrlSearchParams.test.ts new file mode 100644 index 0000000..2e18bc6 --- /dev/null +++ b/src/helpers/setUrlSearchParams.test.ts @@ -0,0 +1,57 @@ +import { expect, describe, test } from "@jest/globals"; +import { setUrlSearchParams } from "./setUrlSearchParams"; + +describe("setUrlSearchParams", () => { + test("relative url", () => { + expect(setUrlSearchParams("/signin")).toBe("/signin"); + expect(setUrlSearchParams("/signin?")).toBe("/signin"); + expect(setUrlSearchParams("/signin?in")).toBe("/signin?in"); + expect(setUrlSearchParams("/signin?in#sec")).toBe("/signin?in#sec"); + expect(setUrlSearchParams("/signin?in#sec", { UPPER: "CASE" })).toBe( + "/signin?in=&UPPER=CASE#sec" + ); + expect( + setUrlSearchParams("/signin?in#sec", { and: "&", equals: "=" }) + ).toBe("/signin?in=&and=%26&equals=%3D#sec"); + }); + + test("ip", () => { + expect(setUrlSearchParams("http://127.127.127.127", {})).toBe( + "http://127.127.127.127/" + ); + + expect(setUrlSearchParams("http://127.127.127.127/ok", {})).toBe( + "http://127.127.127.127/ok" + ); + expect( + setUrlSearchParams("http://127.127.127.127/ok?go", { ng: true }) + ).toBe("http://127.127.127.127/ok?go=&ng=true"); + }); + + test("abs url", () => { + expect(setUrlSearchParams("http://localhost", {})).toBe( + "http://localhost/" + ); + expect(setUrlSearchParams("http://dev.dev/hop", { ciao: "tu" })).toBe( + "http://dev.dev/hop?ciao=tu" + ); + expect( + setUrlSearchParams("https://secure.gong/?ciao=tu", { ciao: "tu" }) + ).toBe("https://secure.gong/?ciao=tu"); + expect( + setUrlSearchParams("http://localhost/?ciao=tu", { ciao: "tu", a: "b" }) + ).toBe("http://localhost/?ciao=tu&a=b"); + expect( + setUrlSearchParams("http://localhost/?ciao=tu", { ciao: "tu", a: "" }) + ).toBe("http://localhost/?ciao=tu&a="); + expect( + setUrlSearchParams("http://localhost/?ciao=tu", { ciao: "tu", a: 1 }) + ).toBe("http://localhost/?ciao=tu&a=1"); + expect( + setUrlSearchParams("http://localhost/?kikko=tu#", { ciao: "tu", a: 1 }) + ).toBe("http://localhost/?kikko=tu&ciao=tu&a=1#"); + expect(setUrlSearchParams("http://localhost/?over=1", { over: "2" })).toBe( + "http://localhost/?over=2" + ); + }); +}); diff --git a/src/helpers/setUrlSearchParams.ts b/src/helpers/setUrlSearchParams.ts new file mode 100644 index 0000000..e07b45f --- /dev/null +++ b/src/helpers/setUrlSearchParams.ts @@ -0,0 +1,20 @@ +export const setUrlSearchParams = ( + currentURL: string, + searchParams: Record<string, string | number | boolean> = {} +) => { + const isRelativeUrl = currentURL.startsWith("/"); + const url = new URL( + currentURL, + isRelativeUrl ? "https://deverything.dev/" : undefined // add base to make relative urls work + ); + + Object.entries(searchParams).forEach(([paramKey, paramValue]) => { + url.searchParams.set(paramKey, paramValue.toString()); + }); + + if (isRelativeUrl) { + return url.pathname + url.search + url.hash; + } + + return url.toString(); +}; diff --git a/src/random/randomEmail.ts b/src/random/randomEmail.ts index a6bf9d2..79b61dd 100644 --- a/src/random/randomEmail.ts +++ b/src/random/randomEmail.ts @@ -2,6 +2,5 @@ import { DOMAIN_NAMES } from "../constants/domains"; import { randomArrayItem } from "./randomArrayItem"; import { randomHandle } from "./randomHandle"; -//Use randomId() to generate a unique email export const randomEmail = () => `${randomHandle()}@${randomArrayItem(DOMAIN_NAMES)}`; diff --git a/src/random/randomHandle.ts b/src/random/randomHandle.ts index 817f278..979121f 100644 --- a/src/random/randomHandle.ts +++ b/src/random/randomHandle.ts @@ -1,10 +1,10 @@ import { WESTERN_FIRST_NAMES, WESTERN_LAST_NAMES } from "../constants/names"; import { randomArrayItem } from "./randomArrayItem"; -import { randomInt } from "./randomInt"; +import { randomNumericId } from "./randomNumericId"; export const randomHandle = () => ( randomArrayItem(WESTERN_FIRST_NAMES) + "." + randomArrayItem(WESTERN_LAST_NAMES) - ).toLowerCase() + randomInt(11, 99); + ).toLowerCase() + randomNumericId(); // use randomNumericId too keep handles process-unique diff --git a/src/validators/isURL.test.ts b/src/validators/isURL.test.ts index f056c0a..36abd76 100644 --- a/src/validators/isURL.test.ts +++ b/src/validators/isURL.test.ts @@ -6,6 +6,9 @@ describe("isURL", function () { expect(isURL("a@a.a")).toBe(false); expect(isURL("")).toBe(false); expect(isURL(" ")).toBe(false); + expect(isURL("/localhost")).toBe(false); + expect(isURL("file://../localhost")).toBe(false); + expect(isURL("../localhost")).toBe(false); expect(isURL("//localhost")).toBe(false); }); diff --git a/src/validators/isURL.ts b/src/validators/isURL.ts index 9fa3051..a623ea5 100644 --- a/src/validators/isURL.ts +++ b/src/validators/isURL.ts @@ -4,9 +4,9 @@ const pattern = new RegExp( "((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path "(\\?[;&a-z\\d%_.~+=-]*)?" + // query string - "(\\#[-a-z\\d_]*)?$", + "(\\#[-a-z\\d_]*)?$", // fragment locator "i" -); // fragment locator +); export const isURL = (arg: string) => { return !!arg && pattern.test(arg);