diff --git a/.browserslistrc b/.browserslistrc index 8e6d57b9..1db06762 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Feb 28, 2024 6:13 PM UTC. +# Last generated Mar 1, 2024 10:35 AM UTC. [production] node >= 20.9.0 diff --git a/.dockerignore b/.dockerignore index 1d6ebf8f..130b6d71 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Feb 28, 2024 6:13 PM UTC. +# Last generated Mar 1, 2024 10:35 AM UTC. # Locals diff --git a/.env.vault b/.env.vault index 6f1893ee..f4d89303 100644 --- a/.env.vault +++ b/.env.vault @@ -8,12 +8,12 @@ DOTENV_VAULT_MAIN="7GW98NfL4hM4N6HbfeDXCKreVxKvcVPupFRnZ8XJ+pomaQ==" DOTENV_VAULT_MAIN_VERSION=1 # dev -DOTENV_VAULT_DEV="1grZ/WtjdQmeCHoygC84p+bV7wOnHsIDEAkrxwN06bke71kjBDQGOxbuGZkVQCNySkZZz6cM+8y03juezC8usBIabKdRO/BrRBbrYmKN4WS6wjnMQmB8rwzdh+4w9SFD6oM0MDHnB9e79WYPrDCqlYcg8r2hbopghyj53rRAEmfzC2o2ZObtPAYb7zdoHW/Q4F/tBY8H6byCntf0X0VBMtKP1KNUtnIbIE6l5NPqMMT+39e++WsUsFrtD6rnfAybiw==" -DOTENV_VAULT_DEV_VERSION=375 +DOTENV_VAULT_DEV="mmKRm7X+Pgmm7u6P0FoPZ/0JtvLznMa0XU6WBuolJIKjy8/FjTFhBNYSRI4qBVNJE2cn9AKjpp9bT2wi9DiboxUtPfO7YGhfvMC11mKMxotP02HOCALV+IV7yPGOd8enldbf024UxRvWsKMokEOCXFfTlSZ7amEbFCCheHJTTS3DRbOURFlFI7i91/jLlqS3t+YRkzoH9E+4F2KdMVwBboD57v6z9X4mI5/U42U/ta2e6HbO5SlvbG/cNbbdwHmY6Q==" +DOTENV_VAULT_DEV_VERSION=377 # ci -DOTENV_VAULT_CI="X11J9vNhZwTSEqSQWUaRX0RDsv/wRSse/IYUWXUBlbTXXGi/p9OLpxqJmr6ArZh2dOfDpp8xRlv8wJ2oxEFLSC9h1yvyo58CttFcR9IVa5QZziPYBRE05R6nEhN11z+jFRliJXQyqLIRuxd7xkdVmXJy2IW3vmf9otBlfU3i/A==" -DOTENV_VAULT_CI_VERSION=375 +DOTENV_VAULT_CI="UY4qlh2/75TKKBRxQxnGFL37z73+/JkNDSR2BBBuo/SOWNr6fY6Zwe0i8FZuIaJ0WxX9PxTw+Lr3c7A1frxEkkayFwSMRKTN24KWkeJT+7Iga+zaqa3K7mkncWUgXhHoR2rhJOhx7kkYGY/REf6Cgdbod8tMoIDrDyL0SK6F2w==" +DOTENV_VAULT_CI_VERSION=377 # stage DOTENV_VAULT_STAGE="aRP8su2YV4jZu3w1HZ/SLaots0IwJDFw75TCpvXEFeNp7tw=" diff --git a/.gitattributes b/.gitattributes index d5c2d1ed..98a63c86 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Feb 28, 2024 6:13 PM UTC. +# Last generated Mar 1, 2024 10:35 AM UTC. # Default diff --git a/.gitignore b/.gitignore index 0ef104d2..db39be0b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Feb 28, 2024 6:13 PM UTC. +# Last generated Mar 1, 2024 10:35 AM UTC. # Locals diff --git a/.npmignore b/.npmignore index bd8093fa..1881f1a8 100644 --- a/.npmignore +++ b/.npmignore @@ -25,7 +25,7 @@ # Generated data. # -# Last generated Feb 28, 2024 6:13 PM UTC. +# Last generated Mar 1, 2024 10:35 AM UTC. # Locals diff --git a/.prettierignore b/.prettierignore index 0935ea8f..c8bf7278 100644 --- a/.prettierignore +++ b/.prettierignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Feb 28, 2024 6:13 PM UTC. +# Last generated Mar 1, 2024 10:35 AM UTC. # Packages diff --git a/.vscode/settings.json b/.vscode/settings.json index a3760384..79fa064c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ * @note This entire file will be updated automatically. * @note Instead of editing here, please review `./settings.mjs`. * - * Last generated using `./settings.mjs` Feb 28, 2024 6:13 PM UTC. + * Last generated using `./settings.mjs` Mar 1, 2024 10:35 AM UTC. */ { "editor.formatOnType": false, diff --git a/.vscodeignore b/.vscodeignore index ecb70861..d2c01c53 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -17,7 +17,7 @@ # Generated data. # -# Last generated Feb 28, 2024 6:13 PM UTC. +# Last generated Mar 1, 2024 10:35 AM UTC. # Locals diff --git a/package-lock.json b/package-lock.json index b2a55d95..61ef27ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@clevercanyon/utilities", - "version": "1.0.873", + "version": "1.0.874", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@clevercanyon/utilities", - "version": "1.0.873", + "version": "1.0.874", "cpu": [ "x64", "arm64" @@ -63,13 +63,13 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -1026,9 +1026,9 @@ } }, "node_modules/@clevercanyon/utilities": { - "version": "1.0.873", - "resolved": "https://registry.npmjs.org/@clevercanyon/utilities/-/utilities-1.0.873.tgz", - "integrity": "sha512-ZJJoX3wKUa/KHbUiQkl4OxfjzuZeLYLY+bB1Y/2G5xm8lBGxBmgyJ31vU1UztwMqWmODWBXrr+bt2YdfCyhXoA==", + "version": "1.0.874", + "resolved": "https://registry.npmjs.org/@clevercanyon/utilities/-/utilities-1.0.874.tgz", + "integrity": "sha512-VxQL23bqqKLqKdoky8I3sXxnQRshIFD4ZJOYqQ2s2/+jV7gjQngID1+NWdDdfToU249SA/k+9KxCYBQCs6T+IQ==", "cpu": [ "x64", "arm64" @@ -3562,13 +3562,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz", - "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -3583,9 +3583,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "engines": { "node": ">=6.0.0" } @@ -3606,9 +3606,9 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz", - "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==", + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.24.tgz", + "integrity": "sha512-+VaWXDa6+l6MhflBvVXjIEAzb59nQ2JUK3bwRp2zRpPtU+8TFRy9Gg/5oIcNlkEL5PGlBFGfemUVvIgLnTzq7Q==", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -3721,9 +3721,9 @@ } }, "node_modules/@microsoft/api-extractor": { - "version": "7.41.1", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.41.1.tgz", - "integrity": "sha512-XVuWS+UFSLjrBjp/ifRQ8QcNiTGvEZDgG8LQpFJf2Y+3WX3d+Jpo0ycewqy/c3ATJFaPp+8xKXGqTbfypeVsGw==", + "version": "7.42.1", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.42.1.tgz", + "integrity": "sha512-eswRJCfiscDDcgu+0DPBdpYKRCVXaeFOOp9h4Y4ixoCCeGoEwgNi/P9zp39O+61pLWxZ3pAShTY0pe8vw5LtrQ==", "dev": true, "dependencies": { "@microsoft/api-extractor-model": "7.28.13", @@ -3732,8 +3732,9 @@ "@rushstack/node-core-library": "4.0.2", "@rushstack/rig-package": "0.5.2", "@rushstack/terminal": "0.10.0", - "@rushstack/ts-command-line": "4.18.0", + "@rushstack/ts-command-line": "4.18.1", "lodash": "~4.17.15", + "minimatch": "~3.0.3", "resolve": "~1.22.1", "semver": "~7.5.4", "source-map": "~0.6.1", @@ -3754,6 +3755,18 @@ "@rushstack/node-core-library": "4.0.2" } }, + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@microsoft/api-extractor/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -5716,9 +5729,9 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.18.0.tgz", - "integrity": "sha512-iq8+NCtnOhz4BR4hwmFCRzULmFd8hSbDqJWbdGG44t/fXMRsK3Drft1fiDkxWGPuD3V1x3RDYdXxAfJqKU3XpQ==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.18.1.tgz", + "integrity": "sha512-A3x3ARWzoW4ARU1XS87wFVJvdywzo2j27aRm5SIFY3Nwl5vQqo0hvzaQDYCq/hEGBxAdBGW8q6N5clNk36H34A==", "dev": true, "dependencies": { "@rushstack/terminal": "0.10.0", @@ -7747,9 +7760,9 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bare-events": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz", - "integrity": "sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.1.tgz", + "integrity": "sha512-9GYPpsPFvrWBkelIhOhTWtkeZxVxZOdb3VnFTCzlOo3OjvmTvzLoZFUT8kNFACx0vJej6QPney1Cf9BvzCNE/A==", "dev": true, "optional": true, "peer": true @@ -8760,9 +8773,9 @@ } }, "node_modules/crc32-stream": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.0.tgz", - "integrity": "sha512-B0EPa1UK+qnpBZpG+7FgPCu0J2ETLpXq09o9BkLkEAhdB6Z61Qo4pJ3JYu0c+Qi+/SAL7QThqnzS06pmSSyZaw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-5.0.1.tgz", + "integrity": "sha512-lO1dFui+CEUh/ztYIpgpKItKW9Bb4NWakCRJrnqAbFIYD+OZAwb2VfD5T5eXMw2FNcsDHkQcNl/Wh3iVXYwU6g==", "dev": true, "peer": true, "dependencies": { @@ -9512,9 +9525,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.685", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.685.tgz", - "integrity": "sha512-yDYeobbTEe4TNooEzOQO6xFqg9XnAkVy2Lod1C1B2it8u47JNLYvl9nLDWBamqUakWB8Jc1hhS1uHUNYTNQdfw==", + "version": "1.4.689", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.689.tgz", + "integrity": "sha512-GatzRKnGPS1go29ep25reM94xxd1Wj8ritU0yRhCJ/tr1Bg8gKnm6R9O/yPOhGQBoLMZ9ezfrpghNaTw97C/PQ==", "dev": true }, "node_modules/emittery": { @@ -9565,18 +9578,18 @@ } }, "node_modules/es-abstract": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.4.tgz", - "integrity": "sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==", + "version": "1.22.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", + "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.6", + "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", "get-intrinsic": "^1.2.4", @@ -9584,15 +9597,15 @@ "globalthis": "^1.0.3", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", "hasown": "^2.0.1", "internal-slot": "^1.0.7", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", @@ -9605,10 +9618,10 @@ "string.prototype.trim": "^1.2.8", "string.prototype.trimend": "^1.0.7", "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.1", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.5", "unbox-primitive": "^1.0.2", "which-typed-array": "^1.1.14" }, @@ -27048,12 +27061,12 @@ } }, "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "dependencies": { - "call-bind": "^1.0.6", + "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" diff --git a/package.json b/package.json index d0200518..ffe1fbea 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "1.0.874", + "version": "1.0.875", "license": "GPL-3.0-or-later", "name": "@clevercanyon/utilities", "description": "Utilities for JavaScript apps running in any environment.", diff --git a/src/error.ts b/src/error.ts index a63c2f2c..26d08b74 100644 --- a/src/error.ts +++ b/src/error.ts @@ -10,10 +10,14 @@ import { $is, $obj, $str } from '#index.ts'; /** * Defines types. */ -export type MessageOptions = { - expectedCauses?: (string | RegExp)[]; +export type SafeMessageFromOptions = { + expectedCauses?: ExpectedCause[]; default: string; }; +export type ThrownByExpectecCauseOptions = { + expectedCauses: ExpectedCause[]; +}; +type ExpectedCause = string | RegExp; /** * Error code regular expression. @@ -38,53 +42,72 @@ export const codeRegExp = $fnꓺmemo((): RegExp => /^[a-z0-9]{8}$/iu); /** * Generates an error message from a thrown value. * - * A safe message from a thrown value is a message that’s attached to a {@see Error}, and is either an error code, or - * was thrown due to an expected cause. Expected causes can be passed as an option to this utility. + * A safe message from a thrown value is a message that’s attached to an {@see Error} and is either an error code, or + * was thrown due to an expected cause code. Expected cause codes can be passed as an option to this utility. * * @param thrown Something thrown and caught by this utility’s caller. - * @param options `{ default: 'Message or code.' }` is required; {@see MessageOptions}. + * @param options `{ default: 'Message or code.' }` required; {@see SafeMessageFromOptions}. * * @returns Safe error message from thrown value. - * - * @see isExpectedCause() */ -export const safeMessageFrom = (thrown: unknown, options: MessageOptions): string => { - const opts = $obj.defaults({}, options || {}, { expectedCauses: [] }) as Required, +export const safeMessageFrom = (thrown: unknown, options: SafeMessageFromOptions): string => { + const opts = $obj.defaults({}, options || {}, { expectedCauses: [] }) as Required, tꓺError𑂱codeꓽ𑂱 = 'Error code: '; // Text token. if ($is.errorCode(thrown)) { return tꓺError𑂱codeꓽ𑂱 + thrown.message + '.'; } - if (opts.expectedCauses.length && $is.error(thrown)) { - let error: unknown = thrown; // Initialize. - - while ($is.error(error) && $is.errorCause(error.cause)) { - if ( - error.message && - (($is.string(error.cause) && isExpectedCause(opts.expectedCauses, error.cause)) || - ($is.plainObject(error.cause) && isExpectedCause(opts.expectedCauses, error.cause.code))) - ) { - return error.message; // Expected; i.e., safe, error message. - } - error = error.cause; // Up the stack we go. + let error: unknown = thrown; // Initialize. + while (opts.expectedCauses.length && $is.error(error) && $is.errorCause(error.cause)) { + if ( + error.message && + (($is.string(error.cause) && isExpectedCauseCode(opts.expectedCauses, error.cause)) || + ($is.plainObject(error.cause) && isExpectedCauseCode(opts.expectedCauses, error.cause.code))) + ) { + return error.message; // Expected; i.e., safe, error message. } + error = error.cause; // Up the stack we go. } return codeRegExp().test(opts.default) ? tꓺError𑂱codeꓽ𑂱 + opts.default + '.' : opts.default; }; +/** + * Checks if thrown by an expected cause. + * + * @param thrown Something thrown and caught by this utility’s caller. + * @param options `{ expectedCauses }` required; {@see ThrownByExpectecCauseOptions}. + * + * @returns True if thrown by an expected cause. + */ +export const thrownByExpectedCause = (thrown: unknown, options: ThrownByExpectecCauseOptions): boolean => { + const opts = $obj.defaults({}, options || {}, { expectedCauses: [] }) as Required; + + let error: unknown = thrown; // Initialize. + while (opts.expectedCauses.length && $is.error(error) && $is.errorCause(error.cause)) { + if ( + ($is.string(error.cause) && isExpectedCauseCode(opts.expectedCauses, error.cause)) || // + ($is.plainObject(error.cause) && isExpectedCauseCode(opts.expectedCauses, error.cause.code)) + ) { + return true; // Expected cause; i.e., true. + } + error = error.cause; // Up the stack we go. + } + return false; +}; + // --- // Misc utilites. /** * Checks if a cause code is expected. * - * @param expectedCauses Expected causes. - * @param code An error’s cause code. + * @param expectedCauses Expected causes; {@see ExpectedCause[]}. + * @param code An error’s cause code for comparison. * * @returns True if cause code is expected. */ -const isExpectedCause = (expectedCauses: (string | RegExp)[], code: string): boolean => { - return expectedCauses.some((expectedCause: string | RegExp): boolean => { +const isExpectedCauseCode = (expectedCauses: ExpectedCause[], code: string): boolean => { + return expectedCauses.some((expectedCause: ExpectedCause): boolean => { if ($is.string(expectedCause)) { return ( code === $str.rTrim(expectedCause, ':') || // Even match. diff --git a/src/tests/error/index.ts b/src/tests/error/index.ts index 3005a5b1..53108a2a 100644 --- a/src/tests/error/index.ts +++ b/src/tests/error/index.ts @@ -31,4 +31,30 @@ describe('$error', async () => { expect($error.safeMessageFrom(thrown8, { expectedCauses: ['foo:bar'], default: 'Unknown error.' })).toBe('Thrown 6.'); expect($error.safeMessageFrom(thrown8, { expectedCauses: ['foo:bar:'], default: 'Unknown error.' })).toBe('Thrown 6.'); }); + test('.thrownByExpectedCause()', async () => { + const thrown1 = Error('Something is wrong.', { cause: 'foo' }); + expect($error.thrownByExpectedCause(thrown1, { expectedCauses: ['foo'] })).toBe(true); + + const thrown2 = Error('Something is wrong.', { cause: 'foo:bar' }); + expect($error.thrownByExpectedCause(thrown2, { expectedCauses: ['foo'] })).toBe(true); + + const thrown3 = Error('Something is wrong.'); + expect($error.thrownByExpectedCause(thrown3, { expectedCauses: ['foo'] })).toBe(false); + + const thrown4 = Error('Something is wrong.', { cause: 'foo' }); + expect($error.thrownByExpectedCause(thrown4, { expectedCauses: ['user', 'foo-bar'] })).toBe(false); + + const thrown5 = Error('Something is wrong.', { cause: 'foo-bar:baz' }); + expect($error.thrownByExpectedCause(thrown5, { expectedCauses: ['foo-bar'] })).toBe(true); + expect($error.thrownByExpectedCause(thrown5, { expectedCauses: ['foo-bar:'] })).toBe(true); + expect($error.thrownByExpectedCause(thrown5, { expectedCauses: [/^foo-bar:/u] })).toBe(true); + + const thrown6 = Error('Thrown 6.', { cause: 'foo:bar:baz' }); + const thrown7 = Error('Thrown 7.', { cause: thrown6 }); + const thrown8 = Error('Thrown 8.', { cause: thrown7 }); + expect($error.thrownByExpectedCause(thrown8, { expectedCauses: ['bar', 'foo', 'bar'] })).toBe(true); + expect($error.thrownByExpectedCause(thrown8, { expectedCauses: ['user', 'foo:bar'] })).toBe(false); + expect($error.thrownByExpectedCause(thrown8, { expectedCauses: [/^foo:bar:/u] })).toBe(true); + expect($error.thrownByExpectedCause(thrown8, { expectedCauses: [/^foo-bar:/u] })).toBe(false); + }); }); diff --git a/tsconfig.json b/tsconfig.json index 097bd4d8..d594cfe4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ * @note This entire file will be updated automatically. * @note Instead of editing here, please review `./tsconfig.mjs`. * - * Last generated using `./tsconfig.mjs` Feb 28, 2024 6:13 PM UTC. + * Last generated using `./tsconfig.mjs` Mar 1, 2024 10:35 AM UTC. */ { "include": ["./src/**/*", "./dev-types.d.ts"], diff --git a/wrangler.toml b/wrangler.toml index 5026abb1..e0d99cf9 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -7,7 +7,7 @@ # @note This entire file will be updated automatically. # @note Instead of editing here, please review `./wrangler.mjs`. # -# Last generated using `./wrangler.mjs` Feb 28, 2024 6:13 PM UTC. +# Last generated using `./wrangler.mjs` Mar 1, 2024 10:35 AM UTC. ## send_metrics = false