univ-conv
Version:
The universal binary and text converter
195 lines • 7.05 kB
JavaScript
import { AbstractConverter, _ } from "./AbstractConverter";
import { EMPTY_UINT8_ARRAY, getStartEnd, } from "./core";
import { closeStream, fileURLToReadable, handleReadable, handleReadableStream, hasStreamOnBlob, isBrowser, isReadableStream, } from "./Environment";
function createReadableStream(u8) {
return new ReadableStream({
start: (controller) => {
controller.enqueue(u8);
},
});
}
export function createPartialReadableStream(source, startEnd) {
const reader = source.getReader();
const start = startEnd.start;
const end = startEnd.end ?? Number.MAX_SAFE_INTEGER;
return new ReadableStream({
start: async (controller) => {
let iStart = 0;
let res;
do {
res = await reader.read();
const value = res.value;
if (value) {
const u8 = value;
const length = u8.byteLength;
const iEnd = iStart + length;
const u8End = (iEnd < end ? iEnd : end) - iStart;
let chunk;
if (iStart <= start && start < iEnd) {
chunk = u8.subarray(start - iStart, u8End);
}
else if (start < iStart && iStart < end) {
chunk = u8.subarray(0, u8End);
}
if (chunk) {
controller.enqueue(chunk);
}
iStart += length;
}
} while (!res.done && iStart < end);
controller.close();
reader.releaseLock();
closeStream(source);
},
cancel: (e) => {
reader.releaseLock();
closeStream(source, e);
},
});
}
function createReadableStreamOfReader(readable, options) {
const startEnd = getStartEnd(options);
const start = startEnd.start;
const end = startEnd.end ?? Number.MAX_SAFE_INTEGER;
const bufferSize = options.bufferSize;
const converter = _()._of("uint8array");
let iStart = 0;
return new ReadableStream({
start: async (controller) => {
await handleReadable(readable, async (value) => {
const u8 = await converter.from(value, { bufferSize });
const size = u8.byteLength;
const iEnd = iStart + size;
const u8End = (iEnd < end ? iEnd : end) - iStart;
let chunk;
if (iStart <= start && start < iEnd) {
chunk = u8.subarray(start - iStart, u8End);
}
else if (start < iStart && iStart < end) {
chunk = u8.subarray(0, u8End);
}
if (chunk) {
controller.enqueue(chunk);
}
iStart += size;
return iStart < end;
});
},
cancel: (err) => {
readable.destroy(err);
},
});
}
export class ReadableStreamConverter extends AbstractConverter {
constructor() {
super(...arguments);
this.type = "readablestream";
}
empty() {
return new ReadableStream({
start: (converter) => {
converter.enqueue(EMPTY_UINT8_ARRAY);
converter.close();
},
});
}
is(input) {
return isReadableStream(input);
}
async _from(input, options) {
if (typeof input === "string" && options.inputStringType === "url") {
if (input.startsWith("http:") || input.startsWith("https:")) {
const resp = await fetch(input);
if (_().is("readable", resp.body, options)) {
input = resp.body;
}
else {
input = resp.body;
}
}
else if (input.startsWith("file:") && fileURLToReadable) {
input = await fileURLToReadable(input);
}
}
if (_().is("readable", input, options)) {
return createReadableStreamOfReader(input, options);
}
if (hasStreamOnBlob) {
if (_().is("blob", input, options)) {
input = input.stream();
}
else {
const blob = await _().convert("blob", input, options);
input = blob.stream();
}
}
if (this.is(input)) {
return createPartialReadableStream(input, await this._getStartEnd(input, options));
}
const u8 = await _().convert("uint8array", input, options);
const { start, end } = getStartEnd(options, u8.byteLength);
return createReadableStream(u8.slice(start, end));
}
_getStartEnd(_input, options) {
return Promise.resolve(getStartEnd(options));
}
_isEmpty() {
return false;
}
_merge(streams) {
const end = streams.length;
const process = (controller, i) => {
if (i < end) {
const stream = streams[i];
handleReadableStream(stream, (chunk) => {
controller.enqueue(chunk);
return Promise.resolve(true);
})
.then(() => process(controller, ++i))
.catch((e) => {
controller.error(e);
for (let j = i; j < end; j++) {
const s = streams[j];
closeStream(s);
}
});
}
else {
controller.close();
}
};
return Promise.resolve(new ReadableStream({
start: (converter) => {
process(converter, 0);
},
}));
}
_size() {
throw new Error("Cannot get size of ReadableStream");
}
async _toArrayBuffer(input, options) {
const u8 = await this._toUint8Array(input, options);
return u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength);
}
async _toBase64(input, options) {
const opts = { bufferSize: options.bufferSize };
const t = isBrowser ? "blob" : "uint8array";
const convertd = await _().convert(t, input, opts);
return await _()._of(t).toBase64(convertd, options);
}
async _toText(input, options) {
const opts = { bufferSize: options.bufferSize };
const t = isBrowser ? "blob" : "uint8array";
const convertd = await _().convert(t, input, opts);
return await _()._of(t).toText(convertd, options);
}
async _toUint8Array(input, options) {
const chunks = [];
await handleReadableStream(input, async (chunk) => {
chunks.push(chunk);
return Promise.resolve(true);
});
return await _()._of("uint8array").merge(chunks, options);
}
}
//# sourceMappingURL=ReadableStreamConverter.js.map