From a5d5d7c1f514371f715d101237ce64305254604f Mon Sep 17 00:00:00 2001 From: JounQin Date: Sun, 31 Jul 2022 13:01:24 +0800 Subject: [PATCH] feat: add polyfill and ponyfill support (#5) --- .changeset/rotten-pumpkins-repeat.md | 5 ++ README.md | 27 +++++- package.json | 29 ++++-- pnpm-lock.yaml | 128 +++++++++++++-------------- src/browser.ts | 4 +- src/polyfill.ts | 6 ++ src/ponyfill.ts | 82 +++++++++++++++++ test/basic.spec.ts | 25 ------ test/decoding.spec.ts | 11 +++ test/encoding.spec.ts | 11 +++ test/ponyfill.spec.ts | 13 +++ tsconfig.json | 3 +- vitest.config.ts | 13 ++- 13 files changed, 249 insertions(+), 108 deletions(-) create mode 100644 .changeset/rotten-pumpkins-repeat.md create mode 100644 src/polyfill.ts create mode 100644 src/ponyfill.ts delete mode 100644 test/basic.spec.ts create mode 100644 test/decoding.spec.ts create mode 100644 test/encoding.spec.ts create mode 100644 test/ponyfill.spec.ts diff --git a/.changeset/rotten-pumpkins-repeat.md b/.changeset/rotten-pumpkins-repeat.md new file mode 100644 index 0000000..06116a9 --- /dev/null +++ b/.changeset/rotten-pumpkins-repeat.md @@ -0,0 +1,5 @@ +--- +"ab64": minor +--- + +feat: add polyfill and ponyfill support diff --git a/README.md b/README.md index a31fb18..017b947 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,16 @@ [![Code Style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) [![changesets](https://img.shields.io/badge/maintained%20with-changesets-176de3.svg)](https://github.com/changesets/changesets) -The smallest and fastest Base64 implementation in JavaScript +The smallest and fastest Base64 implementation in JavaScript based on `atob` and `btoa` from browser native or `Buffer` from node ## TOC - [Usage](#usage) - [Install](#install) - [API](#api) + - [Basic](#basic) + - [Polyfill](#polyfill) + - [Ponyfill](#ponyfill) - [Sponsors](#sponsors) - [Backers](#backers) - [Changelog](#changelog) @@ -42,6 +45,8 @@ npm i ab64 ### API +#### Basic + ```js import { decode, decodeUrl, encode, encodeUrl } from 'ab64' @@ -62,6 +67,26 @@ decode('5bCP6aO85by+') // 小飼弾 decodeUrl('5bCP6aO85by-') // 小飼弾 ``` +#### Polyfill + +If you're running on a non Node environment where `atob` and `btoa` could be unavailable, you may want to include the `polyfill` manually + +```js +import 'ab64/polyfill' + +// same as above then +``` + +#### Ponyfill + +`atob` and `btoa` are also available exported as `ab64/ponyfill` which does not polyfill by default + +```js +import { atob, btoa } from 'ab64/ponyfill' + +// same as browser native +``` + ## Sponsors | 1stG | RxTS | UnTS | diff --git a/package.json b/package.json index 852933d..59a6aea 100644 --- a/package.json +++ b/package.json @@ -38,11 +38,14 @@ }, "funding": "https://opencollective.com/unts", "license": "MIT", - "packageManager": "pnpm@7.7.0", + "packageManager": "pnpm@7.7.1", "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, - "exports": "./src/index.ts", + "exports": { + ".": "./src/index.ts", + "./ponyfill": "./src/ponyfill.ts" + }, "files": [ "lib", "!**/*.tsbuildinfo" @@ -91,9 +94,9 @@ "@changesets/cli": "^2.24.1", "@pkgr/webpack": "^3.2.0", "@pkgr/webpack-mdx": "^2.0.3", - "@size-limit/preset-small-lib": "^7.0.8", + "@size-limit/preset-small-lib": "^8.0.0", "@types/mdx": "^2.0.2", - "@types/node": "^18.6.2", + "@types/node": "^18.6.3", "@types/react": "^18.0.15", "@types/react-dom": "^18.0.6", "@types/web": "^0.0.70", @@ -105,7 +108,7 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.3.0", "sirv-cli": "^2.0.2", - "size-limit": "^7.0.8", + "size-limit": "^8.0.0", "type-coverage": "^2.22.0", "typescript": "4.7.4", "unplugin-auto-import": "^0.10.3", @@ -115,10 +118,14 @@ "main": "./lib/index.cjs", "module": "./lib/index.js", "exports": { - "types": "./lib/index.d.ts", - "browser": "./lib/browser.js", - "import": "./lib/index.js", - "require": "./lib/index.cjs" + ".": { + "types": "./lib/index.d.ts", + "browser": "./lib/browser.js", + "import": "./lib/index.js", + "require": "./lib/index.cjs" + }, + "./polyfill": "./lib/polyfill.js", + "./ponyfill": "./lib/ponyfill.js" }, "types": "./lib/index.d.ts" }, @@ -126,6 +133,10 @@ { "path": "lib/browser.js", "limit": "280B" + }, + { + "path": "lib/ponyfill.js", + "limit": "520B" } ], "typeCoverage": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a131b11..0bd25c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,9 +7,9 @@ specifiers: '@changesets/cli': ^2.24.1 '@pkgr/webpack': ^3.2.0 '@pkgr/webpack-mdx': ^2.0.3 - '@size-limit/preset-small-lib': ^7.0.8 + '@size-limit/preset-small-lib': ^8.0.0 '@types/mdx': ^2.0.2 - '@types/node': ^18.6.2 + '@types/node': ^18.6.3 '@types/react': ^18.0.15 '@types/react-dom': ^18.0.6 '@types/web': ^0.0.70 @@ -21,7 +21,7 @@ specifiers: react-dom: ^18.2.0 react-router-dom: ^6.3.0 sirv-cli: ^2.0.2 - size-limit: ^7.0.8 + size-limit: ^8.0.0 tslib: ^2.4.0 type-coverage: ^2.22.0 typescript: 4.7.4 @@ -38,9 +38,9 @@ devDependencies: '@changesets/cli': 2.24.1 '@pkgr/webpack': 3.2.0_uag5zxiphkyue3uketwavwm4li '@pkgr/webpack-mdx': 2.0.3_wz2jf2tymzn422t44b5paupauy - '@size-limit/preset-small-lib': 7.0.8_size-limit@7.0.8 + '@size-limit/preset-small-lib': 8.0.0_size-limit@8.0.0 '@types/mdx': 2.0.2 - '@types/node': 18.6.2 + '@types/node': 18.6.3 '@types/react': 18.0.15 '@types/react-dom': 18.0.6 '@types/web': 0.0.70 @@ -52,7 +52,7 @@ devDependencies: react-dom: 18.2.0_react@18.2.0 react-router-dom: 6.3.0_biqbaboplfbrettd7655fr4n2y sirv-cli: 2.0.2 - size-limit: 7.0.8 + size-limit: 8.0.0 type-coverage: 2.22.0_typescript@4.7.4 typescript: 4.7.4 unplugin-auto-import: 0.10.3_hwlw6yqdlmgvyf5pwan5vjfl2a @@ -321,7 +321,7 @@ packages: prettier: '>=1.18.0' dependencies: '@1stg/config': 0.2.0 - '@prettier/plugin-pug': 2.1.1_prettier@2.7.1 + '@prettier/plugin-pug': 2.2.0_prettier@2.7.1 '@prettier/plugin-ruby': 3.2.0 '@prettier/plugin-xml': 2.2.0 prettier: 2.7.1 @@ -329,7 +329,7 @@ packages: prettier-plugin-pkg: 0.16.1_prettier@2.7.1 prettier-plugin-properties: 0.1.0_prettier@2.7.1 prettier-plugin-sh: 0.12.8_prettier@2.7.1 - prettier-plugin-stylus: 0.0.1-beta.3 + prettier-plugin-stylus: 0.0.1-beta.4 prettier-plugin-svelte: 2.7.0_o3ioganyptcsrh6x4hnxvjkpqi prettier-plugin-toml: 0.3.1 transitivePeerDependencies: @@ -2280,10 +2280,10 @@ packages: '@commitlint/execute-rule': 17.0.0 '@commitlint/resolve-extends': 17.0.3 '@commitlint/types': 17.0.0 - '@types/node': 18.6.2 + '@types/node': 18.6.3 chalk: 4.1.2 cosmiconfig: 7.0.1 - cosmiconfig-typescript-loader: 2.0.2_cyi3jronpmwutj7hw2n2p72xty + cosmiconfig-typescript-loader: 2.0.2_e2tlcjkk7tlngjdlhzx5hjlnv4 lodash: 4.17.21 resolve-from: 5.0.0 typescript: 4.7.4 @@ -3087,8 +3087,8 @@ packages: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true - /@prettier/plugin-pug/2.1.1_prettier@2.7.1: - resolution: {integrity: sha512-YSeE1Z1BCR/ylKv9WE66mvS0OqXaNRoRFFz/VtLoHeU98QZSljUUehyql7Se5NJSUhRSEbkpla42sPu8Ms6Epg==} + /@prettier/plugin-pug/2.2.0_prettier@2.7.1: + resolution: {integrity: sha512-W5CIXtMauz6xB4b0TeBW1Jxtk2ArggKMwqDHdGOfQ9ahABsttg2YZoQgLytETuo8Gjq0QkaGhAGD1pfDaUOZUQ==} engines: {node: '>=14.6.0', npm: '>=6.0.0'} peerDependencies: prettier: ^2.3.0 @@ -3249,35 +3249,35 @@ packages: engines: {node: '>=4'} dev: true - /@size-limit/esbuild/7.0.8_size-limit@7.0.8: - resolution: {integrity: sha512-AzCrxJJThDvHrBNoolebYVgXu46c6HuS3fOxoXr3V0YWNM0qz81z5F3j7RruzboZnls8ZgME4WrH6GM5rB9gtA==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /@size-limit/esbuild/8.0.0_size-limit@8.0.0: + resolution: {integrity: sha512-du3gGKAzeh8fKEbqG0PcHKyyilpHTC+z/e6r0a82G+9KMCQd2PBHfOrTyN+of8u6EZXkrp0zdwgr84nKLp1nPg==} + engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} peerDependencies: - size-limit: 7.0.8 + size-limit: 8.0.0 dependencies: esbuild: 0.14.51 nanoid: 3.3.4 - size-limit: 7.0.8 + size-limit: 8.0.0 dev: true - /@size-limit/file/7.0.8_size-limit@7.0.8: - resolution: {integrity: sha512-1KeFQuMXIXAH/iELqIX7x+YNYDFvzIvmxcp9PrdwEoSNL0dXdaDIo9WE/yz8xvOmUcKaLfqbWkL75DM0k91WHQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /@size-limit/file/8.0.0_size-limit@8.0.0: + resolution: {integrity: sha512-xd4bBk/YyezsMQfpi2V3/blodCuPNVF5UwMd4L9LxBvon0PK4C1+3zBXxZpvN7AcMvPbJ8RUMS+iHpD4KcwaOg==} + engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} peerDependencies: - size-limit: 7.0.8 + size-limit: 8.0.0 dependencies: - semver: 7.3.5 - size-limit: 7.0.8 + semver: 7.3.7 + size-limit: 8.0.0 dev: true - /@size-limit/preset-small-lib/7.0.8_size-limit@7.0.8: - resolution: {integrity: sha512-CT8nIYA/c2CSD+X4rAUgwqYccQMahJ6rBnaZxvi3YKFdkXIbuGNXHNjHsYaFksgwG9P4UjG/unyO5L73f3zQBw==} + /@size-limit/preset-small-lib/8.0.0_size-limit@8.0.0: + resolution: {integrity: sha512-6Q2fJFuPalvANQIoxYAmQLGk1Rxerw/qtqpEi1dhF9czfWxtYxKwlVQgsE/d0j5eum/N7iJxioDeKZQJjzKPrQ==} peerDependencies: - size-limit: 7.0.8 + size-limit: 8.0.0 dependencies: - '@size-limit/esbuild': 7.0.8_size-limit@7.0.8 - '@size-limit/file': 7.0.8_size-limit@7.0.8 - size-limit: 7.0.8 + '@size-limit/esbuild': 8.0.0_size-limit@8.0.0 + '@size-limit/file': 8.0.0_size-limit@8.0.0 + size-limit: 8.0.0 dev: true /@soda/friendly-errors-webpack-plugin/1.8.1_webpack@5.74.0: @@ -3514,13 +3514,13 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/bonjour/3.5.10: resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/chai-subset/1.3.3: @@ -3536,20 +3536,20 @@ packages: /@types/concat-stream/2.0.0: resolution: {integrity: sha512-t3YCerNM7NTVjLuICZo5gYAXYoDvpuuTceCcFQWcDQz26kxUR5uIWolxbIR5jRNIXpMqhOpW/b8imCR1LEmuJw==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/connect-history-api-fallback/1.3.5: resolution: {integrity: sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==} dependencies: '@types/express-serve-static-core': 4.17.30 - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/connect/3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/debug/4.1.7: @@ -3599,7 +3599,7 @@ packages: /@types/express-serve-static-core/4.17.30: resolution: {integrity: sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -3616,14 +3616,14 @@ packages: /@types/fs-extra/8.1.2: resolution: {integrity: sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/glob/7.2.0: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 3.0.5 - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/hast/2.3.4: @@ -3639,7 +3639,7 @@ packages: /@types/http-proxy/1.17.9: resolution: {integrity: sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/is-ci/3.0.0: @@ -3667,7 +3667,7 @@ packages: /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/mdast/3.0.10: @@ -3704,8 +3704,8 @@ packages: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: true - /@types/node/18.6.2: - resolution: {integrity: sha512-KcfkBq9H4PI6Vpu5B/KoPeuVDAbmi+2mDBqGPGUgoL7yXQtcWGu2vJWmmRkneWK3Rh0nIAX192Aa87AqKHYChQ==} + /@types/node/18.6.3: + resolution: {integrity: sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg==} dev: true /@types/normalize-package-data/2.4.1: @@ -3745,13 +3745,13 @@ packages: /@types/resolve/1.17.1: resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/retry/0.12.0: @@ -3776,13 +3776,13 @@ packages: resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} dependencies: '@types/mime': 3.0.0 - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/sockjs/0.3.33: resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@types/supports-color/8.1.1: @@ -3804,7 +3804,7 @@ packages: /@types/ws/8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 dev: true /@typescript-eslint/eslint-plugin/5.31.0_d5zwcxr4bwkhmuo464cb3a2puu: @@ -5601,16 +5601,16 @@ packages: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: true - /cosmiconfig-typescript-loader/2.0.2_cyi3jronpmwutj7hw2n2p72xty: + /cosmiconfig-typescript-loader/2.0.2_e2tlcjkk7tlngjdlhzx5hjlnv4: resolution: {integrity: sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@types/node': '*' typescript: '>=3' dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 cosmiconfig: 7.0.1 - ts-node: 10.9.1_cyi3jronpmwutj7hw2n2p72xty + ts-node: 10.9.1_e2tlcjkk7tlngjdlhzx5hjlnv4 typescript: 4.7.4 transitivePeerDependencies: - '@swc/core' @@ -9470,7 +9470,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -9479,7 +9479,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.6.2 + '@types/node': 18.6.3 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -12942,8 +12942,8 @@ packages: synckit: 0.8.1 dev: true - /prettier-plugin-stylus/0.0.1-beta.3: - resolution: {integrity: sha512-/Y5yKdUTUtAtI7JRzyA+w+JQWy5xUEeWKPOunMW5X+m8QHzhrFmOncHw643Y2BAgpUaQgO5zSJT+aZiYzvb+sA==} + /prettier-plugin-stylus/0.0.1-beta.4: + resolution: {integrity: sha512-70z1BNs5kxoPCf2sIMBom3bQ7JBYW8hEvfjsuU2TkZ5JYpA0cnAbttxpW862STVYPoavMX4BW2XY/9KPlnKi6A==} dependencies: prettier: 2.7.1 stylus: 0.57.0 @@ -14770,14 +14770,6 @@ packages: hasBin: true dev: true - /semver/7.3.5: - resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - /semver/7.3.7: resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} engines: {node: '>=10'} @@ -14935,9 +14927,9 @@ packages: totalist: 3.0.0 dev: true - /size-limit/7.0.8: - resolution: {integrity: sha512-3h76c9E0e/nNhYLSR7IBI/bSoXICeo7EYkYjlyVqNIsu7KvN/PQmMbIXeyd2QKIF8iZKhaiZQoXLkGWbyPDtvQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /size-limit/8.0.0: + resolution: {integrity: sha512-344dzCZZiTz+N0WS801SNG/qd8MCa6dzJhsj8gLQg4JlmddwUdi/Ol0HfliEVL7jgtOz8fNgDeB2+14xwalvVA==} + engines: {node: ^14.0.0 || ^16.0.0 || >=18.0.0} hasBin: true dependencies: bytes-iec: 3.1.1 @@ -16013,7 +16005,7 @@ packages: resolution: {integrity: sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==} dev: true - /ts-node/10.9.1_cyi3jronpmwutj7hw2n2p72xty: + /ts-node/10.9.1_e2tlcjkk7tlngjdlhzx5hjlnv4: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -16032,7 +16024,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 18.6.2 + '@types/node': 18.6.3 acorn: 8.8.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -16263,7 +16255,7 @@ packages: '@types/concat-stream': 2.0.0 '@types/debug': 4.1.7 '@types/is-empty': 1.2.1 - '@types/node': 18.6.2 + '@types/node': 18.6.3 '@types/unist': 2.0.6 concat-stream: 2.0.0 debug: 4.3.4 @@ -16769,7 +16761,7 @@ packages: dependencies: '@types/chai': 4.3.1 '@types/chai-subset': 1.3.3 - '@types/node': 18.6.2 + '@types/node': 18.6.3 c8: 7.12.0 chai: 4.3.6 debug: 4.3.4 diff --git a/src/browser.ts b/src/browser.ts index 9445f95..f28529b 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -1,3 +1,5 @@ +/* eslint-disable unicorn/prefer-code-point -- for smaller bundler and compatibility */ + const HEX = 16 const CHUNK = 4 @@ -8,7 +10,7 @@ export const decode = (val: string) => ...atob(val), ] .map( - char => '%' + ('00' + char.codePointAt(0)!.toString(HEX)).slice(-1 * 2), + char => '%' + ('00' + char.charCodeAt(0)!.toString(HEX)).slice(-1 * 2), ) .join(''), ) diff --git a/src/polyfill.ts b/src/polyfill.ts new file mode 100644 index 0000000..7832f40 --- /dev/null +++ b/src/polyfill.ts @@ -0,0 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-condition, sonar/deprecation */ + +import { atob, btoa } from './ponyfill.js' + +globalThis.atob = globalThis.atob || atob +globalThis.btoa = globalThis.btoa || btoa diff --git a/src/ponyfill.ts b/src/ponyfill.ts new file mode 100644 index 0000000..3e2f8fd --- /dev/null +++ b/src/ponyfill.ts @@ -0,0 +1,82 @@ +/** + * via @see https://github.com/dankogai/js-base64/blob/main/base64.ts + */ + +/* eslint-disable @typescript-eslint/no-magic-numbers, unicorn/prefer-code-point -- for smaller bundler and compatibility */ + +const b64CharList = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + +const b64Chars = // type-coverage:ignore-next-line + (Array.prototype as string[]).slice.call(b64CharList) + +const b64Table = b64Chars.reduce>((acc, char, index) => { + acc[char] = index + return acc +}, {}) + +const b64Regexp = + /^(?:[\d+/A-Za-z]{4})*?(?:[\d+/A-Za-z]{2}(?:==)?|[\d+/A-Za-z]{3}=?)?$/ + +const fromCharCode = String.fromCharCode.bind(String) + +export const atob = (asc: string) => { + asc = asc.replace(/\s+/g, '') + + if (!b64Regexp.test(asc)) { + throw new TypeError('malformed base64.') + } + + asc += '=='.slice(2 - (asc.length & 3)) + + let u24: number + let binary = '' + let r1: number + let r2: number + + for (let i = 0; i < asc.length; ) { + u24 = + (b64Table[asc.charAt(i++)] << 18) | + (b64Table[asc.charAt(i++)] << 12) | + ((r1 = b64Table[asc.charAt(i++)]) << 6) | + (r2 = b64Table[asc.charAt(i++)]) + binary += + r1 === 64 + ? fromCharCode((u24 >> 16) & 255) + : r2 === 64 + ? fromCharCode((u24 >> 16) & 255, (u24 >> 8) & 255) + : fromCharCode((u24 >> 16) & 255, (u24 >> 8) & 255, u24 & 255) + } + + return binary +} + +export const btoa = (binary: string) => { + let u32: number + let c0: number + let c1: number + let c2: number + + let asc = '' + + const pad = binary.length % 3 + + for (let i = 0; i < binary.length; ) { + if ( + (c0 = binary.charCodeAt(i++)) > 255 || + (c1 = binary.charCodeAt(i++)) > 255 || + (c2 = binary.charCodeAt(i++)) > 255 + ) { + throw new TypeError('invalid character found') + } + + u32 = (c0 << 16) | (c1 << 8) | c2 + asc += + b64Chars[(u32 >> 18) & 63] + + b64Chars[(u32 >> 12) & 63] + + b64Chars[(u32 >> 6) & 63] + + b64Chars[u32 & 63] + } + + return pad ? asc.slice(0, pad - 3) + '==='.slice(pad) : asc +} diff --git a/test/basic.spec.ts b/test/basic.spec.ts deleted file mode 100644 index c7e7cf2..0000000 --- a/test/basic.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { decode, decodeUrl, encode, encodeUrl } from 'ab64' - -describe('encoding', () => { - test('it should just work', () => { - expect(encode('Hello World!')).toBe('SGVsbG8gV29ybGQh') - - expect(encode('dankogai')).toBe('ZGFua29nYWk=') - expect(encodeUrl('dankogai')).toBe('ZGFua29nYWk') - - expect(encode('小飼弾')).toBe('5bCP6aO85by+') - expect(encodeUrl('小飼弾')).toBe('5bCP6aO85by-') - }) -}) - -describe('decoding', () => { - test('it should just work', () => { - expect(decode('SGVsbG8gV29ybGQh')).toBe('Hello World!') - - expect(decode('ZGFua29nYWk=')).toBe('dankogai') - expect(decodeUrl('ZGFua29nYWk')).toBe('dankogai') - - expect(decode('5bCP6aO85by+')).toBe('小飼弾') - expect(decodeUrl('5bCP6aO85by-')).toBe('小飼弾') - }) -}) diff --git a/test/decoding.spec.ts b/test/decoding.spec.ts new file mode 100644 index 0000000..c75e0b1 --- /dev/null +++ b/test/decoding.spec.ts @@ -0,0 +1,11 @@ +import { decode, decodeUrl } from 'ab64' + +test('it should just work', () => { + expect(decode('SGVsbG8gV29ybGQh')).toBe('Hello World!') + + expect(decode('ZGFua29nYWk=')).toBe('dankogai') + expect(decodeUrl('ZGFua29nYWk')).toBe('dankogai') + + expect(decode('5bCP6aO85by+')).toBe('小飼弾') + expect(decodeUrl('5bCP6aO85by-')).toBe('小飼弾') +}) diff --git a/test/encoding.spec.ts b/test/encoding.spec.ts new file mode 100644 index 0000000..1b66d3f --- /dev/null +++ b/test/encoding.spec.ts @@ -0,0 +1,11 @@ +import { encode, encodeUrl } from 'ab64' + +test('it should just work', () => { + expect(encode('Hello World!')).toBe('SGVsbG8gV29ybGQh') + + expect(encode('dankogai')).toBe('ZGFua29nYWk=') + expect(encodeUrl('dankogai')).toBe('ZGFua29nYWk') + + expect(encode('小飼弾')).toBe('5bCP6aO85by+') + expect(encodeUrl('小飼弾')).toBe('5bCP6aO85by-') +}) diff --git a/test/ponyfill.spec.ts b/test/ponyfill.spec.ts new file mode 100644 index 0000000..c8b18eb --- /dev/null +++ b/test/ponyfill.spec.ts @@ -0,0 +1,13 @@ +import { atob, btoa } from 'ab64/ponyfill' + +const MIN = 16 + +test.runIf(process.env.TEST_ENV !== 'node' || +process.versions.node >= MIN)( + 'it should work same as the builtin', + () => { + // eslint-disable-next-line sonar/deprecation -- it's fine on browser + expect(atob('SGVsbG8gV29ybGQh')).toBe(globalThis.atob('SGVsbG8gV29ybGQh')) + // eslint-disable-next-line sonar/deprecation -- it's fine on browser + expect(btoa('Hello World!')).toBe(globalThis.btoa('Hello World!')) + }, +) diff --git a/tsconfig.json b/tsconfig.json index 7a545eb..3fef363 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "rootDir": ".", "removeComments": true - } + }, + "exclude": ["coverage", "dist", "lib"] } diff --git a/vitest.config.ts b/vitest.config.ts index 7272107..a136735 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,9 +7,16 @@ const isNode = process.env.TEST_ENV === 'node' export default defineConfig({ resolve: { - alias: { - ab64: path.resolve(`src/${isNode ? 'index' : 'browser'}`), - }, + alias: [ + { + find: /^ab64$/, + replacement: path.resolve(`src/${isNode ? 'index' : 'browser'}`), + }, + { + find: /^ab64\/ponyfill$/, + replacement: path.resolve('src/ponyfill'), + }, + ], }, plugins: [ /** @see https://github.com/microsoft/TypeScript/issues/50067 */