j-templates 7.0.85 → 7.0.87
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/Node/vNode.js +1 -1
- package/Store/Tree/observableScope.d.ts +36 -12
- package/Store/Tree/observableScope.js +56 -20
- package/Utils/decorators.d.ts +7 -7
- package/Utils/decorators.js +13 -9
- package/index.d.ts +1 -1
- package/index.js +3 -2
- package/package.json +1 -1
package/Node/vNode.js
CHANGED
|
@@ -174,7 +174,7 @@ function CreateChildrenScope(vnode, children, data = DefaultData) {
|
|
|
174
174
|
if ((0, functions_1.IsAsync)(data)) {
|
|
175
175
|
const asyncData = data;
|
|
176
176
|
data = function () {
|
|
177
|
-
return (0, observableScope_1.
|
|
177
|
+
return (0, observableScope_1.InlineScope)(async function () {
|
|
178
178
|
return asyncData();
|
|
179
179
|
});
|
|
180
180
|
};
|
|
@@ -46,38 +46,62 @@ interface IDynamicObservableScope<T> {
|
|
|
46
46
|
*/
|
|
47
47
|
export type IObservableScope<T> = IStaticObservableScope<T> | IDynamicObservableScope<T>;
|
|
48
48
|
/**
|
|
49
|
-
* Creates
|
|
50
|
-
*
|
|
49
|
+
* Creates an inline computed scope registered as a dependency of the parent.
|
|
50
|
+
* The scope is memoized by ID and reused across reads within the same parent evaluation.
|
|
51
|
+
* Emits on every recomputation — no === gating.
|
|
51
52
|
*
|
|
52
|
-
*
|
|
53
|
-
* Scopes are memoized by ID and reused across multiple reads within a single parent scope evaluation.
|
|
54
|
-
* Defaults to "default" ID - provide custom IDs when using multiple calc scopes.
|
|
53
|
+
* Supports async callbacks — Promise<T> is resolved and the resolved value is emitted.
|
|
55
54
|
*
|
|
56
55
|
* Only works within a watch context (during another scope's execution).
|
|
56
|
+
* Throws if called outside a watch context.
|
|
57
|
+
* Creates non-greedy scopes that emit immediately without === gating.
|
|
58
|
+
* @template T The type of value returned by the callback (extracted from Promise if async).
|
|
59
|
+
* @param callback The function to compute the derived value.
|
|
60
|
+
* @param idOverride Optional custom ID for memoization when using multiple scope calls.
|
|
61
|
+
* @returns The computed value, reusing existing scope if available.
|
|
62
|
+
* @throws Error if called outside a watch context.
|
|
63
|
+
*/
|
|
64
|
+
export declare function InlineScope<T>(callback: () => T | Promise<T>, idOverride?: string): T;
|
|
65
|
+
/**
|
|
66
|
+
* Creates a gatekeeper scope that only emits when the value actually changes (===).
|
|
67
|
+
* Prevents unnecessary downstream re-evaluations when the derived value stays the same.
|
|
68
|
+
*
|
|
69
|
+
* Useful for optimizing expensive operations driven by frequently-changing parent state.
|
|
70
|
+
* Scopes are memoized by ID and reused across reads within the same parent evaluation.
|
|
71
|
+
*
|
|
72
|
+
* Supports async callbacks — Promise<T> is resolved and the resolved value is gated.
|
|
73
|
+
*
|
|
74
|
+
* Only works within a watch context (during another scope's execution).
|
|
75
|
+
* Throws if called outside a watch context.
|
|
57
76
|
* Always creates greedy scopes that batch updates via microtask queue.
|
|
58
|
-
* @template T The type of value returned by the callback.
|
|
77
|
+
* @template T The type of value returned by the callback (extracted from Promise if async).
|
|
59
78
|
* @param callback The function to compute the derived value.
|
|
60
|
-
* @param idOverride Optional custom ID for memoization when using multiple
|
|
79
|
+
* @param idOverride Optional custom ID for memoization when using multiple gate calls.
|
|
61
80
|
* @returns The computed value, reusing existing scope if available.
|
|
81
|
+
* @throws Error if called outside a watch context.
|
|
62
82
|
*/
|
|
63
|
-
export declare function
|
|
83
|
+
export declare function GateScope<T>(callback: () => T | Promise<T>, idOverride?: string): T;
|
|
64
84
|
/**
|
|
65
85
|
* Creates a computed scope that isn't registered as a dependency with the parent.
|
|
66
86
|
* Use this function to read reactive data without subscribing to changes
|
|
67
87
|
* to that data.
|
|
68
88
|
*
|
|
69
|
-
* Unlike
|
|
89
|
+
* Unlike GateScope, PeekScope does not register itself as a dependency, meaning
|
|
70
90
|
* changes to the data accessed within the callback will not trigger recomputation
|
|
71
91
|
* of the parent scope. The scope is still memoized by ID within the watch context
|
|
72
92
|
* to avoid redundant computation during the same evaluation.
|
|
73
93
|
*
|
|
94
|
+
* Supports async callbacks — Promise<T> is resolved and the resolved value is returned.
|
|
95
|
+
*
|
|
74
96
|
* Only works within a watch context (during another scope's execution).
|
|
75
|
-
*
|
|
97
|
+
* Throws if called outside a watch context.
|
|
98
|
+
* @template T The type of value returned by the callback (extracted from Promise if async).
|
|
76
99
|
* @param callback The function to compute the derived value.
|
|
77
|
-
* @param idOverride Optional custom ID for memoization when using multiple peek
|
|
100
|
+
* @param idOverride Optional custom ID for memoization when using multiple peek calls.
|
|
78
101
|
* @returns The computed value, reusing existing scope if available.
|
|
102
|
+
* @throws Error if called outside a watch context.
|
|
79
103
|
*/
|
|
80
|
-
export declare function PeekScope<T>(callback: () => T
|
|
104
|
+
export declare function PeekScope<T>(callback: () => T | Promise<T>, idOverride?: string): T;
|
|
81
105
|
export declare namespace ObservableScope {
|
|
82
106
|
/**
|
|
83
107
|
* Creates a new observable scope from a value function.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ObservableScope = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.InlineScope = InlineScope;
|
|
5
|
+
exports.GateScope = GateScope;
|
|
5
6
|
exports.PeekScope = PeekScope;
|
|
6
7
|
const emitter_1 = require("../../Utils/emitter");
|
|
7
8
|
const functions_1 = require("../../Utils/functions");
|
|
@@ -190,7 +191,7 @@ let watchState = null;
|
|
|
190
191
|
* Executes a callback while tracking all scope and emitter dependencies.
|
|
191
192
|
* Creates a watch context that records what was accessed during execution.
|
|
192
193
|
* @param callback The function to execute while tracking dependencies.
|
|
193
|
-
* @param currentCalc Optional map of existing
|
|
194
|
+
* @param currentCalc Optional map of existing inline scopes to reuse.
|
|
194
195
|
* @returns The watch state containing tracked dependencies and result.
|
|
195
196
|
*/
|
|
196
197
|
function WatchFunction(callback, currentCalc, initialEmitters) {
|
|
@@ -280,24 +281,53 @@ function ScopeHelper(callback, id, greedy) {
|
|
|
280
281
|
return nextScopes[id];
|
|
281
282
|
}
|
|
282
283
|
/**
|
|
283
|
-
* Creates
|
|
284
|
-
*
|
|
284
|
+
* Creates an inline computed scope registered as a dependency of the parent.
|
|
285
|
+
* The scope is memoized by ID and reused across reads within the same parent evaluation.
|
|
286
|
+
* Emits on every recomputation — no === gating.
|
|
285
287
|
*
|
|
286
|
-
*
|
|
287
|
-
* Scopes are memoized by ID and reused across multiple reads within a single parent scope evaluation.
|
|
288
|
-
* Defaults to "default" ID - provide custom IDs when using multiple calc scopes.
|
|
288
|
+
* Supports async callbacks — Promise<T> is resolved and the resolved value is emitted.
|
|
289
289
|
*
|
|
290
290
|
* Only works within a watch context (during another scope's execution).
|
|
291
|
+
* Throws if called outside a watch context.
|
|
292
|
+
* Creates non-greedy scopes that emit immediately without === gating.
|
|
293
|
+
* @template T The type of value returned by the callback (extracted from Promise if async).
|
|
294
|
+
* @param callback The function to compute the derived value.
|
|
295
|
+
* @param idOverride Optional custom ID for memoization when using multiple scope calls.
|
|
296
|
+
* @returns The computed value, reusing existing scope if available.
|
|
297
|
+
* @throws Error if called outside a watch context.
|
|
298
|
+
*/
|
|
299
|
+
function InlineScope(callback, idOverride) {
|
|
300
|
+
if (watchState === null) {
|
|
301
|
+
throw new Error("scope() must be called within a watch context");
|
|
302
|
+
}
|
|
303
|
+
const id = idOverride ?? "scope_default";
|
|
304
|
+
const scope = ScopeHelper(callback, id, false);
|
|
305
|
+
RegisterScope(scope);
|
|
306
|
+
return GetScopeValue(scope);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Creates a gatekeeper scope that only emits when the value actually changes (===).
|
|
310
|
+
* Prevents unnecessary downstream re-evaluations when the derived value stays the same.
|
|
311
|
+
*
|
|
312
|
+
* Useful for optimizing expensive operations driven by frequently-changing parent state.
|
|
313
|
+
* Scopes are memoized by ID and reused across reads within the same parent evaluation.
|
|
314
|
+
*
|
|
315
|
+
* Supports async callbacks — Promise<T> is resolved and the resolved value is gated.
|
|
316
|
+
*
|
|
317
|
+
* Only works within a watch context (during another scope's execution).
|
|
318
|
+
* Throws if called outside a watch context.
|
|
291
319
|
* Always creates greedy scopes that batch updates via microtask queue.
|
|
292
|
-
* @template T The type of value returned by the callback.
|
|
320
|
+
* @template T The type of value returned by the callback (extracted from Promise if async).
|
|
293
321
|
* @param callback The function to compute the derived value.
|
|
294
|
-
* @param idOverride Optional custom ID for memoization when using multiple
|
|
322
|
+
* @param idOverride Optional custom ID for memoization when using multiple gate calls.
|
|
295
323
|
* @returns The computed value, reusing existing scope if available.
|
|
324
|
+
* @throws Error if called outside a watch context.
|
|
296
325
|
*/
|
|
297
|
-
function
|
|
298
|
-
if (watchState === null)
|
|
299
|
-
|
|
300
|
-
|
|
326
|
+
function GateScope(callback, idOverride) {
|
|
327
|
+
if (watchState === null) {
|
|
328
|
+
throw new Error("gate() must be called within a watch context");
|
|
329
|
+
}
|
|
330
|
+
const id = idOverride ?? "gate_default";
|
|
301
331
|
const scope = ScopeHelper(callback, id, true);
|
|
302
332
|
RegisterScope(scope);
|
|
303
333
|
return GetScopeValue(scope);
|
|
@@ -307,20 +337,25 @@ function CalcScope(callback, idOverride) {
|
|
|
307
337
|
* Use this function to read reactive data without subscribing to changes
|
|
308
338
|
* to that data.
|
|
309
339
|
*
|
|
310
|
-
* Unlike
|
|
340
|
+
* Unlike GateScope, PeekScope does not register itself as a dependency, meaning
|
|
311
341
|
* changes to the data accessed within the callback will not trigger recomputation
|
|
312
342
|
* of the parent scope. The scope is still memoized by ID within the watch context
|
|
313
343
|
* to avoid redundant computation during the same evaluation.
|
|
314
344
|
*
|
|
345
|
+
* Supports async callbacks — Promise<T> is resolved and the resolved value is returned.
|
|
346
|
+
*
|
|
315
347
|
* Only works within a watch context (during another scope's execution).
|
|
316
|
-
*
|
|
348
|
+
* Throws if called outside a watch context.
|
|
349
|
+
* @template T The type of value returned by the callback (extracted from Promise if async).
|
|
317
350
|
* @param callback The function to compute the derived value.
|
|
318
|
-
* @param idOverride Optional custom ID for memoization when using multiple peek
|
|
351
|
+
* @param idOverride Optional custom ID for memoization when using multiple peek calls.
|
|
319
352
|
* @returns The computed value, reusing existing scope if available.
|
|
353
|
+
* @throws Error if called outside a watch context.
|
|
320
354
|
*/
|
|
321
355
|
function PeekScope(callback, idOverride) {
|
|
322
|
-
if (watchState === null)
|
|
323
|
-
|
|
356
|
+
if (watchState === null) {
|
|
357
|
+
throw new Error("peek() must be called within a watch context");
|
|
358
|
+
}
|
|
324
359
|
const id = idOverride ?? "peek_default";
|
|
325
360
|
const scope = ScopeHelper(callback, id, false);
|
|
326
361
|
return GetScopeValue(scope);
|
|
@@ -387,8 +422,9 @@ function DestroyScope(scope) {
|
|
|
387
422
|
emitter_1.Emitter.Clear(scope.emitter);
|
|
388
423
|
for (const key in scope.scopes)
|
|
389
424
|
DestroyScope(scope.scopes[key]);
|
|
390
|
-
|
|
391
|
-
|
|
425
|
+
if (scope.emitters !== null)
|
|
426
|
+
for (let x = 0; x < scope.emitters.length; x++)
|
|
427
|
+
emitter_1.Emitter.Remove(scope.emitters[x], scope.setCallback);
|
|
392
428
|
scope.value = undefined;
|
|
393
429
|
scope.scopes = null;
|
|
394
430
|
scope.emitters = null;
|
package/Utils/decorators.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* | getters only (expensive) | @Computed | Cached + object reuse via Store |
|
|
22
22
|
* | getters only (simple) | @Scope | Cached, but new reference on update |
|
|
23
23
|
* | getters only (sync, StoreAsync) | @ComputedAsync | Sync getter, StoreAsync caching + object reuse |
|
|
24
|
-
* | async functions (direct scope) | ObservableScope.Create(async) | Direct async, new reference, initial null |
|
|
24
|
+
* | async functions (direct scope) | ObservableScope.Create(async) | Direct async, new reference, initial null |
|
|
25
25
|
* | subscribe to changes | @Watch | Calls method when scope value changes |
|
|
26
26
|
* | dependency injection | @Inject | Gets value from component injector |
|
|
27
27
|
* | cleanup on destroy | @Destroy| Calls .Destroy() on component teardown |
|
|
@@ -233,7 +233,7 @@ export declare function Computed<T extends WeakKey, K extends keyof T, D extends
|
|
|
233
233
|
* Both use synchronous getters and provide object identity preservation.
|
|
234
234
|
*
|
|
235
235
|
* **Comparison**:
|
|
236
|
-
* | Aspect | @Scope | @Computed | @ComputedAsync |
|
|
236
|
+
* | Aspect | @Scope | @Computed | @ComputedAsync | scope(async) + @Scope |
|
|
237
237
|
* |--------|--------|-----------|----------------|----------------------|
|
|
238
238
|
* | Caches value | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes (memoized) |
|
|
239
239
|
* | Getter type | Sync | Sync | Sync | Async supported |
|
|
@@ -461,15 +461,15 @@ export declare function Value(): any;
|
|
|
461
461
|
* - Object identity matters (array/object references used in templates)
|
|
462
462
|
* - You need DOM reference preservation to avoid re-renders
|
|
463
463
|
*
|
|
464
|
-
* **Async pattern with @Scope**: Use `
|
|
464
|
+
* **Async pattern with @Scope**: Use `scope(async () => ...)` for async operations:
|
|
465
465
|
* ```typescript
|
|
466
466
|
* @Scope()
|
|
467
467
|
* get CurrentUser() {
|
|
468
|
-
* return
|
|
468
|
+
* return scope(async () => fetchUser(`/api/user/${this.userId}`));
|
|
469
469
|
* }
|
|
470
470
|
* ```
|
|
471
|
-
* - `
|
|
472
|
-
* - Returns
|
|
471
|
+
* - `scope` creates an inline computed scope that resolves the async operation
|
|
472
|
+
* - Returns the resolved value (not a Promise)
|
|
473
473
|
* - Automatically batches updates via microtask queue
|
|
474
474
|
* - New reference on each update (no object reuse)
|
|
475
475
|
*
|
|
@@ -486,7 +486,7 @@ export declare function Value(): any;
|
|
|
486
486
|
* @see {@link ComputedAsync} for sync getters with StoreAsync backend
|
|
487
487
|
* @see {@link ObservableNode.ApplyDiff} for how @Computed maintains object identity
|
|
488
488
|
* @see {@link ObservableScope} for the scope-based reactivity system
|
|
489
|
-
* @see {@link
|
|
489
|
+
* @see {@link scope} for inline computed scopes within @Scope
|
|
490
490
|
*/
|
|
491
491
|
export declare function Scope(): typeof ScopeDecorator;
|
|
492
492
|
/**
|
package/Utils/decorators.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* | getters only (expensive) | @Computed | Cached + object reuse via Store |
|
|
23
23
|
* | getters only (simple) | @Scope | Cached, but new reference on update |
|
|
24
24
|
* | getters only (sync, StoreAsync) | @ComputedAsync | Sync getter, StoreAsync caching + object reuse |
|
|
25
|
-
* | async functions (direct scope) | ObservableScope.Create(async) | Direct async, new reference, initial null |
|
|
25
|
+
* | async functions (direct scope) | ObservableScope.Create(async) | Direct async, new reference, initial null |
|
|
26
26
|
* | subscribe to changes | @Watch | Calls method when scope value changes |
|
|
27
27
|
* | dependency injection | @Inject | Gets value from component injector |
|
|
28
28
|
* | cleanup on destroy | @Destroy| Calls .Destroy() on component teardown |
|
|
@@ -136,10 +136,14 @@ function GetDestroyArrayForPrototype(prototype, create = true) {
|
|
|
136
136
|
function CreateComputedScope(getter, store, defaultValue) {
|
|
137
137
|
const getterScope = observableScope_1.ObservableScope.Create(getter, true);
|
|
138
138
|
observableScope_1.ObservableScope.Watch(getterScope, (scope) => {
|
|
139
|
-
const data = observableScope_1.ObservableScope.
|
|
139
|
+
const data = observableScope_1.ObservableScope.Peek(scope);
|
|
140
140
|
store.Write(data, "root");
|
|
141
141
|
});
|
|
142
|
-
const
|
|
142
|
+
const data = observableScope_1.ObservableScope.Peek(getterScope);
|
|
143
|
+
store.Write(data, "root");
|
|
144
|
+
const propertyScope = observableScope_1.ObservableScope.Create(() => {
|
|
145
|
+
return store.Get("root", defaultValue);
|
|
146
|
+
});
|
|
143
147
|
observableScope_1.ObservableScope.OnDestroyed(propertyScope, function () {
|
|
144
148
|
observableScope_1.ObservableScope.Destroy(getterScope);
|
|
145
149
|
if (store instanceof Store_1.StoreAsync)
|
|
@@ -356,7 +360,7 @@ function ComputedDecorator(target, prop, descriptor) {
|
|
|
356
360
|
* Both use synchronous getters and provide object identity preservation.
|
|
357
361
|
*
|
|
358
362
|
* **Comparison**:
|
|
359
|
-
* | Aspect | @Scope | @Computed | @ComputedAsync |
|
|
363
|
+
* | Aspect | @Scope | @Computed | @ComputedAsync | scope(async) + @Scope |
|
|
360
364
|
* |--------|--------|-----------|----------------|----------------------|
|
|
361
365
|
* | Caches value | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes (memoized) |
|
|
362
366
|
* | Getter type | Sync | Sync | Sync | Async supported |
|
|
@@ -684,15 +688,15 @@ function ValueDecorator(target, propertyKey) {
|
|
|
684
688
|
* - Object identity matters (array/object references used in templates)
|
|
685
689
|
* - You need DOM reference preservation to avoid re-renders
|
|
686
690
|
*
|
|
687
|
-
* **Async pattern with @Scope**: Use `
|
|
691
|
+
* **Async pattern with @Scope**: Use `scope(async () => ...)` for async operations:
|
|
688
692
|
* ```typescript
|
|
689
693
|
* @Scope()
|
|
690
694
|
* get CurrentUser() {
|
|
691
|
-
* return
|
|
695
|
+
* return scope(async () => fetchUser(`/api/user/${this.userId}`));
|
|
692
696
|
* }
|
|
693
697
|
* ```
|
|
694
|
-
* - `
|
|
695
|
-
* - Returns
|
|
698
|
+
* - `scope` creates an inline computed scope that resolves the async operation
|
|
699
|
+
* - Returns the resolved value (not a Promise)
|
|
696
700
|
* - Automatically batches updates via microtask queue
|
|
697
701
|
* - New reference on each update (no object reuse)
|
|
698
702
|
*
|
|
@@ -709,7 +713,7 @@ function ValueDecorator(target, propertyKey) {
|
|
|
709
713
|
* @see {@link ComputedAsync} for sync getters with StoreAsync backend
|
|
710
714
|
* @see {@link ObservableNode.ApplyDiff} for how @Computed maintains object identity
|
|
711
715
|
* @see {@link ObservableScope} for the scope-based reactivity system
|
|
712
|
-
* @see {@link
|
|
716
|
+
* @see {@link scope} for inline computed scopes within @Scope
|
|
713
717
|
*/
|
|
714
718
|
function Scope() {
|
|
715
719
|
return ScopeDecorator;
|
package/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Component } from './Node/component';
|
|
2
|
-
export {
|
|
2
|
+
export { InlineScope as scope, GateScope as gate, PeekScope as peek } from './Store/Tree/observableScope';
|
package/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.peek = exports.
|
|
3
|
+
exports.peek = exports.gate = exports.scope = 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
|
-
Object.defineProperty(exports, "
|
|
7
|
+
Object.defineProperty(exports, "scope", { enumerable: true, get: function () { return observableScope_1.InlineScope; } });
|
|
8
|
+
Object.defineProperty(exports, "gate", { enumerable: true, get: function () { return observableScope_1.GateScope; } });
|
|
8
9
|
Object.defineProperty(exports, "peek", { enumerable: true, get: function () { return observableScope_1.PeekScope; } });
|