@memberjunction/realtime-widget 6.2.0-edge.6 → 6.2.0-edge.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/embed/chunks/UserViewEngine-ELH6XPEL.js +7 -0
- package/dist/embed/chunks/{chunk-N3BM2W5J.js → chunk-DAGQZKVY.js} +12348 -8244
- package/dist/embed/chunks/{chunk-ZX4SRAYD.js → chunk-DIDGGUK7.js} +548 -332
- package/dist/embed/chunks/{chunk-MRWWS7FK.js → chunk-HDCS43DU.js} +8429 -1733
- package/dist/embed/chunks/{chunk-VZ6KR7K2.js → chunk-JOGHPRST.js} +22 -1
- package/dist/embed/chunks/{chunk-RAIOBPHZ.js → chunk-L4OBOWDD.js} +70 -32
- package/dist/embed/chunks/{dist-P2JMWJFV.js → dist-BOIIDZ2X.js} +5 -5
- package/dist/embed/chunks/{guest-tool-relay-GAWXNIUD.js → guest-tool-relay-IAAOBPAS.js} +6 -6
- package/dist/embed/chunks/{guest-voice-mint-KJMP4VTK.js → guest-voice-mint-GPJQNDR3.js} +6 -6
- package/dist/embed/chunks/{realtime-voice-controller-PCOD3UBR.js → realtime-voice-controller-76SVXCXJ.js} +55 -24
- package/dist/embed/chunks/{runtime-widget-transport-AFGCQC7N.js → runtime-widget-transport-XT55WJB2.js} +10 -9
- package/dist/embed/mj-widget.js +6 -6
- package/dist/transport/runtime-widget-transport.d.ts.map +1 -1
- package/dist/transport/runtime-widget-transport.js +1 -0
- package/dist/transport/runtime-widget-transport.js.map +1 -1
- package/package.json +9 -9
- package/dist/embed/chunks/UserViewEngine-IVGLM2DH.js +0 -7
|
@@ -4146,7 +4146,7 @@ var require_lodash = __commonJS({
|
|
|
4146
4146
|
var tag = baseGetTag(value);
|
|
4147
4147
|
return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject(value);
|
|
4148
4148
|
}
|
|
4149
|
-
function
|
|
4149
|
+
function isFinite2(value) {
|
|
4150
4150
|
return typeof value == "number" && nativeIsFinite(value);
|
|
4151
4151
|
}
|
|
4152
4152
|
function isFunction2(value) {
|
|
@@ -5247,7 +5247,7 @@ var require_lodash = __commonJS({
|
|
|
5247
5247
|
lodash.isEqual = isEqual;
|
|
5248
5248
|
lodash.isEqualWith = isEqualWith;
|
|
5249
5249
|
lodash.isError = isError;
|
|
5250
|
-
lodash.isFinite =
|
|
5250
|
+
lodash.isFinite = isFinite2;
|
|
5251
5251
|
lodash.isFunction = isFunction2;
|
|
5252
5252
|
lodash.isInteger = isInteger;
|
|
5253
5253
|
lodash.isLength = isLength;
|
|
@@ -7821,6 +7821,7 @@ var ClassFactory = class _ClassFactory {
|
|
|
7821
7821
|
this._registrationCache = /* @__PURE__ */ new Map();
|
|
7822
7822
|
this._lazyLoaders = [];
|
|
7823
7823
|
this._reportedResolutionFailures = /* @__PURE__ */ new Set();
|
|
7824
|
+
this._reportedDuplicateCopies = /* @__PURE__ */ new Set();
|
|
7824
7825
|
}
|
|
7825
7826
|
static {
|
|
7826
7827
|
this.MAX_FALLBACK_REPORTS_PER_BASE = 3;
|
|
@@ -7930,6 +7931,10 @@ var ClassFactory = class _ClassFactory {
|
|
|
7930
7931
|
console.warn(`*** ClassFactory.Register: Registering class ${subClassName} for base class ${effectiveBaseClassName} and key/priority ${key}/${priority}. ${existing.length} registrations already exist for that combination. While this is allowed it is not desired and when matching class requests occur, we will simply use the LAST registration we happen to have which can lead to unintended behavior. ***`);
|
|
7931
7932
|
}
|
|
7932
7933
|
} else if (priority === 0 || priority === null || priority === void 0) {
|
|
7934
|
+
if (this.isDuplicateCopy(registrations, subClass, subClassName)) {
|
|
7935
|
+
this.warnDuplicateCopyOnce(subClassName, effectiveBaseClassName, key);
|
|
7936
|
+
return;
|
|
7937
|
+
}
|
|
7933
7938
|
let highestPriority = 0;
|
|
7934
7939
|
for (let i = 0; i < registrations.length; i++) {
|
|
7935
7940
|
if (registrations[i].Priority > highestPriority)
|
|
@@ -8002,6 +8007,37 @@ var ClassFactory = class _ClassFactory {
|
|
|
8002
8007
|
rootBucket.push(reg);
|
|
8003
8008
|
}
|
|
8004
8009
|
}
|
|
8010
|
+
/**
|
|
8011
|
+
* True when `subClass` is a second physical copy of a class that is already registered for
|
|
8012
|
+
* this base class + key: a distinct constructor with the same name and the same source text.
|
|
8013
|
+
*
|
|
8014
|
+
* A class whose parent has its own name (`class X extends X`) is an override of that parent,
|
|
8015
|
+
* not a copy, even when its source matches an earlier registration, so it is never treated as
|
|
8016
|
+
* a duplicate. Name alone cannot tell a copy from a different class that happens to share the
|
|
8017
|
+
* name; the identical source is what separates them.
|
|
8018
|
+
*/
|
|
8019
|
+
isDuplicateCopy(registrations, subClass, subClassName) {
|
|
8020
|
+
if (typeof subClass !== "function") {
|
|
8021
|
+
return false;
|
|
8022
|
+
}
|
|
8023
|
+
const parent = Object.getPrototypeOf(subClass);
|
|
8024
|
+
if (typeof parent === "function" && parent.name === subClassName) {
|
|
8025
|
+
return false;
|
|
8026
|
+
}
|
|
8027
|
+
const source = Function.prototype.toString.call(subClass);
|
|
8028
|
+
return registrations.some((r) => r.SubClass !== subClass && typeof r.SubClass === "function" && r.SubClass.name === subClassName && Function.prototype.toString.call(r.SubClass) === source);
|
|
8029
|
+
}
|
|
8030
|
+
/**
|
|
8031
|
+
* Warns, once per base class + key + class, that a second copy of a registration was ignored.
|
|
8032
|
+
*/
|
|
8033
|
+
warnDuplicateCopyOnce(subClassName, effectiveBaseClassName, key) {
|
|
8034
|
+
const reportKey = `${effectiveBaseClassName}|${key ?? ""}|${subClassName}`;
|
|
8035
|
+
if (this._reportedDuplicateCopies.has(reportKey)) {
|
|
8036
|
+
return;
|
|
8037
|
+
}
|
|
8038
|
+
this._reportedDuplicateCopies.add(reportKey);
|
|
8039
|
+
console.warn(`*** ClassFactory.Register: ignored a second copy of ${subClassName} for base class ${effectiveBaseClassName} with key '${key}'. Its source is identical to a class already registered under that name, which means the package that defines it is installed more than once (for example, nested by peer-dependency resolution). The first registration is kept, so any subclass registered after it still wins. Deduplicate the package to remove this warning. ***`);
|
|
8040
|
+
}
|
|
8005
8041
|
/**
|
|
8006
8042
|
* Emits a single warning when an incoming registration collides with one or more EXISTING
|
|
8007
8043
|
* registrations for the same base class + key that are **not in its inheritance chain**.
|
|
@@ -8091,6 +8127,23 @@ var ClassFactory = class _ClassFactory {
|
|
|
8091
8127
|
}
|
|
8092
8128
|
};
|
|
8093
8129
|
}
|
|
8130
|
+
/**
|
|
8131
|
+
* Describes a key that has no registration for `baseClass`, listing every key that IS
|
|
8132
|
+
* registered for it. Callers that look a class up with `GetRegistration`/`GetRegistrationAsync`
|
|
8133
|
+
* and get nothing back can use this to build an error that shows the known keys next to the
|
|
8134
|
+
* requested one, so a typo or a missing (uninstalled or tree-shaken) class is visible.
|
|
8135
|
+
*
|
|
8136
|
+
* @param baseClass - the base class that was looked up
|
|
8137
|
+
* @param key - the key that was requested
|
|
8138
|
+
* @returns e.g. `ClassFactory: no registration found for base class 'X' with key 'Y'. Registered keys for 'X': 'A', 'B'.`
|
|
8139
|
+
*/
|
|
8140
|
+
DescribeMissingRegistration(baseClass, key) {
|
|
8141
|
+
const baseClassName = baseClass.name;
|
|
8142
|
+
const known = this.GetAllRegistrations(baseClass).map((r) => r.Key).filter((k) => k != null);
|
|
8143
|
+
const uniqueKnown = Array.from(new Set(known)).sort();
|
|
8144
|
+
const knownText = uniqueKnown.length > 0 ? uniqueKnown.map((k) => `'${k}'`).join(", ") : "(none)";
|
|
8145
|
+
return `ClassFactory: no registration found for base class '${baseClassName}' with key '${key ?? "(null)"}'. Registered keys for '${baseClassName}': ${knownText}.`;
|
|
8146
|
+
}
|
|
8094
8147
|
/**
|
|
8095
8148
|
* Builds the diagnostic message for a failed key resolution. The registered-key list is the
|
|
8096
8149
|
* highest-value part: a typo'd or tree-shaken key is immediately obvious next to the keys the
|
|
@@ -8098,10 +8151,7 @@ var ClassFactory = class _ClassFactory {
|
|
|
8098
8151
|
*/
|
|
8099
8152
|
describeResolutionFailure(baseClass, key, requiresSubclass) {
|
|
8100
8153
|
const baseClassName = baseClass.name;
|
|
8101
|
-
|
|
8102
|
-
const uniqueKnown = Array.from(new Set(known)).sort();
|
|
8103
|
-
const knownText = uniqueKnown.length > 0 ? uniqueKnown.map((k) => `'${k}'`).join(", ") : "(none)";
|
|
8104
|
-
return `ClassFactory: no registration found for base class '${baseClassName}' with key '${key ?? "(null)"}'. Registered keys for '${baseClassName}': ${knownText}. RequiresSubclass=${requiresSubclass}. ` + (requiresSubclass ? `'${baseClassName}' is marked '@RequiresSubclass()', so it CANNOT be used as a fallback \u2014 no instance was created. Likely causes: a typo in the key, a class that was never imported (tree-shaken out \u2014 check the class-registration manifest), or a missing @RegisterClass decorator.` : `Falling back to an instance of '${baseClassName}' itself. If that base cannot function standalone, apply the '@RequiresSubclass()' decorator to it so this becomes a hard error.`);
|
|
8154
|
+
return this.DescribeMissingRegistration(baseClass, key) + ` RequiresSubclass=${requiresSubclass}. ` + (requiresSubclass ? `'${baseClassName}' is marked '@RequiresSubclass()', so it CANNOT be used as a fallback \u2014 no instance was created. Likely causes: a typo in the key, a class that was never imported (tree-shaken out \u2014 check the class-registration manifest), or a missing @RegisterClass decorator.` : `Falling back to an instance of '${baseClassName}' itself. If that base cannot function standalone, apply the '@RequiresSubclass()' decorator to it so this becomes a hard error.`);
|
|
8105
8155
|
}
|
|
8106
8156
|
/**
|
|
8107
8157
|
* Emits the fallback diagnostic exactly once per (baseClass, key) pair — resolution happens on
|
|
@@ -9320,9 +9370,7 @@ function BytesToBase64(bytes) {
|
|
|
9320
9370
|
return GetBase64Codec().Encode(bytes);
|
|
9321
9371
|
}
|
|
9322
9372
|
var FLOAT32_VECTOR_BYTES_PER_VALUE = 4;
|
|
9323
|
-
|
|
9324
|
-
return new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
|
|
9325
|
-
}
|
|
9373
|
+
var IS_LITTLE_ENDIAN = new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
|
|
9326
9374
|
var TypedArrayFloat32Codec = {
|
|
9327
9375
|
Name: "typed array (little-endian host)",
|
|
9328
9376
|
Encode(values) {
|
|
@@ -9350,7 +9398,7 @@ var DataViewFloat32Codec = {
|
|
|
9350
9398
|
return values;
|
|
9351
9399
|
}
|
|
9352
9400
|
};
|
|
9353
|
-
var float32Codec =
|
|
9401
|
+
var float32Codec = IS_LITTLE_ENDIAN ? TypedArrayFloat32Codec : DataViewFloat32Codec;
|
|
9354
9402
|
function Float32VectorToBytes(values) {
|
|
9355
9403
|
return float32Codec.Encode(values);
|
|
9356
9404
|
}
|
|
@@ -15083,8 +15131,8 @@ function parse3(input, options) {
|
|
|
15083
15131
|
}
|
|
15084
15132
|
|
|
15085
15133
|
// ../../MJGlobal/dist/SafeExpressionEvaluator.js
|
|
15086
|
-
var
|
|
15087
|
-
["Math", /* @__PURE__ */ new Set([
|
|
15134
|
+
var APPROVED_EXPRESSION_GLOBALS = /* @__PURE__ */ new Map([
|
|
15135
|
+
["Math", { Callable: false, Methods: /* @__PURE__ */ new Set([
|
|
15088
15136
|
"abs",
|
|
15089
15137
|
"ceil",
|
|
15090
15138
|
"floor",
|
|
@@ -15101,275 +15149,159 @@ var SAFE_GLOBAL_NAMESPACE_METHODS = /* @__PURE__ */ new Map([
|
|
|
15101
15149
|
"log10",
|
|
15102
15150
|
"exp",
|
|
15103
15151
|
"hypot"
|
|
15104
|
-
])],
|
|
15105
|
-
["JSON", /* @__PURE__ */ new Set(["parse", "stringify"])],
|
|
15106
|
-
["Object", /* @__PURE__ */ new Set(["keys", "values", "entries"])],
|
|
15107
|
-
["Array", /* @__PURE__ */ new Set(["isArray"])],
|
|
15108
|
-
["Number", /* @__PURE__ */ new Set(["isInteger", "isFinite", "isNaN", "isSafeInteger", "parseInt", "parseFloat"])],
|
|
15109
|
-
["
|
|
15152
|
+
]) }],
|
|
15153
|
+
["JSON", { Callable: false, Methods: /* @__PURE__ */ new Set(["parse", "stringify"]) }],
|
|
15154
|
+
["Object", { Callable: false, Methods: /* @__PURE__ */ new Set(["keys", "values", "entries"]) }],
|
|
15155
|
+
["Array", { Callable: false, Methods: /* @__PURE__ */ new Set(["isArray"]) }],
|
|
15156
|
+
["Number", { Callable: true, Methods: /* @__PURE__ */ new Set(["isInteger", "isFinite", "isNaN", "isSafeInteger", "parseInt", "parseFloat"]) }],
|
|
15157
|
+
["String", { Callable: true, Methods: /* @__PURE__ */ new Set() }],
|
|
15158
|
+
["Boolean", { Callable: true, Methods: /* @__PURE__ */ new Set() }],
|
|
15159
|
+
["Date", { Callable: false, Methods: /* @__PURE__ */ new Set(["now"]) }],
|
|
15160
|
+
["parseInt", { Callable: true, Methods: /* @__PURE__ */ new Set() }],
|
|
15161
|
+
["parseFloat", { Callable: true, Methods: /* @__PURE__ */ new Set() }],
|
|
15162
|
+
["isNaN", { Callable: true, Methods: /* @__PURE__ */ new Set() }],
|
|
15163
|
+
["isFinite", { Callable: true, Methods: /* @__PURE__ */ new Set() }]
|
|
15110
15164
|
]);
|
|
15111
|
-
var
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
"
|
|
15117
|
-
"
|
|
15118
|
-
"
|
|
15165
|
+
var APPROVED_VALUE_GLOBALS = /* @__PURE__ */ new Set(["undefined", "NaN", "Infinity"]);
|
|
15166
|
+
var SAFE_GLOBAL_NAMESPACE_METHODS = new Map([...APPROVED_EXPRESSION_GLOBALS].filter(([, g]) => g.Methods.size > 0).map(([name, g]) => [name, g.Methods]));
|
|
15167
|
+
var SAFE_GLOBAL_CALLABLES = new Set([...APPROVED_EXPRESSION_GLOBALS].filter(([, g]) => g.Callable).map(([name]) => name));
|
|
15168
|
+
var SAFE_EXPRESSION_GLOBALS = new Set(APPROVED_EXPRESSION_GLOBALS.keys());
|
|
15169
|
+
var DANGEROUS_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
15170
|
+
"eval",
|
|
15171
|
+
"Function",
|
|
15172
|
+
"globalThis",
|
|
15173
|
+
"global",
|
|
15174
|
+
"window",
|
|
15175
|
+
"document",
|
|
15176
|
+
"process",
|
|
15177
|
+
"require",
|
|
15178
|
+
"module",
|
|
15179
|
+
"exports",
|
|
15180
|
+
"__dirname",
|
|
15181
|
+
"__filename",
|
|
15182
|
+
"self",
|
|
15183
|
+
"top",
|
|
15184
|
+
"parent",
|
|
15185
|
+
"frames",
|
|
15186
|
+
"Reflect",
|
|
15187
|
+
"Proxy",
|
|
15188
|
+
"WebAssembly",
|
|
15189
|
+
"Atomics",
|
|
15190
|
+
"SharedArrayBuffer",
|
|
15191
|
+
"import",
|
|
15192
|
+
"arguments",
|
|
15193
|
+
"constructor",
|
|
15194
|
+
"prototype",
|
|
15195
|
+
"__proto__"
|
|
15119
15196
|
]);
|
|
15120
|
-
var
|
|
15121
|
-
|
|
15122
|
-
|
|
15197
|
+
var DANGEROUS_PROPERTY_NAMES = /* @__PURE__ */ new Set([
|
|
15198
|
+
"constructor",
|
|
15199
|
+
"__proto__",
|
|
15200
|
+
"prototype"
|
|
15123
15201
|
]);
|
|
15124
|
-
var
|
|
15125
|
-
|
|
15126
|
-
|
|
15127
|
-
|
|
15128
|
-
|
|
15129
|
-
|
|
15130
|
-
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15152
|
-
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
|
|
15163
|
-
|
|
15164
|
-
|
|
15165
|
-
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
this.SAFE_METHODS = [
|
|
15187
|
-
"length",
|
|
15188
|
-
"includes",
|
|
15189
|
-
"startsWith",
|
|
15190
|
-
"endsWith",
|
|
15191
|
-
"indexOf",
|
|
15192
|
-
"lastIndexOf",
|
|
15193
|
-
"toLowerCase",
|
|
15194
|
-
"toUpperCase",
|
|
15195
|
-
"trim",
|
|
15196
|
-
"trimStart",
|
|
15197
|
-
"trimEnd",
|
|
15198
|
-
"toString",
|
|
15199
|
-
"valueOf",
|
|
15200
|
-
"some",
|
|
15201
|
-
"every",
|
|
15202
|
-
"find",
|
|
15203
|
-
"filter",
|
|
15204
|
-
"map",
|
|
15205
|
-
"reduce"
|
|
15206
|
-
];
|
|
15207
|
-
}
|
|
15208
|
-
/**
|
|
15209
|
-
* Evaluates a boolean expression against a context object
|
|
15210
|
-
*
|
|
15211
|
-
* @param {string} expression - The boolean expression to evaluate
|
|
15212
|
-
* @param {Record<string, any>} context - The context object containing variables
|
|
15213
|
-
* @param {boolean} [enableDiagnostics=false] - Whether to include diagnostic information
|
|
15214
|
-
*
|
|
15215
|
-
* @returns {ExpressionEvaluationResult} The evaluation result
|
|
15216
|
-
*/
|
|
15217
|
-
Evaluate(expression, context2, enableDiagnostics = false) {
|
|
15218
|
-
const startTime = Date.now();
|
|
15219
|
-
try {
|
|
15220
|
-
const validationError = this.validateExpression(expression);
|
|
15221
|
-
if (validationError) {
|
|
15222
|
-
return {
|
|
15223
|
-
success: false,
|
|
15224
|
-
error: validationError,
|
|
15225
|
-
diagnostics: enableDiagnostics ? {
|
|
15226
|
-
expression,
|
|
15227
|
-
context: context2,
|
|
15228
|
-
evaluationTime: Date.now() - startTime
|
|
15229
|
-
} : void 0
|
|
15230
|
-
};
|
|
15231
|
-
}
|
|
15232
|
-
const safeContext = this.createSafeContext(context2);
|
|
15233
|
-
const contextKeys = Object.keys(safeContext);
|
|
15234
|
-
const contextValues = contextKeys.map((key) => safeContext[key]);
|
|
15235
|
-
const cacheKey = `${expression}|${contextKeys.join(",")}`;
|
|
15236
|
-
let evaluator = this._compiledExpressionCache.Get(cacheKey);
|
|
15237
|
-
if (!evaluator) {
|
|
15238
|
-
const functionBody = `
|
|
15239
|
-
"use strict";
|
|
15240
|
-
try {
|
|
15241
|
-
return Boolean(${expression});
|
|
15242
|
-
} catch (e) {
|
|
15243
|
-
throw new Error('Expression evaluation failed: ' + e.message);
|
|
15244
|
-
}
|
|
15245
|
-
`;
|
|
15246
|
-
evaluator = new Function(...contextKeys, functionBody);
|
|
15247
|
-
this._compiledExpressionCache.Set(cacheKey, evaluator);
|
|
15248
|
-
}
|
|
15249
|
-
const result = evaluator(...contextValues);
|
|
15250
|
-
return {
|
|
15251
|
-
success: true,
|
|
15252
|
-
value: Boolean(result),
|
|
15253
|
-
diagnostics: enableDiagnostics ? {
|
|
15254
|
-
expression,
|
|
15255
|
-
context: context2,
|
|
15256
|
-
evaluationTime: Date.now() - startTime
|
|
15257
|
-
} : void 0
|
|
15258
|
-
};
|
|
15259
|
-
} catch (error) {
|
|
15260
|
-
return {
|
|
15261
|
-
success: false,
|
|
15262
|
-
error: error instanceof Error ? error.message : String(error),
|
|
15263
|
-
diagnostics: enableDiagnostics ? {
|
|
15264
|
-
expression,
|
|
15265
|
-
context: context2,
|
|
15266
|
-
evaluationTime: Date.now() - startTime
|
|
15267
|
-
} : void 0
|
|
15268
|
-
};
|
|
15269
|
-
}
|
|
15270
|
-
}
|
|
15271
|
-
/** @deprecated Use {@link Evaluate}. */
|
|
15272
|
-
evaluate(expression, context2, enableDiagnostics = false) {
|
|
15273
|
-
return this.Evaluate(expression, context2, enableDiagnostics);
|
|
15274
|
-
}
|
|
15275
|
-
/**
|
|
15276
|
-
* Validates an expression for safety by parsing it and walking the AST against
|
|
15277
|
-
* an allowlist. Returns an error message if any construct is not permitted.
|
|
15278
|
-
*
|
|
15279
|
-
* @param {string} expression - The expression to validate
|
|
15280
|
-
*
|
|
15281
|
-
* @returns {string | null} Error message if invalid, null if valid
|
|
15282
|
-
*
|
|
15283
|
-
* @private
|
|
15284
|
-
*/
|
|
15285
|
-
validateExpression(expression) {
|
|
15286
|
-
if (!expression || typeof expression !== "string") {
|
|
15287
|
-
return "Expression must be a non-empty string";
|
|
15288
|
-
}
|
|
15289
|
-
if (expression.length > 1e3) {
|
|
15290
|
-
return "Expression exceeds maximum length of 1000 characters";
|
|
15291
|
-
}
|
|
15292
|
-
let parenCount = 0;
|
|
15293
|
-
for (const char of expression) {
|
|
15294
|
-
if (char === "(")
|
|
15295
|
-
parenCount++;
|
|
15296
|
-
if (char === ")")
|
|
15297
|
-
parenCount--;
|
|
15298
|
-
if (parenCount < 0) {
|
|
15299
|
-
return "Unbalanced parentheses in expression";
|
|
15300
|
-
}
|
|
15301
|
-
}
|
|
15302
|
-
if (parenCount !== 0) {
|
|
15303
|
-
return "Unbalanced parentheses in expression";
|
|
15304
|
-
}
|
|
15305
|
-
if (expression.trim() === "") {
|
|
15306
|
-
return null;
|
|
15307
|
-
}
|
|
15308
|
-
let program;
|
|
15309
|
-
try {
|
|
15310
|
-
program = parse3(expression, { ecmaVersion: 2022 });
|
|
15311
|
-
} catch (e) {
|
|
15312
|
-
return this.forbidden(`it is not a parseable single expression (${e instanceof Error ? e.message : String(e)})`);
|
|
15313
|
-
}
|
|
15314
|
-
if (program.body.length !== 1 || program.body[0].type !== "ExpressionStatement") {
|
|
15315
|
-
return this.forbidden("only a single expression is permitted \u2014 no statements, blocks, or semicolons");
|
|
15316
|
-
}
|
|
15317
|
-
return this.checkNode(program.body[0].expression);
|
|
15318
|
-
}
|
|
15319
|
-
/** Builds the standard rejection message. @private */
|
|
15320
|
-
forbidden(detail) {
|
|
15321
|
-
return `Expression contains a forbidden construct: ${detail}`;
|
|
15202
|
+
var ALLOWED_BINARY_OPERATORS = /* @__PURE__ */ new Set([
|
|
15203
|
+
"==",
|
|
15204
|
+
"!=",
|
|
15205
|
+
"===",
|
|
15206
|
+
"!==",
|
|
15207
|
+
"<",
|
|
15208
|
+
"<=",
|
|
15209
|
+
">",
|
|
15210
|
+
">=",
|
|
15211
|
+
"+",
|
|
15212
|
+
"-",
|
|
15213
|
+
"*",
|
|
15214
|
+
"/",
|
|
15215
|
+
"%"
|
|
15216
|
+
]);
|
|
15217
|
+
var ALLOWED_UNARY_OPERATORS = /* @__PURE__ */ new Set(["!", "-", "+", "typeof"]);
|
|
15218
|
+
var SAFE_METHODS = /* @__PURE__ */ new Set([
|
|
15219
|
+
"length",
|
|
15220
|
+
"includes",
|
|
15221
|
+
"startsWith",
|
|
15222
|
+
"endsWith",
|
|
15223
|
+
"indexOf",
|
|
15224
|
+
"lastIndexOf",
|
|
15225
|
+
"toLowerCase",
|
|
15226
|
+
"toUpperCase",
|
|
15227
|
+
"trim",
|
|
15228
|
+
"trimStart",
|
|
15229
|
+
"trimEnd",
|
|
15230
|
+
"toString",
|
|
15231
|
+
"valueOf",
|
|
15232
|
+
"some",
|
|
15233
|
+
"every",
|
|
15234
|
+
"find",
|
|
15235
|
+
"filter",
|
|
15236
|
+
"map",
|
|
15237
|
+
"reduce",
|
|
15238
|
+
"split",
|
|
15239
|
+
"slice",
|
|
15240
|
+
"substring",
|
|
15241
|
+
"join",
|
|
15242
|
+
"concat",
|
|
15243
|
+
"charAt",
|
|
15244
|
+
"at",
|
|
15245
|
+
"padStart",
|
|
15246
|
+
"padEnd",
|
|
15247
|
+
"toFixed",
|
|
15248
|
+
"toPrecision"
|
|
15249
|
+
]);
|
|
15250
|
+
var CALLBACK_METHODS = /* @__PURE__ */ new Set(["some", "every", "find", "filter", "map", "reduce"]);
|
|
15251
|
+
var NON_FUNCTION_ARGUMENT_SLOTS = /* @__PURE__ */ new Map([
|
|
15252
|
+
["JSON.parse", 1],
|
|
15253
|
+
["JSON.stringify", 1]
|
|
15254
|
+
]);
|
|
15255
|
+
var CONTEXT_KEY_PATTERN = /^[A-Za-z_$][\w$]*$/;
|
|
15256
|
+
var MAX_EXPRESSION_LENGTH = 1e3;
|
|
15257
|
+
function forbidden(detail) {
|
|
15258
|
+
return `Expression contains a forbidden construct: ${detail}`;
|
|
15259
|
+
}
|
|
15260
|
+
var ExpressionScreen = class {
|
|
15261
|
+
constructor(contextKeys) {
|
|
15262
|
+
this.contextKeys = contextKeys;
|
|
15263
|
+
this.scopes = [];
|
|
15322
15264
|
}
|
|
15323
|
-
|
|
15324
|
-
* Walks an AST node against the allowlist. Returns an error message on the first
|
|
15325
|
-
* disallowed construct, or null if the entire subtree is permitted.
|
|
15326
|
-
* @private
|
|
15327
|
-
*/
|
|
15328
|
-
checkNode(node) {
|
|
15265
|
+
Check(node) {
|
|
15329
15266
|
switch (node.type) {
|
|
15330
15267
|
case "Literal": {
|
|
15331
15268
|
const lit = node;
|
|
15332
15269
|
if (lit.value instanceof RegExp || "regex" in lit) {
|
|
15333
|
-
return
|
|
15334
|
-
}
|
|
15335
|
-
return null;
|
|
15336
|
-
}
|
|
15337
|
-
case "Identifier": {
|
|
15338
|
-
const name = node.name;
|
|
15339
|
-
if (_SafeExpressionEvaluator.DANGEROUS_IDENTIFIERS.has(name)) {
|
|
15340
|
-
return this.forbidden(`the identifier "${name}"`);
|
|
15270
|
+
return forbidden("regular expression literals are not allowed");
|
|
15341
15271
|
}
|
|
15342
15272
|
return null;
|
|
15343
15273
|
}
|
|
15274
|
+
case "Identifier":
|
|
15275
|
+
return this.checkValueIdentifier(node.name);
|
|
15344
15276
|
case "ChainExpression":
|
|
15345
|
-
return this.
|
|
15277
|
+
return this.Check(node.expression);
|
|
15346
15278
|
case "MemberExpression":
|
|
15347
15279
|
return this.checkMemberExpression(node);
|
|
15348
15280
|
case "CallExpression":
|
|
15349
15281
|
return this.checkCallExpression(node);
|
|
15350
15282
|
case "ArrowFunctionExpression":
|
|
15351
|
-
return
|
|
15283
|
+
return forbidden(`arrow functions are allowed only as the inline callback of ${[...CALLBACK_METHODS].map((m) => `.${m}()`).join(", ")}`);
|
|
15352
15284
|
case "BinaryExpression": {
|
|
15353
15285
|
const bin = node;
|
|
15354
|
-
if (!
|
|
15355
|
-
return
|
|
15286
|
+
if (!ALLOWED_BINARY_OPERATORS.has(bin.operator)) {
|
|
15287
|
+
return forbidden(`the operator "${bin.operator}"`);
|
|
15356
15288
|
}
|
|
15357
|
-
return this.
|
|
15289
|
+
return this.Check(bin.left) ?? this.Check(bin.right);
|
|
15358
15290
|
}
|
|
15359
15291
|
case "LogicalExpression": {
|
|
15360
15292
|
const log = node;
|
|
15361
|
-
return this.
|
|
15293
|
+
return this.Check(log.left) ?? this.Check(log.right);
|
|
15362
15294
|
}
|
|
15363
15295
|
case "UnaryExpression": {
|
|
15364
15296
|
const un = node;
|
|
15365
|
-
if (!
|
|
15366
|
-
return
|
|
15297
|
+
if (!ALLOWED_UNARY_OPERATORS.has(un.operator)) {
|
|
15298
|
+
return forbidden(`the unary operator "${un.operator}"`);
|
|
15367
15299
|
}
|
|
15368
|
-
return this.
|
|
15300
|
+
return this.Check(un.argument);
|
|
15369
15301
|
}
|
|
15370
15302
|
case "ConditionalExpression": {
|
|
15371
15303
|
const cond = node;
|
|
15372
|
-
return this.
|
|
15304
|
+
return this.Check(cond.test) ?? this.Check(cond.consequent) ?? this.Check(cond.alternate);
|
|
15373
15305
|
}
|
|
15374
15306
|
case "ArrayExpression": {
|
|
15375
15307
|
const arr = node;
|
|
@@ -15377,128 +15309,314 @@ var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
|
15377
15309
|
if (!el)
|
|
15378
15310
|
continue;
|
|
15379
15311
|
if (el.type === "SpreadElement") {
|
|
15380
|
-
return
|
|
15312
|
+
return forbidden("spread elements are not allowed");
|
|
15381
15313
|
}
|
|
15382
|
-
const err = this.
|
|
15314
|
+
const err = this.Check(el);
|
|
15383
15315
|
if (err)
|
|
15384
15316
|
return err;
|
|
15385
15317
|
}
|
|
15386
15318
|
return null;
|
|
15387
15319
|
}
|
|
15388
15320
|
default:
|
|
15389
|
-
return
|
|
15321
|
+
return forbidden(`${node.type} is not allowed`);
|
|
15322
|
+
}
|
|
15323
|
+
}
|
|
15324
|
+
/** Whether `name` is bound by an enclosing arrow function. */
|
|
15325
|
+
isBoundParameter(name) {
|
|
15326
|
+
return this.scopes.some((scope) => scope.has(name));
|
|
15327
|
+
}
|
|
15328
|
+
/**
|
|
15329
|
+
* An identifier in VALUE position (anything but the callee of a call): it must be an arrow
|
|
15330
|
+
* parameter in scope, a context key, or an approved value global (`undefined`). Approved function
|
|
15331
|
+
* globals are refused here — `items.map(Number)` and `[Math][0]` hand a host function out as a value.
|
|
15332
|
+
*/
|
|
15333
|
+
checkValueIdentifier(name) {
|
|
15334
|
+
if (this.isBoundParameter(name)) {
|
|
15335
|
+
return null;
|
|
15336
|
+
}
|
|
15337
|
+
if (this.contextKeys?.has(name)) {
|
|
15338
|
+
return null;
|
|
15339
|
+
}
|
|
15340
|
+
if (DANGEROUS_IDENTIFIERS.has(name)) {
|
|
15341
|
+
return forbidden(`the identifier "${name}"`);
|
|
15342
|
+
}
|
|
15343
|
+
if (APPROVED_VALUE_GLOBALS.has(name)) {
|
|
15344
|
+
return null;
|
|
15390
15345
|
}
|
|
15346
|
+
if (APPROVED_EXPRESSION_GLOBALS.has(name)) {
|
|
15347
|
+
return forbidden(`"${name}" may only be called (e.g. ${name}${APPROVED_EXPRESSION_GLOBALS.get(name)?.Callable ? "(x)" : ".method(x)"}), not used as a value or callback`);
|
|
15348
|
+
}
|
|
15349
|
+
if (this.contextKeys === null) {
|
|
15350
|
+
return null;
|
|
15351
|
+
}
|
|
15352
|
+
return forbidden(`the identifier "${name}" is not defined in the evaluation context and is not an arrow-function parameter`);
|
|
15391
15353
|
}
|
|
15392
15354
|
/**
|
|
15393
|
-
* Member access is allowed for dotted property reads and literal-key bracket
|
|
15394
|
-
*
|
|
15395
|
-
*
|
|
15396
|
-
* @private
|
|
15355
|
+
* Member access is allowed for dotted property reads and literal-key bracket reads, but never
|
|
15356
|
+
* for a computed key built at runtime (the string-concat escape route) or a prototype-chain
|
|
15357
|
+
* property.
|
|
15397
15358
|
*/
|
|
15398
15359
|
checkMemberExpression(node) {
|
|
15399
15360
|
if (node.property.type === "PrivateIdentifier") {
|
|
15400
|
-
return
|
|
15361
|
+
return forbidden("private fields are not allowed");
|
|
15401
15362
|
}
|
|
15402
15363
|
if (node.computed) {
|
|
15403
15364
|
if (node.property.type !== "Literal") {
|
|
15404
|
-
return
|
|
15365
|
+
return forbidden("computed member access must use a literal index/key");
|
|
15405
15366
|
}
|
|
15406
15367
|
const value = node.property.value;
|
|
15407
15368
|
if (typeof value === "string") {
|
|
15408
|
-
if (
|
|
15409
|
-
return
|
|
15369
|
+
if (DANGEROUS_PROPERTY_NAMES.has(value)) {
|
|
15370
|
+
return forbidden(`access to "${value}"`);
|
|
15410
15371
|
}
|
|
15411
15372
|
} else if (typeof value !== "number") {
|
|
15412
|
-
return
|
|
15373
|
+
return forbidden("computed member access must use a string or numeric literal");
|
|
15413
15374
|
}
|
|
15414
15375
|
} else {
|
|
15415
15376
|
const name = node.property.name;
|
|
15416
|
-
if (
|
|
15417
|
-
return
|
|
15377
|
+
if (DANGEROUS_PROPERTY_NAMES.has(name)) {
|
|
15378
|
+
return forbidden(`access to "${name}"`);
|
|
15418
15379
|
}
|
|
15419
15380
|
}
|
|
15420
|
-
return this.
|
|
15381
|
+
return this.Check(node.object);
|
|
15421
15382
|
}
|
|
15422
15383
|
/**
|
|
15423
|
-
* A call is admitted only when its callee is a FIXED name
|
|
15424
|
-
*
|
|
15425
|
-
* method
|
|
15426
|
-
* unlisted bare identifier
|
|
15427
|
-
*
|
|
15428
|
-
* @private
|
|
15384
|
+
* A call is admitted only when its callee is a FIXED name: a callable approved global
|
|
15385
|
+
* (`parseInt(...)`), a listed method of an approved global namespace (`Math.abs(...)`), or a safe
|
|
15386
|
+
* instance method on a screened receiver (`payload.title.includes(...)`). Everything else — a
|
|
15387
|
+
* computed callee, an unlisted bare identifier, a call on an arrow or call result that is not
|
|
15388
|
+
* itself a safe method — is rejected.
|
|
15429
15389
|
*/
|
|
15430
15390
|
checkCallExpression(node) {
|
|
15431
15391
|
const callee = node.callee;
|
|
15432
15392
|
if (callee.type === "Identifier") {
|
|
15433
15393
|
const name = callee.name;
|
|
15434
|
-
if (!SAFE_GLOBAL_CALLABLES.has(name)) {
|
|
15435
|
-
return
|
|
15394
|
+
if (this.isBoundParameter(name) || !SAFE_GLOBAL_CALLABLES.has(name)) {
|
|
15395
|
+
return forbidden(`calling "${name}" \u2014 only safe methods and approved global functions may be invoked`);
|
|
15436
15396
|
}
|
|
15437
|
-
return this.checkCallArguments(node);
|
|
15397
|
+
return this.checkCallArguments(node, null, null);
|
|
15438
15398
|
}
|
|
15439
15399
|
if (callee.type !== "MemberExpression") {
|
|
15440
|
-
return
|
|
15400
|
+
return forbidden("only method calls on the safe-method allowlist may be invoked");
|
|
15441
15401
|
}
|
|
15442
15402
|
const member = callee;
|
|
15443
15403
|
if (member.computed || member.property.type !== "Identifier") {
|
|
15444
|
-
return
|
|
15404
|
+
return forbidden("methods must be called by a dotted, non-computed name");
|
|
15445
15405
|
}
|
|
15446
15406
|
const method = member.property.name;
|
|
15447
|
-
|
|
15448
|
-
|
|
15407
|
+
const receiver = member.object;
|
|
15408
|
+
if (receiver.type === "Identifier") {
|
|
15409
|
+
const namespace = receiver.name;
|
|
15410
|
+
const shadowed = this.isBoundParameter(namespace) || this.contextKeys?.has(namespace) === true;
|
|
15411
|
+
if (!shadowed && SAFE_GLOBAL_NAMESPACE_METHODS.get(namespace)?.has(method) === true) {
|
|
15412
|
+
return this.checkCallArguments(node, null, `${namespace}.${method}`);
|
|
15413
|
+
}
|
|
15414
|
+
}
|
|
15415
|
+
if (!SAFE_METHODS.has(method)) {
|
|
15416
|
+
return forbidden(`the method "${method}" is not on the safe-method allowlist`);
|
|
15449
15417
|
}
|
|
15450
|
-
const receiverError = this.
|
|
15418
|
+
const receiverError = this.checkReceiver(receiver);
|
|
15451
15419
|
if (receiverError)
|
|
15452
15420
|
return receiverError;
|
|
15453
|
-
return this.checkCallArguments(node);
|
|
15421
|
+
return this.checkCallArguments(node, method, null);
|
|
15454
15422
|
}
|
|
15455
|
-
/**
|
|
15456
|
-
|
|
15457
|
-
|
|
15458
|
-
|
|
15459
|
-
*/
|
|
15460
|
-
isCallableMethod(receiver, method) {
|
|
15461
|
-
if (_SafeExpressionEvaluator.SAFE_METHODS.includes(method)) {
|
|
15462
|
-
return true;
|
|
15423
|
+
/** Screens the receiver of a safe instance method. `super.x()` is never valid. */
|
|
15424
|
+
checkReceiver(receiver) {
|
|
15425
|
+
if (receiver.type === "Super") {
|
|
15426
|
+
return forbidden("super is not allowed");
|
|
15463
15427
|
}
|
|
15464
|
-
|
|
15465
|
-
return false;
|
|
15466
|
-
}
|
|
15467
|
-
return SAFE_GLOBAL_NAMESPACE_METHODS.get(receiver.name)?.has(method) === true;
|
|
15428
|
+
return this.Check(receiver);
|
|
15468
15429
|
}
|
|
15469
|
-
/**
|
|
15470
|
-
|
|
15471
|
-
|
|
15430
|
+
/**
|
|
15431
|
+
* Walks a call's arguments. Spreads are rejected outright. For a callback method the first
|
|
15432
|
+
* argument must be an inline arrow function; for `JSON.parse`/`JSON.stringify` the reviver /
|
|
15433
|
+
* replacer slot must be a literal or array literal.
|
|
15434
|
+
*/
|
|
15435
|
+
checkCallArguments(node, instanceMethod, globalMethod) {
|
|
15436
|
+
const callbackMethod = instanceMethod !== null && CALLBACK_METHODS.has(instanceMethod);
|
|
15437
|
+
const nonFunctionSlot = globalMethod !== null ? NON_FUNCTION_ARGUMENT_SLOTS.get(globalMethod) : void 0;
|
|
15438
|
+
for (let i = 0; i < node.arguments.length; i++) {
|
|
15439
|
+
const arg = node.arguments[i];
|
|
15472
15440
|
if (arg.type === "SpreadElement") {
|
|
15473
|
-
return
|
|
15441
|
+
return forbidden("spread arguments are not allowed");
|
|
15442
|
+
}
|
|
15443
|
+
if (callbackMethod && i === 0) {
|
|
15444
|
+
if (arg.type !== "ArrowFunctionExpression") {
|
|
15445
|
+
return forbidden(`the callback of .${instanceMethod}() must be an inline arrow function (e.g. x => x.price > 100)`);
|
|
15446
|
+
}
|
|
15447
|
+
const err2 = this.checkArrowFunction(arg);
|
|
15448
|
+
if (err2)
|
|
15449
|
+
return err2;
|
|
15450
|
+
continue;
|
|
15451
|
+
}
|
|
15452
|
+
if (nonFunctionSlot === i && arg.type !== "Literal" && arg.type !== "ArrayExpression") {
|
|
15453
|
+
return forbidden(`argument ${i + 1} of ${globalMethod}() must be a literal or an array literal`);
|
|
15474
15454
|
}
|
|
15475
|
-
const err = this.
|
|
15455
|
+
const err = this.Check(arg);
|
|
15476
15456
|
if (err)
|
|
15477
15457
|
return err;
|
|
15478
15458
|
}
|
|
15479
15459
|
return null;
|
|
15480
15460
|
}
|
|
15481
15461
|
/**
|
|
15482
|
-
*
|
|
15483
|
-
*
|
|
15484
|
-
* @private
|
|
15462
|
+
* An inline callback: parameters must be simple identifiers that do not shadow an approved or
|
|
15463
|
+
* denied global, and the body must be an expression. Parameters are in scope for the body only.
|
|
15485
15464
|
*/
|
|
15486
15465
|
checkArrowFunction(node) {
|
|
15487
15466
|
if (node.async) {
|
|
15488
|
-
return
|
|
15467
|
+
return forbidden("async functions are not allowed");
|
|
15468
|
+
}
|
|
15469
|
+
if (node.body.type === "BlockStatement") {
|
|
15470
|
+
return forbidden("arrow-function bodies must be an expression, not a block");
|
|
15489
15471
|
}
|
|
15472
|
+
const params = /* @__PURE__ */ new Set();
|
|
15490
15473
|
for (const param of node.params) {
|
|
15491
15474
|
if (param.type !== "Identifier") {
|
|
15492
|
-
return
|
|
15475
|
+
return forbidden("arrow-function parameters must be simple identifiers");
|
|
15493
15476
|
}
|
|
15494
|
-
|
|
15495
|
-
|
|
15477
|
+
const name = param.name;
|
|
15478
|
+
if (DANGEROUS_IDENTIFIERS.has(name) || APPROVED_EXPRESSION_GLOBALS.has(name) || APPROVED_VALUE_GLOBALS.has(name)) {
|
|
15479
|
+
return forbidden(`the parameter "${name}"`);
|
|
15496
15480
|
}
|
|
15481
|
+
params.add(name);
|
|
15497
15482
|
}
|
|
15498
|
-
|
|
15499
|
-
|
|
15483
|
+
this.scopes.push(params);
|
|
15484
|
+
try {
|
|
15485
|
+
return this.Check(node.body);
|
|
15486
|
+
} finally {
|
|
15487
|
+
this.scopes.pop();
|
|
15488
|
+
}
|
|
15489
|
+
}
|
|
15490
|
+
};
|
|
15491
|
+
var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
15492
|
+
constructor() {
|
|
15493
|
+
this._compiledExpressionCache = new MJLruCache({ maxSize: 1e3 });
|
|
15494
|
+
}
|
|
15495
|
+
/**
|
|
15496
|
+
* Evaluates a boolean expression against a context object
|
|
15497
|
+
*
|
|
15498
|
+
* @param {string} expression - The boolean expression to evaluate
|
|
15499
|
+
* @param {Record<string, any>} [context={}] - The context object containing variables. Its keys
|
|
15500
|
+
* are the only free names the expression may use; a key named like an approved global
|
|
15501
|
+
* shadows it (see {@link ScreenExpression}).
|
|
15502
|
+
* @param {boolean} [enableDiagnostics=false] - Whether to include diagnostic information
|
|
15503
|
+
*
|
|
15504
|
+
* @returns {ExpressionEvaluationResult} The evaluation result
|
|
15505
|
+
*/
|
|
15506
|
+
Evaluate(expression, context2 = {}, enableDiagnostics = false) {
|
|
15507
|
+
const startTime = Date.now();
|
|
15508
|
+
try {
|
|
15509
|
+
const safeContext = this.createSafeContext(context2);
|
|
15510
|
+
const contextKeys = Object.keys(safeContext);
|
|
15511
|
+
const validationError = _SafeExpressionEvaluator.ScreenExpression(expression, contextKeys);
|
|
15512
|
+
if (validationError) {
|
|
15513
|
+
return {
|
|
15514
|
+
success: false,
|
|
15515
|
+
error: validationError,
|
|
15516
|
+
diagnostics: enableDiagnostics ? {
|
|
15517
|
+
expression,
|
|
15518
|
+
context: context2,
|
|
15519
|
+
evaluationTime: Date.now() - startTime
|
|
15520
|
+
} : void 0
|
|
15521
|
+
};
|
|
15522
|
+
}
|
|
15523
|
+
const contextValues = contextKeys.map((key) => safeContext[key]);
|
|
15524
|
+
const cacheKey = `${expression}|${contextKeys.join(",")}`;
|
|
15525
|
+
let evaluator = this._compiledExpressionCache.Get(cacheKey);
|
|
15526
|
+
if (!evaluator) {
|
|
15527
|
+
const functionBody = `
|
|
15528
|
+
"use strict";
|
|
15529
|
+
try {
|
|
15530
|
+
return Boolean(${expression});
|
|
15531
|
+
} catch (e) {
|
|
15532
|
+
throw new Error('Expression evaluation failed: ' + e.message);
|
|
15533
|
+
}
|
|
15534
|
+
`;
|
|
15535
|
+
evaluator = new Function(...contextKeys, functionBody);
|
|
15536
|
+
this._compiledExpressionCache.Set(cacheKey, evaluator);
|
|
15537
|
+
}
|
|
15538
|
+
const result = evaluator(...contextValues);
|
|
15539
|
+
return {
|
|
15540
|
+
success: true,
|
|
15541
|
+
value: Boolean(result),
|
|
15542
|
+
diagnostics: enableDiagnostics ? {
|
|
15543
|
+
expression,
|
|
15544
|
+
context: context2,
|
|
15545
|
+
evaluationTime: Date.now() - startTime
|
|
15546
|
+
} : void 0
|
|
15547
|
+
};
|
|
15548
|
+
} catch (error) {
|
|
15549
|
+
return {
|
|
15550
|
+
success: false,
|
|
15551
|
+
error: error instanceof Error ? error.message : String(error),
|
|
15552
|
+
diagnostics: enableDiagnostics ? {
|
|
15553
|
+
expression,
|
|
15554
|
+
context: context2,
|
|
15555
|
+
evaluationTime: Date.now() - startTime
|
|
15556
|
+
} : void 0
|
|
15557
|
+
};
|
|
15500
15558
|
}
|
|
15501
|
-
|
|
15559
|
+
}
|
|
15560
|
+
/** @deprecated Use {@link Evaluate}. */
|
|
15561
|
+
evaluate(expression, context2 = {}, enableDiagnostics = false) {
|
|
15562
|
+
return this.Evaluate(expression, context2, enableDiagnostics);
|
|
15563
|
+
}
|
|
15564
|
+
/**
|
|
15565
|
+
* Screens an expression against the allowlist WITHOUT compiling or running it. Returns the
|
|
15566
|
+
* rejection message, or `null` when every construct is permitted.
|
|
15567
|
+
*
|
|
15568
|
+
* This is the single policy gate for every place MemberJunction compiles a user- or LLM-authored
|
|
15569
|
+
* expression: {@link Evaluate}, {@link ValidateSyntax}, and the field-rule / integration formula
|
|
15570
|
+
* engine (`FieldTransformEngine`), which passes `['value', 'fields']`.
|
|
15571
|
+
*
|
|
15572
|
+
* A context key or arrow parameter named like an approved global (`Math`, `JSON`, ...) shadows
|
|
15573
|
+
* it and is screened as data, so `Math.abs(x)` with a `Math` context key is refused because
|
|
15574
|
+
* `abs` is not a safe instance method. Do not pass approved-global names as context keys.
|
|
15575
|
+
*
|
|
15576
|
+
* @param expression the expression to screen
|
|
15577
|
+
* @param allowedIdentifiers the names the expression may reference as values — the evaluation
|
|
15578
|
+
* context keys. Pass `null` only when they are not yet known (authoring-time syntax
|
|
15579
|
+
* checks); free identifiers are then left for evaluation time to decide, while approved
|
|
15580
|
+
* globals used as values, callbacks that are not inline arrows, and every structural rule
|
|
15581
|
+
* are still enforced.
|
|
15582
|
+
*/
|
|
15583
|
+
static ScreenExpression(expression, allowedIdentifiers) {
|
|
15584
|
+
if (!expression || typeof expression !== "string") {
|
|
15585
|
+
return "Expression must be a non-empty string";
|
|
15586
|
+
}
|
|
15587
|
+
if (expression.length > MAX_EXPRESSION_LENGTH) {
|
|
15588
|
+
return `Expression exceeds maximum length of ${MAX_EXPRESSION_LENGTH} characters`;
|
|
15589
|
+
}
|
|
15590
|
+
if (expression.trim() === "") {
|
|
15591
|
+
return null;
|
|
15592
|
+
}
|
|
15593
|
+
let program;
|
|
15594
|
+
try {
|
|
15595
|
+
program = parse3(expression, { ecmaVersion: 2022 });
|
|
15596
|
+
} catch (e) {
|
|
15597
|
+
if (_SafeExpressionEvaluator.hasUnbalancedParentheses(expression)) {
|
|
15598
|
+
return "Unbalanced parentheses in expression";
|
|
15599
|
+
}
|
|
15600
|
+
return forbidden(`it is not a parseable single expression (${e instanceof Error ? e.message : String(e)})`);
|
|
15601
|
+
}
|
|
15602
|
+
if (program.body.length !== 1 || program.body[0].type !== "ExpressionStatement") {
|
|
15603
|
+
return forbidden("only a single expression is permitted \u2014 no statements, blocks, or semicolons");
|
|
15604
|
+
}
|
|
15605
|
+
const keys = allowedIdentifiers === null ? null : new Set(allowedIdentifiers);
|
|
15606
|
+
return new ExpressionScreen(keys).Check(program.body[0].expression);
|
|
15607
|
+
}
|
|
15608
|
+
/** Counts parentheses textually; used only to word the error for an expression that failed to parse. @private */
|
|
15609
|
+
static hasUnbalancedParentheses(expression) {
|
|
15610
|
+
let depth = 0;
|
|
15611
|
+
for (const char of expression) {
|
|
15612
|
+
if (char === "(")
|
|
15613
|
+
depth++;
|
|
15614
|
+
if (char === ")")
|
|
15615
|
+
depth--;
|
|
15616
|
+
if (depth < 0)
|
|
15617
|
+
return true;
|
|
15618
|
+
}
|
|
15619
|
+
return depth !== 0;
|
|
15502
15620
|
}
|
|
15503
15621
|
/**
|
|
15504
15622
|
* Creates a safe context object with only allowed properties
|
|
@@ -15510,11 +15628,17 @@ var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
|
15510
15628
|
* @private
|
|
15511
15629
|
*/
|
|
15512
15630
|
createSafeContext(context2) {
|
|
15631
|
+
if (context2 === null || typeof context2 !== "object") {
|
|
15632
|
+
throw new Error("Context must be an object");
|
|
15633
|
+
}
|
|
15513
15634
|
const safeContext = {};
|
|
15514
15635
|
for (const [key, value] of Object.entries(context2)) {
|
|
15515
15636
|
if (this.isDangerousPropertyName(key)) {
|
|
15516
15637
|
continue;
|
|
15517
15638
|
}
|
|
15639
|
+
if (!CONTEXT_KEY_PATTERN.test(key)) {
|
|
15640
|
+
throw new Error(`Context key "${key}" is not a valid identifier; context keys must match ${CONTEXT_KEY_PATTERN.source}`);
|
|
15641
|
+
}
|
|
15518
15642
|
safeContext[key] = this.cloneValue(value);
|
|
15519
15643
|
}
|
|
15520
15644
|
return safeContext;
|
|
@@ -15541,6 +15665,11 @@ var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
|
15541
15665
|
/**
|
|
15542
15666
|
* Safely clones a value for use in evaluation context
|
|
15543
15667
|
*
|
|
15668
|
+
* Functions are never carried into the context: a context should hold data, and a function
|
|
15669
|
+
* reachable from an expression is one more thing that could be invoked. A function value
|
|
15670
|
+
* becomes `undefined`, and function-valued properties are dropped from plain objects and
|
|
15671
|
+
* class instances alike.
|
|
15672
|
+
*
|
|
15544
15673
|
* @param {any} value - The value to clone
|
|
15545
15674
|
*
|
|
15546
15675
|
* @returns {any} The cloned value
|
|
@@ -15552,6 +15681,9 @@ var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
|
15552
15681
|
return value;
|
|
15553
15682
|
}
|
|
15554
15683
|
const type = typeof value;
|
|
15684
|
+
if (type === "function") {
|
|
15685
|
+
return void 0;
|
|
15686
|
+
}
|
|
15555
15687
|
if (type === "string" || type === "number" || type === "boolean") {
|
|
15556
15688
|
return value;
|
|
15557
15689
|
}
|
|
@@ -15561,7 +15693,7 @@ var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
|
15561
15693
|
if (type === "object" && value.constructor === Object) {
|
|
15562
15694
|
const cloned = {};
|
|
15563
15695
|
for (const [key, val] of Object.entries(value)) {
|
|
15564
|
-
if (!this.isDangerousPropertyName(key)) {
|
|
15696
|
+
if (!this.isDangerousPropertyName(key) && typeof val !== "function") {
|
|
15565
15697
|
cloned[key] = this.cloneValue(val);
|
|
15566
15698
|
}
|
|
15567
15699
|
}
|
|
@@ -15573,7 +15705,7 @@ var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
|
15573
15705
|
if (type === "object" && typeof value.constructor === "function") {
|
|
15574
15706
|
const cloned = {};
|
|
15575
15707
|
for (const [key, val] of Object.entries(value)) {
|
|
15576
|
-
if (!this.isDangerousPropertyName(key)) {
|
|
15708
|
+
if (!this.isDangerousPropertyName(key) && typeof val !== "function") {
|
|
15577
15709
|
cloned[key] = val;
|
|
15578
15710
|
}
|
|
15579
15711
|
}
|
|
@@ -15595,43 +15727,43 @@ var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
|
15595
15727
|
* is a perfectly good condition that simply has no data yet. A submit-time check built on
|
|
15596
15728
|
* `evaluate` therefore refuses every legitimate condition.
|
|
15597
15729
|
*
|
|
15598
|
-
* So this
|
|
15599
|
-
* `evaluate` applies (the AST allowlist), then the same function body
|
|
15600
|
-
* never invoked. Values are never consulted, which is precisely the property wanted: unknown
|
|
15730
|
+
* So this parses and never runs. The expression goes through the same policy screen
|
|
15731
|
+
* `evaluate` applies (the AST allowlist), then the same function body `evaluate` compiles is
|
|
15732
|
+
* parsed in strict mode — never compiled, never invoked. Values are never consulted, which is precisely the property wanted: unknown
|
|
15601
15733
|
* identifiers, absent properties and undefined chains all PASS, because none of them is a syntax
|
|
15602
|
-
* error and whether they resolve is a question about a run that has not happened yet.
|
|
15734
|
+
* error and whether they resolve is a question about a run that has not happened yet. Pass
|
|
15735
|
+
* `contextKeys` when the evaluation context's keys ARE known, and unknown identifiers are refused
|
|
15736
|
+
* here exactly as {@link Evaluate} would refuse them.
|
|
15603
15737
|
*
|
|
15604
|
-
* **
|
|
15605
|
-
*
|
|
15606
|
-
* first, so the constructs `evaluate` refuses are refused here too — those produce a
|
|
15607
|
-
* runtime refusal, and an author is better told now.
|
|
15738
|
+
* **Parsing is not evaluation**, and the distinction is load-bearing for safety: no code is
|
|
15739
|
+
* constructed from the expression here, so nothing in it can execute. The policy screen still
|
|
15740
|
+
* runs first, so the constructs `evaluate` refuses are refused here too — those produce a
|
|
15741
|
+
* permanent runtime refusal, and an author is better told now.
|
|
15608
15742
|
*
|
|
15609
|
-
*
|
|
15610
|
-
* CSP without `unsafe-eval`)
|
|
15611
|
-
*
|
|
15743
|
+
* Because nothing is compiled, the answer does not depend on the host allowing dynamic code (a
|
|
15744
|
+
* strict CSP without `unsafe-eval`). `Undecidable` is kept in the result type for callers that
|
|
15745
|
+
* check it, and is no longer returned.
|
|
15612
15746
|
*
|
|
15613
15747
|
* @param expression the expression to check
|
|
15614
|
-
* @
|
|
15615
|
-
*
|
|
15748
|
+
* @param contextKeys the names the expression may reference, when known; omitted, free
|
|
15749
|
+
* identifiers are left for evaluation time (see {@link ScreenExpression})
|
|
15750
|
+
* @returns `Valid: true` when it parses; `Valid: false` with `Error` when it does not
|
|
15616
15751
|
*/
|
|
15617
|
-
ValidateSyntax(expression) {
|
|
15618
|
-
const policyError =
|
|
15752
|
+
ValidateSyntax(expression, contextKeys) {
|
|
15753
|
+
const policyError = _SafeExpressionEvaluator.ScreenExpression(expression, contextKeys ?? null);
|
|
15619
15754
|
if (policyError) {
|
|
15620
15755
|
return { Valid: false, Error: policyError };
|
|
15621
15756
|
}
|
|
15622
15757
|
try {
|
|
15623
|
-
|
|
15758
|
+
parse3(`"use strict"; return Boolean(${expression});`, { ecmaVersion: 2022, allowReturnOutsideFunction: true });
|
|
15624
15759
|
return { Valid: true };
|
|
15625
15760
|
} catch (e) {
|
|
15626
|
-
|
|
15627
|
-
return { Valid: false, Error: e.message };
|
|
15628
|
-
}
|
|
15629
|
-
return { Valid: true, Undecidable: true };
|
|
15761
|
+
return { Valid: false, Error: e instanceof Error ? e.message : String(e) };
|
|
15630
15762
|
}
|
|
15631
15763
|
}
|
|
15632
15764
|
/** @deprecated Use {@link ValidateSyntax}. */
|
|
15633
|
-
validateSyntax(expression) {
|
|
15634
|
-
return this.ValidateSyntax(expression);
|
|
15765
|
+
validateSyntax(expression, contextKeys) {
|
|
15766
|
+
return this.ValidateSyntax(expression, contextKeys);
|
|
15635
15767
|
}
|
|
15636
15768
|
/**
|
|
15637
15769
|
* Evaluates multiple expressions and returns all results
|
|
@@ -15656,6 +15788,77 @@ var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
|
|
|
15656
15788
|
};
|
|
15657
15789
|
var DefaultExpressionEvaluator = new SafeExpressionEvaluator();
|
|
15658
15790
|
|
|
15791
|
+
// ../../MJGlobal/dist/fieldRules/SafeTransformAstEvaluator.js
|
|
15792
|
+
var OPTIONAL_SHORT_CIRCUIT = Symbol("OPTIONAL_SHORT_CIRCUIT");
|
|
15793
|
+
var HIGHER_ORDER_ARRAY_METHODS = /* @__PURE__ */ new Set([
|
|
15794
|
+
"some",
|
|
15795
|
+
"every",
|
|
15796
|
+
"find",
|
|
15797
|
+
"findIndex",
|
|
15798
|
+
"filter",
|
|
15799
|
+
"map",
|
|
15800
|
+
"reduce",
|
|
15801
|
+
"flatMap"
|
|
15802
|
+
]);
|
|
15803
|
+
var SAFE_STRING_METHODS = /* @__PURE__ */ new Set([
|
|
15804
|
+
"includes",
|
|
15805
|
+
"startsWith",
|
|
15806
|
+
"endsWith",
|
|
15807
|
+
"indexOf",
|
|
15808
|
+
"lastIndexOf",
|
|
15809
|
+
"toLowerCase",
|
|
15810
|
+
"toUpperCase",
|
|
15811
|
+
"trim",
|
|
15812
|
+
"trimStart",
|
|
15813
|
+
"trimEnd",
|
|
15814
|
+
"slice",
|
|
15815
|
+
"substring",
|
|
15816
|
+
"split",
|
|
15817
|
+
"replace",
|
|
15818
|
+
"replaceAll",
|
|
15819
|
+
"padStart",
|
|
15820
|
+
"padEnd",
|
|
15821
|
+
"charAt",
|
|
15822
|
+
"charCodeAt",
|
|
15823
|
+
"at",
|
|
15824
|
+
"concat",
|
|
15825
|
+
"toString",
|
|
15826
|
+
"valueOf"
|
|
15827
|
+
]);
|
|
15828
|
+
var SAFE_ARRAY_VALUE_METHODS = /* @__PURE__ */ new Set([
|
|
15829
|
+
"includes",
|
|
15830
|
+
"indexOf",
|
|
15831
|
+
"lastIndexOf",
|
|
15832
|
+
"join",
|
|
15833
|
+
"slice",
|
|
15834
|
+
"concat",
|
|
15835
|
+
"flat",
|
|
15836
|
+
"at",
|
|
15837
|
+
"toString"
|
|
15838
|
+
]);
|
|
15839
|
+
var SAFE_NUMBER_METHODS = /* @__PURE__ */ new Set(["toFixed", "toPrecision", "toString", "valueOf"]);
|
|
15840
|
+
var SAFE_BOOLEAN_METHODS = /* @__PURE__ */ new Set(["toString", "valueOf"]);
|
|
15841
|
+
var SAFE_DATE_METHODS = /* @__PURE__ */ new Set([
|
|
15842
|
+
"toISOString",
|
|
15843
|
+
"getTime",
|
|
15844
|
+
"getFullYear",
|
|
15845
|
+
"getUTCFullYear",
|
|
15846
|
+
"getMonth",
|
|
15847
|
+
"getUTCMonth",
|
|
15848
|
+
"getDate",
|
|
15849
|
+
"getUTCDate",
|
|
15850
|
+
"toString",
|
|
15851
|
+
"valueOf"
|
|
15852
|
+
]);
|
|
15853
|
+
var ALL_SAFE_INSTANCE_METHODS = /* @__PURE__ */ new Set([
|
|
15854
|
+
...SAFE_STRING_METHODS,
|
|
15855
|
+
...SAFE_ARRAY_VALUE_METHODS,
|
|
15856
|
+
...HIGHER_ORDER_ARRAY_METHODS,
|
|
15857
|
+
...SAFE_NUMBER_METHODS,
|
|
15858
|
+
...SAFE_BOOLEAN_METHODS,
|
|
15859
|
+
...SAFE_DATE_METHODS
|
|
15860
|
+
]);
|
|
15861
|
+
|
|
15659
15862
|
// ../../MJGlobal/dist/SQLScreeningScanner.js
|
|
15660
15863
|
var SQL_SCREENING_DIALECTS = ["sqlserver", "postgresql"];
|
|
15661
15864
|
var LEXICAL_RULES = {
|
|
@@ -16924,6 +17127,16 @@ var WarningManager = class _WarningManager extends BaseSingleton {
|
|
|
16924
17127
|
|
|
16925
17128
|
// ../../MJGlobal/dist/EncryptionUtils.js
|
|
16926
17129
|
var ENCRYPTION_MARKER = "$ENC$";
|
|
17130
|
+
var ENCRYPTED_SENTINEL = "[!ENCRYPTED$]";
|
|
17131
|
+
function IsValueEncrypted(value, encryptionMarker) {
|
|
17132
|
+
if (!value || typeof value !== "string") {
|
|
17133
|
+
return false;
|
|
17134
|
+
}
|
|
17135
|
+
return IsEncryptedSentinel(value) || value.startsWith(encryptionMarker || ENCRYPTION_MARKER);
|
|
17136
|
+
}
|
|
17137
|
+
function IsEncryptedSentinel(value) {
|
|
17138
|
+
return value === ENCRYPTED_SENTINEL;
|
|
17139
|
+
}
|
|
16927
17140
|
|
|
16928
17141
|
// ../../MJGlobal/dist/hashing/canonicalize.js
|
|
16929
17142
|
function Canonicalize(value) {
|
|
@@ -16990,6 +17203,7 @@ export {
|
|
|
16990
17203
|
UUIDsEqual,
|
|
16991
17204
|
IsValidUUID,
|
|
16992
17205
|
UUIDSet,
|
|
17206
|
+
UUIDMap,
|
|
16993
17207
|
GroupByUUID,
|
|
16994
17208
|
IndexByUUID,
|
|
16995
17209
|
UserCacheKeySegment,
|
|
@@ -17009,6 +17223,8 @@ export {
|
|
|
17009
17223
|
SQLExpressionValidator,
|
|
17010
17224
|
WarningManager,
|
|
17011
17225
|
ENCRYPTION_MARKER,
|
|
17226
|
+
ENCRYPTED_SENTINEL,
|
|
17227
|
+
IsValueEncrypted,
|
|
17012
17228
|
ComputeContentHashAsync,
|
|
17013
17229
|
Subscription,
|
|
17014
17230
|
Observable,
|