"use strict";
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 __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.withDependencyTracking = withDependencyTracking;
exports.serializeDependencySnapshot = serializeDependencySnapshot;
exports.deserializeDependencySnapshot = deserializeDependencySnapshot;
exports.getPublicationContext = getPublicationContext;
exports.recordDependencyResult = recordDependencyResult;
exports.recordAggregateDependencies = recordAggregateDependencies;
var async_hooks_1 = require("async_hooks");
var common_1 = require("./common");
var dependencyStorage = new async_hooks_1.AsyncLocalStorage();
function withDependencyTracking(fn, metadata) {
    return __awaiter(this, void 0, void 0, function () {
        var context, result;
        var _this = this;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    context = {
                        dependencies: new Map(),
                        filters: new Map(),
                        watchAllCollections: new Set(),
                        queryMetadata: new Map()
                    };
                    if (metadata === null || metadata === void 0 ? void 0 : metadata.publication) {
                        context.publication = metadata.publication;
                    }
                    if (Array.isArray(metadata === null || metadata === void 0 ? void 0 : metadata.subscriptionData)) {
                        context.subscriptionData = metadata.subscriptionData.slice();
                    }
                    if (metadata === null || metadata === void 0 ? void 0 : metadata.userId) {
                        context.userId = metadata.userId;
                    }
                    return [4 /*yield*/, dependencyStorage.run(context, function () { return __awaiter(_this, void 0, void 0, function () {
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0: return [4 /*yield*/, fn()];
                                    case 1:
                                        result = _a.sent();
                                        return [2 /*return*/];
                                }
                            });
                        }); })];
                case 1:
                    _a.sent();
                    return [2 /*return*/, {
                            result: result,
                            snapshot: {
                                dependencies: cloneDependencyMap(context.dependencies),
                                filters: cloneFilterMap(context.filters),
                                watchAllCollections: new Set(context.watchAllCollections),
                                queryMetadata: cloneQueryMetaMap(context.queryMetadata)
                            }
                        }];
            }
        });
    });
}
function serializeDependencySnapshot(snapshot) {
    return {
        dependencies: Array.from(snapshot.dependencies.entries()).map(function (_a) {
            var _b = __read(_a, 2), collection = _b[0], ids = _b[1];
            return [collection, Array.from(ids)];
        }),
        filters: Array.from(snapshot.filters.entries()).map(function (_a) {
            var _b = __read(_a, 2), collection = _b[0], filters = _b[1];
            return [collection, (0, common_1.deepCopy)(filters)];
        }),
        watchAllCollections: Array.from(snapshot.watchAllCollections),
        queryMetadata: Array.from(snapshot.queryMetadata.entries()).map(function (_a) {
            var _b = __read(_a, 2), collection = _b[0], meta = _b[1];
            return [collection, (0, common_1.deepCopy)(meta)];
        })
    };
}
function deserializeDependencySnapshot(snapshot) {
    return {
        dependencies: new Map(snapshot.dependencies.map(function (_a) {
            var _b = __read(_a, 2), collection = _b[0], ids = _b[1];
            return [collection, new Set(ids)];
        })),
        filters: new Map(snapshot.filters.map(function (_a) {
            var _b = __read(_a, 2), collection = _b[0], filters = _b[1];
            return [collection, (0, common_1.deepCopy)(filters)];
        })),
        watchAllCollections: new Set(snapshot.watchAllCollections),
        queryMetadata: new Map(snapshot.queryMetadata.map(function (_a) {
            var _b = __read(_a, 2), collection = _b[0], meta = _b[1];
            return [collection, (0, common_1.deepCopy)(meta)];
        }))
    };
}
function getPublicationContext() {
    var context = dependencyStorage.getStore();
    if (!context) {
        return {};
    }
    var snapshot = {};
    if (context.publication) {
        snapshot.publication = context.publication;
    }
    if (Array.isArray(context.subscriptionData)) {
        snapshot.subscriptionData = context.subscriptionData.slice();
    }
    if (context.userId) {
        snapshot.userId = context.userId;
    }
    return snapshot;
}
function recordDependencyResult(collection, result, options) {
    var e_1, _a;
    var context = dependencyStorage.getStore();
    if (!context) {
        return;
    }
    if (options === null || options === void 0 ? void 0 : options.filter) {
        var filterList = context.filters.get(collection);
        if (!filterList) {
            filterList = [];
            context.filters.set(collection, filterList);
        }
        filterList.push((0, common_1.deepCopy)(options.filter));
        context.watchAllCollections.delete(collection);
    }
    if (options === null || options === void 0 ? void 0 : options.meta) {
        var metaList = context.queryMetadata.get(collection);
        if (!metaList) {
            metaList = [];
            context.queryMetadata.set(collection, metaList);
        }
        metaList.push((0, common_1.deepCopy)(options.meta));
    }
    var ids = new Set();
    collectIdsFromResult(result, ids);
    if (!ids.size && (options === null || options === void 0 ? void 0 : options.filter)) {
        collectIdsFromFilter(options.filter, ids);
    }
    if (ids.size) {
        context.watchAllCollections.delete(collection);
        var existing = context.dependencies.get(collection);
        if (!existing) {
            existing = new Set();
            context.dependencies.set(collection, existing);
        }
        try {
            for (var ids_1 = __values(ids), ids_1_1 = ids_1.next(); !ids_1_1.done; ids_1_1 = ids_1.next()) {
                var id = ids_1_1.value;
                existing.add(id);
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (ids_1_1 && !ids_1_1.done && (_a = ids_1.return)) _a.call(ids_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
    }
    else if (!(options === null || options === void 0 ? void 0 : options.filter)) {
        context.watchAllCollections.add(collection);
    }
}
function recordAggregateDependencies(collection, pipeline, result) {
    var e_2, _a, e_3, _b, e_4, _c;
    var context = dependencyStorage.getStore();
    if (!context) {
        return;
    }
    var lookupTargets = extractLookupTargets(pipeline);
    var lookupAliases = new Set(lookupTargets.map(function (target) { return target.as; }).filter(Boolean));
    var matchFilter = sanitizeMatchFilter(extractMatchFilter(pipeline), lookupAliases);
    var meta = extractAggregateMeta(pipeline);
    recordDependencyResult(collection, result, {
        filter: matchFilter || undefined,
        meta: meta
    });
    if (!matchFilter) {
        context.watchAllCollections.add(collection);
    }
    if (shouldWatchAllForAggregatePipeline(pipeline)) {
        context.watchAllCollections.add(collection);
    }
    if (!lookupTargets.length) {
        return;
    }
    try {
        for (var lookupTargets_1 = __values(lookupTargets), lookupTargets_1_1 = lookupTargets_1.next(); !lookupTargets_1_1.done; lookupTargets_1_1 = lookupTargets_1.next()) {
            var target = lookupTargets_1_1.value;
            var nestedValues = [];
            collectLookupValues(result, target.as, nestedValues);
            var rootDocs = Array.isArray(result) ? result : [result];
            var pipelineFilters = [];
            var pipelineUnresolved = false;
            try {
                for (var rootDocs_1 = (e_3 = void 0, __values(rootDocs)), rootDocs_1_1 = rootDocs_1.next(); !rootDocs_1_1.done; rootDocs_1_1 = rootDocs_1.next()) {
                    var rootDoc = rootDocs_1_1.value;
                    var _d = buildLookupPipelineFilters(rootDoc, target), filtersFromPipeline = _d.filters, unresolved = _d.unresolved;
                    if (unresolved) {
                        pipelineUnresolved = true;
                    }
                    if (filtersFromPipeline === null || filtersFromPipeline === void 0 ? void 0 : filtersFromPipeline.length) {
                        pipelineFilters.push.apply(pipelineFilters, __spreadArray([], __read(filtersFromPipeline), false));
                    }
                }
            }
            catch (e_3_1) { e_3 = { error: e_3_1 }; }
            finally {
                try {
                    if (rootDocs_1_1 && !rootDocs_1_1.done && (_b = rootDocs_1.return)) _b.call(rootDocs_1);
                }
                finally { if (e_3) throw e_3.error; }
            }
            var filter = buildLookupFilter(result, target.localField, target.foreignField);
            var uniquePipelineFilters = dedupeFilters(pipelineFilters);
            if (nestedValues.length) {
                recordDependencyResult(target.from, nestedValues, filter ? { filter: filter } : undefined);
            }
            if (uniquePipelineFilters.length) {
                var pipelineMeta = extractAggregationMetaFromPipeline(target.pipeline);
                try {
                    for (var uniquePipelineFilters_1 = (e_4 = void 0, __values(uniquePipelineFilters)), uniquePipelineFilters_1_1 = uniquePipelineFilters_1.next(); !uniquePipelineFilters_1_1.done; uniquePipelineFilters_1_1 = uniquePipelineFilters_1.next()) {
                        var pipelineFilter = uniquePipelineFilters_1_1.value;
                        recordDependencyResult(target.from, [], { filter: pipelineFilter, meta: pipelineMeta });
                    }
                }
                catch (e_4_1) { e_4 = { error: e_4_1 }; }
                finally {
                    try {
                        if (uniquePipelineFilters_1_1 && !uniquePipelineFilters_1_1.done && (_c = uniquePipelineFilters_1.return)) _c.call(uniquePipelineFilters_1);
                    }
                    finally { if (e_4) throw e_4.error; }
                }
                continue;
            }
            if (!nestedValues.length && filter) {
                recordDependencyResult(target.from, [], { filter: filter });
                continue;
            }
            if (!nestedValues.length) {
                if (pipelineUnresolved) {
                    context.watchAllCollections.add(target.from);
                }
                else if (!target.pipeline || !target.pipeline.length) {
                    context.watchAllCollections.add(target.from);
                }
                else {
                    // Lookup pipeline exists, but we could not derive stable filters/ids.
                    context.watchAllCollections.add(target.from);
                }
            }
        }
    }
    catch (e_2_1) { e_2 = { error: e_2_1 }; }
    finally {
        try {
            if (lookupTargets_1_1 && !lookupTargets_1_1.done && (_a = lookupTargets_1.return)) _a.call(lookupTargets_1);
        }
        finally { if (e_2) throw e_2.error; }
    }
}
function shouldWatchAllForAggregatePipeline(pipeline) {
    if (!Array.isArray(pipeline)) {
        return false;
    }
    return pipeline.some(function (stage) { return isCardinalityChangingStage(stage); });
}
function isCardinalityChangingStage(stage) {
    if (!stage || typeof stage !== 'object') {
        return false;
    }
    if (stage.$group && typeof stage.$group === 'object') {
        return true;
    }
    if (stage.$count !== undefined) {
        return true;
    }
    if (stage.$facet && typeof stage.$facet === 'object') {
        return true;
    }
    if (stage.$bucket && typeof stage.$bucket === 'object') {
        return true;
    }
    if (stage.$bucketAuto && typeof stage.$bucketAuto === 'object') {
        return true;
    }
    if (stage.$sortByCount !== undefined) {
        return true;
    }
    return false;
}
function extractLookupTargets(pipeline) {
    var e_5, _a;
    var lookups = [];
    if (!Array.isArray(pipeline)) {
        return lookups;
    }
    try {
        for (var pipeline_1 = __values(pipeline), pipeline_1_1 = pipeline_1.next(); !pipeline_1_1.done; pipeline_1_1 = pipeline_1.next()) {
            var stage = pipeline_1_1.value;
            if (stage && stage.$lookup && typeof stage.$lookup === 'object') {
                var lookup = stage.$lookup;
                if (typeof lookup.from === 'string' && typeof lookup.as === 'string') {
                    lookups.push({
                        from: lookup.from,
                        as: lookup.as,
                        localField: typeof lookup.localField === 'string' ? lookup.localField : undefined,
                        foreignField: typeof lookup.foreignField === 'string' ? lookup.foreignField : undefined,
                        let: lookup.let && typeof lookup.let === 'object' ? lookup.let : undefined,
                        pipeline: Array.isArray(lookup.pipeline) ? lookup.pipeline : undefined
                    });
                }
            }
        }
    }
    catch (e_5_1) { e_5 = { error: e_5_1 }; }
    finally {
        try {
            if (pipeline_1_1 && !pipeline_1_1.done && (_a = pipeline_1.return)) _a.call(pipeline_1);
        }
        finally { if (e_5) throw e_5.error; }
    }
    return lookups;
}
function extractMatchFilter(pipeline) {
    var e_6, _a;
    if (!Array.isArray(pipeline)) {
        return null;
    }
    var filters = [];
    try {
        for (var pipeline_2 = __values(pipeline), pipeline_2_1 = pipeline_2.next(); !pipeline_2_1.done; pipeline_2_1 = pipeline_2.next()) {
            var stage = pipeline_2_1.value;
            if (stage && stage.$match && typeof stage.$match === 'object') {
                filters.push((0, common_1.deepCopy)(stage.$match));
            }
        }
    }
    catch (e_6_1) { e_6 = { error: e_6_1 }; }
    finally {
        try {
            if (pipeline_2_1 && !pipeline_2_1.done && (_a = pipeline_2.return)) _a.call(pipeline_2);
        }
        finally { if (e_6) throw e_6.error; }
    }
    if (!filters.length) {
        return null;
    }
    if (filters.length === 1) {
        return filters[0];
    }
    return { $and: filters };
}
function sanitizeMatchFilter(filter, lookupAliases) {
    if (!filter || !(lookupAliases === null || lookupAliases === void 0 ? void 0 : lookupAliases.size)) {
        return filter;
    }
    return sanitizeFilterNode(filter, lookupAliases);
}
function sanitizeFilterNode(node, lookupAliases) {
    var e_7, _a;
    if (!node) {
        return null;
    }
    if (Array.isArray(node)) {
        var sanitizedArray = node
            .map(function (entry) { return sanitizeFilterNode(entry, lookupAliases); })
            .filter(isMeaningfulFilterNode);
        return sanitizedArray.length ? sanitizedArray : null;
    }
    if (typeof node !== 'object') {
        return node;
    }
    var result = {};
    try {
        for (var _b = __values(Object.entries(node)), _c = _b.next(); !_c.done; _c = _b.next()) {
            var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
            if (key === '$and' || key === '$or' || key === '$nor') {
                if (Array.isArray(value)) {
                    var sanitizedChildren = value
                        .map(function (entry) { return sanitizeFilterNode(entry, lookupAliases); })
                        .filter(isMeaningfulFilterNode);
                    if (sanitizedChildren.length) {
                        result[key] = sanitizedChildren;
                    }
                }
                continue;
            }
            var rootKey = key.split('.')[0];
            if (lookupAliases.has(rootKey)) {
                continue;
            }
            result[key] = value;
        }
    }
    catch (e_7_1) { e_7 = { error: e_7_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_7) throw e_7.error; }
    }
    return Object.keys(result).length ? result : null;
}
function isMeaningfulFilterNode(node) {
    if (!node) {
        return false;
    }
    if (Array.isArray(node)) {
        return node.length > 0;
    }
    if (typeof node !== 'object') {
        return true;
    }
    return Object.keys(node).length > 0;
}
function extractAggregateMeta(pipeline) {
    var e_8, _a;
    if (!Array.isArray(pipeline)) {
        return null;
    }
    var limit = undefined;
    var skip = undefined;
    var sort = null;
    try {
        for (var pipeline_3 = __values(pipeline), pipeline_3_1 = pipeline_3.next(); !pipeline_3_1.done; pipeline_3_1 = pipeline_3.next()) {
            var stage = pipeline_3_1.value;
            if ((stage === null || stage === void 0 ? void 0 : stage.$limit) !== undefined && typeof stage.$limit === 'number') {
                limit = stage.$limit;
            }
            else if ((stage === null || stage === void 0 ? void 0 : stage.$skip) !== undefined && typeof stage.$skip === 'number') {
                skip = stage.$skip;
            }
            else if ((stage === null || stage === void 0 ? void 0 : stage.$sort) && typeof stage.$sort === 'object') {
                sort = (0, common_1.deepCopy)(stage.$sort);
            }
        }
    }
    catch (e_8_1) { e_8 = { error: e_8_1 }; }
    finally {
        try {
            if (pipeline_3_1 && !pipeline_3_1.done && (_a = pipeline_3.return)) _a.call(pipeline_3);
        }
        finally { if (e_8) throw e_8.error; }
    }
    var meta = {};
    if (limit !== undefined) {
        meta.limit = limit;
    }
    if (skip !== undefined) {
        meta.skip = skip;
    }
    if (sort) {
        meta.sort = sort;
    }
    return Object.keys(meta).length ? meta : null;
}
function extractAggregationMetaFromPipeline(pipeline) {
    var e_9, _a;
    if (!Array.isArray(pipeline)) {
        return null;
    }
    var limit = undefined;
    var skip = undefined;
    var sort = null;
    try {
        for (var pipeline_4 = __values(pipeline), pipeline_4_1 = pipeline_4.next(); !pipeline_4_1.done; pipeline_4_1 = pipeline_4.next()) {
            var stage = pipeline_4_1.value;
            if ((stage === null || stage === void 0 ? void 0 : stage.$limit) !== undefined && typeof stage.$limit === 'number') {
                limit = stage.$limit;
            }
            else if ((stage === null || stage === void 0 ? void 0 : stage.$skip) !== undefined && typeof stage.$skip === 'number') {
                skip = stage.$skip;
            }
            else if ((stage === null || stage === void 0 ? void 0 : stage.$sort) && typeof stage.$sort === 'object') {
                sort = (0, common_1.deepCopy)(stage.$sort);
            }
        }
    }
    catch (e_9_1) { e_9 = { error: e_9_1 }; }
    finally {
        try {
            if (pipeline_4_1 && !pipeline_4_1.done && (_a = pipeline_4.return)) _a.call(pipeline_4);
        }
        finally { if (e_9) throw e_9.error; }
    }
    var meta = {};
    if (limit !== undefined) {
        meta.limit = limit;
    }
    if (skip !== undefined) {
        meta.skip = skip;
    }
    if (sort) {
        meta.sort = sort;
    }
    return Object.keys(meta).length ? meta : null;
}
function collectLookupValues(source, path, output) {
    var e_10, _a;
    if (!source) {
        return;
    }
    if (Array.isArray(source)) {
        try {
            for (var source_1 = __values(source), source_1_1 = source_1.next(); !source_1_1.done; source_1_1 = source_1.next()) {
                var item = source_1_1.value;
                collectLookupValues(item, path, output);
            }
        }
        catch (e_10_1) { e_10 = { error: e_10_1 }; }
        finally {
            try {
                if (source_1_1 && !source_1_1.done && (_a = source_1.return)) _a.call(source_1);
            }
            finally { if (e_10) throw e_10.error; }
        }
        return;
    }
    if (typeof source !== 'object') {
        return;
    }
    var pathSegments = path.split('.');
    collectLookupPathValues(source, pathSegments, 0, output);
}
function collectLookupPathValues(value, path, index, output) {
    var e_11, _a;
    if (!value) {
        return;
    }
    var current = value[path[index]];
    if (current === undefined) {
        return;
    }
    if (index === path.length - 1) {
        output.push(current);
        return;
    }
    if (Array.isArray(current)) {
        try {
            for (var current_1 = __values(current), current_1_1 = current_1.next(); !current_1_1.done; current_1_1 = current_1.next()) {
                var item = current_1_1.value;
                if (item && typeof item === 'object') {
                    collectLookupPathValues(item, path, index + 1, output);
                }
            }
        }
        catch (e_11_1) { e_11 = { error: e_11_1 }; }
        finally {
            try {
                if (current_1_1 && !current_1_1.done && (_a = current_1.return)) _a.call(current_1);
            }
            finally { if (e_11) throw e_11.error; }
        }
    }
    else if (current && typeof current === 'object') {
        collectLookupPathValues(current, path, index + 1, output);
    }
}
function buildLookupFilter(result, localField, foreignField) {
    var _a;
    if (!localField || !foreignField) {
        return null;
    }
    var values = [];
    var segments = localField.split('.');
    collectFieldValues(result, segments, 0, values);
    var uniqueValues = dedupeFilterValues(values);
    if (!uniqueValues.length) {
        return null;
    }
    return _a = {},
        _a[foreignField] = {
            $in: uniqueValues
        },
        _a;
}
function collectFieldValues(source, path, index, output) {
    var e_12, _a, e_13, _b;
    if (!source) {
        return;
    }
    if (Array.isArray(source)) {
        try {
            for (var source_2 = __values(source), source_2_1 = source_2.next(); !source_2_1.done; source_2_1 = source_2.next()) {
                var item = source_2_1.value;
                collectFieldValues(item, path, index, output);
            }
        }
        catch (e_12_1) { e_12 = { error: e_12_1 }; }
        finally {
            try {
                if (source_2_1 && !source_2_1.done && (_a = source_2.return)) _a.call(source_2);
            }
            finally { if (e_12) throw e_12.error; }
        }
        return;
    }
    if (typeof source !== 'object') {
        return;
    }
    var obj = source;
    var key = path[index];
    var current = obj[key];
    if (current === undefined) {
        return;
    }
    if (index === path.length - 1) {
        appendFilterValue(output, current);
        return;
    }
    if (Array.isArray(current)) {
        try {
            for (var current_2 = __values(current), current_2_1 = current_2.next(); !current_2_1.done; current_2_1 = current_2.next()) {
                var item = current_2_1.value;
                collectFieldValues(item, path, index + 1, output);
            }
        }
        catch (e_13_1) { e_13 = { error: e_13_1 }; }
        finally {
            try {
                if (current_2_1 && !current_2_1.done && (_b = current_2.return)) _b.call(current_2);
            }
            finally { if (e_13) throw e_13.error; }
        }
    }
    else {
        collectFieldValues(current, path, index + 1, output);
    }
}
function appendFilterValue(output, value) {
    var e_14, _a;
    if (value === undefined || value === null) {
        return;
    }
    if (Array.isArray(value)) {
        try {
            for (var value_1 = __values(value), value_1_1 = value_1.next(); !value_1_1.done; value_1_1 = value_1.next()) {
                var item = value_1_1.value;
                appendFilterValue(output, item);
            }
        }
        catch (e_14_1) { e_14 = { error: e_14_1 }; }
        finally {
            try {
                if (value_1_1 && !value_1_1.done && (_a = value_1.return)) _a.call(value_1);
            }
            finally { if (e_14) throw e_14.error; }
        }
        return;
    }
    output.push(value);
}
function dedupeFilterValues(values) {
    var e_15, _a;
    var unique = new Map();
    try {
        for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) {
            var value = values_1_1.value;
            var key = serializeFilterValue(value);
            if (!unique.has(key)) {
                unique.set(key, value);
            }
        }
    }
    catch (e_15_1) { e_15 = { error: e_15_1 }; }
    finally {
        try {
            if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1);
        }
        finally { if (e_15) throw e_15.error; }
    }
    return Array.from(unique.values());
}
function serializeFilterValue(value) {
    if (value === null || value === undefined) {
        return 'null';
    }
    if (typeof value === 'object') {
        if (typeof value.toHexString === 'function') {
            return value.toHexString();
        }
        if (value instanceof Date) {
            return value.toISOString();
        }
        if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) {
            return value.toString();
        }
        try {
            return JSON.stringify(value);
        }
        catch (_a) {
            return '[object]';
        }
    }
    return String(value);
}
function collectIdsFromResult(source, ids, visited) {
    var e_16, _a, e_17, _b;
    if (visited === void 0) { visited = new WeakSet(); }
    if (!source) {
        return;
    }
    if (Array.isArray(source)) {
        try {
            for (var source_3 = __values(source), source_3_1 = source_3.next(); !source_3_1.done; source_3_1 = source_3.next()) {
                var item = source_3_1.value;
                collectIdsFromResult(item, ids, visited);
            }
        }
        catch (e_16_1) { e_16 = { error: e_16_1 }; }
        finally {
            try {
                if (source_3_1 && !source_3_1.done && (_a = source_3.return)) _a.call(source_3);
            }
            finally { if (e_16) throw e_16.error; }
        }
        return;
    }
    if (typeof source !== 'object') {
        return;
    }
    var objectSource = source;
    if (visited.has(objectSource)) {
        return;
    }
    visited.add(objectSource);
    var normalizedId = normalizeId(objectSource._id);
    if (normalizedId) {
        ids.add(normalizedId);
    }
    try {
        for (var _c = __values(Object.values(objectSource)), _d = _c.next(); !_d.done; _d = _c.next()) {
            var value = _d.value;
            if (value && (typeof value === 'object' || Array.isArray(value))) {
                collectIdsFromResult(value, ids, visited);
            }
        }
    }
    catch (e_17_1) { e_17 = { error: e_17_1 }; }
    finally {
        try {
            if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
        }
        finally { if (e_17) throw e_17.error; }
    }
}
function collectIdsFromFilter(filter, ids) {
    var e_18, _a, e_19, _b, e_20, _c;
    if (!filter) {
        return;
    }
    if (Array.isArray(filter)) {
        try {
            for (var filter_1 = __values(filter), filter_1_1 = filter_1.next(); !filter_1_1.done; filter_1_1 = filter_1.next()) {
                var item = filter_1_1.value;
                collectIdsFromFilter(item, ids);
            }
        }
        catch (e_18_1) { e_18 = { error: e_18_1 }; }
        finally {
            try {
                if (filter_1_1 && !filter_1_1.done && (_a = filter_1.return)) _a.call(filter_1);
            }
            finally { if (e_18) throw e_18.error; }
        }
        return;
    }
    if (typeof filter !== 'object') {
        return;
    }
    var filterObject = filter;
    if (filterObject._id !== undefined) {
        collectIdsFromFilterValue(filterObject._id, ids);
    }
    try {
        for (var _d = __values(Object.entries(filterObject)), _e = _d.next(); !_e.done; _e = _d.next()) {
            var _f = __read(_e.value, 2), key = _f[0], value = _f[1];
            if (key === '$or' || key === '$and' || key === '$nor') {
                if (Array.isArray(value)) {
                    try {
                        for (var value_2 = (e_20 = void 0, __values(value)), value_2_1 = value_2.next(); !value_2_1.done; value_2_1 = value_2.next()) {
                            var item = value_2_1.value;
                            collectIdsFromFilter(item, ids);
                        }
                    }
                    catch (e_20_1) { e_20 = { error: e_20_1 }; }
                    finally {
                        try {
                            if (value_2_1 && !value_2_1.done && (_c = value_2.return)) _c.call(value_2);
                        }
                        finally { if (e_20) throw e_20.error; }
                    }
                }
            }
            else if (key !== '_id' && value && typeof value === 'object') {
                collectIdsFromFilter(value, ids);
            }
        }
    }
    catch (e_19_1) { e_19 = { error: e_19_1 }; }
    finally {
        try {
            if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
        }
        finally { if (e_19) throw e_19.error; }
    }
}
function collectIdsFromFilterValue(value, ids) {
    var e_21, _a, e_22, _b;
    if (value === null || value === undefined) {
        return;
    }
    if (Array.isArray(value)) {
        try {
            for (var value_3 = __values(value), value_3_1 = value_3.next(); !value_3_1.done; value_3_1 = value_3.next()) {
                var item = value_3_1.value;
                collectIdsFromFilterValue(item, ids);
            }
        }
        catch (e_21_1) { e_21 = { error: e_21_1 }; }
        finally {
            try {
                if (value_3_1 && !value_3_1.done && (_a = value_3.return)) _a.call(value_3);
            }
            finally { if (e_21) throw e_21.error; }
        }
        return;
    }
    if (typeof value === 'object') {
        try {
            for (var _c = __values(Object.entries(value)), _d = _c.next(); !_d.done; _d = _c.next()) {
                var _e = __read(_d.value, 2), key = _e[0], inner = _e[1];
                if (key === '$in' || key === '$nin' || key === '$all') {
                    collectIdsFromFilterValue(inner, ids);
                }
                else if (key === '$eq' || key === '$ne') {
                    collectIdsFromFilterValue(inner, ids);
                }
                else if (key === '$or' || key === '$and' || key === '$nor') {
                    collectIdsFromFilterValue(inner, ids);
                }
                else {
                    collectIdsFromFilterValue(inner, ids);
                }
            }
        }
        catch (e_22_1) { e_22 = { error: e_22_1 }; }
        finally {
            try {
                if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
            }
            finally { if (e_22) throw e_22.error; }
        }
        return;
    }
    var normalized = normalizeId(value);
    if (normalized) {
        ids.add(normalized);
    }
}
function normalizeId(value) {
    if (value === null || value === undefined) {
        return null;
    }
    if (typeof value === 'string') {
        var trimmed = value.trim();
        return trimmed.length ? trimmed : null;
    }
    if (typeof value === 'number' || typeof value === 'bigint') {
        return value.toString();
    }
    if (typeof value === 'object') {
        var objValue = value;
        if (typeof objValue.toHexString === 'function') {
            return objValue.toHexString();
        }
        if (value instanceof Date) {
            return value.toISOString();
        }
        if (typeof objValue.toString === 'function') {
            var str = objValue.toString();
            if (str && str !== '[object Object]') {
                return str;
            }
        }
    }
    return null;
}
function cloneDependencyMap(original) {
    var e_23, _a;
    var cloned = new Map();
    try {
        for (var _b = __values(original.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
            var _d = __read(_c.value, 2), collection = _d[0], ids = _d[1];
            cloned.set(collection, new Set(ids));
        }
    }
    catch (e_23_1) { e_23 = { error: e_23_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_23) throw e_23.error; }
    }
    return cloned;
}
function cloneFilterMap(original) {
    var e_24, _a;
    var cloned = new Map();
    try {
        for (var _b = __values(original.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
            var _d = __read(_c.value, 2), collection = _d[0], filters = _d[1];
            cloned.set(collection, filters.map(function (filter) { return (0, common_1.deepCopy)(filter); }));
        }
    }
    catch (e_24_1) { e_24 = { error: e_24_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_24) throw e_24.error; }
    }
    return cloned;
}
function cloneQueryMetaMap(original) {
    var e_25, _a;
    var cloned = new Map();
    try {
        for (var _b = __values(original.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
            var _d = __read(_c.value, 2), collection = _d[0], metaList = _d[1];
            cloned.set(collection, metaList.map(function (meta) { return (0, common_1.deepCopy)(meta); }));
        }
    }
    catch (e_25_1) { e_25 = { error: e_25_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_25) throw e_25.error; }
    }
    return cloned;
}
function buildLookupPipelineFilters(rootDoc, target) {
    var e_26, _a;
    if (!target.pipeline || !target.pipeline.length) {
        return { filters: [], unresolved: false };
    }
    var matchStages = target.pipeline.map(function (stage) { return ({ stage: stage, base: stage.$match }); }).filter(function (entry) { return entry.base; });
    if (!matchStages.length) {
        return buildLookupPipelineFromStack(target.pipeline, [], rootDoc, target.let);
    }
    var letValuesResult = resolveLetVariables(rootDoc, target.let);
    if (!letValuesResult.resolved) {
        return { filters: [], unresolved: true };
    }
    var combinedMatches = [];
    try {
        for (var matchStages_1 = __values(matchStages), matchStages_1_1 = matchStages_1.next(); !matchStages_1_1.done; matchStages_1_1 = matchStages_1.next()) {
            var base = matchStages_1_1.value.base;
            var substitutionState = { unresolved: false };
            var substituted = substituteLetVariables(base, letValuesResult.values, rootDoc, substitutionState);
            if (substitutionState.unresolved || containsUnresolvedLetReference(substituted)) {
                return { filters: [], unresolved: true };
            }
            combinedMatches.push(substituted);
        }
    }
    catch (e_26_1) { e_26 = { error: e_26_1 }; }
    finally {
        try {
            if (matchStages_1_1 && !matchStages_1_1.done && (_a = matchStages_1.return)) _a.call(matchStages_1);
        }
        finally { if (e_26) throw e_26.error; }
    }
    if (!combinedMatches.length) {
        return buildLookupPipelineFromStack(target.pipeline, [], rootDoc, target.let);
    }
    if (combinedMatches.length === 1) {
        return { filters: [combinedMatches[0]], unresolved: false };
    }
    return { filters: [{ $and: combinedMatches }], unresolved: false };
}
function buildLookupPipelineFromStack(pipeline, initialFilters, rootDoc, letMap) {
    var currentFilters = initialFilters.slice();
    var unresolved = false;
    var entries = pipeline.map(function (stage) { return stage ? Object.keys(stage)[0] : null; });
    for (var i = 0; i < pipeline.length; i++) {
        var stage = pipeline[i];
        var stageType = entries[i];
        if (stageType === '$match' && stage.$match) {
            var letValuesResult = resolveLetVariables(rootDoc, letMap);
            if (!letValuesResult.resolved) {
                unresolved = true;
                break;
            }
            var substitutionState = { unresolved: false };
            var substituted = substituteLetVariables(stage.$match, letValuesResult.values, rootDoc, substitutionState);
            if (substitutionState.unresolved || containsUnresolvedLetReference(substituted)) {
                unresolved = true;
                break;
            }
            currentFilters.push(substituted);
        }
        else if (stageType === '$match') {
            unresolved = true;
            break;
        }
        else if (stageType === '$facet') {
            unresolved = true;
            break;
        }
    }
    if (!currentFilters.length) {
        return { filters: [], unresolved: unresolved };
    }
    if (currentFilters.length === 1) {
        return { filters: currentFilters, unresolved: unresolved };
    }
    return { filters: [{ $and: currentFilters }], unresolved: unresolved };
}
function resolveLetVariables(rootDoc, letMap) {
    var e_27, _a;
    if (!letMap || typeof letMap !== 'object') {
        return { resolved: true, values: {} };
    }
    var values = {};
    try {
        for (var _b = __values(Object.entries(letMap)), _c = _b.next(); !_c.done; _c = _b.next()) {
            var _d = __read(_c.value, 2), key = _d[0], expression = _d[1];
            var evaluation = evaluateLetExpression(rootDoc, expression);
            if (!evaluation.resolved) {
                return { resolved: false, values: null };
            }
            values[key] = evaluation.value;
        }
    }
    catch (e_27_1) { e_27 = { error: e_27_1 }; }
    finally {
        try {
            if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
        }
        finally { if (e_27) throw e_27.error; }
    }
    return { resolved: true, values: values };
}
function evaluateLetExpression(rootDoc, expression) {
    if (typeof expression === 'string') {
        if (expression.startsWith('$$ROOT.')) {
            var path = expression.slice('$$ROOT.'.length);
            return { resolved: true, value: getValueByPath(rootDoc, path) };
        }
        else if (expression === '$$ROOT') {
            return { resolved: true, value: rootDoc };
        }
        else if (expression.startsWith('$$')) {
            return { resolved: false, value: null };
        }
        else if (expression.startsWith('$')) {
            var path = expression.slice(1);
            return { resolved: true, value: getValueByPath(rootDoc, path) };
        }
        else {
            return { resolved: true, value: expression };
        }
    }
    else if (Array.isArray(expression)) {
        return { resolved: false, value: null };
    }
    else if (expression && typeof expression === 'object') {
        return { resolved: false, value: null };
    }
    return { resolved: true, value: expression };
}
function substituteLetVariables(node, letValues, rootDoc, state) {
    var e_28, _a;
    if (Array.isArray(node)) {
        return node.map(function (item) { return substituteLetVariables(item, letValues, rootDoc, state); });
    }
    if (node && typeof node === 'object') {
        var output = {};
        try {
            for (var _b = __values(Object.entries(node)), _c = _b.next(); !_c.done; _c = _b.next()) {
                var _d = __read(_c.value, 2), key = _d[0], value = _d[1];
                output[key] = substituteLetVariables(value, letValues, rootDoc, state);
            }
        }
        catch (e_28_1) { e_28 = { error: e_28_1 }; }
        finally {
            try {
                if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
            }
            finally { if (e_28) throw e_28.error; }
        }
        return output;
    }
    if (typeof node === 'string') {
        if (node.startsWith('$$ROOT.')) {
            var path = node.slice('$$ROOT.'.length);
            return getValueByPath(rootDoc, path);
        }
        if (node === '$$ROOT') {
            return rootDoc;
        }
        if (node.startsWith('$$')) {
            var varName = node.slice(2);
            if (Object.prototype.hasOwnProperty.call(letValues, varName)) {
                return letValues[varName];
            }
            state.unresolved = true;
            return node;
        }
    }
    return node;
}
function containsUnresolvedLetReference(node) {
    if (typeof node === 'string') {
        return node.includes('$$');
    }
    if (Array.isArray(node)) {
        return node.some(function (item) { return containsUnresolvedLetReference(item); });
    }
    if (node && typeof node === 'object') {
        return Object.values(node).some(function (value) { return containsUnresolvedLetReference(value); });
    }
    return false;
}
function getValueByPath(source, path) {
    var e_29, _a;
    if (!source || !path) {
        return undefined;
    }
    var segments = path.split('.');
    var current = source;
    try {
        for (var segments_1 = __values(segments), segments_1_1 = segments_1.next(); !segments_1_1.done; segments_1_1 = segments_1.next()) {
            var segment = segments_1_1.value;
            if (current === null || current === undefined) {
                return undefined;
            }
            if (Array.isArray(current)) {
                var index = parseInt(segment, 10);
                if (isNaN(index) || index < 0 || index >= current.length) {
                    return undefined;
                }
                current = current[index];
            }
            else {
                current = current[segment];
            }
        }
    }
    catch (e_29_1) { e_29 = { error: e_29_1 }; }
    finally {
        try {
            if (segments_1_1 && !segments_1_1.done && (_a = segments_1.return)) _a.call(segments_1);
        }
        finally { if (e_29) throw e_29.error; }
    }
    return current;
}
function canonicalizeFilter(filter) {
    return JSON.stringify(sortKeys(filter));
}
function sortKeys(value) {
    var e_30, _a;
    if (Array.isArray(value)) {
        return value.map(function (item) { return sortKeys(item); });
    }
    if (value && typeof value === 'object') {
        var sorted = {};
        var keys = Object.keys(value).sort();
        try {
            for (var keys_1 = __values(keys), keys_1_1 = keys_1.next(); !keys_1_1.done; keys_1_1 = keys_1.next()) {
                var key = keys_1_1.value;
                sorted[key] = sortKeys(value[key]);
            }
        }
        catch (e_30_1) { e_30 = { error: e_30_1 }; }
        finally {
            try {
                if (keys_1_1 && !keys_1_1.done && (_a = keys_1.return)) _a.call(keys_1);
            }
            finally { if (e_30) throw e_30.error; }
        }
        return sorted;
    }
    return value;
}
function dedupeFilters(filters) {
    var e_31, _a;
    var map = new Map();
    try {
        for (var filters_1 = __values(filters), filters_1_1 = filters_1.next(); !filters_1_1.done; filters_1_1 = filters_1.next()) {
            var filter = filters_1_1.value;
            var key = canonicalizeFilter(filter);
            if (!map.has(key)) {
                map.set(key, filter);
            }
        }
    }
    catch (e_31_1) { e_31 = { error: e_31_1 }; }
    finally {
        try {
            if (filters_1_1 && !filters_1_1.done && (_a = filters_1.return)) _a.call(filters_1);
        }
        finally { if (e_31) throw e_31.error; }
    }
    return Array.from(map.values());
}

//# sourceMappingURL=subscription-dependency-context.js.map
