/**
 * Deep clone handle data to create a snapshot.
 * @internal
 */
function cloneHandleData(data) {
    const clone = {};
    for (const handleName in data) {
        clone[handleName] = {};
        for (const segmentId in data[handleName]) {
            clone[handleName][segmentId] = [...data[handleName][segmentId]];
        }
    }
    return clone;
}
/**
 * Create a new HandleStore instance.
 *
 * @example
 * ```ts
 * const handleStore = createHandleStore();
 *
 * // In router - track without awaiting
 * const component = handleStore.track(entry.handler(context));
 *
 * // In handler - push handle data (value, promise, or async callback result)
 * handleStore.push("breadcrumbs", segmentId, { label: "Home", href: "/" });
 * handleStore.push("meta", segmentId, fetchMetaAsync()); // promise
 *
 * // Stream handle data progressively
 * for await (const handles of handleStore.stream()) {
 *   console.log("Handle update:", handles);
 * }
 * ```
 */
export function createHandleStore() {
    const pending = [];
    const data = {};
    // Queue for pending emissions and resolver for waiting consumer
    let pendingEmissions = [];
    let emissionResolver = null;
    let completed = false;
    // Signal that a new emission is available
    function signalEmission() {
        if (emissionResolver) {
            const resolver = emissionResolver;
            emissionResolver = null;
            resolver();
        }
    }
    // Wait for the next emission or completion
    function waitForEmission() {
        if (pendingEmissions.length > 0 || completed) {
            return Promise.resolve();
        }
        return new Promise((resolve) => {
            emissionResolver = resolve;
        });
    }
    return {
        track(promise) {
            pending.push(promise);
            return promise;
        },
        get settled() {
            if (pending.length === 0) {
                return Promise.resolve();
            }
            return Promise.allSettled(pending).then(() => { });
        },
        push(handleName, segmentId, value) {
            if (!data[handleName]) {
                data[handleName] = {};
            }
            if (!data[handleName][segmentId]) {
                data[handleName][segmentId] = [];
            }
            data[handleName][segmentId].push(value);
            // Queue a snapshot for emission
            pendingEmissions.push(cloneHandleData(data));
            signalEmission();
        },
        getData() {
            return this.settled.then(() => data);
        },
        async *stream() {
            // Set up completion handler
            this.settled.then(() => {
                completed = true;
                signalEmission();
            });
            // Initial small delay to batch rapid synchronous pushes
            // This allows multiple handles pushing in quick succession to be batched
            await new Promise((resolve) => setTimeout(resolve, 0));
            // If we already have data, yield the accumulated state
            if (Object.keys(data).length > 0) {
                // Clear pending emissions since we're yielding current state
                pendingEmissions = [];
                yield cloneHandleData(data);
            }
            // Continue streaming on each push
            while (!completed) {
                await waitForEmission();
                // Yield all pending emissions (yield latest only)
                if (pendingEmissions.length > 0) {
                    // Skip intermediate states, yield the latest
                    const latest = pendingEmissions[pendingEmissions.length - 1];
                    pendingEmissions = [];
                    yield latest;
                }
            }
            // Final yield only if there are pending emissions that weren't yielded
            // (handles that pushed after our last yield but before completion)
            if (pendingEmissions.length > 0) {
                yield cloneHandleData(data);
            }
        },
        getDataForSegment(segmentId) {
            const result = {};
            for (const handleName in data) {
                if (data[handleName][segmentId]) {
                    result[handleName] = [...data[handleName][segmentId]];
                }
            }
            return result;
        },
        replaySegmentData(segmentId, segmentHandles) {
            for (const handleName in segmentHandles) {
                if (!data[handleName]) {
                    data[handleName] = {};
                }
                // Replace with replayed data (not append) to avoid handle bleeding between routes.
                // When a cached segment is restored, its handles should replace any existing data
                // for that segment, not accumulate on top of data from a different route.
                data[handleName][segmentId] = [...segmentHandles[handleName]];
            }
            // Trigger emission for streaming
            pendingEmissions.push(cloneHandleData(data));
            signalEmission();
        },
    };
}
//# sourceMappingURL=handle-store.js.map