{"id":26758463,"url":"https://github.com/alessiofrittoli/web-utils","last_synced_at":"2026-02-26T17:29:06.138Z","repository":{"id":271294530,"uuid":"912986855","full_name":"alessiofrittoli/web-utils","owner":"alessiofrittoli","description":"Common TypeScript web utilities","archived":false,"fork":false,"pushed_at":"2026-02-25T16:42:39.000Z","size":526,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-02-25T18:54:21.699Z","etag":null,"topics":["browser-api","map","string-utilities","web-storage-api","web-utilities"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/@alessiofrittoli/web-utils","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alessiofrittoli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"license.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["alessiofrittoli"]}},"created_at":"2025-01-06T19:55:43.000Z","updated_at":"2026-02-17T19:32:51.000Z","dependencies_parsed_at":"2025-01-06T20:51:44.231Z","dependency_job_id":"dea8cd27-a6d1-4092-ab05-0cdbca71e9f8","html_url":"https://github.com/alessiofrittoli/web-utils","commit_stats":null,"previous_names":["alessiofrittoli/web-utils"],"tags_count":39,"template":false,"template_full_name":null,"purl":"pkg:github/alessiofrittoli/web-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fweb-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fweb-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fweb-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fweb-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alessiofrittoli","download_url":"https://codeload.github.com/alessiofrittoli/web-utils/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Fweb-utils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29863785,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T08:51:08.701Z","status":"ssl_error","status_checked_at":"2026-02-26T08:50:19.607Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser-api","map","string-utilities","web-storage-api","web-utilities"],"created_at":"2025-03-28T16:20:12.266Z","updated_at":"2026-02-26T17:29:06.127Z","avatar_url":"https://github.com/alessiofrittoli.png","language":"TypeScript","funding_links":["https://github.com/sponsors/alessiofrittoli"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eWeb Utils 🛠️\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n  Common TypeScript web utilities\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.org/package/@alessiofrittoli/web-utils\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/@alessiofrittoli/web-utils\" alt=\"Latest version\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://coveralls.io/github/alessiofrittoli/web-utils\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://coveralls.io/repos/github/alessiofrittoli/web-utils/badge.svg\" alt=\"Test coverage\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://socket.dev/npm/package/@alessiofrittoli/web-utils/overview\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://socket.dev/api/badge/npm/package/@alessiofrittoli/web-utils\" alt=\"Socket Security score\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://npmjs.org/package/@alessiofrittoli/web-utils\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/@alessiofrittoli/web-utils.svg\" alt=\"npm downloads\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/package/@alessiofrittoli/web-utils\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/dependency-count/@alessiofrittoli/web-utils\" alt=\"Dependencies\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://libraries.io/npm/%40alessiofrittoli%2Fweb-utils\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://img.shields.io/librariesio/release/npm/@alessiofrittoli/web-utils\" alt=\"Dependencies status\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://bundlephobia.com/package/@alessiofrittoli/web-utils\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/min/@alessiofrittoli/web-utils\" alt=\"minified\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/package/@alessiofrittoli/web-utils\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/minzip/@alessiofrittoli/web-utils\" alt=\"minizipped\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/package/@alessiofrittoli/web-utils\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/tree-shaking/@alessiofrittoli/web-utils\" alt=\"Tree shakable\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/sponsors/alessiofrittoli\" style=\"text-decoration: none;\"\u003e\n    \u003cimg src=\"https://img.shields.io/static/v1?label=Fund%20this%20package\u0026message=%E2%9D%A4\u0026logo=GitHub\u0026color=%23DB61A2\" alt=\"Fund this package\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n### Table of Contents\n\n- [Getting started](#getting-started)\n- [What's Changed](#whats-changed)\n- [API Reference](#api-reference)\n  - [Blob utilities](#blob-utilities)\n  - [Array utilities](#array-utilities)\n  - [Dom utilities](#dom-utilities)\n    - [Scroll utilities](#scroll-utilities)\n    - [Stylesheet utilities](#stylesheet-utilities)\n  - [Generators utilities](#generators-utilities)\n  - [Map utilities](#map-utilities)\n  - [Promises utilities](#promises-utilities)\n  - [Strings utilities](#strings-utilities)\n  - [Types utilities](#types-utilities)\n  - [Validation utilities](#validation-utilities)\n  - [Objects utilities](#objects-utilities)\n  - [Browser API utilities](#browser-api-utilities)\n    - [Document Picture-in-Picture](#document-picture-in-picture)\n  - [Device utilities](#device-utilities)\n  - [Storage utilities](#storage-utilities)\n    - [`Cookie` Class](#cookie-class)\n    - [`LocalStorage` Class](#localstorage-class)\n    - [`SessionStorage` Class](#sessionstorage-class)\n  - [Utils](#utils)\n- [Development](#development)\n  - [Install depenendencies](#install-depenendencies)\n  - [Build the source code](#build-the-source-code)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n- [Contributing](#contributing)\n- [Security](#security)\n- [Credits](#made-with-)\n\n---\n\n### Getting started\n\nRun the following command to start using `web-utils` in your projects:\n\n```bash\nnpm i @alessiofrittoli/web-utils\n```\n\nor using `pnpm`\n\n```bash\npnpm i @alessiofrittoli/web-utils\n```\n\n---\n\n### What's Changed\n\n#### Updates in the latest release 🎉\n\n- Add `deferTask`. See [API Reference](#defertask) for more info.\n- Add `deferCallback`. See [API Reference](#defercallback) for more info.\n- Add `parameterized` function. See [API Reference](#parameterized) for more info.\n- Add Document Picture-in-Picture utilities. See [Document Picture-in-Picture](#document-picture-in-picture) for more info.\n\n---\n\n### API Reference\n\n#### Array utilities\n\n##### `arrayUnique`\n\nRemoves duplicate values from an array.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type  | Description      |\n| --------- | ----- | ---------------- |\n| `array`   | `T[]` | The input array. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `T[]`\n\nThe filtered array.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Removes duplicates from array\n\n```ts\nimport { arrayUnique } from \"@alessiofrittoli/web-utils\";\n\nconst pointer = {};\nconsole.log(arrayUnique([pointer, \"b\", pointer, \"c\", \"b\"]));\n// Outputs: [ {}, 'b', 'c' ]\n```\n\n\u003c/details\u003e\n\n---\n\n##### `arrayObjectUnique`\n\nRemoves duplicate entries from an array referencing an object key.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter  | Type      | Description                      |\n| ---------- | --------- | -------------------------------- |\n| `array`    | `T[]`     | An array of objects.             |\n| `property` | `keyof T` | The Object property to refer to. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `T[]`\n\nThe filtered array.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Removes duplicates from array with the same propery value\n\n```ts\nimport { arrayObjectUnique } from \"@alessiofrittoli/web-utils\";\n\nconst arr = [\n  { id: 1, name: \"a\" },\n  { id: 2, name: \"b\" },\n  { id: 1, name: \"c\" }, // duplicate `id`\n  { id: 3, name: \"d\" },\n  { id: 4, name: \"a\" }, // duplicate `name`\n];\n\nconsole.log(arrayObjectUnique(arr, \"id\"));\n// Outputs: [\n//     { id: 1, name: 'a' },\n//     { id: 2, name: 'b' },\n//     { id: 3, name: 'd' },\n//     { id: 4, name: 'a' },\n// ]\n\nconsole.log(arrayObjectUnique(arr, \"name\"));\n// Outputs: [\n//     { id: 1, name: 'a' },\n//     { id: 2, name: 'b' },\n//     { id: 1, name: 'c' },\n//     { id: 3, name: 'd' },\n// ]\n```\n\n\u003c/details\u003e\n\n---\n\n##### `listToArray`\n\nConvert a stringified Array to Array object.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description          |\n| --------- | -------- | -------------------- |\n| `string`  | `string` | An array of objects. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `string[]`\n\nThe converted stringified Array to Array object.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Basic usage\n\n```ts\nimport { listToArray } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(listToArray(\"1,2, 3, 4\").map(Number));\n// Outputs: [ 1, 2, 3, 4 ]\n```\n\n\u003c/details\u003e\n\n---\n\n##### `chunkInto`\n\nSplit Array into chunks.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType Parameters\u003c/summary\u003e\n\n| Parameter | Default     | Description             |\n| --------- | ----------- | ----------------------- |\n| `T`       | `unknown[]` | The input `array` type. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter       | Type               | Description                                                                                                      |\n| --------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------- |\n| `array`         | `T[]`              | The original Array.                                                                                              |\n| `options`       | `ChunkIntoOptions` | An object defining split criteria.                                                                               |\n| `options.size`  | `number`           | Will split the given Array in a way to ensure each chunk length is, whenever possible, equal to the given value. |\n| `options.count` | `number`           | Will split the given Array in a way to ensure n chunks as the given value.                                       |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `T[]`\n\nAn Array of chunks.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Basic usage\n\n```ts\nimport { chunkInto } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(chunkInto([1, 2, 3, 4, 5], { count: 2 }));\n// Output: [ [ 1, 2, 3 ], [ 4, 5 ] ]\n\nconsole.log(chunkInto([1, 2, 3, 4, 5], { size: 2 }));\n// Output: [ [ 1, 2 ], [ 3, 4 ], [ 5 ] ]\n```\n\n\u003c/details\u003e\n\n---\n\n##### `shuffle`\n\nShuffle the elements of an array in place using the [Fisher-Yates algorithm](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle).\n\nPlease note that this function modify the original given `array`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                                               |\n| --------- | --------------------------------------------------------- |\n| `T`       | The automatically inferred type of elements in the array. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type       | Description           |\n| --------- | ---------- | --------------------- |\n| `array`   | `Array\u003cT\u003e` | The array to shuffle. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `Array\u003cT\u003e`\n\nThe modified shuffled array.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eExample\u003c/summary\u003e\n\n```ts\nimport { shuffle } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(shuffle([1, 2, 3, 4, 5]));\n```\n\n\u003c/details\u003e\n\n---\n\n##### `shuffleCopy`\n\nCopy and shuffle the elements of an array in place using the [Fisher-Yates algorithm](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle).\n\nSame API of [`shuffle`](#shuffle) is applied, but this function **does not modify** the original given `array`.\n\n---\n\n#### Blob utilities\n\n##### `downloadBlob`\n\nCreate and download a blob object.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter  | Type           | Description                         |\n| ---------- | -------------- | ----------------------------------- |\n| `filename` | `string`       | The download file name.             |\n| `data`     | `BodyInit`     | The download file data.             |\n| `init`     | `ResponseInit` | (Optional) The ResponseInit object. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Download file from HTTP Response\n\n```ts\nimport { downloadBlob } from '@alessiofrittoli/web-utils'\n\nfetch( ... )\n  .then( response =\u003e response.formData() )\n  .then( async data =\u003e {\n    await Promise.all(\n      Array.from( data.entries() )\n        .map( async ( [, file ] ) =\u003e {\n          if ( ! ( file instanceof File ) ) return\n          await downloadBlob( file.name, file )\n        } )\n    )\n  } )\n  .catch( error =\u003e {\n    console.error( error )\n  } )\n```\n\n\u003c/details\u003e\n\n---\n\n#### Dom utilities\n\n##### Scroll utilities\n\n###### `blockScroll`\n\nPrevent Element Overflow.\n\nIt calculates the scrollbar width and the resulting value is applied to the target element right padding-right to prevent width grows.\n\nIt also applies the `--scrollbar-size` CSS variable that can be used to apply a padding-right to the position fixed elements inside the target.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type          | Default                    | Description                    |\n| --------- | ------------- | -------------------------- | ------------------------------ |\n| `target`  | `HTMLElement` | `Document.documentElement` | (Optional) The target Element. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Block Document HTML scroll when a popup is opened\n\n```ts\nimport { blockScroll } from \"@alessiofrittoli/web-utils\";\n\nconst openPopUpHandler = () =\u003e {\n  blockScroll();\n  // ... handle popup\n};\n```\n\n```css\n.modal-wrapper {\n  position: fixed;\n  inset: 0;\n  padding-right: var(--scrollbar-size, 0);\n}\n```\n\n---\n\n###### Block scroll of a specific HTMLElement\n\n```ts\nimport { blockScroll } from \"@alessiofrittoli/web-utils\";\n\nconst element = document.querySelector(\".css-selector\");\n\nif (element) {\n  blockScroll(element);\n}\n```\n\n\u003c/details\u003e\n\n---\n\n###### `restoreScroll`\n\nRestore Element Overflow.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type          | Default                    | Description                    |\n| --------- | ------------- | -------------------------- | ------------------------------ |\n| `target`  | `HTMLElement` | `Document.documentElement` | (Optional) The target Element. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Restore Document HTML scroll when a popup is closed\n\n```ts\nimport { restoreScroll } from \"@alessiofrittoli/web-utils\";\n\nconst closePopUpHandler = () =\u003e {\n  // ... handle close\n  restoreScroll();\n};\n```\n\n---\n\n###### Restore scroll of a specific HTMLElement\n\n```ts\nimport { restoreScroll } from \"@alessiofrittoli/web-utils\";\n\nconst element = document.querySelector(\".css-selector\");\n\nif (element) {\n  restoreScroll(element);\n}\n```\n\n\u003c/details\u003e\n\n---\n\n##### Stylesheet utilities\n\n###### Types\n\n###### UrlStylesheet\n\nRepresents a URL stylesheet as a simple URL input, URL object or as an object with URL and fetch configuration options.\n\n```ts\ntype UrlStylesheet =\n  | UrlInput\n  | {\n      /**\n       * The URL string or a URL object of the stylesheet to load.\n       *\n       */\n      url: UrlInput;\n      /**\n       * Indicates whether to fetch the given URL.\n       *\n       * @default false\n       */\n      fetch?: boolean;\n    };\n```\n\n---\n\n###### Style\n\nRepresents a style input.\n\n```ts\ntype Style = UrlStylesheet | HTMLStyleElement | CSSStyleSheet | StyleSheetList;\n```\n\n---\n\n###### Styles\n\nRepresents a single style object or an array of style objects.\n\n```ts\ntype Styles = Style | Style[];\n```\n\n---\n\n###### `cloneStyleSheetList`\n\nClones a StyleSheetList or array of CSSStyleSheets into an array of `HTMLStyleElement` objects.\n\nThis function extracts CSS rules from each stylesheet and creates corresponding `\u003cstyle\u003e`\nelements containing the serialized CSS text. If an error occurs while processing a stylesheet,\nthe error is logged and that stylesheet is skipped.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type                              | Description                                                               |\n| --------- | --------------------------------- | ------------------------------------------------------------------------- |\n| `styles`  | `StyleSheetList\\|CSSStyleSheet[]` | The source `StyleSheetList` or array of `CSSStyleSheet` objects to clone. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `HTMLStyleElement[]`\n\n- An array of `HTMLStyleElement` objects, each containing the CSS rules from the source stylesheets.\n- Failed stylesheets are filtered out and not included in the result.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eExamples\u003c/summary\u003e\n\n```ts\nimport { cloneStyleSheetList } from \"@alessiofrittoli/web-utils\";\n\nconst styles = cloneStyleSheetList(document.styleSheets);\n// do something with cloned stylesheets\n// styles.forEach( style =\u003e shadowRoot.appendChild( style ) )\n```\n\n\u003c/details\u003e\n\n---\n\n###### `cloneStyleSheets`\n\nClones style sheets from various sources into new `HTMLStyleElement` nodes.\n\n- When a URL stylesheet has `fetch: true`, the stylesheet content is fetched and embedded as inline CSS.\n- When `fetch: false` (default), a link element is created instead.\n- URL parsing is handled through the `Url` utility with support for both string and `UrlInput` object formats.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                                                   |\n| --------- | -------- | ------------------------------------------------------------- |\n| `styles`  | `Styles` | A style source or array of style sources.                     |\n|           |          | - See [`Styles`](#styles) type for a list of possible values. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `Promise\u003cCloneStyleSheetsReturn\u003e`\n\n- A promise that resolves to an array of cloned `HTMLStyleElement` and `HTMLLinkElement` nodes.\n- For inline styles and StyleSheetLists, returns `HTMLStyleElement` nodes.\n- For URL-based stylesheets, returns `HTMLLinkElement` nodes (or `HTMLStyleElement` if fetch is `true`).\n- Failed operations are silently ignored.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eExamples\u003c/summary\u003e\n\n###### Cloning `StyleSheetList`\n\n```ts\nimport { cloneStyleSheets } from \"@alessiofrittoli/web-utils\";\n\nconst styles = await cloneStyleSheets(document.styleSheets);\n// do something with cloned dcoument stylesheets\n// styles.forEach( style =\u003e shadowRoot.appendChild( style ) )\n```\n\n---\n\n###### Cloning stylesheets from URL\n\n```ts\nimport { cloneStyleSheets } from \"@alessiofrittoli/web-utils\";\n\nconst styles = await cloneStyleSheets(\"/path-to-stylesheet-file.css\");\n\nconst styles = await cloneStyleSheets({\n  url: \"/path-to-stylesheet-file-2.css\",\n  fetch: true,\n});\n\nconst styles = await cloneStyleSheets([\n  \"/path-to-stylesheet-file-3.css\",\n  { url: \"/path-to-stylesheet-file-4.css\", fetch: true },\n]);\n```\n\n\u003c/details\u003e\n\n---\n\n#### Generators utilities\n\n##### `isGeneratorFunction`\n\nCheck if a function is a `GeneratorFunction` or `AsyncGeneratorFunction`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter   | Type      | Description            |\n| ----------- | --------- | ---------------------- |\n| `reference` | `unknown` | The function to check. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `reference` is `GeneratorFunction | AsyncGeneratorFunction`\n\n- `true` if the given `reference` is a `GeneratorFunction` or `AsyncGeneratorFunction`.\n- `false` otherwise.\n\n\u003c/details\u003e\n\n---\n\n##### `isDefaultGeneratorFunction`\n\nCheck if a function is a `GeneratorFunction`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter   | Type      | Description            |\n| ----------- | --------- | ---------------------- |\n| `reference` | `unknown` | The function to check. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `reference` is `GeneratorFunction`\n\n- `true` if the given `reference` is a `GeneratorFunction`.\n- `false` otherwise.\n\n\u003c/details\u003e\n\n---\n\n##### `isAsyncGeneratorFunction`\n\nCheck if a function is an `AsyncGeneratorFunction`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter   | Type      | Description            |\n| ----------- | --------- | ---------------------- |\n| `reference` | `unknown` | The function to check. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `reference` is `AsyncGeneratorFunction`\n\n- `true` if the given `reference` is an `AsyncGeneratorFunction`.\n- `false` otherwise.\n\n\u003c/details\u003e\n\n---\n\n##### `isGeneratorObject\u003cT\u003e`\n\nCheck if reference is a `Generator` or `AsyncGenerator`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter   | Type      | Description             |\n| ----------- | --------- | ----------------------- |\n| `reference` | `unknown` | The reference to check. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `reference` is `Generator\u003cT\u003e | AsyncGenerator\u003cT\u003e`\n\n- `true` if the given `reference` is a `Generator` or `AsyncGenerator`.\n- `false` otherwise.\n\n\u003c/details\u003e\n\n---\n\n##### `isDefaultGeneratorObject\u003cT\u003e`\n\nCheck if reference is a `Generator`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter   | Type      | Description             |\n| ----------- | --------- | ----------------------- |\n| `reference` | `unknown` | The reference to check. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `reference` is `Generator\u003cT\u003e`\n\n- `true` if the given `reference` is a `Generator`.\n- `false` otherwise.\n\n\u003c/details\u003e\n\n---\n\n##### `isAsyncGeneratorObject\u003cT\u003e`\n\nCheck if reference is an `AsyncGenerator`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter   | Type      | Description             |\n| ----------- | --------- | ----------------------- |\n| `reference` | `unknown` | The reference to check. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `reference` is `AsyncGenerator\u003cT\u003e`\n\n- `true` if the given `reference` is an `AsyncGenerator`.\n- `false` otherwise.\n\n\u003c/details\u003e\n\n---\n\n#### Map utilities\n\n##### Interface `TypedMap\u003cT, P, K\u003e`\n\nA type-safe extension of the Map class that enforces key-value relationships based on a provided type.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Type                      | Default   | Description                                                                         |\n| --------- | ------------------------- | --------- | ----------------------------------------------------------------------------------- |\n| `T`       | `Record\u003cstring, unknown\u003e` | `unknown` | The object type defining the key-value relationships.                               |\n| `P`       | `boolean`                 | `true`    | Defines whether the `Map.get()` method should return a possibily `undefined` value. |\n| `K`       | `keyof T`                 | `keyof T` | Internal - The subset of keys in T that are allowed in the Map.                     |\n\n\u003c/details\u003e\n\n---\n\n##### `getTypedMap\u003cT, P, K\u003e`\n\nCreates a new instance of a type-safe `Map` with the given type.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n- See [Interface `TypedMap\u003cT, P, K\u003e` - Type parameters](#interface-typedmapt-p-k)\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter  | Type                                       | Description                                |\n| ---------- | ------------------------------------------ | ------------------------------------------ |\n| `iterable` | `Iterable\u003creadonly [ K, T[ K ] ]\u003e \\| null` | Initial `Map` constructor iterable object. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `TypedMap\u003cT, P, K\u003e`\n\nA new instance of a type-safe `Map`.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Basic usage\n\n```ts\nimport { getTypedMap } from \"@alessiofrittoli/web-utils\";\n\ninterface User {\n  name: string;\n  age: number;\n  isActive: boolean;\n}\n\nconst user = getTypedMap\u003cUser\u003e([\n  [\"name\", \"Foo\"],\n  [\"age\", 27],\n  [\"isActive\", true],\n]);\n\nconsole.log(user.get(\"name\")); // type: `string | undefined`\nconsole.log(user.get(\"age\")); // type: `number | undefined`\nconsole.log(user.get(\"isActive\")); // type: `boolean | undefined`\n```\n\n---\n\n###### Respect the given type\n\n```ts\nimport { getTypedMap } from \"@alessiofrittoli/web-utils\";\n\ninterface User {\n  name: string;\n  age: number;\n  isActive: boolean;\n  banned?: boolean;\n}\n\nconst user = getTypedMap\u003cUser, false\u003e([\n  [\"name\", \"Foo\"],\n  [\"age\", 27],\n  [\"isActive\", true],\n]);\n\nconsole.log(user.get(\"name\")); // type: `string`\nconsole.log(user.get(\"age\")); // type: `number`\nconsole.log(user.get(\"isActive\")); // type: `boolean`\nconsole.log(user.get(\"banned\")); // type: `boolean | undefined`\n```\n\n\u003c/details\u003e\n\n---\n\n#### Promises utilities\n\n##### `sleep`\n\nAwait a void Promise that resolves after the given time.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                                                    |\n| --------- | -------- | -------------------------------------------------------------- |\n| `time`    | `number` | The sleep time in milliseconds after the Promise get resolved. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `Promise\u003cvoid\u003e`\n\nA new Promise which get resolved after the specified time.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { sleep } from \"@alessiofrittoli/web-utils\";\n\nconst fn = async () =\u003e {\n  // ...\n  await sleep(2000);\n  // ...\n};\n```\n\n\u003c/details\u003e\n\n---\n\n##### `deferTask`\n\nDefer task so main-thread is not blocked in order to quickly paint and respond to user interaction.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType Parameters\u003c/summary\u003e\n\n| Parameter | Description                                                                                       |\n| --------- | ------------------------------------------------------------------------------------------------- |\n| `T`       | The task function definition. `unknown` types will be inherited by your function type definition. |\n| `U`       | The task function arguments. `unknown` types will be inherited by your function type.             |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type | Description                                      |\n| --------- | ---- | ------------------------------------------------ |\n| `task`    | `T`  | The task callable function.                      |\n| `...args` | `U`  | Arguments required by the given `task` function. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `Promise\u003cAwaited\u003cReturnType\u003cT\u003e\u003e\u003e`\n\nA new Promise which returns the `task` result once fulfilled.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Basic usage\n\n```ts\nimport { deferTask } from '@alessiofrittoli/web-utils'\n\nconst myLongTask = () =\u003e {\n  ...\n}\n\nbutton.addEventListener( 'click', () =\u003e {\n  deferTask( myLongTask )\n} )\n```\n\n---\n\n###### With custom arguments\n\n```ts\nimport { deferTask } from '@alessiofrittoli/web-utils'\n\nconst myLongTask = ( target: HTMLButtonElement ) =\u003e {\n  ...\n}\n\nbutton.addEventListener( 'click', event =\u003e {\n  const target = event.target as HTMLButtonElement\n  deferTask( myLongTask, target )\n} )\n```\n\n\u003c/details\u003e\n\n---\n\n##### `deferCallback`\n\nDefer task handler so main-thread is not blocked in order to quickly paint and respond to user interaction.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType Parameters\u003c/summary\u003e\n\n| Parameter | Description                                                                                       |\n| --------- | ------------------------------------------------------------------------------------------------- |\n| `T`       | The task function definition. `unknown` types will be inherited by your function type definition. |\n| `U`       | The task function arguments. `unknown` types will be inherited by your function type.             |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type | Description                 |\n| --------- | ---- | --------------------------- |\n| `task`    | `T`  | The task callable function. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `( ...args: U ) =\u003e Promise\u003cAwaited\u003cReturnType\u003cT\u003e\u003e\u003e`\n\nA new handler which returns a new Promise that returns the `task` result once fulfilled.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { deferCallback } from '@alessiofrittoli/web-utils'\n\nconst myLongTask = ( event: Event ) =\u003e {\n  ...\n}\n\nbutton.addEventListener( 'click', deferCallback( myLongTask ) )\n```\n\n\u003c/details\u003e\n\n---\n\n#### Strings utilities\n\n##### `ucFirst`\n\nMake first letter uppercase.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                  |\n| --------- | -------- | ---------------------------- |\n| `input`   | `string` | The input string to convert. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `string`\n\nThe processed string.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { ucFirst } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(ucFirst(\"String value\")); // Outputs: 'String value'\nconsole.log(ucFirst(\"string value\")); // Outputs: 'String value'\n```\n\n\u003c/details\u003e\n\n---\n\n##### `lcFirst`\n\nMake first letter lowercase.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                  |\n| --------- | -------- | ---------------------------- |\n| `input`   | `string` | The input string to convert. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `string`\n\nThe processed string.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { lcFirst } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(lcFirst(\"String value\")); // Outputs: 'string value'\nconsole.log(lcFirst(\"string value\")); // Outputs: 'string value'\n```\n\n\u003c/details\u003e\n\n---\n\n##### `toCamelCase`\n\nConvert string to camelCase.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                  |\n| --------- | -------- | ---------------------------- |\n| `input`   | `string` | The input string to convert. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `string`\n\nThe converted string to camelCase.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { toCamelCase } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(toCamelCase(\"font-family\")); // Outputs: 'fontFamily'\nconsole.log(toCamelCase(\"background-color\")); // Outputs: 'backgroundColor'\nconsole.log(toCamelCase(\"-webkit-align-content\")); // Outputs: 'WebkitAlignContent'\nconsole.log(toCamelCase(\"some value\")); // Outputs: 'someValue'\nconsole.log(toCamelCase(\"some_value\")); // Outputs: 'someValue'\nconsole.log(toCamelCase(\"some value_with mixed_Cases\")); // Outputs: 'someValueWithMixedCases'\nconsole.log(toCamelCase(\"-string@with#special$characters\")); // Outputs: 'StringWithSpecialCharacters'\n```\n\n\u003c/details\u003e\n\n---\n\n##### `toKebabCase`\n\nConvert string to kebab-case string.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                  |\n| --------- | -------- | ---------------------------- |\n| `input`   | `string` | The input string to convert. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `string`\n\nThe converted string to kebab-case.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { toKebabCase } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(toKebabCase(\"fontFamily\")); // Outputs: 'font-family'\nconsole.log(toKebabCase(\"backgroundColor\")); // Outputs: 'background-color'\nconsole.log(toKebabCase(\"string with spaces\")); // Outputs: 'string-with-spaces'\nconsole.log(toKebabCase(\"string_with_underscores\")); // Outputs: 'string-with-underscores'\nconsole.log(toKebabCase(\"WebkitAlignContent\")); // Outputs: '-webkit-align-content'\nconsole.log(toKebabCase(\"some value_with mixed_Cases\")); // Outputs: 'some-value-with-mixed-cases'\nconsole.log(toKebabCase(\"-string@with#special$characters\")); // Outputs: '-string-with-special-characters\n```\n\n\u003c/details\u003e\n\n---\n\n##### `stringifyValue`\n\nStringify value.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type  | Description             |\n| --------- | ----- | ----------------------- |\n| `input`   | `any` | The value to stringify. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `string`\n\nThe stringified `input`.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { stringifyValue } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(stringifyValue(new Date(\"Sat, 20 Apr 2025 16:20:00 GMT\")));\n// Outputs: '2025-04-20T16:20:00.000Z'\n\nconsole.log(stringifyValue(null));\n// Outputs: 'null'\n\nconsole.log(stringifyValue({ prop: \"value\", prop2: true }));\n// Outputs: '{\"prop\":\"value\",\"prop2\":true}'\n\nconsole.log(stringifyValue([1, 2, true, null, () =\u003e {}]));\n// Outputs: '[1,2,true,null,null]'\n\nconsole.log(\n  stringifyValue(\n    new Map([\n      [\"key\", \"value\"],\n      [\"key2\", \"value\"],\n    ]),\n  ),\n);\n// Outputs: '[[\"key\",\"value\"],[\"key2\",\"value\"]]'\n\nconsole.log(\n  stringifyValue(\n    new Headers({\n      key: \"value\",\n      key2: \"value\",\n    }),\n  ),\n);\n// Outputs: '[[\"key\",\"value\"],[\"key2\",\"value\"]]'\n\nconsole.log(stringifyValue(true)); // Outputs: 'true'\nconsole.log(stringifyValue(false)); // Outputs: 'false'\nconsole.log(stringifyValue(0)); // Outputs: '0'\nconsole.log(stringifyValue(420)); // Outputs: '420'\n\nconsole.log(stringifyValue(undefined)); // Outputs: ''\nconsole.log(stringifyValue(() =\u003e {})); // Outputs: ''\nconsole.log(stringifyValue(new Promise\u003cvoid\u003e((resolve) =\u003e resolve()))); // Outputs: ''\n```\n\n\u003c/details\u003e\n\n---\n\n##### `parseValue`\n\nParse stringified value.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                       |\n| --------- | --------------------------------- |\n| `T`       | The expected returned value type. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description         |\n| --------- | -------- | ------------------- |\n| `input`   | `string` | The value to parse. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `T | undefined`\n\n- The parsed `input`.\n- `undefined` if no `input` or empty `string` is given.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { parseValue } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(parseValue\u003cDate\u003e(stringifyValue(new Date())));\n// Outputs: current Date object.\n\nconsole.log(parseValue\u003cnumber\u003e(\"12345\")); // Outputs: 12345\nconsole.log(parseValue()); // Outputs: undefined\nconsole.log(parseValue(\" \")); // Outputs: undefined\n\nconsole.log(parseValue\u003ctrue\u003e(stringifyValue(true)));\n// Outputs: true\n\nconsole.log(parseValue(stringifyValue({ key: \"value\" })));\n// Outputs: { key: 'value' }\n\nconsole.log(parseValue(stringifyValue([1, 2, 3, 4, 5])));\n// Outputs: [ 1, 2, 3, 4, 5 ]\n\nconsole.log(parseValue(\"String value\")); // Outputs: 'String value'\n```\n\n\u003c/details\u003e\n\n---\n\n##### `parameterized`\n\nCreates a parameterized string with placeholder values.\n\n###### Types\n\n###### `ParameterizedValue`\n\nRepresents a value that can be used as a parameter in string operations.\n\n```ts\ntype ParameterizedValue = string | boolean | number | bigint;\n```\n\n###### `Parameterized`\n\nRepresents a parameterized string with its corresponding values.\n\n```ts\ntype Parameterized = [string, ParameterizedValue[]];\n```\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eExamples\u003c/summary\u003e\n\n```ts\nimport { parameterized } from \"@alessiofrittoli/web-utils\";\n\nconst data = {\n  value: \"parameterized\",\n};\n\nconsole.log(parameterized`My string with ${data.value} values.`); // [ 'My string with ? values.', [ 'parameterized' ] ]\n```\n\n\u003c/details\u003e\n\n---\n\n#### Types utilities\n\n⚠️ Docs coming soon\n\n---\n\n#### Validation utilities\n\n⚠️ Docs coming soon\n\n#### Objects utilities\n\n⚠️ Docs coming soon\n\n---\n\n#### Browser API utilities\n\n##### `getMediaMatches`\n\nSafely executes `window.matchMedia()` in server and browser environments.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                      |\n| --------- | -------- | -------------------------------- |\n| `query`   | `string` | The Media Query string to check. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `boolean`\n\n- `false` if `window` is not defined or if the `document` currently doesn't matches the given `query`.\n- `true` otherwise.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Check if current device is landscape oriented\n\n```ts\nimport { getMediaMatches } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(!getMediaMatches(\"(orientation:portrait)\"));\n```\n\n\u003c/details\u003e\n\n---\n\n##### `openBrowserPopUp`\n\nOpens a webpage in a browser PopUp.\n\nThe `openBrowserPopUp` uses [`Window.open()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) under the hood, but provides default options to make your work easier.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter          | Type                      | Default | Description                                                                                                                                                                  |\n| ------------------ | ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `options`          | `OpenBrowserPopUpOptions` | -       | An object defining custom PopUp options.                                                                                                                                     |\n| `options.url`      | `UrlInput`                | -       | The URL or path of the resource to be loaded. See [UrlInput](https://github.com/alessiofrittoli/url-utils?tab=readme-ov-file#urlinput) for more info about accepted formats. |\n| `options.width`    | `number`                  | `600`   | The PopUp width.                                                                                                                                                             |\n| `options.height`   | `number`                  | `800`   | The PopUp height.                                                                                                                                                            |\n| `options.context`  | `string`                  | -       | A string, without whitespace, specifying the name of the browsing context the resource is being loaded into.                                                                 |\n| `options.features` | `OptionsFeatures`         | -       | Additional custom PopUp features.                                                                                                                                            |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `WindowProxy | null`\n\n- a `WindowProxy` object is returned if the browser successfully opens the new browsing context.\n- `null` is returned if the browser fails to open the new browsing context, for example because it was blocked by a browser popup blocker.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Re-focus a previously opened popup\n\n```ts\nimport { openBrowserPopUp } from \"@alessiofrittoli/web-utils\";\n\nlet windowProxy: WindowProxy | null = null;\n\nconst clickHandler = () =\u003e {\n  if (windowProxy \u0026\u0026 !windowProxy.closed) {\n    return windowProxy.focus();\n  }\n\n  windowProxy = openBrowserPopUp({\n    url: {\n      pathname: \"/\",\n      query: { key: \"value\" },\n    },\n  });\n};\n```\n\n---\n\n###### Re-use a popup\n\n```ts\nimport { openBrowserPopUp } from \"@alessiofrittoli/web-utils\";\n\nconst clickHandler = () =\u003e {\n  openBrowserPopUp({\n    context: \"some-context-name\",\n    url: {\n      pathname: \"/\",\n      query: { key: \"value\" },\n    },\n  });\n};\n\nconst clickHandler2 = () =\u003e {\n  openBrowserPopUp({\n    context: \"some-context-name\",\n    url: \"/other-path\",\n  });\n};\n```\n\n\u003c/details\u003e\n\n---\n\n##### Document Picture-in-Picture\n\n###### Types\n\n###### `OpenDocumentPictureInPictureOptions`\n\nDefines configuration options for opening a Document Picture-in-Picture window.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eProperties\u003c/summary\u003e\n\n| Property                       | Type              | Default        | Description                                                                                |\n| ------------------------------ | ----------------- | -------------- | ------------------------------------------------------------------------------------------ |\n| `sizes`                        | `InputDimensions` | `[ 250, 250 ]` | A tuple defining non-negative numbers representing the width and the height to set         |\n|                                |                   |                | for the Picture-in-Picture window's viewport, in pixels.                                   |\n|                                |                   |                | - See [`InputDimensions`](#inputdimensions) type for a list of possible values.            |\n| `disallowReturnToOpener`       | `boolean`         | `false`        | Hints to the browser that it should not display a UI control that enables the              |\n|                                |                   |                | user to return to the originating tab and close the Picture-in-Picture window.             |\n| `preferInitialWindowPlacement` | `boolean`         | `false`        | Defines whether the Picture-in-Picture window will always appear back at the               |\n|                                |                   |                | position and size it initially opened at, when it is closed and then reopened.             |\n|                                |                   |                | By contrast, if `preferInitialWindowPlacement` is `false` the                              |\n|                                |                   |                | Picture-in-Picture window's size and position will be remembered when closed               |\n|                                |                   |                | and reopened — it will reopen at its previous position and size,                           |\n|                                |                   |                | for example as set by the user.                                                            |\n| `styles`                       | `Styles`          | -              | Custom styles to load inside the Picture-in-Picture window.                                |\n|                                |                   |                | - See [`Styles`](#styles) type for a list of possible values.                              |\n|                                |                   |                | ⚠️ To keep consistent styling with your web-app, document styles are automatically cloned. |\n| `onQuit`                       | `() =\u003e void`      | -              | A callback to execute when Picture-in-Picture window is closed.                            |\n\n\u003c/details\u003e\n\n---\n\n###### `OpenDocumentPictureInPicture`\n\nDefines the returned result of opening a Document Picture-in-Picture window.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eProperties\u003c/summary\u003e\n\n| Property | Type     | Description                                                         |\n| -------- | -------- | ------------------------------------------------------------------- |\n| `window` | `Window` | The browsing context inside the Document Picture-in-Picture window. |\n\n\u003c/details\u003e\n\n---\n\n###### `isDocumentPictureInPictureSupported`\n\nChecks if the Document Picture-in-Picture API is supported by the current browser.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `boolean`\n\n- `true` if Document Picture-in-Picture is supported.\n- `false` otherwise.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eExamples\u003c/summary\u003e\n\n```ts\nimport { isDocumentPictureInPictureSupported } from \"@alessiofrittoli/web-utils\";\n\nif ( isDocumentPictureInPictureSupported() ) {\n  ...\n}\n```\n\n\u003c/details\u003e\n\n---\n\n###### `requiresDocumentPictureInPictureAPI`\n\nValidates that the Document Picture-in-Picture API is supported in the current browser.\n\n- Throws a new `Exception` with code `ErrorCode.DOCUMENT_PIP_NOT_SUPPORTED` if the Document Picture-in-Picture API is not supported.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eExamples\u003c/summary\u003e\n\n```ts\nimport { Exception } from \"@alessiofrittoli/exception\";\nimport { requiresDocumentPictureInPictureAPI, ErrorCode } from \"@alessiofrittoli/web-utils\";\n\nconst myFunction = () =\u003e {\n  requiresDocumentPictureInPictureAPI()\n  ...\n}\n\ntry {\n  myFunction()\n} catch ( _err ) {\n  const err = _err as Error\n\n  const error = (\n    Exception.isException\u003cstring, ErrorCode\u003e(err)\n      ? err\n      : (\n        new Exception(\n          err.message,\n          {\n            code  : ErrorCode.UNKNOWN,\n            name  : err.name,\n            cause : err,\n          }\n        )\n      )\n  )\n\n  switch ( error.code ) {\n    case ErrorCode.DOCUMENT_PIP_NOT_SUPPORTED:\n      console.warn( 'Document Picture-in-Picture is not supported.' )\n      break\n    default:\n      console.error( 'Unknown error', error )\n  }\n}\n\n```\n\n\u003c/details\u003e\n\n###### `openDocumentPictureInPicture`\n\nOpens a Document Picture-in-Picture window.\n\n- Throws a new `Exception` with code `ErrorCode.DOCUMENT_PIP_NOT_SUPPORTED` if the Document Picture-in-Picture API is not supported.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type                                  | Description                                                                 |\n| --------- | ------------------------------------- | --------------------------------------------------------------------------- |\n| `options` | `OpenDocumentPictureInPictureOptions` | Configuration options for opening a new Document Picture-in-Picture window. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `Promise\u003cOpenDocumentPictureInPicture\u003e`\n\n- A new Promise that resolves to the Document Picture-in-Picture result containing the `window` of the new browsing context.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eExamples\u003c/summary\u003e\n\n###### Simple usage\n\n```ts\nimport { Exception } from \"@alessiofrittoli/exception\";\nimport {\n  openDocumentPictureInPicture,\n  ErrorCode,\n} from \"@alessiofrittoli/web-utils\";\n\nconst openPictureInPicture = async () =\u003e {\n  try {\n    const content = document.createElement(\"div\");\n\n    const { window } = await openDocumentPictureInPicture();\n\n    window.document.body.appendChild(content);\n  } catch (_err) {\n    const err = _err as Error;\n\n    const error = Exception.isException\u003cstring, ErrorCode\u003e(err)\n      ? err\n      : new Exception(err.message, {\n          code: ErrorCode.UNKNOWN,\n          name: err.name,\n          cause: err,\n        });\n\n    switch (error.code) {\n      case ErrorCode.DOCUMENT_PIP_NOT_SUPPORTED:\n        console.warn(\"Document Picture-in-Picture is not supported.\");\n        break;\n      default:\n        console.error(\"Unknown error\", error);\n    }\n  }\n};\n```\n\n---\n\n###### Load render blocking styles\n\n```ts\nimport { Exception } from \"@alessiofrittoli/exception\";\nimport {\n  openDocumentPictureInPicture,\n  ErrorCode,\n} from \"@alessiofrittoli/web-utils\";\n\nconst openPictureInPicture = async () =\u003e {\n  try {\n    const content = document.createElement(\"div\");\n\n    const { window } = await openDocumentPictureInPicture({\n      styles: {\n        url: \"/important-stylesheet-fetched-before-opening.css\",\n        fetch: true,\n      },\n    });\n\n    window.document.body.appendChild(content);\n  } catch (error) {\n    // ...\n  }\n};\n```\n\n\u003c/details\u003e\n\n---\n\n#### Device utilities\n\n##### `isPortrait`\n\nCheck if device is in portrait orientation.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `boolean`\n\n- `true` if the device is in portrait orientation when this function is executed.\n- `false` otherwise.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Check if current device is landscape oriented\n\n```ts\nimport { isPortrait } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(!isPortrait());\n```\n\n\u003c/details\u003e\n\n---\n\n#### Storage utilities\n\n##### `Cookie` Class\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eImporting enum and types\u003c/summary\u003e\n\n```ts\nimport { Priority, SameSite } from \"@alessiofrittoli/web-utils\";\n\nimport type {\n  RawCookie,\n  ParsedCookie,\n  ParsedCookieMap,\n} from \"@alessiofrittoli/web-utils\";\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eEnumerators\u003c/summary\u003e\n\n###### `Priority` Enum\n\nThe Cookie Priority.\n\n| Constant | Value  | Description                |\n| -------- | ------ | -------------------------- |\n| `Low`    | Low    | Low priority.              |\n| `Medium` | Medium | Medium priority (default). |\n| `High`   | High   | High priority.             |\n\n---\n\n###### `SameSite` Enum\n\nControls whether or not a cookie is sent with cross-site requests, providing some protection against cross-site request forgery attacks ([CSRF](https://developer.mozilla.org/en-US/docs/Glossary/CSRF)).\n\n| Constant | Value  | Description                                                                                                                                                                  |\n| -------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `Strict` | Strict | The browser sends the cookie only for same-site requests.                                                                                                                    |\n| `Lax`    | Lax    | The cookie is not sent on cross-site requests, such as on requests to load images or frames, but is sent when a user is navigating to the origin site from an external site. |\n| `None`   | None   | The browser sends the cookie with both cross-site and same-site requests.                                                                                                    |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eTypes\u003c/summary\u003e\n\n###### `RawCookie\u003cK, V\u003e`\n\nInterface representing Cookie properties before it get parsed.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eProperties\u003c/summary\u003e\n\n| Property      | Type                       | Description                                                                                                                                                                                               |\n| ------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `name`        | `K`                        | The Cookie name.                                                                                                                                                                                          |\n| `value`       | `V`                        | The Cookie value.                                                                                                                                                                                         |\n| `domain`      | `string`                   | Defines the host to which the cookie will be sent.                                                                                                                                                        |\n| `expires`     | `string \\| number \\| Date` | Indicates the maximum lifetime of the cookie.                                                                                                                                                             |\n| `httpOnly`    | `boolean`                  | Forbids JavaScript from accessing the cookie, for example, through the [`Document.cookie`](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) property.                                    |\n| `maxAge`      | `number`                   | Indicates the number of seconds until the cookie expires. If set, `expires` is ignored.                                                                                                                   |\n| `partitioned` | `boolean`                  | Indicates that the cookie should be stored using partitioned storage.                                                                                                                                     |\n| `path`        | `string`                   | Indicates the path that must exist in the requested URL for the browser to send the `Cookie` header.                                                                                                      |\n| `sameSite`    | `SameSite`                 | Controls whether or not a cookie is sent with cross-site requests, providing some protection against cross-site request forgery attacks ([CSRF](https://developer.mozilla.org/en-US/docs/Glossary/CSRF)). |\n| `secure`      | `boolean`                  | Indicates that the cookie is sent to the server only when a request is made with the https: scheme.                                                                                                       |\n| `priority`    | `Priority`                 | Defines the Cookie priority.                                                                                                                                                                              |\n\n\u003c/details\u003e\n\n---\n\n###### `ParsedCookie\u003cK, V\u003e`\n\nInterface representing Cookie properties after it get parsed.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eProperties\u003c/summary\u003e\n\n- Extends and overrides - [`RawCookie\u003cK, V\u003e`](#rawcookiek-v)\n\n| Property  | Type   | Description                                   |\n| --------- | ------ | --------------------------------------------- |\n| `expires` | `Date` | Indicates the maximum lifetime of the cookie. |\n\n\u003c/details\u003e\n\n---\n\n###### `ParsedCookieMap\u003cK, V\u003e`\n\nMap representation of a parsed Cookie.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eStatic methods\u003c/summary\u003e\n\n###### `Cookie.parse\u003cK, V\u003e()`\n\nParse the given cookie parameters to a Cookie Map.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                   |\n| --------- | ----------------------------- |\n| `K`       | The typed cookie name.        |\n| `V`       | The type of the cookie value. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type                                       | Description                                |\n| --------- | ------------------------------------------ | ------------------------------------------ |\n| `options` | `RawCookie\u003cK, V\u003e \\| ParsedCookieMap\u003cK, V\u003e` | The cookie options or a parsed Cookie Map. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `ParsedCookieMap\u003cK, V\u003e`\n\nThe parsed Cookie Map.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\nconst cookie = Cookie.parse({\n  name: \"cookiename\",\n  value: { test: \"value\" },\n  path: \"/specific-path\",\n  priority: Priority.High,\n  expires: Date.now() + 20 * 60 * 1000,\n  domain: \"example.com\",\n  secure: true,\n  httpOnly: true,\n  sameSite: SameSite.Lax,\n  maxAge: Date.now() + 30 * 60 * 1000,\n  partitioned: true,\n});\n```\n\n\u003c/details\u003e\n\n---\n\n###### `Cookie.toString\u003cK, V\u003e()`\n\nStringify a Cookie ready to be stored.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                   |\n| --------- | ----------------------------- |\n| `K`       | The typed cookie name.        |\n| `V`       | The type of the cookie value. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type                                       | Description                                |\n| --------- | ------------------------------------------ | ------------------------------------------ |\n| `options` | `RawCookie\u003cK, V\u003e \\| ParsedCookieMap\u003cK, V\u003e` | The cookie options or a parsed Cookie Map. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `string`\n\nThe stringified Cookie ready to be stored.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\ndocument.cookie = Cookie.toString({\n  name: \"cookiename\",\n  value: { test: \"value\" },\n  path: \"/specific-path\",\n  priority: Priority.High,\n  expires: Date.now() + 20 * 60 * 1000,\n  domain: \"example.com\",\n  secure: true,\n  httpOnly: false,\n  sameSite: SameSite.Lax,\n  maxAge: Date.now() + 30 * 60 * 1000,\n  partitioned: true,\n});\n```\n\n\u003c/details\u003e\n\n---\n\n###### `Cookie.fromString\u003cK, V\u003e()`\n\nParse a cookie string to a Cookie Map.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                     |\n| --------- | ------------------------------- |\n| `K`       | The typed cookie name.          |\n| `V`       | The expected cookie value type. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description        |\n| --------- | -------- | ------------------ |\n| `cookie`  | `string` | The cookie string. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `ParsedCookieMap\u003cK, V\u003e | null`\n\nThe parsed Cookie Map or `null` if parsing fails.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\nconst cookies = document.cookie\n  .split(\"; \")\n  .map(Cookie.fromString)\n  .filter(Boolean);\n```\n\n\u003c/details\u003e\n\n---\n\n###### `Cookie.fromListString\u003cT, K\u003e()`\n\nParse a cookie list string to a Map of cookies.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                                                                         |\n| --------- | ----------------------------------------------------------------------------------- |\n| `T`       | A `Record` o key-value pairs (key: cookie name, value: expected cookie value type). |\n| `K`       | Internal.                                                                           |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description             |\n| --------- | -------- | ----------------------- |\n| `list`    | `string` | The cookie list string. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `TypedMap\u003c{ [P in K]: ParsedCookieMap\u003cP, T[P]\u003e; }\u003e`\n\nThe Map of parsed cookies indexed by the Cookie name.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Defining custom types\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\n/** On-site stubbed cookie names. */\nenum CookieName {\n  COOKIE_1 = \"cookie-1\",\n  COOKIE_2 = \"cookie-2\",\n}\n\ninterface Cookie1 {\n  test: \"value\";\n}\n\ninterface Cookie2 {\n  test: boolean;\n}\n\ntype CookiesMap = {\n  [CookieName.COOKIE_1]: Cookie1;\n  [CookieName.COOKIE_2]: Cookie2;\n};\n```\n\n---\n\n###### Get parsed cookies from `Document.cookie`\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\nconst cookies = Cookie.fromListString\u003cCookiesMap\u003e(document.cookie);\nconst cookie = cookies.get(CookieName.COOKIE_1); // `ParsedCookieMap\u003cCookieName.COOKIE_1, Cookie1\u003e | undefined`\nconst cookieValue = cookie?.get(\"value\"); // `Cookie1 | undefined`\n```\n\n---\n\n###### Get parsed cookies from a request `Cookie` header\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\nconst { headers } = request;\nconst cookielist = headers.get(\"Cookie\");\n\nif (cookielist) {\n  const cookies = Cookie.fromListString\u003cCookiesMap\u003e(cookielist);\n  const cookie = cookies.get(CookieName.COOKIE_2); // `ParsedCookieMap\u003cCookieName.COOKIE_2, Cookie2\u003e | undefined`\n  const cookieValue = cookie?.get(\"value\"); // `Cookie2 | undefined`\n}\n```\n\n\u003c/details\u003e\n\n---\n\n###### `Cookie.get\u003cT\u003e()`\n\nGet a cookie by cookie name from `Document.cookie`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                             |\n| --------- | --------------------------------------- |\n| `T`       | The expected type for the Cookie value. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description             |\n| --------- | -------- | ----------------------- |\n| `name`    | `string` | The name of the cookie. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `ParsedCookieMap\u003ctypeof name, T\u003e | undefined`\n\n- The found parsed cookie.\n- `undefined` if no cookie has been found in `Document.cookie`.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\nconst cookie = Cookie.get\u003cstring\u003e(\"access_token\");\nconst value = cookie?.get(\"value\"); // `string | undefined`\n```\n\n\u003c/details\u003e\n\n---\n\n###### `Cookie.getAll\u003cT\u003e()`\n\nGet a `Map` of all cookies found in `Document.cookie`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                                                                         |\n| --------- | ----------------------------------------------------------------------------------- |\n| `T`       | A `Record` o key-value pairs (key: cookie name, value: expected cookie value type). |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `TypedMap\u003c{ [P in K]: ParsedCookieMap\u003cP, T[P]\u003e; }\u003e`\n\nThe Map of parsed cookies indexed by the Cookie name.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\nconst cookies = Cookie.getAll();\nconst cookie = cookies.get(\"somecookie\");\n```\n\n\u003c/details\u003e\n\n###### `Cookie.set\u003cK, V\u003e()`\n\nSet a cookie to `Document.cookie`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description            |\n| --------- | ---------------------- |\n| `K`       | The typed cookie name. |\n| `V`       | The cookie value type. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type                                       | Description                                |\n| --------- | ------------------------------------------ | ------------------------------------------ |\n| `options` | `RawCookie\u003cK, V\u003e \\| ParsedCookieMap\u003cK, V\u003e` | The cookie options or a parsed Cookie Map. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `ParsedCookieMap\u003cK, V\u003e | false`\n\n- The set Cookie `Map` if successful.\n- `false` on failure.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { Cookie, type RawCookie } from \"@alessiofrittoli/web-utils\";\n\nconst cookieOptions: RawCookie = {\n  name: \"cookiename\",\n  value: { test: \"value\" },\n  path: \"/specific-path\",\n  priority: Priority.High,\n  expires: Date.now() + 20 * 60 * 1000,\n  domain: \"example.com\",\n  secure: true,\n  httpOnly: false,\n  sameSite: SameSite.Lax,\n  maxAge: Date.now() + 30 * 60 * 1000,\n  partitioned: true,\n};\n\nCookie.set(cookieOptions);\n// or\nCookie.set(Coookie.parse(cookieOptions));\n```\n\n\u003c/details\u003e\n\n---\n\n###### `Cookie.delete()`\n\nDelete a cookie by cookie name from `Document.cookie`.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                |\n| --------- | -------- | -------------------------- |\n| `name`    | `string` | The cookie name to delete. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `boolean`\n\n- `true` on successfull.\n- `false` on failure.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { Cookie } from \"@alessiofrittoli/web-utils\";\n\nCookie.delete(\"some_cookie\");\n```\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n---\n\n##### `LocalStorage` Class\n\nA browser-compatible implementation of [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage).\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eStatic methods\u003c/summary\u003e\n\n###### `LocalStorage.key()`\n\nGet storage item name by item numeric index.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description                    |\n| --------- | -------- | ------------------------------ |\n| `index`   | `number` | The item index in the storage. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `string | null`\n\n- The name of the nth key.\n- `null` if n is greater than or equal to the number of key/value pairs.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { LocalStorage } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(LocalStorage.key(0)); // Outputs: first item name if any.\n```\n\n\u003c/details\u003e\n\n---\n\n###### `LocalStorage.getLength()`\n\nGet the number of key/value pairs.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `number`\n\nThe number of key/value pairs.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { LocalStorage } from \"@alessiofrittoli/web-utils\";\n\nconsole.log(LocalStorage.getLength());\n```\n\n\u003c/details\u003e\n\n---\n\n###### `LocalStorage.get\u003cT\u003e()`\n\nGet the current value associated with the given `key`, or `undefined` if the given `key` does not exist.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description                   |\n| --------- | ----------------------------- |\n| `T`       | The expected item value type. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description    |\n| --------- | -------- | -------------- |\n| `key`     | `string` | The item name. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `T | undefined`\n\n- The current value associated with the given `key`.\n- `undefined` if the given `key` does not exist.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { LocalStorage } from \"@alessiofrittoli/web-utils\";\n\nLocalStorage.get\u003cDate\u003e(\"expiration\");\n```\n\n\u003c/details\u003e\n\n---\n\n###### `LocalStorage.set\u003cT\u003e()`\n\nSets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.\n\nDispatches a storage event on Window objects holding an equivalent Storage object.\n\nIf a nullish or empty string value is provided, the `LocalStorage.delete()` method is invoked.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Description          |\n| --------- | -------------------- |\n| `T`       | The item value type. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description     |\n| --------- | -------- | --------------- |\n| `key`     | `string` | The item name.  |\n| `value`   | `T`      | The item value. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eThrows\u003c/summary\u003e\n\nType: `DOMException`\n\nA \"QuotaExceededError\" DOMException exception if the new value couldn't be set. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.)\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { LocalStorage } from \"@alessiofrittoli/web-utils\";\n\nLocalStorage.set\u003cDate\u003e(\"expiration\", new Date());\n```\n\n\u003c/details\u003e\n\n---\n\n###### `LocalStorage.delete()`\n\nRemoves the key/value pair with the given key, if a key/value pair with the given key exists.\n\nDispatches a storage event on Window objects holding an equivalent Storage object.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type     | Description    |\n| --------- | -------- | -------------- |\n| `key`     | `string` | The item name. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { LocalStorage } from \"@alessiofrittoli/web-utils\";\n\nLocalStorage.delete(\"expiration\");\n```\n\n\u003c/details\u003e\n\n---\n\n###### `LocalStorage.clear()`\n\nRemoves all key/value pairs, if there are any.\n\nDispatches a storage event on Window objects holding an equivalent Storage object.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { LocalStorage } from \"@alessiofrittoli/web-utils\";\n\nLocalStorage.clear();\n```\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n---\n\n##### `SessionStorage` Class\n\nA browser-compatible implementation of [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage).\n\nSame API References of [`LocalStorage` Class](#localstorage-class) is applied to the `SessionStorage` Class.\n\nPlease, refer to [`LocalStorage` Class](#localstorage-class) static methods API Reference for more informations.\n\n---\n\n#### Utils\n\n##### `getDimensions`\n\nExtracts and normalizes dimensions from various input formats.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eTypes\u003c/summary\u003e\n\n###### `InputDimensions`\n\nRepresents valid input types for specifying dimensions.\n\n```ts\ntype InputDimensions = string | number | [xy: Dimensions[number]] | Dimensions;\n```\n\n---\n\n###### `Dimensions`\n\nRepresents a tuple of two optional numeric values.\n\n```ts\ntype Dimensions = [x: number | undefined, y: number | undefined];\n```\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter    | Type              | Description           |\n| ------------ | ----------------- | --------------------- |\n| `dimensions` | `InputDimensions` | The input dimensions. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `Dimensions`\n\nA tuple containing `[ number, number ]` where either value can be `undefined`.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eExamples\u003c/summary\u003e\n\n```ts\nimport { getDimensions } from \"@alessiofrittoli/web-utils\";\n\nconst [width, height] = getDimensions(); // [ undefined, undefined ]\nconst [width, height] = getDimensions(100); // [ 100, 100 ]\nconst [width, height] = getDimensions([200, 300]); // [ 200, 300 ]\nconst [width, height] = getDimensions([200]); // [ 200, 200 ]\nconst [width, height] = getDimensions(\"200x300\"); // [ 200, 300 ]\n```\n\n\u003c/details\u003e\n\n---\n\n### Development\n\n#### Install depenendencies\n\n```bash\nnpm install\n```\n\nor using `pnpm`\n\n```bash\npnpm i\n```\n\n#### Build the source code\n\nRun the following command to test and build code for distribution.\n\n```bash\npnpm build\n```\n\n#### [ESLint](https://www.npmjs.com/package/eslint)\n\nwarnings / errors check.\n\n```bash\npnpm lint\n```\n\n#### [Jest](https://npmjs.com/package/jest)\n\nRun all the defined test suites by running the following:\n\n```bash\n# Run tests and watch file changes.\npnpm test:watch\n\n# Run tests in a CI environment.\npnpm test:ci\n```\n\n- See [`package.json`](./package.json) file scripts for more info.\n\nRun tests with coverage.\n\nAn HTTP server is then started to serve coverage files from `./coverage` folder.\n\n⚠️ You may see a blank page the first time you run this command. Simply refresh the browser to see the updates.\n\n```bash\ntest:coverage:serve\n```\n\n---\n\n### Contributing\n\nContributions are truly welcome!\n\nPlease refer to the [Contributing Doc](./CONTRIBUTING.md) for more information on how to start contributing to this project.\n\nHelp keep this project up to date with [GitHub Sponsor][sponsor-url].\n\n[![GitHub Sponsor][sponsor-badge]][sponsor-url]\n\n---\n\n### Security\n\nIf you believe you have found a security vulnerability, we encourage you to **_responsibly disclose this and NOT open a public issue_**. We will investigate all legitimate reports. Email `security@alessiofrittoli.it` to disclose any security vulnerabilities.\n\n### Made with ☕\n\n\u003ctable style='display:flex;gap:20px;'\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003cimg alt=\"avatar\" src='https://avatars.githubusercontent.com/u/35973186' style='width:60px;border-radius:50%;object-fit:contain;'\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n        \u003ctable style='display:flex;gap:2px;flex-direction:column;'\u003e\n          \u003ctbody\u003e\n              \u003ctr\u003e\n                \u003ctd\u003e\n                  \u003ca href='https://github.com/alessiofrittoli' target='_blank' rel='noopener'\u003eAlessio Frittoli\u003c/a\u003e\n                \u003c/td\u003e\n              \u003c/tr\u003e\n              \u003ctr\u003e\n                \u003ctd\u003e\n                  \u003csmall\u003e\n                    \u003ca href='https://alessiofrittoli.it' target='_blank' rel='noopener'\u003ehttps://alessiofrittoli.it\u003c/a\u003e |\n                    \u003ca href='mailto:info@alessiofrittoli.it' target='_blank' rel='noopener'\u003einfo@alessiofrittoli.it\u003c/a\u003e\n                  \u003c/small\u003e\n                \u003c/td\u003e\n              \u003c/tr\u003e\n          \u003c/tbody\u003e\n        \u003c/table\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falessiofrittoli%2Fweb-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falessiofrittoli%2Fweb-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falessiofrittoli%2Fweb-utils/lists"}