j-templates 7.0.69 → 7.0.71

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.
@@ -169,9 +169,10 @@ exports.DOMNodeConfig = {
169
169
  target.appendChild(children[x]);
170
170
  },
171
171
  reconcileChild(target, child) {
172
- if (target.childElementCount === 0)
173
- target.appendChild(child);
174
- else if (target.childElementCount > 1 || target.firstChild !== child)
175
- target.replaceChildren(child);
172
+ if (target.firstChild === child)
173
+ return;
174
+ target.appendChild(child);
175
+ while (target.firstChild !== child)
176
+ target.removeChild(target.firstChild);
176
177
  },
177
178
  };
@@ -38,14 +38,6 @@ export declare class Component<D = void, T = void, E = {}> {
38
38
  * Accessor for the component's template collection.
39
39
  */
40
40
  protected get Templates(): T;
41
- /**
42
- * Creates a new Component instance. Not intended to be overriden.
43
- *
44
- * @param data - Initial data or a factory function returning data/promise.
45
- * @param templates - Template definitions for rendering.
46
- * @param vNode - The underlying virtual node instance.
47
- * @param componentEvents - Optional event callbacks.
48
- */
49
41
  constructor(vNode: vNodeType, config: vComponentConfig<D, E>, templates: T);
50
42
  /**
51
43
  * Returns the component's rendered vNode(s).
package/Node/component.js CHANGED
@@ -48,32 +48,9 @@ class Component {
48
48
  get Templates() {
49
49
  return this.templates;
50
50
  }
51
- /**
52
- * Creates a new Component instance. Not intended to be overriden.
53
- *
54
- * @param data - Initial data or a factory function returning data/promise.
55
- * @param templates - Template definitions for rendering.
56
- * @param vNode - The underlying virtual node instance.
57
- * @param componentEvents - Optional event callbacks.
58
- */
59
- /* constructor(
60
- data: D | (() => D | Promise<D>),
61
- templates: T,
62
- private vNode: vNodeType,
63
- private componentEvents: ComponentEvents<E>,
64
- ) {
65
- if (typeof data === "function")
66
- this.scope = new ObservableScope<D>(data as () => D | Promise<D>);
67
- else this.scope = new ObservableScope<D>(() => data);
68
-
69
- this.templates = templates || ({} as T);
70
- } */
71
51
  constructor(vNode, config, templates) {
72
52
  this.vNode = vNode;
73
53
  const { data, on } = config;
74
- /* if (typeof data === "function")
75
- this.scope = new ObservableScope<D>(data as () => D | Promise<D>);
76
- else this.scope = new ObservableScope<D>(() => data); */
77
54
  if (typeof data === "function")
78
55
  this.scope = Store_1.ObservableScope.Create(data);
79
56
  else
@@ -119,30 +96,13 @@ class Component {
119
96
  }
120
97
  }
121
98
  exports.Component = Component;
122
- /* type ComponentConstructor<D, T, E> = {
123
- new (
124
- data: D | (() => D | Promise<D>),
125
- templates: T,
126
- vNode: vNodeType,
127
- componentEvents: ComponentEvents<E>,
128
- ): Component<D, T, E>;
129
- }; */
130
99
  (function (Component) {
131
100
  /**
132
101
  * Function wraps the Component as a function that can be used to create vNode objects
133
102
  * and generate templates.
134
103
  */
135
- function ToFunction(type,
136
- // constructor: ComponentConstructor<D, T, E>,
137
- constructor, namespace) {
104
+ function ToFunction(type, constructor, namespace) {
138
105
  return function (config, templates) {
139
- /* const { data, on, props } = config;
140
-
141
- class ConcreteComponent extends constructor {
142
- constructor(vnode: vNodeType) {
143
- super(data, templates, vnode, on);
144
- }
145
- } */
146
106
  function ComponentFactory(vnode) {
147
107
  return new constructor(vnode, config, templates);
148
108
  }
@@ -151,7 +111,6 @@ exports.Component = Component;
151
111
  namespace: namespace ?? null,
152
112
  props: config.props,
153
113
  componentFactory: ComponentFactory,
154
- // componentConstructor: ConcreteComponent,
155
114
  };
156
115
  return vNode_1.vNode.Create(definition);
157
116
  };
package/Node/vNode.js CHANGED
@@ -14,7 +14,6 @@ var vNode;
14
14
  return {
15
15
  definition,
16
16
  type: definition.type,
17
- // injector: definition.componentConstructor
18
17
  injector: definition.componentFactory
19
18
  ? injector_1.Injector.Scope(injector_1.Injector.Current(), function () {
20
19
  return new injector_1.Injector();
@@ -95,9 +94,7 @@ var vNode;
95
94
  vNode.Attach = Attach;
96
95
  })(vNode || (exports.vNode = vNode = {}));
97
96
  function InitNode(vnode) {
98
- const { type, namespace, props, attrs, on, data,
99
- // componentConstructor,
100
- componentFactory, children, childrenArray, } = vnode.definition;
97
+ const { type, namespace, props, attrs, on, data, componentFactory, children, childrenArray, } = vnode.definition;
101
98
  const node = (vnode.node =
102
99
  vnode.definition.node ?? nodeConfig_1.NodeConfig.createNode(type, namespace));
103
100
  vnode.definition = null;
@@ -105,11 +102,8 @@ function InitNode(vnode) {
105
102
  const assignProperties = nodeConfig_1.NodeConfig.createPropertyAssignment(node);
106
103
  if (typeof props === "function") {
107
104
  const scope = Store_1.ObservableScope.Create(props);
108
- // const [value, scope] = ObservableScope.CreateIf(props as () => any);
109
- // if (scope) {
110
105
  vnode.scopes.push(scope);
111
106
  Store_1.ObservableScope.Watch(scope, ScheduledAssignment(assignProperties));
112
- // }
113
107
  assignProperties(Store_1.ObservableScope.Value(scope));
114
108
  }
115
109
  else
@@ -119,11 +113,8 @@ function InitNode(vnode) {
119
113
  const assignEvents = nodeConfig_1.NodeConfig.createEventAssignment(node);
120
114
  if (typeof on === "function") {
121
115
  const scope = Store_1.ObservableScope.Create(on);
122
- // const [value, scope] = ObservableScope.CreateIf(on);
123
- // if (scope) {
124
116
  vnode.scopes.push(scope);
125
117
  Store_1.ObservableScope.Watch(scope, ScheduledAssignment(assignEvents));
126
- // }
127
118
  assignEvents(Store_1.ObservableScope.Value(scope));
128
119
  }
129
120
  else
@@ -133,19 +124,14 @@ function InitNode(vnode) {
133
124
  const assignAttributes = nodeConfig_1.NodeConfig.createAttributeAssignment(node);
134
125
  if (typeof attrs === "function") {
135
126
  const scope = Store_1.ObservableScope.Create(attrs);
136
- // const [value, scope] = ObservableScope.CreateIf(attrs);
137
- // if (scope) {
138
127
  vnode.scopes.push(scope);
139
128
  Store_1.ObservableScope.Watch(scope, ScheduledAssignment(assignAttributes));
140
- //}
141
129
  assignAttributes(Store_1.ObservableScope.Value(scope));
142
130
  }
143
131
  else
144
132
  assignAttributes(attrs);
145
133
  }
146
- // if (componentConstructor) {
147
134
  if (componentFactory) {
148
- // vnode.component = new componentConstructor(vnode);
149
135
  vnode.component = componentFactory(vnode);
150
136
  vnode.component.Bound();
151
137
  function componentChildren() {
@@ -171,7 +157,6 @@ function Children(vnode, children, data) {
171
157
  return;
172
158
  const startChildren = vnode.children;
173
159
  const newChildren = Store_1.ObservableScope.Value(scope);
174
- // AssignChildren(vnode, scope);
175
160
  if (startChildren !== newChildren) {
176
161
  vnode.children = newChildren;
177
162
  UpdateChildren(vnode);
@@ -179,21 +164,7 @@ function Children(vnode, children, data) {
179
164
  }));
180
165
  }
181
166
  vnode.children = Store_1.ObservableScope.Value(childrenScope);
182
- // AssignChildren(vnode, childrenScope);
183
167
  }
184
- /* function AssignChildren(
185
- vnode: vNodeType,
186
- childrenScope: IObservableScope<
187
- [
188
- any,
189
- vNodeType[],
190
- IObservableScope<string | vNodeType | vNodeType[]> | null,
191
- ][]
192
- >,
193
- ) {
194
- const children = ObservableScope.Peek(childrenScope);
195
- vnode.children = children;
196
- } */
197
168
  const DEFAULT_DATA = [undefined];
198
169
  function DefaultData() {
199
170
  return DEFAULT_DATA;
@@ -302,7 +273,6 @@ function EvaluateNextNodesLarge(injector, getNextChildren, nextData, nodeArray)
302
273
  if (currentChildIndex !== -1) {
303
274
  const currentChild = currentChildren[currentChildIndex];
304
275
  currentChildren[currentChildIndex] = null;
305
- // if (currentChild[2]) {
306
276
  const scope = currentChild[2];
307
277
  const value = scope.value;
308
278
  const updatedValue = Store_1.ObservableScope.Value(scope);
@@ -310,13 +280,7 @@ function EvaluateNextNodesLarge(injector, getNextChildren, nextData, nodeArray)
310
280
  vNode.DestroyAll(currentChild[1]);
311
281
  currentChild[1] = CreateNodeArray(updatedValue);
312
282
  }
313
- // }
314
- /* if (currentChild[2]?.dirty) {
315
- const nextChildren = ObservableScope.Value(currentChild[2]);
316
- currentChild[1] = CreateNodeArray(nextChildren);
317
- } */
318
283
  nextNodes[x] = currentChild;
319
- // currentChildren[currentChildIndex] = null;
320
284
  if (currentChildIndex === 0)
321
285
  dataMap.delete(data);
322
286
  }
@@ -324,9 +288,6 @@ function EvaluateNextNodesLarge(injector, getNextChildren, nextData, nodeArray)
324
288
  const scope = Store_1.ObservableScope.Create(function () {
325
289
  return injector_1.Injector.Scope(injector, getNextChildren, data);
326
290
  });
327
- /* const [nextChildren, scope] = ObservableScope.CreateIf(function () {
328
- return Injector.Scope(injector, getNextChildren, data);
329
- }); */
330
291
  nextNodes[x] = [
331
292
  data,
332
293
  CreateNodeArray(Store_1.ObservableScope.Value(scope)),
@@ -411,7 +372,6 @@ function ScheduledAssignment(assign) {
411
372
  return;
412
373
  scheduled = true;
413
374
  nodeConfig_1.NodeConfig.scheduleUpdate(function () {
414
- // if (scope.destroyed) return;
415
375
  scheduled = false;
416
376
  const value = Store_1.ObservableScope.Peek(scope);
417
377
  assign(value);
@@ -1,12 +1,56 @@
1
1
  import { JsonDiffResult } from "../../Utils/json";
2
- export declare const IS_OBSERVABLE_NODE = "____isObservableNode";
3
- export declare const GET_OBSERVABLE_VALUE = "____getObservableValue";
4
- export declare const GET_TO_JSON = "toJSON";
2
+ /**
3
+ * Symbol to identify observable nodes.
4
+ * Used to check if a value is an observable node proxy.
5
+ */
6
+ export declare const IS_OBSERVABLE_NODE: unique symbol;
7
+ /**
8
+ * Symbol to get the raw underlying value from an observable node.
9
+ * Returns the unwrapped, non-proxied value.
10
+ */
11
+ export declare const GET_OBSERVABLE_VALUE: unique symbol;
12
+ /**
13
+ * Symbol for the toJSON method on observable nodes.
14
+ * Used to serialize observable nodes to plain JSON.
15
+ */
16
+ export declare const GET_TO_JSON: unique symbol;
5
17
  export declare namespace ObservableNode {
6
- function BypassProxy(value: boolean): void;
18
+ /**
19
+ * Unwraps an observable node to get the raw underlying value.
20
+ * Recursively unwraps nested objects and arrays.
21
+ * @template T The type of value to unwrap.
22
+ * @param value The value to unwrap, which may be an observable node or plain value.
23
+ * @returns The unwrapped raw value without proxy wrappers.
24
+ */
7
25
  function Unwrap<T>(value: T): T;
26
+ /**
27
+ * Creates an observable node from a plain value.
28
+ * Wraps the value in a proxy that tracks changes and enables reactive updates.
29
+ * @template T The type of value to wrap.
30
+ * @param value The plain value (object, array, or primitive) to make observable.
31
+ * @returns A proxied version of the value that emits change events.
32
+ */
8
33
  function Create<T>(value: T): T;
34
+ /**
35
+ * Marks an observable node or its property as changed, triggering reactive updates.
36
+ * Used internally to notify dependencies that a value has been modified.
37
+ * @param value The observable node to touch.
38
+ * @param prop Optional property name or index to touch a specific nested property.
39
+ */
9
40
  function Touch(value: unknown, prop?: string | number): void;
41
+ /**
42
+ * Applies a JSON diff result to an observable node, efficiently updating only changed properties.
43
+ * Optimizes nested object updates by computing paths incrementally and touching modified properties.
44
+ * @param rootNode The observable node to apply the diff to.
45
+ * @param diffResult The diff result from JsonDiff containing path-value pairs of changes.
46
+ */
10
47
  function ApplyDiff(rootNode: any, diffResult: JsonDiffResult): void;
48
+ /**
49
+ * Creates a factory function for making values observable with optional aliasing.
50
+ * The alias function transforms values before creating the observable proxy,
51
+ * useful for read-only views or value transformations.
52
+ * @param alias Optional function to transform values before making them observable.
53
+ * @returns A function that creates observable nodes from plain values.
54
+ */
11
55
  function CreateFactory(alias?: (value: any) => any | undefined): <T>(value: T) => T;
12
56
  }
@@ -4,10 +4,21 @@ exports.ObservableNode = exports.GET_TO_JSON = exports.GET_OBSERVABLE_VALUE = ex
4
4
  const json_1 = require("../../Utils/json");
5
5
  const jsonType_1 = require("../../Utils/jsonType");
6
6
  const observableScope_1 = require("./observableScope");
7
- let bypassProxy = false;
8
- exports.IS_OBSERVABLE_NODE = "____isObservableNode";
9
- exports.GET_OBSERVABLE_VALUE = "____getObservableValue";
10
- exports.GET_TO_JSON = "toJSON";
7
+ /**
8
+ * Symbol to identify observable nodes.
9
+ * Used to check if a value is an observable node proxy.
10
+ */
11
+ exports.IS_OBSERVABLE_NODE = Symbol("isObservableNode");
12
+ /**
13
+ * Symbol to get the raw underlying value from an observable node.
14
+ * Returns the unwrapped, non-proxied value.
15
+ */
16
+ exports.GET_OBSERVABLE_VALUE = Symbol("getObservableValue");
17
+ /**
18
+ * Symbol for the toJSON method on observable nodes.
19
+ * Used to serialize observable nodes to plain JSON.
20
+ */
21
+ exports.GET_TO_JSON = Symbol("toJSON");
11
22
  const proxyCache = new WeakMap();
12
23
  const scopeCache = new WeakMap();
13
24
  const leafScopeCache = new WeakMap();
@@ -40,7 +51,7 @@ function ownKeysArray(value) {
40
51
  function UnwrapProxy(value, type = (0, jsonType_1.JsonType)(value)) {
41
52
  if (type === "value")
42
53
  return value;
43
- if (value[exports.IS_OBSERVABLE_NODE] === true)
54
+ if (value[exports.IS_OBSERVABLE_NODE])
44
55
  return value[exports.GET_OBSERVABLE_VALUE];
45
56
  switch (type) {
46
57
  case "object": {
@@ -114,8 +125,6 @@ function CreateProxyFactory(alias) {
114
125
  function ArrayProxySetter(array, prop, value) {
115
126
  if (readOnly)
116
127
  throw `Object is readonly`;
117
- if (prop === exports.IS_OBSERVABLE_NODE)
118
- throw `Cannot assign read-only property: ${exports.IS_OBSERVABLE_NODE}`;
119
128
  value = UnwrapProxy(value);
120
129
  array[prop] = value;
121
130
  const scope = scopeCache.get(array);
@@ -147,8 +156,6 @@ function CreateProxyFactory(alias) {
147
156
  const scope = scopeCache.get(array);
148
157
  array = observableScope_1.ObservableScope.Value(scope);
149
158
  const arrayValue = array[prop];
150
- if (bypassProxy)
151
- return arrayValue;
152
159
  if (typeof prop === "symbol")
153
160
  return arrayValue;
154
161
  if (typeof arrayValue === "function")
@@ -188,8 +195,6 @@ function CreateProxyFactory(alias) {
188
195
  function ObjectProxySetter(object, prop, value) {
189
196
  if (readOnly)
190
197
  throw `Object is readonly`;
191
- if (prop === exports.IS_OBSERVABLE_NODE)
192
- throw `Cannot assign read-only property: ${exports.IS_OBSERVABLE_NODE}`;
193
198
  const jsonType = (0, jsonType_1.JsonType)(value);
194
199
  if (jsonType === "value") {
195
200
  value !== object[prop] && SetPropertyValue(object, prop, value);
@@ -224,8 +229,6 @@ function CreateProxyFactory(alias) {
224
229
  case exports.GET_OBSERVABLE_VALUE:
225
230
  return object;
226
231
  default: {
227
- if (bypassProxy)
228
- return object[prop];
229
232
  return GetAccessorValue(object, prop);
230
233
  }
231
234
  }
@@ -264,18 +267,34 @@ function CreateProxyFactory(alias) {
264
267
  const DefaultCreateProxy = CreateProxyFactory();
265
268
  var ObservableNode;
266
269
  (function (ObservableNode) {
267
- function BypassProxy(value) {
268
- bypassProxy = value;
269
- }
270
- ObservableNode.BypassProxy = BypassProxy;
270
+ /**
271
+ * Unwraps an observable node to get the raw underlying value.
272
+ * Recursively unwraps nested objects and arrays.
273
+ * @template T The type of value to unwrap.
274
+ * @param value The value to unwrap, which may be an observable node or plain value.
275
+ * @returns The unwrapped raw value without proxy wrappers.
276
+ */
271
277
  function Unwrap(value) {
272
278
  return UnwrapProxy(value);
273
279
  }
274
280
  ObservableNode.Unwrap = Unwrap;
281
+ /**
282
+ * Creates an observable node from a plain value.
283
+ * Wraps the value in a proxy that tracks changes and enables reactive updates.
284
+ * @template T The type of value to wrap.
285
+ * @param value The plain value (object, array, or primitive) to make observable.
286
+ * @returns A proxied version of the value that emits change events.
287
+ */
275
288
  function Create(value) {
276
289
  return DefaultCreateProxy(value);
277
290
  }
278
291
  ObservableNode.Create = Create;
292
+ /**
293
+ * Marks an observable node or its property as changed, triggering reactive updates.
294
+ * Used internally to notify dependencies that a value has been modified.
295
+ * @param value The observable node to touch.
296
+ * @param prop Optional property name or index to touch a specific nested property.
297
+ */
279
298
  function Touch(value, prop) {
280
299
  let scope;
281
300
  if (prop !== undefined) {
@@ -286,6 +305,12 @@ var ObservableNode;
286
305
  observableScope_1.ObservableScope.Update(scope);
287
306
  }
288
307
  ObservableNode.Touch = Touch;
308
+ /**
309
+ * Applies a JSON diff result to an observable node, efficiently updating only changed properties.
310
+ * Optimizes nested object updates by computing paths incrementally and touching modified properties.
311
+ * @param rootNode The observable node to apply the diff to.
312
+ * @param diffResult The diff result from JsonDiff containing path-value pairs of changes.
313
+ */
289
314
  function ApplyDiff(rootNode, diffResult) {
290
315
  const root = rootNode[exports.GET_OBSERVABLE_VALUE];
291
316
  const pathTuples = [["", root]];
@@ -312,6 +337,13 @@ var ObservableNode;
312
337
  }
313
338
  }
314
339
  ObservableNode.ApplyDiff = ApplyDiff;
340
+ /**
341
+ * Creates a factory function for making values observable with optional aliasing.
342
+ * The alias function transforms values before creating the observable proxy,
343
+ * useful for read-only views or value transformations.
344
+ * @param alias Optional function to transform values before making them observable.
345
+ * @returns A function that creates observable nodes from plain values.
346
+ */
315
347
  function CreateFactory(alias) {
316
348
  return CreateProxyFactory(alias);
317
349
  }
@@ -5,6 +5,7 @@ exports.CalcScope = CalcScope;
5
5
  const array_1 = require("../../Utils/array");
6
6
  const emitter_1 = require("../../Utils/emitter");
7
7
  const functions_1 = require("../../Utils/functions");
8
+ const list_1 = require("../../Utils/list");
8
9
  /**
9
10
  * Creates a dynamic (reactive) observable scope.
10
11
  * @template T The type of value stored in the scope.
@@ -47,6 +48,10 @@ function CreateStaticScope(initialValue) {
47
48
  };
48
49
  }
49
50
  let scopeQueue = [];
51
+ /**
52
+ * Processes all queued scopes, recomputing dirty scopes and emitting changes.
53
+ * Executed as a microtask to batch updates efficiently.
54
+ */
50
55
  function ProcessScopeQueue() {
51
56
  const queue = scopeQueue;
52
57
  scopeQueue = [];
@@ -60,6 +65,11 @@ function ProcessScopeQueue() {
60
65
  }
61
66
  }
62
67
  }
68
+ /**
69
+ * Queues a scope for batched update processing.
70
+ * Schedules ProcessScopeQueue as a microtask if the queue was empty.
71
+ * @param scope The scope to queue for update.
72
+ */
63
73
  function OnSetQueued(scope) {
64
74
  if (scopeQueue.length === 0)
65
75
  queueMicrotask(ProcessScopeQueue);
@@ -84,43 +94,93 @@ function OnSet(scope) {
84
94
  return false;
85
95
  }
86
96
  /**
87
- * Registers an emitter as a dependency for the current watch context.
88
- * Tracks which emitters are accessed during a scope's execution.
89
- * @param emitter The emitter to register as a dependency.
97
+ * Registers an emitter using SAME_STRATEGY.
98
+ * If the emitter is already registered at the current index, increments the index.
99
+ * Otherwise, switches to PUSH_STRATEGY and appends the emitter.
100
+ * @param state The current watch state.
101
+ * @param emitter The emitter to register.
90
102
  */
91
- function RegisterEmitter(emitter) {
92
- if (watchState === null)
93
- return;
94
- if (watchState.emitterIndex !== null &&
95
- watchState.emitters[watchState.emitterIndex] === emitter) {
96
- watchState.emitterIndex++;
103
+ function RegisterSame(state, emitter) {
104
+ if (state.emitterIndex < state.emitters.length &&
105
+ state.emitters[state.emitterIndex] === emitter) {
106
+ state.emitterIndex++;
97
107
  return;
98
108
  }
99
- else if (watchState.emitterIndex !== null) {
100
- const index = watchState.emitterIndex;
101
- watchState.emitterIndex = null;
102
- watchState.emitters = watchState.emitters.slice(0, index);
103
- }
104
- if (watchState.emitterIds) {
105
- if (!watchState.emitterIds.has(emitter[0])) {
106
- watchState.emitters.push(emitter);
107
- watchState.emitterIds.add(emitter[0]);
108
- }
109
- return;
109
+ state.emitters = state.emitters.slice(0, state.emitterIndex);
110
+ state.strategy = SORTED_STRATEGY;
111
+ RegisterSorted(state, emitter);
112
+ }
113
+ /**
114
+ * Registers an emitter while maintaining sorted order by emitter ID.
115
+ * If insertion would break sort order, falls back to PUSH_STRATEGY.
116
+ * @param state The current watch state.
117
+ * @param emitter The emitter to register.
118
+ */
119
+ function RegisterSorted(state, emitter) {
120
+ if (state.emitters.length === 0 ||
121
+ state.emitters[state.emitters.length - 1][0] < emitter[0])
122
+ state.emitters.push(emitter);
123
+ else {
124
+ state.strategy = PUSH_STRATEGY;
125
+ RegisterPush(state, emitter);
110
126
  }
111
- watchState.emitters.push(emitter);
112
- if (watchState.emitters.length > 50) {
113
- const idSet = (watchState.emitterIds = new Set());
127
+ }
128
+ /**
129
+ * Registers an emitter using PUSH_STRATEGY.
130
+ * Switches to DISTINCT_STRATEGY when the emitter count exceeds 50 to optimize memory.
131
+ * @param state The current watch state.
132
+ * @param emitter The emitter to register.
133
+ */
134
+ function RegisterPush(state, emitter) {
135
+ state.emitters.push(emitter);
136
+ if (state.emitters.length > 50) {
137
+ const idSet = (state.emitterIds = new Set([state.emitters[0][0]]));
114
138
  let writePos = 0;
115
- for (let x = 1; x < watchState.emitters.length; x++) {
116
- if (!idSet.has(watchState.emitters[x][0])) {
117
- watchState.emitters[++writePos] = watchState.emitters[x];
118
- idSet.add(watchState.emitters[x][0]);
139
+ for (let x = 1; x < state.emitters.length; x++) {
140
+ if (!idSet.has(state.emitters[x][0])) {
141
+ state.emitters[++writePos] = state.emitters[x];
142
+ idSet.add(state.emitters[x][0]);
119
143
  }
120
144
  }
121
145
  writePos++;
122
- if (writePos < watchState.emitters.length)
123
- watchState.emitters.splice(writePos);
146
+ if (writePos < state.emitters.length)
147
+ state.emitters.splice(writePos);
148
+ state.strategy = DISTINCT_STRATEGY;
149
+ }
150
+ }
151
+ /**
152
+ * Registers an emitter using DISTINCT_STRATEGY.
153
+ * Only adds emitters that haven't been registered yet, using an ID set for O(1) lookup.
154
+ * @param state The current watch state.
155
+ * @param emitter The emitter to register.
156
+ */
157
+ function RegisterDistinct(state, emitter) {
158
+ if (!state.emitterIds.has(emitter[0])) {
159
+ state.emitters.push(emitter);
160
+ state.emitterIds.add(emitter[0]);
161
+ }
162
+ }
163
+ /**
164
+ * Routes an emitter to the appropriate registration strategy based on current watch state.
165
+ * Does nothing if not within a watch context.
166
+ * @param emitter The emitter to register.
167
+ */
168
+ function RegisterEmitter(emitter) {
169
+ if (watchState === null)
170
+ return;
171
+ switch (watchState.strategy) {
172
+ case SAME_STRATEGY:
173
+ RegisterSame(watchState, emitter);
174
+ break;
175
+ case SORTED_STRATEGY:
176
+ RegisterSorted(watchState, emitter);
177
+ break;
178
+ case PUSH_STRATEGY:
179
+ RegisterPush(watchState, emitter);
180
+ break;
181
+ case DISTINCT_STRATEGY:
182
+ RegisterDistinct(watchState, emitter);
183
+ break;
124
184
  }
125
185
  }
126
186
  /**
@@ -144,7 +204,48 @@ function GetScopeValue(scope) {
144
204
  ExecuteScope(scope);
145
205
  return scope.value;
146
206
  }
207
+ /**
208
+ * Strategy constants for optimizing emitter registration during watch operations.
209
+ * Each strategy represents a different approach to tracking and deduplicating dependencies.
210
+ */
211
+ const SAME_STRATEGY = 1;
212
+ const PUSH_STRATEGY = 2;
213
+ const SORTED_STRATEGY = 3;
214
+ const DISTINCT_STRATEGY = 4;
215
+ const SHRINK_STRATEGY = 5;
147
216
  let watchState = null;
217
+ const watchPool = list_1.List.Create();
218
+ /**
219
+ * Creates a new watch state object, reusing from pool if available.
220
+ * Object pooling reduces GC pressure during frequent watch operations.
221
+ * @returns A new or recycled watch state object.
222
+ */
223
+ function CreateWatchState() {
224
+ return (list_1.List.Pop(watchPool) ?? {
225
+ emitterIndex: 0,
226
+ value: null,
227
+ emitters: null,
228
+ emitterIds: null,
229
+ currentCalc: null,
230
+ nextCalc: null,
231
+ strategy: PUSH_STRATEGY,
232
+ });
233
+ }
234
+ /**
235
+ * Returns a watch state object to the pool for reuse.
236
+ * Resets all fields to their default values before pooling.
237
+ * @param state The watch state to return to the pool.
238
+ */
239
+ function ReturnWatchState(state) {
240
+ state.emitterIndex = 0;
241
+ state.value = null;
242
+ state.emitters = null;
243
+ state.emitterIds = null;
244
+ state.currentCalc = null;
245
+ state.nextCalc = null;
246
+ state.strategy = SORTED_STRATEGY;
247
+ list_1.List.Push(watchPool, state);
248
+ }
148
249
  /**
149
250
  * Executes a callback while tracking all scope and emitter dependencies.
150
251
  * Creates a watch context that records what was accessed during execution.
@@ -152,22 +253,21 @@ let watchState = null;
152
253
  * @param currentCalc Optional map of existing calc scopes to reuse.
153
254
  * @returns The watch state containing tracked dependencies and result.
154
255
  */
155
- function WatchFunction(callback, currentCalc = null, initialEmitters = []) {
256
+ function WatchFunction(callback, currentCalc, initialEmitters) {
156
257
  const parent = watchState;
157
- watchState = {
158
- emitterIndex: initialEmitters.length > 0 ? 0 : null,
159
- value: null,
160
- emitters: initialEmitters,
161
- emitterIds: null,
162
- currentCalc: currentCalc,
163
- nextCalc: null,
164
- };
258
+ watchState = CreateWatchState();
259
+ watchState.emitters = initialEmitters ?? [];
260
+ watchState.currentCalc = currentCalc;
261
+ if (initialEmitters !== null)
262
+ watchState.strategy = SAME_STRATEGY;
165
263
  watchState.value = callback();
166
264
  const resultState = watchState;
167
265
  watchState = parent;
168
- if (resultState.emitterIndex !== null &&
169
- resultState.emitterIndex < resultState.emitters.length)
266
+ if (resultState.strategy === SAME_STRATEGY &&
267
+ resultState.emitterIndex < resultState.emitters.length) {
170
268
  resultState.emitters = resultState.emitters.slice(0, resultState.emitterIndex);
269
+ resultState.strategy = SHRINK_STRATEGY;
270
+ }
171
271
  return resultState;
172
272
  }
173
273
  /**
@@ -178,8 +278,7 @@ function WatchFunction(callback, currentCalc = null, initialEmitters = []) {
178
278
  function ExecuteScope(scope) {
179
279
  scope.dirty = false;
180
280
  const state = WatchFunction(scope.getFunction, scope.calcScopes, scope.emitters);
181
- if (state.emitters !== scope.emitters)
182
- UpdateEmitters(scope, state.emitters, !!state.emitterIds);
281
+ UpdateEmitters(scope, state.emitters, state.strategy);
183
282
  const calcScopes = state.currentCalc;
184
283
  scope.calcScopes = state.nextCalc;
185
284
  for (const key in calcScopes)
@@ -191,6 +290,7 @@ function ExecuteScope(scope) {
191
290
  });
192
291
  else
193
292
  scope.value = state.value;
293
+ ReturnWatchState(state);
194
294
  }
195
295
  /**
196
296
  * Creates a scope from a function, choosing between static and dynamic based on dependencies.
@@ -202,19 +302,22 @@ function ExecuteScope(scope) {
202
302
  */
203
303
  function ExecuteFunction(callback, greedy, allowStatic) {
204
304
  const async = (0, functions_1.IsAsync)(callback);
205
- const state = WatchFunction(callback);
206
- if (!allowStatic || async || state.emitters.length > 0) {
305
+ const state = WatchFunction(callback, null, null);
306
+ if (!allowStatic || async || state.emitters !== null) {
207
307
  const scope = CreateDynamicScope(callback, greedy, async ? null : state.value);
208
308
  scope.calcScopes = state.nextCalc;
209
- UpdateEmitters(scope, state.emitters, !!state.emitterIds);
309
+ UpdateEmitters(scope, state.emitters, state.strategy);
210
310
  if (async)
211
311
  state.value.then(function (result) {
212
312
  scope.value = result;
213
313
  emitter_1.Emitter.Emit(scope.emitter, scope);
214
314
  });
315
+ ReturnWatchState(state);
215
316
  return scope;
216
317
  }
217
- return CreateStaticScope(state.value);
318
+ const value = state.value;
319
+ ReturnWatchState(state);
320
+ return CreateStaticScope(value);
218
321
  }
219
322
  /**
220
323
  * Creates a computed scope that acts as a gatekeeper for parent scope emissions.
@@ -254,27 +357,32 @@ function CalcScope(callback, idOverride) {
254
357
  * @param right The new list of emitters to track.
255
358
  * @param distinct Whether emitters are already unique (sorted and deduplicated).
256
359
  */
257
- function UpdateEmitters(scope, right, distinct = false) {
258
- distinct ? emitter_1.Emitter.Sort(right) : emitter_1.Emitter.Distinct(right);
259
- if (scope.emitters === null) {
260
- for (let x = 0; x < right.length; x++)
261
- emitter_1.Emitter.On(right[x], scope.setCallback);
262
- scope.emitters = right;
263
- return;
264
- }
265
- if (right.length === 0) {
266
- if (scope.emitters.length > 0) {
267
- for (let x = 0; x < scope.emitters.length; x++)
360
+ function UpdateEmitters(scope, right, strategy) {
361
+ switch (strategy) {
362
+ case SHRINK_STRATEGY: {
363
+ for (let x = right.length; x < scope.emitters.length; x++)
268
364
  emitter_1.Emitter.Remove(scope.emitters[x], scope.setCallback);
269
- scope.emitters = [];
365
+ break;
270
366
  }
271
- return;
367
+ case PUSH_STRATEGY:
368
+ case DISTINCT_STRATEGY:
369
+ strategy === PUSH_STRATEGY
370
+ ? emitter_1.Emitter.Distinct(right)
371
+ : emitter_1.Emitter.Sort(right);
372
+ case SORTED_STRATEGY:
373
+ if (scope.emitters === null || scope.emitters.length === 0) {
374
+ for (let x = 0; x < right.length; x++)
375
+ emitter_1.Emitter.On(right[x], scope.setCallback);
376
+ }
377
+ else {
378
+ (0, array_1.ReconcileSortedEmitters)(scope.emitters, right, function (emitter) {
379
+ emitter_1.Emitter.On(emitter, scope.setCallback);
380
+ }, function (emitter) {
381
+ emitter_1.Emitter.Remove(emitter, scope.setCallback);
382
+ });
383
+ }
384
+ break;
272
385
  }
273
- (0, array_1.ReconcileSortedEmitters)(scope.emitters, right, function (emitter) {
274
- emitter_1.Emitter.On(emitter, scope.setCallback);
275
- }, function (emitter) {
276
- emitter_1.Emitter.Remove(emitter, scope.setCallback);
277
- });
278
386
  scope.emitters = right;
279
387
  }
280
388
  /**
@@ -20,9 +20,7 @@ exports.Scope = Scope;
20
20
  exports.Watch = Watch;
21
21
  exports.Inject = Inject;
22
22
  exports.Destroy = Destroy;
23
- // import { Component } from "../Node/component";
24
23
  const observableScope_1 = require("../Store/Tree/observableScope");
25
- // import { ElementNodeRefTypes } from "../Node/nodeRef.types";
26
24
  const observableNode_1 = require("../Store/Tree/observableNode");
27
25
  const Store_1 = require("../Store");
28
26
  /**
@@ -365,14 +363,6 @@ function WatchDecorator(target, propertyKey, descriptor, scopeFunction) {
365
363
  observableScope_1.ObservableScope.Watch(scope, function (scope) {
366
364
  instance[propertyKey](observableScope_1.ObservableScope.Value(scope));
367
365
  });
368
- /* const [value, scope] = ObservableScope.CreateIf(scopeFunctionWrapper, true);
369
- if (scope) {
370
- const propertyMap = GetScopeMapForInstance(this);
371
- propertyMap[propertyKey as string] = [scope, undefined];
372
- ObservableScope.Watch(scope, function (scope) {
373
- (instance as any)[propertyKey](ObservableScope.Value(scope));
374
- });
375
- } */
376
366
  instance[propertyKey](observableScope_1.ObservableScope.Value(scope));
377
367
  return instance;
378
368
  }
package/Utils/emitter.js CHANGED
@@ -42,11 +42,6 @@ var Emitter;
42
42
  function Distinct(emitters) {
43
43
  if (emitters.length < 2)
44
44
  return;
45
- // emitters.length < 51 ? DistinctSmall(emitters) : DistinctLarge(emitters);
46
- DistinctSmall(emitters);
47
- }
48
- Emitter.Distinct = Distinct;
49
- function DistinctSmall(emitters) {
50
45
  Sort(emitters);
51
46
  let writePos = 0;
52
47
  for (let x = 1; x < emitters.length; x++) {
@@ -58,20 +53,7 @@ var Emitter;
58
53
  if (writePos < emitters.length)
59
54
  emitters.splice(writePos);
60
55
  }
61
- function DistinctLarge(emitters) {
62
- let writePos = 0;
63
- const ids = new Set();
64
- for (let x = 0; x < emitters.length; x++) {
65
- const id = emitters[x][0];
66
- if (!ids.has(id)) {
67
- ids.add(id);
68
- emitters[writePos++] = emitters[x];
69
- }
70
- }
71
- if (writePos < emitters.length)
72
- emitters.splice(writePos);
73
- Sort(emitters);
74
- }
56
+ Emitter.Distinct = Distinct;
75
57
  function Sort(emitters) {
76
58
  if (emitters.length < 11)
77
59
  (0, array_1.InsertionSortTuples)(emitters);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "j-templates",
3
- "version": "7.0.69",
3
+ "version": "7.0.71",
4
4
  "description": "j-templates",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/TypesInCode/jTemplates",