/**
 * Data-access helpers for the {@link Record} class.
 *
 * `RecordData` is the object returned by `Record.data`. It wraps a lazily
 * evaluated `stream()` function with convenience accessors that mirror the
 * Fetch `Response` API (`blob()`, `bytes()`, `json()`, `text()`).
 *
 * Data is **cached after first read** so that subsequent calls to any accessor
 * return the same data without re-fetching. Concurrent calls are safe — only
 * one fetch is performed and all callers share the result.
 *
 * Extracted from `record.ts` so the convenience-method boilerplate lives in
 * its own module while the stream-resolution logic (which is tightly coupled
 * to `Record` internals) stays inside the `Record` class.
 *
 * @module
 */
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { Stream } from '@enbox/common';
/**
 * Maximum data size (in bytes) that will be cached in-memory after the first
 * read. Payloads larger than this threshold are not cached and will be
 * re-fetched on every access.
 */
const DATA_CACHE_LIMIT = 10 * 1024 * 1024; // 10 MB
/**
 * Create a {@link RecordData} wrapper around a `stream` provider function.
 *
 * The first call to any accessor (`blob`, `bytes`, `json`, `text`, `stream`)
 * consumes the underlying stream and caches the raw bytes (up to
 * {@link DATA_CACHE_LIMIT}). Subsequent calls reconstruct a fresh stream
 * from the cache, preventing the common footgun of stale data after stream
 * consumption.
 *
 * Concurrent calls are safe: if multiple accessors are called simultaneously,
 * only one stream fetch is performed and all callers share the result.
 *
 * @param streamFn   - A function that returns a `Promise<ReadableStream>` for the record data.
 * @param dataFormat - The MIME type used when constructing Blobs.
 * @returns A {@link RecordData} object with convenience accessors.
 *
 * @beta
 */
export function createRecordData(streamFn, dataFormat) {
    // In-memory byte cache. Populated after the first successful read.
    let cachedBytes;
    // In-flight read promise. Ensures concurrent callers share a single fetch.
    let inflight;
    /**
     * Returns the record data as raw bytes, using the cache when available.
     * If a read is already in-flight, waits for it instead of starting a new one.
     */
    function getBytes() {
        return __awaiter(this, void 0, void 0, function* () {
            // Fast path: cache hit.
            if (cachedBytes) {
                return cachedBytes;
            }
            // If another call is already fetching, wait for it.
            if (inflight) {
                return inflight;
            }
            // Start a new fetch and share the promise.
            inflight = (() => __awaiter(this, void 0, void 0, function* () {
                const readableStream = yield streamFn();
                const bytes = yield Stream.consumeToBytes({ readableStream });
                // Cache only if within the size limit.
                if (bytes.byteLength <= DATA_CACHE_LIMIT) {
                    cachedBytes = bytes;
                }
                return bytes;
            }))();
            try {
                return yield inflight;
            }
            finally {
                inflight = undefined;
            }
        });
    }
    const dataObj = {
        /**
         * Returns the data of the current record as a `Blob`.
         *
         * @returns A promise that resolves to a Blob containing the record's data.
         * @throws If the record data is not available or cannot be converted to a `Blob`.
         *
         * @beta
         */
        blob() {
            return __awaiter(this, void 0, void 0, function* () {
                return new Blob([yield getBytes()], { type: dataFormat });
            });
        },
        /**
         * Returns the data of the current record as a `Uint8Array`.
         *
         * @returns A Promise that resolves to a `Uint8Array` containing the record's data bytes.
         * @throws If the record data is not available or cannot be converted to a byte array.
         *
         * @beta
         */
        bytes() {
            return __awaiter(this, void 0, void 0, function* () {
                return yield getBytes();
            });
        },
        /**
         * Parses the data of the current record as JSON and returns it as a JavaScript object.
         *
         * @returns A Promise that resolves to a JavaScript object parsed from the record's JSON data.
         * @throws If the record data is not available, not in JSON format, or cannot be parsed.
         *
         * @beta
         */
        json() {
            return __awaiter(this, void 0, void 0, function* () {
                const bytes = yield getBytes();
                return JSON.parse(new TextDecoder().decode(bytes));
            });
        },
        /**
         * Returns the data of the current record as a `string`.
         *
         * @returns A promise that resolves to a `string` containing the record's text data.
         * @throws If the record data is not available or cannot be converted to text.
         *
         * @beta
         */
        text() {
            return __awaiter(this, void 0, void 0, function* () {
                const bytes = yield getBytes();
                return new TextDecoder().decode(bytes);
            });
        },
        /**
         * Provides a Web `ReadableStream` containing the record's data.
         *
         * Uses the standard Web Streams API for cross-platform compatibility across
         * browsers, Node.js, Bun, and Deno.
         *
         * If the data has already been read and cached, returns a fresh stream
         * reconstructed from the cache.
         *
         * @returns A promise that resolves to a Web `ReadableStream` of the record's data.
         * @throws If the record data is not available in-memory and cannot be fetched.
         *
         * @beta
         */
        stream() {
            return __awaiter(this, void 0, void 0, function* () {
                // If we have cached bytes, reconstruct a fresh stream from them.
                if (cachedBytes) {
                    return new ReadableStream({
                        start(controller) {
                            controller.enqueue(cachedBytes);
                            controller.close();
                        },
                    });
                }
                // Otherwise delegate to the original stream provider.
                return streamFn();
            });
        },
        /**
         * Attaches callbacks for the resolution and/or rejection of the `Promise` returned by
         * `stream()`.
         *
         * This method is a proxy to the `then` method of the `Promise` returned by `stream()`,
         * allowing for a seamless integration with promise-based workflows.
         * @param onFulfilled - A function to asynchronously execute when the `stream()` promise
         *                      becomes fulfilled.
         * @param onRejected - A function to asynchronously execute when the `stream()` promise
         *                     becomes rejected.
         * @returns A `Promise` for the completion of which ever callback is executed.
         */
        then(onFulfilled, onRejected) {
            return this.stream().then(onFulfilled, onRejected);
        },
        /**
         * Attaches a rejection handler callback to the `Promise` returned by the `stream()` method.
         * This method is a shorthand for `.then(undefined, onRejected)`, specifically designed for handling
         * rejection cases in the promise chain initiated by accessing the record's data. It ensures that
         * errors during data retrieval or processing can be caught and handled appropriately.
         *
         * @param onRejected - A function to asynchronously execute when the `stream()` promise
         *                     becomes rejected.
         * @returns A `Promise` that resolves to the value of the callback if it is called, or to its
         *          original fulfillment value if the promise is instead fulfilled.
         */
        catch(onRejected) {
            return this.stream().catch(onRejected);
        }
    };
    return dataObj;
}
//# sourceMappingURL=record-data.js.map