diff --git a/package.json b/package.json index dd284cc..6e814fe 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "url": "https://github.com/webdeveric/utils/issues" }, "homepage": "https://github.com/webdeveric/utils/#readme", - "packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af", + "packageManager": "pnpm@10.5.0+sha512.11106a5916c7406fe4b8cb8e3067974b8728f47308a4f5ac5e850304afa6f57e2847d7950dfe78877d8d36bfb401d381c4215db3a4c3547ffa63c14333a6fa51", "scripts": { "clean": "rimraf ./dist/", "prebuild": "pnpm clean", @@ -88,7 +88,7 @@ "@commitlint/config-conventional": "^19.7.1", "@commitlint/types": "^19.5.0", "@types/node": "^22.13.5", - "@vitest/coverage-v8": "^3.0.6", + "@vitest/coverage-v8": "^3.0.7", "@webdeveric/eslint-config-ts": "^0.11.0", "@webdeveric/prettier-config": "^0.3.0", "commitlint": "^19.7.1", @@ -107,11 +107,6 @@ "semantic-release": "^24.2.3", "typescript": "^5.7.3", "validate-package-exports": "^0.8.0", - "vitest": "^3.0.6" - }, - "pnpm": { - "overrides": { - "esbuild@<=0.24.2": ">=0.25.0" - } + "vitest": "^3.0.7" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ae5611..43696f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,9 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -overrides: - esbuild@<=0.24.2: '>=0.25.0' - importers: .: @@ -21,8 +18,8 @@ importers: specifier: ^22.13.5 version: 22.13.5 '@vitest/coverage-v8': - specifier: ^3.0.6 - version: 3.0.6(vitest@3.0.6(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0)) + specifier: ^3.0.7 + version: 3.0.7(vitest@3.0.7(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0)) '@webdeveric/eslint-config-ts': specifier: ^0.11.0 version: 0.11.0(eslint@8.57.1)(typescript@5.7.3) @@ -78,8 +75,8 @@ importers: specifier: ^0.8.0 version: 0.8.0 vitest: - specifier: ^3.0.6 - version: 3.0.6(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0) + specifier: ^3.0.7 + version: 3.0.7(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0) packages: @@ -1036,20 +1033,20 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@vitest/coverage-v8@3.0.6': - resolution: {integrity: sha512-JRTlR8Bw+4BcmVTICa7tJsxqphAktakiLsAmibVLAWbu1lauFddY/tXeM6sAyl1cgkPuXtpnUgaCPhTdz1Qapg==} + '@vitest/coverage-v8@3.0.7': + resolution: {integrity: sha512-Av8WgBJLTrfLOer0uy3CxjlVuWK4CzcLBndW1Nm2vI+3hZ2ozHututkfc7Blu1u6waeQ7J8gzPK/AsBRnWA5mQ==} peerDependencies: - '@vitest/browser': 3.0.6 - vitest: 3.0.6 + '@vitest/browser': 3.0.7 + vitest: 3.0.7 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@3.0.6': - resolution: {integrity: sha512-zBduHf/ja7/QRX4HdP1DSq5XrPgdN+jzLOwaTq/0qZjYfgETNFCKf9nOAp2j3hmom3oTbczuUzrzg9Hafh7hNg==} + '@vitest/expect@3.0.7': + resolution: {integrity: sha512-QP25f+YJhzPfHrHfYHtvRn+uvkCFCqFtW9CktfBxmB+25QqWsx7VB2As6f4GmwllHLDhXNHvqedwhvMmSnNmjw==} - '@vitest/mocker@3.0.6': - resolution: {integrity: sha512-KPztr4/tn7qDGZfqlSPQoF2VgJcKxnDNhmfR3VgZ6Fy1bO8T9Fc1stUiTXtqz0yG24VpD00pZP5f8EOFknjNuQ==} + '@vitest/mocker@3.0.7': + resolution: {integrity: sha512-qui+3BLz9Eonx4EAuR/i+QlCX6AUZ35taDQgwGkK/Tw6/WgwodSrjN1X2xf69IA/643ZX5zNKIn2svvtZDrs4w==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 @@ -1059,20 +1056,20 @@ packages: vite: optional: true - '@vitest/pretty-format@3.0.6': - resolution: {integrity: sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==} + '@vitest/pretty-format@3.0.7': + resolution: {integrity: sha512-CiRY0BViD/V8uwuEzz9Yapyao+M9M008/9oMOSQydwbwb+CMokEq3XVaF3XK/VWaOK0Jm9z7ENhybg70Gtxsmg==} - '@vitest/runner@3.0.6': - resolution: {integrity: sha512-JopP4m/jGoaG1+CBqubV/5VMbi7L+NQCJTu1J1Pf6YaUbk7bZtaq5CX7p+8sY64Sjn1UQ1XJparHfcvTTdu9cA==} + '@vitest/runner@3.0.7': + resolution: {integrity: sha512-WeEl38Z0S2ZcuRTeyYqaZtm4e26tq6ZFqh5y8YD9YxfWuu0OFiGFUbnxNynwLjNRHPsXyee2M9tV7YxOTPZl2g==} - '@vitest/snapshot@3.0.6': - resolution: {integrity: sha512-qKSmxNQwT60kNwwJHMVwavvZsMGXWmngD023OHSgn873pV0lylK7dwBTfYP7e4URy5NiBCHHiQGA9DHkYkqRqg==} + '@vitest/snapshot@3.0.7': + resolution: {integrity: sha512-eqTUryJWQN0Rtf5yqCGTQWsCFOQe4eNz5Twsu21xYEcnFJtMU5XvmG0vgebhdLlrHQTSq5p8vWHJIeJQV8ovsA==} - '@vitest/spy@3.0.6': - resolution: {integrity: sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q==} + '@vitest/spy@3.0.7': + resolution: {integrity: sha512-4T4WcsibB0B6hrKdAZTM37ekuyFZt2cGbEGd2+L0P8ov15J1/HUsUaqkXEQPNAWr4BtPPe1gI+FYfMHhEKfR8w==} - '@vitest/utils@3.0.6': - resolution: {integrity: sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==} + '@vitest/utils@3.0.7': + resolution: {integrity: sha512-xePVpCRfooFX3rANQjwoditoXgWb1MaFbzmGuPP59MK6i13mrnDw/yEIyJudLeW6/38mCNcwCiJIGmpDPibAIg==} '@webdeveric/eslint-config-ts@0.11.0': resolution: {integrity: sha512-QClsqZWRhESB1CT3uZl2As2INzNq/VD3bQRwhL9sUmxHcXfhcnGXFBwt+4UseDIni5u9RXtVfGaR0kUj+3tbpg==} @@ -1880,8 +1877,8 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} form-data@4.0.2: @@ -3183,8 +3180,8 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfdc@1.4.1: @@ -3689,13 +3686,13 @@ packages: engines: {node: '>=20.17.0'} hasBin: true - vite-node@3.0.6: - resolution: {integrity: sha512-s51RzrTkXKJrhNbUzQRsarjmAae7VmMPAsRT7lppVpIg6mK3zGthP9Hgz0YQQKuNcF+Ii7DfYk3Fxz40jRmePw==} + vite-node@3.0.7: + resolution: {integrity: sha512-2fX0QwX4GkkkpULXdT1Pf4q0tC1i1lFOyseKoonavXUNlQ77KpW2XqBGGNIm/J4Ows4KxgGJzDguYVPKwG/n5A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@6.1.1: - resolution: {integrity: sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==} + vite@6.2.0: + resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -3734,16 +3731,16 @@ packages: yaml: optional: true - vitest@3.0.6: - resolution: {integrity: sha512-/iL1Sc5VeDZKPDe58oGK4HUFLhw6b5XdY1MYawjuSaDA4sEfYlY9HnS6aCEG26fX+MgUi7MwlduTBHHAI/OvMA==} + vitest@3.0.7: + resolution: {integrity: sha512-IP7gPK3LS3Fvn44x30X1dM9vtawm0aesAa2yBIZ9vQf+qB69NXC5776+Qmcr7ohUXIQuLhk7xQR0aSUIDPqavg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.0.6 - '@vitest/ui': 3.0.6 + '@vitest/browser': 3.0.7 + '@vitest/ui': 3.0.7 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -4927,7 +4924,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitest/coverage-v8@3.0.6(vitest@3.0.6(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0))': + '@vitest/coverage-v8@3.0.7(vitest@3.0.7(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -4941,47 +4938,47 @@ snapshots: std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.0.6(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0) + vitest: 3.0.7(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0) transitivePeerDependencies: - supports-color - '@vitest/expect@3.0.6': + '@vitest/expect@3.0.7': dependencies: - '@vitest/spy': 3.0.6 - '@vitest/utils': 3.0.6 + '@vitest/spy': 3.0.7 + '@vitest/utils': 3.0.7 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.0.6(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0))': + '@vitest/mocker@3.0.7(vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0))': dependencies: - '@vitest/spy': 3.0.6 + '@vitest/spy': 3.0.7 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) + vite: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) - '@vitest/pretty-format@3.0.6': + '@vitest/pretty-format@3.0.7': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.0.6': + '@vitest/runner@3.0.7': dependencies: - '@vitest/utils': 3.0.6 + '@vitest/utils': 3.0.7 pathe: 2.0.3 - '@vitest/snapshot@3.0.6': + '@vitest/snapshot@3.0.7': dependencies: - '@vitest/pretty-format': 3.0.6 + '@vitest/pretty-format': 3.0.7 magic-string: 0.30.17 pathe: 2.0.3 - '@vitest/spy@3.0.6': + '@vitest/spy@3.0.7': dependencies: tinyspy: 3.0.2 - '@vitest/utils@3.0.6': + '@vitest/utils@3.0.7': dependencies: - '@vitest/pretty-format': 3.0.6 + '@vitest/pretty-format': 3.0.7 loupe: 3.1.3 tinyrainbow: 2.0.0 @@ -5920,7 +5917,7 @@ snapshots: fastq@1.19.0: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 fdir@6.4.3(picomatch@4.0.2): optionalDependencies: @@ -5985,7 +5982,7 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.3.0: + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 @@ -6108,7 +6105,7 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 @@ -6117,7 +6114,7 @@ snapshots: glob@11.0.1: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 4.1.0 minimatch: 10.0.1 minipass: 7.1.2 @@ -7271,7 +7268,7 @@ snapshots: retry@0.12.0: {} - reusify@1.0.4: {} + reusify@1.1.0: {} rfdc@1.4.1: {} @@ -7849,13 +7846,13 @@ snapshots: transitivePeerDependencies: - supports-color - vite-node@3.0.6(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0): + vite-node@3.0.7(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) + vite: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) transitivePeerDependencies: - '@types/node' - jiti @@ -7870,7 +7867,7 @@ snapshots: - tsx - yaml - vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0): + vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0): dependencies: esbuild: 0.25.0 postcss: 8.5.3 @@ -7881,15 +7878,15 @@ snapshots: jiti: 2.4.2 yaml: 2.7.0 - vitest@3.0.6(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0): + vitest@3.0.7(@types/node@22.13.5)(jiti@2.4.2)(jsdom@26.0.0)(yaml@2.7.0): dependencies: - '@vitest/expect': 3.0.6 - '@vitest/mocker': 3.0.6(vite@6.1.1(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0)) - '@vitest/pretty-format': 3.0.6 - '@vitest/runner': 3.0.6 - '@vitest/snapshot': 3.0.6 - '@vitest/spy': 3.0.6 - '@vitest/utils': 3.0.6 + '@vitest/expect': 3.0.7 + '@vitest/mocker': 3.0.7(vite@6.2.0(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0)) + '@vitest/pretty-format': 3.0.7 + '@vitest/runner': 3.0.7 + '@vitest/snapshot': 3.0.7 + '@vitest/spy': 3.0.7 + '@vitest/utils': 3.0.7 chai: 5.2.0 debug: 4.4.0 expect-type: 1.1.0 @@ -7900,8 +7897,8 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.1.1(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) - vite-node: 3.0.6(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) + vite: 6.2.0(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) + vite-node: 3.0.7(@types/node@22.13.5)(jiti@2.4.2)(yaml@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.13.5 diff --git a/src/assertion/assertPredicate.ts b/src/assertion/assertPredicate.ts new file mode 100644 index 0000000..e4651f5 --- /dev/null +++ b/src/assertion/assertPredicate.ts @@ -0,0 +1,15 @@ +import { getError } from './getError.js'; + +import type { TypePredicateFn } from '../types/functions.js'; + +export type ErrorFactory = (input: unknown) => Error; + +export function assertPredicate( + input: unknown, + predicate: TypePredicateFn, + error: string | Error | ErrorFactory = 'invalid input', +): asserts input is T { + if (!predicate(input)) { + throw typeof error === 'function' ? error(input) : getError(error); + } +} diff --git a/src/assertion/index.ts b/src/assertion/index.ts index 531fddc..c8f2a84 100644 --- a/src/assertion/index.ts +++ b/src/assertion/index.ts @@ -43,3 +43,4 @@ export * from './assertIsSymbol.js'; export * from './assertIsSymbolArray.js'; export * from './assertIsUndefined.js'; export * from './assertIsUndefinedArray.js'; +export * from './assertPredicate.js'; diff --git a/test/assertion/assertPredicate.test.ts b/test/assertion/assertPredicate.test.ts new file mode 100644 index 0000000..670985c --- /dev/null +++ b/test/assertion/assertPredicate.test.ts @@ -0,0 +1,36 @@ +import { AssertionError } from 'node:assert'; + +import { describe, it, expect } from 'vitest'; + +import { assertPredicate } from '../../src/assertion/assertPredicate.js'; +import { isInteger } from '../../src/predicate/isInteger.js'; + +describe('assertPredicate', () => { + it('asserts that the input satisfies the predicate', () => { + expect(() => { + assertPredicate(1, isInteger); + }).not.toThrow(); + + expect(() => { + assertPredicate('test', isInteger); + }).toThrow(); + }); + + it('Can customize the error', () => { + expect(() => { + assertPredicate('test', isInteger, 'input is not an integer'); + }).toThrow('input is not an integer'); + + expect(() => { + assertPredicate('test', isInteger, new Error('input is not an integer')); + }).toThrow('input is not an integer'); + + expect(() => { + assertPredicate( + 'test', + isInteger, + (input) => new AssertionError({ message: 'input is not an integer', actual: input }), + ); + }).toThrowError(AssertionError); + }); +});