diff --git a/.gitignore b/.gitignore index 375fae9..ce9324a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules src/blurhash-decode.wasm dist *.tgz +coverage diff --git a/.npmignore b/.npmignore index bc284f8..d3d88ba 100644 --- a/.npmignore +++ b/.npmignore @@ -14,3 +14,5 @@ pnpm-lock.yaml rollup.config.js tsconfig.json spec +coverage +.nycrc diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000..0ae0f55 --- /dev/null +++ b/.nycrc @@ -0,0 +1,6 @@ +{ + "report-dir": "coverage", + "exclude": [ + "spec/" + ] +} diff --git a/package.json b/package.json index ed00980..221330c 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,15 @@ { "type": "module", "name": "@fries/blurhash-c-wasm", - "version": "0.1.2", + "version": "0.2.0", "description": "A WASM module using the Blurhash C library", "main": "dist/blurhash-c-wasm.cjs", "module": "dist/blurhash-c-wasm.js", - "types": "dist/library.d.ts", + "types": "dist/blurhash-c-wasm.d.ts", "scripts": { "build": "make && rollup -c rollup.config.js", "test": "jasmine", + "test:coverage": "c8 -r html pnpm run test", "prepublishOnly": "pnpm build && pnpm test" }, "author": "Fries", @@ -16,6 +17,7 @@ "@rollup/plugin-typescript": "^11.1.2", "@rollup/plugin-wasm": "^6.1.3", "@types/jasmine": "^4.3.5", + "c8": "^8.0.1", "jasmine": "^5.1.0", "rollup": "^3.28.0", "tslib": "^2.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b14d68..fe99378 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ devDependencies: '@types/jasmine': specifier: ^4.3.5 version: 4.3.5 + c8: + specifier: ^8.0.1 + version: 8.0.1 jasmine: specifier: ^5.1.0 version: 5.1.0 @@ -29,6 +32,10 @@ devDependencies: packages: + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -41,6 +48,27 @@ packages: wrap-ansi-cjs: /wrap-ansi@7.0.0 dev: true + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -99,6 +127,10 @@ packages: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} dev: true + /@types/istanbul-lib-coverage@2.0.4: + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: true + /@types/jasmine@4.3.5: resolution: {integrity: sha512-9YHUdvuNDDRJYXZwHqSsO72Ok0vmqoJbNn73ttyITQp/VA60SarnZ+MPLD37rJAhVoKp+9BWOvJP5tHIRfZylQ==} dev: true @@ -129,12 +161,47 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + /brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 dev: true + /c8@8.0.1: + resolution: {integrity: sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==} + engines: {node: '>=12'} + hasBin: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@istanbuljs/schema': 0.1.3 + find-up: 5.0.0 + foreground-child: 2.0.0 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.1.6 + rimraf: 3.0.2 + test-exclude: 6.0.0 + v8-to-istanbul: 9.1.0 + yargs: 17.7.2 + yargs-parser: 21.1.1 + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -146,6 +213,14 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -167,10 +242,31 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} dev: true + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /foreground-child@2.0.0: + resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} + engines: {node: '>=8.0.0'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 3.0.7 + dev: true + /foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -179,6 +275,10 @@ packages: signal-exit: 4.1.0 dev: true + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + /fsevents@2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -191,6 +291,11 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + /glob@10.3.3: resolution: {integrity: sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==} engines: {node: '>=16 || 14 >=14.17'} @@ -203,6 +308,22 @@ packages: path-scurry: 1.10.1 dev: true + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + /has@1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} engines: {node: '>= 0.4.0'} @@ -210,6 +331,21 @@ packages: function-bind: 1.1.1 dev: true + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + /is-core-module@2.13.0: resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} dependencies: @@ -225,6 +361,28 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true + /istanbul-lib-coverage@3.2.0: + resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.0 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-reports@3.1.6: + resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + /jackspeak@2.2.3: resolution: {integrity: sha512-pF0kfjmg8DJLxDrizHoCZGUFz4P4czQ3HyfW4BU0ffebYkzAVlBywp5zaxW/TM+r0sGbmrQdi8EQQVTJFxnGsQ==} engines: {node: '>=14'} @@ -246,11 +404,38 @@ packages: jasmine-core: 5.1.0 dev: true + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + /lru-cache@10.0.1: resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} engines: {node: 14 || >=16.14} dev: true + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + dependencies: + semver: 7.5.4 + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -263,6 +448,36 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dev: true + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -285,6 +500,11 @@ packages: engines: {node: '>=8.6'} dev: true + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + /resolve@1.22.4: resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} hasBin: true @@ -294,6 +514,13 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + /rollup@3.28.0: resolution: {integrity: sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} @@ -302,6 +529,14 @@ packages: fsevents: 2.3.2 dev: true + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -314,6 +549,10 @@ packages: engines: {node: '>=8'} dev: true + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + /signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -351,11 +590,27 @@ packages: ansi-regex: 6.0.1 dev: true + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} dev: true + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + /tslib@2.6.1: resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==} dev: true @@ -366,6 +621,15 @@ packages: hasBin: true dev: true + /v8-to-istanbul@9.1.0: + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.19 + '@types/istanbul-lib-coverage': 2.0.4 + convert-source-map: 1.9.0 + dev: true + /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -391,3 +655,39 @@ packages: string-width: 5.1.2 strip-ansi: 7.1.0 dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/rollup.config.js b/rollup.config.js index c83d0d6..2922736 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -9,7 +9,7 @@ const dirname = path.dirname(url.fileURLToPath(import.meta.url)); export default [ defineConfig({ plugins: [Typescript(), Wasm({ targetEnv: "auto-inline" })], - input: path.resolve(dirname, "src/library.ts"), + input: path.resolve(dirname, "src/blurhash-c-wasm.ts"), output: [ { name: "blurhash-c-wasm", @@ -22,7 +22,7 @@ export default [ }), defineConfig({ plugins: [Typescript(), Wasm({ targetEnv: "auto-inline" })], - input: path.resolve(dirname, "src/library.ts"), + input: path.resolve(dirname, "src/blurhash-c-wasm.ts"), output: [ { name: "blurhash-c-wasm", diff --git a/spec/librarySpec.js b/spec/librarySpec.js index 7038894..3086d80 100644 --- a/spec/librarySpec.js +++ b/spec/librarySpec.js @@ -3,48 +3,102 @@ import * as blurhash from "../dist/blurhash-c-wasm.js"; describe("blurhash-c-wasm tests", () => { + /** @type {import("../dist/blurhash-c-wasm").BlurhashInstanceType} */ + let instance; + beforeAll(async () => { - await blurhash.initWasm(); + instance = + /** @type {import("../dist/blurhash-c-wasm").BlurhashInstanceType} */ ( + /** @type {unknown} */ await blurhash.initWasm() + ); }); it("detects valid blurhash", () => { - expect(blurhash.isValidBlurhash("LEHLk~WB2yk8pyo0adR*.7kCMdnj")).toBe(true); - expect(blurhash.isValidBlurhash("LGF5]+Yk^6#M@-5c,1J5@[or[Q6.")).toBe(true); - expect(blurhash.isValidBlurhash("L6PZfSjE.AyE_3t7t7R**0o#DgR4")).toBe(true); - expect(blurhash.isValidBlurhash("LKO2:N%2Tw=w]~RBVZRi};RPxuwH")).toBe(true); + expect( + blurhash.isValidBlurhash(instance, "LEHLk~WB2yk8pyo0adR*.7kCMdnj") + ).toBe(true); + expect( + blurhash.isValidBlurhash(instance, "LGF5]+Yk^6#M@-5c,1J5@[or[Q6.") + ).toBe(true); + expect( + blurhash.isValidBlurhash(instance, "L6PZfSjE.AyE_3t7t7R**0o#DgR4") + ).toBe(true); + expect( + blurhash.isValidBlurhash(instance, "LKO2:N%2Tw=w]~RBVZRi};RPxuwH") + ).toBe(true); }); - it("generates valid blurhash images", () => { - expect(blurhash.decode("LEHLk~WB2yk8pyo0adR*.7kCMdnj", 1, 1)).toEqual( - Uint8ClampedArray.of(134, 164, 176, 255) - ); - expect(blurhash.decode("LGF5]+Yk^6#M@-5c,1J5@[or[Q6.", 1, 1)).toEqual( - Uint8ClampedArray.of(176, 118, 163, 255) - ); - expect(blurhash.decode("L6PZfSjE.AyE_3t7t7R**0o#DgR4", 1, 1)).toEqual( - Uint8ClampedArray.of(229, 228, 226, 255) - ); - expect(blurhash.decode("LKO2:N%2Tw=w]~RBVZRi};RPxuwH", 1, 1)).toEqual( - Uint8ClampedArray.of(251, 192, 161, 255) - ); + it("generates valid 1:1 blurhash images", () => { + expect( + blurhash.decode(instance, "LEHLk~WB2yk8pyo0adR*.7kCMdnj", 1, 1) + ).toEqual(Uint8ClampedArray.of(134, 164, 176, 255)); + expect( + blurhash.decode(instance, "LGF5]+Yk^6#M@-5c,1J5@[or[Q6.", 1, 1) + ).toEqual(Uint8ClampedArray.of(176, 118, 163, 255)); + expect( + blurhash.decode(instance, "L6PZfSjE.AyE_3t7t7R**0o#DgR4", 1, 1) + ).toEqual(Uint8ClampedArray.of(229, 228, 226, 255)); + expect( + blurhash.decode(instance, "LKO2:N%2Tw=w]~RBVZRi};RPxuwH", 1, 1) + ).toEqual(Uint8ClampedArray.of(251, 192, 161, 255)); + }); + + it("generates valid non 1:1 blurhash images", () => { + expect( + blurhash.decode(instance, "LEHLk~WB2yk8pyo0adR*.7kCMdnj", 1, 2) + ).toEqual(Uint8ClampedArray.of(134, 164, 176, 255, 119, 148, 161, 255)); + expect( + blurhash.decode(instance, "LGF5]+Yk^6#M@-5c,1J5@[or[Q6.", 1, 2) + ).toEqual(Uint8ClampedArray.of(176, 118, 163, 255, 137, 155, 177, 255)); + expect( + blurhash.decode(instance, "L6PZfSjE.AyE_3t7t7R**0o#DgR4", 1, 2) + ).toEqual(Uint8ClampedArray.of(229, 228, 226, 255, 226, 223, 223, 255)); + expect( + blurhash.decode(instance, "LKO2:N%2Tw=w]~RBVZRi};RPxuwH", 1, 2) + ).toEqual(Uint8ClampedArray.of(251, 192, 161, 255, 200, 187, 180, 255)); }); it("detects invalid blurhash", () => { - expect(blurhash.isValidBlurhash("1234")).toBe(false); - expect(blurhash.isValidBlurhash("sus")).toBe(false); + expect(blurhash.isValidBlurhash(instance, "1234")).toBe(false); + expect(blurhash.isValidBlurhash(instance, "sus")).toBe(false); + }); + + it("fails on invalid blurhash", () => { + const decodingHasFailed = "Decoding the Blurhash string has failed."; + expect(() => blurhash.decode(instance, "1234", 1, 1)).toThrow( + new Error(decodingHasFailed) + ); + expect(() => blurhash.decode(instance, "sus", 1, 1)).toThrow( + new Error(decodingHasFailed) + ); + }); + + it("don't accept less then the required parameters in the isValidBlurhash function", () => { + // @ts-expect-error + expect(() => blurhash.isValidBlurhash(undefined)).toThrow( + new Error("You are required to give the WASM Module.") + ); + // @ts-expect-error + expect(() => blurhash.isValidBlurhash(instance, undefined)).toThrow( + new Error("You are required to give the Blurhash string.") + ); }); it("don't accept less then the required paramaters in the decode function", () => { // @ts-expect-error expect(() => blurhash.decode(undefined)).toThrow( + new Error("You are required to give the WASM Module.") + ); + // @ts-expect-error + expect(() => blurhash.decode(instance, undefined)).toThrow( new Error("You are required to give the Blurhash string.") ); // @ts-expect-error - expect(() => blurhash.decode("test")).toThrow( + expect(() => blurhash.decode(instance, "test")).toThrow( new Error("You are required to give the width.") ); // @ts-expect-error - expect(() => blurhash.decode("test", 32)).toThrow( + expect(() => blurhash.decode(instance, "test", 32)).toThrow( new Error("You are required to give the height.") ); }); diff --git a/src/library.ts b/src/blurhash-c-wasm.ts similarity index 69% rename from src/library.ts rename to src/blurhash-c-wasm.ts index 0fe25e7..6789764 100644 --- a/src/library.ts +++ b/src/blurhash-c-wasm.ts @@ -1,7 +1,7 @@ // @ts-ignore import wasm from "./blurhash-decode.wasm"; -type InstanceType = { +export type BlurhashInstanceType = { memory: WebAssembly.Memory; malloc: (bytes: number) => number; free: (ptr: number) => void; @@ -16,16 +16,16 @@ type InstanceType = { isValidBlurhash: (stringPtr: number) => number; }; -const wasmInitializationError = "The WASM module has not been initialized"; const failed = -1; const youAreRequiredToGiveThe = (thing: string) => `You are required to give the ${thing}.`; -const blurHashStringRequiredError = youAreRequiredToGiveThe("Blurhash string"); -let wasmInstance: WebAssembly.Instance | undefined; -let instance: InstanceType | undefined; +const blurhashStringRequiredError = youAreRequiredToGiveThe("Blurhash string"); +const wasmMissingError = youAreRequiredToGiveThe("WASM Module"); -function encodeStringToWasmArray(string: string): number { - if (!instance) throw Error(wasmInitializationError); +function encodeStringToWasmArray( + instance: BlurhashInstanceType, + string: string +): number { const encoder = new TextEncoder(); const encodedString = encoder.encode(string); const stringPtr: number = instance.malloc(encodedString.byteLength + 1); @@ -42,49 +42,55 @@ function encodeStringToWasmArray(string: string): number { } /** - * Initialize the WASM instance. This is to support browsers without top level await. + * Initialize a WASM instance. + * @returns A Blurhash WASM instance. */ -export async function initWasm() { - wasmInstance = await WebAssembly.instantiate( +export async function initWasm(): Promise { + const wasmInstance = await WebAssembly.instantiate( (await wasm()) as WebAssembly.Module, { env: { // stub for a emscripten import + /* c8 ignore next */ emscripten_notify_memory_growth: () => {}, }, } ); - instance = wasmInstance.exports as InstanceType; + return wasmInstance.exports as unknown as BlurhashInstanceType; } /** * Decode a Blurhash string into a Pixel Array that you can use to generate a Blurhash image. + * @param instance A Blurhash WASM instance. * @param blurhashString A valid Blurhash string. * @param width The width of the image you want to generate. * @param height The height of the image you want to generate. * @returns A `Uint8ClampedArray` pixel array. */ export function decode( + instance: BlurhashInstanceType, blurhashString: string, width: number, height: number ): Uint8ClampedArray { - if (!instance) throw Error(wasmInitializationError); + if (!instance) throw Error(wasmMissingError); if (!blurhashString) { - throw Error(blurHashStringRequiredError); + throw Error(blurhashStringRequiredError); } + if (!width) { throw Error(youAreRequiredToGiveThe("width")); } + if (!height) { throw Error(youAreRequiredToGiveThe("height")); } - const pixelsPtrSize = width * 4 * width; + const pixelsPtrSize = width * 4 * height; const pixelsPtr = instance.malloc(pixelsPtrSize); - const stringPtr = encodeStringToWasmArray(blurhashString); + const stringPtr = encodeStringToWasmArray(instance, blurhashString); const result = instance.decodeToArray( stringPtr, @@ -118,14 +124,17 @@ export function decode( * @param blurhashString A Blurhash string. * @returns `true` if your string is valid, `false` elsewise. */ -export function isValidBlurhash(blurhashString: string) { - if (!instance) throw Error(wasmInitializationError); +export function isValidBlurhash( + instance: BlurhashInstanceType, + blurhashString: string +) { + if (!instance) throw Error(wasmMissingError); if (!blurhashString) { - throw Error(blurHashStringRequiredError); + throw Error(blurhashStringRequiredError); } - const stringPtr = encodeStringToWasmArray(blurhashString); + const stringPtr = encodeStringToWasmArray(instance, blurhashString); const result: number = instance.isValidBlurhash(stringPtr); instance.free(stringPtr); return Boolean(result);