Repository: exadel-inc/esl
Pull request #216 (2021-05-28 10:08:35)

feat:  add omit function

github-actions[bot]: ****CLA Assistant Lite bot**** All contributors have signed the CLA  ✍️ 
ala-n: Discussed with a core team, the result proposed strategy is the following:
`copyDefinedKeys` is not going to be deprecated but should be defined as an alias  (PAF) for `omit` call with `(key, val) => val !== void 0` predicate.
'omit' is approved to be released in v2 stable release.


Patch:
diff --git a/package-lock.json b/package-lock.json
index 81471275e..1003d00e8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -810,15 +810,15 @@
       "dev": true
     },
     "@eslint/eslintrc": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz",
-      "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==",
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz",
+      "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==",
       "dev": true,
       "requires": {
         "ajv": "^6.12.4",
         "debug": "^4.1.1",
         "espree": "^7.3.0",
-        "globals": "^12.1.0",
+        "globals": "^13.9.0",
         "ignore": "^4.0.6",
         "import-fresh": "^3.2.1",
         "js-yaml": "^3.13.1",
@@ -835,15 +835,6 @@
             "ms": "2.1.2"
           }
         },
-        "globals": {
-          "version": "12.4.0",
-          "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
-          "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
-          "dev": true,
-          "requires": {
-            "type-fest": "^0.8.1"
-          }
-        },
         "ignore": {
           "version": "4.0.6",
           "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
@@ -861,12 +852,6 @@
           "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
           "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
           "dev": true
-        },
-        "type-fest": {
-          "version": "0.8.1",
-          "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
-          "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
-          "dev": true
         }
       }
     },
@@ -945,13 +930,13 @@
       }
     },
     "@jest/core": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.0.3.tgz",
-      "integrity": "sha512-rN8lr/OJ8iApcQUh4khnMaOCVX4oRnLwy2tPW3Vh70y62K8Da8fhkxMUq0xX9VPa4+yWUm0tGc/jUSJi+Jzuwg==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.0.4.tgz",
+      "integrity": "sha512-+dsmV8VUs1h/Szb+rEWk8xBM1fp1I///uFy9nk3wXGvRsF2lBp8EVPmtWc+QFRb3MY2b7u2HbkGF1fzoDzQTLA==",
       "dev": true,
       "requires": {
         "@jest/console": "^27.0.2",
-        "@jest/reporters": "^27.0.2",
+        "@jest/reporters": "^27.0.4",
         "@jest/test-result": "^27.0.2",
         "@jest/transform": "^27.0.2",
         "@jest/types": "^27.0.2",
@@ -962,15 +947,15 @@
         "exit": "^0.1.2",
         "graceful-fs": "^4.2.4",
         "jest-changed-files": "^27.0.2",
-        "jest-config": "^27.0.3",
+        "jest-config": "^27.0.4",
         "jest-haste-map": "^27.0.2",
         "jest-message-util": "^27.0.2",
         "jest-regex-util": "^27.0.1",
-        "jest-resolve": "^27.0.2",
-        "jest-resolve-dependencies": "^27.0.3",
-        "jest-runner": "^27.0.3",
-        "jest-runtime": "^27.0.3",
-        "jest-snapshot": "^27.0.2",
+        "jest-resolve": "^27.0.4",
+        "jest-resolve-dependencies": "^27.0.4",
+        "jest-runner": "^27.0.4",
+        "jest-runtime": "^27.0.4",
+        "jest-snapshot": "^27.0.4",
         "jest-util": "^27.0.2",
         "jest-validate": "^27.0.2",
         "jest-watcher": "^27.0.2",
@@ -1146,9 +1131,9 @@
       }
     },
     "@jest/reporters": {
-      "version": "27.0.2",
-      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.0.2.tgz",
-      "integrity": "sha512-SVQjew/kafNxSN1my4praGQP+VPVGHsU8zqiEDppLvq6j1lryIjdNb9P+bZSsKeifU4bIoaPnf9Ui0tK9WOpFA==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.0.4.tgz",
+      "integrity": "sha512-Xa90Nm3JnV0xCe4M6A10M9WuN9krb+WFKxV1A98Y4ePCw40n++r7uxFUNU7DT1i9Behj7fjrAIju9oU0t1QtCg==",
       "dev": true,
       "requires": {
         "@bcoe/v8-coverage": "^0.2.3",
@@ -1167,7 +1152,7 @@
         "istanbul-lib-source-maps": "^4.0.0",
         "istanbul-reports": "^3.0.2",
         "jest-haste-map": "^27.0.2",
-        "jest-resolve": "^27.0.2",
+        "jest-resolve": "^27.0.4",
         "jest-util": "^27.0.2",
         "jest-worker": "^27.0.2",
         "slash": "^3.0.0",
@@ -1249,15 +1234,15 @@
       }
     },
     "@jest/test-sequencer": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.0.3.tgz",
-      "integrity": "sha512-DcLTzraZ8xLr5fcIl+CF14vKeBBpBrn55wFxI9Ju+dhEBdjRdJQ/Z/pLkMehkPZWIQ+rR23J8e+wFDkfjree0Q==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.0.4.tgz",
+      "integrity": "sha512-6UFEVwdmxYdyNffBxVVZxmXEdBE4riSddXYSnFNH0ELFQFk/bvagizim8WfgJTqF4EKd+j1yFxvhb8BMHfOjSQ==",
       "dev": true,
       "requires": {
         "@jest/test-result": "^27.0.2",
         "graceful-fs": "^4.2.4",
         "jest-haste-map": "^27.0.2",
-        "jest-runtime": "^27.0.3"
+        "jest-runtime": "^27.0.4"
       }
     },
     "@jest/transform": {
@@ -4812,13 +4797,13 @@
       }
     },
     "eslint": {
-      "version": "7.27.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.27.0.tgz",
-      "integrity": "sha512-JZuR6La2ZF0UD384lcbnd0Cgg6QJjiCwhMD6eU4h/VGPcVGwawNNzKU41tgokGXnfjOOyI6QIffthhJTPzzuRA==",
+      "version": "7.28.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz",
+      "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "7.12.11",
-        "@eslint/eslintrc": "^0.4.1",
+        "@eslint/eslintrc": "^0.4.2",
         "ajv": "^6.10.0",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
@@ -4835,7 +4820,7 @@
         "fast-deep-equal": "^3.1.3",
         "file-entry-cache": "^6.0.1",
         "functional-red-black-tree": "^1.0.1",
-        "glob-parent": "^5.0.0",
+        "glob-parent": "^5.1.2",
         "globals": "^13.6.0",
         "ignore": "^4.0.6",
         "import-fresh": "^3.0.0",
@@ -6135,9 +6120,9 @@
       }
     },
     "globals": {
-      "version": "13.8.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz",
-      "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==",
+      "version": "13.9.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz",
+      "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==",
       "dev": true,
       "requires": {
         "type-fest": "^0.20.2"
@@ -6981,14 +6966,14 @@
       "dev": true
     },
     "jest": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/jest/-/jest-27.0.3.tgz",
-      "integrity": "sha512-0G9+QqXFIZWgf5rs3yllpaA+13ZawVHfyuhuCV1EnoFbX++rVMRrYWCAnk+dfhwyv9/VTQvn+XG969u8aPRsBg==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest/-/jest-27.0.4.tgz",
+      "integrity": "sha512-Px1iKFooXgGSkk1H8dJxxBIrM3tsc5SIuI4kfKYK2J+4rvCvPGr/cXktxh0e9zIPQ5g09kOMNfHQEmusBUf/ZA==",
       "dev": true,
       "requires": {
-        "@jest/core": "^27.0.3",
+        "@jest/core": "^27.0.4",
         "import-local": "^3.0.2",
-        "jest-cli": "^27.0.3"
+        "jest-cli": "^27.0.4"
       },
       "dependencies": {
         "@jest/types": {
@@ -7014,19 +6999,19 @@
           }
         },
         "jest-cli": {
-          "version": "27.0.3",
-          "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.0.3.tgz",
-          "integrity": "sha512-7bt9Sgv4nWH5pUnyJfdLf8CHWfo4+7lSPxeBwQx4r0vBj9jweJam/piE2U91SXtQI+ckm+TIN97OVnqIYpVhSg==",
+          "version": "27.0.4",
+          "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.0.4.tgz",
+          "integrity": "sha512-E0T+/i2lxsWAzV7LKYd0SB7HUAvePqaeIh5vX43/G5jXLhv1VzjYzJAGEkTfvxV774ll9cyE2ljcL73PVMEOXQ==",
           "dev": true,
           "requires": {
-            "@jest/core": "^27.0.3",
+            "@jest/core": "^27.0.4",
             "@jest/test-result": "^27.0.2",
             "@jest/types": "^27.0.2",
             "chalk": "^4.0.0",
             "exit": "^0.1.2",
             "graceful-fs": "^4.2.4",
             "import-local": "^3.0.2",
-            "jest-config": "^27.0.3",
+            "jest-config": "^27.0.4",
             "jest-util": "^27.0.2",
             "jest-validate": "^27.0.2",
             "prompts": "^2.0.1",
@@ -7069,9 +7054,9 @@
           }
         },
         "execa": {
-          "version": "5.0.1",
-          "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.1.tgz",
-          "integrity": "sha512-4hFTjFbFzQa3aCLobpbPJR/U+VoL1wdV5ozOWjeet0AWDeYr9UFGM1eUFWHX+VtOWFq4p0xXUXfW1YxUaP4fpw==",
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.0.tgz",
+          "integrity": "sha512-CkdUB7s2y6S+d4y+OM/+ZtQcJCiKUCth4cNImGMqrt2zEVtW2rfHGspQBE1GDo6LjeNIQmTPKXqTCKjqFKyu3A==",
           "dev": true,
           "requires": {
             "cross-spawn": "^7.0.3",
@@ -7100,9 +7085,9 @@
       }
     },
     "jest-circus": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.0.3.tgz",
-      "integrity": "sha512-tdMfzs7SgD5l7jRcI1iB3vtQi5fHwCgo4RlO8bzZnYc05PZ+tlAOMZeS8eGYkZ2tPaRY/aRLMFWQp/8zXBrolQ==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.0.4.tgz",
+      "integrity": "sha512-QD+eblDiRphta630WRKewuASLs/oY1Zki2G4bccntRvrTHQ63ljwFR5TLduuK4Zg0ZPzW0+8o6AP7KRd1yKOjw==",
       "dev": true,
       "requires": {
         "@jest/environment": "^27.0.3",
@@ -7117,8 +7102,8 @@
         "jest-each": "^27.0.2",
         "jest-matcher-utils": "^27.0.2",
         "jest-message-util": "^27.0.2",
-        "jest-runtime": "^27.0.3",
-        "jest-snapshot": "^27.0.2",
+        "jest-runtime": "^27.0.4",
+        "jest-snapshot": "^27.0.4",
         "jest-util": "^27.0.2",
         "pretty-format": "^27.0.2",
         "slash": "^3.0.0",
@@ -7169,13 +7154,13 @@
       }
     },
     "jest-config": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.0.3.tgz",
-      "integrity": "sha512-zgtI2YQo+ekKsmYNyDlXFY/7w7WWBSJFoj/WRe173WB88CDUrEYWr0sLdbLOQe+sRu6l1Y2S0MCS6BOJm5jkoA==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.0.4.tgz",
+      "integrity": "sha512-VkQFAHWnPQefdvHU9A+G3H/Z3NrrTKqWpvxgQz3nkUdkDTWeKJE6e//BL+R7z79dXOMVksYgM/z6ndtN0hfChg==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.1.0",
-        "@jest/test-sequencer": "^27.0.3",
+        "@jest/test-sequencer": "^27.0.4",
         "@jest/types": "^27.0.2",
         "babel-jest": "^27.0.2",
         "chalk": "^4.0.0",
@@ -7183,14 +7168,14 @@
         "glob": "^7.1.1",
         "graceful-fs": "^4.2.4",
         "is-ci": "^3.0.0",
-        "jest-circus": "^27.0.3",
+        "jest-circus": "^27.0.4",
         "jest-environment-jsdom": "^27.0.3",
         "jest-environment-node": "^27.0.3",
         "jest-get-type": "^27.0.1",
-        "jest-jasmine2": "^27.0.3",
+        "jest-jasmine2": "^27.0.4",
         "jest-regex-util": "^27.0.1",
-        "jest-resolve": "^27.0.2",
-        "jest-runner": "^27.0.3",
+        "jest-resolve": "^27.0.4",
+        "jest-runner": "^27.0.4",
         "jest-util": "^27.0.2",
         "jest-validate": "^27.0.2",
         "micromatch": "^4.0.4",
@@ -7488,9 +7473,9 @@
       }
     },
     "jest-jasmine2": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.0.3.tgz",
-      "integrity": "sha512-odJ2ia8P5c+IsqOcWJPmku4AqbXIfTVLRjYTKHri3TEvbmTdLw0ghy13OAPIl/0v7cVH0TURK7+xFOHKDLvKIA==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.0.4.tgz",
+      "integrity": "sha512-yj3WrjjquZwkJw+eA4c9yucHw4/+EHndHWSqgHbHGQfT94ihaaQsa009j1a0puU8CNxPDk0c1oAPeOpdJUElwA==",
       "dev": true,
       "requires": {
         "@babel/traverse": "^7.1.0",
@@ -7506,8 +7491,8 @@
         "jest-each": "^27.0.2",
         "jest-matcher-utils": "^27.0.2",
         "jest-message-util": "^27.0.2",
-        "jest-runtime": "^27.0.3",
-        "jest-snapshot": "^27.0.2",
+        "jest-runtime": "^27.0.4",
+        "jest-snapshot": "^27.0.4",
         "jest-util": "^27.0.2",
         "pretty-format": "^27.0.2",
         "throat": "^6.0.1"
@@ -7813,9 +7798,9 @@
       "dev": true
     },
     "jest-resolve": {
-      "version": "27.0.2",
-      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.0.2.tgz",
-      "integrity": "sha512-rmfLGyZhwAUR5z3EwPAW7LQTorWAuCYCcsQJoQxT2it+BOgX3zKxa67r1pfpK3ihy2k9TjYD3/lMp5rPm/CL1Q==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.0.4.tgz",
+      "integrity": "sha512-BcfyK2i3cG79PDb/6gB6zFeFQlcqLsQjGBqznFCpA0L/3l1L/oOsltdUjs5eISAWA9HS9qtj8v2PSZr/yWxONQ==",
       "dev": true,
       "requires": {
         "@jest/types": "^27.0.2",
@@ -7854,14 +7839,14 @@
       }
     },
     "jest-resolve-dependencies": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.3.tgz",
-      "integrity": "sha512-HdjWOvFAgT5CYChF2eiBN2rRKicjaTCCtA3EtH47REIdGzEHGUhYrWYgLahXsiOovvWN6edhcHL5WCa3gbc04A==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.0.4.tgz",
+      "integrity": "sha512-F33UPfw1YGWCV2uxJl7wD6TvcQn5IC0LtguwY3r4L7R6H4twpLkp5Q2ZfzRx9A2I3G8feiy0O0sqcn/Qoym71A==",
       "dev": true,
       "requires": {
         "@jest/types": "^27.0.2",
         "jest-regex-util": "^27.0.1",
-        "jest-snapshot": "^27.0.2"
+        "jest-snapshot": "^27.0.4"
       },
       "dependencies": {
         "@jest/types": {
@@ -7889,9 +7874,9 @@
       }
     },
     "jest-runner": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.0.3.tgz",
-      "integrity": "sha512-zH23uIIh1ro1JCD7XX1bQ0bQwXEsBzLX2UJVE/AVLsk4YJRmTfyXIzzRzBWRdnMHHg1NWkJ4fGs7eFP15IqZpQ==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.0.4.tgz",
+      "integrity": "sha512-NfmvSYLCsCJk2AG8Ar2NAh4PhsJJpO+/r+g4bKR5L/5jFzx/indUpnVBdrfDvuqhGLLAvrKJ9FM/Nt8o1dsqxg==",
       "dev": true,
       "requires": {
         "@jest/console": "^27.0.2",
@@ -7905,11 +7890,13 @@
         "exit": "^0.1.2",
         "graceful-fs": "^4.2.4",
         "jest-docblock": "^27.0.1",
+        "jest-environment-jsdom": "^27.0.3",
+        "jest-environment-node": "^27.0.3",
         "jest-haste-map": "^27.0.2",
         "jest-leak-detector": "^27.0.2",
         "jest-message-util": "^27.0.2",
-        "jest-resolve": "^27.0.2",
-        "jest-runtime": "^27.0.3",
+        "jest-resolve": "^27.0.4",
+        "jest-runtime": "^27.0.4",
         "jest-util": "^27.0.2",
         "jest-worker": "^27.0.2",
         "source-map-support": "^0.5.6",
@@ -7941,9 +7928,9 @@
       }
     },
     "jest-runtime": {
-      "version": "27.0.3",
-      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.0.3.tgz",
-      "integrity": "sha512-k1Hl2pWWHBkSXdCggX2lyLRuDnnnmMlnJd+DPLb8LmmAeHW87WgGC6TplD377VxY3KQu73sklkhGUIdwFgsRVQ==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.0.4.tgz",
+      "integrity": "sha512-voJB4xbAjS/qYPboV+e+gmg3jfvHJJY4CagFWBOM9dQKtlaiTjcpD2tWwla84Z7PtXSQPeIpXY0qksA9Dum29A==",
       "dev": true,
       "requires": {
         "@jest/console": "^27.0.2",
@@ -7965,8 +7952,8 @@
         "jest-message-util": "^27.0.2",
         "jest-mock": "^27.0.3",
         "jest-regex-util": "^27.0.1",
-        "jest-resolve": "^27.0.2",
-        "jest-snapshot": "^27.0.2",
+        "jest-resolve": "^27.0.4",
+        "jest-snapshot": "^27.0.4",
         "jest-util": "^27.0.2",
         "jest-validate": "^27.0.2",
         "slash": "^3.0.0",
@@ -8015,9 +8002,9 @@
       }
     },
     "jest-snapshot": {
-      "version": "27.0.2",
-      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.0.2.tgz",
-      "integrity": "sha512-4RcgvZbPrrbEE/hT6XQ4hr+NVVLNrmsgUnYSnZRT6UAvW9Q2yzGMS+tfJh+xlQJAapnnkNJzsMn6vUa+yfiVHA==",
+      "version": "27.0.4",
+      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.0.4.tgz",
+      "integrity": "sha512-hnjrvpKGdSMvKfbHyaG5Kul7pDJGZvjVy0CKpzhu28MmAssDXS6GpynhXzgst1wBQoKD8c9b2VS2a5yhDLQRCA==",
       "dev": true,
       "requires": {
         "@babel/core": "^7.7.2",
@@ -8039,7 +8026,7 @@
         "jest-haste-map": "^27.0.2",
         "jest-matcher-utils": "^27.0.2",
         "jest-message-util": "^27.0.2",
-        "jest-resolve": "^27.0.2",
+        "jest-resolve": "^27.0.4",
         "jest-util": "^27.0.2",
         "natural-compare": "^1.4.0",
         "pretty-format": "^27.0.2",
@@ -13743,9 +13730,9 @@
       "dev": true
     },
     "ts-jest": {
-      "version": "27.0.2",
-      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.2.tgz",
-      "integrity": "sha512-pozjHOOfm+sbv9kXCvTFVyDntWvuJztzkNFql/akD75hSMZ2jsbidVauOhBRImAopXohqcLtPK/NTTIS8Y49Ug==",
+      "version": "27.0.3",
+      "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.3.tgz",
+      "integrity": "sha512-U5rdMjnYam9Ucw+h0QvtNDbc5+88nxt7tbIvqaZUhFrfG4+SkWhMXjejCLVGcpILTPuV+H3W/GZDZrnZFpPeXw==",
       "dev": true,
       "requires": {
         "bs-logger": "0.x",
@@ -13772,9 +13759,9 @@
       }
     },
     "ts-loader": {
-      "version": "9.2.2",
-      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.2.2.tgz",
-      "integrity": "sha512-hNIhGTQHtNKjOzR2ZtQ2OSVbXPykOae+zostf1IlHCf61Mt41GMJurKNqrYUbzHgpmj6UWRu8eBfb7q0XliV0g==",
+      "version": "9.2.3",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.2.3.tgz",
+      "integrity": "sha512-sEyWiU3JMHBL55CIeC4iqJQadI0U70A5af0kvgbNLHVNz2ACztQg0j/9x10bjjIht8WfFYLKfn4L6tkZ+pu+8Q==",
       "dev": true,
       "requires": {
         "chalk": "^4.1.0",
diff --git a/package.json b/package.json
index 499c090fd..2c0a1a7c4 100644
--- a/package.json
+++ b/package.json
@@ -86,14 +86,14 @@
     "chokidar-cli": "^2.1.0",
     "concurrently": "^6.2.0",
     "copyfiles": "^2.4.1",
-    "eslint": "^7.27.0",
+    "eslint": "^7.28.0",
     "eslint-plugin-editorconfig": "^3.0.2",
     "eslint-plugin-import": "^2.23.4",
     "eslint-plugin-sonarjs": "^0.7.0",
     "foreach-cli": "^1.8.1",
     "husky": "^6.0.0",
     "intersection-observer": "^0.12.0",
-    "jest": "^27.0.3",
+    "jest": "^27.0.4",
     "less": "^4.1.1",
     "lintspaces-cli": "^0.7.1",
     "postcss": "^8.3.0",
@@ -102,8 +102,8 @@
     "semantic-release": "^17.4.3",
     "smoothscroll-polyfill": "^0.4.4",
     "stylelint": "^13.13.1",
-    "ts-jest": "^27.0.2",
-    "ts-loader": "^9.2.2",
+    "ts-jest": "^27.0.3",
+    "ts-loader": "^9.2.3",
     "typescript": "4.3.2",
     "webpack": "^5.38.1",
     "webpack-cli": "^4.7.0"
diff --git a/src/modules/esl-utils/environment/device-detector.ts b/src/modules/esl-utils/environment/device-detector.ts
index 919a58a64..4f8dab550 100644
--- a/src/modules/esl-utils/environment/device-detector.ts
+++ b/src/modules/esl-utils/environment/device-detector.ts
@@ -1,8 +1,6 @@
 import {ExportNs} from './export-ns';
 
-const ua = window.navigator.userAgent;
-const vendor = window.navigator.vendor;
-const platform = window.navigator.platform;
+const {userAgent, vendor, platform} = window.navigator;
 
 /**
  * Device detection utility
@@ -11,55 +9,47 @@ const platform = window.navigator.platform;
 @ExportNs('DeviceDetector')
 export abstract class DeviceDetector {
   // IE Detection
-  public static readonly isTrident = /trident/i.test(ua);
+  public static readonly isTrident = /trident/i.test(userAgent);
   public static readonly isIE = DeviceDetector.isTrident;
 
   // Edge Detection
-  public static readonly isEdgeHTML = /edg([ea]|ios)/i.test(ua);
-  public static readonly isBlinkEdge = /\sedg\//i.test(ua);
+  public static readonly isEdgeHTML = /edg([ea]|ios)/i.test(userAgent);
+  public static readonly isBlinkEdge = /\sedg\//i.test(userAgent);
   public static readonly isEdge = DeviceDetector.isEdgeHTML || DeviceDetector.isBlinkEdge;
 
   // Gecko
-  public static readonly isGecko = /gecko/i.test(ua) && !/like gecko/i.test(ua);
-  public static readonly isFirefox = /firefox|iceweasel|fxios/i.test(ua);
+  public static readonly isGecko = /gecko/i.test(userAgent) && !/like gecko/i.test(userAgent);
+  public static readonly isFirefox = /firefox|iceweasel|fxios/i.test(userAgent);
 
   // Opera / Chrome
-  public static readonly isOpera = /(?:^opera.+?version|opr)/.test(ua);
+  public static readonly isOpera = /(?:^opera.+?version|opr)/.test(userAgent);
   public static readonly isChrome = !DeviceDetector.isOpera && /google inc/.test(vendor);
 
   // Webkit
-  public static readonly isWebkit = /(apple)?webkit/i.test(ua);
+  public static readonly isWebkit = /(apple)?webkit/i.test(userAgent);
 
   // Safari
-  public static readonly isSafari = DeviceDetector.isWebkit && /^((?!chrome|android).)*safari/i.test(ua);
+  public static readonly isSafari = DeviceDetector.isWebkit && /^((?!chrome|android).)*safari/i.test(userAgent);
 
   // Blink
   public static readonly isBlink = DeviceDetector.isWebkit && !DeviceDetector.isSafari;
 
   // Bot detection
-  public static readonly isBot = /Chrome-Lighthouse|Google Page Speed Insights/i.test(ua);
+  public static readonly isBot = /Chrome-Lighthouse|Google Page Speed Insights/i.test(userAgent);
 
   // Mobile
-  public static readonly isAndroid = /Android/i.test(ua);
+  public static readonly isAndroid = /Android/i.test(userAgent);
   public static readonly isMobileIOS13 = /* iOS 13+ detection */ (platform === 'MacIntel' && window.navigator.maxTouchPoints > 1);
   public static readonly isMobileIOS = /iPad|iPhone|iPod/.test(platform) || DeviceDetector.isMobileIOS13;
   public static readonly isLegacyMobile = /webOS|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
 
   public static readonly isMobile = DeviceDetector.isMobileIOS || DeviceDetector.isAndroid || DeviceDetector.isLegacyMobile;
-  public static readonly isMobileSafari = DeviceDetector.isMobileIOS && DeviceDetector.isWebkit && /CriOS/i.test(ua);
+  public static readonly isMobileSafari = DeviceDetector.isMobileIOS && DeviceDetector.isWebkit && /CriOS/i.test(userAgent);
 
   // Touch Detection
-  protected static touchMQ = (() => {
-    const prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
-    const mediaQuery = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
-    return matchMedia(mediaQuery);
-  })();
-
   public static get isTouchDevice() {
-    if (('ontouchstart' in window) || 'TouchEvent ' in window || 'DocumentTouch' in window && document instanceof Touch) {
-      return true;
-    }
-    return DeviceDetector.touchMQ.matches;
+    if (window.navigator.maxTouchPoints || window.navigator.msMaxTouchPoints) return true;
+    return ('ontouchstart' in window) || ('DocumentTouch' in window && document instanceof Touch);
   }
 
   static get TOUCH_EVENTS() {
diff --git a/src/modules/esl-utils/misc/object.ts b/src/modules/esl-utils/misc/object.ts
index 9f8df5f39..121ebcc4d 100644
--- a/src/modules/esl-utils/misc/object.ts
+++ b/src/modules/esl-utils/misc/object.ts
@@ -1,6 +1,8 @@
 export const isObject = (obj: any): obj is Record<string, any> => obj && typeof obj === 'object';
 export const isObjectLike = (obj: any) => isObject(obj) || typeof obj === 'function';
 
+export type CopyPredicate = (key: string, value: any) => boolean;
+
 /** Deep object compare */
 export function deepCompare(obj1: any, obj2: any): boolean {
   if (Object.is(obj1, obj2)) return true;
@@ -31,15 +33,26 @@ export function defined<T>(...params: T[]) {
   }
 }
 
-/** Makes a flat copy without undefined keys */
-export function copyDefinedKeys<T>(obj?: T): Partial<T> {
+/** Makes a plain copy of obj with properties satisfying the predicate
+ * If no predicate provided copies all own properties */
+export function copy<T>(obj: T, predicate: CopyPredicate = () => true): Partial<T> {
   const result: any = Object.assign({}, obj || {});
   Object.keys(result).forEach((key) => {
-    (result[key] === void 0) && delete result[key];
+    (!predicate(key, result[key])) && delete result[key];
   });
   return result;
 }
 
+/** Makes a flat copy without undefined keys */
+export function copyDefinedKeys<T>(obj?: T): Partial<T> {
+  return copy(obj || {}, (key, value) => value !== void 0);
+}
+
+/** Omit copying provided properties from object */
+export function omit<T>(obj: T, keys: string[]) {
+  return copy(obj, key => !keys.includes(key));
+}
+
 /**
  * Set object property using "path" key
  *


Test patch:
diff --git a/src/modules/esl-utils/misc/test/object.test.ts b/src/modules/esl-utils/misc/test/object.test.ts
index f873dc957..b6a9b0e66 100644
--- a/src/modules/esl-utils/misc/test/object.test.ts
+++ b/src/modules/esl-utils/misc/test/object.test.ts
@@ -1,4 +1,4 @@
-import {defined, deepCompare, getPropertyDescriptor, get, set, copyDefinedKeys} from '../object';
+import {defined, deepCompare, getPropertyDescriptor, get, set, copyDefinedKeys, copy, omit} from '../object';
 
 describe('misc/object', () => {
   describe('deepCompare', () => {
@@ -97,6 +97,38 @@ describe('misc/object', () => {
     });
   });
 
+  describe('copy', () => {
+
+    test.each([
+      [undefined, {}],
+      [null, {}],
+      [{}, {}],
+      [[1, 2], {0: 1, 1: 2}],
+      [{a: 1, b: {c: 2}}, {a: 1, b: {c: 2}}],
+    ])('full copy of %p', (inp, out) => {
+      expect(copy(inp)).toEqual(out);
+    });
+
+    const predicate = (key: string) => !key.startsWith('_');
+    test.each([
+      [undefined, {}],
+      [null, {}],
+      [{}, {}],
+      [[1, 2], {0: 1, 1: 2}],
+      [{_: 1, _b: 1}, {}],
+      [{_a: 1, b: 1}, {b: 1}],
+      [{a: 1, b: {c: 2}}, {a: 1, b: {c: 2}}],
+    ])('copy %p with predicate', (inp, out) => {
+      expect(copy(inp, predicate)).toEqual(out);
+    });
+
+    test('special cases', () => {
+      const obj = {_a: 1, b: 2};
+      Object.setPrototypeOf(obj, {c: 3, _d: 4});
+      expect(copy(obj, predicate)).toEqual({b: 2});
+    });
+  });
+
   describe('copyDefinedKeys', () => {
     test.each([
       [undefined, {}],
@@ -111,6 +143,23 @@ describe('misc/object', () => {
     })
   });
 
+  describe('omit', () => {
+    const predicate = (key: string) => !key.startsWith('_');
+    test.each([
+      [undefined, ['prop'], {}],
+      [null, ['prop'], {}],
+      [{}, ['prop'], {}],
+      [[1, 2], ['0'], {1: 2}],
+      [[1, 2], ['0', '1'], {}],
+      [{a: 1, b: 1}, ['a', 'b'], {}],
+      [{a: 1, b: 1}, ['a'], {b: 1}],
+      [{a: 1, b: 1}, ['c'], {a: 1, b: 1}],
+      [{a: 1, b: {}}, [], {a: 1, b: {}}],
+    ])('omit from %p properties %p', (inp, keys, out) => {
+      expect(omit(inp, keys)).toEqual(out);
+    });
+  });
+
   describe('get', () => {
     test.each([
       ['', {a: 1}, undefined],