"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
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.objectIdHexString = objectIdHexString;
exports.buildRbSchema = buildRbSchema;
exports.buildRbLookups = buildRbLookups;
exports.throwErr = throwErr;
exports.roundPrecision = roundPrecision;
exports.round = round;
exports.floorPrecision = floorPrecision;
exports.deepCopy = deepCopy;
exports.dateReviver = dateReviver;
exports.getAllowedOrigins = getAllowedOrigins;
exports.isAllowedOrigin = isAllowedOrigin;
exports.getMongoUpdates = getMongoUpdates;
exports.getMongoMergeUpdatedDoc = getMongoMergeUpdatedDoc;
exports.mergeUpdates = mergeUpdates;
exports.getDeepDiff = getDeepDiff;
exports.applyMongoUpdate = applyMongoUpdate;
exports.deepDiffDetails = deepDiffDetails;
exports.getBinarySize = getBinarySize;
exports.pad = pad;
exports.randomString = randomString;
exports.toTitleCase = toTitleCase;
exports.isObject = isObject;
exports.mergeDeep = mergeDeep;
exports.generateSearchTokensFromFields = generateSearchTokensFromFields;
/* eslint-disable no-restricted-properties */
var rusDiff = require('rus-diff');
var deepDiff = require('deep-diff').diff;
var deep_object_diff_1 = require("deep-object-diff");
var moment = require("moment-timezone");
var mongodb_1 = require("mongodb");
var clone = require('clone');
function objectIdHexString() {
    var objectId = new mongodb_1.ObjectId();
    return objectId.toHexString();
}
function buildRbSchema(rbSchema) {
    Object.keys(rbSchema).forEach(function (key) {
        if (rbSchema[key].type === Object) {
            rbSchema[key].type = 'Object';
            rbSchema[key].rbType = 'Object';
        }
        else if (rbSchema[key].type === Array) {
            rbSchema[key].type = 'Array';
            rbSchema[key].rbType = 'Array';
        }
        else if (rbSchema[key].type === String) {
            rbSchema[key].type = 'String';
            rbSchema[key].rbType = 'String';
        }
        else if (rbSchema[key].type === Date) {
            rbSchema[key].type = 'Date';
            rbSchema[key].rbType = 'Date';
        }
        else if (rbSchema[key].type === Number) {
            rbSchema[key].type = 'Number';
            rbSchema[key].rbType = 'Number';
        }
        else if (rbSchema[key].type === Boolean) {
            rbSchema[key].rbType = 'Boolean';
            rbSchema[key].type = 'Boolean';
        }
    });
    Object.keys(rbSchema).forEach(function (key) {
        delete rbSchema[key].type;
    });
    return rbSchema;
}
function buildRbLookups(rbLookups, defaultSchema, default_checked_array) {
    // Remember: lkp.local_key will be removed later on, because it has 'id_'
    if (default_checked_array === void 0) { default_checked_array = []; }
    rbLookups.forEach(function (lkp) {
        var errString = '';
        var localKey, newPropertyName;
        var localKeyDir = '';
        var localKeyDirDot = '';
        var collectionAlias = toTitleCase(lkp.lookup.replace(/\_/g, ' ')) + ' (' + toTitleCase(lkp.local_key.replace('id_', '').replace(/\_/g, ' ').replace(/\.\$\./g, ' -> ').replace(/\./g, ' -> ')) + ') (Lookup)';
        if (lkp.local_key.match(/\./)) {
            localKey = lkp.local_key.replace(/\.\$/g, '');
            localKeyDirDot = lkp.local_key.replace(/^(.+\.).*$/, '$1');
            localKeyDir = localKeyDirDot.replace(/\.$/, '');
        }
        else {
            // local_key is a root leaf node.
            localKey = lkp.local_key;
        }
        newPropertyName = localKeyDirDot + collectionAlias;
        if (!defaultSchema[lkp.local_key]) {
            errString += '--local_key: \'' + lkp.local_key + '\' MUST be a valid schema path! ';
        }
        else if (defaultSchema[lkp.local_key].type !== String) {
            errString += '--local_key: \'' + lkp.local_key + '\' MUST be a String! ';
        }
        var lookupNode = {
            lookup_collection: lkp.lookup,
            lookup_collection_alias: collectionAlias,
            local_key: localKey,
            field_path: localKeyDir,
            lookup_as: newPropertyName,
            lookup_key: '_id',
            type: 'Object',
        };
        defaultSchema[newPropertyName] = lookupNode;
        if (errString) {
            throwErr(errString, lkp, lookupNode, newPropertyName);
        }
    });
    for (var ii = 0; ii < default_checked_array.length; ii++) {
        defaultSchema[default_checked_array[ii]] = {};
        defaultSchema[default_checked_array[ii]]['default_checked'] = true;
        Object.assign(defaultSchema[default_checked_array[ii]], defaultSchema['tree'][default_checked_array[ii]]);
    }
    return defaultSchema;
}
function throwErr(err, lkp, lookupNode, newPropertyName) {
    if (newPropertyName === void 0) { newPropertyName = ''; }
    // err could be from a try/throw/catch
    console.log('------- ERROR thrown by rbLookups: ');
    console.log(err);
    console.log('---item: ');
    console.log(lkp);
    console.log('---Attempted to create rbSchema[\'' + newPropertyName + '\']: ');
    console.log(lookupNode);
    process.exit(1);
}
function roundPrecision(value, precision) {
    if (precision === void 0) { precision = 0; }
    var stringValue = value.toString();
    if (stringValue.includes('.')) {
        if (stringValue[0] === '0') {
            precision += 1;
        }
        precision -= stringValue.indexOf('.');
    }
    var exponent = Math.pow(10, precision);
    return Math.round(parseFloat(stringValue) * exponent) / exponent;
}
function round(value, precision) {
    if (precision === void 0) { precision = 0; }
    var power = Math.pow(10, precision);
    var adjustment = 1e-6;
    return Math.round((value * power) + adjustment) / power;
}
function floorPrecision(value, precision) {
    if (precision === void 0) { precision = 0; }
    var stringValue = value.toString();
    if (stringValue.includes('.')) {
        if (stringValue[0] === '0') {
            precision += 1;
        }
        precision -= stringValue.indexOf('.');
    }
    var exponent = Math.pow(10, precision);
    return Math.floor(parseFloat(stringValue) * exponent) / exponent;
}
function deepCopy(value) {
    return clone(value);
}
function dateReviver(key, value) {
    if (typeof value === 'string') {
        var a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
        if (a) {
            return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6].split('.')[0], +a[6].split('.')[1]));
        }
    }
    return value;
}
function buildAllowedOrigins(configOrigins) {
    var origins = [];
    if (Array.isArray(configOrigins)) {
        origins = configOrigins.map(function (origin) { return typeof origin === 'string' ? origin.trim() : origin; });
    }
    else if (typeof configOrigins === 'string') {
        var splitOrigins = configOrigins.split(',').map(function (origin) { return origin.trim(); }).filter(function (origin) { return origin; });
        origins = splitOrigins;
    }
    return origins.filter(function (origin) { return typeof origin === 'string' && origin.trim() !== ''; });
}
function getAllowedOrigins(serverConfig) {
    var allowedOrigins = new Set();
    var defaultKeys = ['ROOT_URL', 'SEC_ROOT_URL', 'RESOLVEIO_URL', 'RESOLVEIO_SECONDARY_URL'];
    defaultKeys.forEach(function (key) {
        var value = serverConfig[key] || process.env[key];
        if (typeof value === 'string' && value.trim()) {
            allowedOrigins.add(value.trim());
        }
    });
    buildAllowedOrigins(serverConfig['ALLOWED_ORIGINS']).forEach(function (origin) { return allowedOrigins.add(origin); });
    buildAllowedOrigins(serverConfig['ADDITIONAL_ALLOWED_ORIGINS']).forEach(function (origin) { return allowedOrigins.add(origin); });
    buildAllowedOrigins(process.env.ALLOWED_ORIGINS).forEach(function (origin) { return allowedOrigins.add(origin); });
    buildAllowedOrigins(process.env.ADDITIONAL_ALLOWED_ORIGINS).forEach(function (origin) { return allowedOrigins.add(origin); });
    return Array.from(allowedOrigins);
}
function isAllowedOrigin(origin, serverConfig) {
    if (!origin) {
        return false;
    }
    var allowedOrigins = getAllowedOrigins(serverConfig);
    return allowedOrigins.includes(origin);
}
function getMongoUpdates(currDoc, modifiedDoc, incVersion) {
    if (currDoc.createdAt) {
        modifiedDoc.createdAt = currDoc.createdAt;
    }
    if (currDoc.updatedAt) {
        modifiedDoc.updatedAt = currDoc.updatedAt;
    }
    var updates = rusDiff.diff(currDoc, modifiedDoc);
    if (updates) {
        if (updates.$set && !Object.keys(updates.$set).length) {
            delete updates.$set;
        }
        if (incVersion) {
            updates.$inc = { __v: 1 };
        }
    }
    else {
        if (incVersion) {
            updates = {
                $inc: { __v: 1 }
            };
        }
    }
    return updates;
}
function getMongoMergeUpdatedDoc(f_doc, currentDoc, oldDoc) {
    var upDoc = deepCopy(currentDoc); // was oldDoc
    upDoc.__v = currentDoc.__v;
    var diff_1 = getDeepDiff(oldDoc, currentDoc);
    var diff_2 = getDeepDiff(oldDoc, f_doc);
    var updates = mergeUpdates(diff_2, diff_1);
    applyMongoUpdate(upDoc, updates);
    return upDoc;
}
function mergeUpdates() {
    var updatesToMerge = [];
    for (var _i = 0; _i < arguments.length; _i++) {
        updatesToMerge[_i] = arguments[_i];
    }
    var merged = {
        $set: {},
        $unset: {},
        $inc: {},
        $push: {},
        $pull: {}
    };
    updatesToMerge.filter(Boolean).forEach(function (anUpdate) {
        anUpdate.forEach(function (change) {
            var path = change.path.join('.');
            switch (change.kind) {
                case 'E':
                    handleEditedChange(merged, path, change);
                    break;
                case 'A':
                    handleArrayChange(merged, path, change);
                    break;
                default:
                    break;
            }
        });
    });
    return merged;
}
function handleEditedChange(merged, path, change) {
    // Later diffs override earlier ones
    if (typeof (change.rhs) === 'number' && (path.includes('quantity') || path.includes('qty'))) {
        merged.$inc[path] = (merged.$inc[path] || 0) + change.rhs - change.lhs;
    }
    else {
        merged.$set[path] = change.rhs;
    }
}
function handleArrayChange(merged, path, change) {
    if (change.item.kind === 'N') {
        if (!merged.$push[path])
            merged.$push[path] = { $each: [] };
        if (!merged.$push[path].$each.some(function (a) { return JSON.stringify(a) === JSON.stringify(change.item.rhs); })) {
            merged.$push[path].$each.push(change.item.rhs);
        }
    }
    else if (change.item.kind === 'D') {
        if (!merged.$pull[path])
            merged.$pull[path] = [];
        if (!merged.$pull[path].some(function (a) { return JSON.stringify(a) === JSON.stringify(change.item.lhs); })) {
            merged.$pull[path].push(change.item.lhs);
        }
    }
}
function getDeepDiff(doc1, doc2) {
    delete doc1._id;
    delete doc1.__v;
    if (doc1.updatedAt) {
        delete doc1.updatedAt;
    }
    if (doc1.createdAt) {
        delete doc1.createdAt;
    }
    delete doc2._id;
    delete doc2.__v;
    if (doc2.updatedAt) {
        delete doc2.updatedAt;
    }
    if (doc2.createdAt) {
        delete doc2.createdAt;
    }
    return deepDiff(doc1, doc2);
}
function applyMongoUpdate(uDoc, delta) {
    var orig = deepCopy(uDoc);
    if (delta) {
        if (delta.$rename)
            handleRename(uDoc, delta.$rename);
        if (delta.$set)
            handleSet(uDoc, delta.$set);
        if (delta.$inc)
            handleInc(uDoc, delta.$inc);
        if (delta.$unset)
            handleUnset(uDoc, delta.$unset);
        if (delta.$pull)
            handlePull(uDoc, orig, delta.$pull);
        if (delta.$push)
            handlePush(uDoc, delta.$push);
    }
    return uDoc;
}
function handleRename(uDoc, renameMap) {
    for (var k in renameMap) {
        var v = renameMap[k];
        var _a = __read(resolve(uDoc, k), 2), o1 = _a[0], n1 = _a[1];
        var _b = __read(resolve(uDoc, v), 2), o2 = _b[0], n2 = _b[1];
        if (o1 && n1.length === 1) {
            if (o2 && n2.length === 1) {
                o2[n2[0]] = o1[n1[0]];
                delete o1[n1[0]];
            }
            else {
                logError(o2, n2, uDoc, v);
            }
        }
        else {
            logError(o1, n1, uDoc, k);
        }
    }
}
function handleSet(uDoc, setMap) {
    for (var k in setMap) {
        var v = setMap[k];
        var _a = __read(resolve(uDoc, k, { force: true }), 2), o = _a[0], n = _a[1];
        if (o && n.length === 1) {
            o[n[0]] = v;
        }
        else {
            logError(o, n, uDoc, k);
        }
    }
}
function handleInc(uDoc, incMap) {
    for (var k in incMap) {
        var v = incMap[k];
        var _a = __read(resolve(uDoc, k, { force: true }), 2), o = _a[0], n = _a[1];
        if (o && n.length === 1) {
            if (typeof o[n[0]] !== 'number') {
                o[n[0]] = 0;
            }
            o[n[0]] += v;
        }
        else {
            logError(o, n, uDoc, k);
        }
    }
}
function handleUnset(uDoc, unsetMap) {
    for (var k in unsetMap) {
        var _a = __read(resolve(uDoc, k), 2), o = _a[0], n = _a[1];
        if (o && n.length === 1) {
            delete o[n[0]];
        }
        else {
            logError(o, n, uDoc, k);
        }
    }
}
function handlePull(uDoc, orig, pullMap) {
    var e_1, _a;
    for (var pathk in pullMap) {
        var pullValues = pullMap[pathk];
        var _loop_1 = function (vk) {
            var _b = __read(resolve(uDoc, pathk), 2), o = _b[0], n = _b[1];
            var _c = __read(resolve(orig, pathk), 2), o2 = _c[0], n2 = _c[1];
            if (o && n.length === 1) {
                var index = o2[n2[0]].findIndex(function (z) { return JSON.stringify(z) === JSON.stringify(vk); });
                if (index !== -1) {
                    o[n[0]].splice(index, 1);
                }
            }
            else {
                logError(o, n, uDoc, pathk);
            }
        };
        try {
            for (var pullValues_1 = (e_1 = void 0, __values(pullValues)), pullValues_1_1 = pullValues_1.next(); !pullValues_1_1.done; pullValues_1_1 = pullValues_1.next()) {
                var vk = pullValues_1_1.value;
                _loop_1(vk);
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (pullValues_1_1 && !pullValues_1_1.done && (_a = pullValues_1.return)) _a.call(pullValues_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
    }
}
function handlePush(uDoc, pushMap) {
    var e_2, _a;
    for (var pathk in pushMap) {
        var pushValues = pushMap[pathk];
        // Normalize ─ accept $each, scalar, or array
        var values = [];
        if (Array.isArray(pushValues)) {
            values = pushValues;
        }
        else if (pushValues && typeof pushValues === 'object') {
            if (Array.isArray(pushValues.$each)) {
                values = pushValues.$each; // canonical `$each`
            }
            else {
                values = [pushValues]; // single element object
            }
        }
        else {
            values = [pushValues]; // scalar push
        }
        try {
            for (var values_1 = (e_2 = void 0, __values(values)), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) {
                var vk = values_1_1.value;
                var _b = __read(resolve(uDoc, pathk), 2), o = _b[0], n = _b[1];
                if (o && n.length === 1 && Array.isArray(o[n[0]])) {
                    o[n[0]].push(vk);
                }
                else {
                    logError(o, n, uDoc, pathk);
                }
            }
        }
        catch (e_2_1) { e_2 = { error: e_2_1 }; }
        finally {
            try {
                if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1);
            }
            finally { if (e_2) throw e_2.error; }
        }
    }
}
function logError(o, n, uDoc, key) {
    console.log('Error', o + '/' + n + ' - couldn\'t resolve for ' + JSON.stringify(uDoc) + ' ' + key);
}
function resolve(a, path, options) {
    if (options === void 0) { options = {}; }
    var stack = arrize(path);
    var last = [];
    if (stack.length > 0) {
        last.unshift(stack.pop());
    }
    var e = a;
    while (stack.length > 0) {
        var k = stack.shift();
        if (e[k] !== void 0) {
            e = e[k];
        }
        else {
            if (!options.force) {
                last.unshift(k);
                last = stack.concat(last);
                break;
            }
            var isNextNum = typeof stack[0] === 'number';
            var isLastNum = stack.length === 0 && typeof last[0] === 'number';
            e[k] = isNextNum || isLastNum ? [] : {};
            e = e[k];
        }
    }
    return [e, last];
}
function arrize(path, glue) {
    if (glue === void 0) { glue = '.'; }
    if (Array.isArray(path)) {
        return path.slice();
    }
    if (path === void 0 || path === null || path === false || path === '') {
        return [];
    }
    return path.toString().split(glue)
        .map(function (e) {
        if ([void 0, null, false, ''].includes(e)) {
            return null;
        }
        return e.toString();
    })
        .filter(function (e) { return e !== null; });
}
function deepDiffDetails(object1, object2) {
    var obj1 = deepCopy(object1);
    var obj2 = deepCopy(object2);
    var diff = (0, deep_object_diff_1.detailedDiff)(obj1, obj2);
    var diffString = '';
    var formatValue = function (value) {
        if (value instanceof Date) {
            return moment(value).format('llll');
        }
        else if (typeof value === 'object' && value !== null) {
            Object.keys(value).filter(function (a) { return a.startsWith('_id') || a.startsWith('id_'); }).forEach(function (key) {
                delete value[key];
            });
            return JSON.stringify(value); // Pretty print objects
        }
        else {
            return typeof value === 'string' ? "\"".concat(value, "\"") : String(value);
        }
    };
    var customArrayDiff = function (arr1, arr2) {
        var arrayDiffResults = [];
        var maxLength = Math.max(arr1.length, arr2.length);
        for (var i = 0; i < maxLength; i++) {
            if (arr1[i] && !arr2[i]) {
                arrayDiffResults.push("Removed at index ".concat(i, ": ").concat(formatValue(arr1[i])));
            }
            else if (!arr1[i] && arr2[i]) {
                arrayDiffResults.push("Added at index ".concat(i, ": ").concat(formatValue(arr2[i])));
            }
            else if (arr1[i] && arr2[i] && JSON.stringify(arr1[i]) !== JSON.stringify(arr2[i])) {
                arrayDiffResults.push("Changed at index ".concat(i, ": {").concat(formatObjectDiff(arr1[i], arr2[i]), "}"));
            }
        }
        return arrayDiffResults.join(', ');
    };
    var formatObjectDiff = function (obj1, obj2) {
        if (obj1 === null || obj2 === null || typeof obj1 !== 'object' || typeof obj2 !== 'object') {
            return "from: ".concat(formatValue(obj1), " to: ").concat(formatValue(obj2));
        }
        if (Array.isArray(obj1) || Array.isArray(obj2)) {
            return customArrayDiff(Array.isArray(obj1) ? obj1 : [], Array.isArray(obj2) ? obj2 : []);
        }
        return Object.keys(__assign(__assign({}, obj1), obj2)).filter(function (a) { return !a.startsWith('_id') && !a.startsWith('id_'); }).map(function (key) {
            var obj1Val = obj1 ? obj1[key] : undefined;
            var obj2Val = obj2 ? obj2[key] : undefined;
            return "".concat(toTitleCase(key), ": ").concat(formatObjectDiff(obj1Val, obj2Val));
        }).join(', ');
    };
    // Handle each diff type
    ['added', 'deleted', 'updated'].forEach(function (diffType) {
        if (Object.keys(diff[diffType]).length > 0) {
            var updates = Object.keys(diff[diffType])
                .filter(function (key) { return diffType === 'added' ? true : (diffType === 'deleted' ? !diff['added'][key] : !diff['added'][key] && !diff['deleted'][key]); })
                .filter(function (key) { return !key.startsWith('_id') && !key.startsWith('id_'); })
                .map(function (key) { return "\n".concat(toTitleCase(key), ": ").concat(formatObjectDiff(obj1[key], obj2[key])); })
                .join(',');
            diffString += updates ? "".concat(updates) : '';
        }
    });
    return diffString;
}
function getBinarySize(string) {
    if (string) {
        return Buffer.byteLength(string, 'utf8');
    }
    else {
        return 0;
    }
}
function pad(num, size) {
    var s = num + '';
    while (s.length < size) {
        s = '0' + s;
    }
    return s;
}
function randomString(length, chars) {
    var result = '';
    for (var i = length; i > 0; --i) {
        result += chars[Math.floor(Math.random() * chars.length)];
    }
    return result;
}
function toTitleCase(str) {
    return str.replace(/\w\S*/g, function (txt) {
        return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    });
}
function isObject(item) {
    return (item && typeof item === 'object' && !Array.isArray(item));
}
function mergeDeep(target) {
    var _a, _b;
    var sources = [];
    for (var _i = 1; _i < arguments.length; _i++) {
        sources[_i - 1] = arguments[_i];
    }
    if (!sources.length) {
        return target;
    }
    var source = sources.shift();
    if (isObject(target) && isObject(source)) {
        for (var key in source) {
            if (isObject(source[key])) {
                if (!target[key]) {
                    Object.assign(target, (_a = {}, _a[key] = {}, _a));
                }
                mergeDeep(target[key], source[key]);
            }
            else {
                Object.assign(target, (_b = {}, _b[key] = source[key], _b));
            }
        }
    }
    return mergeDeep.apply(void 0, __spreadArray([target], __read(sources), false));
}
function generateSearchTokensFromFields(doc, fields) {
    var tokens = new Set();
    fields.forEach(function (field) {
        var val = doc[field];
        if (typeof val === 'string' && val.trim().length) {
            var words = val.toLowerCase().split(/\s+/);
            words.forEach(function (word) {
                for (var i = 3; i <= word.length; i++) {
                    tokens.add(word.slice(0, i)); // prefix-based tokens only
                }
            });
        }
    });
    return Array.from(tokens);
}

//# sourceMappingURL=common.js.map
