From 3924a52807fe3931bcc2f92190f76765d56e6075 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Wed, 15 May 2024 14:59:34 +0800 Subject: [PATCH 01/17] chore: initialized mobileOsDetect utils --- package-lock.json | 7 + package.json | 1 + src/constants/mobile-devices.constants.ts | 128 ++++++++++++++++++ src/utils/mobileOSDetectAsync.utils.ts | 95 +++++++++++++ .../Constants/mobile-devices.constants.md | 0 utils-docs/docs/Utils/mobileOSDetectAsync.md | 30 ++++ 6 files changed, 261 insertions(+) create mode 100644 src/constants/mobile-devices.constants.ts create mode 100644 src/utils/mobileOSDetectAsync.utils.ts create mode 100644 utils-docs/docs/Constants/mobile-devices.constants.md create mode 100644 utils-docs/docs/Utils/mobileOSDetectAsync.md diff --git a/package-lock.json b/package-lock.json index 354dc09..339a28b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@semantic-release/npm": "^12.0.0", "@semantic-release/release-notes-generator": "^13.0.0", "@types/node": "^20.11.18", + "@types/ua-parser-js": "^0.7.39", "@typescript-eslint/eslint-plugin": "^6.21.0", "@vitest/coverage-istanbul": "^1.2.2", "@vitest/coverage-v8": "^1.2.2", @@ -2615,6 +2616,12 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, + "node_modules/@types/ua-parser-js": { + "version": "0.7.39", + "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", + "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", diff --git a/package.json b/package.json index 7de18aa..1b69031 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@semantic-release/npm": "^12.0.0", "@semantic-release/release-notes-generator": "^13.0.0", "@types/node": "^20.11.18", + "@types/ua-parser-js": "^0.7.39", "@typescript-eslint/eslint-plugin": "^6.21.0", "@vitest/coverage-istanbul": "^1.2.2", "@vitest/coverage-v8": "^1.2.2", diff --git a/src/constants/mobile-devices.constants.ts b/src/constants/mobile-devices.constants.ts new file mode 100644 index 0000000..6bbffd8 --- /dev/null +++ b/src/constants/mobile-devices.constants.ts @@ -0,0 +1,128 @@ +/** + * @description + * This pattern matches any string of three uppercase letters followed by a hyphen. + * @example huaweiDevicesRegex.test("ALP-") // returns true + */ +export const huaweiDevicesRegex = /\b([A-Z]{3}-)\b/gi; + +/** + * @regex /\b([A-Z]{3}-)\b/gi + * @description + * This pattern matches any string that contains a sequence of three uppercase letters followed by a hyphen. + * The sequence must be a word on its own (i.e., it must be surrounded by word boundaries). + * The 'g' flag is used for global search (to find all matches rather than stopping after the first match), and the 'i' flag is used for case-insensitive search. + * @example huaweiDevicesRegex.test("AMN-") // returns true + * @example huaweiDevicesRegex.test("ANA-") // returns true + * @example huaweiDevicesRegex.test("ANE-") // returns true + */ +export const validCodes = new Set([ + "ALP-", + "AMN-", + "ANA-", + "ANE-", + "ANG-", + "AQM-", + "ARS-", + "ART-", + "ATU-", + "BAC-", + "BLA-", + "BRQ-", + "CAG-", + "CAM-", + "CAN-", + "CAZ-", + "CDL-", + "CDY-", + "CLT-", + "CRO-", + "CUN-", + "DIG-", + "DRA-", + "DUA-", + "DUB-", + "DVC-", + "ELE-", + "ELS-", + "EML-", + "EVA-", + "EVR-", + "FIG-", + "FLA-", + "FRL-", + "GLK-", + "HMA-", + "HW-", + "HWI-", + "INE-", + "JAT-", + "JEF-", + "JER-", + "JKM-", + "JNY-", + "JSC-", + "LDN-", + "LIO-", + "LON-", + "LUA-", + "LYA-", + "LYO-", + "MAR-", + "MED-", + "MHA-", + "MLA-", + "MRD-", + "MYA-", + "NCE-", + "NEO-", + "NOH-", + "NOP-", + "OCE-", + "PAR-", + "PIC-", + "POT-", + "PPA-", + "PRA-", + "RNE-", + "SEA-", + "SLA-", + "SNE-", + "SPN-", + "STK-", + "TAH-", + "TAS-", + "TET-", + "TRT-", + "VCE-", + "VIE-", + "VKY-", + "VNS-", + "VOG-", + "VTR-", + "WAS-", + "WKG-", + "WLZ-", + "JAD-", + "MLD-", + "RTE-", + "NAM-", + "NEN-", + "BAL-", + "JLN-", + "YAL-", + "MGA-", + "FGD-", + "XYAO-", + "BON-", + "ALN-", + "ALT-", + "BRA-", + "DBY2-", + "STG-", + "MAO-", + "LEM-", + "GOA-", + "FOA-", + "MNA-", + "LNA-", +]); diff --git a/src/utils/mobileOSDetectAsync.utils.ts b/src/utils/mobileOSDetectAsync.utils.ts new file mode 100644 index 0000000..19e7055 --- /dev/null +++ b/src/utils/mobileOSDetectAsync.utils.ts @@ -0,0 +1,95 @@ +import UAParser from "ua-parser-js"; +import { huaweiDevicesRegex, validCodes } from "../constants/mobile-devices.constants"; + +/** + * This file contains utility functions and types for detecting the mobile operating system. + * It uses the User-Agent string and the User-Agent Client Hints API to determine the OS. + */ + +declare global { + interface Window { + MSStream?: { + msClose: () => void; + msDetachStream: () => void; + readonly type: string; + }; + opera?: string; + } + interface Navigator { + userAgentData?: NavigatorUAData; + } +} + +/** + * Type representing the User-Agent Client Hints API. + */ +type NavigatorUAData = { + brands: { brand: string; version: string }[]; + getHighEntropyValues(hints: string[]): Promise; + mobile: boolean; +}; + +/** + * Type representing the high entropy values that can be obtained from the User-Agent Client Hints API. + */ +type HighEntropyValues = { + model?: string; + platform?: string; + platformVersion?: string; + uaFullVersion?: string; +}; + +/** + * This function validates Huawei device codes from a string. + * It checks if the input string contains any of the valid Huawei device codes. + * + * @param {string} inputString - The string to check for Huawei device codes. + * @returns {boolean} Returns true if the input string contains a valid Huawei device code, false otherwise. + */ +function validateHuaweiCodes(inputString: string) { + const matches = inputString.match(huaweiDevicesRegex); + if (matches) { + return matches.filter((code) => validCodes.has(code.toUpperCase())).length > 0; + } + return false; +} + +/** + * This function detects the mobile operating system asynchronously. + * It uses the User-Agent string and the User-Agent Client Hints API to determine the OS. + * + * @returns {Promise} Returns a promise that resolves to the name of the detected mobile OS. + */ +export const mobileOSDetectAsync = async () => { + const userAgent = navigator.userAgent ?? window.opera ?? ""; + // Windows Phone must come first because its UA also contains "Android" + if (/windows phone/i.test(userAgent)) { + return "Windows Phone"; + } + + if (/android/i.test(userAgent)) { + // Check if navigator.userAgentData is available for modern browsers + if (navigator?.userAgentData) { + const ua = await navigator.userAgentData.getHighEntropyValues(["model"]); + if (validateHuaweiCodes(ua?.model || "")) { + return "huawei"; + } + } else if (validateHuaweiCodes(userAgent) || /huawei/i.test(userAgent)) { + return "huawei"; + } + return "Android"; + } + + if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { + return "iOS"; + } + + return "unknown"; +}; + +/** + * This function uses the UAParser library to get the name of the operating system. + * + * @returns {string} Returns the name of the operating system. + */ +export const getOSNameWithUAParser = () => UAParser().os.name; diff --git a/utils-docs/docs/Constants/mobile-devices.constants.md b/utils-docs/docs/Constants/mobile-devices.constants.md new file mode 100644 index 0000000..e69de29 diff --git a/utils-docs/docs/Utils/mobileOSDetectAsync.md b/utils-docs/docs/Utils/mobileOSDetectAsync.md new file mode 100644 index 0000000..eb19259 --- /dev/null +++ b/utils-docs/docs/Utils/mobileOSDetectAsync.md @@ -0,0 +1,30 @@ +# User Agent Parser Utility Documentation + +This utility module provides functions to detect mobile operating systems and extract information from user agent strings. + +## `mobileOSDetectAsync` + +This function asynchronously detects the mobile operating system based on the user agent string. + +### Returns + +- `"Windows Phone"` if the user agent string indicates a Windows Phone device. +- `"huawei"` if the user agent string indicates a Huawei device running Android. +- `"Android"` if the user agent string indicates an Android device. +- `"iOS"` if the user agent string indicates an iOS device (iPad, iPhone, or iPod). +- `"unknown"` if the mobile operating system cannot be determined. + +### Usage + +```typescript +import { mobileOSDetectAsync } from "@deriv-com/utils"; + +const os = await mobileOSDetectAsync(); + +if (os === "iOS") { + console.log("client on iOS"); +} else if (os === "huawei") { + console.log("client on huawei"); +} +console.log("client on android"); +``` From 46c28a626cfb680afebca7a669a102489065b8d9 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Wed, 15 May 2024 16:55:56 +0800 Subject: [PATCH 02/17] chore: export the utils from index.ts --- src/constants/index.ts | 2 ++ src/utils/index.ts | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/constants/index.ts b/src/constants/index.ts index 9751b23..7782efc 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -5,6 +5,7 @@ import * as LocalStorageConstants from "./localstorage.constants"; import * as URLConstants from "./url.constants"; import * as ValidationConstants from "./validation.constants"; import * as BrandConstants from "./brand.constants"; +import * as MobileDevicesConstants from "./mobile-devices.constants"; export { AppIDConstants, @@ -14,4 +15,5 @@ export { URLConstants, ValidationConstants, BrandConstants, + MobileDevicesConstants, }; diff --git a/src/utils/index.ts b/src/utils/index.ts index 19c337e..eba70f6 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -6,5 +6,16 @@ import * as PromiseUtils from "./promise.utils"; import * as URLUtils from "./url.utils"; import * as WebSocketUtils from "./websocket.utils"; import * as BrandUtils from "./brand.utils"; +import * as mobileOSDetectAsync from "./mobileOSDetectAsync.utils"; -export { ImageUtils, FormatUtils, LocalStorageUtils, ObjectUtils, PromiseUtils, URLUtils, WebSocketUtils, BrandUtils }; +export { + ImageUtils, + FormatUtils, + LocalStorageUtils, + ObjectUtils, + PromiseUtils, + URLUtils, + WebSocketUtils, + BrandUtils, + mobileOSDetectAsync, +}; From 74301988a12e322c17d53a2b18928f19bab85f99 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Wed, 15 May 2024 17:06:23 +0800 Subject: [PATCH 03/17] chore: tests --- package-lock.json | 25 +++++++++++ package.json | 3 ++ .../mobileOSDetectAsync.utils.spec.ts | 44 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/utils/__test__/mobileOSDetectAsync.utils.spec.ts diff --git a/package-lock.json b/package-lock.json index 339a28b..d00687e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "@deriv-com/utils", "version": "0.0.11", + "dependencies": { + "ua-parser-js": "^1.0.37" + }, "devDependencies": { "@commitlint/cli": "^19.2.1", "@commitlint/config-conventional": "^19.1.0", @@ -11629,6 +11632,28 @@ "node": ">=14.17" } }, + "node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "engines": { + "node": "*" + } + }, "node_modules/ufo": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", diff --git a/package.json b/package.json index 1b69031..92afda5 100644 --- a/package.json +++ b/package.json @@ -69,5 +69,8 @@ "vite": "^5.1.2", "vite-plugin-dts": "^3.7.2", "vitest": "^1.2.2" + }, + "dependencies": { + "ua-parser-js": "^1.0.37" } } diff --git a/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts b/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts new file mode 100644 index 0000000..7b700b4 --- /dev/null +++ b/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts @@ -0,0 +1,44 @@ +import { mobileOSDetectAsync, getOSNameWithUAParser } from "../mobileOSDetectAsync.utils"; +import UAParser from "ua-parser-js"; + +describe("mobileOSDetectAsync", () => { + test('should return "Windows Phone" for Windows Phone user agent', async () => { + jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("windows phone"); + expect(await mobileOSDetectAsync()).toBe("Windows Phone"); + }); + + test('should return "Android" for Android user agent', async () => { + jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("android"); + expect(await mobileOSDetectAsync()).toBe("Android"); + }); + + test('should return "iOS" for iOS user agent', async () => { + jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("iPhone"); + jest.spyOn(window, "MSStream", "get").mockReturnValue({ + msClose: () => {}, + msDetachStream: () => {}, + type: "someType", + }); + expect(await mobileOSDetectAsync()).toBe("iOS"); + }); + + test('should return "iOS" for iOS user agent', async () => { + jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("iPhone"); + jest.spyOn(window, "MSStream", "get").mockReturnValue({ + msClose: () => {}, + msDetachStream: () => {}, + type: "someType", + }); + expect(await mobileOSDetectAsync()).toBe("iOS"); + }); + + test('should return "unknown" for unknown user agent', async () => { + jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("unknown"); + expect(await mobileOSDetectAsync()).toBe("unknown"); + }); + + test("should return the OS name", () => { + jest.spyOn(UAParser, "OS", "get").mockReturnValue({ NAME: "name", VERSION: "version" }); + expect(getOSNameWithUAParser()).toBe("name"); + }); +}); From f49021e084afeeb596fcc0ee92fd487e472e2ad3 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Wed, 15 May 2024 17:15:05 +0800 Subject: [PATCH 04/17] chore: small nit --- src/utils/index.ts | 4 ++-- src/utils/mobileOSDetectAsync.utils.ts | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/utils/index.ts b/src/utils/index.ts index eba70f6..46a9d90 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -6,7 +6,7 @@ import * as PromiseUtils from "./promise.utils"; import * as URLUtils from "./url.utils"; import * as WebSocketUtils from "./websocket.utils"; import * as BrandUtils from "./brand.utils"; -import * as mobileOSDetectAsync from "./mobileOSDetectAsync.utils"; +import * as MobileOSDetectAsync from "./mobileOSDetectAsync.utils"; export { ImageUtils, @@ -17,5 +17,5 @@ export { URLUtils, WebSocketUtils, BrandUtils, - mobileOSDetectAsync, + MobileOSDetectAsync, }; diff --git a/src/utils/mobileOSDetectAsync.utils.ts b/src/utils/mobileOSDetectAsync.utils.ts index 19e7055..4743fff 100644 --- a/src/utils/mobileOSDetectAsync.utils.ts +++ b/src/utils/mobileOSDetectAsync.utils.ts @@ -40,7 +40,6 @@ type HighEntropyValues = { }; /** - * This function validates Huawei device codes from a string. * It checks if the input string contains any of the valid Huawei device codes. * * @param {string} inputString - The string to check for Huawei device codes. @@ -55,8 +54,7 @@ function validateHuaweiCodes(inputString: string) { } /** - * This function detects the mobile operating system asynchronously. - * It uses the User-Agent string and the User-Agent Client Hints API to determine the OS. + * It uses the User-Agent string and the User-Agent Client Hints API to detects the mobile operating system asynchronously. * * @returns {Promise} Returns a promise that resolves to the name of the detected mobile OS. */ From 88d871a0227b869910cc229917ceaedc2f4d9b7e Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Wed, 15 May 2024 17:43:52 +0800 Subject: [PATCH 05/17] fix: jest -> vitest --- .../mobileOSDetectAsync.utils.spec.ts | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts b/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts index 7b700b4..9d1ce11 100644 --- a/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts +++ b/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts @@ -1,44 +1,36 @@ -import { mobileOSDetectAsync, getOSNameWithUAParser } from "../mobileOSDetectAsync.utils"; -import UAParser from "ua-parser-js"; +import { describe, expect, test } from "vitest"; +import { mobileOSDetectAsync } from "../mobileOSDetectAsync.utils"; describe("mobileOSDetectAsync", () => { test('should return "Windows Phone" for Windows Phone user agent', async () => { - jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("windows phone"); + Object.defineProperty(window.navigator, "userAgent", { + value: "windows phone", + configurable: true, + }); expect(await mobileOSDetectAsync()).toBe("Windows Phone"); }); test('should return "Android" for Android user agent', async () => { - jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("android"); - expect(await mobileOSDetectAsync()).toBe("Android"); - }); - - test('should return "iOS" for iOS user agent', async () => { - jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("iPhone"); - jest.spyOn(window, "MSStream", "get").mockReturnValue({ - msClose: () => {}, - msDetachStream: () => {}, - type: "someType", + Object.defineProperty(window.navigator, "userAgent", { + value: "android", + configurable: true, }); - expect(await mobileOSDetectAsync()).toBe("iOS"); + expect(await mobileOSDetectAsync()).toBe("Android"); }); test('should return "iOS" for iOS user agent', async () => { - jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("iPhone"); - jest.spyOn(window, "MSStream", "get").mockReturnValue({ - msClose: () => {}, - msDetachStream: () => {}, - type: "someType", + Object.defineProperty(window.navigator, "userAgent", { + value: "iPhone", + configurable: true, }); expect(await mobileOSDetectAsync()).toBe("iOS"); }); test('should return "unknown" for unknown user agent', async () => { - jest.spyOn(window.navigator, "userAgent", "get").mockReturnValue("unknown"); + Object.defineProperty(window.navigator, "userAgent", { + value: "unknown", + configurable: true, + }); expect(await mobileOSDetectAsync()).toBe("unknown"); }); - - test("should return the OS name", () => { - jest.spyOn(UAParser, "OS", "get").mockReturnValue({ NAME: "name", VERSION: "version" }); - expect(getOSNameWithUAParser()).toBe("name"); - }); }); From 12f53b4ab5b9e6b0106807c5259fb35b4f1884ef Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Wed, 15 May 2024 17:59:11 +0800 Subject: [PATCH 06/17] chore: empty commit From a6f8e751a2139803264c6387f9fc267df2f2cdb9 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Wed, 15 May 2024 18:11:33 +0800 Subject: [PATCH 07/17] chore: mobile-devices constants md --- .../Constants/mobile-devices.constants.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/utils-docs/docs/Constants/mobile-devices.constants.md b/utils-docs/docs/Constants/mobile-devices.constants.md index e69de29..7234779 100644 --- a/utils-docs/docs/Constants/mobile-devices.constants.md +++ b/utils-docs/docs/Constants/mobile-devices.constants.md @@ -0,0 +1,31 @@ +--- +sidebar_position: 2 +--- + +# mobile-devices + +This utility module provides a regular expression and a set of valid codes to detect and validate Huawei device codes in a string. + +### `huaweiDevicesRegex` + +This regex matches standalone sequences of three uppercase letters followed by a hyphen, using global and case-insensitive search. + +```typescript +import { huaweiDevicesRegex } from "@deriv-com/utils"; + +const isValid = huaweiDevicesRegex.test("ALP-"); // returns true +``` + +### `validCodes` + +This is a set of valid Huawei device codes. It can be used to check if a detected code is a valid Huawei device code. + +```typescript +import { validCodes } from "@deriv-com/utils"; + +const isValidCode = validCodes.has("ALP-"); // returns true +``` + +#### Note + +These utilities can be used in conjunction with the `mobileOSDetectAsync` function to detect if a user is on a Huawei device running Android. If `mobileOSDetectAsync` returns "huawei", you can use `huaweiDevicesRegex` and `validCodes` to further validate the device code. From ec714a2025466796fe742049edc502190bf4dc1b Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Thu, 16 May 2024 10:30:27 +0800 Subject: [PATCH 08/17] refactor: moved placement of regex description --- src/constants/mobile-devices.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/mobile-devices.constants.ts b/src/constants/mobile-devices.constants.ts index 6bbffd8..66ce1bf 100644 --- a/src/constants/mobile-devices.constants.ts +++ b/src/constants/mobile-devices.constants.ts @@ -1,12 +1,12 @@ /** * @description + * @regex /\b([A-Z]{3}-)\b/gi * This pattern matches any string of three uppercase letters followed by a hyphen. * @example huaweiDevicesRegex.test("ALP-") // returns true */ export const huaweiDevicesRegex = /\b([A-Z]{3}-)\b/gi; /** - * @regex /\b([A-Z]{3}-)\b/gi * @description * This pattern matches any string that contains a sequence of three uppercase letters followed by a hyphen. * The sequence must be a word on its own (i.e., it must be surrounded by word boundaries). From b5b973d4ea78397dcc8b7d6eafe583b474a78c14 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Thu, 16 May 2024 12:02:46 +0800 Subject: [PATCH 09/17] refactor: use regex for validCodes instead of const arr of str --- src/constants/mobile-devices.constants.ts | 114 +--------------------- src/utils/mobileOSDetectAsync.utils.ts | 3 +- 2 files changed, 5 insertions(+), 112 deletions(-) diff --git a/src/constants/mobile-devices.constants.ts b/src/constants/mobile-devices.constants.ts index 66ce1bf..5a9504f 100644 --- a/src/constants/mobile-devices.constants.ts +++ b/src/constants/mobile-devices.constants.ts @@ -14,115 +14,7 @@ export const huaweiDevicesRegex = /\b([A-Z]{3}-)\b/gi; * @example huaweiDevicesRegex.test("AMN-") // returns true * @example huaweiDevicesRegex.test("ANA-") // returns true * @example huaweiDevicesRegex.test("ANE-") // returns true + * Source of list is from: https://gist.github.com/megaacheyounes/e1c7eec5c790e577db602381b8c50bfa */ -export const validCodes = new Set([ - "ALP-", - "AMN-", - "ANA-", - "ANE-", - "ANG-", - "AQM-", - "ARS-", - "ART-", - "ATU-", - "BAC-", - "BLA-", - "BRQ-", - "CAG-", - "CAM-", - "CAN-", - "CAZ-", - "CDL-", - "CDY-", - "CLT-", - "CRO-", - "CUN-", - "DIG-", - "DRA-", - "DUA-", - "DUB-", - "DVC-", - "ELE-", - "ELS-", - "EML-", - "EVA-", - "EVR-", - "FIG-", - "FLA-", - "FRL-", - "GLK-", - "HMA-", - "HW-", - "HWI-", - "INE-", - "JAT-", - "JEF-", - "JER-", - "JKM-", - "JNY-", - "JSC-", - "LDN-", - "LIO-", - "LON-", - "LUA-", - "LYA-", - "LYO-", - "MAR-", - "MED-", - "MHA-", - "MLA-", - "MRD-", - "MYA-", - "NCE-", - "NEO-", - "NOH-", - "NOP-", - "OCE-", - "PAR-", - "PIC-", - "POT-", - "PPA-", - "PRA-", - "RNE-", - "SEA-", - "SLA-", - "SNE-", - "SPN-", - "STK-", - "TAH-", - "TAS-", - "TET-", - "TRT-", - "VCE-", - "VIE-", - "VKY-", - "VNS-", - "VOG-", - "VTR-", - "WAS-", - "WKG-", - "WLZ-", - "JAD-", - "MLD-", - "RTE-", - "NAM-", - "NEN-", - "BAL-", - "JLN-", - "YAL-", - "MGA-", - "FGD-", - "XYAO-", - "BON-", - "ALN-", - "ALT-", - "BRA-", - "DBY2-", - "STG-", - "MAO-", - "LEM-", - "GOA-", - "FOA-", - "MNA-", - "LNA-", -]); +export const validCodes = + /(ALP-|AMN-|ANA-|ANE-|ANG-|AQM-|ARS-|ART-|ATU-|BAC-|BLA-|BRQ-|CAG-|CAM-|CAN-|CAZ-|CDL-|CDY-|CLT-|CRO-|CUN-|DIG-|DRA-|DUA-|DUB-|DVC-|ELE-|ELS-|EML-|EVA-|EVR-|FIG-|FLA-|FRL-|GLK-|HMA-|HW-|HWI-|INE-|JAT-|JEF-|JER-|JKM-|JNY-|JSC-|LDN-|LIO-|LON-|LUA-|LYA-|LYO-|MAR-|MED-|MHA-|MLA-|MRD-|MYA-|NCE-|NEO-|NOH-|NOP-|OCE-|PAR-|PIC-|POT-|PPA-|PRA-|RNE-|SEA-|SLA-|SNE-|SPN-|STK-|TAH-|TAS-|TET-|TRT-|VCE-|VIE-|VKY-|VNS-|VOG-|VTR-|WAS-|WKG-|WLZ-|JAD-|MLD-|RTE-|NAM-|NEN-|BAL-|JLN-|YAL-|MGA-|FGD-|XYAO-|BON-|ALN-|ALT-|BRA-|DBY2-|STG-|MAO-|LEM-|GOA-|FOA-|MNA-|LNA-)/; diff --git a/src/utils/mobileOSDetectAsync.utils.ts b/src/utils/mobileOSDetectAsync.utils.ts index 4743fff..3f3f8db 100644 --- a/src/utils/mobileOSDetectAsync.utils.ts +++ b/src/utils/mobileOSDetectAsync.utils.ts @@ -48,7 +48,7 @@ type HighEntropyValues = { function validateHuaweiCodes(inputString: string) { const matches = inputString.match(huaweiDevicesRegex); if (matches) { - return matches.filter((code) => validCodes.has(code.toUpperCase())).length > 0; + return matches.filter((code) => validCodes.test(code.toUpperCase())).length > 0; } return false; } @@ -67,6 +67,7 @@ export const mobileOSDetectAsync = async () => { if (/android/i.test(userAgent)) { // Check if navigator.userAgentData is available for modern browsers + // userAgent only returns a string, while userAgentData returns an object with more detailed information if (navigator?.userAgentData) { const ua = await navigator.userAgentData.getHighEntropyValues(["model"]); if (validateHuaweiCodes(ua?.model || "")) { From 20a47b733311d620cde1d146ff9ac2a5122b1f5d Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Thu, 16 May 2024 12:03:33 +0800 Subject: [PATCH 10/17] refactor: validCodes is redundant of huaweiDevicesRegex --- src/constants/mobile-devices.constants.ts | 10 +--------- src/utils/mobileOSDetectAsync.utils.ts | 8 ++------ 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/constants/mobile-devices.constants.ts b/src/constants/mobile-devices.constants.ts index 5a9504f..7ab3a8a 100644 --- a/src/constants/mobile-devices.constants.ts +++ b/src/constants/mobile-devices.constants.ts @@ -1,11 +1,3 @@ -/** - * @description - * @regex /\b([A-Z]{3}-)\b/gi - * This pattern matches any string of three uppercase letters followed by a hyphen. - * @example huaweiDevicesRegex.test("ALP-") // returns true - */ -export const huaweiDevicesRegex = /\b([A-Z]{3}-)\b/gi; - /** * @description * This pattern matches any string that contains a sequence of three uppercase letters followed by a hyphen. @@ -16,5 +8,5 @@ export const huaweiDevicesRegex = /\b([A-Z]{3}-)\b/gi; * @example huaweiDevicesRegex.test("ANE-") // returns true * Source of list is from: https://gist.github.com/megaacheyounes/e1c7eec5c790e577db602381b8c50bfa */ -export const validCodes = +export const huaweiDevicesRegex = /(ALP-|AMN-|ANA-|ANE-|ANG-|AQM-|ARS-|ART-|ATU-|BAC-|BLA-|BRQ-|CAG-|CAM-|CAN-|CAZ-|CDL-|CDY-|CLT-|CRO-|CUN-|DIG-|DRA-|DUA-|DUB-|DVC-|ELE-|ELS-|EML-|EVA-|EVR-|FIG-|FLA-|FRL-|GLK-|HMA-|HW-|HWI-|INE-|JAT-|JEF-|JER-|JKM-|JNY-|JSC-|LDN-|LIO-|LON-|LUA-|LYA-|LYO-|MAR-|MED-|MHA-|MLA-|MRD-|MYA-|NCE-|NEO-|NOH-|NOP-|OCE-|PAR-|PIC-|POT-|PPA-|PRA-|RNE-|SEA-|SLA-|SNE-|SPN-|STK-|TAH-|TAS-|TET-|TRT-|VCE-|VIE-|VKY-|VNS-|VOG-|VTR-|WAS-|WKG-|WLZ-|JAD-|MLD-|RTE-|NAM-|NEN-|BAL-|JLN-|YAL-|MGA-|FGD-|XYAO-|BON-|ALN-|ALT-|BRA-|DBY2-|STG-|MAO-|LEM-|GOA-|FOA-|MNA-|LNA-)/; diff --git a/src/utils/mobileOSDetectAsync.utils.ts b/src/utils/mobileOSDetectAsync.utils.ts index 3f3f8db..a118a12 100644 --- a/src/utils/mobileOSDetectAsync.utils.ts +++ b/src/utils/mobileOSDetectAsync.utils.ts @@ -1,5 +1,5 @@ import UAParser from "ua-parser-js"; -import { huaweiDevicesRegex, validCodes } from "../constants/mobile-devices.constants"; +import { huaweiDevicesRegex } from "../constants/mobile-devices.constants"; /** * This file contains utility functions and types for detecting the mobile operating system. @@ -46,11 +46,7 @@ type HighEntropyValues = { * @returns {boolean} Returns true if the input string contains a valid Huawei device code, false otherwise. */ function validateHuaweiCodes(inputString: string) { - const matches = inputString.match(huaweiDevicesRegex); - if (matches) { - return matches.filter((code) => validCodes.test(code.toUpperCase())).length > 0; - } - return false; + return huaweiDevicesRegex.test(inputString); } /** From 3ac474e4b99216f5a54ad9a4a5ec512c4166c8f4 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Thu, 16 May 2024 13:11:57 +0800 Subject: [PATCH 11/17] chore: naming convention --- src/utils/__test__/mobileOSDetectAsync.utils.spec.ts | 2 +- src/utils/index.ts | 2 +- ...mobileOSDetectAsync.utils.ts => mobile-os-detect.utils.ts} | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/utils/{mobileOSDetectAsync.utils.ts => mobile-os-detect.utils.ts} (98%) diff --git a/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts b/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts index 9d1ce11..bc3de21 100644 --- a/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts +++ b/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vitest"; -import { mobileOSDetectAsync } from "../mobileOSDetectAsync.utils"; +import { mobileOSDetectAsync } from "../mobile-os-detect.utils"; describe("mobileOSDetectAsync", () => { test('should return "Windows Phone" for Windows Phone user agent', async () => { diff --git a/src/utils/index.ts b/src/utils/index.ts index 46a9d90..a25c79b 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -6,7 +6,7 @@ import * as PromiseUtils from "./promise.utils"; import * as URLUtils from "./url.utils"; import * as WebSocketUtils from "./websocket.utils"; import * as BrandUtils from "./brand.utils"; -import * as MobileOSDetectAsync from "./mobileOSDetectAsync.utils"; +import * as MobileOSDetectAsync from "./mobile-os-detect.utils"; export { ImageUtils, diff --git a/src/utils/mobileOSDetectAsync.utils.ts b/src/utils/mobile-os-detect.utils.ts similarity index 98% rename from src/utils/mobileOSDetectAsync.utils.ts rename to src/utils/mobile-os-detect.utils.ts index a118a12..d2f4e63 100644 --- a/src/utils/mobileOSDetectAsync.utils.ts +++ b/src/utils/mobile-os-detect.utils.ts @@ -45,9 +45,9 @@ type HighEntropyValues = { * @param {string} inputString - The string to check for Huawei device codes. * @returns {boolean} Returns true if the input string contains a valid Huawei device code, false otherwise. */ -function validateHuaweiCodes(inputString: string) { +const validateHuaweiCodes = (inputString: string) => { return huaweiDevicesRegex.test(inputString); -} +}; /** * It uses the User-Agent string and the User-Agent Client Hints API to detects the mobile operating system asynchronously. From 3024241d99f7413bb8e05d43a7c9a05667e2380c Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Thu, 16 May 2024 14:09:56 +0800 Subject: [PATCH 12/17] Refactor: Replace global interface type definitions with local extended types in mobile OS detection utility --- src/utils/mobile-os-detect.utils.ts | 40 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/utils/mobile-os-detect.utils.ts b/src/utils/mobile-os-detect.utils.ts index d2f4e63..90b4c70 100644 --- a/src/utils/mobile-os-detect.utils.ts +++ b/src/utils/mobile-os-detect.utils.ts @@ -6,19 +6,21 @@ import { huaweiDevicesRegex } from "../constants/mobile-devices.constants"; * It uses the User-Agent string and the User-Agent Client Hints API to determine the OS. */ -declare global { - interface Window { - MSStream?: { - msClose: () => void; - msDetachStream: () => void; - readonly type: string; - }; - opera?: string; - } - interface Navigator { - userAgentData?: NavigatorUAData; - } -} +type ExtendedWindow = Window & { + // MSStream is specific to IE and Edge browsers + MSStream?: { + msClose: () => void; + msDetachStream: () => void; + readonly type: string; + }; + // opera is specific to Opera browser + opera?: string; +}; + +type ExtendedNavigator = Navigator & { + // userAgentData is part of the User-Agent Client Hints API + userAgentData?: NavigatorUAData; +}; /** * Type representing the User-Agent Client Hints API. @@ -55,7 +57,11 @@ const validateHuaweiCodes = (inputString: string) => { * @returns {Promise} Returns a promise that resolves to the name of the detected mobile OS. */ export const mobileOSDetectAsync = async () => { - const userAgent = navigator.userAgent ?? window.opera ?? ""; + const extendedWindow = window as ExtendedWindow; + const extendedNavigator = navigator as ExtendedNavigator; + + const userAgent = extendedNavigator.userAgent ?? extendedWindow.opera ?? ""; + // Windows Phone must come first because its UA also contains "Android" if (/windows phone/i.test(userAgent)) { return "Windows Phone"; @@ -64,8 +70,8 @@ export const mobileOSDetectAsync = async () => { if (/android/i.test(userAgent)) { // Check if navigator.userAgentData is available for modern browsers // userAgent only returns a string, while userAgentData returns an object with more detailed information - if (navigator?.userAgentData) { - const ua = await navigator.userAgentData.getHighEntropyValues(["model"]); + if (extendedNavigator.userAgentData) { + const ua = await extendedNavigator.userAgentData.getHighEntropyValues(["model"]); if (validateHuaweiCodes(ua?.model || "")) { return "huawei"; } @@ -75,7 +81,7 @@ export const mobileOSDetectAsync = async () => { return "Android"; } - if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { + if (/iPad|iPhone|iPod/.test(userAgent) && !extendedWindow.MSStream) { return "iOS"; } From 37614a25de846cf36add97f4b5b138800bf4456c Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Thu, 16 May 2024 14:13:35 +0800 Subject: [PATCH 13/17] chore: rename md file --- .../docs/Utils/{mobileOSDetectAsync.md => mobile-os-detect.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename utils-docs/docs/Utils/{mobileOSDetectAsync.md => mobile-os-detect.md} (100%) diff --git a/utils-docs/docs/Utils/mobileOSDetectAsync.md b/utils-docs/docs/Utils/mobile-os-detect.md similarity index 100% rename from utils-docs/docs/Utils/mobileOSDetectAsync.md rename to utils-docs/docs/Utils/mobile-os-detect.md From 9a4939727bcdd6eb28d4be0fe282e8383da36ad4 Mon Sep 17 00:00:00 2001 From: shontzu <108507236+shontzu-deriv@users.noreply.github.com> Date: Fri, 17 May 2024 13:08:12 +0800 Subject: [PATCH 14/17] Apply suggestions from code review chore: readme heading format Co-authored-by: Niloofar Sadeghi <93518187+niloofar-deriv@users.noreply.github.com> --- utils-docs/docs/Utils/mobile-os-detect.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/utils-docs/docs/Utils/mobile-os-detect.md b/utils-docs/docs/Utils/mobile-os-detect.md index eb19259..7041a60 100644 --- a/utils-docs/docs/Utils/mobile-os-detect.md +++ b/utils-docs/docs/Utils/mobile-os-detect.md @@ -1,12 +1,16 @@ -# User Agent Parser Utility Documentation +--- +sidebar_position: 2 +--- + +# Mobile OS Detect This utility module provides functions to detect mobile operating systems and extract information from user agent strings. -## `mobileOSDetectAsync` +### `mobileOSDetectAsync` This function asynchronously detects the mobile operating system based on the user agent string. -### Returns +#### Returns - `"Windows Phone"` if the user agent string indicates a Windows Phone device. - `"huawei"` if the user agent string indicates a Huawei device running Android. @@ -14,7 +18,7 @@ This function asynchronously detects the mobile operating system based on the us - `"iOS"` if the user agent string indicates an iOS device (iPad, iPhone, or iPod). - `"unknown"` if the mobile operating system cannot be determined. -### Usage +#### Usage ```typescript import { mobileOSDetectAsync } from "@deriv-com/utils"; From 55130c1c11bb29833260fe0df7dca1b644be8008 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Fri, 17 May 2024 16:08:57 +0800 Subject: [PATCH 15/17] revert: uninstall ua-parser-js --- package-lock.json | 32 ------------------- package.json | 4 --- src/utils/mobile-os-detect.utils.ts | 8 ----- ...devices.constants.md => mobile-devices.md} | 0 4 files changed, 44 deletions(-) rename utils-docs/docs/Constants/{mobile-devices.constants.md => mobile-devices.md} (100%) diff --git a/package-lock.json b/package-lock.json index d00687e..354dc09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,6 @@ "": { "name": "@deriv-com/utils", "version": "0.0.11", - "dependencies": { - "ua-parser-js": "^1.0.37" - }, "devDependencies": { "@commitlint/cli": "^19.2.1", "@commitlint/config-conventional": "^19.1.0", @@ -18,7 +15,6 @@ "@semantic-release/npm": "^12.0.0", "@semantic-release/release-notes-generator": "^13.0.0", "@types/node": "^20.11.18", - "@types/ua-parser-js": "^0.7.39", "@typescript-eslint/eslint-plugin": "^6.21.0", "@vitest/coverage-istanbul": "^1.2.2", "@vitest/coverage-v8": "^1.2.2", @@ -2619,12 +2615,6 @@ "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, - "node_modules/@types/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", - "dev": true - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -11632,28 +11622,6 @@ "node": ">=14.17" } }, - "node_modules/ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "engines": { - "node": "*" - } - }, "node_modules/ufo": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", diff --git a/package.json b/package.json index 92afda5..7de18aa 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,6 @@ "@semantic-release/npm": "^12.0.0", "@semantic-release/release-notes-generator": "^13.0.0", "@types/node": "^20.11.18", - "@types/ua-parser-js": "^0.7.39", "@typescript-eslint/eslint-plugin": "^6.21.0", "@vitest/coverage-istanbul": "^1.2.2", "@vitest/coverage-v8": "^1.2.2", @@ -69,8 +68,5 @@ "vite": "^5.1.2", "vite-plugin-dts": "^3.7.2", "vitest": "^1.2.2" - }, - "dependencies": { - "ua-parser-js": "^1.0.37" } } diff --git a/src/utils/mobile-os-detect.utils.ts b/src/utils/mobile-os-detect.utils.ts index 90b4c70..446632c 100644 --- a/src/utils/mobile-os-detect.utils.ts +++ b/src/utils/mobile-os-detect.utils.ts @@ -1,4 +1,3 @@ -import UAParser from "ua-parser-js"; import { huaweiDevicesRegex } from "../constants/mobile-devices.constants"; /** @@ -87,10 +86,3 @@ export const mobileOSDetectAsync = async () => { return "unknown"; }; - -/** - * This function uses the UAParser library to get the name of the operating system. - * - * @returns {string} Returns the name of the operating system. - */ -export const getOSNameWithUAParser = () => UAParser().os.name; diff --git a/utils-docs/docs/Constants/mobile-devices.constants.md b/utils-docs/docs/Constants/mobile-devices.md similarity index 100% rename from utils-docs/docs/Constants/mobile-devices.constants.md rename to utils-docs/docs/Constants/mobile-devices.md From e2e3c76fb334a477adb0015e20ce4e12fea799f8 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Tue, 21 May 2024 12:09:54 +0800 Subject: [PATCH 16/17] =?UTF-8?q?chore:=20small=20nit=20=F0=9F=A4=8F=20=20?= =?UTF-8?q?=20-=20rename=20mobile-os-detect=20to=20os-detect=20=20=20-=20n?= =?UTF-8?q?amepsace=20groups=20the=20exported=20function=20with=20a=20func?= =?UTF-8?q?tional=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/index.ts | 4 ++-- src/utils/{mobile-os-detect.utils.ts => os-detect.utils.ts} | 0 utils-docs/docs/Utils/{mobile-os-detect.md => os-detect.md} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/utils/{mobile-os-detect.utils.ts => os-detect.utils.ts} (100%) rename utils-docs/docs/Utils/{mobile-os-detect.md => os-detect.md} (98%) diff --git a/src/utils/index.ts b/src/utils/index.ts index a25c79b..6c6e9db 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -6,7 +6,7 @@ import * as PromiseUtils from "./promise.utils"; import * as URLUtils from "./url.utils"; import * as WebSocketUtils from "./websocket.utils"; import * as BrandUtils from "./brand.utils"; -import * as MobileOSDetectAsync from "./mobile-os-detect.utils"; +import * as OSDetectionUtils from "./os-detect.utils"; export { ImageUtils, @@ -17,5 +17,5 @@ export { URLUtils, WebSocketUtils, BrandUtils, - MobileOSDetectAsync, + OSDetectionUtils, }; diff --git a/src/utils/mobile-os-detect.utils.ts b/src/utils/os-detect.utils.ts similarity index 100% rename from src/utils/mobile-os-detect.utils.ts rename to src/utils/os-detect.utils.ts diff --git a/utils-docs/docs/Utils/mobile-os-detect.md b/utils-docs/docs/Utils/os-detect.md similarity index 98% rename from utils-docs/docs/Utils/mobile-os-detect.md rename to utils-docs/docs/Utils/os-detect.md index 7041a60..c9a5a3c 100644 --- a/utils-docs/docs/Utils/mobile-os-detect.md +++ b/utils-docs/docs/Utils/os-detect.md @@ -2,7 +2,7 @@ sidebar_position: 2 --- -# Mobile OS Detect +# os-detect This utility module provides functions to detect mobile operating systems and extract information from user agent strings. From 7abd37b812b6a3e64445eb99f7cab804ea9c06d6 Mon Sep 17 00:00:00 2001 From: shontzu-deriv Date: Tue, 21 May 2024 12:38:42 +0800 Subject: [PATCH 17/17] chore: update import in test file --- src/utils/__test__/mobileOSDetectAsync.utils.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts b/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts index bc3de21..dc5562c 100644 --- a/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts +++ b/src/utils/__test__/mobileOSDetectAsync.utils.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "vitest"; -import { mobileOSDetectAsync } from "../mobile-os-detect.utils"; +import { mobileOSDetectAsync } from "../os-detect.utils"; describe("mobileOSDetectAsync", () => { test('should return "Windows Phone" for Windows Phone user agent', async () => {