364 lines
11 KiB
JavaScript
364 lines
11 KiB
JavaScript
|
|
var __defProp = Object.defineProperty;
|
||
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||
|
|
var __export = (target, all) => {
|
||
|
|
for (var name in all)
|
||
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
||
|
|
};
|
||
|
|
var __copyProps = (to, from, except, desc) => {
|
||
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
||
|
|
for (let key of __getOwnPropNames(from))
|
||
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
||
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||
|
|
}
|
||
|
|
return to;
|
||
|
|
};
|
||
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||
|
|
var core_exports = {};
|
||
|
|
__export(core_exports, {
|
||
|
|
ComputeOptions: () => ComputeOptions,
|
||
|
|
Context: () => Context,
|
||
|
|
OperatorType: () => OperatorType,
|
||
|
|
ProcessingMode: () => ProcessingMode,
|
||
|
|
computeValue: () => computeValue,
|
||
|
|
getOperator: () => getOperator,
|
||
|
|
initOptions: () => initOptions,
|
||
|
|
redact: () => redact,
|
||
|
|
useOperators: () => useOperators
|
||
|
|
});
|
||
|
|
module.exports = __toCommonJS(core_exports);
|
||
|
|
var import_util = require("./util");
|
||
|
|
var ProcessingMode = /* @__PURE__ */ ((ProcessingMode2) => {
|
||
|
|
ProcessingMode2[ProcessingMode2["CLONE_OFF"] = 0] = "CLONE_OFF";
|
||
|
|
ProcessingMode2[ProcessingMode2["CLONE_INPUT"] = 1] = "CLONE_INPUT";
|
||
|
|
ProcessingMode2[ProcessingMode2["CLONE_OUTPUT"] = 2] = "CLONE_OUTPUT";
|
||
|
|
ProcessingMode2[ProcessingMode2["CLONE_ALL"] = 3] = "CLONE_ALL";
|
||
|
|
return ProcessingMode2;
|
||
|
|
})(ProcessingMode || {});
|
||
|
|
class ComputeOptions {
|
||
|
|
#options;
|
||
|
|
/** Reference to the root object when processing subgraphs of the object. */
|
||
|
|
#root;
|
||
|
|
#local;
|
||
|
|
constructor(options, root, local) {
|
||
|
|
this.#options = options;
|
||
|
|
this.update(root, local);
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Initialize new ComputeOptions.
|
||
|
|
* @returns {ComputeOptions}
|
||
|
|
*/
|
||
|
|
static init(options, root, local) {
|
||
|
|
return !(options instanceof ComputeOptions) ? new ComputeOptions(options, root, local) : new ComputeOptions(options.#options, options.root ?? root, {
|
||
|
|
...options.#local,
|
||
|
|
...local,
|
||
|
|
variables: Object.assign(
|
||
|
|
{},
|
||
|
|
options.#local?.variables,
|
||
|
|
local?.variables
|
||
|
|
)
|
||
|
|
});
|
||
|
|
}
|
||
|
|
/**
|
||
|
|
* Updates the internal state.
|
||
|
|
*
|
||
|
|
* @param root The new root context for this object.
|
||
|
|
* @param local The new local state to merge into current if it exists.
|
||
|
|
* @returns
|
||
|
|
*/
|
||
|
|
update(root, local) {
|
||
|
|
this.#root = root;
|
||
|
|
const variables = Object.assign(
|
||
|
|
{},
|
||
|
|
this.#local?.variables,
|
||
|
|
local?.variables
|
||
|
|
);
|
||
|
|
if (Object.keys(variables).length) {
|
||
|
|
this.#local = { ...local, variables };
|
||
|
|
} else {
|
||
|
|
this.#local = local ?? {};
|
||
|
|
}
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
getOptions() {
|
||
|
|
return Object.freeze({
|
||
|
|
...this.#options,
|
||
|
|
context: Context.from(this.#options.context)
|
||
|
|
});
|
||
|
|
}
|
||
|
|
get root() {
|
||
|
|
return this.#root;
|
||
|
|
}
|
||
|
|
get local() {
|
||
|
|
return this.#local;
|
||
|
|
}
|
||
|
|
get idKey() {
|
||
|
|
return this.#options.idKey;
|
||
|
|
}
|
||
|
|
get collation() {
|
||
|
|
return this.#options?.collation;
|
||
|
|
}
|
||
|
|
get processingMode() {
|
||
|
|
return this.#options?.processingMode || 0 /* CLONE_OFF */;
|
||
|
|
}
|
||
|
|
get useStrictMode() {
|
||
|
|
return this.#options?.useStrictMode;
|
||
|
|
}
|
||
|
|
get scriptEnabled() {
|
||
|
|
return this.#options?.scriptEnabled;
|
||
|
|
}
|
||
|
|
get useGlobalContext() {
|
||
|
|
return this.#options?.useGlobalContext;
|
||
|
|
}
|
||
|
|
get hashFunction() {
|
||
|
|
return this.#options?.hashFunction;
|
||
|
|
}
|
||
|
|
get collectionResolver() {
|
||
|
|
return this.#options?.collectionResolver;
|
||
|
|
}
|
||
|
|
get jsonSchemaValidator() {
|
||
|
|
return this.#options?.jsonSchemaValidator;
|
||
|
|
}
|
||
|
|
get variables() {
|
||
|
|
return this.#options?.variables;
|
||
|
|
}
|
||
|
|
get context() {
|
||
|
|
return this.#options?.context;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function initOptions(options) {
|
||
|
|
return options instanceof ComputeOptions ? options.getOptions() : Object.freeze({
|
||
|
|
idKey: "_id",
|
||
|
|
scriptEnabled: true,
|
||
|
|
useStrictMode: true,
|
||
|
|
useGlobalContext: true,
|
||
|
|
processingMode: 0 /* CLONE_OFF */,
|
||
|
|
...options,
|
||
|
|
context: options?.context ? Context.from(options?.context) : Context.init()
|
||
|
|
});
|
||
|
|
}
|
||
|
|
var OperatorType = /* @__PURE__ */ ((OperatorType2) => {
|
||
|
|
OperatorType2["ACCUMULATOR"] = "accumulator";
|
||
|
|
OperatorType2["EXPRESSION"] = "expression";
|
||
|
|
OperatorType2["PIPELINE"] = "pipeline";
|
||
|
|
OperatorType2["PROJECTION"] = "projection";
|
||
|
|
OperatorType2["QUERY"] = "query";
|
||
|
|
OperatorType2["WINDOW"] = "window";
|
||
|
|
return OperatorType2;
|
||
|
|
})(OperatorType || {});
|
||
|
|
class Context {
|
||
|
|
#operators = /* @__PURE__ */ new Map();
|
||
|
|
constructor() {
|
||
|
|
}
|
||
|
|
static init() {
|
||
|
|
return new Context();
|
||
|
|
}
|
||
|
|
static from(ctx) {
|
||
|
|
const instance = Context.init();
|
||
|
|
if ((0, import_util.isNil)(ctx)) return instance;
|
||
|
|
ctx.#operators.forEach((v, k) => instance.addOperators(k, v));
|
||
|
|
return instance;
|
||
|
|
}
|
||
|
|
addOperators(type, operators) {
|
||
|
|
if (!this.#operators.has(type)) this.#operators.set(type, {});
|
||
|
|
for (const [name, fn] of Object.entries(operators)) {
|
||
|
|
if (!this.getOperator(type, name)) {
|
||
|
|
this.#operators.get(type)[name] = fn;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return this;
|
||
|
|
}
|
||
|
|
getOperator(type, name) {
|
||
|
|
const ops = this.#operators.get(type) ?? {};
|
||
|
|
return ops[name] ?? null;
|
||
|
|
}
|
||
|
|
addAccumulatorOps(ops) {
|
||
|
|
return this.addOperators("accumulator", ops);
|
||
|
|
}
|
||
|
|
addExpressionOps(ops) {
|
||
|
|
return this.addOperators("expression", ops);
|
||
|
|
}
|
||
|
|
addQueryOps(ops) {
|
||
|
|
return this.addOperators("query", ops);
|
||
|
|
}
|
||
|
|
addPipelineOps(ops) {
|
||
|
|
return this.addOperators("pipeline", ops);
|
||
|
|
}
|
||
|
|
addProjectionOps(ops) {
|
||
|
|
return this.addOperators("projection", ops);
|
||
|
|
}
|
||
|
|
addWindowOps(ops) {
|
||
|
|
return this.addOperators("window", ops);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const GLOBAL_CONTEXT = Context.init();
|
||
|
|
function useOperators(type, operators) {
|
||
|
|
for (const [name, fn] of Object.entries(operators)) {
|
||
|
|
(0, import_util.assert)(
|
||
|
|
(0, import_util.isFunction)(fn) && (0, import_util.isOperator)(name),
|
||
|
|
`'${name}' is not a valid operator`
|
||
|
|
);
|
||
|
|
const currentFn = getOperator(type, name, null);
|
||
|
|
(0, import_util.assert)(
|
||
|
|
!currentFn || fn === currentFn,
|
||
|
|
`${name} already exists for '${type}' operators. Cannot change operator function once registered.`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
switch (type) {
|
||
|
|
case "accumulator":
|
||
|
|
GLOBAL_CONTEXT.addAccumulatorOps(operators);
|
||
|
|
break;
|
||
|
|
case "expression":
|
||
|
|
GLOBAL_CONTEXT.addExpressionOps(operators);
|
||
|
|
break;
|
||
|
|
case "pipeline":
|
||
|
|
GLOBAL_CONTEXT.addPipelineOps(operators);
|
||
|
|
break;
|
||
|
|
case "projection":
|
||
|
|
GLOBAL_CONTEXT.addProjectionOps(operators);
|
||
|
|
break;
|
||
|
|
case "query":
|
||
|
|
GLOBAL_CONTEXT.addQueryOps(operators);
|
||
|
|
break;
|
||
|
|
case "window":
|
||
|
|
GLOBAL_CONTEXT.addWindowOps(operators);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function getOperator(type, name, options) {
|
||
|
|
const { context: ctx, useGlobalContext: fallback } = options || {};
|
||
|
|
const fn = ctx ? ctx.getOperator(type, name) : null;
|
||
|
|
return !fn && fallback ? GLOBAL_CONTEXT.getOperator(type, name) : fn;
|
||
|
|
}
|
||
|
|
function computeValue(obj, expr, operator, options) {
|
||
|
|
const copts = ComputeOptions.init(options, obj);
|
||
|
|
return !!operator && (0, import_util.isOperator)(operator) ? computeOperator(obj, expr, operator, copts) : computeExpression(obj, expr, copts);
|
||
|
|
}
|
||
|
|
const SYSTEM_VARS = ["$$ROOT", "$$CURRENT", "$$REMOVE", "$$NOW"];
|
||
|
|
function computeExpression(obj, expr, options) {
|
||
|
|
if ((0, import_util.isString)(expr) && expr.length > 0 && expr[0] === "$") {
|
||
|
|
if (REDACT_ACTIONS.includes(expr)) return expr;
|
||
|
|
let ctx = options.root;
|
||
|
|
const arr = expr.split(".");
|
||
|
|
if (SYSTEM_VARS.includes(arr[0])) {
|
||
|
|
switch (arr[0]) {
|
||
|
|
case "$$ROOT":
|
||
|
|
break;
|
||
|
|
case "$$CURRENT":
|
||
|
|
ctx = obj;
|
||
|
|
break;
|
||
|
|
case "$$REMOVE":
|
||
|
|
ctx = void 0;
|
||
|
|
break;
|
||
|
|
case "$$NOW":
|
||
|
|
ctx = /* @__PURE__ */ new Date();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
expr = expr.slice(arr[0].length + 1);
|
||
|
|
} else if (arr[0].slice(0, 2) === "$$") {
|
||
|
|
ctx = Object.assign(
|
||
|
|
{},
|
||
|
|
// global vars
|
||
|
|
options.variables,
|
||
|
|
// current item is added before local variables because the binding may be changed.
|
||
|
|
{ this: obj },
|
||
|
|
// local vars
|
||
|
|
options?.local?.variables
|
||
|
|
);
|
||
|
|
const name = arr[0].slice(2);
|
||
|
|
(0, import_util.assert)((0, import_util.has)(ctx, name), `Use of undefined variable: ${name}`);
|
||
|
|
expr = expr.slice(2);
|
||
|
|
} else {
|
||
|
|
expr = expr.slice(1);
|
||
|
|
}
|
||
|
|
return expr === "" ? ctx : (0, import_util.resolve)(ctx, expr);
|
||
|
|
}
|
||
|
|
if ((0, import_util.isArray)(expr)) {
|
||
|
|
return expr.map((item) => computeExpression(obj, item, options));
|
||
|
|
}
|
||
|
|
if ((0, import_util.isObject)(expr)) {
|
||
|
|
const result = {};
|
||
|
|
const elems = Object.entries(expr);
|
||
|
|
for (const [key, val] of elems) {
|
||
|
|
if ((0, import_util.isOperator)(key)) {
|
||
|
|
(0, import_util.assert)(elems.length == 1, "expression must have single operator.");
|
||
|
|
return computeOperator(obj, val, key, options);
|
||
|
|
}
|
||
|
|
result[key] = computeExpression(obj, val, options);
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
return expr;
|
||
|
|
}
|
||
|
|
function computeOperator(obj, expr, operator, options) {
|
||
|
|
const callExpression = getOperator(
|
||
|
|
"expression",
|
||
|
|
operator,
|
||
|
|
options
|
||
|
|
);
|
||
|
|
if (callExpression) return callExpression(obj, expr, options);
|
||
|
|
const callAccumulator = getOperator(
|
||
|
|
"accumulator",
|
||
|
|
operator,
|
||
|
|
options
|
||
|
|
);
|
||
|
|
(0, import_util.assert)(!!callAccumulator, `accumulator '${operator}' is not registered.`);
|
||
|
|
if (!(0, import_util.isArray)(obj)) {
|
||
|
|
obj = computeExpression(obj, expr, options);
|
||
|
|
expr = null;
|
||
|
|
}
|
||
|
|
(0, import_util.assert)((0, import_util.isArray)(obj), `arguments must resolve to array for ${operator}.`);
|
||
|
|
return callAccumulator(obj, expr, options);
|
||
|
|
}
|
||
|
|
const REDACT_ACTIONS = ["$$KEEP", "$$PRUNE", "$$DESCEND"];
|
||
|
|
function redact(obj, expr, options) {
|
||
|
|
const action = computeValue(obj, expr, null, options);
|
||
|
|
switch (action) {
|
||
|
|
case "$$KEEP":
|
||
|
|
return obj;
|
||
|
|
case "$$PRUNE":
|
||
|
|
return void 0;
|
||
|
|
case "$$DESCEND": {
|
||
|
|
if (!(0, import_util.has)(expr, "$cond")) return obj;
|
||
|
|
const output = {};
|
||
|
|
for (const [key, value] of Object.entries(obj)) {
|
||
|
|
if ((0, import_util.isArray)(value)) {
|
||
|
|
const res = new Array();
|
||
|
|
for (let elem of value) {
|
||
|
|
if ((0, import_util.isObject)(elem)) {
|
||
|
|
elem = redact(elem, expr, options.update(elem));
|
||
|
|
}
|
||
|
|
if (!(0, import_util.isNil)(elem)) res.push(elem);
|
||
|
|
}
|
||
|
|
output[key] = res;
|
||
|
|
} else if ((0, import_util.isObject)(value)) {
|
||
|
|
const res = redact(
|
||
|
|
value,
|
||
|
|
expr,
|
||
|
|
options.update(value)
|
||
|
|
);
|
||
|
|
if (!(0, import_util.isNil)(res)) output[key] = res;
|
||
|
|
} else {
|
||
|
|
output[key] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return output;
|
||
|
|
}
|
||
|
|
default:
|
||
|
|
return action;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// Annotate the CommonJS export names for ESM import in node:
|
||
|
|
0 && (module.exports = {
|
||
|
|
ComputeOptions,
|
||
|
|
Context,
|
||
|
|
OperatorType,
|
||
|
|
ProcessingMode,
|
||
|
|
computeValue,
|
||
|
|
getOperator,
|
||
|
|
initOptions,
|
||
|
|
redact,
|
||
|
|
useOperators
|
||
|
|
});
|