@jxsuite/runtime 3.0.0 → 4.0.0

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/src/expression.ts CHANGED
@@ -375,6 +375,33 @@ function resolveOperand(
375
375
  }
376
376
 
377
377
  /** Resolve a $ref string within expression context. */
378
+ /**
379
+ * The receiver a `call` binds: the value the pointer's parent path names. `#/state/x` and
380
+ * `parent#/x` are owned by the scope itself; `$map/item/fn` by the item; a pointer with no parent
381
+ * segment (`$map/item`, `$args/x`) has no owner.
382
+ */
383
+ function calleeOwner(ref: string, state: JxScope, event: Event | null, iterCtx?: IterCtx): unknown {
384
+ const parentRef = (prefix: string): unknown => {
385
+ const rest = ref.slice(prefix.length);
386
+ if (!rest.includes("/")) {
387
+ return state;
388
+ }
389
+ return resolveExprRef(ref.slice(0, ref.lastIndexOf("/")), state, event, iterCtx);
390
+ };
391
+ if (ref.startsWith("#/state/")) {
392
+ return parentRef("#/state/");
393
+ }
394
+ if (ref.startsWith("parent#/")) {
395
+ return parentRef("parent#/");
396
+ }
397
+ if (ref.startsWith("$map/") || ref.startsWith("$args/")) {
398
+ return ref.split("/").length > 2
399
+ ? resolveExprRef(ref.slice(0, ref.lastIndexOf("/")), state, event, iterCtx)
400
+ : undefined;
401
+ }
402
+ return undefined;
403
+ }
404
+
378
405
  function resolveExprRef(ref: string, state: JxScope, event: Event | null, iterCtx?: IterCtx) {
379
406
  if (ref === "$reduce/acc") {
380
407
  return iterCtx?.acc;
@@ -558,9 +585,14 @@ function evaluateNode(
558
585
  }
559
586
 
560
587
  const callee = resolveExprRef(calleeRef, state, event, iterCtx);
561
- // A scope callable (buildScope lowers parameterized formula entries to functions).
588
+ // A scope callable (buildScope lowers parameterized formula entries to functions). The receiver
589
+ // Is the object that owns the pointer's last segment — what the compiled lowering binds when it
590
+ // Emits `s.commands.run(...)` — so a host method handed in through the scope keeps its `this`.
562
591
  if (typeof callee === "function") {
563
- return (callee as (...a: unknown[]) => unknown)(...argValues);
592
+ return (callee as (...a: unknown[]) => unknown).apply(
593
+ calleeOwner(calleeRef, state, event, iterCtx),
594
+ argValues,
595
+ );
564
596
  }
565
597
  // A raw named-formula def (standalone evaluation, e.g. editor preview).
566
598
  if (callee && typeof callee === "object" && "$expression" in callee) {