diff --git a/.eslintrc b/.eslintrc index 365fc121..c9af6d19 100644 --- a/.eslintrc +++ b/.eslintrc @@ -80,7 +80,7 @@ "message": "Don't declare const enum, because it is not supported by Babel used for building RN SDK" } ], - "compat/compat": ["error", "defaults, ie 10, node 6"], + "compat/compat": ["error", "defaults, node >=14"], "no-throw-literal": "error", "import/no-default-export": "error", "import/no-self-import": "error" diff --git a/CHANGES.txt b/CHANGES.txt index 6acceb03..84260af8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,7 +7,8 @@ - Removed `/mySegments` endpoint from SplitAPI module, as it is replaced by `/memberships` endpoint. - Removed support for MY_SEGMENTS_UPDATE and MY_SEGMENTS_UPDATE_V2 notification types, as they are replaced by MEMBERSHIPS_MS_UPDATE and MEMBERSHIPS_LS_UPDATE notification types. - Removed the deprecated `GOOGLE_ANALYTICS_TO_SPLIT` and `SPLIT_TO_GOOGLE_ANALYTICS` integrations. - - Bugfixing - Fixed an issue with the client `ready` method that was causing the returned promise to hang on async/await syntax if the promise was rejected. The fix implies a breaking change, since the promise rejection must be handled by the user. + - Removed internal ponyfills for `Map`, `Set` and `Array.from` global objects, dropping support for IE and other outdated browsers. The SDK now requires the runtime environment to support these features natively or to provide a polyfill. + - Bugfixing - Fixed an issue with the client `ready` method that was causing the returned promise to hang on async/await syntax if the promise was rejected. The fix implies a breaking change, since now the user must handle the promise rejection explicitly. 1.17.0 (September 6, 2024) - Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance SDK HTTP request Headers for Authorization Frameworks. diff --git a/package-lock.json b/package-lock.json index 8fc190d7..2493ec38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@typescript-eslint/parser": "^6.6.0", "cross-env": "^7.0.2", "eslint": "^8.48.0", - "eslint-plugin-compat": "^4.2.0", + "eslint-plugin-compat": "^6.0.1", "eslint-plugin-import": "^2.25.3", "fetch-mock": "^9.11.0", "ioredis": "^4.28.0", @@ -1363,9 +1363,9 @@ "dev": true }, "node_modules/@mdn/browser-compat-data": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.3.14.tgz", - "integrity": "sha512-Y9XQrphVcE6u9xMm+gIqN86opbU/5s2W1pdPyKRyFV5B7+2jWM2gLI5JpfhZncaoDKvhy6FYwK04aCz5UM/bTQ==", + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.6.4.tgz", + "integrity": "sha512-bOOF4GGzn0exmvNHpSWmTfOXB9beTpIFCm2KPY2UVoCdn1YVfr8heuHr1C++BYI9Tun8REgi5TNVdKbBs249CA==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -2228,9 +2228,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "funding": [ { @@ -2247,10 +2247,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -2318,9 +2318,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001528", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001528.tgz", - "integrity": "sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q==", + "version": "1.0.30001667", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", + "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", "dev": true, "funding": [ { @@ -2678,9 +2678,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.510", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.510.tgz", - "integrity": "sha512-xPfLIPFcN/WLXBpQ/K4UgE98oUBO5Tia6BD4rkSR0wE7ep/PwBVlgvPJQrIBpmJGVAmUzwPKuDbVt9XV6+uC2g==", + "version": "1.5.33", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz", + "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==", "dev": true }, "node_modules/emittery": { @@ -2762,9 +2762,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" @@ -3028,24 +3028,25 @@ } }, "node_modules/eslint-plugin-compat": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz", - "integrity": "sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.0.1.tgz", + "integrity": "sha512-0MeIEuoy8kWkOhW38kK8hU4vkb6l/VvyjpuYDymYOXmUY9NvTgyErF16lYuX+HPS5hkmym7lfA+XpYZiWYWmYA==", "dev": true, "dependencies": { - "@mdn/browser-compat-data": "^5.3.13", + "@mdn/browser-compat-data": "^5.5.35", "ast-metadata-inferer": "^0.8.0", - "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001524", + "browserslist": "^4.23.1", + "caniuse-lite": "^1.0.30001639", "find-up": "^5.0.0", + "globals": "^15.7.0", "lodash.memoize": "^4.1.2", - "semver": "^7.5.4" + "semver": "^7.6.2" }, "engines": { - "node": ">=14.x" + "node": ">=18.x" }, "peerDependencies": { - "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/eslint-plugin-compat/node_modules/find-up": { @@ -3064,6 +3065,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-plugin-compat/node_modules/globals": { + "version": "15.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz", + "integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-compat/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3110,13 +3123,10 @@ } }, "node_modules/eslint-plugin-compat/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -6583,9 +6593,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/normalize-path": { @@ -6839,9 +6849,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, "node_modules/picomatch": { @@ -7714,9 +7724,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -7733,8 +7743,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -9043,9 +9053,9 @@ } }, "@mdn/browser-compat-data": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.3.14.tgz", - "integrity": "sha512-Y9XQrphVcE6u9xMm+gIqN86opbU/5s2W1pdPyKRyFV5B7+2jWM2gLI5JpfhZncaoDKvhy6FYwK04aCz5UM/bTQ==", + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.6.4.tgz", + "integrity": "sha512-bOOF4GGzn0exmvNHpSWmTfOXB9beTpIFCm2KPY2UVoCdn1YVfr8heuHr1C++BYI9Tun8REgi5TNVdKbBs249CA==", "dev": true }, "@nodelib/fs.scandir": { @@ -9698,15 +9708,15 @@ "dev": true }, "browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" } }, "bs-logger": { @@ -9756,9 +9766,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001528", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001528.tgz", - "integrity": "sha512-0Db4yyjR9QMNlsxh+kKWzQtkyflkG/snYheSzkjmvdEtEXB1+jt7A2HmSEiO6XIJPIbo92lHNGNySvE5pZcs5Q==", + "version": "1.0.30001667", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", + "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", "dev": true }, "chalk": { @@ -10020,9 +10030,9 @@ } }, "electron-to-chromium": { - "version": "1.4.510", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.510.tgz", - "integrity": "sha512-xPfLIPFcN/WLXBpQ/K4UgE98oUBO5Tia6BD4rkSR0wE7ep/PwBVlgvPJQrIBpmJGVAmUzwPKuDbVt9XV6+uC2g==", + "version": "1.5.33", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz", + "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==", "dev": true }, "emittery": { @@ -10086,9 +10096,9 @@ } }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, "escape-string-regexp": { @@ -10422,18 +10432,19 @@ } }, "eslint-plugin-compat": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz", - "integrity": "sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-6.0.1.tgz", + "integrity": "sha512-0MeIEuoy8kWkOhW38kK8hU4vkb6l/VvyjpuYDymYOXmUY9NvTgyErF16lYuX+HPS5hkmym7lfA+XpYZiWYWmYA==", "dev": true, "requires": { - "@mdn/browser-compat-data": "^5.3.13", + "@mdn/browser-compat-data": "^5.5.35", "ast-metadata-inferer": "^0.8.0", - "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001524", + "browserslist": "^4.23.1", + "caniuse-lite": "^1.0.30001639", "find-up": "^5.0.0", + "globals": "^15.7.0", "lodash.memoize": "^4.1.2", - "semver": "^7.5.4" + "semver": "^7.6.2" }, "dependencies": { "find-up": { @@ -10446,6 +10457,12 @@ "path-exists": "^4.0.0" } }, + "globals": { + "version": "15.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz", + "integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==", + "dev": true + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10474,13 +10491,10 @@ } }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true } } }, @@ -12929,9 +12943,9 @@ "dev": true }, "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "normalize-path": { @@ -13116,9 +13130,9 @@ "dev": true }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, "picomatch": { @@ -13746,13 +13760,13 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" } }, "uri-js": { diff --git a/package.json b/package.json index 883888e7..1c1a2eb2 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@typescript-eslint/parser": "^6.6.0", "cross-env": "^7.0.2", "eslint": "^8.48.0", - "eslint-plugin-compat": "^4.2.0", + "eslint-plugin-compat": "^6.0.1", "eslint-plugin-import": "^2.25.3", "fetch-mock": "^9.11.0", "ioredis": "^4.28.0", diff --git a/src/evaluator/__tests__/evaluate-features.spec.ts b/src/evaluator/__tests__/evaluate-features.spec.ts index 45431c64..761f2804 100644 --- a/src/evaluator/__tests__/evaluate-features.spec.ts +++ b/src/evaluator/__tests__/evaluate-features.spec.ts @@ -2,7 +2,6 @@ import { evaluateFeatures, evaluateFeaturesByFlagSets } from '../index'; import { EXCEPTION, NOT_IN_SPLIT, SPLIT_ARCHIVED, SPLIT_KILLED, SPLIT_NOT_FOUND } from '../../utils/labels'; import { loggerMock } from '../../logger/__tests__/sdkLogger.mock'; -import { _Set } from '../../utils/lang/sets'; import { WARN_FLAGSET_WITHOUT_FLAGS } from '../../logger/constants'; const splitsMock = { @@ -17,8 +16,8 @@ const splitsMock = { }; const flagSetsMock = { - reg_and_config: new _Set(['regular', 'config']), - arch_and_killed: new _Set(['killed', 'archived']), + reg_and_config: new Set(['regular', 'config']), + arch_and_killed: new Set(['killed', 'archived']), }; const mockStorage = { @@ -38,7 +37,7 @@ const mockStorage = { return splits; }, getNamesByFlagSets(flagSets) { - return flagSets.map(flagset => flagSetsMock[flagset] || new _Set()); + return flagSets.map(flagset => flagSetsMock[flagset] || new Set()); } } }; @@ -192,7 +191,7 @@ describe('EVALUATOR - Multiple evaluations at once by flag sets', () => { // Should support async storage too expect(await getResultsByFlagsets(['inexistent_set1', 'inexistent_set2'], { splits: { - getNamesByFlagSets(flagSets) { return Promise.resolve(flagSets.map(flagset => flagSetsMock[flagset] || new _Set())); } + getNamesByFlagSets(flagSets) { return Promise.resolve(flagSets.map(flagset => flagSetsMock[flagset] || new Set())); } } })).toEqual({}); expect(loggerMock.warn.mock.calls).toEqual([ diff --git a/src/evaluator/index.ts b/src/evaluator/index.ts index 73527d42..883df997 100644 --- a/src/evaluator/index.ts +++ b/src/evaluator/index.ts @@ -7,7 +7,7 @@ import { IStorageAsync, IStorageSync } from '../storages/types'; import { IEvaluationResult } from './types'; import { SplitIO } from '../types'; import { ILogger } from '../logger/types'; -import { ISet, setToArray, returnSetsUnion, _Set } from '../utils/lang/sets'; +import { returnSetsUnion } from '../utils/lang/sets'; import { WARN_FLAGSET_WITHOUT_FLAGS } from '../logger/constants'; const treatmentException = { @@ -97,12 +97,12 @@ export function evaluateFeaturesByFlagSets( storage: IStorageSync | IStorageAsync, method: string, ): MaybeThenable> { - let storedFlagNames: MaybeThenable[]>; + let storedFlagNames: MaybeThenable[]>; function evaluate( - featureFlagsByFlagSets: ISet[], + featureFlagsByFlagSets: Set[], ) { - let featureFlags = new _Set(); + let featureFlags = new Set(); for (let i = 0; i < flagSets.length; i++) { const featureFlagByFlagSet = featureFlagsByFlagSets[i]; if (featureFlagByFlagSet.size) { @@ -113,7 +113,7 @@ export function evaluateFeaturesByFlagSets( } return featureFlags.size ? - evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage) : + evaluateFeatures(log, key, Array.from(featureFlags), attributes, storage) : {}; } diff --git a/src/evaluator/matchers/semver_inlist.ts b/src/evaluator/matchers/semver_inlist.ts index 6d09f7ba..c21b10d8 100644 --- a/src/evaluator/matchers/semver_inlist.ts +++ b/src/evaluator/matchers/semver_inlist.ts @@ -1,11 +1,10 @@ -import { _Set } from '../../utils/lang/sets'; import { Semver } from '../../utils/Semver'; export function inListSemverMatcherContext(ruleAttr: string[]) { // @TODO ruleAttr validation should be done at the `parser` or `matchersTransform` level to reuse for all matchers if (!ruleAttr || ruleAttr.length === 0) throw new Error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type'); - const listOfSemvers = new _Set(ruleAttr.map((version) => new Semver(version).version)); + const listOfSemvers = new Set(ruleAttr.map((version) => new Semver(version).version)); return function inListSemverMatcher(runtimeAttr: string): boolean { const runtimeSemver = new Semver(runtimeAttr).version; diff --git a/src/evaluator/matchers/whitelist.ts b/src/evaluator/matchers/whitelist.ts index 082772ae..309b1540 100644 --- a/src/evaluator/matchers/whitelist.ts +++ b/src/evaluator/matchers/whitelist.ts @@ -1,7 +1,5 @@ -import { _Set } from '../../utils/lang/sets'; - export function whitelistMatcherContext(ruleAttr: string[]) { - const whitelistSet = new _Set(ruleAttr); + const whitelistSet = new Set(ruleAttr); return function whitelistMatcher(runtimeAttr: string): boolean { const isInWhitelist = whitelistSet.has(runtimeAttr); diff --git a/src/listeners/browser.ts b/src/listeners/browser.ts index 409241cc..2320879f 100644 --- a/src/listeners/browser.ts +++ b/src/listeners/browser.ts @@ -115,7 +115,6 @@ export class BrowserSignalListener implements ISignalListener { * Returns true if beacon API was used successfully, false otherwise. */ private _sendBeacon(url: string, data: any, extraMetadata?: {}) { - // eslint-disable-next-line compat/compat if (typeof navigator !== 'undefined' && navigator.sendBeacon) { const json = { entries: data, @@ -130,7 +129,7 @@ export class BrowserSignalListener implements ISignalListener { const payload = JSON.stringify(json); // https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch - try { // eslint-disable-next-line compat/compat + try { return navigator.sendBeacon(url, payload); } catch (e) { return false; diff --git a/src/logger/__tests__/index.spec.ts b/src/logger/__tests__/index.spec.ts index 20db51e9..754acf0f 100644 --- a/src/logger/__tests__/index.spec.ts +++ b/src/logger/__tests__/index.spec.ts @@ -1,5 +1,4 @@ import { LogLevel } from '../../types'; -import { _Map } from '../../utils/lang/maps'; import { Logger, LogLevels, isLogLevelString, _sprintf } from '../index'; // We'll set this only once. These are the constants we will use for @@ -59,7 +58,7 @@ function testLogLevels(levelToTest: LogLevel) { const logMethod = levelToTest.toLowerCase(); const logCategory = `test-category-${logMethod}`; const instance = new Logger({ prefix: logCategory, showLevel }, - useCodes ? new _Map([[1, 'Test log for level %s with showLevel: %s %s']]) : undefined); + useCodes ? new Map([[1, 'Test log for level %s with showLevel: %s %s']]) : undefined); LOG_LEVELS_IN_ORDER.forEach((logLevel, i) => { const logMsg = `Test log for level ${levelToTest} with showLevel: ${showLevel} ${logLevelLogsCounter}`; diff --git a/src/logger/browser/DebugLogger.ts b/src/logger/browser/DebugLogger.ts index 105e1890..354a497b 100644 --- a/src/logger/browser/DebugLogger.ts +++ b/src/logger/browser/DebugLogger.ts @@ -1,7 +1,6 @@ import { Logger } from '../index'; import { codesDebug } from '../messages/debug'; -import { _Map } from '../../utils/lang/maps'; export function DebugLogger() { - return new Logger({ logLevel: 'DEBUG' }, new _Map(codesDebug)); + return new Logger({ logLevel: 'DEBUG' }, new Map(codesDebug)); } diff --git a/src/logger/browser/ErrorLogger.ts b/src/logger/browser/ErrorLogger.ts index f0702d89..4a685237 100644 --- a/src/logger/browser/ErrorLogger.ts +++ b/src/logger/browser/ErrorLogger.ts @@ -1,7 +1,6 @@ import { Logger } from '../index'; import { codesError } from '../messages/error'; -import { _Map } from '../../utils/lang/maps'; export function ErrorLogger() { - return new Logger({ logLevel: 'ERROR' }, new _Map(codesError)); + return new Logger({ logLevel: 'ERROR' }, new Map(codesError)); } diff --git a/src/logger/browser/InfoLogger.ts b/src/logger/browser/InfoLogger.ts index bdf9be75..a57d1cf2 100644 --- a/src/logger/browser/InfoLogger.ts +++ b/src/logger/browser/InfoLogger.ts @@ -1,7 +1,6 @@ import { Logger } from '../index'; import { codesInfo } from '../messages/info'; -import { _Map } from '../../utils/lang/maps'; export function InfoLogger() { - return new Logger({ logLevel: 'INFO' }, new _Map(codesInfo)); + return new Logger({ logLevel: 'INFO' }, new Map(codesInfo)); } diff --git a/src/logger/browser/WarnLogger.ts b/src/logger/browser/WarnLogger.ts index 8456d012..ebeb59ab 100644 --- a/src/logger/browser/WarnLogger.ts +++ b/src/logger/browser/WarnLogger.ts @@ -1,7 +1,6 @@ import { Logger } from '../index'; import { codesWarn } from '../messages/warn'; -import { _Map } from '../../utils/lang/maps'; export function WarnLogger() { - return new Logger({ logLevel: 'WARN' }, new _Map(codesWarn)); + return new Logger({ logLevel: 'WARN' }, new Map(codesWarn)); } diff --git a/src/logger/index.ts b/src/logger/index.ts index 136b2e58..f343ba75 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -2,7 +2,6 @@ import { objectAssign } from '../utils/lang/objectAssign'; import { ILoggerOptions, ILogger } from './types'; import { find, isObject } from '../utils/lang'; import { LogLevel } from '../types'; -import { IMap, _Map } from '../utils/lang/maps'; export const LogLevels: { [level: string]: LogLevel } = { DEBUG: 'DEBUG', @@ -47,12 +46,12 @@ const defaultOptions = { export class Logger implements ILogger { private options: Required; - private codes: IMap; + private codes: Map; private logLevel: number; - constructor(options?: ILoggerOptions, codes?: IMap) { + constructor(options?: ILoggerOptions, codes?: Map) { this.options = objectAssign({}, defaultOptions, options); - this.codes = codes || new _Map(); + this.codes = codes || new Map(); this.logLevel = LogLevelIndexes[this.options.logLevel]; } diff --git a/src/services/decorateHeaders.ts b/src/services/decorateHeaders.ts index 5764ffab..4a95219f 100644 --- a/src/services/decorateHeaders.ts +++ b/src/services/decorateHeaders.ts @@ -1,8 +1,7 @@ import { objectAssign } from '../utils/lang/objectAssign'; -import { _Set } from '../utils/lang/sets'; import { ISettings } from '../types'; -const FORBIDDEN_HEADERS = new _Set([ +const FORBIDDEN_HEADERS = new Set([ 'splitsdkclientkey', 'splitsdkversion', 'splitsdkmachineip', diff --git a/src/storages/AbstractSplitsCacheAsync.ts b/src/storages/AbstractSplitsCacheAsync.ts index 9e4e136c..8374c8ae 100644 --- a/src/storages/AbstractSplitsCacheAsync.ts +++ b/src/storages/AbstractSplitsCacheAsync.ts @@ -1,7 +1,6 @@ import { ISplitsCacheAsync } from './types'; import { ISplit } from '../dtos/types'; import { objectAssign } from '../utils/lang/objectAssign'; -import { ISet } from '../utils/lang/sets'; /** * This class provides a skeletal implementation of the ISplitsCacheAsync interface @@ -18,7 +17,7 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync { abstract getChangeNumber(): Promise abstract getAll(): Promise abstract getSplitNames(): Promise - abstract getNamesByFlagSets(flagSets: string[]): Promise[]> + abstract getNamesByFlagSets(flagSets: string[]): Promise[]> abstract trafficTypeExists(trafficType: string): Promise abstract clear(): Promise diff --git a/src/storages/AbstractSplitsCacheSync.ts b/src/storages/AbstractSplitsCacheSync.ts index ef44db40..92df46d5 100644 --- a/src/storages/AbstractSplitsCacheSync.ts +++ b/src/storages/AbstractSplitsCacheSync.ts @@ -1,7 +1,6 @@ import { ISplitsCacheSync } from './types'; import { ISplit } from '../dtos/types'; import { objectAssign } from '../utils/lang/objectAssign'; -import { ISet } from '../utils/lang/sets'; import { IN_SEGMENT, IN_LARGE_SEGMENT } from '../utils/constants'; /** @@ -80,7 +79,7 @@ export abstract class AbstractSplitsCacheSync implements ISplitsCacheSync { return false; } - abstract getNamesByFlagSets(flagSets: string[]): ISet[] + abstract getNamesByFlagSets(flagSets: string[]): Set[] } diff --git a/src/storages/inLocalStorage/SplitsCacheInLocal.ts b/src/storages/inLocalStorage/SplitsCacheInLocal.ts index ccd4859f..2990a094 100644 --- a/src/storages/inLocalStorage/SplitsCacheInLocal.ts +++ b/src/storages/inLocalStorage/SplitsCacheInLocal.ts @@ -4,7 +4,6 @@ import { isFiniteNumber, toNumber, isNaNNumber } from '../../utils/lang'; import { KeyBuilderCS } from '../KeyBuilderCS'; import { ILogger } from '../../logger/types'; import { LOG_PREFIX } from './constants'; -import { ISet, _Set, setToArray } from '../../utils/lang/sets'; import { ISettings } from '../../types'; import { getStorageHash } from '../KeyBuilder'; @@ -259,12 +258,12 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync { // if the filter didn't change, nothing is done } - getNamesByFlagSets(flagSets: string[]): ISet[] { + getNamesByFlagSets(flagSets: string[]): Set[] { return flagSets.map(flagSet => { const flagSetKey = this.keys.buildFlagSetKey(flagSet); const flagSetFromLocalStorage = localStorage.getItem(flagSetKey); - return new _Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []); + return new Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []); }); } @@ -279,10 +278,10 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync { const flagSetFromLocalStorage = localStorage.getItem(flagSetKey); - const flagSetCache = new _Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []); + const flagSetCache = new Set(flagSetFromLocalStorage ? JSON.parse(flagSetFromLocalStorage) : []); flagSetCache.add(featureFlag.name); - localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache))); + localStorage.setItem(flagSetKey, JSON.stringify(Array.from(flagSetCache))); }); } @@ -301,7 +300,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync { if (!flagSetFromLocalStorage) return; - const flagSetCache = new _Set(JSON.parse(flagSetFromLocalStorage)); + const flagSetCache = new Set(JSON.parse(flagSetFromLocalStorage)); flagSetCache.delete(featureFlagName); if (flagSetCache.size === 0) { @@ -309,7 +308,7 @@ export class SplitsCacheInLocal extends AbstractSplitsCacheSync { return; } - localStorage.setItem(flagSetKey, JSON.stringify(setToArray(flagSetCache))); + localStorage.setItem(flagSetKey, JSON.stringify(Array.from(flagSetCache))); } } diff --git a/src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts b/src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts index 732ca8b7..4d8ec076 100644 --- a/src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts +++ b/src/storages/inLocalStorage/__tests__/SplitsCacheInLocal.spec.ts @@ -2,7 +2,6 @@ import { SplitsCacheInLocal } from '../SplitsCacheInLocal'; import { KeyBuilderCS } from '../../KeyBuilderCS'; import { splitWithUserTT, splitWithAccountTT, splitWithAccountTTAndUsesSegments, something, somethingElse, featureFlagOne, featureFlagTwo, featureFlagThree, featureFlagWithEmptyFS, featureFlagWithoutFS } from '../../__tests__/testUtils'; import { ISplit } from '../../../dtos/types'; -import { _Set } from '../../../utils/lang/sets'; import { fullSettings } from '../../../utils/settingsValidation/__tests__/settings.mocks'; @@ -174,7 +173,7 @@ test('SPLIT CACHE / LocalStorage / flag set cache tests', () => { } } }, new KeyBuilderCS('SPLITIO', 'user')); - const emptySet = new _Set([]); + const emptySet = new Set([]); cache.addSplits([ [featureFlagOne.name, featureFlagOne], @@ -183,21 +182,21 @@ test('SPLIT CACHE / LocalStorage / flag set cache tests', () => { ]); cache.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS); - expect(cache.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_one', 'ff_two'])]); - expect(cache.getNamesByFlagSets(['n'])).toEqual([new _Set(['ff_one'])]); - expect(cache.getNamesByFlagSets(['e'])).toEqual([new _Set(['ff_one', 'ff_three'])]); + expect(cache.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_one', 'ff_two'])]); + expect(cache.getNamesByFlagSets(['n'])).toEqual([new Set(['ff_one'])]); + expect(cache.getNamesByFlagSets(['e'])).toEqual([new Set(['ff_one', 'ff_three'])]); expect(cache.getNamesByFlagSets(['t'])).toEqual([emptySet]); // 't' not in filter - expect(cache.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new _Set(['ff_one', 'ff_two']), new _Set(['ff_one']), new _Set(['ff_one', 'ff_three'])]); + expect(cache.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new Set(['ff_one', 'ff_two']), new Set(['ff_one']), new Set(['ff_one', 'ff_three'])]); cache.addSplit(featureFlagOne.name, { ...featureFlagOne, sets: ['1'] }); expect(cache.getNamesByFlagSets(['1'])).toEqual([emptySet]); // '1' not in filter - expect(cache.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_two'])]); + expect(cache.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_two'])]); expect(cache.getNamesByFlagSets(['n'])).toEqual([emptySet]); cache.addSplit(featureFlagOne.name, { ...featureFlagOne, sets: ['x'] }); - expect(cache.getNamesByFlagSets(['x'])).toEqual([new _Set(['ff_one'])]); - expect(cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([new _Set(['ff_two']), new _Set(['ff_three']), new _Set(['ff_one'])]); + expect(cache.getNamesByFlagSets(['x'])).toEqual([new Set(['ff_one'])]); + expect(cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([new Set(['ff_two']), new Set(['ff_three']), new Set(['ff_one'])]); cache.removeSplit(featureFlagOne.name); @@ -214,7 +213,7 @@ test('SPLIT CACHE / LocalStorage / flag set cache tests', () => { // if FlagSets are not defined, it should store all FlagSets in memory. test('SPLIT CACHE / LocalStorage / flag set cache tests without filters', () => { const cacheWithoutFilters = new SplitsCacheInLocal(fullSettings, new KeyBuilderCS('SPLITIO', 'user')); - const emptySet = new _Set([]); + const emptySet = new Set([]); cacheWithoutFilters.addSplits([ [featureFlagOne.name, featureFlagOne], @@ -223,12 +222,12 @@ test('SPLIT CACHE / LocalStorage / flag set cache tests without filters', () => ]); cacheWithoutFilters.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS); - expect(cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_one', 'ff_two'])]); - expect(cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual([new _Set(['ff_one'])]); - expect(cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual([new _Set(['ff_one', 'ff_three'])]); - expect(cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual([new _Set(['ff_two', 'ff_three'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_one', 'ff_two'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual([new Set(['ff_one'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual([new Set(['ff_one', 'ff_three'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual([new Set(['ff_two', 'ff_three'])]); expect(cacheWithoutFilters.getNamesByFlagSets(['y'])).toEqual([emptySet]); - expect(cacheWithoutFilters.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new _Set(['ff_one', 'ff_two']), new _Set(['ff_one']), new _Set(['ff_one', 'ff_three'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new Set(['ff_one', 'ff_two']), new Set(['ff_one']), new Set(['ff_one', 'ff_three'])]); // Validate that the feature flag cache is cleared when calling `clear` method cacheWithoutFilters.clear(); diff --git a/src/storages/inMemory/SegmentsCacheInMemory.ts b/src/storages/inMemory/SegmentsCacheInMemory.ts index a7d52b7c..f3b2cef5 100644 --- a/src/storages/inMemory/SegmentsCacheInMemory.ts +++ b/src/storages/inMemory/SegmentsCacheInMemory.ts @@ -1,5 +1,4 @@ import { AbstractSegmentsCacheSync } from '../AbstractSegmentsCacheSync'; -import { ISet, _Set } from '../../utils/lang/sets'; import { isIntegerNumber } from '../../utils/lang'; /** @@ -8,12 +7,12 @@ import { isIntegerNumber } from '../../utils/lang'; */ export class SegmentsCacheInMemory extends AbstractSegmentsCacheSync { - private segmentCache: Record> = {}; + private segmentCache: Record> = {}; private segmentChangeNumber: Record = {}; addToSegment(name: string, segmentKeys: string[]): boolean { const values = this.segmentCache[name]; - const keySet = values ? values : new _Set(); + const keySet = values ? values : new Set(); segmentKeys.forEach(k => keySet.add(k)); @@ -24,7 +23,7 @@ export class SegmentsCacheInMemory extends AbstractSegmentsCacheSync { removeFromSegment(name: string, segmentKeys: string[]): boolean { const values = this.segmentCache[name]; - const keySet = values ? values : new _Set(); + const keySet = values ? values : new Set(); segmentKeys.forEach(k => keySet.delete(k)); @@ -50,7 +49,7 @@ export class SegmentsCacheInMemory extends AbstractSegmentsCacheSync { private _registerSegment(name: string) { if (!this.segmentCache[name]) { - this.segmentCache[name] = new _Set(); + this.segmentCache[name] = new Set(); } return true; diff --git a/src/storages/inMemory/SplitsCacheInMemory.ts b/src/storages/inMemory/SplitsCacheInMemory.ts index 9294cc43..53da8def 100644 --- a/src/storages/inMemory/SplitsCacheInMemory.ts +++ b/src/storages/inMemory/SplitsCacheInMemory.ts @@ -1,7 +1,6 @@ import { ISplit, ISplitFiltersValidation } from '../../dtos/types'; import { AbstractSplitsCacheSync, usesSegments } from '../AbstractSplitsCacheSync'; import { isFiniteNumber } from '../../utils/lang'; -import { ISet, _Set } from '../../utils/lang/sets'; /** * Default ISplitsCacheSync implementation that stores split definitions in memory. @@ -14,7 +13,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync { private ttCache: Record = {}; private changeNumber: number = -1; private segmentsCount: number = 0; - private flagSetsCache: Record> = {}; + private flagSetsCache: Record> = {}; constructor(splitFiltersValidation?: ISplitFiltersValidation) { super(); @@ -104,8 +103,8 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync { return this.getChangeNumber() === -1 || this.segmentsCount > 0; } - getNamesByFlagSets(flagSets: string[]): ISet[] { - return flagSets.map(flagSet => this.flagSetsCache[flagSet] || new _Set()); + getNamesByFlagSets(flagSets: string[]): Set[] { + return flagSets.map(flagSet => this.flagSetsCache[flagSet] || new Set()); } private addToFlagSets(featureFlag: ISplit) { @@ -114,7 +113,7 @@ export class SplitsCacheInMemory extends AbstractSplitsCacheSync { if (this.flagSetsFilter.length > 0 && !this.flagSetsFilter.some(filterFlagSet => filterFlagSet === featureFlagSet)) return; - if (!this.flagSetsCache[featureFlagSet]) this.flagSetsCache[featureFlagSet] = new _Set([]); + if (!this.flagSetsCache[featureFlagSet]) this.flagSetsCache[featureFlagSet] = new Set([]); this.flagSetsCache[featureFlagSet].add(featureFlag.name); }); diff --git a/src/storages/inMemory/UniqueKeysCacheInMemory.ts b/src/storages/inMemory/UniqueKeysCacheInMemory.ts index e176aa0a..ecb468da 100644 --- a/src/storages/inMemory/UniqueKeysCacheInMemory.ts +++ b/src/storages/inMemory/UniqueKeysCacheInMemory.ts @@ -1,17 +1,16 @@ import { IUniqueKeysCacheBase } from '../types'; -import { ISet, setToArray, _Set } from '../../utils/lang/sets'; import { UniqueKeysPayloadSs } from '../../sync/submitters/types'; import { DEFAULT_CACHE_SIZE } from '../inRedis/constants'; /** * Converts `uniqueKeys` data from cache into request payload for SS. */ -export function fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: ISet }): UniqueKeysPayloadSs { +export function fromUniqueKeysCollector(uniqueKeys: { [featureName: string]: Set }): UniqueKeysPayloadSs { const payload = []; const featureNames = Object.keys(uniqueKeys); for (let i = 0; i < featureNames.length; i++) { const featureName = featureNames[i]; - const userKeys = setToArray(uniqueKeys[featureName]); + const userKeys = Array.from(uniqueKeys[featureName]); const uniqueKeysPayload = { f: featureName, ks: userKeys @@ -27,7 +26,7 @@ export class UniqueKeysCacheInMemory implements IUniqueKeysCacheBase { protected onFullQueue?: () => void; private readonly maxStorage: number; private uniqueTrackerSize = 0; - protected uniqueKeysTracker: { [featureName: string]: ISet } = {}; + protected uniqueKeysTracker: { [featureName: string]: Set } = {}; constructor(uniqueKeysQueueSize = DEFAULT_CACHE_SIZE) { this.maxStorage = uniqueKeysQueueSize; @@ -41,7 +40,7 @@ export class UniqueKeysCacheInMemory implements IUniqueKeysCacheBase { * Store unique keys per feature. */ track(userKey: string, featureName: string) { - if (!this.uniqueKeysTracker[featureName]) this.uniqueKeysTracker[featureName] = new _Set(); + if (!this.uniqueKeysTracker[featureName]) this.uniqueKeysTracker[featureName] = new Set(); const tracker = this.uniqueKeysTracker[featureName]; if (!tracker.has(userKey)) { tracker.add(userKey); diff --git a/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts b/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts index 66f54d0c..54b946e0 100644 --- a/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +++ b/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts @@ -1,5 +1,4 @@ import { IUniqueKeysCacheBase } from '../types'; -import { ISet, setToArray, _Set } from '../../utils/lang/sets'; import { UniqueKeysPayloadCs } from '../../sync/submitters/types'; import { DEFAULT_CACHE_SIZE } from '../inRedis/constants'; @@ -8,7 +7,7 @@ export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase { private onFullQueue?: () => void; private readonly maxStorage: number; private uniqueTrackerSize = 0; - private uniqueKeysTracker: { [userKey: string]: ISet } = {}; + private uniqueKeysTracker: { [userKey: string]: Set } = {}; /** * @@ -28,7 +27,7 @@ export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase { */ track(userKey: string, featureName: string) { - if (!this.uniqueKeysTracker[userKey]) this.uniqueKeysTracker[userKey] = new _Set(); + if (!this.uniqueKeysTracker[userKey]) this.uniqueKeysTracker[userKey] = new Set(); const tracker = this.uniqueKeysTracker[userKey]; if (!tracker.has(featureName)) { tracker.add(featureName); @@ -66,12 +65,12 @@ export class UniqueKeysCacheInMemoryCS implements IUniqueKeysCacheBase { /** * Converts `uniqueKeys` data from cache into request payload. */ - private fromUniqueKeysCollector(uniqueKeys: { [userKey: string]: ISet }): UniqueKeysPayloadCs { + private fromUniqueKeysCollector(uniqueKeys: { [userKey: string]: Set }): UniqueKeysPayloadCs { const payload = []; const userKeys = Object.keys(uniqueKeys); for (let k = 0; k < userKeys.length; k++) { const userKey = userKeys[k]; - const featureNames = setToArray(uniqueKeys[userKey]); + const featureNames = Array.from(uniqueKeys[userKey]); const uniqueKeysPayload = { k: userKey, fs: featureNames diff --git a/src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts b/src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts index 14fa62fd..62812586 100644 --- a/src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts +++ b/src/storages/inMemory/__tests__/SplitsCacheInMemory.spec.ts @@ -1,7 +1,6 @@ import { SplitsCacheInMemory } from '../SplitsCacheInMemory'; import { ISplit } from '../../../dtos/types'; import { splitWithUserTT, splitWithAccountTT, something, somethingElse, featureFlagWithEmptyFS, featureFlagWithoutFS, featureFlagOne, featureFlagTwo, featureFlagThree } from '../../__tests__/testUtils'; -import { _Set } from '../../../utils/lang/sets'; test('SPLITS CACHE / In Memory', () => { const cache = new SplitsCacheInMemory(); @@ -118,7 +117,7 @@ test('SPLITS CACHE / In Memory / killLocally', () => { test('SPLITS CACHE / In Memory / flag set cache tests', () => { // @ts-ignore const cache = new SplitsCacheInMemory({ groupedFilters: { bySet: ['o', 'n', 'e', 'x'] } }); - const emptySet = new _Set([]); + const emptySet = new Set([]); cache.addSplits([ [featureFlagOne.name, featureFlagOne], @@ -127,21 +126,21 @@ test('SPLITS CACHE / In Memory / flag set cache tests', () => { ]); cache.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS); - expect(cache.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_one', 'ff_two'])]); - expect(cache.getNamesByFlagSets(['n'])).toEqual([new _Set(['ff_one'])]); - expect(cache.getNamesByFlagSets(['e'])).toEqual([new _Set(['ff_one', 'ff_three'])]); + expect(cache.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_one', 'ff_two'])]); + expect(cache.getNamesByFlagSets(['n'])).toEqual([new Set(['ff_one'])]); + expect(cache.getNamesByFlagSets(['e'])).toEqual([new Set(['ff_one', 'ff_three'])]); expect(cache.getNamesByFlagSets(['t'])).toEqual([emptySet]); // 't' not in filter - expect(cache.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new _Set(['ff_one', 'ff_two']), new _Set(['ff_one']), new _Set(['ff_one', 'ff_three'])]); + expect(cache.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new Set(['ff_one', 'ff_two']), new Set(['ff_one']), new Set(['ff_one', 'ff_three'])]); cache.addSplit(featureFlagOne.name, { ...featureFlagOne, sets: ['1'] }); expect(cache.getNamesByFlagSets(['1'])).toEqual([emptySet]); // '1' not in filter - expect(cache.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_two'])]); + expect(cache.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_two'])]); expect(cache.getNamesByFlagSets(['n'])).toEqual([emptySet]); cache.addSplit(featureFlagOne.name, { ...featureFlagOne, sets: ['x'] }); - expect(cache.getNamesByFlagSets(['x'])).toEqual([new _Set(['ff_one'])]); - expect(cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([new _Set(['ff_two']), new _Set(['ff_three']), new _Set(['ff_one'])]); + expect(cache.getNamesByFlagSets(['x'])).toEqual([new Set(['ff_one'])]); + expect(cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([new Set(['ff_two']), new Set(['ff_three']), new Set(['ff_one'])]); cache.removeSplit(featureFlagOne.name); @@ -158,7 +157,7 @@ test('SPLITS CACHE / In Memory / flag set cache tests', () => { // if FlagSets are not defined, it should store all FlagSets in memory. test('SPLIT CACHE / LocalStorage / flag set cache tests without filters', () => { const cacheWithoutFilters = new SplitsCacheInMemory(); - const emptySet = new _Set([]); + const emptySet = new Set([]); cacheWithoutFilters.addSplits([ [featureFlagOne.name, featureFlagOne], @@ -167,10 +166,10 @@ test('SPLIT CACHE / LocalStorage / flag set cache tests without filters', () => ]); cacheWithoutFilters.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS); - expect(cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_one', 'ff_two'])]); - expect(cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual([new _Set(['ff_one'])]); - expect(cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual([new _Set(['ff_one', 'ff_three'])]); - expect(cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual([new _Set(['ff_two', 'ff_three'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_one', 'ff_two'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual([new Set(['ff_one'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual([new Set(['ff_one', 'ff_three'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual([new Set(['ff_two', 'ff_three'])]); expect(cacheWithoutFilters.getNamesByFlagSets(['y'])).toEqual([emptySet]); - expect(cacheWithoutFilters.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new _Set(['ff_one', 'ff_two']), new _Set(['ff_one']), new _Set(['ff_one', 'ff_three'])]); + expect(cacheWithoutFilters.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new Set(['ff_one', 'ff_two']), new Set(['ff_one']), new Set(['ff_one', 'ff_three'])]); }); diff --git a/src/storages/inRedis/RedisAdapter.ts b/src/storages/inRedis/RedisAdapter.ts index 6d738606..049f9e78 100644 --- a/src/storages/inRedis/RedisAdapter.ts +++ b/src/storages/inRedis/RedisAdapter.ts @@ -1,7 +1,6 @@ import ioredis, { Pipeline } from 'ioredis'; import { ILogger } from '../../logger/types'; import { merge, isString } from '../../utils/lang'; -import { _Set, setToArray, ISet } from '../../utils/lang/sets'; import { thenable } from '../../utils/promise/thenable'; import { timeout } from '../../utils/promise/timeout'; @@ -37,7 +36,7 @@ export class RedisAdapter extends ioredis { private readonly log: ILogger; private _options: object; private _notReadyCommandsQueue?: IRedisCommand[]; - private _runningCommands: ISet>; + private _runningCommands: Set>; constructor(log: ILogger, storageSettings: Record = {}) { const options = RedisAdapter._defineOptions(storageSettings); @@ -47,7 +46,7 @@ export class RedisAdapter extends ioredis { this.log = log; this._options = options; this._notReadyCommandsQueue = []; - this._runningCommands = new _Set(); + this._runningCommands = new Set(); this._listenToEvents(); this._setTimeoutWrappers(); this._setDisconnectWrapper(); @@ -150,7 +149,7 @@ export class RedisAdapter extends ioredis { if (instance._runningCommands.size > 0) { instance.log.info(LOG_PREFIX + `Attempting to disconnect but there are ${instance._runningCommands.size} commands still waiting for resolution. Defering disconnection until those finish.`); - Promise.all(setToArray(instance._runningCommands)) + Promise.all(Array.from(instance._runningCommands)) .then(() => { instance.log.debug(LOG_PREFIX + 'Pending commands finished successfully, disconnecting.'); originalMethod.apply(instance, params); diff --git a/src/storages/inRedis/SplitsCacheInRedis.ts b/src/storages/inRedis/SplitsCacheInRedis.ts index 8822647e..428efb94 100644 --- a/src/storages/inRedis/SplitsCacheInRedis.ts +++ b/src/storages/inRedis/SplitsCacheInRedis.ts @@ -4,7 +4,7 @@ import { ILogger } from '../../logger/types'; import { LOG_PREFIX } from './constants'; import { ISplit, ISplitFiltersValidation } from '../../dtos/types'; import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync'; -import { ISet, _Set, returnDifference } from '../../utils/lang/sets'; +import { returnDifference } from '../../utils/lang/sets'; import type { RedisAdapter } from './RedisAdapter'; /** @@ -215,14 +215,14 @@ export class SplitsCacheInRedis extends AbstractSplitsCacheAsync { * The returned promise is resolved with the list of feature flag names per flag set, * or rejected if the pipelined redis operation fails (e.g., timeout). */ - getNamesByFlagSets(flagSets: string[]): Promise[]> { + getNamesByFlagSets(flagSets: string[]): Promise[]> { return this.redis.pipeline(flagSets.map(flagSet => ['smembers', this.keys.buildFlagSetKey(flagSet)])).exec() .then((results) => results.map(([e, value], index) => { if (e === null) return value; this.log.error(LOG_PREFIX + `Could not read result from get members of flag set ${flagSets[index]} due to an error: ${e}`); })) - .then(namesByFlagSets => namesByFlagSets.map(namesByFlagSet => new _Set(namesByFlagSet))); + .then(namesByFlagSets => namesByFlagSets.map(namesByFlagSet => new Set(namesByFlagSet))); } /** diff --git a/src/storages/inRedis/TelemetryCacheInRedis.ts b/src/storages/inRedis/TelemetryCacheInRedis.ts index 78108c3d..9cb44711 100644 --- a/src/storages/inRedis/TelemetryCacheInRedis.ts +++ b/src/storages/inRedis/TelemetryCacheInRedis.ts @@ -6,7 +6,6 @@ import { findLatencyIndex } from '../findLatencyIndex'; import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter'; import { CONSUMER_MODE, STORAGE_REDIS } from '../../utils/constants'; import { isNaNNumber, isString } from '../../utils/lang'; -import { _Map } from '../../utils/lang/maps'; import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory'; import { parseLatencyField, parseExceptionField, parseMetadata } from '../utils'; import type { RedisAdapter } from './RedisAdapter'; @@ -46,7 +45,7 @@ export class TelemetryCacheInRedis implements ITelemetryCacheAsync { popLatencies(): Promise { return this.redis.hgetall(this.keys.latencyPrefix).then(latencies => { - const result: MultiMethodLatencies = new _Map(); + const result: MultiMethodLatencies = new Map(); Object.keys(latencies).forEach(field => { @@ -86,7 +85,7 @@ export class TelemetryCacheInRedis implements ITelemetryCacheAsync { popExceptions(): Promise { return this.redis.hgetall(this.keys.exceptionPrefix).then(exceptions => { - const result: MultiMethodExceptions = new _Map(); + const result: MultiMethodExceptions = new Map(); Object.keys(exceptions).forEach(field => { @@ -119,7 +118,7 @@ export class TelemetryCacheInRedis implements ITelemetryCacheAsync { popConfigs(): Promise { return this.redis.hgetall(this.keys.initPrefix).then(configs => { - const result: MultiConfigs = new _Map(); + const result: MultiConfigs = new Map(); Object.keys(configs).forEach(field => { diff --git a/src/storages/inRedis/UniqueKeysCacheInRedis.ts b/src/storages/inRedis/UniqueKeysCacheInRedis.ts index 6abdb88a..50651314 100644 --- a/src/storages/inRedis/UniqueKeysCacheInRedis.ts +++ b/src/storages/inRedis/UniqueKeysCacheInRedis.ts @@ -1,6 +1,5 @@ import { IUniqueKeysCacheBase } from '../types'; import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory'; -import { setToArray } from '../../utils/lang/sets'; import { DEFAULT_CACHE_SIZE, REFRESH_RATE, TTL_REFRESH } from './constants'; import { LOG_PREFIX } from './constants'; import { ILogger } from '../../logger/types'; @@ -29,7 +28,7 @@ export class UniqueKeysCacheInRedis extends UniqueKeysCacheInMemory implements I if (!featureNames.length) return Promise.resolve(false); const uniqueKeysArray = featureNames.map((featureName) => { - const featureKeys = setToArray(this.uniqueKeysTracker[featureName]); + const featureKeys = Array.from(this.uniqueKeysTracker[featureName]); const uniqueKeysPayload = { f: featureName, ks: featureKeys diff --git a/src/storages/inRedis/__tests__/RedisAdapter.spec.ts b/src/storages/inRedis/__tests__/RedisAdapter.spec.ts index a8ef69da..6668803c 100644 --- a/src/storages/inRedis/__tests__/RedisAdapter.spec.ts +++ b/src/storages/inRedis/__tests__/RedisAdapter.spec.ts @@ -2,7 +2,6 @@ import forEach from 'lodash/forEach'; import merge from 'lodash/merge'; import reduce from 'lodash/reduce'; -import { _Set, setToArray } from '../../../utils/lang/sets'; // Mocking sdkLogger import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; @@ -95,7 +94,7 @@ describe('STORAGE Redis Adapter', () => { expect(typeof instance._options === 'object').toBe(true); // The instance will have an options object. expect(Array.isArray(instance._notReadyCommandsQueue)).toBe(true); // The instance will have an array as the _notReadyCommandsQueue property. - expect(instance._runningCommands instanceof _Set).toBe(true); // The instance will have a set as the _runningCommands property. + expect(instance._runningCommands instanceof Set).toBe(true); // The instance will have a set as the _runningCommands property. }); test('ioredis constructor params and static method _defineLibrarySettings', () => { @@ -374,7 +373,7 @@ describe('STORAGE Redis Adapter', () => { setTimeout(() => { // queued with rejection timeout wrapper expect(loggerMock.info.mock.calls).toEqual([[LOG_PREFIX + 'Attempting to disconnect but there are 2 commands still waiting for resolution. Defering disconnection until those finish.']]); - Promise.all(setToArray(instance._runningCommands)).catch(e => { + Promise.all(Array.from(instance._runningCommands)).catch(e => { setImmediate(() => { // Allow the callback to execute before checking. expect(loggerMock.warn.mock.calls[0]).toEqual([`${LOG_PREFIX}Pending commands finished with error: ${e}. Proceeding with disconnection.`]); // Should warn about the error but tell user that will disconnect anyways. expect(ioredisMock.disconnect).toBeCalledTimes(1); // Original method should have been called once, asynchronously @@ -394,7 +393,7 @@ describe('STORAGE Redis Adapter', () => { setTimeout(() => { expect(loggerMock.info.mock.calls).toEqual([[LOG_PREFIX + 'Attempting to disconnect but there are 4 commands still waiting for resolution. Defering disconnection until those finish.']]); - Promise.all(setToArray(instance._runningCommands)).then(() => { // This one will go through success path + Promise.all(Array.from(instance._runningCommands)).then(() => { // This one will go through success path setImmediate(() => { expect(loggerMock.debug.mock.calls).toEqual([[LOG_PREFIX + 'Pending commands finished successfully, disconnecting.']]); expect(ioredisMock.disconnect).toBeCalledTimes(1); // Original method should have been called once, asynchronously diff --git a/src/storages/inRedis/__tests__/SplitsCacheInRedis.spec.ts b/src/storages/inRedis/__tests__/SplitsCacheInRedis.spec.ts index d10db711..3f577254 100644 --- a/src/storages/inRedis/__tests__/SplitsCacheInRedis.spec.ts +++ b/src/storages/inRedis/__tests__/SplitsCacheInRedis.spec.ts @@ -4,7 +4,6 @@ import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { splitWithUserTT, splitWithAccountTT, featureFlagOne, featureFlagThree, featureFlagTwo, featureFlagWithEmptyFS, featureFlagWithoutFS } from '../../__tests__/testUtils'; import { ISplit } from '../../../dtos/types'; import { metadata } from '../../__tests__/KeyBuilder.spec'; -import { _Set } from '../../../utils/lang/sets'; import { RedisAdapter } from '../RedisAdapter'; const prefix = 'splits_cache_ut'; @@ -150,7 +149,7 @@ describe('SPLITS CACHE REDIS', () => { const connection = new RedisAdapter(loggerMock); // @ts-ignore const cache = new SplitsCacheInRedis(loggerMock, keysBuilder, connection, { groupedFilters: { bySet: ['o', 'n', 'e', 'x'] } }); - const emptySet = new _Set([]); + const emptySet = new Set([]); await cache.addSplits([ [featureFlagOne.name, featureFlagOne], @@ -159,27 +158,27 @@ describe('SPLITS CACHE REDIS', () => { ]); await cache.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS); - expect(await cache.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_one', 'ff_two'])]); - expect(await cache.getNamesByFlagSets(['n'])).toEqual([new _Set(['ff_one'])]); - expect(await cache.getNamesByFlagSets(['e'])).toEqual([new _Set(['ff_one', 'ff_three'])]); + expect(await cache.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_one', 'ff_two'])]); + expect(await cache.getNamesByFlagSets(['n'])).toEqual([new Set(['ff_one'])]); + expect(await cache.getNamesByFlagSets(['e'])).toEqual([new Set(['ff_one', 'ff_three'])]); expect(await cache.getNamesByFlagSets(['t'])).toEqual([emptySet]); // 't' not in filter - expect(await cache.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new _Set(['ff_one', 'ff_two']), new _Set(['ff_one']), new _Set(['ff_one', 'ff_three'])]); + expect(await cache.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new Set(['ff_one', 'ff_two']), new Set(['ff_one']), new Set(['ff_one', 'ff_three'])]); await cache.addSplit(featureFlagOne.name, { ...featureFlagOne, sets: ['1'] }); expect(await cache.getNamesByFlagSets(['1'])).toEqual([emptySet]); // '1' not in filter - expect(await cache.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_two'])]); + expect(await cache.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_two'])]); expect(await cache.getNamesByFlagSets(['n'])).toEqual([emptySet]); await cache.addSplit(featureFlagOne.name, { ...featureFlagOne, sets: ['x'] }); - expect(await cache.getNamesByFlagSets(['x'])).toEqual([new _Set(['ff_one'])]); - expect(await cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([new _Set(['ff_two']), new _Set(['ff_three']), new _Set(['ff_one'])]); + expect(await cache.getNamesByFlagSets(['x'])).toEqual([new Set(['ff_one'])]); + expect(await cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([new Set(['ff_two']), new Set(['ff_three']), new Set(['ff_one'])]); // @ts-ignore Simulate an error in connection.pipeline().exec() jest.spyOn(connection, 'pipeline').mockImplementationOnce(() => { return { exec: () => Promise.resolve([['error', null], [null, ['ff_three']], [null, ['ff_one']]]) }; }); - expect(await cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([emptySet, new _Set(['ff_three']), new _Set(['ff_one'])]); + expect(await cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([emptySet, new Set(['ff_three']), new Set(['ff_one'])]); (connection.pipeline as jest.Mock).mockRestore(); await cache.removeSplit(featureFlagOne.name); @@ -203,7 +202,7 @@ describe('SPLITS CACHE REDIS', () => { const connection = new RedisAdapter(loggerMock); const cacheWithoutFilters = new SplitsCacheInRedis(loggerMock, keysBuilder, connection); - const emptySet = new _Set([]); + const emptySet = new Set([]); await cacheWithoutFilters.addSplits([ [featureFlagOne.name, featureFlagOne], @@ -212,12 +211,12 @@ describe('SPLITS CACHE REDIS', () => { ]); await cacheWithoutFilters.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS); - expect(await cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_one', 'ff_two'])]); - expect(await cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual([new _Set(['ff_one'])]); - expect(await cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual([new _Set(['ff_one', 'ff_three'])]); - expect(await cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual([new _Set(['ff_two', 'ff_three'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_one', 'ff_two'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual([new Set(['ff_one'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual([new Set(['ff_one', 'ff_three'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual([new Set(['ff_two', 'ff_three'])]); expect(await cacheWithoutFilters.getNamesByFlagSets(['y'])).toEqual([emptySet]); - expect(await cacheWithoutFilters.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new _Set(['ff_one', 'ff_two']), new _Set(['ff_one']), new _Set(['ff_one', 'ff_three'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new Set(['ff_one', 'ff_two']), new Set(['ff_one']), new Set(['ff_one', 'ff_three'])]); // Delete splits, TT and flag set keys await cacheWithoutFilters.removeSplits([featureFlagThree.name, featureFlagTwo.name, featureFlagOne.name, featureFlagWithEmptyFS.name]); diff --git a/src/storages/pluggable/SegmentsCachePluggable.ts b/src/storages/pluggable/SegmentsCachePluggable.ts index 995c66df..0ec44588 100644 --- a/src/storages/pluggable/SegmentsCachePluggable.ts +++ b/src/storages/pluggable/SegmentsCachePluggable.ts @@ -5,7 +5,6 @@ import { KeyBuilderSS } from '../KeyBuilderSS'; import { IPluggableStorageWrapper, ISegmentsCacheAsync } from '../types'; import { ILogger } from '../../logger/types'; import { LOG_PREFIX } from './constants'; -import { _Set } from '../../utils/lang/sets'; /** * ISegmentsCacheAsync implementation for pluggable storages. diff --git a/src/storages/pluggable/SplitsCachePluggable.ts b/src/storages/pluggable/SplitsCachePluggable.ts index d35299f6..c2bc17fa 100644 --- a/src/storages/pluggable/SplitsCachePluggable.ts +++ b/src/storages/pluggable/SplitsCachePluggable.ts @@ -5,7 +5,7 @@ import { ILogger } from '../../logger/types'; import { ISplit, ISplitFiltersValidation } from '../../dtos/types'; import { LOG_PREFIX } from './constants'; import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync'; -import { ISet, _Set, returnDifference } from '../../utils/lang/sets'; +import { returnDifference } from '../../utils/lang/sets'; /** * ISplitsCacheAsync implementation for pluggable storages. @@ -181,11 +181,11 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync { * The returned promise is resolved with the list of feature flag names per flag set. * It never rejects (If there is a wrapper error for some flag set, an empty set is returned for it). */ - getNamesByFlagSets(flagSets: string[]): Promise[]> { + getNamesByFlagSets(flagSets: string[]): Promise[]> { return Promise.all(flagSets.map(flagSet => { const flagSetKey = this.keys.buildFlagSetKey(flagSet); return this.wrapper.getItems(flagSetKey).catch(() => []); - })).then(namesByFlagSets => namesByFlagSets.map(namesByFlagSet => new _Set(namesByFlagSet))); + })).then(namesByFlagSets => namesByFlagSets.map(namesByFlagSet => new Set(namesByFlagSet))); } /** diff --git a/src/storages/pluggable/TelemetryCachePluggable.ts b/src/storages/pluggable/TelemetryCachePluggable.ts index 5f459f10..995fc6b0 100644 --- a/src/storages/pluggable/TelemetryCachePluggable.ts +++ b/src/storages/pluggable/TelemetryCachePluggable.ts @@ -6,7 +6,6 @@ import { findLatencyIndex } from '../findLatencyIndex'; import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter'; import { CONSUMER_MODE, STORAGE_PLUGGABLE } from '../../utils/constants'; import { isString, isNaNNumber } from '../../utils/lang'; -import { _Map } from '../../utils/lang/maps'; import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory'; import { parseLatencyField, parseExceptionField, parseMetadata } from '../utils'; @@ -43,7 +42,7 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync { return latencyKeys.length ? this.wrapper.getMany(latencyKeys).then(latencies => { - const result: MultiMethodLatencies = new _Map(); + const result: MultiMethodLatencies = new Map(); for (let i = 0; i < latencyKeys.length; i++) { const field = latencyKeys[i].split('::')[1]; @@ -77,7 +76,7 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync { return Promise.all(latencyKeys.map((latencyKey) => this.wrapper.del(latencyKey))).then(() => result); }) : // If latencyKeys is empty, return an empty map. - new _Map(); + new Map(); }); } @@ -90,7 +89,7 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync { return exceptionKeys.length ? this.wrapper.getMany(exceptionKeys).then(exceptions => { - const result: MultiMethodExceptions = new _Map(); + const result: MultiMethodExceptions = new Map(); for (let i = 0; i < exceptionKeys.length; i++) { const field = exceptionKeys[i].split('::')[1]; @@ -117,7 +116,7 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync { return Promise.all(exceptionKeys.map((exceptionKey) => this.wrapper.del(exceptionKey))).then(() => result); }) : // If exceptionKeys is empty, return an empty map. - new _Map(); + new Map(); }); } @@ -130,7 +129,7 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync { return configKeys.length ? this.wrapper.getMany(configKeys).then(configs => { - const result: MultiConfigs = new _Map(); + const result: MultiConfigs = new Map(); for (let i = 0; i < configKeys.length; i++) { const field = configKeys[i].split('::')[1]; @@ -154,7 +153,7 @@ export class TelemetryCachePluggable implements ITelemetryCacheAsync { return Promise.all(configKeys.map((configKey) => this.wrapper.del(configKey))).then(() => result); }) : // If configKeys is empty, return an empty map. - new _Map(); + new Map(); }); } } diff --git a/src/storages/pluggable/UniqueKeysCachePluggable.ts b/src/storages/pluggable/UniqueKeysCachePluggable.ts index d430682e..ae46171d 100644 --- a/src/storages/pluggable/UniqueKeysCachePluggable.ts +++ b/src/storages/pluggable/UniqueKeysCachePluggable.ts @@ -1,6 +1,5 @@ import { IPluggableStorageWrapper, IUniqueKeysCacheBase } from '../types'; import { UniqueKeysCacheInMemory } from '../inMemory/UniqueKeysCacheInMemory'; -import { setToArray } from '../../utils/lang/sets'; import { DEFAULT_CACHE_SIZE, REFRESH_RATE } from '../inRedis/constants'; import { LOG_PREFIX } from './constants'; import { ILogger } from '../../logger/types'; @@ -28,7 +27,7 @@ export class UniqueKeysCachePluggable extends UniqueKeysCacheInMemory implements if (!featureNames.length) return Promise.resolve(false); const uniqueKeysArray = featureNames.map((featureName) => { - const featureKeys = setToArray(this.uniqueKeysTracker[featureName]); + const featureKeys = Array.from(this.uniqueKeysTracker[featureName]); const uniqueKeysPayload = { f: featureName, ks: featureKeys diff --git a/src/storages/pluggable/__tests__/SplitsCachePluggable.spec.ts b/src/storages/pluggable/__tests__/SplitsCachePluggable.spec.ts index ea8aa73e..57fc34b3 100644 --- a/src/storages/pluggable/__tests__/SplitsCachePluggable.spec.ts +++ b/src/storages/pluggable/__tests__/SplitsCachePluggable.spec.ts @@ -4,7 +4,6 @@ import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; import { wrapperMockFactory } from './wrapper.mock'; import { splitWithUserTT, splitWithAccountTT, featureFlagOne, featureFlagThree, featureFlagTwo, featureFlagWithEmptyFS, featureFlagWithoutFS } from '../../__tests__/testUtils'; import { ISplit } from '../../../dtos/types'; -import { _Set } from '../../../utils/lang/sets'; const keysBuilder = new KeyBuilder(); @@ -154,7 +153,7 @@ describe('SPLITS CACHE PLUGGABLE', () => { test('flag set cache tests', async () => { const wrapper = wrapperMockFactory(); // @ts-ignore const cache = new SplitsCachePluggable(loggerMock, keysBuilder, wrapper, { groupedFilters: { bySet: ['o', 'n', 'e', 'x'] } }); - const emptySet = new _Set([]); + const emptySet = new Set([]); await cache.addSplits([ [featureFlagOne.name, featureFlagOne], @@ -163,25 +162,25 @@ describe('SPLITS CACHE PLUGGABLE', () => { ]); await cache.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS); - expect(await cache.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_one', 'ff_two'])]); - expect(await cache.getNamesByFlagSets(['n'])).toEqual([new _Set(['ff_one'])]); - expect(await cache.getNamesByFlagSets(['e'])).toEqual([new _Set(['ff_one', 'ff_three'])]); + expect(await cache.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_one', 'ff_two'])]); + expect(await cache.getNamesByFlagSets(['n'])).toEqual([new Set(['ff_one'])]); + expect(await cache.getNamesByFlagSets(['e'])).toEqual([new Set(['ff_one', 'ff_three'])]); expect(await cache.getNamesByFlagSets(['t'])).toEqual([emptySet]); // 't' not in filter - expect(await cache.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new _Set(['ff_one', 'ff_two']), new _Set(['ff_one']), new _Set(['ff_one', 'ff_three'])]); + expect(await cache.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new Set(['ff_one', 'ff_two']), new Set(['ff_one']), new Set(['ff_one', 'ff_three'])]); await cache.addSplit(featureFlagOne.name, { ...featureFlagOne, sets: ['1'] }); expect(await cache.getNamesByFlagSets(['1'])).toEqual([emptySet]); // '1' not in filter - expect(await cache.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_two'])]); + expect(await cache.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_two'])]); expect(await cache.getNamesByFlagSets(['n'])).toEqual([emptySet]); await cache.addSplit(featureFlagOne.name, { ...featureFlagOne, sets: ['x'] }); - expect(await cache.getNamesByFlagSets(['x'])).toEqual([new _Set(['ff_one'])]); - expect(await cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([new _Set(['ff_two']), new _Set(['ff_three']), new _Set(['ff_one'])]); + expect(await cache.getNamesByFlagSets(['x'])).toEqual([new Set(['ff_one'])]); + expect(await cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([new Set(['ff_two']), new Set(['ff_three']), new Set(['ff_one'])]); // Simulate one error in getItems wrapper.getItems.mockImplementationOnce(() => Promise.reject('error')); - expect(await cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([emptySet, new _Set(['ff_three']), new _Set(['ff_one'])]); + expect(await cache.getNamesByFlagSets(['o', 'e', 'x'])).toEqual([emptySet, new Set(['ff_three']), new Set(['ff_one'])]); await cache.removeSplit(featureFlagOne.name); expect(await cache.getNamesByFlagSets(['x'])).toEqual([emptySet]); @@ -197,7 +196,7 @@ describe('SPLITS CACHE PLUGGABLE', () => { // if FlagSets filter is not defined, it should store all FlagSets in memory. test('flag set cache tests without filters', async () => { const cacheWithoutFilters = new SplitsCachePluggable(loggerMock, keysBuilder, wrapperMockFactory()); - const emptySet = new _Set([]); + const emptySet = new Set([]); await cacheWithoutFilters.addSplits([ [featureFlagOne.name, featureFlagOne], @@ -206,12 +205,12 @@ describe('SPLITS CACHE PLUGGABLE', () => { ]); await cacheWithoutFilters.addSplit(featureFlagWithEmptyFS.name, featureFlagWithEmptyFS); - expect(await cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual([new _Set(['ff_one', 'ff_two'])]); - expect(await cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual([new _Set(['ff_one'])]); - expect(await cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual([new _Set(['ff_one', 'ff_three'])]); - expect(await cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual([new _Set(['ff_two', 'ff_three'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['o'])).toEqual([new Set(['ff_one', 'ff_two'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['n'])).toEqual([new Set(['ff_one'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['e'])).toEqual([new Set(['ff_one', 'ff_three'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['t'])).toEqual([new Set(['ff_two', 'ff_three'])]); expect(await cacheWithoutFilters.getNamesByFlagSets(['y'])).toEqual([emptySet]); - expect(await cacheWithoutFilters.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new _Set(['ff_one', 'ff_two']), new _Set(['ff_one']), new _Set(['ff_one', 'ff_three'])]); + expect(await cacheWithoutFilters.getNamesByFlagSets(['o', 'n', 'e'])).toEqual([new Set(['ff_one', 'ff_two']), new Set(['ff_one']), new Set(['ff_one', 'ff_three'])]); }); }); diff --git a/src/storages/pluggable/inMemoryWrapper.ts b/src/storages/pluggable/inMemoryWrapper.ts index 7d8a8837..afc00285 100644 --- a/src/storages/pluggable/inMemoryWrapper.ts +++ b/src/storages/pluggable/inMemoryWrapper.ts @@ -1,6 +1,5 @@ import { IPluggableStorageWrapper } from '../types'; import { startsWith, toNumber } from '../../utils/lang'; -import { ISet, setToArray, _Set } from '../../utils/lang/sets'; /** * Creates a IPluggableStorageWrapper implementation that stores items in memory. @@ -9,9 +8,9 @@ import { ISet, setToArray, _Set } from '../../utils/lang/sets'; * * @param connDelay delay in millis for `connect` resolve. If not provided, `connect` resolves immediately. */ -export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWrapper & { _cache: Record>, _setConnDelay(connDelay: number): void } { +export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWrapper & { _cache: Record>, _setConnDelay(connDelay: number): void } { - let _cache: Record> = {}; + let _cache: Record> = {}; let _connDelay = connDelay; return { @@ -84,22 +83,22 @@ export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWra itemContains(key: string, item: string) { const set = _cache[key]; if (!set) return Promise.resolve(false); - if (set instanceof _Set) return Promise.resolve(set.has(item)); + if (set instanceof Set) return Promise.resolve(set.has(item)); return Promise.reject('key is not a set'); }, addItems(key: string, items: string[]) { - if (!(key in _cache)) _cache[key] = new _Set(); + if (!(key in _cache)) _cache[key] = new Set(); const set = _cache[key]; - if (set instanceof _Set) { + if (set instanceof Set) { items.forEach(item => set.add(item)); return Promise.resolve(); } return Promise.reject('key is not a set'); }, removeItems(key: string, items: string[]) { - if (!(key in _cache)) _cache[key] = new _Set(); + if (!(key in _cache)) _cache[key] = new Set(); const set = _cache[key]; - if (set instanceof _Set) { + if (set instanceof Set) { items.forEach(item => set.delete(item)); return Promise.resolve(); } @@ -108,7 +107,7 @@ export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWra getItems(key: string) { const set = _cache[key]; if (!set) return Promise.resolve([]); - if (set instanceof _Set) return Promise.resolve(setToArray(set)); + if (set instanceof Set) return Promise.resolve(Array.from(set)); return Promise.reject('key is not a set'); }, diff --git a/src/storages/types.ts b/src/storages/types.ts index b3b1076c..a345ac7d 100644 --- a/src/storages/types.ts +++ b/src/storages/types.ts @@ -2,7 +2,6 @@ import { MaybeThenable, ISplit, IMySegmentsResponse } from '../dtos/types'; import { MySegmentsData } from '../sync/polling/types'; import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, MultiMethodExceptions, MultiMethodLatencies, MultiConfigs, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs, TelemetryUsageStatsPayload, UpdatesFromSSEEnum } from '../sync/submitters/types'; import { SplitIO, ImpressionDTO, ISettings } from '../types'; -import { ISet } from '../utils/lang/sets'; /** * Interface of a pluggable storage wrapper. @@ -211,7 +210,7 @@ export interface ISplitsCacheBase { // should never reject or throw an exception. Instead return false by default, to avoid emitting SDK_READY_FROM_CACHE. checkCache(): MaybeThenable, killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable, - getNamesByFlagSets(flagSets: string[]): MaybeThenable[]> + getNamesByFlagSets(flagSets: string[]): MaybeThenable[]> } export interface ISplitsCacheSync extends ISplitsCacheBase { @@ -228,7 +227,7 @@ export interface ISplitsCacheSync extends ISplitsCacheBase { clear(): void, checkCache(): boolean, killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean, - getNamesByFlagSets(flagSets: string[]): ISet[] + getNamesByFlagSets(flagSets: string[]): Set[] } export interface ISplitsCacheAsync extends ISplitsCacheBase { @@ -245,7 +244,7 @@ export interface ISplitsCacheAsync extends ISplitsCacheBase { clear(): Promise, checkCache(): Promise, killLocally(name: string, defaultTreatment: string, changeNumber: number): Promise, - getNamesByFlagSets(flagSets: string[]): Promise[]> + getNamesByFlagSets(flagSets: string[]): Promise[]> } /** Segments cache */ diff --git a/src/sync/polling/updaters/splitChangesUpdater.ts b/src/sync/polling/updaters/splitChangesUpdater.ts index 669a2010..a125c8e2 100644 --- a/src/sync/polling/updaters/splitChangesUpdater.ts +++ b/src/sync/polling/updaters/splitChangesUpdater.ts @@ -1,4 +1,3 @@ -import { _Set, setToArray, ISet } from '../../../utils/lang/sets'; import { ISegmentsCacheBase, ISplitsCacheBase } from '../../../storages/types'; import { ISplitChangesFetcher } from '../fetchers/types'; import { ISplit, ISplitChangesResponse, ISplitFiltersValidation } from '../../../dtos/types'; @@ -27,8 +26,8 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise { * Collect segments from a raw split definition. * Exported for testing purposes. */ -export function parseSegments({ conditions }: ISplit): ISet { - let segments = new _Set(); +export function parseSegments({ conditions }: ISplit): Set { + let segments = new Set(); for (let i = 0; i < conditions.length; i++) { const matchers = conditions[i].matcherGroup.matchers; @@ -74,7 +73,7 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) { * Exported for testing purposes. */ export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersValidation): ISplitMutations { - const segments = new _Set(); + const segments = new Set(); const computed = entries.reduce((accum, split) => { if (split.status === 'ACTIVE' && matchFilters(split, filters)) { accum.added.push([split.name, split]); @@ -89,7 +88,7 @@ export function computeSplitsMutation(entries: ISplit[], filters: ISplitFiltersV return accum; }, { added: [], removed: [], segments: [] } as ISplitMutations); - computed.segments = setToArray(segments); + computed.segments = Array.from(segments); return computed; } diff --git a/src/sync/streaming/__tests__/parseUtils.spec.ts b/src/sync/streaming/__tests__/parseUtils.spec.ts index a1501917..a12a0a4e 100644 --- a/src/sync/streaming/__tests__/parseUtils.spec.ts +++ b/src/sync/streaming/__tests__/parseUtils.spec.ts @@ -2,7 +2,6 @@ import { hash64 } from '../../../utils/murmur3/murmur3_64'; import { keylists, bitmaps, splitNotifications } from './dataMocks'; import { parseKeyList, parseBitmap, isInBitmap, parseFFUpdatePayload, getDelay } from '../parseUtils'; -import { _Set } from '../../../utils/lang/sets'; test('parseKeyList', () => { keylists.forEach(keylist => { @@ -10,8 +9,8 @@ test('parseKeyList', () => { expect(parseKeyList(keyListDataCompressed, compression)).toEqual(keyListData); // decompress KeyList - const added = new _Set(keyListData.a); - const removed = new _Set(keyListData.r); + const added = new Set(keyListData.a); + const removed = new Set(keyListData.r); addedUserKeys.forEach(userKey => { const hash = hash64(userKey); diff --git a/src/sync/streaming/parseUtils.ts b/src/sync/streaming/parseUtils.ts index 925b0524..2f2a0c49 100644 --- a/src/sync/streaming/parseUtils.ts +++ b/src/sync/streaming/parseUtils.ts @@ -13,7 +13,6 @@ function Uint8ArrayToString(myUint8Arr: Uint8Array) { // @ts-ignore function StringToUint8Array(myString: string) { const charCodes = myString.split('').map((e) => e.charCodeAt(0)); - // eslint-disable-next-line compat/compat return new Uint8Array(charCodes); } diff --git a/src/sync/streaming/pushManager.ts b/src/sync/streaming/pushManager.ts index 01eeeffc..d089a167 100644 --- a/src/sync/streaming/pushManager.ts +++ b/src/sync/streaming/pushManager.ts @@ -15,7 +15,6 @@ import { MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, PUSH_NONRETRYABLE_ERROR, import { STREAMING_FALLBACK, STREAMING_REFRESH_TOKEN, STREAMING_CONNECTING, STREAMING_DISABLED, ERROR_STREAMING_AUTH, STREAMING_DISCONNECTING, STREAMING_RECONNECT, STREAMING_PARSING_MEMBERSHIPS_UPDATE, STREAMING_PARSING_SPLIT_UPDATE } from '../../logger/constants'; import { IMembershipMSUpdateData, IMembershipLSUpdateData, KeyList, UpdateStrategy } from './SSEHandler/types'; import { getDelay, isInBitmap, parseBitmap, parseFFUpdatePayload, parseKeyList } from './parseUtils'; -import { ISet, _Set } from '../../utils/lang/sets'; import { Hash64, hash64 } from '../../utils/murmur3/murmur3_64'; import { IAuthTokenPushEnabled } from './AuthClient/types'; import { TOKEN_REFRESH, AUTH_REJECTION } from '../../utils/constants'; @@ -254,11 +253,11 @@ export function pushManagerFactory( return; } case UpdateStrategy.KeyList: { - let keyList: KeyList, added: ISet, removed: ISet; + let keyList: KeyList, added: Set, removed: Set; try { keyList = parseKeyList(parsedData.d!, parsedData.c!); - added = new _Set(keyList.a); - removed = new _Set(keyList.r); + added = new Set(keyList.a); + removed = new Set(keyList.r); } catch (e) { log.warn(STREAMING_PARSING_MEMBERSHIPS_UPDATE, ['KeyList', e]); break; diff --git a/src/sync/submitters/types.ts b/src/sync/submitters/types.ts index 8aa61c2b..d1629c34 100644 --- a/src/sync/submitters/types.ts +++ b/src/sync/submitters/types.ts @@ -1,7 +1,6 @@ /* eslint-disable no-use-before-define */ import { IMetadata } from '../../dtos/types'; import { SplitIO } from '../../types'; -import { IMap } from '../../utils/lang/maps'; import { ISyncTask } from '../types'; export type ImpressionsPayload = { @@ -88,11 +87,11 @@ export type StoredEventWithMetadata = { e: SplitIO.EventData } -export type MultiMethodLatencies = IMap +export type MultiMethodLatencies = Map -export type MultiMethodExceptions = IMap +export type MultiMethodExceptions = Map -export type MultiConfigs = IMap +export type MultiConfigs = Map /** * Telemetry usage stats diff --git a/src/utils/LRUCache/index.ts b/src/utils/LRUCache/index.ts index edf1b59b..2d1a0ec5 100644 --- a/src/utils/LRUCache/index.ts +++ b/src/utils/LRUCache/index.ts @@ -1,14 +1,13 @@ -import { IMap, _Map } from '../lang/maps'; import { LinkedList, Node } from './LinkedList'; export class LRUCache { maxLen: number; - items: IMap>; + items: Map>; lru: LinkedList<{ key: K, value: V }>; constructor(maxSize?: number) { this.maxLen = maxSize || 1; - this.items = new _Map(); + this.items = new Map(); this.lru = new LinkedList(); } diff --git a/src/utils/lang/__tests__/maps.spec.ts b/src/utils/lang/__tests__/maps.spec.ts deleted file mode 100644 index 02312d50..00000000 --- a/src/utils/lang/__tests__/maps.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { __getMapConstructor, MapPoly } from '../maps'; - -test('__getMapConstructor', () => { - - // should return global Map constructor if available - expect(__getMapConstructor()).toBe(global.Map); - - const originalMap = global.Map; // @ts-ignore - global.Map = undefined; // overwrite global Map - - // should return Map polyfill if global Map constructor is not available - expect(__getMapConstructor()).toBe(MapPoly); - - global.Map = originalMap; // restore original global Map - -}); diff --git a/src/utils/lang/__tests__/sets.spec.ts b/src/utils/lang/__tests__/sets.spec.ts index 1cb99853..8be359eb 100644 --- a/src/utils/lang/__tests__/sets.spec.ts +++ b/src/utils/lang/__tests__/sets.spec.ts @@ -1,29 +1,26 @@ -import { __getSetConstructor, _Set, returnSetsUnion, SetPoly } from '../sets'; - -test('__getSetConstructor', () => { - - // should return global Set constructor if available - expect(__getSetConstructor()).toBe(global.Set); - - const originalSet = global.Set; // @ts-ignore - global.Set = undefined; // overwrite global Set - - // should return Set polyfill if global Set constructor is not available - expect(__getSetConstructor()).toBe(SetPoly); - - global.Set = originalSet; // restore original global Set - -}); +import { returnSetsUnion, returnDifference } from '../sets'; test('returnSetsUnion', () => { - const set = new _Set(['1','2','3']); - const set2 = new _Set(['4','5','6']); - expect(returnSetsUnion(set, set2)).toEqual(new _Set(['1','2','3','4','5','6'])); - expect(set).toEqual(new _Set(['1','2','3'])); - expect(set2).toEqual(new _Set(['4','5','6'])); + const set = new Set(['1', '2', '3', '4']); + const set2 = new Set(['4', '5', '6', '1']); + expect(returnSetsUnion(set, set2)).toEqual(new Set(['1', '2', '3', '4', '5', '6'])); + expect(set).toEqual(new Set(['1', '2', '3', '4'])); + expect(set2).toEqual(new Set(['4', '5', '6', '1'])); - const emptySet = new _Set([]); + const emptySet = new Set([]); expect(returnSetsUnion(emptySet, emptySet)).toEqual(emptySet); expect(returnSetsUnion(set, emptySet)).toEqual(set); expect(returnSetsUnion(emptySet, set2)).toEqual(set2); }); + +test('returnDifference', () => { + const list = ['1', '2', '3']; + const list2 = ['2', '3', '4']; + expect(returnDifference(list, list2)).toEqual(['1']); + expect(list).toEqual(['1', '2', '3']); + expect(list2).toEqual(['2', '3', '4']); + + expect(returnDifference([], [])).toEqual([]); + expect(returnDifference(list, [])).toEqual(list); + expect(returnDifference([], list2)).toEqual([]); +}); diff --git a/src/utils/lang/index.ts b/src/utils/lang/index.ts index 0a828dda..11b6afd0 100644 --- a/src/utils/lang/index.ts +++ b/src/utils/lang/index.ts @@ -122,10 +122,9 @@ export function isBoolean(val: any): boolean { */ export function isFiniteNumber(val: any): boolean { if (val instanceof Number) val = val.valueOf(); - // @TODO remove `isFinite` once `Number.isFinite` is fully supported by targets - // eslint-disable-next-line compat/compat - if (typeof val === 'number') return Number.isFinite ? Number.isFinite(val) : isFinite(val); - return false; + return typeof val === 'number' ? + Number.isFinite ? Number.isFinite(val) : isFinite(val) : + false; } /** @@ -134,9 +133,9 @@ export function isFiniteNumber(val: any): boolean { */ export function isIntegerNumber(val: any): boolean { if (val instanceof Number) val = val.valueOf(); - // eslint-disable-next-line compat/compat - if (typeof val === 'number') return Number.isInteger ? Number.isInteger(val) : isFinite(val) && Math.floor(val) === val; - return false; + return typeof val === 'number' ? + Number.isInteger ? Number.isInteger(val) : isFinite(val) && Math.floor(val) === val : + false; } /** diff --git a/src/utils/lang/maps.ts b/src/utils/lang/maps.ts deleted file mode 100644 index 277712bd..00000000 --- a/src/utils/lang/maps.ts +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Map implementation based on es6-map polyfill (https://github.com/medikoo/es6-map/blob/master/polyfill.js), - * with the minimal features used by the SDK. - -Copyright (C) 2013 Mariusz Nowak (www.medikoo.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -**/ - -export interface IMap { - clear(): void; - delete(key: K): boolean; - forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any): void; - get(key: K): V | undefined; - has(key: K): boolean; - set(key: K, value: V): this; - readonly size: number; -} - -export class MapPoly implements IMap{ - private __mapKeysData__: K[] = []; - private __mapValuesData__: V[] = []; - - // unlike ES6 `Map`, it only accepts an array as first argument iterable - constructor(entries?: readonly (readonly [K, V])[] | null) { - if (Array.isArray(entries)) entries.forEach(entry => { this.set(entry[0], entry[1]); }); - } - - clear() { - if (!this.__mapKeysData__.length) return; - this.__mapKeysData__.length = 0; - this.__mapValuesData__.length = 0; - } - - delete(key: K) { - const index = this.__mapKeysData__.indexOf(key); - if (index === -1) return false; - this.__mapKeysData__.splice(index, 1); - this.__mapValuesData__.splice(index, 1); - return true; - } - - forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any) { - for (let i = 0; i < this.__mapKeysData__.length; i++) { - callbackfn.call(thisArg, this.__mapValuesData__[i], this.__mapKeysData__[i], this as any); - } - } - - get(key: K) { - const index = this.__mapKeysData__.indexOf(key); - if (index === -1) return; - return this.__mapValuesData__[index]; - } - - has(key: K): boolean { - return this.__mapKeysData__.indexOf(key) !== -1; - } - - set(key: K, value: V) { - let index = this.__mapKeysData__.indexOf(key); - if (index === -1) index = this.__mapKeysData__.push(key) - 1; - this.__mapValuesData__[index] = value; - return this; - } - - get size() { - return this.__mapKeysData__.length; - } - -} - -interface IMapConstructor { - new(): IMap; - new (entries?: readonly (readonly [K, V])[] | null): IMap; - readonly prototype: IMap; -} - -/** - * return the Map constructor to use. If native Map is not available or it doesn't support the required features (e.g., IE11), - * a ponyfill with minimal features is returned instead. - * - * Exported for testing purposes only. - */ -export function __getMapConstructor(): IMapConstructor { - // eslint-disable-next-line compat/compat - if (typeof Array.from === 'function' && typeof Map === 'function' && Map.prototype && Map.prototype.values) { - return Map; - } - return MapPoly; -} - -export const _Map = __getMapConstructor(); diff --git a/src/utils/lang/objectAssign.ts b/src/utils/lang/objectAssign.ts index 23fcbd9a..1253b355 100644 --- a/src/utils/lang/objectAssign.ts +++ b/src/utils/lang/objectAssign.ts @@ -1,71 +1,6 @@ -/* -Adaptation of "object-assign" library (https://www.npmjs.com/package/object-assign) -exported as an ES module instead of CommonJS, to avoid extra configuration steps when using -the ESM build of the SDK with tools that doesn't support CommonJS by default (e.g. Rollup). - -object-assign -(c) Sindre Sorhus -@license MIT -*/ - -/* eslint-disable */ -// @ts-nocheck - -var getOwnPropertySymbols = Object.getOwnPropertySymbols; -var hasOwnProperty = Object.prototype.hasOwnProperty; -var propIsEnumerable = Object.prototype.propertyIsEnumerable; - -function toObject(val) { - if (val === null || val === undefined) { - throw new TypeError('Object.assign cannot be called with null or undefined'); - } - - return Object(val); -} - -function shouldUseNative() { - try { - if (!Object.assign) { - return false; - } - - // Detect buggy property enumeration order in older V8 versions. - - // https://bugs.chromium.org/p/v8/issues/detail?id=4118 - var test1 = new String('abc'); - test1[5] = 'de'; - if (Object.getOwnPropertyNames(test1)[0] === '5') { - return false; - } - - // https://bugs.chromium.org/p/v8/issues/detail?id=3056 - var test2 = {}; - for (var i = 0; i < 10; i++) { - test2['_' + String.fromCharCode(i)] = i; - } - var order2 = Object.getOwnPropertyNames(test2).map(function (n) { - return test2[n]; - }); - if (order2.join('') !== '0123456789') { - return false; - } - - // https://bugs.chromium.org/p/v8/issues/detail?id=3056 - var test3 = {}; - 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { - test3[letter] = letter; - }); - if (Object.keys(Object.assign({}, test3)).join('') !== - 'abcdefghijklmnopqrst') { - return false; - } - - return true; - } catch (err) { - // We don't expect any of the above to throw, but better to be safe. - return false; - } -} +/** + * A tiny polyfill for Object.assign + */ // https://www.npmjs.com/package/@types/object-assign type ObjectAssign = ((target: T, source: U) => T & U) & @@ -74,31 +9,17 @@ type ObjectAssign = ((target: T, source: U) => T & U) & ((target: T, source1: U, source2: V, source3: W, source4: Q) => T & U & V & W & Q) & ((target: T, source1: U, source2: V, source3: W, source4: Q, source5: R) => T & U & V & W & Q & R) & ((target: any, ...sources: any[]) => any); - -export const objectAssign: ObjectAssign = shouldUseNative() ? Object.assign : function (target, source) { - var from; - var to = toObject(target); - var symbols; - - for (var s = 1; s < arguments.length; s++) { - from = Object(arguments[s]); - - // eslint-disable-next-line no-restricted-syntax - for (var key in from) { - if (hasOwnProperty.call(from, key)) { - to[key] = from[key]; - } - } - - if (getOwnPropertySymbols) { - symbols = getOwnPropertySymbols(from); - for (var i = 0; i < symbols.length; i++) { - if (propIsEnumerable.call(from, symbols[i])) { - to[symbols[i]] = from[symbols[i]]; - } +export const objectAssign: ObjectAssign = Object.assign || function (target: any) { + if (target === null || target === undefined) throw new TypeError('Object.assign cannot be called with null or undefined'); + target = Object(target); + + for (let i = 1; i < arguments.length; i++) { + const source = Object(arguments[i]); // eslint-disable-next-line no-restricted-syntax + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; } } } - - return to; + return target; }; diff --git a/src/utils/lang/sets.ts b/src/utils/lang/sets.ts index d8d63e7a..155c4587 100644 --- a/src/utils/lang/sets.ts +++ b/src/utils/lang/sets.ts @@ -1,129 +1,7 @@ -/** - * Set implementation based on es6-set polyfill (https://github.com/medikoo/es6-set/blob/master/polyfill.js), - * with the minimal features used by the SDK. - -Copyright (C) 2013 Mariusz Nowak (www.medikoo.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -**/ - -export interface ISet { - add(value: T): this; - clear(): void; - delete(value: T): boolean; - forEach(callbackfn: (value: T, value2: T, set: ISet) => void, thisArg?: any): void; - has(value: T): boolean; - readonly size: number; -} - -export class SetPoly implements ISet{ - __setData__: T[] = []; - - // unlike ES6 `Set`, it only accepts an array as first argument iterable - constructor(values?: readonly T[] | null) { - if (Array.isArray(values)) values.forEach(value => { this.add(value); }); - } - - clear() { - if (!this.__setData__.length) return; - this.__setData__.length = 0; - } - - add(value: T) { - if (this.has(value)) return this; - this.__setData__.push(value); - return this; - } - - delete(value: T) { - let index = this.__setData__.indexOf(value); - if (index === -1) return false; - this.__setData__.splice(index, 1); - return true; - } - - has(value: T) { - return this.__setData__.indexOf(value) !== -1; - } - - forEach(callbackfn: (value: T, value2: T, set: SetPoly) => void, thisArg?: any): void { - if (typeof callbackfn !== 'function') throw new TypeError(callbackfn + ' is not a function'); - - for (let i = 0; i < this.__setData__.length; i++) { - const value = this.__setData__[i]; - callbackfn.call(thisArg, value, value, this); - } - } - - get size() { - return this.__setData__.length; - } - -} - - -/** - * return an array containing the items of the given set. - * @param set Set or SetPoly instance - */ -export function setToArray(set: ISet): T[] { - if (set instanceof SetPoly) { - return set.__setData__.slice(); - } - // if not using SetPoly as set, it means Array.from is supported - // eslint-disable-next-line compat/compat - return Array.from(set as Set); -} - -interface ISetConstructor { - new (values?: readonly T[] | null): ISet; - readonly prototype: ISet; -} - -/** - * return the Set constructor to use. If `Array.from` built-in or native Set is not available or it doesn't support the required features, - * a ponyfill with minimal features is returned instead. - * - * Exported for testing purposes only. - */ -export function __getSetConstructor(): ISetConstructor { - // eslint-disable-next-line compat/compat - if (typeof Array.from === 'function' && typeof Set === 'function' && Set.prototype && Set.prototype.values) { - return Set; - } - return SetPoly; -} - -export const _Set = __getSetConstructor(); - -export function returnSetsUnion(set: ISet, set2: ISet): ISet { - const result = new _Set(setToArray(set)); - set2.forEach(value => { - result.add(value); - }); - return result; +export function returnSetsUnion(set: Set, set2: Set): Set { + return new Set(Array.from(set).concat(Array.from(set2))); } export function returnDifference(list: T[] = [], list2: T[] = []): T[] { - const result = new _Set(list); - list2.forEach(item => { - result.delete(item); - }); - return setToArray(result); + return list.filter(item => list2.indexOf(item) === -1); } diff --git a/src/utils/settingsValidation/logger/builtinLogger.ts b/src/utils/settingsValidation/logger/builtinLogger.ts index 5db9cfb0..abc4f56d 100644 --- a/src/utils/settingsValidation/logger/builtinLogger.ts +++ b/src/utils/settingsValidation/logger/builtinLogger.ts @@ -3,11 +3,10 @@ import { ILogger } from '../../../logger/types'; import { isLocalStorageAvailable } from '../../env/isLocalStorageAvailable'; import { isNode } from '../../env/isNode'; import { codesDebug } from '../../../logger/messages/debug'; -import { _Map } from '../../lang/maps'; import { getLogLevel } from './commons'; import { LogLevel } from '../../../types'; -const allCodes = new _Map(codesDebug); +const allCodes = new Map(codesDebug); // @TODO set default debug setting instead of initialLogLevel when integrating in JS and Node packages const LS_KEY = 'splitio_debug';