diff --git a/.eslintrc.js b/.eslintrc.js index c058c631..d31ecc3a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,6 +12,7 @@ module.exports = { rules: { "arrow-parens": ["error", "as-needed"], "no-sparse-arrays": 0, + "no-unused-vars": 0, "padding-line-between-statements": [ "error", { blankLine: "always", prev: "*", next: "export" }, diff --git a/.gitignore b/.gitignore index 8ea881d8..60b08df6 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ index.umd.js **/*.d.ts .coverage docs/dist +!*.interface.d.ts diff --git a/.npmignore b/.npmignore index b48a1bc6..843307fb 100644 --- a/.npmignore +++ b/.npmignore @@ -21,6 +21,7 @@ document.js ignore.js indexReadme.js jest.config.js +mappings.js normalizeLineEndings.js package-lock.json regenerate.js diff --git a/README.md b/README.md index 3a77485d..efbdf1b6 100644 --- a/README.md +++ b/README.md @@ -1113,36 +1113,167 @@ Computes a difference between two objects. #### base64url -Provides a way to encode strings and bytes from and into Base64URL. +##### decode -##### Type signature +Decodes the given Base64URL back into string. + +###### Type signature ```typescript -{ - decode: (text: string, context?: DecodeContext) => string; - decodeBytes: ( - text: string, - context?: { - atob: (byteString: string) => string; - TextDecoder: new (encoding: string) => { - decode: (input?: Uint8Array) => string; - }; - } - ) => number[]; - encode: ( - text: string, - context?: { - btoa: (byteString: string) => string; - TextEncoder: new () => { - encode: (input?: string) => Uint8Array; - }; - } - ) => string; - encodeBytes: (bytes: number[], context?: EncodeContext) => string; - fromByteString: (byteString: string) => number[]; - toByteString: (bytes: number[]) => string; -} +(text: string, context?: DecodeContext) => string +``` + + +###### Examples + + +```javascript +decode("PDw_Pz8-Pg"); // ⇒ "<>" +``` + + +##### decodeBytes + +Decodes the given Base64URL back into byte array. + +###### Type signature + + +```typescript +(text: string, context?: DecodeContext) => number[] +``` + + +###### Examples + + +```javascript +decodeBytes("w4Jnw6vCp20-bBsQfA"); +// ⇒ [0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c] +``` + + +##### encode + +Encodes the given string into Base64URL. + +###### Type signature + + +```typescript +(text: string, context?: EncodeContext) => string +``` + + +###### Examples + + +```javascript +encode("<>"); // ⇒ "PDw_Pz8-Pg" +``` + + +##### encodeBytes + +Encodes the given bytes into Base64URL. + +###### Type signature + + +```typescript +(bytes: number[], context?: EncodeContext) => string +``` + + +###### Examples + + +```javascript +encodeBytes([0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c]); +// ⇒ "w4Jnw6vCp20-bBsQfA" +``` + + +##### fromBase64 + +Converts Base64 string into Base64URL one. + +###### Type signature + + +```typescript +(base64: string) => string +``` + + +###### Examples + + +```javascript +fromBase64("PDw/Pz8+Pg=="); // ⇒ "PDw_Pz8-Pg" +``` + + +##### toBase64 + +Converts Base64URL string into Base64 one. + +###### Type signature + + +```typescript +(base64Url: string) => string +``` + + +###### Examples + + +```javascript +toBase64("PDw_Pz8-Pg"); // ⇒ "PDw/Pz8+Pg==" +``` + + +#### byteString + +##### from + +Converts string to byte array. + +###### Type signature + + +```typescript +(byteString: string) => number[] +``` + + +###### Examples + + +```javascript +from("PQR"); // ⇒ [80, 81, 82] +``` + + +##### to + +Coverts byte array to string. + +###### Type signature + + +```typescript +(bytes: number[]) => string +``` + + +###### Examples + + +```javascript +to([0x50, 0x51, 0x52]); // ⇒ "PQR" ``` diff --git a/compile.js b/compile.js index 88cb1f3f..83536e3a 100644 --- a/compile.js +++ b/compile.js @@ -7,14 +7,14 @@ import path from "path"; import os from "os"; import pQueue from "p-queue"; +import ignored from "./ignore.js"; + const CONCURRENCY = Math.max(1, os.cpus().length - 1); const { default: PQueue } = pQueue; const execAsync = promisify(exec); -import ignored from "./ignore.js"; - const [sourceIgnoredFiles, ignoredDirectories] = ignored; const ignoredFiles = sourceIgnoredFiles.filter(x => x !== "index.ts"); diff --git a/docs/scripts/docs.js b/docs/scripts/docs.js index 8f2ad7e2..713716f1 100644 --- a/docs/scripts/docs.js +++ b/docs/scripts/docs.js @@ -1,6 +1,5 @@ /* eslint-env browser */ -// eslint-disable-next-line no-unused-vars function tryInREPL(event, scope) { var target = event.target; var isReplRun = target.matches(".btn-repl"); @@ -25,7 +24,6 @@ function tryInREPL(event, scope) { }); } -// eslint-disable-next-line no-unused-vars function toggleTableOfContents() { var className = "toc-active"; diff --git a/document.js b/document.js index 28fce76b..d8e97902 100644 --- a/document.js +++ b/document.js @@ -4,6 +4,7 @@ import { promises, existsSync } from "fs"; import path from "path"; import ignored from "./ignore.js"; +import mappings from "./mappings.js"; const template = ({ name, description, signature, examples, questions }) => { let content = `# ${name} @@ -82,11 +83,7 @@ const { const [, , cwd = process.cwd()] = process.argv; -const mapping = { - function: "_function" -}; - -const identifier = name => mapping[name] || name; +const identifier = name => mappings[name] || name; // Do not match type definition files *.d.ts but match *.ts: // https://stackoverflow.com/a/43493203/1384679 diff --git a/encoding/README.md b/encoding/README.md index 78bb2f76..170cac8b 100644 --- a/encoding/README.md +++ b/encoding/README.md @@ -1,34 +1,165 @@ # base64url -Provides a way to encode strings and bytes from and into Base64URL. - -## Type signature - - -```typescript -{ - decode: (text: string, context?: DecodeContext) => string; - decodeBytes: ( - text: string, - context?: { - atob: (byteString: string) => string; - TextDecoder: new (encoding: string) => { - decode: (input?: Uint8Array) => string; - }; - } - ) => number[]; - encode: ( - text: string, - context?: { - btoa: (byteString: string) => string; - TextEncoder: new () => { - encode: (input?: string) => Uint8Array; - }; - } - ) => string; - encodeBytes: (bytes: number[], context?: EncodeContext) => string; - fromByteString: (byteString: string) => number[]; - toByteString: (bytes: number[]) => string; -} +## decode + +Decodes the given Base64URL back into string. + +### Type signature + + +```typescript +(text: string, context?: DecodeContext) => string +``` + + +### Examples + + +```javascript +decode("PDw_Pz8-Pg"); // ⇒ "<>" +``` + + +## decodeBytes + +Decodes the given Base64URL back into byte array. + +### Type signature + + +```typescript +(text: string, context?: DecodeContext) => number[] +``` + + +### Examples + + +```javascript +decodeBytes("w4Jnw6vCp20-bBsQfA"); +// ⇒ [0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c] +``` + + +## encode + +Encodes the given string into Base64URL. + +### Type signature + + +```typescript +(text: string, context?: EncodeContext) => string +``` + + +### Examples + + +```javascript +encode("<>"); // ⇒ "PDw_Pz8-Pg" +``` + + +## encodeBytes + +Encodes the given bytes into Base64URL. + +### Type signature + + +```typescript +(bytes: number[], context?: EncodeContext) => string +``` + + +### Examples + + +```javascript +encodeBytes([0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c]); +// ⇒ "w4Jnw6vCp20-bBsQfA" +``` + + +## fromBase64 + +Converts Base64 string into Base64URL one. + +### Type signature + + +```typescript +(base64: string) => string +``` + + +### Examples + + +```javascript +fromBase64("PDw/Pz8+Pg=="); // ⇒ "PDw_Pz8-Pg" +``` + + +## toBase64 + +Converts Base64URL string into Base64 one. + +### Type signature + + +```typescript +(base64Url: string) => string +``` + + +### Examples + + +```javascript +toBase64("PDw_Pz8-Pg"); // ⇒ "PDw/Pz8+Pg==" +``` + + +# byteString + +## from + +Converts string to byte array. + +### Type signature + + +```typescript +(byteString: string) => number[] +``` + + +### Examples + + +```javascript +from("PQR"); // ⇒ [80, 81, 82] +``` + + +## to + +Coverts byte array to string. + +### Type signature + + +```typescript +(bytes: number[]) => string +``` + + +### Examples + + +```javascript +to([0x50, 0x51, 0x52]); // ⇒ "PQR" ``` diff --git a/encoding/base64url.js b/encoding/base64url.js deleted file mode 100644 index 6c6d1a70..00000000 --- a/encoding/base64url.js +++ /dev/null @@ -1,66 +0,0 @@ -/* eslint-env browser, node */ -const toArray = typedArray => [...typedArray]; - -export const toByteString = bytes => - bytes.map(_ => String.fromCharCode(_)).join(""); - -export const fromByteString = byteString => - [...byteString].map(_ => _.codePointAt(0) || 0); - -const ENCODING = "utf-8"; - -const btoaImplementation = ( - text, - context = typeof window !== "undefined" ? window : undefined -) => - context - ? context.btoa( - toByteString(toArray(new context.TextEncoder().encode(text))) - ) - : Buffer.from(text, ENCODING).toString("base64"); - -const atobImplementation = ( - text, - context = typeof window !== "undefined" ? window : undefined -) => - context - ? new context.TextDecoder(ENCODING).decode( - new Uint8Array(fromByteString(context.atob(text))) - ) - : Buffer.from(text, "base64").toString(ENCODING); - -export const encode = (text, context) => - btoaImplementation(text, context) - .replace(/=/g, "") - .replace(/\+/g, "-") - .replace(/\//g, "_"); - -export const decode = (text, context) => - atobImplementation(text.replace(/-/g, "+").replace(/_/g, "/"), context); - -export const toBase64Url = base64 => - base64.replace(/\+/g, "-").replace(/\//g, "_"); - -export const fromBase64Url = base64 => - base64.replace(/-/g, "+").replace(/_/g, "/"); - -export const encodeBytes = (bytes, context) => { - const sourceText = toByteString(bytes); - - return encode(sourceText, context); -}; - -export const decodeBytes = (text, context) => { - const decoded = decode(text, context); - - return fromByteString(decoded); -}; - -export default { - decode, - decodeBytes, - encode, - encodeBytes, - fromByteString, - toByteString -}; diff --git a/encoding/base64url.jsdom.test.ts b/encoding/base64url.jsdom.test.ts index 29ad1ea7..adf0a7eb 100644 --- a/encoding/base64url.jsdom.test.ts +++ b/encoding/base64url.jsdom.test.ts @@ -1,9 +1,8 @@ /* eslint-env jest, node */ -import { - decode, - encode - // @ts-ignore ambiguous import -} from "./base64url.ts"; +// @ts-ignore ambiguous import +import encode from "./base64url/encode.ts"; +// @ts-ignore ambiguous import +import decode from "./base64url/decode.ts"; const unicodeText = "Zombies everywhere 🧟"; @@ -78,7 +77,13 @@ describe("base64url", () => { let out = ""; for (let i = 0; i < s.length; i += 3) { - const groupsOfSix = [undefined, undefined, undefined, undefined]; + const groupsOfSix: [ + number | undefined, + number | undefined, + number | undefined, + number | undefined + ] = [undefined, undefined, undefined, undefined]; + groupsOfSix[0] = s.charCodeAt(i) >> 2; groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; @@ -88,15 +93,17 @@ describe("base64url", () => { } if (s.length > i + 2) { - groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; + groupsOfSix[2] = (groupsOfSix[2] || 0) | (s.charCodeAt(i + 2) >> 6); groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; } for (let j = 0; j < groupsOfSix.length; j++) { - if (typeof groupsOfSix[j] === "undefined") { + const x = groupsOfSix[j]; + + if (x === undefined) { out += "="; } else { - out += btoaLookup(groupsOfSix[j]); + out += btoaLookup(x); } } } @@ -120,19 +127,21 @@ describe("base64url", () => { throw new RangeError("Index out of range."); }; + const globalAny: any = global; + const context = { atob, btoa, - TextEncoder: global["TextEncoder"], - TextDecoder: global["TextDecoder"] + TextEncoder: globalAny["TextEncoder"], + TextDecoder: globalAny["TextDecoder"] }; expect(decode(encode(unicodeText, context), context)).toEqual(unicodeText); - global["window"] = context; + globalAny["window"] = context; expect(decode(encode(unicodeText))).toEqual(unicodeText); - delete global["window"]; + delete globalAny["window"]; }); }); diff --git a/encoding/base64url.json b/encoding/base64url.json deleted file mode 100644 index 34c52133..00000000 --- a/encoding/base64url.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "base64url", - "description": "Provides a way to encode strings and bytes from and into Base64URL.", - "signature": "{\n decode: (text: string, context?: DecodeContext) => string;\n decodeBytes: (\n text: string,\n context?: {\n atob: (byteString: string) => string;\n TextDecoder: new (encoding: string) => {\n decode: (input?: Uint8Array) => string;\n };\n }\n ) => number[];\n encode: (\n text: string,\n context?: {\n btoa: (byteString: string) => string;\n TextEncoder: new () => {\n encode: (input?: string) => Uint8Array;\n };\n }\n ) => string;\n encodeBytes: (bytes: number[], context?: EncodeContext) => string;\n fromByteString: (byteString: string) => number[];\n toByteString: (bytes: number[]) => string;\n}", - "examples": [ - { - "language": "javascript", - "content": "base64url(); // ⇒ TODO" - } - ], - "questions": ["TODO: List questions that may this function answers."] -} diff --git a/encoding/base64url.md b/encoding/base64url.md deleted file mode 100644 index 78bb2f76..00000000 --- a/encoding/base64url.md +++ /dev/null @@ -1,34 +0,0 @@ -# base64url - -Provides a way to encode strings and bytes from and into Base64URL. - -## Type signature - - -```typescript -{ - decode: (text: string, context?: DecodeContext) => string; - decodeBytes: ( - text: string, - context?: { - atob: (byteString: string) => string; - TextDecoder: new (encoding: string) => { - decode: (input?: Uint8Array) => string; - }; - } - ) => number[]; - encode: ( - text: string, - context?: { - btoa: (byteString: string) => string; - TextEncoder: new () => { - encode: (input?: string) => Uint8Array; - }; - } - ) => string; - encodeBytes: (bytes: number[], context?: EncodeContext) => string; - fromByteString: (byteString: string) => number[]; - toByteString: (bytes: number[]) => string; -} -``` - diff --git a/encoding/base64url.test.ts b/encoding/base64url.test.ts index f6a3c99c..ebb2b981 100644 --- a/encoding/base64url.test.ts +++ b/encoding/base64url.test.ts @@ -1,15 +1,20 @@ /* eslint-env jest, node */ -import { - decode, - decodeBytes, - encode, - encodeBytes, - fromBase64Url, - fromByteString, - toBase64Url, - toByteString - // @ts-ignore ambiguous import -} from "./base64url.ts"; +// @ts-ignore ambiguous import +import encode from "./base64url/encode.ts"; +// @ts-ignore ambiguous import +import decode from "./base64url/decode.ts"; +// @ts-ignore ambiguous import +import encodeBytes from "./base64url/encodeBytes.ts"; +// @ts-ignore ambiguous import +import decodeBytes from "./base64url/decodeBytes.ts"; +// @ts-ignore ambiguous import +import toBase64 from "./base64url/toBase64.ts"; +// @ts-ignore ambiguous import +import fromBase64 from "./base64url/fromBase64.ts"; +// @ts-ignore ambiguous import +import fromByteString from "./byteString/from.ts"; +// @ts-ignore ambiguous import +import toByteString from "./byteString/to.ts"; // @ts-ignore ambiguous import import range from "../array/range.ts"; @@ -62,6 +67,14 @@ describe("base64url", () => { expect(encode(text)).toEqual("QmFzZTY0VVJMIGVuY29kZS9kZWNvZGUgdGVzdA"); }); + it("properly replaces non-URL-safe characters", () => { + expect(encode("<>")).toEqual("PDw_Pz8-Pg"); + expect(Buffer.from("<>", "utf-8").toString("base64")).toEqual( + "PDw/Pz8+Pg==" + ); + expect(decode(encode("<>"))).toEqual("<>"); + }); + it("is does not include padding characters", () => { const text = "Zażółć gęślą jaźń"; @@ -88,6 +101,15 @@ describe("base64url", () => { expect(decode(encode(text))).toEqual(text); }); + it("encodes/decodes bytes", () => { + const bytes = [0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c]; + + const encoded = "w4Jnw6vCp20-bBsQfA"; + + expect(encodeBytes(bytes)).toEqual(encoded); + expect(decodeBytes(encoded)).toEqual(bytes); + }); + it("handles Unicode", () => { expect(decode(encode(unicodeText))).toEqual(unicodeText); }); @@ -99,16 +121,42 @@ describe("base64url", () => { it("converts base64 to base64URL", () => { const text = toByteString(range(256)); - expect(toBase64Url(Buffer.from(text, "utf-8").toString("base64"))).toEqual( + expect(fromBase64(Buffer.from(text, "utf-8").toString("base64"))).toEqual( encode(text) ); }); it("converts base64URL to base64", () => { const text = toByteString(range(256)); + const base64url = encode(text); + const encoded = toBase64(base64url); + const expected = Buffer.from(text, "utf-8").toString("base64"); - expect(fromBase64Url(encode(text))).toEqual( - Buffer.from(text, "utf-8").toString("base64") - ); + expect(encoded).toEqual(expected); + }); + + it("fromBase64 strips padding characters", () => { + expect(fromBase64("PDw/Pz8+Pg==")).toEqual("PDw_Pz8-Pg"); + }); + + it("toBase64 adds padding characters when needed", () => { + expect(toBase64("PDw_Pz8-Pg")).toEqual("PDw/Pz8+Pg=="); + }); + + it("converts base64 to base64URL and vice versa", () => { + const base64 = "PDw/Pz8+Pg=="; + const base64url = "PDw_Pz8-Pg"; + + expect(fromBase64(base64)).toEqual(base64url); + expect(toBase64(base64url)).toEqual(base64); + }); + + it("encodes/decodes bytes to and from string", () => { + const bytes = [0x50, 0x51, 0x52]; + + const encoded = "PQR"; + + expect(toByteString(bytes)).toEqual(encoded); + expect(fromByteString(encoded)).toEqual(bytes); }); }); diff --git a/encoding/base64url.ts b/encoding/base64url.ts deleted file mode 100644 index 98c75bc4..00000000 --- a/encoding/base64url.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* eslint-env browser, node */ - -export interface EncodeContext { - btoa: (byteString: string) => string; - TextEncoder: new () => { - encode: { (input?: string): Uint8Array }; - }; -} - -export interface DecodeContext { - atob: (byteString: string) => string; - TextDecoder: new (encoding: string) => { - decode: (input?: Uint8Array) => string; - }; -} - -const toArray = (typedArray: Uint8Array): number[] => [...typedArray]; - -export const toByteString = (bytes: number[]) => - bytes.map(_ => String.fromCharCode(_)).join(""); - -export const fromByteString = (byteString: string): number[] => - [...byteString].map(_ => _.codePointAt(0) || 0); - -const ENCODING = "utf-8"; - -const btoaImplementation = ( - text: string, - context: EncodeContext | undefined = typeof window !== "undefined" - ? window - : undefined -) => - context - ? context.btoa( - toByteString(toArray(new context.TextEncoder().encode(text))) - ) - : Buffer.from(text, ENCODING).toString("base64"); - -const atobImplementation = ( - text: string, - context: DecodeContext | undefined = typeof window !== "undefined" - ? window - : undefined -) => - context - ? new context.TextDecoder(ENCODING).decode( - new Uint8Array(fromByteString(context.atob(text))) - ) - : Buffer.from(text, "base64").toString(ENCODING); - -export const encode = ( - text: string, - context?: { - btoa: (byteString: string) => string; - TextEncoder: new () => { encode: (input?: string) => Uint8Array }; - } -) => - btoaImplementation(text, context) - .replace(/=/g, "") - .replace(/\+/g, "-") - .replace(/\//g, "_"); - -export const decode = (text: string, context?: DecodeContext) => - atobImplementation(text.replace(/-/g, "+").replace(/_/g, "/"), context); - -export const toBase64Url = (base64: string) => - base64.replace(/\+/g, "-").replace(/\//g, "_"); - -export const fromBase64Url = (base64: string) => - base64.replace(/-/g, "+").replace(/_/g, "/"); - -export const encodeBytes = (bytes: number[], context?: EncodeContext) => { - const sourceText = toByteString(bytes); - - return encode(sourceText, context); -}; - -export const decodeBytes = ( - text: string, - context?: { - atob: (byteString: string) => string; - TextDecoder: new (encoding: string) => { - decode: (input?: Uint8Array) => string; - }; - } -) => { - const decoded = decode(text, context); - - return fromByteString(decoded); -}; - -export default { - decode, - decodeBytes, - encode, - encodeBytes, - fromByteString, - toByteString -}; diff --git a/encoding/base64url/DecodeContext.interface.d.ts b/encoding/base64url/DecodeContext.interface.d.ts new file mode 100644 index 00000000..a477cedd --- /dev/null +++ b/encoding/base64url/DecodeContext.interface.d.ts @@ -0,0 +1,6 @@ +export interface DecodeContext { + atob: (byteString: string) => string; + TextDecoder: new (encoding: string) => { + decode: (input?: Uint8Array) => string; + }; +} diff --git a/encoding/base64url/EncodeContext.interface.d.ts b/encoding/base64url/EncodeContext.interface.d.ts new file mode 100644 index 00000000..fa791926 --- /dev/null +++ b/encoding/base64url/EncodeContext.interface.d.ts @@ -0,0 +1,6 @@ +export interface EncodeContext { + btoa: (byteString: string) => string; + TextEncoder: new () => { + encode: { (input?: string): Uint8Array }; + }; +} diff --git a/encoding/base64url/README.md b/encoding/base64url/README.md new file mode 100644 index 00000000..e02753f8 --- /dev/null +++ b/encoding/base64url/README.md @@ -0,0 +1,121 @@ +# decode + +Decodes the given Base64URL back into string. + +## Type signature + + +```typescript +(text: string, context?: DecodeContext) => string +``` + + +## Examples + + +```javascript +decode("PDw_Pz8-Pg"); // ⇒ "<>" +``` + + +# decodeBytes + +Decodes the given Base64URL back into byte array. + +## Type signature + + +```typescript +(text: string, context?: DecodeContext) => number[] +``` + + +## Examples + + +```javascript +decodeBytes("w4Jnw6vCp20-bBsQfA"); +// ⇒ [0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c] +``` + + +# encode + +Encodes the given string into Base64URL. + +## Type signature + + +```typescript +(text: string, context?: EncodeContext) => string +``` + + +## Examples + + +```javascript +encode("<>"); // ⇒ "PDw_Pz8-Pg" +``` + + +# encodeBytes + +Encodes the given bytes into Base64URL. + +## Type signature + + +```typescript +(bytes: number[], context?: EncodeContext) => string +``` + + +## Examples + + +```javascript +encodeBytes([0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c]); +// ⇒ "w4Jnw6vCp20-bBsQfA" +``` + + +# fromBase64 + +Converts Base64 string into Base64URL one. + +## Type signature + + +```typescript +(base64: string) => string +``` + + +## Examples + + +```javascript +fromBase64("PDw/Pz8+Pg=="); // ⇒ "PDw_Pz8-Pg" +``` + + +# toBase64 + +Converts Base64URL string into Base64 one. + +## Type signature + + +```typescript +(base64Url: string) => string +``` + + +## Examples + + +```javascript +toBase64("PDw_Pz8-Pg"); // ⇒ "PDw/Pz8+Pg==" +``` + diff --git a/encoding/base64url/decode.js b/encoding/base64url/decode.js new file mode 100644 index 00000000..4c4f663a --- /dev/null +++ b/encoding/base64url/decode.js @@ -0,0 +1,17 @@ +/* eslint-env browser, node */ +import fromByteString from "../byteString/from.js"; + +const ENCODING = "utf-8"; + +const atobImplementation = ( + text, + context = typeof window !== "undefined" ? window : undefined +) => + context + ? new context.TextDecoder(ENCODING).decode( + new Uint8Array(fromByteString(context.atob(text))) + ) + : Buffer.from(text, "base64").toString(ENCODING); + +export default (text, context) => + atobImplementation(text.replace(/-/g, "+").replace(/_/g, "/"), context); diff --git a/encoding/base64url/decode.json b/encoding/base64url/decode.json new file mode 100644 index 00000000..53086a6c --- /dev/null +++ b/encoding/base64url/decode.json @@ -0,0 +1,12 @@ +{ + "name": "decode", + "description": "Decodes the given Base64URL back into string.", + "signature": "(text: string, context?: DecodeContext) => string", + "examples": [ + { + "language": "javascript", + "content": "decode(\"PDw_Pz8-Pg\"); // ⇒ \"<>\"" + } + ], + "questions": ["TODO: List questions that may this function answers."] +} diff --git a/encoding/base64url/decode.md b/encoding/base64url/decode.md new file mode 100644 index 00000000..a75a8ad2 --- /dev/null +++ b/encoding/base64url/decode.md @@ -0,0 +1,19 @@ +# decode + +Decodes the given Base64URL back into string. + +## Type signature + + +```typescript +(text: string, context?: DecodeContext) => string +``` + + +## Examples + + +```javascript +decode("PDw_Pz8-Pg"); // ⇒ "<>" +``` + diff --git a/encoding/base64url/decode.ts b/encoding/base64url/decode.ts new file mode 100644 index 00000000..a6de6290 --- /dev/null +++ b/encoding/base64url/decode.ts @@ -0,0 +1,20 @@ +/* eslint-env browser, node */ +import fromByteString from "../byteString/from"; +import { DecodeContext } from "./DecodeContext.interface"; + +const ENCODING = "utf-8"; + +const atobImplementation = ( + text: string, + context: DecodeContext | undefined = typeof window !== "undefined" + ? window + : undefined +) => + context + ? new context.TextDecoder(ENCODING).decode( + new Uint8Array(fromByteString(context.atob(text))) + ) + : Buffer.from(text, "base64").toString(ENCODING); + +export default (text: string, context?: DecodeContext) => + atobImplementation(text.replace(/-/g, "+").replace(/_/g, "/"), context); diff --git a/encoding/base64url/decodeBytes.js b/encoding/base64url/decodeBytes.js new file mode 100644 index 00000000..7328e957 --- /dev/null +++ b/encoding/base64url/decodeBytes.js @@ -0,0 +1,9 @@ +/* eslint-env browser, node */ +import decode from "./decode.js"; +import fromByteString from "../byteString/from.js"; + +export default (text, context) => { + const decoded = decode(text, context); + + return fromByteString(decoded); +}; diff --git a/encoding/base64url/decodeBytes.json b/encoding/base64url/decodeBytes.json new file mode 100644 index 00000000..9630f2f7 --- /dev/null +++ b/encoding/base64url/decodeBytes.json @@ -0,0 +1,12 @@ +{ + "name": "decodeBytes", + "description": "Decodes the given Base64URL back into byte array.", + "signature": "(text: string, context?: DecodeContext) => number[]", + "examples": [ + { + "language": "javascript", + "content": "decodeBytes(\"w4Jnw6vCp20-bBsQfA\");\n// ⇒ [0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c]" + } + ], + "questions": ["TODO: List questions that may this function answers."] +} diff --git a/encoding/base64url/decodeBytes.md b/encoding/base64url/decodeBytes.md new file mode 100644 index 00000000..37a827af --- /dev/null +++ b/encoding/base64url/decodeBytes.md @@ -0,0 +1,20 @@ +# decodeBytes + +Decodes the given Base64URL back into byte array. + +## Type signature + + +```typescript +(text: string, context?: DecodeContext) => number[] +``` + + +## Examples + + +```javascript +decodeBytes("w4Jnw6vCp20-bBsQfA"); +// ⇒ [0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c] +``` + diff --git a/encoding/base64url/decodeBytes.ts b/encoding/base64url/decodeBytes.ts new file mode 100644 index 00000000..249ab5ae --- /dev/null +++ b/encoding/base64url/decodeBytes.ts @@ -0,0 +1,10 @@ +/* eslint-env browser, node */ +import decode from "./decode"; +import fromByteString from "../byteString/from"; +import { DecodeContext } from "./DecodeContext.interface"; + +export default (text: string, context?: DecodeContext) => { + const decoded = decode(text, context); + + return fromByteString(decoded); +}; diff --git a/encoding/base64url/encode.js b/encoding/base64url/encode.js new file mode 100644 index 00000000..7876e8a8 --- /dev/null +++ b/encoding/base64url/encode.js @@ -0,0 +1,21 @@ +/* eslint-env browser, node */ +import toByteString from "../byteString/to.js"; + +const toArray = typedArray => [...typedArray]; +const ENCODING = "utf-8"; + +const btoaImplementation = ( + text, + context = typeof window !== "undefined" ? window : undefined +) => + context + ? context.btoa( + toByteString(toArray(new context.TextEncoder().encode(text))) + ) + : Buffer.from(text, ENCODING).toString("base64"); + +export default (text, context) => + btoaImplementation(text, context) + .replace(/=/g, "") + .replace(/\+/g, "-") + .replace(/\//g, "_"); diff --git a/encoding/base64url/encode.json b/encoding/base64url/encode.json new file mode 100644 index 00000000..e63c07d4 --- /dev/null +++ b/encoding/base64url/encode.json @@ -0,0 +1,12 @@ +{ + "name": "encode", + "description": "Encodes the given string into Base64URL.", + "signature": "(text: string, context?: EncodeContext) => string", + "examples": [ + { + "language": "javascript", + "content": "encode(\"<>\"); // ⇒ \"PDw_Pz8-Pg\"" + } + ], + "questions": ["TODO: List questions that may this function answers."] +} diff --git a/encoding/base64url/encode.md b/encoding/base64url/encode.md new file mode 100644 index 00000000..7d24bf7d --- /dev/null +++ b/encoding/base64url/encode.md @@ -0,0 +1,19 @@ +# encode + +Encodes the given string into Base64URL. + +## Type signature + + +```typescript +(text: string, context?: EncodeContext) => string +``` + + +## Examples + + +```javascript +encode("<>"); // ⇒ "PDw_Pz8-Pg" +``` + diff --git a/encoding/base64url/encode.ts b/encoding/base64url/encode.ts new file mode 100644 index 00000000..d32c8285 --- /dev/null +++ b/encoding/base64url/encode.ts @@ -0,0 +1,25 @@ +/* eslint-env browser, node */ +import toByteString from "../byteString/to"; +import { EncodeContext } from "./EncodeContext.interface"; + +const toArray = (typedArray: Uint8Array): number[] => [...typedArray]; + +const ENCODING = "utf-8"; + +const btoaImplementation = ( + text: string, + context: EncodeContext | undefined = typeof window !== "undefined" + ? window + : undefined +) => + context + ? context.btoa( + toByteString(toArray(new context.TextEncoder().encode(text))) + ) + : Buffer.from(text, ENCODING).toString("base64"); + +export default (text: string, context?: EncodeContext) => + btoaImplementation(text, context) + .replace(/=/g, "") + .replace(/\+/g, "-") + .replace(/\//g, "_"); diff --git a/encoding/base64url/encodeBytes.js b/encoding/base64url/encodeBytes.js new file mode 100644 index 00000000..e4935e7b --- /dev/null +++ b/encoding/base64url/encodeBytes.js @@ -0,0 +1,9 @@ +/* eslint-env browser, node */ +import encode from "./encode.js"; +import toByteString from "../byteString/to.js"; + +export default (bytes, context) => { + const sourceText = toByteString(bytes); + + return encode(sourceText, context); +}; diff --git a/encoding/base64url/encodeBytes.json b/encoding/base64url/encodeBytes.json new file mode 100644 index 00000000..1f00edd7 --- /dev/null +++ b/encoding/base64url/encodeBytes.json @@ -0,0 +1,12 @@ +{ + "name": "encodeBytes", + "description": "Encodes the given bytes into Base64URL.", + "signature": "(bytes: number[], context?: EncodeContext) => string", + "examples": [ + { + "language": "javascript", + "content": "encodeBytes([0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c]);\n// ⇒ \"w4Jnw6vCp20-bBsQfA\"" + } + ], + "questions": ["TODO: List questions that may this function answers."] +} diff --git a/encoding/base64url/encodeBytes.md b/encoding/base64url/encodeBytes.md new file mode 100644 index 00000000..2015a68c --- /dev/null +++ b/encoding/base64url/encodeBytes.md @@ -0,0 +1,20 @@ +# encodeBytes + +Encodes the given bytes into Base64URL. + +## Type signature + + +```typescript +(bytes: number[], context?: EncodeContext) => string +``` + + +## Examples + + +```javascript +encodeBytes([0xc2, 0x67, 0xeb, 0xa7, 0x6d, 0x3e, 0x6c, 0x1b, 0x10, 0x7c]); +// ⇒ "w4Jnw6vCp20-bBsQfA" +``` + diff --git a/encoding/base64url/encodeBytes.ts b/encoding/base64url/encodeBytes.ts new file mode 100644 index 00000000..833e1e8c --- /dev/null +++ b/encoding/base64url/encodeBytes.ts @@ -0,0 +1,10 @@ +/* eslint-env browser, node */ +import encode from "./encode"; +import toByteString from "../byteString/to"; +import { EncodeContext } from "./EncodeContext.interface"; + +export default (bytes: number[], context?: EncodeContext) => { + const sourceText = toByteString(bytes); + + return encode(sourceText, context); +}; diff --git a/encoding/base64url/fromBase64.js b/encoding/base64url/fromBase64.js new file mode 100644 index 00000000..a03f49f6 --- /dev/null +++ b/encoding/base64url/fromBase64.js @@ -0,0 +1,5 @@ +export default base64 => + base64 + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); diff --git a/encoding/base64url/fromBase64.json b/encoding/base64url/fromBase64.json new file mode 100644 index 00000000..fdd321e1 --- /dev/null +++ b/encoding/base64url/fromBase64.json @@ -0,0 +1,12 @@ +{ + "name": "fromBase64", + "description": "Converts Base64 string into Base64URL one.", + "signature": "(base64: string) => string", + "examples": [ + { + "language": "javascript", + "content": "fromBase64(\"PDw/Pz8+Pg==\"); // ⇒ \"PDw_Pz8-Pg\"" + } + ], + "questions": ["TODO: List questions that may this function answers."] +} diff --git a/encoding/base64url/fromBase64.md b/encoding/base64url/fromBase64.md new file mode 100644 index 00000000..fa877d00 --- /dev/null +++ b/encoding/base64url/fromBase64.md @@ -0,0 +1,19 @@ +# fromBase64 + +Converts Base64 string into Base64URL one. + +## Type signature + + +```typescript +(base64: string) => string +``` + + +## Examples + + +```javascript +fromBase64("PDw/Pz8+Pg=="); // ⇒ "PDw_Pz8-Pg" +``` + diff --git a/encoding/base64url/fromBase64.ts b/encoding/base64url/fromBase64.ts new file mode 100644 index 00000000..f5b9f97c --- /dev/null +++ b/encoding/base64url/fromBase64.ts @@ -0,0 +1,5 @@ +export default (base64: string) => + base64 + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); diff --git a/encoding/base64url/index.js b/encoding/base64url/index.js new file mode 100644 index 00000000..5b34a5d6 --- /dev/null +++ b/encoding/base64url/index.js @@ -0,0 +1,17 @@ +import decode from "./decode.js"; +import decodeBytes from "./decodeBytes.js"; +import encode from "./encode.js"; +import encodeBytes from "./encodeBytes.js"; +import fromBase64 from "./fromBase64.js"; +import toBase64 from "./toBase64.js"; + +export { decode, decodeBytes, encode, encodeBytes, fromBase64, toBase64 }; + +export default { + decode, + decodeBytes, + encode, + encodeBytes, + fromBase64, + toBase64 +}; diff --git a/encoding/base64url/index.ts b/encoding/base64url/index.ts new file mode 100644 index 00000000..51e49552 --- /dev/null +++ b/encoding/base64url/index.ts @@ -0,0 +1,17 @@ +import decode from "./decode"; +import decodeBytes from "./decodeBytes"; +import encode from "./encode"; +import encodeBytes from "./encodeBytes"; +import fromBase64 from "./fromBase64"; +import toBase64 from "./toBase64"; + +export { decode, decodeBytes, encode, encodeBytes, fromBase64, toBase64 }; + +export default { + decode, + decodeBytes, + encode, + encodeBytes, + fromBase64, + toBase64 +}; diff --git a/encoding/base64url/toBase64.js b/encoding/base64url/toBase64.js new file mode 100644 index 00000000..ef94bd62 --- /dev/null +++ b/encoding/base64url/toBase64.js @@ -0,0 +1,5 @@ +export default base64Url => + base64Url + .replace(/-/g, "+") + .replace(/_/g, "/") + .padEnd(Math.ceil(base64Url.length / 4) * 4, "="); diff --git a/encoding/base64url/toBase64.json b/encoding/base64url/toBase64.json new file mode 100644 index 00000000..2b7631e6 --- /dev/null +++ b/encoding/base64url/toBase64.json @@ -0,0 +1,12 @@ +{ + "name": "toBase64", + "description": "Converts Base64URL string into Base64 one.", + "signature": "(base64Url: string) => string", + "examples": [ + { + "language": "javascript", + "content": "toBase64(\"PDw_Pz8-Pg\"); // ⇒ \"PDw/Pz8+Pg==\"" + } + ], + "questions": ["TODO: List questions that may this function answers."] +} diff --git a/encoding/base64url/toBase64.md b/encoding/base64url/toBase64.md new file mode 100644 index 00000000..5856bf2d --- /dev/null +++ b/encoding/base64url/toBase64.md @@ -0,0 +1,19 @@ +# toBase64 + +Converts Base64URL string into Base64 one. + +## Type signature + + +```typescript +(base64Url: string) => string +``` + + +## Examples + + +```javascript +toBase64("PDw_Pz8-Pg"); // ⇒ "PDw/Pz8+Pg==" +``` + diff --git a/encoding/base64url/toBase64.ts b/encoding/base64url/toBase64.ts new file mode 100644 index 00000000..146ef611 --- /dev/null +++ b/encoding/base64url/toBase64.ts @@ -0,0 +1,5 @@ +export default (base64Url: string) => + base64Url + .replace(/-/g, "+") + .replace(/_/g, "/") + .padEnd(Math.ceil(base64Url.length / 4) * 4, "="); diff --git a/encoding/byteString/README.md b/encoding/byteString/README.md new file mode 100644 index 00000000..78f90898 --- /dev/null +++ b/encoding/byteString/README.md @@ -0,0 +1,39 @@ +# from + +Converts string to byte array. + +## Type signature + + +```typescript +(byteString: string) => number[] +``` + + +## Examples + + +```javascript +from("PQR"); // ⇒ [80, 81, 82] +``` + + +# to + +Coverts byte array to string. + +## Type signature + + +```typescript +(bytes: number[]) => string +``` + + +## Examples + + +```javascript +to([0x50, 0x51, 0x52]); // ⇒ "PQR" +``` + diff --git a/encoding/byteString/from.js b/encoding/byteString/from.js new file mode 100644 index 00000000..dac94828 --- /dev/null +++ b/encoding/byteString/from.js @@ -0,0 +1 @@ +export default byteString => [...byteString].map(_ => _.codePointAt(0) || 0); diff --git a/encoding/byteString/from.json b/encoding/byteString/from.json new file mode 100644 index 00000000..fee05323 --- /dev/null +++ b/encoding/byteString/from.json @@ -0,0 +1,12 @@ +{ + "name": "from", + "description": "Converts string to byte array.", + "signature": "(byteString: string) => number[]", + "examples": [ + { + "language": "javascript", + "content": "from(\"PQR\"); // ⇒ [80, 81, 82]" + } + ], + "questions": ["TODO: List questions that may this function answers."] +} diff --git a/encoding/byteString/from.md b/encoding/byteString/from.md new file mode 100644 index 00000000..64050ff5 --- /dev/null +++ b/encoding/byteString/from.md @@ -0,0 +1,19 @@ +# from + +Converts string to byte array. + +## Type signature + + +```typescript +(byteString: string) => number[] +``` + + +## Examples + + +```javascript +from("PQR"); // ⇒ [80, 81, 82] +``` + diff --git a/encoding/byteString/from.ts b/encoding/byteString/from.ts new file mode 100644 index 00000000..c0ebd1b3 --- /dev/null +++ b/encoding/byteString/from.ts @@ -0,0 +1,2 @@ +export default (byteString: string): number[] => + [...byteString].map(_ => _.codePointAt(0) || 0); diff --git a/encoding/byteString/index.js b/encoding/byteString/index.js new file mode 100644 index 00000000..fa795a66 --- /dev/null +++ b/encoding/byteString/index.js @@ -0,0 +1,6 @@ +import from from "./from.js"; +import to from "./to.js"; + +export { from, to }; + +export default { from, to }; diff --git a/encoding/byteString/index.ts b/encoding/byteString/index.ts new file mode 100644 index 00000000..f1f3d34f --- /dev/null +++ b/encoding/byteString/index.ts @@ -0,0 +1,6 @@ +import from from "./from"; +import to from "./to"; + +export { from, to }; + +export default { from, to }; diff --git a/encoding/byteString/to.js b/encoding/byteString/to.js new file mode 100644 index 00000000..b4a4302d --- /dev/null +++ b/encoding/byteString/to.js @@ -0,0 +1 @@ +export default bytes => bytes.map(_ => String.fromCharCode(_)).join(""); diff --git a/encoding/byteString/to.json b/encoding/byteString/to.json new file mode 100644 index 00000000..c63fe0c8 --- /dev/null +++ b/encoding/byteString/to.json @@ -0,0 +1,12 @@ +{ + "name": "to", + "description": "Coverts byte array to string.", + "signature": "(bytes: number[]) => string", + "examples": [ + { + "language": "javascript", + "content": "to([0x50, 0x51, 0x52]); // ⇒ \"PQR\"" + } + ], + "questions": ["TODO: List questions that may this function answers."] +} diff --git a/encoding/byteString/to.md b/encoding/byteString/to.md new file mode 100644 index 00000000..ff0c0a8c --- /dev/null +++ b/encoding/byteString/to.md @@ -0,0 +1,19 @@ +# to + +Coverts byte array to string. + +## Type signature + + +```typescript +(bytes: number[]) => string +``` + + +## Examples + + +```javascript +to([0x50, 0x51, 0x52]); // ⇒ "PQR" +``` + diff --git a/encoding/byteString/to.ts b/encoding/byteString/to.ts new file mode 100644 index 00000000..7066f842 --- /dev/null +++ b/encoding/byteString/to.ts @@ -0,0 +1,2 @@ +export default (bytes: number[]) => + bytes.map(_ => String.fromCharCode(_)).join(""); diff --git a/encoding/index.js b/encoding/index.js index 78553ab2..48b2fa76 100644 --- a/encoding/index.js +++ b/encoding/index.js @@ -1,5 +1,6 @@ -import base64url from "./base64url.js"; +import base64url from "./base64url/index.js"; +import byteString from "./byteString/index.js"; -export { base64url }; +export { base64url, byteString }; -export default { base64url }; +export default { base64url, byteString }; diff --git a/encoding/index.ts b/encoding/index.ts index 347392d6..6de7c628 100644 --- a/encoding/index.ts +++ b/encoding/index.ts @@ -1,5 +1,6 @@ -import base64url from "./base64url"; +import base64url from "./base64url/index"; +import byteString from "./byteString/index"; -export { base64url }; +export { base64url, byteString }; -export default { base64url }; +export default { base64url, byteString }; diff --git a/ignore.js b/ignore.js index a45af36b..7a1e106f 100644 --- a/ignore.js +++ b/ignore.js @@ -21,6 +21,7 @@ const ignoredFiles = [ "indexReadme.js", "jest.config.js", "LICENSE", + "mappings.js", "normalizeLineEndings.js", "package-lock.json", "package.json", diff --git a/indexReadme.js b/indexReadme.js index 546bf64b..1a6feb2f 100644 --- a/indexReadme.js +++ b/indexReadme.js @@ -4,6 +4,7 @@ import { promises, existsSync } from "fs"; import path from "path"; import ignored from "./ignore.js"; +import mappings from "./mappings.js"; const [ignoredFiles, ignoredDirectories] = ignored; @@ -15,11 +16,7 @@ const { const [, , cwd = process.cwd()] = process.argv; -const mapping = { - function: "_function" -}; - -const identifier = name => mapping[name] || name; +const identifier = name => mappings[name] || name; // Do not match type definition files *.d.ts but match *.ts: // https://stackoverflow.com/a/43493203/1384679 diff --git a/mappings.js b/mappings.js new file mode 100644 index 00000000..0901159d --- /dev/null +++ b/mappings.js @@ -0,0 +1,3 @@ +export default { + function: "_function" +}; diff --git a/normalizeLineEndings.js b/normalizeLineEndings.js index 02ee26dd..49fef276 100644 --- a/normalizeLineEndings.js +++ b/normalizeLineEndings.js @@ -5,12 +5,12 @@ import path from "path"; import os from "os"; import pQueue from "p-queue"; +import ignored from "./ignore.js"; + const CONCURRENCY = Math.max(1, os.cpus().length - 1); const { default: PQueue } = pQueue; -import ignored from "./ignore.js"; - const [, ignoredDirectories] = ignored; const { diff --git a/regenerate.js b/regenerate.js index 99391995..2cab411c 100644 --- a/regenerate.js +++ b/regenerate.js @@ -4,6 +4,7 @@ import { promises } from "fs"; import path from "path"; import ignored from "./ignore.js"; +import mappings from "./mappings.js"; const [ignoredFiles, ignoredDirectories] = ignored; @@ -11,11 +12,7 @@ const { readdir: readDirectoryAsync, writeFile: writeFileAsync } = promises; const [, , cwd = process.cwd()] = process.argv; -const mapping = { - function: "_function" -}; - -const identifier = name => mapping[name] || name; +const identifier = name => mappings[name] || name; // Do not match type definition files *.d.ts but match *.ts: // https://stackoverflow.com/a/43493203/1384679