Files
fetch-blob-fixed/index.js
T
Jimmy Wärting 870be1e2d5 Use a WeakMap for private properties (#42)
* options.buffer isn't a valid option

* truly private internal properties

* 100% test coverage
2020-06-06 00:09:32 -04:00

122 lines
2.6 KiB
JavaScript

// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js
// (MIT licensed)
const {Readable: ReadableStream} = require('stream');
const wm = new WeakMap();
class Blob {
constructor(blobParts = [], options = {type: ''}) {
const buffers = [];
let size = 0;
blobParts.forEach(element => {
let buffer;
if (element instanceof Buffer) {
buffer = element;
} else if (ArrayBuffer.isView(element)) {
buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength);
} else if (element instanceof ArrayBuffer) {
buffer = Buffer.from(element);
} else if (element instanceof Blob) {
buffer = wm.get(element).buffer;
} else {
buffer = Buffer.from(typeof element === 'string' ? element : String(element));
}
size += buffer.length;
buffers.push(buffer);
});
const buffer = Buffer.concat(buffers, size);
const type = options.type === undefined ? '' : String(options.type).toLowerCase();
wm.set(this, {
type: /[^\u0020-\u007E]/.test(type) ? '' : type,
size,
buffer
});
}
get size() {
return wm.get(this).size;
}
get type() {
return wm.get(this).type;
}
text() {
return Promise.resolve(wm.get(this).buffer.toString());
}
arrayBuffer() {
const buf = wm.get(this).buffer;
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
return Promise.resolve(ab);
}
stream() {
const readable = new ReadableStream();
readable._read = () => { };
readable.push(wm.get(this).buffer);
readable.push(null);
return readable;
}
toString() {
return '[object Blob]';
}
slice(...args) {
const {size} = this;
const start = args[0];
const end = args[1];
let relativeStart;
let relativeEnd;
if (start === undefined) {
relativeStart = 0; //
} else if (start < 0) {
relativeStart = Math.max(size + start, 0); //
} else {
relativeStart = Math.min(start, size);
}
if (end === undefined) {
relativeEnd = size; //
} else if (end < 0) {
relativeEnd = Math.max(size + end, 0); //
} else {
relativeEnd = Math.min(end, size);
}
const span = Math.max(relativeEnd - relativeStart, 0);
const slicedBuffer = wm.get(this).buffer.slice(
relativeStart,
relativeStart + span
);
const blob = new Blob([], {type: args[2]});
const _ = wm.get(blob);
_.buffer = slicedBuffer;
return blob;
}
}
Object.defineProperties(Blob.prototype, {
size: {enumerable: true},
type: {enumerable: true},
slice: {enumerable: true}
});
Object.defineProperty(Blob.prototype, Symbol.toStringTag, {
value: 'Blob',
writable: false,
enumerable: false,
configurable: true
});
module.exports = Blob;