mirror of
https://github.com/bitwarden/browser
synced 2026-02-05 19:23:19 +00:00
refactor: introduce @bitwarden/encoding
This commit is contained in:
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -103,6 +103,7 @@ libs/core-test-utils @bitwarden/team-platform-dev
|
||||
libs/state @bitwarden/team-platform-dev
|
||||
libs/state-test-utils @bitwarden/team-platform-dev
|
||||
libs/device-type @bitwarden/team-platform-dev
|
||||
libs/encoding @bitwarden/team-platform-dev
|
||||
# Web utils used across app and connectors
|
||||
apps/web/src/utils/ @bitwarden/team-platform-dev
|
||||
# Web core and shared files
|
||||
|
||||
@@ -71,6 +71,7 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromB64ToArray(str: string): Uint8Array {
|
||||
if (str == null) {
|
||||
return null;
|
||||
@@ -88,10 +89,12 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromUrlB64ToArray(str: string): Uint8Array {
|
||||
return Utils.fromB64ToArray(Utils.fromUrlB64ToB64(str));
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromHexToArray(str: string): Uint8Array {
|
||||
if (Utils.isNode) {
|
||||
return new Uint8Array(Buffer.from(str, "hex"));
|
||||
@@ -104,6 +107,7 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromUtf8ToArray(str: string): Uint8Array {
|
||||
if (Utils.isNode) {
|
||||
return new Uint8Array(Buffer.from(str, "utf8"));
|
||||
@@ -117,6 +121,7 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromByteStringToArray(str: string): Uint8Array {
|
||||
if (str == null) {
|
||||
return null;
|
||||
@@ -128,6 +133,7 @@ export class Utils {
|
||||
return arr;
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromBufferToB64(buffer: ArrayBuffer): string {
|
||||
if (buffer == null) {
|
||||
return null;
|
||||
@@ -144,23 +150,28 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromBufferToUrlB64(buffer: ArrayBuffer): string {
|
||||
return Utils.fromB64toUrlB64(Utils.fromBufferToB64(buffer));
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromB64toUrlB64(b64Str: string) {
|
||||
return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromBufferToUtf8(buffer: ArrayBuffer): string {
|
||||
return BufferLib.from(buffer).toString("utf8");
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromBufferToByteString(buffer: ArrayBuffer): string {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buffer));
|
||||
}
|
||||
|
||||
// ref: https://stackoverflow.com/a/40031979/1090359
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromBufferToHex(buffer: ArrayBuffer): string {
|
||||
if (Utils.isNode) {
|
||||
return Buffer.from(buffer).toString("hex");
|
||||
@@ -179,6 +190,7 @@ export class Utils {
|
||||
* @param {string} hexString - A string of hexadecimal characters.
|
||||
* @returns {ArrayBuffer} The ArrayBuffer representation of the hex string.
|
||||
*/
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static hexStringToArrayBuffer(hexString: string): ArrayBuffer {
|
||||
// Check if the hexString has an even length, as each hex digit represents half a byte (4 bits),
|
||||
// and it takes two hex digits to represent a full byte (8 bits).
|
||||
@@ -209,6 +221,7 @@ export class Utils {
|
||||
return arrayBuffer;
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromUrlB64ToB64(urlB64Str: string): string {
|
||||
let output = urlB64Str.replace(/-/g, "+").replace(/_/g, "/");
|
||||
switch (output.length % 4) {
|
||||
@@ -227,10 +240,12 @@ export class Utils {
|
||||
return output;
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromUrlB64ToUtf8(urlB64Str: string): string {
|
||||
return Utils.fromB64ToUtf8(Utils.fromUrlB64ToB64(urlB64Str));
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromUtf8ToB64(utfStr: string): string {
|
||||
if (Utils.isNode) {
|
||||
return Buffer.from(utfStr, "utf8").toString("base64");
|
||||
@@ -239,10 +254,12 @@ export class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromUtf8ToUrlB64(utfStr: string): string {
|
||||
return Utils.fromBufferToUrlB64(Utils.fromUtf8ToArray(utfStr));
|
||||
}
|
||||
|
||||
/** @deprecated Moved to @bitwarden/encoding as a directly exported function*/
|
||||
static fromB64ToUtf8(b64Str: string): string {
|
||||
if (Utils.isNode) {
|
||||
return Buffer.from(b64Str, "base64").toString("utf8");
|
||||
|
||||
5
libs/encoding/README.md
Normal file
5
libs/encoding/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# encoding
|
||||
|
||||
Owned by: platform
|
||||
|
||||
Encoding helpers
|
||||
3
libs/encoding/eslint.config.mjs
Normal file
3
libs/encoding/eslint.config.mjs
Normal file
@@ -0,0 +1,3 @@
|
||||
import baseConfig from "../../eslint.config.mjs";
|
||||
|
||||
export default [...baseConfig];
|
||||
10
libs/encoding/jest.config.js
Normal file
10
libs/encoding/jest.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
displayName: "encoding",
|
||||
preset: "../../jest.preset.js",
|
||||
testEnvironment: "node",
|
||||
transform: {
|
||||
"^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }],
|
||||
},
|
||||
moduleFileExtensions: ["ts", "js", "html"],
|
||||
coverageDirectory: "../../coverage/libs/encoding",
|
||||
};
|
||||
11
libs/encoding/package.json
Normal file
11
libs/encoding/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@bitwarden/encoding",
|
||||
"version": "0.0.1",
|
||||
"description": "Encoding helpers",
|
||||
"private": true,
|
||||
"type": "commonjs",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "GPL-3.0",
|
||||
"author": "platform"
|
||||
}
|
||||
33
libs/encoding/project.json
Normal file
33
libs/encoding/project.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "encoding",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/encoding/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/encoding",
|
||||
"main": "libs/encoding/src/index.ts",
|
||||
"tsConfig": "libs/encoding/tsconfig.lib.json",
|
||||
"assets": ["libs/encoding/*.md"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/encoding/**/*.ts"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/encoding/jest.config.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
libs/encoding/src/encoding.spec.ts
Normal file
8
libs/encoding/src/encoding.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as lib from "./index";
|
||||
|
||||
describe("encoding", () => {
|
||||
// This test will fail until something is exported from index.ts
|
||||
it("should work", () => {
|
||||
expect(lib).toBeDefined();
|
||||
});
|
||||
});
|
||||
180
libs/encoding/src/index.ts
Normal file
180
libs/encoding/src/index.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import { Buffer as BufferLib } from "buffer/";
|
||||
|
||||
const isNode = typeof (globalThis as any)?.process?.versions?.node === "string";
|
||||
// We cast globalThis to any to keep typescript happy
|
||||
const g: any = globalThis as any;
|
||||
|
||||
export function fromB64ToArray(str: string): Uint8Array | null {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
if (isNode) {
|
||||
return new Uint8Array(Buffer.from(str, "base64"));
|
||||
} else {
|
||||
const binaryString = g.atob(str);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
export function fromUrlB64ToArray(str: string): Uint8Array | null {
|
||||
return fromB64ToArray(fromUrlB64ToB64(str));
|
||||
}
|
||||
|
||||
export function fromHexToArray(str: string): Uint8Array {
|
||||
if (isNode) {
|
||||
return new Uint8Array(Buffer.from(str, "hex"));
|
||||
} else {
|
||||
const bytes = new Uint8Array(str.length / 2);
|
||||
for (let i = 0; i < str.length; i += 2) {
|
||||
bytes[i / 2] = parseInt(str.substr(i, 2), 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
export function fromUtf8ToArray(str: string): Uint8Array {
|
||||
if (isNode) {
|
||||
return new Uint8Array(Buffer.from(str, "utf8"));
|
||||
} else {
|
||||
const strUtf8 = unescape(encodeURIComponent(str));
|
||||
const arr = new Uint8Array(strUtf8.length);
|
||||
for (let i = 0; i < strUtf8.length; i++) {
|
||||
arr[i] = strUtf8.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
export function fromByteStringToArray(str: string): Uint8Array | null {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
const arr = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
arr[i] = str.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
export function fromBufferToB64(buffer: ArrayBuffer): string | null {
|
||||
if (buffer == null) {
|
||||
return null;
|
||||
}
|
||||
if (isNode) {
|
||||
return Buffer.from(buffer).toString("base64");
|
||||
} else {
|
||||
let binary = "";
|
||||
const bytes = new Uint8Array(buffer);
|
||||
for (let i = 0; i < bytes.byteLength; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return g.btoa(binary);
|
||||
}
|
||||
}
|
||||
|
||||
export function fromBufferToUrlB64(buffer: ArrayBuffer): string | null {
|
||||
const b64 = fromBufferToB64(buffer);
|
||||
return b64 == null ? null : fromB64toUrlB64(b64);
|
||||
}
|
||||
|
||||
export function fromB64toUrlB64(b64Str: string): string {
|
||||
return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||
}
|
||||
|
||||
export function fromBufferToUtf8(buffer: ArrayBuffer): string {
|
||||
return BufferLib.from(buffer).toString("utf8");
|
||||
}
|
||||
|
||||
export function fromBufferToByteString(buffer: ArrayBuffer): string {
|
||||
return String.fromCharCode.apply(null, new Uint8Array(buffer) as unknown as number[]);
|
||||
}
|
||||
|
||||
// ref: https://stackoverflow.com/a/40031979/1090359
|
||||
export function fromBufferToHex(buffer: ArrayBuffer): string {
|
||||
if (isNode) {
|
||||
return Buffer.from(buffer).toString("hex");
|
||||
} else {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
return Array.prototype.map
|
||||
.call(bytes, (x: number) => ("00" + x.toString(16)).slice(-2))
|
||||
.join("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a hex string to an ArrayBuffer.
|
||||
* Note: this doesn't need any Node specific code as parseInt() / ArrayBuffer / Uint8Array
|
||||
* work the same in Node and the browser.
|
||||
* @param {string} hexString - A string of hexadecimal characters.
|
||||
* @returns {ArrayBuffer} The ArrayBuffer representation of the hex string.
|
||||
*/
|
||||
export function hexStringToArrayBuffer(hexString: string): ArrayBuffer {
|
||||
// Check if the hexString has an even length, as each hex digit represents half a byte (4 bits),
|
||||
// and it takes two hex digits to represent a full byte (8 bits).
|
||||
if (hexString.length % 2 !== 0) {
|
||||
throw "HexString has to be an even length";
|
||||
}
|
||||
|
||||
// Create an ArrayBuffer with a length that is half the length of the hex string,
|
||||
// because each pair of hex digits will become a single byte.
|
||||
const arrayBuffer = new ArrayBuffer(hexString.length / 2);
|
||||
|
||||
// Create a Uint8Array view on top of the ArrayBuffer (each position represents a byte)
|
||||
// as ArrayBuffers cannot be edited directly.
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
|
||||
// Loop through the bytes
|
||||
for (let i = 0; i < uint8Array.length; i++) {
|
||||
// Extract two hex characters (1 byte)
|
||||
const hexByte = hexString.substr(i * 2, 2);
|
||||
|
||||
// Convert hexByte into a decimal value from base 16. (ex: ff --> 255)
|
||||
const byteValue = parseInt(hexByte, 16);
|
||||
|
||||
// Place the byte value into the uint8Array
|
||||
uint8Array[i] = byteValue;
|
||||
}
|
||||
|
||||
return arrayBuffer;
|
||||
}
|
||||
|
||||
export function fromUrlB64ToB64(urlB64Str: string): string {
|
||||
let output = urlB64Str.replace(/-/g, "+").replace(/_/g, "/");
|
||||
switch (output.length % 4) {
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
output += "==";
|
||||
break;
|
||||
case 3:
|
||||
output += "=";
|
||||
break;
|
||||
default:
|
||||
throw new Error("Illegal base64url string!");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
export function fromUtf8ToB64(utfStr: string): string {
|
||||
if (isNode) {
|
||||
return Buffer.from(utfStr, "utf8").toString("base64");
|
||||
} else {
|
||||
return BufferLib.from(utfStr, "utf8").toString("base64");
|
||||
}
|
||||
}
|
||||
|
||||
export function fromUtf8ToUrlB64(utfStr: string): string | null {
|
||||
return fromBufferToUrlB64(fromUtf8ToArray(utfStr));
|
||||
}
|
||||
|
||||
export function fromB64ToUtf8(b64Str: string): string {
|
||||
if (isNode) {
|
||||
return Buffer.from(b64Str, "base64").toString("utf8");
|
||||
} else {
|
||||
return BufferLib.from(b64Str, "base64").toString("utf8");
|
||||
}
|
||||
}
|
||||
6
libs/encoding/tsconfig.eslint.json
Normal file
6
libs/encoding/tsconfig.eslint.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": ["src/**/*.ts", "src/**/*.js"],
|
||||
"exclude": ["**/build", "**/dist"]
|
||||
}
|
||||
13
libs/encoding/tsconfig.json
Normal file
13
libs/encoding/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
libs/encoding/tsconfig.lib.json
Normal file
10
libs/encoding/tsconfig.lib.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["jest.config.js", "src/**/*.spec.ts"]
|
||||
}
|
||||
10
libs/encoding/tsconfig.spec.json
Normal file
10
libs/encoding/tsconfig.spec.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -347,6 +347,11 @@
|
||||
"version": "0.0.0",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"libs/encoding": {
|
||||
"name": "@bitwarden/encoding",
|
||||
"version": "0.0.1",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"libs/guid": {
|
||||
"name": "@bitwarden/guid",
|
||||
"version": "0.0.1",
|
||||
@@ -4612,6 +4617,10 @@
|
||||
"resolved": "libs/dirt/card",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/encoding": {
|
||||
"resolved": "libs/encoding",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/generator-components": {
|
||||
"resolved": "libs/tools/generator/components",
|
||||
"link": true
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"@bitwarden/core-test-utils": ["libs/core-test-utils/src/index.ts"],
|
||||
"@bitwarden/device-type": ["libs/device-type/src/index.ts"],
|
||||
"@bitwarden/dirt-card": ["./libs/dirt/card/src"],
|
||||
"@bitwarden/encoding": ["libs/encoding/src/index.ts"],
|
||||
"@bitwarden/generator-components": ["./libs/tools/generator/components/src"],
|
||||
"@bitwarden/generator-core": ["./libs/tools/generator/core/src"],
|
||||
"@bitwarden/generator-history": ["./libs/tools/generator/extensions/history/src"],
|
||||
|
||||
Reference in New Issue
Block a user