j-templates 7.0.84 → 7.0.85

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.
@@ -35,8 +35,8 @@ interface IDynamicObservableScope<T> {
35
35
  emitters: (Emitter | null)[];
36
36
  /** Emitter for notifying when the scope is destroyed */
37
37
  onDestroyed: Emitter | null;
38
- /** Map of nested calc scopes created during this scope's execution */
39
- calcScopes: {
38
+ /** Map of nested scopes created during this scope's execution */
39
+ scopes: {
40
40
  [id: string]: IObservableScope<unknown> | null;
41
41
  } | null;
42
42
  }
@@ -61,6 +61,23 @@ export type IObservableScope<T> = IStaticObservableScope<T> | IDynamicObservable
61
61
  * @returns The computed value, reusing existing scope if available.
62
62
  */
63
63
  export declare function CalcScope<T>(callback: () => T, idOverride?: string): T;
64
+ /**
65
+ * Creates a computed scope that isn't registered as a dependency with the parent.
66
+ * Use this function to read reactive data without subscribing to changes
67
+ * to that data.
68
+ *
69
+ * Unlike CalcScope, PeekScope does not register itself as a dependency, meaning
70
+ * changes to the data accessed within the callback will not trigger recomputation
71
+ * of the parent scope. The scope is still memoized by ID within the watch context
72
+ * to avoid redundant computation during the same evaluation.
73
+ *
74
+ * Only works within a watch context (during another scope's execution).
75
+ * @template T The type of value returned by the callback.
76
+ * @param callback The function to compute the derived value.
77
+ * @param idOverride Optional custom ID for memoization when using multiple peek scopes.
78
+ * @returns The computed value, reusing existing scope if available.
79
+ */
80
+ export declare function PeekScope<T>(callback: () => T, idOverride?: string): T;
64
81
  export declare namespace ObservableScope {
65
82
  /**
66
83
  * Creates a new observable scope from a value function.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ObservableScope = void 0;
4
4
  exports.CalcScope = CalcScope;
5
+ exports.PeekScope = PeekScope;
5
6
  const emitter_1 = require("../../Utils/emitter");
6
7
  const functions_1 = require("../../Utils/functions");
7
8
  /**
@@ -26,7 +27,7 @@ function CreateDynamicScope(getFunction, greedy, value) {
26
27
  emitter: emitter_1.Emitter.Create(),
27
28
  emitters: null,
28
29
  onDestroyed: null,
29
- calcScopes: null,
30
+ scopes: null,
30
31
  };
31
32
  scope.setCallback = OnSet.bind(scope);
32
33
  return scope;
@@ -220,17 +221,21 @@ function WatchFunction(callback, currentCalc, initialEmitters) {
220
221
  */
221
222
  function ExecuteScope(scope) {
222
223
  scope.dirty = false;
223
- const state = WatchFunction(scope.getFunction, scope.calcScopes, scope.emitters);
224
+ const state = WatchFunction(scope.getFunction, scope.scopes, scope.emitters);
224
225
  UpdateEmitters(scope, state);
225
226
  const calcScopes = state.currentCalc;
226
- scope.calcScopes = state.nextCalc;
227
+ scope.scopes = state.nextCalc;
227
228
  for (const key in calcScopes)
228
229
  DestroyScope(calcScopes[key]);
229
- if (scope.async)
230
- state.value.then(function (result) {
230
+ if (scope.async) {
231
+ const promise = state.value;
232
+ promise.then(function (result) {
233
+ if (state.value !== promise)
234
+ return;
231
235
  scope.value = result;
232
236
  emitter_1.Emitter.Emit(scope.emitter, scope);
233
237
  });
238
+ }
234
239
  else
235
240
  scope.value = state.value;
236
241
  }
@@ -247,18 +252,33 @@ function ExecuteFunction(callback, greedy, allowStatic) {
247
252
  const state = WatchFunction(callback, null, null);
248
253
  if (!allowStatic || async || state.emitters !== null) {
249
254
  const scope = CreateDynamicScope(callback, greedy, async ? null : state.value);
250
- scope.calcScopes = state.nextCalc;
255
+ scope.scopes = state.nextCalc;
251
256
  UpdateEmitters(scope, state);
252
- if (async)
253
- state.value.then(function (result) {
257
+ if (async) {
258
+ const promise = state.value;
259
+ promise.then(function (result) {
260
+ if (state.value !== promise)
261
+ return;
254
262
  scope.value = result;
255
263
  emitter_1.Emitter.Emit(scope.emitter, scope);
256
264
  });
265
+ }
257
266
  return scope;
258
267
  }
259
268
  const value = state.value;
260
269
  return CreateStaticScope(value);
261
270
  }
271
+ function ScopeHelper(callback, id, greedy) {
272
+ const nextScopes = (watchState.nextCalc ??= {});
273
+ if (nextScopes[id])
274
+ return nextScopes[id];
275
+ const currentScopes = watchState.currentCalc;
276
+ nextScopes[id] =
277
+ currentScopes?.[id] ?? ExecuteFunction(callback, greedy, true);
278
+ if (currentScopes?.[id])
279
+ delete currentScopes[id];
280
+ return nextScopes[id];
281
+ }
262
282
  /**
263
283
  * Creates a computed scope that acts as a gatekeeper for parent scope emissions.
264
284
  * If this scope's value doesn't change (=== comparison) it won't emit.
@@ -277,18 +297,33 @@ function ExecuteFunction(callback, greedy, allowStatic) {
277
297
  function CalcScope(callback, idOverride) {
278
298
  if (watchState === null)
279
299
  return callback();
280
- const nextScopes = (watchState.nextCalc ??= {});
281
- const id = idOverride ?? "default";
282
- if (nextScopes[id]) {
283
- RegisterScope(nextScopes[id]);
284
- return GetScopeValue(nextScopes[id]);
285
- }
286
- const currentScopes = watchState.currentCalc;
287
- nextScopes[id] = currentScopes?.[id] ?? ExecuteFunction(callback, true, true);
288
- if (currentScopes?.[id])
289
- delete currentScopes[id];
290
- RegisterScope(nextScopes[id]);
291
- return GetScopeValue(nextScopes[id]);
300
+ const id = idOverride ?? "calc_default";
301
+ const scope = ScopeHelper(callback, id, true);
302
+ RegisterScope(scope);
303
+ return GetScopeValue(scope);
304
+ }
305
+ /**
306
+ * Creates a computed scope that isn't registered as a dependency with the parent.
307
+ * Use this function to read reactive data without subscribing to changes
308
+ * to that data.
309
+ *
310
+ * Unlike CalcScope, PeekScope does not register itself as a dependency, meaning
311
+ * changes to the data accessed within the callback will not trigger recomputation
312
+ * of the parent scope. The scope is still memoized by ID within the watch context
313
+ * to avoid redundant computation during the same evaluation.
314
+ *
315
+ * Only works within a watch context (during another scope's execution).
316
+ * @template T The type of value returned by the callback.
317
+ * @param callback The function to compute the derived value.
318
+ * @param idOverride Optional custom ID for memoization when using multiple peek scopes.
319
+ * @returns The computed value, reusing existing scope if available.
320
+ */
321
+ function PeekScope(callback, idOverride) {
322
+ if (watchState === null)
323
+ return callback();
324
+ const id = idOverride ?? "peek_default";
325
+ const scope = ScopeHelper(callback, id, false);
326
+ return GetScopeValue(scope);
292
327
  }
293
328
  /**
294
329
  * Updates a scope's dependency emitters using efficient diffing.
@@ -350,12 +385,12 @@ function DestroyScope(scope) {
350
385
  if (!scope || scope.type === "static")
351
386
  return;
352
387
  emitter_1.Emitter.Clear(scope.emitter);
353
- for (const key in scope.calcScopes)
354
- DestroyScope(scope.calcScopes[key]);
388
+ for (const key in scope.scopes)
389
+ DestroyScope(scope.scopes[key]);
355
390
  for (let x = 0; x < scope.emitters.length; x++)
356
391
  emitter_1.Emitter.Remove(scope.emitters[x], scope.setCallback);
357
392
  scope.value = undefined;
358
- scope.calcScopes = null;
393
+ scope.scopes = null;
359
394
  scope.emitters = null;
360
395
  scope.emitter = null;
361
396
  scope.getFunction = null;
package/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export { Component } from './Node/component';
2
- export { CalcScope as calc } from './Store/Tree/observableScope';
2
+ export { CalcScope as calc, PeekScope as peek } from './Store/Tree/observableScope';
package/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calc = exports.Component = void 0;
3
+ exports.peek = exports.calc = exports.Component = void 0;
4
4
  var component_1 = require("./Node/component");
5
5
  Object.defineProperty(exports, "Component", { enumerable: true, get: function () { return component_1.Component; } });
6
6
  var observableScope_1 = require("./Store/Tree/observableScope");
7
7
  Object.defineProperty(exports, "calc", { enumerable: true, get: function () { return observableScope_1.CalcScope; } });
8
+ Object.defineProperty(exports, "peek", { enumerable: true, get: function () { return observableScope_1.PeekScope; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "j-templates",
3
- "version": "7.0.84",
3
+ "version": "7.0.85",
4
4
  "description": "j-templates",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/TypesInCode/jTemplates",