import { workUnitAsyncStorage } from './work-unit-async-storage.external';
function createSegmentVaryParamsAccumulator() {
    const accumulator = {
        varyParams: new Set(),
        status: 'pending',
        value: new Set(),
        then (onfulfilled) {
            if (onfulfilled) {
                if (accumulator.status === 'pending') {
                    accumulator.resolvers.push(onfulfilled);
                } else {
                    onfulfilled(accumulator.value);
                }
            }
        },
        resolvers: []
    };
    return accumulator;
}
/**
 * A singleton accumulator that's already resolved to an empty Set. Use this for
 * segments where we know upfront that no params will be accessed, such as
 * client components or segments without user code.
 *
 * Benefits:
 * - No need to accumulate or resolve later
 * - Resilient: resolves correctly even if other tracking fails
 * - Memory efficient: reuses the same object
 */ const emptySet = new Set();
export const emptyVaryParamsAccumulator = {
    varyParams: emptySet,
    status: 'fulfilled',
    value: emptySet,
    then (onfulfilled) {
        if (onfulfilled) {
            onfulfilled(emptySet);
        }
    },
    resolvers: []
};
export function createResponseVaryParamsAccumulator() {
    // Create the head and rootParams accumulators as top-level fields.
    // Segment accumulators are added to the segments set as they are created.
    const head = createSegmentVaryParamsAccumulator();
    const rootParams = createSegmentVaryParamsAccumulator();
    const segments = new Set();
    return {
        head,
        rootParams,
        segments
    };
}
/**
 * Allocates a new VaryParamsAccumulator and adds it to the response accumulator
 * associated with the current WorkUnitStore.
 *
 * Returns a thenable that resolves to the segment's vary params once rendering
 * is complete. The thenable can be passed directly to React Flight for
 * serialization.
 */ export function createVaryParamsAccumulator() {
    const workUnitStore = workUnitAsyncStorage.getStore();
    if (workUnitStore) {
        switch(workUnitStore.type){
            case 'prerender':
            case 'prerender-runtime':
                {
                    const responseAccumulator = workUnitStore.varyParamsAccumulator;
                    if (responseAccumulator !== null) {
                        const accumulator = createSegmentVaryParamsAccumulator();
                        responseAccumulator.segments.add(accumulator);
                        return accumulator;
                    }
                    return null;
                }
            case 'prerender-ppr':
            case 'prerender-legacy':
            case 'request':
            case 'cache':
            case 'private-cache':
            case 'prerender-client':
            case 'validation-client':
            case 'unstable-cache':
            case 'generate-static-params':
                break;
            default:
                workUnitStore;
        }
    }
    return null;
}
export function getMetadataVaryParamsAccumulator() {
    const workUnitStore = workUnitAsyncStorage.getStore();
    if (workUnitStore) {
        switch(workUnitStore.type){
            case 'prerender':
            case 'prerender-runtime':
                {
                    const responseAccumulator = workUnitStore.varyParamsAccumulator;
                    if (responseAccumulator !== null) {
                        return responseAccumulator.head;
                    }
                    return null;
                }
            case 'prerender-ppr':
            case 'prerender-legacy':
            case 'request':
            case 'cache':
            case 'private-cache':
            case 'prerender-client':
            case 'validation-client':
            case 'unstable-cache':
            case 'generate-static-params':
                return null;
            default:
                workUnitStore;
        }
    }
    return null;
}
export function getVaryParamsThenable(accumulator) {
    return accumulator;
}
export function getMetadataVaryParamsThenable() {
    const accumulator = getMetadataVaryParamsAccumulator();
    if (accumulator !== null) {
        return getVaryParamsThenable(accumulator);
    }
    return null;
}
// The metadata and viewport are always delivered in a single payload, so they
// don't need to be tracked separately. This may change in the future, but for
// now this is just an alias.
export const getViewportVaryParamsAccumulator = getMetadataVaryParamsAccumulator;
export function getRootParamsVaryParamsAccumulator() {
    const workUnitStore = workUnitAsyncStorage.getStore();
    if (workUnitStore) {
        switch(workUnitStore.type){
            case 'prerender':
            case 'prerender-runtime':
                {
                    const responseAccumulator = workUnitStore.varyParamsAccumulator;
                    if (responseAccumulator !== null) {
                        return responseAccumulator.rootParams;
                    }
                    return null;
                }
            case 'prerender-ppr':
            case 'prerender-legacy':
            case 'request':
            case 'cache':
            case 'private-cache':
            case 'prerender-client':
            case 'validation-client':
            case 'unstable-cache':
            case 'generate-static-params':
                return null;
            default:
                workUnitStore;
        }
    }
    return null;
}
/**
 * Records that a param was accessed. Adds the param name to the accumulator's
 * varyParams set.
 */ export function accumulateVaryParam(accumulator, paramName) {
    accumulator.varyParams.add(paramName);
}
/**
 * Records a root param access.
 */ export function accumulateRootVaryParam(paramName) {
    const rootParamsAccumulator = getRootParamsVaryParamsAccumulator();
    if (rootParamsAccumulator !== null) {
        accumulateVaryParam(rootParamsAccumulator, paramName);
    }
}
export function createVaryingParams(accumulator, originalParamsObject, optionalCatchAllParamName) {
    if (optionalCatchAllParamName !== null) {
        // When there's an optional catch-all param with no value (e.g.,
        // [[...slug]] at /), the param doesn't exist as a property on the params
        // object. Use a Proxy to track all param access — both existing params
        // and the missing optional param — including enumeration patterns like
        // Object.keys(), spread, for...in, and `in` checks.
        return new Proxy(originalParamsObject, {
            get (target, prop, receiver) {
                if (typeof prop === 'string') {
                    if (prop === optionalCatchAllParamName || Object.prototype.hasOwnProperty.call(target, prop)) {
                        accumulateVaryParam(accumulator, prop);
                    }
                }
                return Reflect.get(target, prop, receiver);
            },
            has (target, prop) {
                if (prop === optionalCatchAllParamName) {
                    accumulateVaryParam(accumulator, optionalCatchAllParamName);
                }
                return Reflect.has(target, prop);
            },
            ownKeys (target) {
                // Enumerating the params object means the user's code may depend on
                // which params are present, so conservatively track the optional
                // param as accessed.
                accumulateVaryParam(accumulator, optionalCatchAllParamName);
                return Reflect.ownKeys(target);
            }
        });
    }
    // When there's no optional catch-all, all params exist as properties on the
    // object, so we can use defineProperty getters instead of a Proxy. This is
    // faster because the engine can optimize property access on regular objects
    // more aggressively than Proxy trap calls.
    const underlyingParamsWithVarying = {};
    for(const paramName in originalParamsObject){
        Object.defineProperty(underlyingParamsWithVarying, paramName, {
            get () {
                accumulateVaryParam(accumulator, paramName);
                return originalParamsObject[paramName];
            },
            enumerable: true
        });
    }
    return underlyingParamsWithVarying;
}
export function createVaryingSearchParams(accumulator, originalSearchParamsObject) {
    const underlyingSearchParamsWithVarying = {};
    for(const searchParamName in originalSearchParamsObject){
        Object.defineProperty(underlyingSearchParamsWithVarying, searchParamName, {
            get () {
                // TODO: Unlike path params, we don't vary track each search param
                // individually. The entire search string is treated as a single param.
                // This may change in the future.
                accumulateVaryParam(accumulator, '?');
                return originalSearchParamsObject[searchParamName];
            },
            enumerable: true
        });
    }
    return underlyingSearchParamsWithVarying;
}
/**
 * Resolves all segment accumulators in a ResponseVaryParamsAccumulator with
 * their final vary params. Call this after rendering is complete.
 *
 * Each segment's thenable is resolved with its vary params merged with the
 * root params. If we can't track vary params (e.g., legacy prerender), simply
 * don't call this function - the client treats unresolved thenables as
 * "unknown" vary params.
 */ export async function finishAccumulatingVaryParams(responseAccumulator) {
    const rootVaryParams = responseAccumulator.rootParams.varyParams;
    // Resolve head
    finishSegmentAccumulator(responseAccumulator.head, rootVaryParams);
    // Resolve each segment
    for (const segmentAccumulator of responseAccumulator.segments){
        finishSegmentAccumulator(segmentAccumulator, rootVaryParams);
    }
    // Now that the thenables are resolved, Flight should be able to flush the
    // vary params into the response stream. This work gets scheduled internally
    // by Flight using a microtask as soon as we notify the thenable listeners.
    //
    // We need to ensure that Flight's pending queues are emptied before this
    // function returns; the caller will abort the prerender immediately after.
    // We can't use a macrotask, because that would allow dynamic IO to sneak
    // into the response. So we use microtasks instead.
    //
    // The exact number of awaits here isn't important (indeed, one seems to be
    // sufficient, at the time of writing), as long as we wait enough ticks for
    // Flight to finish writing the response.
    //
    // Anything that remains in Flight's internal queue after these awaits must
    // be actual dynamic IO, not caused by pending vary params tasks. In other
    // words, failing to do this would cause us to treat a fully static prerender
    // as if it were partially dynamic.
    await Promise.resolve();
    await Promise.resolve();
    await Promise.resolve();
}
function finishSegmentAccumulator(accumulator, rootVaryParams) {
    if (accumulator.status !== 'pending') {
        return;
    }
    const merged = new Set(accumulator.varyParams);
    for (const param of rootVaryParams){
        merged.add(param);
    }
    accumulator.value = merged;
    accumulator.status = 'fulfilled';
    for (const resolver of accumulator.resolvers){
        resolver(merged);
    }
    accumulator.resolvers = [];
}

//# sourceMappingURL=vary-params.js.map