Source: packages/compressed-textures/src/resources/BlobResource.ts

import { BufferResource, ViewableBuffer } from '@pixi/core';

import type { BufferType, IBufferResourceOptions } from '@pixi/core';

/**
 * Constructor options for BlobResource.
 * @memberof PIXI
 */
export interface IBlobResourceOptions extends IBufferResourceOptions
{
    autoLoad?: boolean;
}

/**
 * Resource that fetches texture data over the network and stores it in a buffer.
 * @class
 * @extends PIXI.Resource
 * @memberof PIXI
 */
export abstract class BlobResource extends BufferResource
{
    /** The URL of the texture file. */
    protected origin: string | null;

    /** The viewable buffer on the data. */
    protected buffer: ViewableBuffer | null;

    protected loaded: boolean;

    /**
     * Promise when loading.
     * @default null
     */
    private _load: Promise<this>;

    /**
     * @param source - The buffer/URL of the texture file.
     * @param {PIXI.IBlobResourceOptions} [options]
     * @param {boolean} [options.autoLoad=false] - Whether to fetch the data immediately;
     *  you can fetch it later via PIXI.BlobResource#load.
     * @param {number} [options.width=1] - The width in pixels.
     * @param {number} [options.height=1] - The height in pixels.
     * @param {1|2|4|8} [options.unpackAlignment=4] - The alignment of the pixel rows.
     */
    constructor(source: string | BufferType, options: IBlobResourceOptions = { width: 1, height: 1, autoLoad: true })
    {
        let origin: string | null;
        let data: BufferType;

        if (typeof source === 'string')
        {
            origin = source;
            data = new Uint8Array();
        }
        else
        {
            origin = null;
            data = source;
        }

        super(data, options);

        this.origin = origin;
        this.buffer = data ? new ViewableBuffer(data) : null;

        this._load = null;
        this.loaded = false;

        // Allow autoLoad = "undefined" still load the resource by default
        if (this.origin !== null && options.autoLoad !== false)
        {
            this.load();
        }
        if (this.origin === null && this.buffer)
        {
            this._load = Promise.resolve(this);
            this.loaded = true;
            this.onBlobLoaded(this.buffer.rawBinaryData);
        }
    }

    protected onBlobLoaded(_data: ArrayBuffer): void
    {
        // TODO: Override this method
    }

    /** Loads the blob */
    load(): Promise<this>
    {
        if (this._load)
        {
            return this._load;
        }

        this._load = fetch(this.origin)
            .then((response) => response.blob())
            .then((blob) => blob.arrayBuffer())
            .then((arrayBuffer) =>
            {
                this.data = new Uint32Array(arrayBuffer);
                this.buffer = new ViewableBuffer(arrayBuffer);
                this.loaded = true;

                this.onBlobLoaded(arrayBuffer);
                this.update();

                return this;
            });

        return this._load;
    }
}