├── .eslintignore
├── .npmrc
├── .gitignore
├── types
└── index.d.ts
├── tsconfig.json
├── CHANGELOG.md
├── runall.mjs
├── UNLICENSE
├── src
└── index.js
├── package.json
├── lib
└── index.js
├── README.md
└── test
└── index.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | lib
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ignore-scripts=true
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.log
3 | *.tgz
4 |
5 | pnpm-lock.yaml
6 | package-lock.json
7 | yarn.lock
8 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | export function encode(base64: string): string;
2 | export function decode(safe: string): string;
3 | export function trim(string: string): string;
4 | export function isBase64(string: string): boolean;
5 | export function isUrlSafeBase64(string: string): boolean;
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "allowSyntheticDefaultImports": true,
5 | "checkJs": true,
6 | "declaration": true,
7 | "declarationDir": "types",
8 | "moduleResolution": "node",
9 | "noImplicitAny": false,
10 | "strict": true,
11 | "target": "ES2021",
12 | "emitDeclarationOnly": true,
13 | "noEmitOnError": true
14 | },
15 | "include": [
16 | "**/*.js"
17 | ],
18 | "exclude": [
19 | "lib",
20 | "bin",
21 | "coverage",
22 | "dist",
23 | "docs",
24 | "examples",
25 | "node_modules",
26 | "test",
27 | "tmp"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 1.3.1 (2023-08-19)
2 |
3 | - chore: bump dev dependencies (#40c195a)
4 |
5 | # 1.3.0 (2022-12-23)
6 |
7 | - chore: bump devdependencies (#74049c4)
8 | - fix(#4): use = instead of . for padding (#78b119e)
9 | - chore: bump (#3671ec2)
10 | - chore: bump dependencies (#198bb4b)
11 |
12 | # 1.2.0 (2022-03-27)
13 |
14 | - chore: overhaul (#49a10b5)
15 | - chore: sort package json (#601486e)
16 |
17 | # 1.1.1 (2019-10-26)
18 |
19 | - chore: bump deps (#6124ae2)
20 | - bump dev dependencies (#dfb7e1c)
21 |
22 | # 1.1.0 (2017-06-25)
23 |
24 | - adding checks (#042c902)
25 |
26 | # 1.0.0 (2017-04-20)
27 |
28 | - update links (#b0002fd)
29 | - init (#3110f34)
30 |
31 |
--------------------------------------------------------------------------------
/runall.mjs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * `npm run` with multiple arguments
5 | */
6 |
7 | // @ts-nocheck
8 | import { spawn } from 'child_process'
9 |
10 | function npmRun (arg) {
11 | return new Promise((resolve, reject) => {
12 | const sub = spawn('npm', ['run', arg], { stdio: 'inherit' })
13 | sub.on('error', err => { reject(err) })
14 | sub.on('exit', code => {
15 | code > 0
16 | ? reject(new Error('' + code))
17 | : resolve()
18 | })
19 | })
20 | }
21 |
22 | async function main (argv) {
23 | for (const arg of argv) {
24 | await npmRun(arg)
25 | }
26 | }
27 |
28 | main(process.argv.slice(2))
29 | .catch(() => {
30 | process.exit(1)
31 | })
32 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module url-safe-base64
3 | * @license UNLICENSE
4 | * @example
5 | * import {
6 | * encode, decode, trim,
7 | * isBase64, isUrlSafeBase64
8 | * } from 'url-safe-base64'
9 | * const safe = encode('A/B+C==')
10 | * // > 'A-B_C..'
11 | * trim(safe)
12 | * // > 'A-B_C'
13 | * const base64 = decode(safe)
14 | * // > 'A/B+C=='
15 | * isBase64(base64)
16 | * // > true
17 | * isBase64(safe)
18 | * // > false
19 | * isUrlSafeBase64(base64)
20 | * // > false
21 | * isUrlSafeBase64(safe)
22 | * // > true
23 | */
24 |
25 | const ENC = {
26 | '+': '-',
27 | '/': '_'
28 | }
29 | const DEC = {
30 | '-': '+',
31 | _: '/',
32 | '.': '='
33 | }
34 |
35 | /**
36 | * encode base64 string url safe
37 | * @param {String} base64 - base64 encoded string
38 | * @return {String} url-safe-base64 encoded
39 | */
40 | export const encode = (base64) => {
41 | return base64.replace(/[+/]/g, (m) => ENC[m])
42 | }
43 |
44 | /**
45 | * decode url-safe-base64 string to base64
46 | * @param {String} safe - url-safe-base64 string
47 | * @return {String} base64 encoded
48 | */
49 | export const decode = (safe) => {
50 | return safe.replace(/[-_.]/g, (m) => DEC[m])
51 | }
52 |
53 | /**
54 | * trim padding - `window.atob` might handle trimmed strings, e.g. in Chrome@57, Firefox@52
55 | * @param {String} string - base64 or url-safe-base64 string
56 | * @return {String} string with padding chars removed
57 | */
58 | export const trim = (string) => {
59 | return string.replace(/[.=]{1,2}$/, '')
60 | }
61 |
62 | /**
63 | * checks if `string` is base64 encoded
64 | * @param {String} string
65 | * @return {Boolean} true if base64 encoded
66 | */
67 | export const isBase64 = (string) => /^[A-Za-z0-9+/]*[=]{0,2}$/.test(string)
68 |
69 | /**
70 | * checks if `string` is url-safe-base64 encoded
71 | * @param {String} string
72 | * @return {Boolean} true if url-safe-base64 encoded
73 | */
74 | export const isUrlSafeBase64 = (string) => /^[A-Za-z0-9_-]*[.=]{0,2}$/.test(string)
75 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "url-safe-base64",
3 | "version": "1.3.1-0",
4 | "description": "url safe base64 en- and decoding",
5 | "keywords": [
6 | "base64",
7 | "safe",
8 | "url"
9 | ],
10 | "homepage": "https://github.com/commenthol/url-safe-base64#readme",
11 | "bugs": {
12 | "url": "https://github.com/commenthol/url-safe-base64/issues"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/commenthol/url-safe-base64.git"
17 | },
18 | "license": "Unlicense",
19 | "author": "commenthol@gmail.com",
20 | "main": "lib",
21 | "module": "src",
22 | "types": "types",
23 | "directories": {
24 | "lib": "lib",
25 | "test": "test"
26 | },
27 | "files": [
28 | "src",
29 | "lib",
30 | "types"
31 | ],
32 | "scripts": {
33 | "changelog": "npx conv-changelog -o CHANGELOG.md",
34 | "ci": "./runall.mjs clean lint transpile test types",
35 | "clean": "rimraf lib *.tgz",
36 | "dox": "dox -r < lib/index.js | doxme --readme > README.md",
37 | "lint": "eslint --ext .js .",
38 | "test": "mocha",
39 | "transpile": "babel -d lib src",
40 | "types": "rimraf types; tsc"
41 | },
42 | "babel": {
43 | "presets": [
44 | "@babel/preset-env"
45 | ]
46 | },
47 | "eslintConfig": {
48 | "extends": "standard",
49 | "rules": {}
50 | },
51 | "mocha": {
52 | "checkLeaks": true,
53 | "colors": true,
54 | "require": [
55 | "@babel/register"
56 | ]
57 | },
58 | "dependencies": {},
59 | "devDependencies": {
60 | "@babel/cli": "^7.22.10",
61 | "@babel/core": "^7.22.10",
62 | "@babel/preset-env": "^7.22.10",
63 | "@babel/register": "^7.22.5",
64 | "eslint": "^8.47.0",
65 | "eslint-config-standard": "^17.1.0",
66 | "eslint-plugin-import": "^2.28.1",
67 | "eslint-plugin-n": "^16.0.1",
68 | "eslint-plugin-promise": "^6.1.1",
69 | "mocha": "^10.2.0",
70 | "rimraf": "^5.0.1",
71 | "typescript": "^5.1.6"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.trim = exports.isUrlSafeBase64 = exports.isBase64 = exports.encode = exports.decode = void 0;
7 | /**
8 | * @module url-safe-base64
9 | * @license UNLICENSE
10 | * @example
11 | * import {
12 | * encode, decode, trim,
13 | * isBase64, isUrlSafeBase64
14 | * } from 'url-safe-base64'
15 | * const safe = encode('A/B+C==')
16 | * // > 'A-B_C..'
17 | * trim(safe)
18 | * // > 'A-B_C'
19 | * const base64 = decode(safe)
20 | * // > 'A/B+C=='
21 | * isBase64(base64)
22 | * // > true
23 | * isBase64(safe)
24 | * // > false
25 | * isUrlSafeBase64(base64)
26 | * // > false
27 | * isUrlSafeBase64(safe)
28 | * // > true
29 | */
30 |
31 | var ENC = {
32 | '+': '-',
33 | '/': '_'
34 | };
35 | var DEC = {
36 | '-': '+',
37 | _: '/',
38 | '.': '='
39 | };
40 |
41 | /**
42 | * encode base64 string url safe
43 | * @param {String} base64 - base64 encoded string
44 | * @return {String} url-safe-base64 encoded
45 | */
46 | var encode = function encode(base64) {
47 | return base64.replace(/[+/]/g, function (m) {
48 | return ENC[m];
49 | });
50 | };
51 |
52 | /**
53 | * decode url-safe-base64 string to base64
54 | * @param {String} safe - url-safe-base64 string
55 | * @return {String} base64 encoded
56 | */
57 | exports.encode = encode;
58 | var decode = function decode(safe) {
59 | return safe.replace(/[-_.]/g, function (m) {
60 | return DEC[m];
61 | });
62 | };
63 |
64 | /**
65 | * trim padding - `window.atob` might handle trimmed strings, e.g. in Chrome@57, Firefox@52
66 | * @param {String} string - base64 or url-safe-base64 string
67 | * @return {String} string with padding chars removed
68 | */
69 | exports.decode = decode;
70 | var trim = function trim(string) {
71 | return string.replace(/[.=]{1,2}$/, '');
72 | };
73 |
74 | /**
75 | * checks if `string` is base64 encoded
76 | * @param {String} string
77 | * @return {Boolean} true if base64 encoded
78 | */
79 | exports.trim = trim;
80 | var isBase64 = function isBase64(string) {
81 | return /^[A-Za-z0-9+/]*[=]{0,2}$/.test(string);
82 | };
83 |
84 | /**
85 | * checks if `string` is url-safe-base64 encoded
86 | * @param {String} string
87 | * @return {Boolean} true if url-safe-base64 encoded
88 | */
89 | exports.isBase64 = isBase64;
90 | var isUrlSafeBase64 = function isUrlSafeBase64(string) {
91 | return /^[A-Za-z0-9_-]*[.=]{0,2}$/.test(string);
92 | };
93 | exports.isUrlSafeBase64 = isUrlSafeBase64;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # url-safe-base64
2 |
3 | > url safe base64 en- and decoding
4 |
5 | [](https://www.npmjs.com/package/url-safe-base64/)
6 |
7 | ## TOC
8 |
9 | * [Example](#example)
10 | * [API](#api)
11 | * [`encode(base64)`](#encodebase64)
12 | * [`decode(safe)`](#decodesafe)
13 | * [`trim(string)`](#trimstr)
14 | * [`isBase64(string)`](#isbase64string)
15 | * [`isUrlSafeBase64(string)`](#isurlsafebase64string)
16 | * [Installation](#installation)
17 | * [Tests](#tests)
18 | * [LICENSE](#license)
19 |
20 | ## Example
21 |
22 | ```js
23 | import {
24 | encode, decode, trim,
25 | isBase64, isUrlSafeBase64
26 | } from 'url-safe-base64'
27 | const safe = encode('A/B+C==')
28 | // > 'A-B_C=='
29 | trim(safe)
30 | // > 'A-B_C'
31 | const base64 = decode(safe)
32 | // > 'A/B+C=='
33 | isBase64(base64)
34 | // > true
35 | isBase64(safe)
36 | // > false
37 | isUrlSafeBase64(base64)
38 | // > false
39 | isUrlSafeBase64(safe)
40 | // > true
41 | ```
42 |
43 | ## API
44 |
45 | ### `encode(base64)`
46 |
47 | encode base64 string url safe
48 |
49 | **Parameters**
50 |
51 | | parameter | type | description |
52 | | --------- | ------ | ----------------------- |
53 | | `base64` | String | base64 encoded string |
54 |
55 | **Returns** `String`, url-safe-base64 encoded
56 |
57 |
58 | ### `decode(safe)`
59 |
60 | decode url-safe-base64 string to base64
61 |
62 | **Parameters**
63 |
64 | | parameter | type | description |
65 | | --------- | ------ | ------------------------ |
66 | | `safe` | String | - url-safe-base64 string |
67 |
68 | **Returns** `String`, base64 encoded
69 |
70 |
71 | ### `trim(string)`
72 |
73 | trim padding - `window.atob` might handle trimmed strings, e.g. in Chrome@57, Firefox@52
74 |
75 | **Parameters**
76 |
77 | | parameter | type | description |
78 | | --------- | ------ | ---------------------------------- |
79 | | `string` | String | - base64 or url-safe-base64 string |
80 |
81 | **Returns** `String`, string with padding chars removed
82 |
83 |
84 | ### `isBase64(string)`
85 |
86 | checks if `string` is base64 encoded
87 |
88 | **Returns** `Boolean`, true if base64 encoded
89 |
90 |
91 | ### `isUrlSafeBase64(string)`
92 |
93 | checks if `string` is url-safe-base64 encoded
94 |
95 | **Returns** `Boolean`, true if url-safe-base64 encoded
96 |
97 | ## Installation
98 |
99 | ```sh
100 | $ npm install --save url-safe-base64
101 | ```
102 |
103 | ## Tests
104 |
105 | ```sh
106 | $ npm test
107 | ```
108 |
109 | ## LICENSE
110 |
111 | UNLICENSE
112 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | /* global describe, it */
2 |
3 | import assert from 'assert'
4 | import { encode, decode, trim, isBase64, isUrlSafeBase64 } from '..'
5 |
6 | const base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
7 | const urlSafeChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_='
8 | // non standard padding uses `.`
9 | const urlSafeCharsAlt = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.'
10 |
11 | describe('url-safe', function () {
12 | const b64 = [base64chars, base64chars, base64chars].join()
13 | const safe = [urlSafeChars, urlSafeChars, urlSafeChars].join()
14 |
15 | it('should encode base64 chars', function () {
16 | const res = encode(b64)
17 | assert.strictEqual(res, safe)
18 | })
19 |
20 | it('should decode url-safe-base64 chars into base64', function () {
21 | const res = decode(safe)
22 | assert.strictEqual(res, b64)
23 | })
24 |
25 | describe('should check if base64 encoded', function () {
26 | it('base64chars', function () {
27 | const res = isBase64(base64chars)
28 | assert.strictEqual(res, true)
29 | })
30 | it('urlSafeChars', function () {
31 | const res = isBase64(urlSafeChars)
32 | assert.strictEqual(res, false)
33 | })
34 | it('b64', function () {
35 | const res = isBase64(b64)
36 | assert.strictEqual(res, false)
37 | })
38 | it('safe', function () {
39 | const res = isBase64(safe)
40 | assert.strictEqual(res, false)
41 | })
42 | })
43 |
44 | describe('should check if url-safe-base64 encoded', function () {
45 | it('base64chars', function () {
46 | const res = isUrlSafeBase64(base64chars)
47 | assert.strictEqual(res, false)
48 | })
49 | it('urlSafeChars', function () {
50 | const res = isUrlSafeBase64(urlSafeChars)
51 | assert.strictEqual(res, true)
52 | })
53 | it('urlSafeCharsAlt', function () {
54 | const res = isUrlSafeBase64(urlSafeCharsAlt)
55 | assert.strictEqual(res, true)
56 | })
57 | it('b64', function () {
58 | const res = isUrlSafeBase64(b64)
59 | assert.strictEqual(res, false)
60 | })
61 | it('safe', function () {
62 | const res = isUrlSafeBase64(safe)
63 | assert.strictEqual(res, false)
64 | })
65 | })
66 |
67 | describe('should trim padding from url-safe-base64', function () {
68 | ;[
69 | ['A', 'A'],
70 | ['A=', 'A'],
71 | ['A==', 'A'],
72 | ['A===', 'A='],
73 | ['A.', 'A'],
74 | ['A..', 'A'],
75 | ['A...', 'A.']
76 | ].forEach((test) => {
77 | it(test[0], function () {
78 | const res = trim(test[0])
79 | assert.strictEqual(res, test[1])
80 | })
81 | })
82 | })
83 | })
84 |
--------------------------------------------------------------------------------