mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-04 15:49:49 +00:00
811 lines
24 KiB
JavaScript
811 lines
24 KiB
JavaScript
(function () {
|
|
const createBlobObjectURL = globalThis.__gopeed_create_blob_object_url;
|
|
const revokeObjectURL = globalThis.__gopeed_revoke_object_url;
|
|
|
|
if (typeof globalThis.ReadableStream === "undefined") {
|
|
class ReadableStreamDefaultController {
|
|
constructor(stream) {
|
|
this._stream = stream;
|
|
}
|
|
|
|
enqueue(chunk) {
|
|
this._stream._enqueue(chunk);
|
|
}
|
|
|
|
close() {
|
|
this._stream._close();
|
|
}
|
|
|
|
error(err) {
|
|
this._stream._error(err);
|
|
}
|
|
}
|
|
|
|
class ReadableStreamDefaultReader {
|
|
constructor(stream) {
|
|
this._stream = stream;
|
|
}
|
|
|
|
read(view) {
|
|
if (view !== undefined) {
|
|
return this._stream._readInto(view);
|
|
}
|
|
return this._stream._read();
|
|
}
|
|
|
|
cancel(reason) {
|
|
if (!this._stream) {
|
|
return Promise.resolve();
|
|
}
|
|
return this._stream.cancel(reason);
|
|
}
|
|
|
|
releaseLock() {
|
|
if (this._stream) {
|
|
this._stream._reader = null;
|
|
this._stream.locked = false;
|
|
this._stream = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
class ReadableStream {
|
|
constructor(source = {}) {
|
|
this._source = source;
|
|
this._queue = [];
|
|
this._waiters = [];
|
|
this._closed = false;
|
|
this._errored = null;
|
|
this._reader = null;
|
|
this._pulling = false;
|
|
this._disturbed = false;
|
|
this.locked = false;
|
|
this._controller = new ReadableStreamDefaultController(this);
|
|
if (typeof source.start === "function") {
|
|
source.start(this._controller);
|
|
}
|
|
}
|
|
|
|
_enqueue(chunk) {
|
|
if (this._closed || this._errored) {
|
|
return;
|
|
}
|
|
if (this._waiters.length > 0) {
|
|
const waiter = this._waiters.shift();
|
|
waiter.resolve({ done: false, value: chunk });
|
|
return;
|
|
}
|
|
this._queue.push(chunk);
|
|
}
|
|
|
|
_close() {
|
|
this._closed = true;
|
|
while (this._waiters.length > 0) {
|
|
this._waiters.shift().resolve({ done: true, value: undefined });
|
|
}
|
|
}
|
|
|
|
_error(err) {
|
|
this._errored = err || new Error("ReadableStream error");
|
|
while (this._waiters.length > 0) {
|
|
this._waiters.shift().reject(this._errored);
|
|
}
|
|
}
|
|
|
|
_read() {
|
|
if (this._errored) {
|
|
return Promise.reject(this._errored);
|
|
}
|
|
this._disturbed = true;
|
|
this._markBodyUsed();
|
|
if (this._queue.length > 0) {
|
|
return Promise.resolve({ done: false, value: this._queue.shift() });
|
|
}
|
|
if (this._closed) {
|
|
return Promise.resolve({ done: true, value: undefined });
|
|
}
|
|
this._maybePull();
|
|
if (this._queue.length > 0) {
|
|
return Promise.resolve({ done: false, value: this._queue.shift() });
|
|
}
|
|
if (this._closed) {
|
|
return Promise.resolve({ done: true, value: undefined });
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
this._waiters.push({ resolve, reject });
|
|
this._maybePull();
|
|
});
|
|
}
|
|
|
|
async _readInto(view) {
|
|
if (!ArrayBuffer.isView(view) || view.byteLength === 0) {
|
|
throw new TypeError("BYOB reads require a non-empty ArrayBufferView");
|
|
}
|
|
const result = await this._read();
|
|
if (result.done) {
|
|
return { done: true, value: new Uint8Array(view.buffer, view.byteOffset, 0) };
|
|
}
|
|
if (!(result.value instanceof Uint8Array)) {
|
|
throw new TypeError("Byte streams require Uint8Array chunks");
|
|
}
|
|
const length = Math.min(view.byteLength, result.value.byteLength);
|
|
const target = new Uint8Array(view.buffer, view.byteOffset, length);
|
|
target.set(result.value.subarray(0, length));
|
|
if (length < result.value.byteLength) {
|
|
this._queue.unshift(result.value.subarray(length));
|
|
}
|
|
return { done: false, value: target };
|
|
}
|
|
|
|
_maybePull() {
|
|
if (this._closed || this._errored || this._pulling) {
|
|
return;
|
|
}
|
|
if (this._queue.length > 0) {
|
|
return;
|
|
}
|
|
if (!this._source || typeof this._source.pull !== "function") {
|
|
return;
|
|
}
|
|
this._pulling = true;
|
|
Promise.resolve(this._source.pull(this._controller))
|
|
.catch((err) => {
|
|
this._error(err);
|
|
})
|
|
.finally(() => {
|
|
this._pulling = false;
|
|
if (!this._closed && !this._errored && this._queue.length === 0 && this._waiters.length > 0) {
|
|
this._maybePull();
|
|
}
|
|
});
|
|
}
|
|
|
|
_markBodyUsed() {
|
|
if (typeof this.__gopeedMarkBodyUsed === "function") {
|
|
this.__gopeedMarkBodyUsed();
|
|
}
|
|
}
|
|
|
|
getReader() {
|
|
if (this.locked) {
|
|
throw new TypeError("ReadableStream is locked");
|
|
}
|
|
this.locked = true;
|
|
this._reader = new ReadableStreamDefaultReader(this);
|
|
return this._reader;
|
|
}
|
|
|
|
cancel(reason) {
|
|
this._disturbed = true;
|
|
this._markBodyUsed();
|
|
if (this._source && typeof this._source.cancel === "function") {
|
|
return Promise.resolve(this._source.cancel(reason)).then(() => {
|
|
this._close();
|
|
});
|
|
}
|
|
this._close();
|
|
return Promise.resolve();
|
|
}
|
|
|
|
async pipeTo(dest) {
|
|
const reader = this.getReader();
|
|
const writer = dest.getWriter();
|
|
try {
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) {
|
|
await writer.close();
|
|
break;
|
|
}
|
|
await writer.write(value);
|
|
}
|
|
} catch (err) {
|
|
await writer.abort(err);
|
|
throw err;
|
|
} finally {
|
|
reader.releaseLock();
|
|
writer.releaseLock();
|
|
}
|
|
}
|
|
|
|
tee() {
|
|
const reader = this.getReader();
|
|
let reading = false;
|
|
let currentRead = null;
|
|
let finished = false;
|
|
const waiting = [[], []];
|
|
const queues = [[], []];
|
|
|
|
const cloneChunk = (chunk) => {
|
|
if (chunk instanceof ArrayBuffer) return chunk.slice(0);
|
|
if (ArrayBuffer.isView(chunk)) {
|
|
const buffer = chunk.buffer.slice(0);
|
|
if (chunk instanceof DataView) return new DataView(buffer, chunk.byteOffset, chunk.byteLength);
|
|
return new chunk.constructor(buffer, chunk.byteOffset, chunk.length);
|
|
}
|
|
return chunk;
|
|
};
|
|
const flush = (index) => {
|
|
while (waiting[index].length && queues[index].length) {
|
|
waiting[index].shift().enqueue(queues[index].shift());
|
|
}
|
|
if (finished && queues[index].length === 0) {
|
|
while (waiting[index].length) waiting[index].shift().close();
|
|
}
|
|
};
|
|
const pump = () => {
|
|
if (reading) return currentRead;
|
|
if (finished) return Promise.resolve();
|
|
reading = true;
|
|
currentRead = (async () => {
|
|
try {
|
|
const result = await reader.read();
|
|
if (result.done) {
|
|
finished = true;
|
|
} else {
|
|
queues[0].push(result.value);
|
|
queues[1].push(cloneChunk(result.value));
|
|
}
|
|
} catch (error) {
|
|
finished = true;
|
|
for (const branches of waiting) {
|
|
while (branches.length) branches.shift().error(error);
|
|
}
|
|
throw error;
|
|
} finally {
|
|
reading = false;
|
|
currentRead = null;
|
|
flush(0);
|
|
flush(1);
|
|
}
|
|
})();
|
|
return currentRead;
|
|
};
|
|
const branch = (index) => new ReadableStream({
|
|
pull(controller) {
|
|
waiting[index].push(controller);
|
|
flush(index);
|
|
return pump();
|
|
},
|
|
});
|
|
return [branch(0), branch(1)];
|
|
}
|
|
}
|
|
|
|
globalThis.ReadableStream = ReadableStream;
|
|
}
|
|
|
|
if (typeof globalThis.WritableStream === "undefined") {
|
|
class WritableStreamDefaultWriter {
|
|
constructor(stream) {
|
|
if (stream.locked) {
|
|
throw new TypeError("WritableStream is locked");
|
|
}
|
|
this._stream = stream;
|
|
this._stream.locked = true;
|
|
}
|
|
|
|
write(chunk) {
|
|
return this._stream._write(chunk);
|
|
}
|
|
|
|
close() {
|
|
return this._stream._close();
|
|
}
|
|
|
|
abort(reason) {
|
|
return this._stream._abort(reason);
|
|
}
|
|
|
|
releaseLock() {
|
|
if (this._stream) {
|
|
this._stream.locked = false;
|
|
this._stream = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
class WritableStream {
|
|
constructor(sink = {}) {
|
|
this._sink = sink;
|
|
this._observers = [];
|
|
this._state = "writable";
|
|
this.locked = false;
|
|
}
|
|
|
|
_targets() {
|
|
return [this._sink, ...this._observers];
|
|
}
|
|
|
|
_write(chunk) {
|
|
if (this._state !== "writable") {
|
|
return Promise.reject(new TypeError("WritableStream is not writable"));
|
|
}
|
|
let chain = Promise.resolve();
|
|
for (const target of this._targets()) {
|
|
if (target && typeof target.write === "function") {
|
|
chain = chain.then(() => target.write(chunk));
|
|
}
|
|
}
|
|
return chain;
|
|
}
|
|
|
|
_close() {
|
|
if (this._state !== "writable") {
|
|
return Promise.reject(new TypeError("WritableStream is not writable"));
|
|
}
|
|
this._state = "closed";
|
|
let chain = Promise.resolve();
|
|
for (const target of this._targets()) {
|
|
if (target && typeof target.close === "function") {
|
|
chain = chain.then(() => target.close());
|
|
}
|
|
}
|
|
return chain;
|
|
}
|
|
|
|
_abort(reason) {
|
|
if (this._state === "errored") {
|
|
return Promise.resolve();
|
|
}
|
|
this._state = "errored";
|
|
let chain = Promise.resolve();
|
|
for (const target of this._targets()) {
|
|
if (target && typeof target.abort === "function") {
|
|
chain = chain.then(() => target.abort(reason));
|
|
}
|
|
}
|
|
return chain;
|
|
}
|
|
|
|
_addObserver(observer) {
|
|
this._observers.push(observer);
|
|
}
|
|
|
|
getWriter() {
|
|
return new WritableStreamDefaultWriter(this);
|
|
}
|
|
}
|
|
|
|
globalThis.WritableStream = WritableStream;
|
|
}
|
|
|
|
if (typeof globalThis.TransformStream === "undefined") {
|
|
class TransformStream {
|
|
constructor(transformer = {}) {
|
|
let readableController;
|
|
this.readable = new ReadableStream({
|
|
start(controller) {
|
|
readableController = controller;
|
|
}
|
|
});
|
|
this.writable = new WritableStream({
|
|
async write(chunk) {
|
|
if (typeof transformer.transform === "function") {
|
|
await transformer.transform(chunk, readableController);
|
|
return;
|
|
}
|
|
readableController.enqueue(chunk);
|
|
},
|
|
async close() {
|
|
if (typeof transformer.flush === "function") {
|
|
await transformer.flush(readableController);
|
|
}
|
|
readableController.close();
|
|
},
|
|
async abort(reason) {
|
|
if (typeof transformer.abort === "function") {
|
|
await transformer.abort(reason);
|
|
}
|
|
readableController.error(reason || new Error("TransformStream aborted"));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
globalThis.TransformStream = TransformStream;
|
|
}
|
|
|
|
if (typeof globalThis.ByteLengthQueuingStrategy === "undefined") {
|
|
globalThis.ByteLengthQueuingStrategy = class ByteLengthQueuingStrategy {
|
|
constructor({ highWaterMark }) {
|
|
this.highWaterMark = highWaterMark;
|
|
}
|
|
|
|
size(chunk) {
|
|
if (typeof chunk === "string") {
|
|
return chunk.length;
|
|
}
|
|
if (chunk && typeof chunk.byteLength === "number") {
|
|
return chunk.byteLength;
|
|
}
|
|
return 1;
|
|
}
|
|
};
|
|
}
|
|
|
|
if (typeof globalThis.CountQueuingStrategy === "undefined") {
|
|
globalThis.CountQueuingStrategy = class CountQueuingStrategy {
|
|
constructor({ highWaterMark }) {
|
|
this.highWaterMark = highWaterMark;
|
|
}
|
|
|
|
size() {
|
|
return 1;
|
|
}
|
|
};
|
|
}
|
|
|
|
const blobReaders = new Map();
|
|
|
|
function getValueTypeName(value) {
|
|
if (value === null) {
|
|
return "null";
|
|
}
|
|
if (value === undefined) {
|
|
return "undefined";
|
|
}
|
|
if (value && value.constructor && typeof value.constructor.name === "string" && value.constructor.name) {
|
|
return value.constructor.name;
|
|
}
|
|
return typeof value;
|
|
}
|
|
|
|
function toReadableStreamReader(value, sourceLabel) {
|
|
if (value && typeof value.getReader === "function") {
|
|
return value.getReader();
|
|
}
|
|
throw new TypeError(sourceLabel + " must return a ReadableStream, got " + getValueTypeName(value));
|
|
}
|
|
|
|
async function openBlobReadable(blob, request) {
|
|
const { offset, end } = request;
|
|
const sliced = blob.slice(offset, end >= offset ? end + 1 : undefined);
|
|
if (sliced && typeof sliced.stream === "function") {
|
|
try {
|
|
const stream = sliced.stream();
|
|
if (stream && typeof stream.getReader === "function") {
|
|
return stream;
|
|
}
|
|
} catch (_) {
|
|
}
|
|
}
|
|
if (sliced && typeof sliced.arrayBuffer === "function") {
|
|
const buffer = await sliced.arrayBuffer();
|
|
return new ReadableStream({
|
|
start(controller) {
|
|
controller.enqueue(new Uint8Array(buffer));
|
|
controller.close();
|
|
},
|
|
});
|
|
}
|
|
throw new TypeError("Blob source cannot be converted to a ReadableStream");
|
|
}
|
|
|
|
function describeObjectURLValue(value) {
|
|
if (value instanceof Blob) {
|
|
return {
|
|
kind: "blob",
|
|
value,
|
|
};
|
|
}
|
|
if (typeof value === "function") {
|
|
return {
|
|
kind: "opener",
|
|
openReadable: value,
|
|
sourceLabel: "gopeed.runtime.blob.createObjectURL opener function",
|
|
};
|
|
}
|
|
return {
|
|
kind: "other",
|
|
value,
|
|
};
|
|
}
|
|
|
|
function releaseReader(reader, reason) {
|
|
if (!reader) {
|
|
return;
|
|
}
|
|
try {
|
|
if (reason !== undefined && typeof reader.cancel === "function") {
|
|
const result = reader.cancel(reason);
|
|
if (result && typeof result.catch === "function") {
|
|
result.catch(function () {});
|
|
}
|
|
}
|
|
} catch (_) {
|
|
}
|
|
if (typeof reader.releaseLock === "function") {
|
|
try {
|
|
reader.releaseLock();
|
|
} catch (_) {
|
|
}
|
|
}
|
|
}
|
|
|
|
function normalizeBlobObjectURLOptions(value, options, defaultRange) {
|
|
const normalized = {
|
|
contentType: "",
|
|
size: 0,
|
|
range: !!defaultRange,
|
|
};
|
|
if (options && typeof options === "object") {
|
|
if (typeof options.contentType === "string") {
|
|
normalized.contentType = options.contentType;
|
|
}
|
|
if (Number.isFinite(Number(options.size)) && Number(options.size) > 0) {
|
|
normalized.size = Number(options.size);
|
|
}
|
|
if (typeof options.range === "boolean") {
|
|
normalized.range = options.range;
|
|
}
|
|
}
|
|
if (!normalized.contentType && value && typeof value.type === "string") {
|
|
normalized.contentType = value.type;
|
|
}
|
|
if (!normalized.size && value) {
|
|
if (Number.isFinite(Number(value.size)) && Number(value.size) > 0) {
|
|
normalized.size = Number(value.size);
|
|
} else if (value._buffer && Number.isFinite(Number(value._buffer.byteLength))) {
|
|
normalized.size = Number(value._buffer.byteLength);
|
|
}
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
function validateBlobObjectURLOptions(options) {
|
|
if (options.range && !(Number.isFinite(Number(options.size)) && Number(options.size) > 0)) {
|
|
throw new TypeError("gopeed.runtime.blob.createObjectURL options.range requires a positive options.size");
|
|
}
|
|
}
|
|
|
|
function normalizeOpenRequest(request) {
|
|
const offset = Math.max(0, Number(request && request.offset) || 0);
|
|
const endValue = Number(request && request.end);
|
|
const end = Number.isFinite(endValue) ? endValue : -1;
|
|
return { offset, end };
|
|
}
|
|
|
|
function yieldBlobPipeTask() {
|
|
return new Promise((resolve) => {
|
|
if (typeof setTimeout === "function") {
|
|
setTimeout(resolve, 0);
|
|
} else {
|
|
Promise.resolve().then(resolve);
|
|
}
|
|
});
|
|
}
|
|
|
|
function createPendingBlobReader(openReadable, request) {
|
|
let activeReader;
|
|
let ready;
|
|
function ensureReader() {
|
|
if (ready) {
|
|
return ready;
|
|
}
|
|
ready = (async function () {
|
|
const source = await openReadable(normalizeOpenRequest(request));
|
|
activeReader = toReadableStreamReader(source, "gopeed.runtime.blob.createObjectURL opener function");
|
|
return activeReader;
|
|
})();
|
|
return ready;
|
|
}
|
|
return {
|
|
async read() {
|
|
const reader = await ensureReader();
|
|
return reader.read();
|
|
},
|
|
cancel(reason) {
|
|
return ensureReader().then(function (reader) {
|
|
return reader.cancel(reason);
|
|
}, function () {});
|
|
},
|
|
releaseLock() {
|
|
if (!ready) {
|
|
return;
|
|
}
|
|
return ready.then(function (reader) {
|
|
return reader.releaseLock();
|
|
}, function () {});
|
|
}
|
|
};
|
|
}
|
|
|
|
globalThis.__gopeed_blob_open_source = function (openReadable, request) {
|
|
if (typeof openReadable !== "function") {
|
|
throw new TypeError("blob opener must be callable");
|
|
}
|
|
const id = crypto.randomUUID ? crypto.randomUUID() : String(Date.now()) + "-" + String(Math.random());
|
|
blobReaders.set(id, createPendingBlobReader(openReadable, request));
|
|
return id;
|
|
};
|
|
|
|
globalThis.__gopeed_blob_read_source = async function (id, chunkSize) {
|
|
const reader = blobReaders.get(id);
|
|
if (!reader) {
|
|
return null;
|
|
}
|
|
const { done, value } = await reader.read();
|
|
if (done) {
|
|
blobReaders.delete(id);
|
|
releaseReader(reader);
|
|
return null;
|
|
}
|
|
const chunk = value instanceof Uint8Array ? value : new Uint8Array(value);
|
|
const size = Math.max(1, Number(chunkSize) || chunk.byteLength);
|
|
if (chunk.byteLength <= size) {
|
|
return chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
|
|
}
|
|
const head = chunk.slice(0, size);
|
|
let offset = size;
|
|
blobReaders.set(id, {
|
|
read() {
|
|
if (offset >= chunk.byteLength) {
|
|
return reader.read();
|
|
}
|
|
const next = chunk.slice(offset, Math.min(offset + size, chunk.byteLength));
|
|
offset += next.byteLength;
|
|
return Promise.resolve({ done: false, value: next });
|
|
},
|
|
cancel(reason) {
|
|
return reader.cancel(reason);
|
|
},
|
|
releaseLock() {
|
|
return reader.releaseLock();
|
|
}
|
|
});
|
|
return head.buffer.slice(head.byteOffset, head.byteOffset + head.byteLength);
|
|
};
|
|
|
|
globalThis.__gopeed_blob_close_source = function (id) {
|
|
const reader = blobReaders.get(id);
|
|
if (!reader) {
|
|
return;
|
|
}
|
|
blobReaders.delete(id);
|
|
releaseReader(reader, "blob request closed");
|
|
};
|
|
|
|
const blobPipeReaderStates = new Map();
|
|
|
|
function releaseBlobPipeReader(state) {
|
|
if (!state || state.released) {
|
|
return;
|
|
}
|
|
state.released = true;
|
|
if (state.reader && typeof state.reader.releaseLock === "function") {
|
|
try {
|
|
state.reader.releaseLock();
|
|
} catch (_) {
|
|
}
|
|
}
|
|
if (blobPipeReaderStates.get(state.pipeId) === state) {
|
|
blobPipeReaderStates.delete(state.pipeId);
|
|
}
|
|
}
|
|
|
|
function finishBlobPipeReader(state, cancel, reason) {
|
|
if (!state) {
|
|
return Promise.resolve();
|
|
}
|
|
if (cancel) {
|
|
state.cancelRequested = true;
|
|
if (reason !== undefined) {
|
|
state.cancelReason = reason;
|
|
}
|
|
}
|
|
if (state.cleanupPromise) {
|
|
return state.cleanupPromise;
|
|
}
|
|
if (!state.reader) {
|
|
// A cancellation may arrive while the asynchronous opener is pending.
|
|
// Keep the state until the reader exists so it can still be canceled.
|
|
if (!state.cancelRequested || state.openSettled) {
|
|
releaseBlobPipeReader(state);
|
|
}
|
|
return Promise.resolve();
|
|
}
|
|
|
|
state.cleanupPromise = (async function () {
|
|
try {
|
|
if (state.cancelRequested && typeof state.reader.cancel === "function") {
|
|
await state.reader.cancel(state.cancelReason);
|
|
}
|
|
} catch (_) {
|
|
} finally {
|
|
releaseBlobPipeReader(state);
|
|
}
|
|
})();
|
|
return state.cleanupPromise;
|
|
}
|
|
|
|
globalThis.__gopeed_blob_cancel_pipe_source = function (pipeId, reason) {
|
|
const state = blobPipeReaderStates.get(pipeId);
|
|
if (!state) {
|
|
return;
|
|
}
|
|
// finishBlobPipeReader invokes reader.cancel() synchronously up to its
|
|
// first await. Do not return the cleanup Promise: Go only needs to ensure
|
|
// cancellation has started before releasing the source session.
|
|
finishBlobPipeReader(state, true, reason).catch(function () {});
|
|
};
|
|
|
|
globalThis.__gopeed_blob_pipe_source = function (openReadable, request, pipeId) {
|
|
if (typeof openReadable !== "function") {
|
|
throw new TypeError("blob opener must be callable");
|
|
}
|
|
const state = {
|
|
pipeId,
|
|
reader: null,
|
|
cancelRequested: false,
|
|
cancelReason: "blob pipe closed",
|
|
cleanupPromise: null,
|
|
openSettled: false,
|
|
released: false,
|
|
};
|
|
blobPipeReaderStates.set(pipeId, state);
|
|
const startPipe = async function () {
|
|
try {
|
|
const source = await openReadable(normalizeOpenRequest(request));
|
|
state.openSettled = true;
|
|
state.reader = toReadableStreamReader(source, "gopeed.runtime.blob.createObjectURL opener function");
|
|
if (state.cancelRequested) {
|
|
await finishBlobPipeReader(state, true, state.cancelReason);
|
|
return;
|
|
}
|
|
while (true) {
|
|
const { done, value } = await state.reader.read();
|
|
if (done) {
|
|
globalThis.__gopeed_blob_pipe_close(pipeId);
|
|
return;
|
|
}
|
|
const chunk = value instanceof Uint8Array ? value : new Uint8Array(value);
|
|
const buffer = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
|
|
if (!globalThis.__gopeed_blob_pipe_chunk(pipeId, buffer)) {
|
|
state.cancelRequested = true;
|
|
state.cancelReason = "blob pipe closed";
|
|
return;
|
|
}
|
|
await yieldBlobPipeTask();
|
|
}
|
|
} catch (error) {
|
|
if (!state.cancelRequested) {
|
|
globalThis.__gopeed_blob_pipe_error(pipeId, error && error.stack ? error.stack : String(error));
|
|
}
|
|
} finally {
|
|
state.openSettled = true;
|
|
await finishBlobPipeReader(state, state.cancelRequested, state.cancelReason);
|
|
}
|
|
};
|
|
if (typeof setTimeout === "function") {
|
|
setTimeout(startPipe, 0);
|
|
} else {
|
|
Promise.resolve().then(startPipe);
|
|
}
|
|
};
|
|
|
|
globalThis.__gopeed_blob_create_object_url = function (value, options) {
|
|
const described = describeObjectURLValue(value);
|
|
if (described.kind === "blob") {
|
|
const blob = described.value;
|
|
const normalized = normalizeBlobObjectURLOptions(blob, options, Number(blob.size) > 0);
|
|
validateBlobObjectURLOptions(normalized);
|
|
const opener = async (request) => openBlobReadable(blob, request);
|
|
return createBlobObjectURL(opener, normalized);
|
|
}
|
|
if (described.kind === "opener") {
|
|
const normalized = normalizeBlobObjectURLOptions(null, options, false);
|
|
validateBlobObjectURLOptions(normalized);
|
|
return createBlobObjectURL(described.openReadable, normalized);
|
|
}
|
|
throw new TypeError("Unsupported object type for gopeed.runtime.blob.createObjectURL: " + getValueTypeName(described.value) + ". Expected Blob or opener function");
|
|
};
|
|
|
|
globalThis.__gopeed_blob_revoke_object_url = function (url) {
|
|
if (typeof url === "string") {
|
|
revokeObjectURL(url);
|
|
}
|
|
};
|
|
|
|
})();
|