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());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import { Observable, gql, useApolloClient, useQuery, useSubscription } from '@apollo/client/index.js';
import { generateApolloClient } from '@deep-foundation/hasura/client.js';
import { useLocalStore } from '@deep-foundation/store/local.js';
import atob from 'atob';
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
import { corePckg } from './core.js';
import { debug } from './debug.js';
import { deleteMutation, generateQuery, generateQueryData, generateSerial, insertMutation, updateMutation } from './gql/index.js';
import { MinilinkCollection, MinilinksLink, useMinilinks, useMinilinksApply, useMinilinksQuery, useMinilinksSubscription } from './minilinks.js';
import { awaitPromise } from './promise.js';
import { useTokenController } from './react-token.js';
import { reserve } from './reserve.js';
import { Traveler as NativeTraveler } from './traveler.js';
import { evalClientHandler } from './client-handler.js';
import { Packager } from './packager.js';
import isEqual from 'lodash/isEqual.js';
import isNaN from 'lodash/isNaN.js';
import axios from 'axios';
import EventEmitter from 'events';
import { matchSorter } from 'match-sorter';
const moduleLog = debug.extend('client');
const log = debug.extend('log');
const error = debug.extend('error');
const corePckgIds = {};
corePckg.data.filter(l => !!l.type).forEach((l, i) => {
    corePckgIds[l.id] = i + 1;
});
export const random = () => Math.random().toString(36).slice(2, 7);
export function upload(linkId, file, deep) {
    return __awaiter(this, void 0, void 0, function* () {
        var formData = new FormData();
        formData.append("file", file);
        console.log('drop-zone formData', formData);
        yield axios.post(`http${deep.client.ssl ? 's' : ''}://${deep.client.path.slice(0, -4)}/file`, formData, {
            headers: {
                'linkId': linkId,
                "Authorization": `Bearer ${deep.token}`,
            },
        });
    });
}
export const _ids = {
    '@deep-foundation/core': corePckgIds,
};
export const _serialize = {
    links: {
        returning: 'id type_id from_id to_id value',
        virtualize: {
            id: ['id', '_id'],
            type_id: ['type_id', '_type_id'],
            from_id: ['from_id', '_from_id'],
            to_id: ['to_id', '_to_id'],
        },
        fields: {
            id: 'number',
            from_id: 'number',
            to_id: 'number',
            type_id: 'number',
            _id: 'number',
            _from_id: 'number',
            _to_id: 'number',
            _type_id: 'number',
        },
        relations: {
            from: 'links',
            to: 'links',
            type: 'links',
            in: 'links',
            out: 'links',
            typed: 'links',
            selected: 'selector',
            selectors: 'selector',
            value: 'value',
            string: 'strings',
            number: 'numbers',
            object: 'objects',
            file: 'files',
            can_rule: 'can',
            can_action: 'can',
            can_object: 'can',
            can_subject: 'can',
            down: 'tree',
            up: 'tree',
            tree: 'tree',
            root: 'tree',
        },
    },
    selector: {
        returning: 'item_id selector_id',
        fields: {
            item_id: 'number',
            selector_id: 'number',
            query_id: 'number',
            selector_include_id: 'number',
        },
        relations: {
            item: 'links',
            selector: 'links',
            query: 'links',
        }
    },
    can: {
        returning: 'rule_id action_id object_id subject_id',
        fields: {
            rule_id: 'number',
            action_id: 'number',
            object_id: 'number',
            subject_id: 'number',
        },
        relations: {
            rule: 'links',
            action: 'links',
            object: 'links',
            subject: 'links',
        }
    },
    tree: {
        returning: 'id link_id tree_id root_id parent_id depth position_id',
        fields: {
            id: 'number',
            link_id: 'number',
            tree_id: 'number',
            root_id: 'number',
            parent_id: 'number',
            depth: 'number',
            position_id: 'string',
        },
        relations: {
            link: 'links',
            tree: 'links',
            root: 'links',
            parent: 'links',
            by_link: 'tree',
            by_tree: 'tree',
            by_root: 'tree',
            by_parent: 'tree',
            by_position: 'tree',
        }
    },
    value: {
        returning: 'id link_id value',
        fields: {
            id: 'number',
            link_id: 'number',
            value: 'value',
        },
        relations: {
            link: 'links',
        },
    },
    strings: {
        returning: 'id link_id value',
        fields: {
            id: 'number',
            link_id: 'number',
            value: 'string',
        },
        relations: {
            link: 'links',
        },
    },
    numbers: {
        returning: 'id link_id value',
        fields: {
            id: 'number',
            link_id: 'number',
            value: 'number',
        },
        relations: {
            link: 'links',
        },
    },
    objects: {
        returning: 'id link_id value',
        fields: {
            id: 'number',
            link_id: 'number',
            value: 'object',
        },
        relations: {
            link: 'links',
        },
    },
    files: {
        returning: 'id link_id mimeType name size',
        fields: {
            bucketId: 'string',
            etag: 'string',
            id: 'number',
            isUploaded: 'boolean',
            link_id: 'number',
            mimeType: 'string',
            name: 'string',
            size: 'number',
        },
        relations: {
            link: 'links',
        },
    },
    handlers: {
        returning: 'dist_id src_id handler_id execution_provider_id isolation_provider_id',
        fields: {
            dist_id: 'number',
            execution_provider_id: 'number',
            handler_id: 'number',
            isolation_provider_id: 'number',
            src_id: 'number',
        },
        relations: {
            link: 'links',
            dist: 'links',
            execution_provider: 'links',
            handler: 'links',
            isolation_provider: 'links',
            src: 'links',
        },
    },
};
_serialize.strings = _serialize.value;
_serialize.numbers = _serialize.value;
_serialize.objects = _serialize.value;
export const _boolExpFields = {
    _and: true,
    _not: true,
    _or: true,
};
export const pathToWhere = (start, ...path) => {
    var _a, _b;
    const pckg = typeof (start) === 'string' ? { type_id: (_a = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _a === void 0 ? void 0 : _a.Package, value: start } : { id: start };
    let where = pckg;
    for (let p = 0; p < path.length; p++) {
        const item = path[p];
        if (typeof (item) !== 'boolean') {
            const nextWhere = { in: { type_id: (_b = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _b === void 0 ? void 0 : _b.Contain, value: item, from: where } };
            where = nextWhere;
        }
    }
    return where;
};
export const serializeWhere = (exp, env = 'links', unvertualizeId = defaultUnvertualizeId, globalExp) => {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _r;
    if (Object.prototype.toString.call(exp) === '[object Array]')
        return exp.map((e) => serializeWhere(e, env, unvertualizeId, globalExp));
    else if (typeof (exp) === 'object') {
        const keys = Object.keys(exp);
        const result = {};
        for (let k = 0; k < keys.length; k++) {
            let key = keys[k];
            let value = exp[key];
            let type = typeof (value);
            if (typeof (value) === 'undefined')
                throw new Error(`${key} === undefined${globalExp ? `in exp ${JSON.stringify(globalExp)}` : ''}`);
            let setted = false;
            key = addIdsToRelationsIfSimple(exp, key, env);
            if (env === 'links' && key === 'value' && (type === 'string' || type === 'number')) {
                key = type;
            }
            if (env === 'links' && (key === 'string' || key === 'number') && (type === key)) {
                value = { value: { _eq: value } };
            }
            if (((_b = (_a = _serialize === null || _serialize === void 0 ? void 0 : _serialize[env]) === null || _a === void 0 ? void 0 : _a.fields) === null || _b === void 0 ? void 0 : _b[key]) && (type === 'string' || type === 'number')) {
                value = { _eq: key.slice(-3) === '_id' ? unvertualizeId(value) : value };
            }
            const is_id_field = !!~['type_id', 'from_id', 'to_id'].indexOf(key);
            if (env === 'links') {
                if (key === 'relation') {
                    setted = result[key] = value;
                }
                else if (!_boolExpFields[key] && Object.prototype.toString.call(value) === '[object Array]') {
                    if ((_d = (_c = _serialize === null || _serialize === void 0 ? void 0 : _serialize[env]) === null || _c === void 0 ? void 0 : _c.relations) === null || _d === void 0 ? void 0 : _d[key]) {
                        setted = result[key] = serializeWhere(value, (_f = (_e = _serialize === null || _serialize === void 0 ? void 0 : _serialize[env]) === null || _e === void 0 ? void 0 : _e.relations) === null || _f === void 0 ? void 0 : _f[key], unvertualizeId, globalExp);
                    }
                    else {
                        setted = result[key] = serializeWhere(pathToWhere(...value), 'links', unvertualizeId, globalExp);
                    }
                }
            }
            else if (env === 'tree') {
                if (!_boolExpFields[key] && Object.prototype.toString.call(value) === '[object Array]') {
                    setted = result[key] = serializeWhere(pathToWhere(...value), 'links', unvertualizeId, globalExp);
                }
            }
            if (key === 'return') {
                setted = result[key] = {};
                for (let r in value) {
                    const relation = (_g = value[r]) === null || _g === void 0 ? void 0 : _g.relation;
                    if ((_j = (_h = _serialize === null || _serialize === void 0 ? void 0 : _serialize[env]) === null || _h === void 0 ? void 0 : _h.relations) === null || _j === void 0 ? void 0 : _j[relation])
                        result[key][r] = serializeWhere(value[r], ((_l = (_k = _serialize === null || _serialize === void 0 ? void 0 : _serialize[env]) === null || _k === void 0 ? void 0 : _k.relations) === null || _l === void 0 ? void 0 : _l[relation]) || env, unvertualizeId, globalExp);
                }
            }
            if (type === 'object' && (value === null || value === void 0 ? void 0 : value.hasOwnProperty('_type_of')) && ((env === 'links' && (is_id_field || key === 'id')) ||
                (env === 'selector' && key === 'item_id') ||
                (env === 'can' && !!~['rule_id', 'action_id', 'subject_id', 'object_id',].indexOf(key)) ||
                (env === 'tree' && !!~['link_id', 'tree_id', 'root_id', 'parent_id'].indexOf(key)) ||
                (env === 'value' && key === 'link_id'))) {
                const _temp = setted = { _by_item: { path_item_id: { _eq: unvertualizeId(value._type_of) }, group_id: { _eq: _ids['@deep-foundation/core'].typesTree } } };
                if (key === 'id') {
                    result._and = result._and ? [...result._and, _temp] : [_temp];
                }
                else {
                    result[key.slice(0, -3)] = _temp;
                }
            }
            else if (type === 'object' && (value === null || value === void 0 ? void 0 : value.hasOwnProperty('_id')) && ((env === 'links' && (is_id_field || key === 'id')) ||
                (env === 'selector' && key === 'item_id') ||
                (env === 'can' && !!~['rule_id', 'action_id', 'subject_id', 'object_id',].indexOf(key)) ||
                (env === 'tree' && !!~['link_id', 'tree_id', 'root_id', 'parent_id'].indexOf(key)) ||
                (env === 'value' && key === 'link_id')) && Object.prototype.toString.call(value._id) === '[object Array]' && value._id.length >= 1) {
                const root = value._id[0];
                const _temp = setted = serializeWhere(pathToWhere(typeof (root) === 'number' ? unvertualizeId(root) : root, ...value._id.slice(1)), 'links', unvertualizeId, globalExp);
                if (key === 'id') {
                    result._and = result._and ? [...result._and, _temp] : [_temp];
                }
                else {
                    result[key.slice(0, -3)] = _temp;
                }
            }
            if (!setted) {
                const _temp = (_boolExpFields[key]) ? (serializeWhere(value, env, unvertualizeId, globalExp)) : ((_o = (_m = _serialize === null || _serialize === void 0 ? void 0 : _serialize[env]) === null || _m === void 0 ? void 0 : _m.relations) === null || _o === void 0 ? void 0 : _o[key]) ? (serializeWhere(value, (_r = (_p = _serialize === null || _serialize === void 0 ? void 0 : _serialize[env]) === null || _p === void 0 ? void 0 : _p.relations) === null || _r === void 0 ? void 0 : _r[key], unvertualizeId, globalExp)) : (value);
                if (key === '_and')
                    result._and ? result._and.push(..._temp) : result._and = _temp;
                else
                    result[key] = _temp;
            }
        }
        return result;
    }
    else {
        if (typeof (exp) === 'undefined')
            throw new Error('undefined in query');
        return exp;
    }
};
const defaultUnvertualizeId = (id) => id;
export const serializeQuery = (exp, env = 'links', unvertualizeId = defaultUnvertualizeId) => {
    const { limit, order_by, offset, distinct_on } = exp, where = __rest(exp, ["limit", "order_by", "offset", "distinct_on"]);
    const result = { where: typeof (exp) === 'object' ? Object.prototype.toString.call(exp) === '[object Array]' ? { id: { _in: exp.map(id => unvertualizeId(id)) } } : serializeWhere(where, env, unvertualizeId, exp) : { id: { _eq: unvertualizeId(exp) } } };
    if (limit)
        result.limit = limit;
    if (order_by)
        result.order_by = order_by;
    if (offset)
        result.offset = offset;
    if (distinct_on)
        result.distinct_on = distinct_on;
    return result;
};
export function parseJwt(token) {
    var base64Url = token.split('.')[1];
    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    var jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
    const parsed = JSON.parse(jsonPayload);
    const _a = parsed['https://hasura.io/jwt/claims'] || {}, { 'x-hasura-allowed-roles': roles, 'x-hasura-default-role': role, 'x-hasura-user-id': userId } = _a, other = __rest(_a, ['x-hasura-allowed-roles', 'x-hasura-default-role', 'x-hasura-user-id']);
    return Object.assign({ userId: +userId, role, roles }, other);
}
;
;
;
export function addIdsToRelationsIfSimple(exp, key, env) {
    var _a, _b;
    const type = typeof (exp[key]);
    if (((_b = (_a = _serialize === null || _serialize === void 0 ? void 0 : _serialize[env]) === null || _a === void 0 ? void 0 : _a.relations) === null || _b === void 0 ? void 0 : _b[key]) === 'links' && (type === 'string' || type === 'number')) {
        if (exp[`${key}_id`])
            throw new Error(`Cant work with ${key} and ${key}_id in one link`);
        return key + '_id';
    }
    return key;
}
export function checkAndFillShorts(obj, table, containerId, Contain, field) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j;
    if (obj.hasOwnProperty('containerId') && !obj.containerId) {
        throw new Error(`containerId is undefined`);
    }
    const cId = obj.containerId || containerId;
    for (var _key in obj) {
        let key = _key;
        const type = obj[key];
        key = addIdsToRelationsIfSimple(obj, key, table);
        if (!obj.hasOwnProperty(key))
            continue;
        if ((typeof obj[key]) == 'object' && obj[key] !== null) {
            if (typeof obj[key] === 'object' && key === 'object' && ((_b = (_a = obj[key]) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.value) === undefined) {
                obj[key] = { data: { value: obj[key] } };
                continue;
            }
            if (typeof obj[key] === 'object' && (key === 'to' || key === 'from' || key === 'in' || key === 'out' || key === 'typed' || key === 'type') && ((_c = obj[key]) === null || _c === void 0 ? void 0 : _c.data) === undefined)
                obj[key] = { data: obj[key] };
            checkAndFillShorts(obj[key], ((_e = (_d = _serialize[table]) === null || _d === void 0 ? void 0 : _d.relations) === null || _e === void 0 ? void 0 : _e[key]) || table, cId, Contain, key);
        }
        else if (key === 'string' && typeof obj[key] === 'string' || key === 'number' && typeof obj[key] === 'number')
            obj[key] = { data: { value: obj[key] } };
    }
    if (cId && !Array.isArray(obj) && !obj.data && table === 'links' && ((!field || [
        'to', 'from', 'in', 'out', 'type', 'typed'
    ].includes(field)) || obj.type_id || obj.type)) {
        obj.in = obj.in || {};
        obj.in.data = ((_f = obj === null || obj === void 0 ? void 0 : obj.in) === null || _f === void 0 ? void 0 : _f.data) ? Array.isArray((_g = obj === null || obj === void 0 ? void 0 : obj.in) === null || _g === void 0 ? void 0 : _g.data) ? (_h = obj === null || obj === void 0 ? void 0 : obj.in) === null || _h === void 0 ? void 0 : _h.data : [(_j = obj === null || obj === void 0 ? void 0 : obj.in) === null || _j === void 0 ? void 0 : _j.data] : [];
        obj.in.data.push(Object.assign({ type_id: Contain, from_id: cId }, (typeof (obj.name) === 'string' ? { string: { data: { value: obj.name } } } : {})));
    }
    delete obj.name;
    delete obj.containerId;
}
export function convertDeepInsertToMinilinksApplyAndPatchVirtualIds(deep, objects, table, result = [], errors, patch = {}) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _r, _s, _t, _u, _v, _w;
    const levelIds = [];
    if (table === 'links') {
        for (let i = 0; i < objects.length; i++) {
            const o = objects[i];
            let id = o.id;
            if (!id) {
                id = deep.minilinks.virtualCounter--;
                (_b = (_a = deep.minilinks) === null || _a === void 0 ? void 0 : _a.byId[id]) === null || _b === void 0 ? void 0 : _b._id = apply;
            }
            levelIds.push(id);
            const patchRelationIds = {};
            if (o === null || o === void 0 ? void 0 : o.from) {
                const { objects: objs, levelIds } = convertDeepInsertToMinilinksApplyAndPatchVirtualIds(deep, [(_c = o === null || o === void 0 ? void 0 : o.from) === null || _c === void 0 ? void 0 : _c.data], table, result, errors);
                o.from.data = objs;
                patchRelationIds.from_id = levelIds[0];
            }
            if (o === null || o === void 0 ? void 0 : o.to) {
                const { objects: objs, levelIds } = convertDeepInsertToMinilinksApplyAndPatchVirtualIds(deep, [(_d = o === null || o === void 0 ? void 0 : o.to) === null || _d === void 0 ? void 0 : _d.data], table, result, errors);
                o.to.data = objs;
                patchRelationIds.to_id = levelIds[0];
            }
            if (o === null || o === void 0 ? void 0 : o.type) {
                const { objects: objs, levelIds } = convertDeepInsertToMinilinksApplyAndPatchVirtualIds(deep, [(_e = o === null || o === void 0 ? void 0 : o.type) === null || _e === void 0 ? void 0 : _e.data], table, result, errors);
                o.type.data = objs;
                patchRelationIds.type_id = levelIds[0];
            }
            if (o === null || o === void 0 ? void 0 : o.out) {
                const { objects: objs, levelIds } = convertDeepInsertToMinilinksApplyAndPatchVirtualIds(deep, (((_g = (_f = o === null || o === void 0 ? void 0 : o.out) === null || _f === void 0 ? void 0 : _f.data) === null || _g === void 0 ? void 0 : _g.length) ? (_h = o === null || o === void 0 ? void 0 : o.out) === null || _h === void 0 ? void 0 : _h.data : [(_j = o === null || o === void 0 ? void 0 : o.out) === null || _j === void 0 ? void 0 : _j.data]), table, result, errors, { from_id: id });
                o.out.data = objs;
            }
            if (o === null || o === void 0 ? void 0 : o.in) {
                const { objects: objs, levelIds } = convertDeepInsertToMinilinksApplyAndPatchVirtualIds(deep, (((_l = (_k = o === null || o === void 0 ? void 0 : o.in) === null || _k === void 0 ? void 0 : _k.data) === null || _l === void 0 ? void 0 : _l.length) ? (_m = o === null || o === void 0 ? void 0 : o.in) === null || _m === void 0 ? void 0 : _m.data : [(_o = o === null || o === void 0 ? void 0 : o.in) === null || _o === void 0 ? void 0 : _o.data]), table, result, errors, { to_id: id });
                o.in.data = objs;
            }
            if (o === null || o === void 0 ? void 0 : o.types) {
                const { objects: objs, levelIds } = convertDeepInsertToMinilinksApplyAndPatchVirtualIds(deep, (((_r = (_p = o === null || o === void 0 ? void 0 : o.types) === null || _p === void 0 ? void 0 : _p.data) === null || _r === void 0 ? void 0 : _r.length) ? (_s = o === null || o === void 0 ? void 0 : o.types) === null || _s === void 0 ? void 0 : _s.data : [(_t = o === null || o === void 0 ? void 0 : o.types) === null || _t === void 0 ? void 0 : _t.data]), table, result, errors, { type_id: id });
                o.types.data = objs;
            }
            if (typeof (o.from_id) === 'number' || typeof (o.from_id) === 'string') {
                if (o.from_id < 0) {
                    if (deep.minilinks.virtual[o.from_id])
                        o.from_id = deep.minilinks.virtual[o.from_id];
                    else
                        errors.push(`.from_id=${o.from_id} can't be devertualized, not exists in minilinks.virtual[${o.from_id}]`);
                }
            }
            if (typeof (o.to_id) === 'number' || typeof (o.to_id) === 'string') {
                if (o.to_id < 0) {
                    if (deep.minilinks.virtual[o.to_id])
                        o.to_id = deep.minilinks.virtual[o.to_id];
                    else
                        errors.push(`.to_id=${o.to_id} can't be devertualized, not exists in minilinks.virtual[${o.to_id}]`);
                }
            }
            if (typeof (o.type_id) === 'number' || typeof (o.type_id) === 'string') {
                if (o.type_id < 0) {
                    if (deep.minilinks.virtual[o.type_id])
                        o.type_id = deep.minilinks.virtual[o.type_id];
                    else
                        errors.push(`.type_id=${o.type_id} can't be devertualized, not exists in minilinks.virtual[${o.type_id}]`);
                }
            }
            result.push(Object.assign(Object.assign({ id: id, from_id: o.from_id, to_id: o.to_id, type_id: o.type_id, value: ((_u = o.string) === null || _u === void 0 ? void 0 : _u.data) || ((_v = o.number) === null || _v === void 0 ? void 0 : _v.data) || ((_w = o.object) === null || _w === void 0 ? void 0 : _w.data) }, patch), patchRelationIds));
        }
    }
    return { objects: objects.length == 1 ? objects[0] : objects, levelIds };
}
export function convertDeepUpdateToMinilinksApply(ml, _exp, _set, table, toUpdate = []) {
    if (table === 'links') {
        try {
            const founded = ml.query(_exp);
            for (let f of founded) {
                toUpdate.push(Object.assign({ id: f.id, from_id: f.from_id, to_id: f.to_id, type_id: f.type_id, value: f.value }, _set));
            }
        }
        catch (e) { }
    }
    else if (table === 'strings' || table === 'numbers' || table === 'objects') {
        const key = table.slice(0, -1);
        const founded = ml.query({ [key]: _exp });
        for (let f of founded) {
            toUpdate.push({
                id: f.id, from_id: f.from_id, to_id: f.to_id, type_id: f.type_id,
                value: Object.assign(Object.assign({}, f === null || f === void 0 ? void 0 : f.value), { value: _set.value }),
            });
        }
    }
}
export function convertDeepDeleteToMinilinksApply(ml, _exp, table, toDelete = [], toUpdate = []) {
    if (table === 'links') {
        try {
            const founded = ml.query(_exp);
            toDelete.push(...founded.map(l => l.id));
        }
        catch (e) { }
    }
    else if (table === 'strings' || table === 'numbers' || table === 'objects') {
        const key = table.slice(0, -1);
        const founded = ml.query({ [key]: _exp });
        toUpdate.push(...founded.map(o => ({
            id: o.id, from_id: o.from_id, to_id: o.to_id, type_id: o.type_id,
        })));
    }
}
export class DeepClient {
    _silent(options = {}) {
        return typeof (options.silent) === 'boolean' ? options.silent : this.silent;
    }
    constructor(options) {
        var _a, _b, _c, _d, _e, _f, _g;
        this.useDeep = useDeep;
        this.DeepProvider = DeepProvider;
        this.DeepContext = DeepContext;
        this.DeepClient = DeepClient;
        this.namespace = (options === null || options === void 0 ? void 0 : options.namespace) || random();
        this.emitter = new EventEmitter();
        this.local = typeof (options === null || options === void 0 ? void 0 : options.local) === 'boolean' ? options === null || options === void 0 ? void 0 : options.local : true;
        this.remote = typeof (options === null || options === void 0 ? void 0 : options.remote) === 'boolean' ? options === null || options === void 0 ? void 0 : options.remote : true;
        if ((options === null || options === void 0 ? void 0 : options.needConnection) != false) {
            this.deep = options === null || options === void 0 ? void 0 : options.deep;
            this.apolloClient = options === null || options === void 0 ? void 0 : options.apolloClient;
            this.token = options === null || options === void 0 ? void 0 : options.token;
            this.client = this.apolloClient;
            this.table = (options === null || options === void 0 ? void 0 : options.table) || 'links';
            if ((this.deep && !this.apolloClient) || (!!options.path && typeof (options.ssl) === 'boolean')) {
                const token = ((_a = this === null || this === void 0 ? void 0 : this.deep) === null || _a === void 0 ? void 0 : _a.token) || options.token;
                if (!token) {
                    throw new Error('token for apolloClient is invalid or not provided');
                }
                this.apolloClient = generateApolloClient({
                    path: ((_c = (_b = this.deep) === null || _b === void 0 ? void 0 : _b.apolloClient) === null || _c === void 0 ? void 0 : _c.path) || options.path,
                    ssl: ((_e = (_d = this.deep) === null || _d === void 0 ? void 0 : _d.apolloClient) === null || _e === void 0 ? void 0 : _e.ssl) || options.ssl,
                    token: token,
                });
            }
            if (!this.apolloClient)
                throw new Error('apolloClient is invalid or not provided');
            if (this.token) {
                const decoded = parseJwt(this.token);
                const linkId = decoded === null || decoded === void 0 ? void 0 : decoded.userId;
                if (!linkId) {
                    throw new Error(`Unable to parse linkId from jwt token.`);
                }
                if (options.linkId && options.linkId !== linkId) {
                    throw new Error(`linkId (${linkId}) parsed from jwt token is not the same as linkId passed via options (${options.linkId}).`);
                }
                this.linkId = linkId;
            }
            else {
                this.linkId = options.linkId;
            }
        }
        this.unvertualizeId = (id) => {
            return this.minilinks.virtual.hasOwnProperty(id) ? this.minilinks.virtual[id] : id;
        };
        this.linksSelectReturning = options.selectReturning || _serialize.links.returning;
        this.selectReturning = options.selectReturning || this.linksSelectReturning;
        this.selectorsSelectReturning = options.selectorsSelectReturning || _serialize.selector.returning;
        this.canSelectReturning = options.canSelectReturning || _serialize.can.returning;
        this.treeSelectReturning = options.treeSelectReturning || _serialize.tree.returning;
        this.valuesSelectReturning = options.valuesSelectReturning || _serialize.value.returning;
        this.filesSelectReturning = options.filesSelectReturning || _serialize.files.returning;
        this.handlersSelectReturning = options.handlersSelectReturning || _serialize.handlers.returning;
        this.insertReturning = options.insertReturning || 'id';
        this.updateReturning = options.updateReturning || 'id';
        this.deleteReturning = options.deleteReturning || 'id';
        this.defaultSelectName = options.defaultSelectName || 'SELECT';
        this.defaultInsertName = options.defaultInsertName || 'INSERT';
        this.defaultUpdateName = options.defaultUpdateName || 'UPDATE';
        this.defaultDeleteName = options.defaultDeleteName || 'DELETE';
        this.silent = options.silent || false;
        this.unsafe = options.unsafe || {};
        this.handleAuth = (options === null || options === void 0 ? void 0 : options.handleAuth) || ((_f = options === null || options === void 0 ? void 0 : options.deep) === null || _f === void 0 ? void 0 : _f.handleAuth);
        this.handleOperation = (options === null || options === void 0 ? void 0 : options.handleOperation) || ((_g = options === null || options === void 0 ? void 0 : options.deep) === null || _g === void 0 ? void 0 : _g.handleOperation);
        this.minilinks = options.minilinks || new MinilinkCollection();
        this.ml = this.minilinks;
        if (!this.ml.deep)
            this.ml.deep = this;
        this._generateHooks(this);
    }
    _generateHooks(deep) {
        this.useDeepId = (start, ...path) => _useDeepId(deep, start, ...path);
        this.useId = this.useDeepId;
        this.useDeepSubscription = (query, options) => useDeepSubscription(query, Object.assign(Object.assign({}, (options || {})), { deep: deep }));
        this.useSubscription = useDeepSubscription;
        this.useDeepQuery = (query, options) => useDeepQuery(query, Object.assign(Object.assign({}, (options || {})), { deep: deep }));
        this.useQuery = useDeepQuery;
        this.useLink = useLink;
        this.useLinks = useLinks;
        this.useMinilinksQuery = (query, options) => useMinilinksQuery(deep.minilinks, query, options);
        this.useMinilinksSubscription = (query, options) => useMinilinksSubscription(deep.minilinks, query, options);
        this.useMinilinksApply = (data, name) => useMinilinksApply(deep.minilinks, name, data);
        this.useLocalQuery = this.useMinilinksQuery;
        this.useLocalSubscription = this.useMinilinksSubscription;
        this.useLocalApply = this.useMinilinksApply;
        this.useSearch = useSearch;
    }
    stringify(any) {
        if (typeof (any) === 'string') {
            let json;
            try {
                json = JSON.parse(any);
            }
            catch (e) { }
            return json ? JSON.stringify(json, null, 2) : any.toString();
        }
        else if (typeof (any) === 'object') {
            return JSON.stringify(any, null, 2);
        }
        return any;
    }
    serializeQuery(exp, env) { return serializeQuery(exp, env, this.unvertualizeId); }
    serializeWhere(exp, env) { return serializeWhere(exp, env, this.unvertualizeId, exp); }
    _generateQuery(exp, options) {
        var _a;
        const query = serializeQuery(exp, (options === null || options === void 0 ? void 0 : options.table) || 'links', this.unvertualizeId);
        const table = (options === null || options === void 0 ? void 0 : options.table) || this.table;
        const returning = (options === null || options === void 0 ? void 0 : options.returning) || ((_a = _serialize === null || _serialize === void 0 ? void 0 : _serialize[table]) === null || _a === void 0 ? void 0 : _a.returning) || 'id';
        const tableNamePostfix = options === null || options === void 0 ? void 0 : options.tableNamePostfix;
        const aggregate = options === null || options === void 0 ? void 0 : options.aggregate;
        const variables = options === null || options === void 0 ? void 0 : options.variables;
        const name = (options === null || options === void 0 ? void 0 : options.name) || this.defaultSelectName;
        const queryData = generateQueryData({
            tableName: table,
            tableNamePostfix: tableNamePostfix || aggregate ? '_aggregate' : '',
            returning: aggregate ? `aggregate { ${aggregate} }` : returning,
            variables: Object.assign(Object.assign({}, variables), query),
        });
        return {
            query: generateQuery({
                operation: (options === null || options === void 0 ? void 0 : options.subscription) ? 'subscription' : 'query',
                queries: [
                    queryData,
                ],
                name: name,
            }),
            queryData,
        };
    }
    _generateResult(exp, options, data) {
        return data;
    }
    select(exp, options) {
        var _a, _b, _c, _d, _e, _f;
        return __awaiter(this, void 0, void 0, function* () {
            if (!exp)
                return { error: { message: '!exp' }, data: undefined, loading: false, networkStatus: undefined };
            const aggregate = options === null || options === void 0 ? void 0 : options.aggregate;
            const name = random();
            this.emitter.emit('select.before', { deep: this, name, query: exp, options, remote: true, local: false });
            const queryData = this._generateQuery(exp, options);
            try {
                const q = yield this.apolloClient.query({ query: queryData.query.query, variables: (_a = queryData === null || queryData === void 0 ? void 0 : queryData.query) === null || _a === void 0 ? void 0 : _a.variables });
                const results = Object.assign(Object.assign({}, q), { data: aggregate ? (_e = (_d = (_c = (_b = (q)) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.q0) === null || _d === void 0 ? void 0 : _d.aggregate) === null || _e === void 0 ? void 0 : _e[aggregate] : yield this._generateResult(exp, options, (_f = q === null || q === void 0 ? void 0 : q.data) === null || _f === void 0 ? void 0 : _f.q0), return: exp === null || exp === void 0 ? void 0 : exp.return, query: exp });
                if (options === null || options === void 0 ? void 0 : options.apply) {
                    results.originalData = results.data;
                    const { data: localData, plainLinks } = this.minilinks.apply(results.data, options.apply);
                    results.data = localData;
                    results.plainLinks = plainLinks;
                    this.emitter.emit('select', { deep: this, name, query: exp, options, remoteQuery: queryData, remoteData: results.data, localData: results.data, plainLinks, remote: true, local: false, error: results.error });
                }
                else {
                    this.emitter.emit('select', { deep: this, name, query: exp, options, remoteQuery: queryData, remoteData: results.data, remote: true, local: false, error: results.error });
                }
                return results;
            }
            catch (e) {
                this.emitter.emit('select', { deep: this, name, query: exp, options, remoteQuery: queryData, error: e, remote: true, local: false });
                console.dir({ queryData }, { depth: null });
                throw new Error(`DeepClient Select Error: ${e.message}`, { cause: e });
            }
        });
    }
    ;
    subscribe(exp, options) {
        var _a;
        if (!exp)
            return new Observable((observer) => observer.error('!exp'));
        const aggregate = options === null || options === void 0 ? void 0 : options.aggregate;
        const queryData = this._generateQuery(exp, Object.assign(Object.assign({}, options), { subscription: true }));
        const name = random();
        this.emitter.emit('subscribe.before', { deep: this, name, query: exp, options, remote: true, local: false });
        try {
            const apolloObservable = this.apolloClient.subscribe({ query: queryData.query.query, variables: (_a = queryData === null || queryData === void 0 ? void 0 : queryData.query) === null || _a === void 0 ? void 0 : _a.variables });
            const observable = new Observable((observer) => {
                observer.name = name;
                const subscription = apolloObservable.subscribe({
                    next: (data) => __awaiter(this, void 0, void 0, function* () {
                        var _a, _b, _c, _d;
                        const results = aggregate ? (_c = (_b = (_a = data === null || data === void 0 ? void 0 : data.data) === null || _a === void 0 ? void 0 : _a.q0) === null || _b === void 0 ? void 0 : _b.aggregate) === null || _c === void 0 ? void 0 : _c[aggregate] : Object.assign(Object.assign({}, (yield this._generateResult(exp, options, (_d = data === null || data === void 0 ? void 0 : data.data) === null || _d === void 0 ? void 0 : _d.q0))), { return: exp === null || exp === void 0 ? void 0 : exp.return, query: exp });
                        if (options === null || options === void 0 ? void 0 : options.apply) {
                            results.originalData = results.data;
                            const { data: localData, plainLinks } = this.minilinks.apply(results.data, options.apply);
                            results.data = localData;
                            results.plainLinks = plainLinks;
                            this.emitter.emit('subscribe', { deep: this, name, query: exp, options, remoteQuery: queryData, remoteData: results.data, localData: results.data, plainLinks, remote: true, local: false, error: results.error });
                        }
                        else {
                            this.emitter.emit('subscribe', { deep: this, name, query: exp, options, remoteQuery: queryData, remoteData: results.data, remote: true, local: false, error: results.error });
                        }
                        observer.next(results);
                    }),
                    error: (error) => {
                        this.emitter.emit('subscribe', { deep: this, name, query: exp, options, remoteQuery: queryData, error, remote: true, local: false });
                        observer.error(error);
                    },
                });
                return () => subscription.unsubscribe();
            });
            return observable;
        }
        catch (e) {
            throw new Error(`DeepClient Subscription Error: ${e.message}`, { cause: e });
        }
    }
    ;
    insert(objects, options) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
        return __awaiter(this, void 0, void 0, function* () {
            const o = Object.assign({ local: this.local, remote: this.remote }, options);
            const { local, remote } = o;
            const _name = random();
            this.emitter.emit('insert.before', { deep: this, name: _name, value: objects, options, remote, local });
            const table = (options === null || options === void 0 ? void 0 : options.table) || this.table;
            const returning = (options === null || options === void 0 ? void 0 : options.returning) || this.insertReturning;
            const variables = options === null || options === void 0 ? void 0 : options.variables;
            const name = (options === null || options === void 0 ? void 0 : options.name) || this.defaultInsertName;
            let q = {};
            const containerId = options === null || options === void 0 ? void 0 : options.containerId;
            let _objects = Object.prototype.toString.call(objects) === '[object Array]' ? objects : [objects];
            checkAndFillShorts(_objects, table, containerId, this.idLocal('@deep-foundation/core', 'Contain'));
            this.handleOperation && this.handleOperation('insert', null, _objects, o);
            let file;
            if ((_a = _objects === null || _objects === void 0 ? void 0 : _objects[0]) === null || _a === void 0 ? void 0 : _a.file) {
                if ((_objects === null || _objects === void 0 ? void 0 : _objects.length) != 1)
                    throw new Error('Cannot insert multiple files at once');
                if (table != 'links')
                    throw new Error('Cannot insert file if table is not links');
                file = (_b = _objects === null || _objects === void 0 ? void 0 : _objects[0]) === null || _b === void 0 ? void 0 : _b.file;
                (_c = _objects === null || _objects === void 0 ? void 0 : _objects[0]) === null || _c === void 0 ? true : delete _c.file;
                if (typeof (file) !== 'string' && !(file instanceof Blob))
                    throw new Error('File must be a string or Blob');
            }
            const toApply = [];
            if (this.minilinks && local !== false) {
                const errors = [];
                const { objects: __objects } = convertDeepInsertToMinilinksApplyAndPatchVirtualIds(this, _objects, table, toApply, errors);
                _objects = __objects;
                if (errors.length)
                    console.log('convertDeepInsertToMinilinksApplyAndPatchVirtualIds', 'errors', errors);
                this.minilinks.add(toApply);
            }
            let mutate;
            if (remote !== false) {
                try {
                    mutate = generateSerial({
                        actions: [insertMutation(table, Object.assign(Object.assign({}, variables), { objects: _objects }), { tableName: table, operation: 'insert', returning })],
                        name: name,
                    });
                    q = yield this.apolloClient.mutate(mutate);
                }
                catch (e) {
                    const sqlError = (_g = (_f = (_e = (_d = e === null || e === void 0 ? void 0 : e.graphQLErrors) === null || _d === void 0 ? void 0 : _d[0]) === null || _e === void 0 ? void 0 : _e.extensions) === null || _f === void 0 ? void 0 : _f.internal) === null || _g === void 0 ? void 0 : _g.error;
                    if (sqlError === null || sqlError === void 0 ? void 0 : sqlError.message)
                        e.message = sqlError.message;
                    if (!this._silent(options))
                        throw new Error(`DeepClient Insert Error: ${e.message}`, { cause: e });
                    this.emitter.emit('insert', { deep: this, name: _name, value: objects, options, remoteQuery: mutate, file, error: e, remote, local });
                    return Object.assign(Object.assign({}, q), { data: (_k = (_j = (_h = (q)) === null || _h === void 0 ? void 0 : _h.data) === null || _j === void 0 ? void 0 : _j.m0) === null || _k === void 0 ? void 0 : _k.returning, error: e });
                }
                if (file) {
                    if (!((_l = toApply === null || toApply === void 0 ? void 0 : toApply[0]) === null || _l === void 0 ? void 0 : _l.id))
                        throw new Error('Cannot insert file without link');
                    yield upload(toApply[0].id, file, this);
                }
                ;
            }
            else {
                this.emitter.emit('insert', { deep: this, name: _name, value: objects, options, remoteQuery: mutate, file, remote, local });
                return Object.assign(Object.assign({}, q), { data: toApply.map(l => l.id), loading: false });
            }
            this.emitter.emit('insert', { deep: this, name: _name, value: objects, options, remoteQuery: mutate, remote, local, error: q.error });
            return Object.assign(Object.assign({}, q), { data: (_p = (_o = (_m = (q)) === null || _m === void 0 ? void 0 : _m.data) === null || _o === void 0 ? void 0 : _o.m0) === null || _p === void 0 ? void 0 : _p.returning });
        });
    }
    ;
    update(exp, value, options) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        return __awaiter(this, void 0, void 0, function* () {
            const _exp = typeof (exp) === 'number' ? { id: exp } : exp;
            if (_exp === null)
                return this.insert([value], options);
            if (value === null)
                return this.delete(_exp, options);
            const table = (options === null || options === void 0 ? void 0 : options.table) || this.table;
            const _name = random();
            if (isEqual(_exp, {}))
                throw new Error('exp {} is wrong');
            const keys = Object.keys(_exp);
            for (let k in keys) {
                const key = keys[k];
                _exp[addIdsToRelationsIfSimple(_exp, key, table)] = _exp[key];
            }
            const o = Object.assign({ local: this.local, remote: this.remote }, options);
            this.handleOperation && this.handleOperation('update', _exp, value, o);
            const { local, remote } = o;
            this.emitter.emit('update.before', { deep: this, name: _name, query: _exp, value, options, remote, local });
            const query = serializeQuery(_exp, (options === null || options === void 0 ? void 0 : options.table) === this.table || !(options === null || options === void 0 ? void 0 : options.table) ? 'links' : 'value', this.unvertualizeId);
            const toUpdate = [];
            if (this.minilinks && local !== false) {
                convertDeepUpdateToMinilinksApply(this.minilinks, _exp, value, table, toUpdate);
                this.minilinks.update(toUpdate);
            }
            const returning = (options === null || options === void 0 ? void 0 : options.returning) || this.updateReturning;
            const variables = options === null || options === void 0 ? void 0 : options.variables;
            const name = (options === null || options === void 0 ? void 0 : options.name) || this.defaultUpdateName;
            let q;
            let mutate;
            if (remote !== false) {
                try {
                    mutate = generateSerial({
                        actions: [updateMutation(table, Object.assign(Object.assign(Object.assign({}, variables), query), { _set: value }), { tableName: table, operation: 'update', returning })],
                        name: name,
                    });
                    q = yield this.apolloClient.mutate(mutate);
                }
                catch (e) {
                    const sqlError = (_d = (_c = (_b = (_a = e === null || e === void 0 ? void 0 : e.graphQLErrors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.extensions) === null || _c === void 0 ? void 0 : _c.internal) === null || _d === void 0 ? void 0 : _d.error;
                    if (sqlError === null || sqlError === void 0 ? void 0 : sqlError.message)
                        e.message = sqlError.message;
                    if (!this._silent(options))
                        throw new Error(`DeepClient Update Error: ${e.message}`, { cause: e });
                    this.emitter.emit('update', { deep: this, name: _name, query: _exp, value, options, remoteQuery: mutate, remote, error, local, toUpdate });
                    return Object.assign(Object.assign({}, q), { data: (_g = (_f = (_e = (q)) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.m0) === null || _g === void 0 ? void 0 : _g.returning, error: e });
                }
            }
            else {
                this.emitter.emit('update', { deep: this, name: _name, query: _exp, value, options, remoteQuery: mutate, remote, local, toUpdate });
                return Object.assign(Object.assign({}, q), { data: toUpdate.map(l => l.id), loading: false });
            }
            this.emitter.emit('update', { deep: this, name: _name, query: _exp, value, options, remoteQuery: mutate, remote, local, toUpdate, error: q.error });
            return Object.assign(Object.assign({}, q), { data: (_k = (_j = (_h = (q)) === null || _h === void 0 ? void 0 : _h.data) === null || _j === void 0 ? void 0 : _j.m0) === null || _k === void 0 ? void 0 : _k.returning });
        });
    }
    ;
    delete(exp, options) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
        return __awaiter(this, void 0, void 0, function* () {
            const _exp = typeof (exp) === 'number' ? { id: exp } : exp;
            if (!_exp)
                throw new Error('!exp');
            const o = Object.assign({ local: this.local, remote: this.remote }, options);
            const _name = random();
            this.handleOperation && this.handleOperation('delete', _exp, null, o);
            const { local, remote } = o;
            this.emitter.emit('delete.before', { deep: this, name: _name, query: _exp, options, remote, local });
            if (isEqual(_exp, {}))
                throw new Error('exp {} is wrong');
            const query = serializeQuery(_exp, (options === null || options === void 0 ? void 0 : options.table) === this.table || !(options === null || options === void 0 ? void 0 : options.table) ? 'links' : 'value', this.unvertualizeId);
            const table = (options === null || options === void 0 ? void 0 : options.table) || this.table;
            const returning = (options === null || options === void 0 ? void 0 : options.returning) || this.deleteReturning;
            const variables = options === null || options === void 0 ? void 0 : options.variables;
            const name = (options === null || options === void 0 ? void 0 : options.name) || this.defaultDeleteName;
            let q;
            const toDelete = [];
            const toUpdate = [];
            if (this.minilinks && local !== false) {
                convertDeepDeleteToMinilinksApply(this.minilinks, _exp, table, toDelete, toUpdate);
                this.minilinks.update(toUpdate);
                this.minilinks.remove(toDelete);
            }
            let mutate;
            if (remote !== false) {
                try {
                    mutate = generateSerial({
                        actions: [deleteMutation(table, Object.assign(Object.assign(Object.assign({}, variables), query), { returning }), { tableName: table, operation: 'delete', returning })],
                        name: name,
                    });
                    q = yield this.apolloClient.mutate(mutate);
                }
                catch (e) {
                    const sqlError = (_d = (_c = (_b = (_a = e === null || e === void 0 ? void 0 : e.graphQLErrors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.extensions) === null || _c === void 0 ? void 0 : _c.internal) === null || _d === void 0 ? void 0 : _d.error;
                    if (sqlError === null || sqlError === void 0 ? void 0 : sqlError.message)
                        e.message = sqlError.message;
                    if (!this._silent(options))
                        throw new Error(`DeepClient Delete Error: ${e.message}`, { cause: e });
                    this.emitter.emit('delete', { deep: this, name: _name, query: _exp, options, remoteQuery: mutate, error: e, remote, local, toUpdate });
                    return Object.assign(Object.assign({}, q), { data: (_g = (_f = (_e = (q)) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.m0) === null || _g === void 0 ? void 0 : _g.returning, error: e });
                }
            }
            else {
                this.emitter.emit('delete', { deep: this, name: _name, query: _exp, options, remoteQuery: mutate, remote, local, toDelete });
                return Object.assign(Object.assign({}, q), { data: toDelete.map(l => l.id), loading: false });
            }
            this.emitter.emit('delete', { deep: this, name: _name, query: _exp, options, remoteQuery: mutate, remote, local, toDelete, error: q.error });
            return Object.assign(Object.assign({}, q), { data: (_k = (_j = (_h = (q)) === null || _h === void 0 ? void 0 : _h.data) === null || _j === void 0 ? void 0 : _j.m0) === null || _k === void 0 ? void 0 : _k.returning });
        });
    }
    ;
    serial(options) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j;
        return __awaiter(this, void 0, void 0, function* () {
            const { name, operations, returning, silent } = options;
            let operationsGroupedByTypeAndTable = {};
            operationsGroupedByTypeAndTable = operations.reduce((acc, operation) => {
                if (!acc[operation.type]) {
                    acc[operation.type] = {};
                }
                if (!acc[operation.type][operation.table]) {
                    acc[operation.type][operation.table] = [];
                }
                acc[operation.type][operation.table].push(operation);
                return acc;
            }, operationsGroupedByTypeAndTable);
            let serialActions = [];
            Object.keys(operationsGroupedByTypeAndTable).map((operationType) => {
                const operationsGroupedByTable = operationsGroupedByTypeAndTable[operationType];
                Object.keys(operationsGroupedByTable).map((table) => {
                    const operations = operationsGroupedByTable[table];
                    if (operationType === 'insert') {
                        const insertOperations = operations;
                        const serialAction = insertMutation(table, { objects: insertOperations.map(operation => Array.isArray(operation.objects) ? operation.objects : [operation.objects]).flat() }, { tableName: table, operation: operationType, returning });
                        serialActions.push(serialAction);
                    }
                    else if (operationType === 'update') {
                        const updateOperations = operations;
                        const newSerialActions = updateOperations.map(operation => {
                            const exp = operation.exp;
                            const value = operation.value;
                            const query = serializeQuery(exp, table === this.table || !table ? 'links' : 'value', this.unvertualizeId);
                            return updateMutation(table, Object.assign(Object.assign({}, query), { _set: value }), { tableName: table, operation: operationType, returning });
                        });
                        serialActions = [...serialActions, ...newSerialActions];
                    }
                    else if (operationType === 'delete') {
                        const deleteOperations = operations;
                        ;
                        const newSerialActions = deleteOperations.map(operation => {
                            const exp = operation.exp;
                            const query = serializeQuery(exp, table === this.table || !table ? 'links' : 'value', this.unvertualizeId);
                            return deleteMutation(table, Object.assign({}, query), { tableName: table, operation: operationType, returning });
                        });
                        serialActions = [...serialActions, ...newSerialActions];
                    }
                });
            });
            let result;
            try {
                result = yield this.apolloClient.mutate(generateSerial({
                    actions: serialActions,
                    name: name !== null && name !== void 0 ? name : 'Name',
                }));
            }
            catch (e) {
                const sqlError = (_d = (_c = (_b = (_a = e === null || e === void 0 ? void 0 : e.graphQLErrors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.extensions) === null || _c === void 0 ? void 0 : _c.internal) === null || _d === void 0 ? void 0 : _d.error;
                if (sqlError === null || sqlError === void 0 ? void 0 : sqlError.message)
                    e.message = sqlError.message;
                if (!silent)
                    throw new Error(`DeepClient Serial Error: ${e.message}`, { cause: e });
                return Object.assign(Object.assign({}, result), { data: (_g = (_f = (_e = (result)) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.m0) === null || _g === void 0 ? void 0 : _g.returning, error: e });
            }
            return Object.assign(Object.assign({}, result), { data: ((_h = (result)) === null || _h === void 0 ? void 0 : _h.data) && Object.values((_j = (result)) === null || _j === void 0 ? void 0 : _j.data).flatMap(m => m.returning) });
        });
    }
    ;
    reserve(count) {
        return reserve({ count, client: this.apolloClient });
    }
    ;
    await(id, options = { results: false }) {
        return __awaiter(this, void 0, void 0, function* () {
            return awaitPromise({
                id, client: this.apolloClient,
                Then: yield this.id('@deep-foundation/core', 'Then'),
                Promise: yield this.id('@deep-foundation/core', 'Promise'),
                Resolved: yield this.id('@deep-foundation/core', 'Resolved'),
                Rejected: yield this.id('@deep-foundation/core', 'Rejected'),
                Results: options.results
            });
        });
    }
    ;
    value(id, value) {
        var _a, _b, _c, _d;
        return __awaiter(this, arguments, void 0, function* () {
            if (value === undefined) {
                if (arguments.length === 2)
                    throw new Error(`Trying to set undefined as value for ${id} .`);
                return {
                    id: id,
                    data: (_d = (_c = (_b = (_a = (yield this.select(id))) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.value) === null || _d === void 0 ? void 0 : _d.value,
                };
            }
            else {
                const { data: [link] } = yield this.select(id);
                if (!link)
                    throw new Error(`The link ##${id} not founded`);
                const { data: [Value] } = yield this.select({
                    type_id: this.idLocal('@deep-foundation/core', 'Value'),
                    from_id: link.type_id,
                });
                if (!Value)
                    throw new Error(`The link ##${id} cannot have a value, its type is not bound |-Value-> String|Number|Object|File`);
                const type = Value.to_id === this.idLocal('@deep-foundation/core', 'String') ? 'string' : Value.to_id === this.idLocal('@deep-foundation/core', 'Number') ? 'number' : Value.to_id === this.idLocal('@deep-foundation/core', 'Object') ? 'object' : undefined;
                if (!type)
                    throw new Error(`Type of ##${id}- - > ##${Value.from_id} |-Value-> ##${Value.id} is not compatable with .value()`);
                const table = type + 's';
                let result;
                if ((Value === null || Value === void 0 ? void 0 : Value.to_id) === this.idLocal('@deep-foundation/core', 'File')) {
                    if (typeof (value) !== 'string' && !(value instanceof Blob))
                        throw new Error('File must be a string or Blob');
                    yield upload(id, value, this);
                }
                else {
                    if (link === null || link === void 0 ? void 0 : link.value) {
                        result = yield this.update({ link_id: id }, { value }, { table });
                    }
                    else {
                        result = yield this.insert({ link_id: id, value }, { table });
                    }
                }
                return Object.assign(Object.assign({}, result), { link,
                    Value, data: value });
            }
        });
    }
    id(start, ...path) {
        var _a, _b, _c, _d, _e, _f, _g;
        return __awaiter(this, void 0, void 0, function* () {
            if (typeof (start) === 'object') {
                return (_c = (_b = (_a = ((yield this.select(start)))) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.id;
            }
            if ((_d = _ids === null || _ids === void 0 ? void 0 : _ids[start]) === null || _d === void 0 ? void 0 : _d[path[0]]) {
                return _ids[start][path[0]];
            }
            const q = yield this.select(pathToWhere(start, ...path));
            if (q.error) {
                throw q.error;
            }
            const result = (((_f = (_e = q === null || q === void 0 ? void 0 : q.data) === null || _e === void 0 ? void 0 : _e[0]) === null || _f === void 0 ? void 0 : _f.id) | ((_g = _ids === null || _ids === void 0 ? void 0 : _ids[start]) === null || _g === void 0 ? void 0 : _g[path === null || path === void 0 ? void 0 : path[0]]) | 0);
            if (!result && path[path.length - 1] !== true) {
                throw new Error(`Id not found by [${JSON.stringify([start, ...path])}]`);
            }
            return result;
        });
    }
    ;
    ids(...paths) {
        return __awaiter(this, void 0, void 0, function* () {
            const appendPath = (accumulator, keys, value) => {
                const lastKey = keys.pop();
                const lastObject = keys.reduce((obj, key) => obj[key] = obj[key] || {}, accumulator);
                lastObject[lastKey] = value;
            };
            const result = {};
            yield Promise.all(paths.map(([start, ...path]) => __awaiter(this, void 0, void 0, function* () {
                const id = yield this.id(start, ...path);
                appendPath(result, [start, ...path], id);
            })));
            return result;
        });
    }
    idLocal(start, ...path) {
        return this.minilinks.id(start, ...path);
    }
    ;
    guest(options = {}) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const relogin = typeof (options.relogin) === 'boolean' ? options.relogin : true;
            const result = yield this.apolloClient.query({ query: GUEST });
            const { linkId, token, error } = ((_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.guest) || {};
            if (!error && !!token && relogin) {
                if (this === null || this === void 0 ? void 0 : this.handleAuth)
                    setTimeout(() => this === null || this === void 0 ? void 0 : this.handleAuth(+linkId, token), 0);
            }
            return { linkId, token, error: !error && (!linkId || !token) ? 'unexepted' : error };
        });
    }
    ;
    jwt(options) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const relogin = typeof (options.relogin) === 'boolean' ? options.relogin : false;
            if (options === null || options === void 0 ? void 0 : options.token) {
                try {
                    const token = options === null || options === void 0 ? void 0 : options.token;
                    const decoded = parseJwt(token);
                    const linkId = decoded === null || decoded === void 0 ? void 0 : decoded.userId;
                    if (!!token && relogin) {
                        if (this === null || this === void 0 ? void 0 : this.handleAuth)
                            setTimeout(() => this === null || this === void 0 ? void 0 : this.handleAuth(+linkId, token), 0);
                    }
                    return { linkId, token, error: (!linkId || !token) ? 'unexepted' : undefined };
                }
                catch (e) {
                    return { error: e };
                }
            }
            else if (options === null || options === void 0 ? void 0 : options.linkId) {
                const result = yield this.apolloClient.query({ query: JWT, variables: { linkId: +options.linkId } });
                const { linkId, token, error } = ((_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.jwt) || {};
                if (!error && !!token && relogin) {
                    if (this === null || this === void 0 ? void 0 : this.handleAuth)
                        setTimeout(() => this === null || this === void 0 ? void 0 : this.handleAuth(+linkId, token), 0);
                }
                return { linkId, token, error: error ? error : (!linkId) ? 'unexepted' : undefined };
            }
            else
                return { error: `linkId or token must be provided` };
        });
    }
    ;
    whoami() {
        var _a, _b, _c, _d;
        return __awaiter(this, void 0, void 0, function* () {
            const result = yield this.apolloClient.query({ query: WHOISME });
            this.linkId = (_b = (_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.jwt) === null || _b === void 0 ? void 0 : _b.linkId;
            return (_d = (_c = result === null || result === void 0 ? void 0 : result.data) === null || _c === void 0 ? void 0 : _c.jwt) === null || _d === void 0 ? void 0 : _d.linkId;
        });
    }
    login(options) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.jwt(Object.assign(Object.assign({}, options), { relogin: true }));
        });
    }
    ;
    logout() {
        return __awaiter(this, void 0, void 0, function* () {
            if (this === null || this === void 0 ? void 0 : this.handleAuth)
                setTimeout(() => this === null || this === void 0 ? void 0 : this.handleAuth(0, ''), 0);
            return { linkId: 0, token: '' };
        });
    }
    ;
    can(objectIds, subjectIds, actionIds, userIds = this.linkId) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            const where = {};
            if (objectIds)
                where.object_id = typeof (objectIds) === 'number' ? { _eq: +objectIds } : { _in: objectIds };
            if (subjectIds)
                where.subject_id = typeof (subjectIds) === 'number' ? { _eq: +subjectIds } : { _in: subjectIds };
            if (actionIds)
                where.action_id = typeof (actionIds) === 'number' ? { _eq: +actionIds } : { _in: actionIds };
            const result = yield this.select(where, { table: 'can', returning: 'rule_id' });
            return !!((_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.length);
        });
    }
    symbol(input) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
        return __awaiter(this, void 0, void 0, function* () {
            const id = typeof (input) === 'number' || typeof (input) === 'string' ? input : input.id;
            return ((_e = (_d = (_c = (_b = (_a = this.minilinks.byId[id]) === null || _a === void 0 ? void 0 : _a.inByType) === null || _b === void 0 ? void 0 : _b[_ids['@deep-foundation/core']['Symbol']]) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.value) === null || _e === void 0 ? void 0 : _e.value) || ((_l = (_k = (_j = (_h = (_g = (_f = this.minilinks.byId[id]) === null || _f === void 0 ? void 0 : _f.type) === null || _g === void 0 ? void 0 : _g.inByType) === null || _h === void 0 ? void 0 : _h[_ids['@deep-foundation/core']['Symbol']]) === null || _j === void 0 ? void 0 : _j[0]) === null || _k === void 0 ? void 0 : _k.value) === null || _l === void 0 ? void 0 : _l.value) || '📍';
        });
    }
    ;
    symbolLocal(input) {
        return this.minilinks.symbol(input);
    }
    name(input) {
        var _a, _b, _c;
        return __awaiter(this, void 0, void 0, function* () {
            const id = typeof (input) === 'number' || typeof (input) === 'string' ? input : input.id;
            const { data: [containLink] } = yield this.select({
                type_id: { _id: ['@deep-foundation/core', 'Contain'] },
                to_id: id,
            });
            if (!((_a = containLink === null || containLink === void 0 ? void 0 : containLink.value) === null || _a === void 0 ? void 0 : _a.value)) {
                const { data: [packageLink] } = yield this.select(id);
                if ((packageLink === null || packageLink === void 0 ? void 0 : packageLink.type_id) === this.idLocal('@deep-foundation/core', 'Package'))
                    return (_b = packageLink === null || packageLink === void 0 ? void 0 : packageLink.value) === null || _b === void 0 ? void 0 : _b.value;
            }
            return (_c = containLink === null || containLink === void 0 ? void 0 : containLink.value) === null || _c === void 0 ? void 0 : _c.value;
        });
    }
    ;
    nameLocal(input) {
        return this.minilinks.name(input);
    }
    import(path) {
        return __awaiter(this, void 0, void 0, function* () {
            if (typeof DeepClient.resolveDependency !== 'undefined') {
                try {
                    return yield DeepClient.resolveDependency(path);
                }
                catch (e) {
                    console.log(`IGNORED ERROR (ignore if you don't see other errors): Call to DeepClient.resolveDependency is failed with`, e);
                }
            }
            if (typeof require !== 'undefined') {
                try {
                    return yield require(path);
                }
                catch (e) {
                    console.log(`IGNORED ERROR (ignore if you don't see other errors): Call to require is failed with`, e);
                }
            }
            return yield import(path);
        });
    }
    Traveler(links) {
        return new NativeTraveler(this, links);
    }
    ;
    Packager() {
        return new Packager(this);
    }
    _findHandler({ handlerId, context = [], execution_provider_id, isolation_provider_id, }) {
        return __awaiter(this, void 0, void 0, function* () {
            if (handlerId) {
                const { data: handlers } = yield this.select(Object.assign(Object.assign({ execution_provider_id: { _eq: execution_provider_id || this.idLocal('@deep-foundation/core', 'JSExecutionProvider'), } }, (isolation_provider_id ? { isolation_provider_id: { _eq: isolation_provider_id, } } : {})), { _or: [
                        { handler_id: { _eq: handlerId } },
                        { dist_id: { _eq: handlerId } },
                        { src_id: { _eq: handlerId } },
                    ] }), { table: 'handlers', returning: 'handler_id dist_id src_id' });
                if (handlers === null || handlers === void 0 ? void 0 : handlers[0])
                    return handlers === null || handlers === void 0 ? void 0 : handlers[0];
            }
            else {
                const { data: handlers } = yield this.select(Object.assign(Object.assign({ execution_provider_id: { _eq: this.idLocal('@deep-foundation/core', 'JSExecutionProvider'), } }, (isolation_provider_id ? { isolation_provider_id: { _eq: isolation_provider_id, } } : {})), { handler: {
                        in: {
                            type_id: { _eq: yield this.id('@deep-foundation/deepcase', 'Context') },
                            from_id: { _in: context }
                        },
                    } }), { table: 'handlers', returning: 'handler_id dist_id src_id' });
                if (handlers === null || handlers === void 0 ? void 0 : handlers[0])
                    return handlers === null || handlers === void 0 ? void 0 : handlers[0];
            }
        });
    }
    _queryTypesNamesSymbols() {
        var _a, _b;
        const Contain = (_a = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _a === void 0 ? void 0 : _a.Contain;
        const Symbol = (_b = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _b === void 0 ? void 0 : _b.Symbol;
        return {
            return: {
                _contain: { relation: 'in', type_id: Contain },
                _symbol: { relation: 'in', type_id: Symbol },
                _type: {
                    relation: 'type',
                    return: {
                        _contain: { relation: 'in', type_id: Contain },
                        _symbol: { relation: 'in', type_id: Symbol },
                    },
                },
            },
        };
    }
    eval({ linkId, handlerId, value, context = [], input, }) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            let code = value;
            let handler;
            if (handlerId || (!value && linkId)) {
                handler = yield this._findHandler({ handlerId, context });
                const { data: [file] } = yield this.select(handler === null || handler === void 0 ? void 0 : handler.dist_id);
                code = (_a = file === null || file === void 0 ? void 0 : file.value) === null || _a === void 0 ? void 0 : _a.value;
            }
            return evalClientHandler({
                value: code, input, deep: this,
            });
        });
    }
    isId(id) {
        return typeof (id) === 'string' || (typeof (id) === 'number' && !isNaN(id));
    }
    isLink(link) {
        return typeof (link) === 'object' && link instanceof MinilinksLink;
    }
    get(...id) {
        const [founded] = this.minilinks.select({ id: { _id: id } });
        return founded;
    }
    getRemote(...id) {
        var _a, _b;
        return __awaiter(this, void 0, void 0, function* () {
            return (_b = (_a = (yield this.select({ id: { _id: id } }))) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b[0];
        });
    }
    search(value, options = {}) {
        var _a, _b, _c;
        return __awaiter(this, void 0, void 0, function* () {
            const o = Object.assign({ remote: true, count: false, sort: true }, options);
            const query = this.searchQuery(value, o);
            let results;
            if (o.remote) {
                results = yield this.select(query, Object.assign({ apply: o.apply }, (o.count ? { aggregate: 'count' } : {})));
            }
            else {
                results = { data: this.minilinks.select(query, Object.assign({}, (o.count ? { aggregate: 'count' } : {}))), query };
            }
            if (o.sort) {
                const sorted = sort(results.data, value);
                results.data = sorted.data;
                results.ids = sorted.ids;
            }
            else {
                const ids = results.ids = {};
                if ((_a = results === null || results === void 0 ? void 0 : results.data) === null || _a === void 0 ? void 0 : _a.length)
                    for (let i in results.data)
                        ids[(_c = (_b = results === null || results === void 0 ? void 0 : results.data) === null || _b === void 0 ? void 0 : _b[i]) === null || _c === void 0 ? void 0 : _c.id];
            }
            return results;
        });
    }
    searchQuery(value, options) {
        const o = Object.assign({ values: false, contains: false, regexp: false }, options);
        const num = parseFloat(value);
        const _q = o.query || {};
        const q = Object.assign(Object.assign({}, _q), { _or: [...((_q === null || _q === void 0 ? void 0 : _q._or) || [])] });
        const _or = q._or;
        if (!Number.isNaN(num)) {
            _or.push({ id: num });
            if (o.values)
                _or.push({ number: { value: num } });
        }
        if (o.values || o.contains) {
            if (o.regexp)
                _or.push({ string: { value: { _iregex: value } } });
            else
                _or.push({ string: { value: { _ilike: `%${value}%` } } });
        }
        ;
        _or.push({ in: { type_id: this.idLocal('@deep-foundation/core', 'Contain'), string: { value: o.regexp ? { _iregex: value } : { _ilike: `%${value}%` } } } });
        _or.push({ type_id: this.idLocal('@deep-foundation/core', 'Package'), string: { value: o.regexp ? { _iregex: value } : { _ilike: `%${value}%` } } });
        if (!o.contains)
            q._not = { type_id: this.idLocal('@deep-foundation/core', 'Contain') };
        else if (!o.values)
            q.type_id = this.idLocal('@deep-foundation/core', 'Contain');
        return q;
    }
}
export const JWT = gql `query JWT($linkId: Int) {
  jwt(input: {linkId: $linkId}) {
    linkId
    token
  }
}`;
export const WHOISME = gql `query WHOISME {
  jwt(input: {}) {
    linkId
  }
}`;
export const GUEST = gql `query GUEST {
  guest {
    linkId
    token
  }
}`;
export function useAuthNode() {
    return useLocalStore('use_auth_link_id', 0);
}
export const DeepNamespaceContext = createContext({
    all: () => ({}),
    select: (namespace) => { },
    insert: (namespace, deep) => { },
    delete: (namespace) => { },
});
export const DeepNamespacesContext = createContext({});
export function useDeepNamespaces() {
    return useContext(DeepNamespacesContext);
}
;
export function useDeepNamespace(namespace, deep) {
    const namespaces = useContext(DeepNamespaceContext);
    const nameRef = useRef();
    useMemo(() => {
        if ((!!nameRef.current && nameRef.current != namespace)) {
            namespaces.delete(nameRef.current);
        }
        if (namespace) {
            if (namespaces.select(namespace) !== deep) {
                namespaces.delete(namespace);
            }
            namespaces.insert(namespace, deep);
        }
    }, [namespace, deep]);
    useEffect(() => {
        return () => nameRef.current && namespaces.delete(namespace);
    }, []);
}
export function DeepNamespaceProvider({ children }) {
    const [namespaces, setNamespaces] = useState({});
    const ref = useRef(namespaces);
    ref.current = namespaces;
    const api = useMemo(() => {
        return {
            all: () => ref.current,
            select: (namespace) => ref.current[namespace],
            insert: (namespace, deep) => {
                console.log('DeepNamespaceProvider', 'insert', namespace, deep);
                setNamespaces(namespaces => (Object.assign(Object.assign({}, namespaces), { [namespace]: deep })));
            },
            delete: (namespace) => {
                console.log('DeepNamespaceProvider', 'delete', namespace);
                setNamespaces(namespaces => (Object.assign(Object.assign({}, namespaces), { [namespace]: undefined })));
            },
        };
    }, []);
    useEffect(() => {
        console.log('DeepNamespaceProvider', 'mounted', api);
        if (typeof (window) == 'object')
            window.dn = api;
    }, []);
    return React.createElement(DeepNamespaceContext.Provider, { value: api },
        React.createElement(DeepNamespacesContext.Provider, { value: namespaces }, children));
}
export const DeepContext = createContext(undefined);
export function useDeepGenerator(generatorOptions) {
    const { apolloClient: apolloClientProps, minilinks } = generatorOptions, otherGeneratorOptions = __rest(generatorOptions, ["apolloClient", "minilinks"]);
    const log = debug.extend(useDeepGenerator.name);
    const apolloClientHook = useApolloClient();
    log({ apolloClientHook });
    const apolloClient = apolloClientProps || apolloClientHook;
    log({ apolloClient });
    const [token, setToken] = useTokenController();
    log({ token, setToken });
    const deep = useMemo(() => {
        if (!(apolloClient === null || apolloClient === void 0 ? void 0 : apolloClient.jwt_token)) {
            log({ token, apolloClient });
        }
        try {
            return new DeepClient(Object.assign(Object.assign({}, otherGeneratorOptions), { apolloClient, token,
                minilinks, handleAuth: (linkId, token) => {
                    setToken(token);
                } }));
        }
        catch (error) {
            console.error(error);
            return undefined;
        }
    }, [apolloClient]);
    log({ deep });
    return deep;
}
export function DeepProvider({ apolloClient: apolloClientProps, minilinks: inputMinilinks, namespace, children, }) {
    const providedMinilinks = useMinilinks();
    const deep = useDeepGenerator({
        apolloClient: apolloClientProps,
        minilinks: inputMinilinks || providedMinilinks,
        namespace,
    });
    useDeepNamespace(namespace, deep);
    return React.createElement(DeepContext.Provider, { value: deep }, children);
}
export function useDeep() {
    return useContext(DeepContext);
}
export function useDeepQuery(query, options) {
    var _a, _b, _c, _d, _e;
    const miniName = useMemo(() => (options === null || options === void 0 ? void 0 : options.mini) || random(), []);
    debug('useDeepQuery', miniName, query, options);
    const _deep = useDeep();
    const deep = (options === null || options === void 0 ? void 0 : options.deep) || _deep;
    useMemo(() => {
        deep.emitter.emit('useQuery.mount', { name: miniName, deep, query, options });
    }, []);
    const prevRef = useRef({ query, options });
    const { query: q, options: o } = useMemo(() => {
        const obj = { query, options: Object.assign({ table: 'links' }, options) };
        if (isEqual(obj, prevRef.current))
            return prevRef.current = obj;
        else
            return obj;
    }, [query, options]);
    const wq = useMemo(() => {
        return deep._generateQuery(q, Object.assign({}, o));
    }, [q, o]);
    const result = useQuery((_a = wq === null || wq === void 0 ? void 0 : wq.query) === null || _a === void 0 ? void 0 : _a.query, Object.assign({ variables: (_b = wq === null || wq === void 0 ? void 0 : wq.query) === null || _b === void 0 ? void 0 : _b.variables, client: deep.apolloClient }, o));
    const [generatedResult, setGeneratedResult] = useState([]);
    useEffect(() => {
        var _a, _b, _c, _d;
        if (o === null || o === void 0 ? void 0 : o.aggregate)
            setGeneratedResult((_d = (_c = (_b = (_a = (result)) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.q0) === null || _c === void 0 ? void 0 : _c.aggregate) === null || _d === void 0 ? void 0 : _d[o.aggregate]);
        else {
            (() => __awaiter(this, void 0, void 0, function* () {
                var _e;
                setGeneratedResult(yield deep._generateResult(q, o, (_e = result === null || result === void 0 ? void 0 : result.data) === null || _e === void 0 ? void 0 : _e.q0));
            }))();
        }
    }, [result]);
    const toReturn = Object.assign(Object.assign({}, result), { originalData: (o === null || o === void 0 ? void 0 : o.aggregate) ? (_d = (_c = result === null || result === void 0 ? void 0 : result.data) === null || _c === void 0 ? void 0 : _c.q0) === null || _d === void 0 ? void 0 : _d.aggregate : (_e = result === null || result === void 0 ? void 0 : result.data) === null || _e === void 0 ? void 0 : _e.q0, data: generatedResult, options: o, deep, links: [], plainLinks: [], return: q === null || q === void 0 ? void 0 : q.return, name: miniName });
    const { data: minilinksResults, plainLinks } = useMinilinksApply(deep.minilinks, miniName, toReturn);
    toReturn.data = (o === null || o === void 0 ? void 0 : o.aggregate) || (o === null || o === void 0 ? void 0 : o.table) !== 'links' ? toReturn.data || [] : minilinksResults;
    toReturn.links = minilinksResults;
    toReturn.plainLinks = plainLinks;
    useMemo(() => {
        var _a;
        deep.emitter.emit('useQuery', {
            name: miniName, deep, query, options: o,
            remoteQuery: wq,
            loading: result.loading,
            remoteData: (_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.q0,
            localData: minilinksResults,
            error: result.error,
            plainLinks,
        });
        return () => {
            var _a;
            deep.emitter.emit('useQuery.unmount', {
                name: miniName, deep, query, options: o,
                remoteQuery: wq,
                loading: result.loading,
                remoteData: (_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.q0,
                localData: minilinksResults,
                error: result.error,
                plainLinks,
            });
        };
    }, [toReturn]);
    return toReturn;
}
export function useDeepSubscription(query, options) {
    var _a, _b, _c, _d, _e;
    const miniName = useMemo(() => (options === null || options === void 0 ? void 0 : options.mini) || random(), []);
    debug('useDeepSubscription', miniName, query, options);
    const _deep = useDeep();
    const deep = (options === null || options === void 0 ? void 0 : options.deep) || _deep;
    useMemo(() => {
        deep.emitter.emit('useSubscription.mount', { name: miniName, deep, query, options });
    }, []);
    const prevRef = useRef({ query, options });
    const { query: q, options: o } = useMemo(() => {
        const obj = { query, options: Object.assign({ table: 'links' }, options) };
        if (isEqual(obj, prevRef.current))
            return prevRef.current = obj;
        else
            return obj;
    }, [query, options]);
    const wq = useMemo(() => {
        return deep._generateQuery(q, Object.assign(Object.assign({}, o), { subscription: true }));
    }, [q, o]);
    const result = useSubscription((_a = wq === null || wq === void 0 ? void 0 : wq.query) === null || _a === void 0 ? void 0 : _a.query, Object.assign({ variables: (_b = wq === null || wq === void 0 ? void 0 : wq.query) === null || _b === void 0 ? void 0 : _b.variables, client: deep.apolloClient }, o));
    const [generatedResult, setGeneratedResult] = useState([]);
    useEffect(() => {
        var _a, _b, _c, _d;
        if (o === null || o === void 0 ? void 0 : o.aggregate)
            setGeneratedResult((_d = (_c = (_b = (_a = (result)) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.q0) === null || _c === void 0 ? void 0 : _c.aggregate) === null || _d === void 0 ? void 0 : _d[o.aggregate]);
        else {
            if (!result.loading) {
                (() => __awaiter(this, void 0, void 0, function* () {
                    var _e;
                    setGeneratedResult(yield deep._generateResult(q, o, (_e = result === null || result === void 0 ? void 0 : result.data) === null || _e === void 0 ? void 0 : _e.q0));
                }))();
            }
        }
    }, [result]);
    const toReturn = Object.assign(Object.assign({}, result), { originalData: (o === null || o === void 0 ? void 0 : o.aggregate) ? (_d = (_c = result === null || result === void 0 ? void 0 : result.data) === null || _c === void 0 ? void 0 : _c.q0) === null || _d === void 0 ? void 0 : _d.aggregate : (_e = result === null || result === void 0 ? void 0 : result.data) === null || _e === void 0 ? void 0 : _e.q0, data: generatedResult, options: o, deep, links: [], plainLinks: [], return: q === null || q === void 0 ? void 0 : q.return, name: miniName, query: query });
    const { data: minilinksResults, plainLinks } = useMinilinksApply(deep.minilinks, miniName, toReturn);
    toReturn.data = (o === null || o === void 0 ? void 0 : o.aggregate) || (o === null || o === void 0 ? void 0 : o.table) !== 'links' ? toReturn.data || [] : minilinksResults;
    toReturn.links = minilinksResults;
    toReturn.plainLinks = plainLinks;
    useMemo(() => {
        var _a;
        deep.emitter.emit('useSubscription', {
            name: miniName, deep, query, options: o,
            remoteQuery: wq,
            loading: result.loading,
            remoteData: (_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.q0,
            localData: minilinksResults,
            error: result.error,
            plainLinks,
        });
        return () => {
            var _a;
            deep.emitter.emit('useSubscription.unmount', {
                name: miniName, deep, query, options: o,
                remoteQuery: wq,
                loading: result.loading,
                remoteData: (_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.q0,
                localData: minilinksResults,
                error: result.error,
                plainLinks,
            });
        };
    }, [toReturn]);
    return toReturn;
}
export function sort(links, value) {
    const ids = {};
    return {
        data: matchSorter((links || []).map(l => {
            var _a;
            ids[l.id] = l;
            return { id: l.id, name: l.name, value: (_a = l === null || l === void 0 ? void 0 : l.value) === null || _a === void 0 ? void 0 : _a.value };
        }), value, { keys: ['id', 'name', 'value'] }).map((l) => ids[l.id]),
        ids,
    };
}
export function useSearch(value, options = {}) {
    const deep = useDeep();
    const o = useMemo(() => (Object.assign({ skip: false, remote: true, count: false, sort: true }, options)), [options]);
    const query = useMemo(() => this.searchQuery(value, o), [value, o]);
    const qo = useMemo(() => (Object.assign(Object.assign({}, (o.count ? { aggregate: 'count' } : {})), { skip: o.skip })), [o.count, o.skip]);
    const useHook = useMemo(() => o.remote ? o.subscription ? deep.useSubscription : deep.useQuery : deep.useLocalQuery, [o.remote, o.subscription]);
    const _results = useHook(query, qo);
    const results = o.remote ? _results : { data: _results };
    if (o.sort) {
        const sorted = useMemo(() => sort(results.data, value), [results.data]);
        results.data = sorted.data;
        results.ids = sorted.ids;
    }
    else {
        results.ids = useMemo(() => {
            var _a, _b, _c, _d;
            const ids = {};
            if ((_a = results === null || results === void 0 ? void 0 : results.data) === null || _a === void 0 ? void 0 : _a.length)
                for (let i = 0; i < results.data.length; i++)
                    ids[(_c = (_b = results === null || results === void 0 ? void 0 : results.data) === null || _b === void 0 ? void 0 : _b[i]) === null || _c === void 0 ? void 0 : _c.id] = (_d = results === null || results === void 0 ? void 0 : results.data) === null || _d === void 0 ? void 0 : _d[i];
            return ids;
        }, [results.data]);
    }
    results.query = query;
    results.options = qo;
    return results;
}
export function useLink(link) {
    const deep = useDeep();
    const id = typeof (link) === 'object' ? link === null || link === void 0 ? void 0 : link.id : link;
    const [actual, setActual] = useState(deep.get(id));
    useEffect(() => {
        if (!id)
            return;
        const onLink = (o, n) => setActual(n || o);
        deep.minilinks.emitter.on(`link.${id}`, onLink);
        return () => {
            deep.minilinks.emitter.removeListener('removed', onLink);
        };
    }, []);
    return actual;
}
export function useLinks(...links) {
    const deep = useDeep();
    const ids = useMemo(() => {
        return links.map(l => typeof (l) === 'object' ? l === null || l === void 0 ? void 0 : l.id : l);
    }, [links]);
    const [updated, setUpdated] = useState(0);
    useEffect(() => {
        const onLink = (o, n) => setUpdated(u => u + 1);
        for (let i in ids)
            deep.minilinks.emitter.on(`link.${ids[i]}`, onLink);
        return () => {
            for (let i in ids)
                deep.minilinks.emitter.removeListener(`link.${ids[i]}`, onLink);
        };
    }, []);
    return useMemo(() => {
        return ids.map(id => id ? deep.get(id) : undefined);
    }, [updated]);
}
export function useDeepId(start, ...path) {
    return useDeep().useDeepId(start, ...path);
}
export function _useDeepId(deep, start, ...path) {
    var _a, _b;
    const result = deep.useDeepQuery({ id: { _id: [start, ...path] } });
    return { data: (_b = (_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.id, loading: result === null || result === void 0 ? void 0 : result.loading, error: result === null || result === void 0 ? void 0 : result.error };
}
//# sourceMappingURL=client.js.map