import { createContext as createContext$1, useRef, useState, createElement, useEffect, useLayoutEffect, useContext, useReducer } from 'react';
import { s as schedulerExports } from '../../../../../../_virtual/index4.js';

const CONTEXT_VALUE = Symbol();
const ORIGINAL_PROVIDER = Symbol();
const isSSR = typeof window === 'undefined' ||
    /ServerSideRendering/.test(window.navigator && window.navigator.userAgent);
const useIsomorphicLayoutEffect = isSSR ? useEffect : useLayoutEffect;
// for preact that doesn't have runWithPriority
const runWithNormalPriority = schedulerExports.unstable_runWithPriority
    ? (fn) => {
        try {
            schedulerExports.unstable_runWithPriority(schedulerExports.unstable_NormalPriority, fn);
        }
        catch (e) {
            if (e.message === 'Not implemented.') {
                fn();
            }
            else {
                throw e;
            }
        }
    }
    : (fn) => fn();
const createProvider = (ProviderOrig) => {
    const ContextProvider = ({ value, children, }) => {
        const valueRef = useRef(value);
        const versionRef = useRef(0);
        const [resolve, setResolve] = useState(null);
        if (resolve) {
            resolve(value);
            setResolve(null);
        }
        const contextValue = useRef();
        if (!contextValue.current) {
            const listeners = new Set();
            const update = (fn, options) => {
                versionRef.current += 1;
                const action = {
                    n: versionRef.current,
                };
                if (options === null || options === void 0 ? void 0 : options.suspense) {
                    action.n *= -1; // this is intentional to make it temporary version
                    action.p = new Promise((r) => {
                        setResolve(() => (v) => {
                            action.v = v;
                            delete action.p;
                            r(v);
                        });
                    });
                }
                listeners.forEach((listener) => listener(action));
                fn();
            };
            contextValue.current = {
                [CONTEXT_VALUE]: {
                    /* "v"alue     */ v: valueRef,
                    /* versio"n"   */ n: versionRef,
                    /* "l"isteners */ l: listeners,
                    /* "u"pdate    */ u: update,
                },
            };
        }
        useIsomorphicLayoutEffect(() => {
            valueRef.current = value;
            versionRef.current += 1;
            runWithNormalPriority(() => {
                contextValue.current[CONTEXT_VALUE].l.forEach((listener) => {
                    listener({ n: versionRef.current, v: value });
                });
            });
        }, [value]);
        return createElement(ProviderOrig, { value: contextValue.current }, children);
    };
    return ContextProvider;
};
/**
 * This creates a special context for `useContextSelector`.
 *
 * @example
 * import { createContext } from 'use-context-selector';
 *
 * const PersonContext = createContext({ firstName: '', familyName: '' });
 */
function createContext(defaultValue) {
    const context = createContext$1({
        [CONTEXT_VALUE]: {
            /* "v"alue     */ v: { current: defaultValue },
            /* versio"n"   */ n: { current: -1 },
            /* "l"isteners */ l: new Set(),
            /* "u"pdate    */ u: (f) => f(),
        },
    });
    context[ORIGINAL_PROVIDER] = context.Provider;
    context.Provider = createProvider(context.Provider);
    delete context.Consumer; // no support for Consumer
    return context;
}
/**
 * This hook returns context selected value by selector.
 *
 * It will only accept context created by `createContext`.
 * It will trigger re-render if only the selected value is referentially changed.
 *
 * The selector should return referentially equal result for same input for better performance.
 *
 * @example
 * import { useContextSelector } from 'use-context-selector';
 *
 * const firstName = useContextSelector(PersonContext, (state) => state.firstName);
 */
function useContextSelector(context, selector) {
    const contextValue = useContext(context)[CONTEXT_VALUE];
    if (typeof process === 'object' && process.env.NODE_ENV !== 'production') {
        if (!contextValue) {
            throw new Error('useContextSelector requires special context');
        }
    }
    const { 
    /* "v"alue     */ v: { current: value }, 
    /* versio"n"   */ n: { current: version }, 
    /* "l"isteners */ l: listeners, } = contextValue;
    const selected = selector(value);
    const [state, dispatch] = useReducer((prev, action) => {
        if (!action) {
            // case for `dispatch()` below
            return [value, selected];
        }
        if ('p' in action) {
            throw action.p;
        }
        if (action.n === version) {
            if (Object.is(prev[1], selected)) {
                return prev; // bail out
            }
            return [value, selected];
        }
        try {
            if ('v' in action) {
                if (Object.is(prev[0], action.v)) {
                    return prev; // do not update
                }
                const nextSelected = selector(action.v);
                if (Object.is(prev[1], nextSelected)) {
                    return prev; // do not update
                }
                return [action.v, nextSelected];
            }
        }
        catch (_e) {
            // ignored (stale props or some other reason)
        }
        return [...prev]; // schedule update
    }, [value, selected]);
    if (!Object.is(state[1], selected)) {
        // schedule re-render
        // this is safe because it's self contained
        dispatch();
    }
    useIsomorphicLayoutEffect(() => {
        listeners.add(dispatch);
        return () => {
            listeners.delete(dispatch);
        };
    }, [listeners]);
    return state[1];
}

export { createContext, useContextSelector };
//# sourceMappingURL=index.js.map
