std/encoding/base64url_stream.ts
Doctor 0f7a71f96b
feat(encoding/unstable): adds streaming versions for hex, base32, base32hex, base64 and base64url (#4915)
* feat(encoding): adds streaming versions of several encoding methods.

Adds streaming methods for:
- Hex
- Base32
- Base64
- Base64Url

* move(encoding): streaming versions to their own modules.

* fix(encoding): jsdoc examples

* fix(encoding): error when chunk's are smaller than the minimum size.

* add(encoding): Base32Hex Encoder/Decoder Streams

* fix(encoding): mod.ts not exporting ./base32hex_stream.ts

* hex stream

* RandomSliceStream

* polish

* base32

* base64hex

* base64

* base64url

* tweak

* fix

---------

Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com>
2024-08-22 15:02:36 +10:00

105 lines
3.0 KiB
TypeScript

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/**
* Utilities for encoding and decoding to and from base64url in a streaming manner.
*
* @experimental **UNSTABLE**: New API, yet to be vetted.
*
* @module
*/
import { decodeBase64Url, encodeBase64Url } from "./base64url.ts";
/**
* Converts a Uint8Array stream into a base64url-encoded stream.
*
* @experimental **UNSTABLE**: New API, yet to be vetted.
*
* @see {@link https://www.rfc-editor.org/rfc/rfc4648.html#section-5}
*
* @example Usage
* ```ts
* import { assertEquals } from "@std/assert";
* import { encodeBase64Url } from "@std/encoding/base64url";
* import { Base64UrlEncoderStream } from "@std/encoding/base64url-stream";
* import { toText } from "@std/streams/to-text";
*
* const stream = ReadableStream.from(["Hello,", " world!"])
* .pipeThrough(new TextEncoderStream())
* .pipeThrough(new Base64UrlEncoderStream());
*
* assertEquals(await toText(stream), encodeBase64Url(new TextEncoder().encode("Hello, world!")));
* ```
*/
export class Base64UrlEncoderStream
extends TransformStream<Uint8Array, string> {
constructor() {
let push = new Uint8Array(0);
super({
transform(chunk, controller) {
const concat = new Uint8Array(push.length + chunk.length);
concat.set(push);
concat.set(chunk, push.length);
const remainder = -concat.length % 3;
controller.enqueue(
encodeBase64Url(concat.slice(0, remainder || undefined)),
);
push = remainder ? concat.slice(remainder) : new Uint8Array(0);
},
flush(controller) {
if (push.length) {
controller.enqueue(encodeBase64Url(push));
}
},
});
}
}
/**
* Decodes a base64url-encoded stream into a Uint8Array stream.
*
* @experimental **UNSTABLE**: New API, yet to be vetted.
*
* @see {@link https://www.rfc-editor.org/rfc/rfc4648.html#section-5}
*
* @example Usage
* ```ts
* import { assertEquals } from "@std/assert";
* import { encodeBase64Url } from "@std/encoding/base64url";
* import { Base64UrlDecoderStream } from "@std/encoding/base64url-stream";
* import { toText } from "@std/streams/to-text";
*
* const stream = ReadableStream.from(["SGVsbG8s", "IHdvcmxkIQ"])
* .pipeThrough(new Base64UrlDecoderStream())
* .pipeThrough(new TextDecoderStream());
*
* assertEquals(await toText(stream), "Hello, world!");
* ```
*/
export class Base64UrlDecoderStream
extends TransformStream<string, Uint8Array> {
constructor() {
let push = "";
super({
transform(chunk, controller) {
push += chunk;
if (push.length < 4) {
return;
}
const remainder = -push.length % 4;
controller.enqueue(
decodeBase64Url(push.slice(0, remainder || undefined)),
);
push = remainder ? push.slice(remainder) : "";
},
flush(controller) {
if (push.length) {
controller.enqueue(decodeBase64Url(push));
}
},
});
}
}