@acorex/platform 20.9.42 → 20.9.44

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.
@@ -2077,15 +2077,27 @@ class AXPWidgetRendererDirective {
2077
2077
  });
2078
2078
  }
2079
2079
  //#region ---- Expression Caching Methods ----
2080
+ /**
2081
+ * Returns a cache hit only when an entry exists and is still within TTL.
2082
+ * Must not use `value !== null` as the hit check: a cached `undefined` would look like a hit
2083
+ * while a cached `null` would look like a miss (`getCachedExpressionResult` used to return `null` for both).
2084
+ */
2080
2085
  getCachedExpressionResult(expressionKey) {
2081
2086
  const cached = this.expressionCache.get(expressionKey);
2082
2087
  if (cached && performance.now() - cached.timestamp < this.EXPRESSION_CACHE_TTL) {
2083
- // console.log(`💾 [${this.node().type}] Using cached expression result for '${expressionKey}'`);
2084
- return cached.value;
2088
+ return { hit: true, value: cached.value };
2085
2089
  }
2086
- return null;
2090
+ return { hit: false };
2087
2091
  }
2092
+ /**
2093
+ * Skip caching nullish results so a late-arriving value (e.g. options.node going from null to a widget)
2094
+ * is re-evaluated on the next updateOptionsBasedOnContext pass instead of being frozen for TTL.
2095
+ */
2088
2096
  setCachedExpressionResult(expressionKey, value) {
2097
+ if (value == null) {
2098
+ this.expressionCache.delete(expressionKey);
2099
+ return;
2100
+ }
2089
2101
  this.expressionCache.set(expressionKey, {
2090
2102
  value,
2091
2103
  timestamp: performance.now(),
@@ -2660,11 +2672,11 @@ class AXPWidgetRendererDirective {
2660
2672
  }
2661
2673
  // console.log(`🔍 [${this.node().type}] Evaluating ${this.expressionEvaluators.size} expressions`);
2662
2674
  const updatePromises = Array.from(this.expressionEvaluators).map(async ([path, evaluator]) => {
2663
- // Check cache first
2664
- const cachedValue = this.getCachedExpressionResult(path);
2665
- if (cachedValue !== null) {
2666
- // console.log(`💾 [${this.node().type}] Using cached expression result for '${path}'`);
2667
- return { path, newValue: cachedValue, fromCache: true };
2675
+ const cached = this.getCachedExpressionResult(path);
2676
+ if (cached.hit) {
2677
+ const hasLastValue = this.lastExpressionResults.has(path);
2678
+ const hasChanged = !hasLastValue || !isEqual(cached.value, this.lastExpressionResults.get(path));
2679
+ return { path, newValue: cached.value, fromCache: true, hasChanged };
2668
2680
  }
2669
2681
  // Evaluate expression if not cached
2670
2682
  const evalStartTime = performance.now();