UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

196 lines (175 loc) 4.94 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/webstreams/encoding.js import { TextDecoder, TextEncoder } from "nstdlib/lib/internal/encoding"; import { TransformStream } from "nstdlib/lib/internal/webstreams/transformstream"; import { customInspect } from "nstdlib/lib/internal/webstreams/util"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import { customInspectSymbol as kInspect, kEmptyObject, kEnumerableProperty, } from "nstdlib/lib/internal/util"; const { ERR_INVALID_THIS } = __codes__; /** * @typedef {import('./readablestream').ReadableStream} ReadableStream * @typedef {import('./writablestream').WritableStream} WritableStream */ class TextEncoderStream { #pendingHighSurrogate = null; #handle; #transform; constructor() { this.#handle = new TextEncoder(); this.#transform = new TransformStream({ transform: (chunk, controller) => { // https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk chunk = String(chunk); let finalChunk = ""; for (let i = 0; i < chunk.length; i++) { const item = chunk[i]; const codeUnit = String.prototype.charCodeAt.call(item, 0); if (this.#pendingHighSurrogate !== null) { const highSurrogate = this.#pendingHighSurrogate; this.#pendingHighSurrogate = null; if (0xdc00 <= codeUnit && codeUnit <= 0xdfff) { finalChunk += highSurrogate + item; continue; } finalChunk += "\uFFFD"; } if (0xd800 <= codeUnit && codeUnit <= 0xdbff) { this.#pendingHighSurrogate = item; continue; } if (0xdc00 <= codeUnit && codeUnit <= 0xdfff) { finalChunk += "\uFFFD"; continue; } finalChunk += item; } if (finalChunk) { const value = this.#handle.encode(finalChunk); controller.enqueue(value); } }, flush: (controller) => { // https://encoding.spec.whatwg.org/#encode-and-flush if (this.#pendingHighSurrogate !== null) { controller.enqueue(new Uint8Array([0xef, 0xbf, 0xbd])); } }, }); } /** * @readonly * @type {string} */ get encoding() { return this.#handle.encoding; } /** * @readonly * @type {ReadableStream} */ get readable() { return this.#transform.readable; } /** * @readonly * @type {WritableStream} */ get writable() { return this.#transform.writable; } [kInspect](depth, options) { if (this == null) throw new ERR_INVALID_THIS("TextEncoderStream"); return customInspect(depth, options, "TextEncoderStream", { encoding: this.#handle.encoding, readable: this.#transform.readable, writable: this.#transform.writable, }); } } class TextDecoderStream { #handle; #transform; /** * @param {string} [encoding] * @param {{ * fatal? : boolean, * ignoreBOM? : boolean, * }} [options] */ constructor(encoding = "utf-8", options = kEmptyObject) { this.#handle = new TextDecoder(encoding, options); this.#transform = new TransformStream({ transform: (chunk, controller) => { const value = this.#handle.decode(chunk, { stream: true }); if (value) controller.enqueue(value); }, flush: (controller) => { const value = this.#handle.decode(); if (value) controller.enqueue(value); controller.terminate(); }, }); } /** * @readonly * @type {string} */ get encoding() { return this.#handle.encoding; } /** * @readonly * @type {boolean} */ get fatal() { return this.#handle.fatal; } /** * @readonly * @type {boolean} */ get ignoreBOM() { return this.#handle.ignoreBOM; } /** * @readonly * @type {ReadableStream} */ get readable() { return this.#transform.readable; } /** * @readonly * @type {WritableStream} */ get writable() { return this.#transform.writable; } [kInspect](depth, options) { if (this == null) throw new ERR_INVALID_THIS("TextDecoderStream"); return customInspect(depth, options, "TextDecoderStream", { encoding: this.#handle.encoding, fatal: this.#handle.fatal, ignoreBOM: this.#handle.ignoreBOM, readable: this.#transform.readable, writable: this.#transform.writable, }); } } Object.defineProperties(TextEncoderStream.prototype, { encoding: kEnumerableProperty, readable: kEnumerableProperty, writable: kEnumerableProperty, }); Object.defineProperties(TextDecoderStream.prototype, { encoding: kEnumerableProperty, fatal: kEnumerableProperty, ignoreBOM: kEnumerableProperty, readable: kEnumerableProperty, writable: kEnumerableProperty, }); export { TextEncoderStream }; export { TextDecoderStream };