(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('dexie'), require('react')) :
    typeof define === 'function' && define.amd ? define(['exports', 'dexie', 'react'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.DexieReactHooks = {}, global.Dexie, global.React));
})(this, (function (exports, dexie, React) { 'use strict';

    function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

    var React__default = /*#__PURE__*/_interopDefaultLegacy(React);

    function useObservable(observableFactory, arg2, arg3) {
        // Resolve vars from overloading variants of this function:
        var deps;
        var defaultResult;
        if (typeof observableFactory === 'function') {
            deps = arg2 || [];
            defaultResult = arg3;
        }
        else {
            deps = [];
            defaultResult = arg2;
        }
        // Create a ref that keeps the state we need
        var monitor = React__default["default"].useRef({
            hasResult: false,
            result: defaultResult,
            error: null,
        });
        // We control when component should rerender. Make triggerUpdate
        // as examplified on React's docs at:
        // https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate
        var _a = React__default["default"].useReducer(function (x) { return x + 1; }, 0); _a[0]; var triggerUpdate = _a[1];
        // Memoize the observable based on deps
        var observable = React__default["default"].useMemo(function () {
            // Make it remember previous subscription's default value when
            // resubscribing.
            var observable = typeof observableFactory === 'function'
                ? observableFactory()
                : observableFactory;
            if (!observable || typeof observable.subscribe !== 'function') {
                if (observableFactory === observable) {
                    throw new TypeError("Given argument to useObservable() was neither a valid observable nor a function.");
                }
                else {
                    throw new TypeError("Observable factory given to useObservable() did not return a valid observable.");
                }
            }
            if (!monitor.current.hasResult &&
                typeof window !== 'undefined' // Don't do this in SSR
            ) {
                // Optimize for BehaviorSubject and other observables implementing getValue():
                if (typeof observable.hasValue !== 'function' || observable.hasValue()) {
                    if (typeof observable.getValue === 'function') {
                        monitor.current.result = observable.getValue();
                        monitor.current.hasResult = true;
                    }
                    else {
                        // Find out if the observable has a current value: try get it by subscribing and
                        // unsubscribing synchronously
                        var subscription = observable.subscribe(function (val) {
                            monitor.current.result = val;
                            monitor.current.hasResult = true;
                        });
                        // Unsubscribe directly. We only needed any synchronous value if it was possible.
                        if (typeof subscription === 'function') {
                            subscription();
                        }
                        else {
                            subscription.unsubscribe();
                        }
                    }
                }
            }
            return observable;
        }, deps);
        // Integrate with react devtools:
        React__default["default"].useDebugValue(monitor.current.result);
        // Subscribe to the observable
        React__default["default"].useEffect(function () {
            var subscription = observable.subscribe(function (val) {
                var current = monitor.current;
                if (current.error !== null || current.result !== val) {
                    current.error = null;
                    current.result = val;
                    current.hasResult = true;
                    triggerUpdate();
                }
            }, function (err) {
                var current = monitor.current;
                if (current.error !== err) {
                    current.error = err;
                    triggerUpdate();
                }
            });
            return typeof subscription === 'function'
                ? subscription // Support observables that return unsubscribe directly
                : subscription.unsubscribe.bind(subscription);
        }, deps);
        // Throw if observable has emitted error so that
        // an ErrorBoundrary can catch it
        if (monitor.current.error)
            throw monitor.current.error;
        // Return the current result
        return monitor.current.result;
    }

    function useLiveQuery(querier, deps, defaultResult) {
        return useObservable(function () { return dexie.Dexie.liveQuery(querier); }, deps || [], defaultResult);
    }

    function usePermissions(firstArg, table, obj) {
        if (!firstArg)
            throw new TypeError("Invalid arguments to usePermissions(): undefined or null");
        var db;
        if (arguments.length >= 3) {
            if (!('transaction' in firstArg)) {
                // Using ducktyping instead of instanceof in case there are multiple Dexie modules in app.
                // First arg is  ensures first arg is a Dexie instance
                throw new TypeError("Invalid arguments to usePermission(db, table, obj): 1st arg must be a Dexie instance");
            }
            if (typeof table !== 'string')
                throw new TypeError("Invalid arguments to usePermission(db, table, obj): 2nd arg must be string");
            if (!obj || typeof obj !== 'object')
                throw new TypeError("Invalid arguments to usePermission(db, table, obj): 3rd arg must be an object");
            db = firstArg;
        }
        else {
            if (firstArg instanceof dexie.Dexie)
                throw new TypeError("Invalid arguments to usePermission(db, table, obj): Missing table and obj arguments.");
            if (typeof firstArg.table === 'function' &&
                typeof firstArg.db === 'object') {
                db = firstArg.db;
                obj = firstArg;
                table = firstArg.table();
            }
            else {
                throw new TypeError("Invalid arguments to usePermissions(). " +
                    "Expected usePermissions(entity: DexieCloudEntity) or " +
                    "usePermissions(db: Dexie, table: string, obj: DexieCloudObject)");
            }
        }
        if (!('cloud' in db))
            throw new Error("usePermissions() is only for Dexie Cloud but there's no dexie-cloud-addon active in given db.");
        if (!('permissions' in db.cloud))
            throw new Error("usePermissions() requires a newer version of dexie-cloud-addon. Please upgrade it.");
        return useObservable(
        // @ts-ignore
        function () { return db.cloud.permissions(obj, table); }, [obj.realmId, obj.owner, table]);
    }

    var gracePeriod = 100; // 100 ms = grace period to optimize for unload/reload scenarios
    var fr = typeof FinalizationRegistry !== 'undefined' && new FinalizationRegistry(function (doc) {
        // If coming here, react effect never ran. This is a fallback cleanup mechanism.
        var DexieYProvider = dexie.Dexie['DexieYProvider'];
        if (DexieYProvider)
            DexieYProvider.release(doc);
    });
    function useDocument(doc) {
        var _a, _b;
        if (!fr)
            throw new TypeError('FinalizationRegistry not supported.');
        var providerRef = React__default["default"].useRef(null);
        var DexieYProvider = dexie.Dexie['DexieYProvider'];
        if (!DexieYProvider) {
            throw new Error('DexieYProvider is not available. Make sure `y-dexie` is installed and imported.');
        }
        var unregisterToken = undefined;
        if (doc) {
            if (doc !== ((_a = providerRef.current) === null || _a === void 0 ? void 0 : _a.doc)) {
                providerRef.current = DexieYProvider.load(doc, { gracePeriod: gracePeriod });
                unregisterToken = Object.create(null);
                fr.register(providerRef, doc, unregisterToken);
            }
        }
        else if ((_b = providerRef.current) === null || _b === void 0 ? void 0 : _b.doc) {
            providerRef.current = null;
        }
        React__default["default"].useEffect(function () {
            if (doc) {
                // Doc is set or changed. Unregister provider from FinalizationRegistry
                // and instead take over from here to release the doc when component is unmounted
                // or when doc is changed. What we're doing here is to avoid relying on FinalizationRegistry
                // in all the normal cases and instead rely on React's lifecycle to release the doc.
                // But there can be situations when react never calls this effect and therefore, we
                // need to rely on FinalizationRegistry to release the doc as a fallback.
                // We cannot wait with loading the document until the effect happens, because the doc
                // could have been destroyed in the meantime.
                if (unregisterToken)
                    fr.unregister(unregisterToken);
                var provider = DexieYProvider.for(doc);
                if (provider) {
                    return function () {
                        DexieYProvider.release(doc);
                    };
                }
                else {
                    // Maybe the doc was destroyed in the meantime.
                    // Can not happen if React and FinalizationRegistry works as we expect them to.
                    // Except if a user had called DexieYProvider.release() on the doc
                    throw new Error("FATAL. DexieYProvider.release() has been called somewhere in application code, making us lose the document.");
                }
            }
        }, [doc, unregisterToken]);
        return providerRef.current;
    }

    exports.useDocument = useDocument;
    exports.useLiveQuery = useLiveQuery;
    exports.useObservable = useObservable;
    exports.usePermissions = usePermissions;

    Object.defineProperty(exports, '__esModule', { value: true });

}));
//# sourceMappingURL=dexie-react-hooks.js.map
