@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.
@@ -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 isFinite(value) {
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 = 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
- const known = this.GetAllRegistrations(baseClass).map((r) => r.Key).filter((k) => k != null);
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
- function hostIsLittleEndian() {
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 = hostIsLittleEndian() ? TypedArrayFloat32Codec : DataViewFloat32Codec;
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 SAFE_GLOBAL_NAMESPACE_METHODS = /* @__PURE__ */ new Map([
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
- ["Date", /* @__PURE__ */ new Set(["now"])]
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 SAFE_GLOBAL_CALLABLES = /* @__PURE__ */ new Set([
15112
- "parseInt",
15113
- "parseFloat",
15114
- "isNaN",
15115
- "isFinite",
15116
- "Number",
15117
- "String",
15118
- "Boolean"
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 SAFE_EXPRESSION_GLOBALS = /* @__PURE__ */ new Set([
15121
- ...SAFE_GLOBAL_NAMESPACE_METHODS.keys(),
15122
- ...SAFE_GLOBAL_CALLABLES
15197
+ var DANGEROUS_PROPERTY_NAMES = /* @__PURE__ */ new Set([
15198
+ "constructor",
15199
+ "__proto__",
15200
+ "prototype"
15123
15201
  ]);
15124
- var SafeExpressionEvaluator = class _SafeExpressionEvaluator {
15125
- constructor() {
15126
- this._compiledExpressionCache = new MJLruCache({ maxSize: 1e3 });
15127
- }
15128
- static {
15129
- this.DANGEROUS_IDENTIFIERS = /* @__PURE__ */ new Set([
15130
- "eval",
15131
- "Function",
15132
- "globalThis",
15133
- "global",
15134
- "window",
15135
- "document",
15136
- "process",
15137
- "require",
15138
- "module",
15139
- "exports",
15140
- "__dirname",
15141
- "__filename",
15142
- "self",
15143
- "top",
15144
- "parent",
15145
- "frames",
15146
- "Reflect",
15147
- "Proxy",
15148
- "WebAssembly",
15149
- "Atomics",
15150
- "SharedArrayBuffer",
15151
- "import",
15152
- "arguments",
15153
- "constructor",
15154
- "prototype",
15155
- "__proto__"
15156
- ]);
15157
- }
15158
- static {
15159
- this.DANGEROUS_PROPERTY_NAMES = /* @__PURE__ */ new Set([
15160
- "constructor",
15161
- "__proto__",
15162
- "prototype"
15163
- ]);
15164
- }
15165
- static {
15166
- this.ALLOWED_BINARY_OPERATORS = /* @__PURE__ */ new Set([
15167
- "==",
15168
- "!=",
15169
- "===",
15170
- "!==",
15171
- "<",
15172
- "<=",
15173
- ">",
15174
- ">=",
15175
- "+",
15176
- "-",
15177
- "*",
15178
- "/",
15179
- "%"
15180
- ]);
15181
- }
15182
- static {
15183
- this.ALLOWED_UNARY_OPERATORS = /* @__PURE__ */ new Set(["!", "-", "+", "typeof"]);
15184
- }
15185
- static {
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 this.forbidden("regular expression literals are not allowed");
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.checkNode(node.expression);
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 this.checkArrowFunction(node);
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 (!_SafeExpressionEvaluator.ALLOWED_BINARY_OPERATORS.has(bin.operator)) {
15355
- return this.forbidden(`the operator "${bin.operator}"`);
15286
+ if (!ALLOWED_BINARY_OPERATORS.has(bin.operator)) {
15287
+ return forbidden(`the operator "${bin.operator}"`);
15356
15288
  }
15357
- return this.checkNode(bin.left) ?? this.checkNode(bin.right);
15289
+ return this.Check(bin.left) ?? this.Check(bin.right);
15358
15290
  }
15359
15291
  case "LogicalExpression": {
15360
15292
  const log = node;
15361
- return this.checkNode(log.left) ?? this.checkNode(log.right);
15293
+ return this.Check(log.left) ?? this.Check(log.right);
15362
15294
  }
15363
15295
  case "UnaryExpression": {
15364
15296
  const un = node;
15365
- if (!_SafeExpressionEvaluator.ALLOWED_UNARY_OPERATORS.has(un.operator)) {
15366
- return this.forbidden(`the unary operator "${un.operator}"`);
15297
+ if (!ALLOWED_UNARY_OPERATORS.has(un.operator)) {
15298
+ return forbidden(`the unary operator "${un.operator}"`);
15367
15299
  }
15368
- return this.checkNode(un.argument);
15300
+ return this.Check(un.argument);
15369
15301
  }
15370
15302
  case "ConditionalExpression": {
15371
15303
  const cond = node;
15372
- return this.checkNode(cond.test) ?? this.checkNode(cond.consequent) ?? this.checkNode(cond.alternate);
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 this.forbidden("spread elements are not allowed");
15312
+ return forbidden("spread elements are not allowed");
15381
15313
  }
15382
- const err = this.checkNode(el);
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 this.forbidden(`${node.type} is not allowed`);
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
- * reads, but never for a computed key built at runtime (the string-concat
15395
- * escape route) or a prototype-chain property.
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 this.forbidden("private fields are not allowed");
15361
+ return forbidden("private fields are not allowed");
15401
15362
  }
15402
15363
  if (node.computed) {
15403
15364
  if (node.property.type !== "Literal") {
15404
- return this.forbidden("computed member access must use a literal index/key");
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 (_SafeExpressionEvaluator.DANGEROUS_PROPERTY_NAMES.has(value)) {
15409
- return this.forbidden(`access to "${value}"`);
15369
+ if (DANGEROUS_PROPERTY_NAMES.has(value)) {
15370
+ return forbidden(`access to "${value}"`);
15410
15371
  }
15411
15372
  } else if (typeof value !== "number") {
15412
- return this.forbidden("computed member access must use a string or numeric literal");
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 (_SafeExpressionEvaluator.DANGEROUS_PROPERTY_NAMES.has(name)) {
15417
- return this.forbidden(`access to "${name}"`);
15377
+ if (DANGEROUS_PROPERTY_NAMES.has(name)) {
15378
+ return forbidden(`access to "${name}"`);
15418
15379
  }
15419
15380
  }
15420
- return this.checkNode(node.object);
15381
+ return this.Check(node.object);
15421
15382
  }
15422
15383
  /**
15423
- * A call is admitted only when its callee is a FIXED name on one of two allowlists: a bare
15424
- * safe global (`parseInt(...)`) or a dotted method that is either a safe instance method or a
15425
- * method of a safe global namespace (`Math.abs(...)`). Everything else — a computed callee, an
15426
- * unlisted bare identifier (`eval(...)`, `Function(...)`), a call on a call's result that is
15427
- * not itself a safe method — is rejected.
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 this.forbidden(`calling "${name}" \u2014 only safe methods and safe global functions may be invoked`);
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 this.forbidden("only method calls on the safe-method allowlist may be invoked");
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 this.forbidden("methods must be called by a dotted, non-computed name");
15404
+ return forbidden("methods must be called by a dotted, non-computed name");
15445
15405
  }
15446
15406
  const method = member.property.name;
15447
- if (!this.isCallableMethod(member.object, method)) {
15448
- return this.forbidden(`the method "${method}" is not on the safe-method allowlist`);
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.checkNode(member.object);
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
- * Whether `<receiver>.<method>()` names a permitted call: a safe instance method on any value,
15457
- * or a method of a safe global namespace when the receiver is that namespace by bare name.
15458
- * @private
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
- if (receiver.type !== "Identifier") {
15465
- return false;
15466
- }
15467
- return SAFE_GLOBAL_NAMESPACE_METHODS.get(receiver.name)?.has(method) === true;
15428
+ return this.Check(receiver);
15468
15429
  }
15469
- /** Walks a call's arguments; spreads are rejected outright. @private */
15470
- checkCallArguments(node) {
15471
- for (const arg of node.arguments) {
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 this.forbidden("spread arguments are not allowed");
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.checkNode(arg);
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
- * Arrow functions are permitted only as callback arguments to the safe array
15483
- * methods; parameters must be simple identifiers and the body an expression.
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 this.forbidden("async functions are not allowed");
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 this.forbidden("arrow-function parameters must be simple identifiers");
15475
+ return forbidden("arrow-function parameters must be simple identifiers");
15493
15476
  }
15494
- if (_SafeExpressionEvaluator.DANGEROUS_IDENTIFIERS.has(param.name)) {
15495
- return this.forbidden(`the parameter "${param.name}"`);
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
- if (node.body.type === "BlockStatement") {
15499
- return this.forbidden("arrow-function bodies must be an expression, not a block");
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
- return this.checkNode(node.body);
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 compiles and never runs. The expression goes through the same policy screen
15599
- * `evaluate` applies (the AST allowlist), then the same function body is BUILT and discarded —
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
- * **Compilation is not evaluation**, and the distinction is load-bearing for safety: `Function`
15605
- * parses the body and returns; nothing in the expression executes. The policy screen still runs
15606
- * first, so the constructs `evaluate` refuses are refused here too — those produce a permanent
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
- * `Undecidable` is the honest third answer. A host that forbids dynamic compilation (a strict
15610
- * CSP without `unsafe-eval`) cannot answer the question at all, and a validator that read that
15611
- * as "invalid" would refuse every condition in the browser. Callers should treat it as a pass.
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
- * @returns `Valid: true` when it parses; `Valid: false` with `Error` when it definitely does
15615
- * not; `Valid: true` with `Undecidable: true` when this environment cannot compile
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 = this.validateExpression(expression);
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
- new Function(`"use strict"; return Boolean(${expression});`);
15758
+ parse3(`"use strict"; return Boolean(${expression});`, { ecmaVersion: 2022, allowReturnOutsideFunction: true });
15624
15759
  return { Valid: true };
15625
15760
  } catch (e) {
15626
- if (e instanceof SyntaxError) {
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,