@mintjamsinc/ichigojs 0.1.1 → 0.1.3
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/dist/ichigo.esm.js +216 -41
- package/dist/ichigo.esm.js.map +1 -1
- package/dist/ichigo.esm.min.js +1 -1
- package/dist/ichigo.esm.min.js.map +1 -1
- package/dist/ichigo.umd.js +216 -41
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/ichigo.umd.min.js.map +1 -1
- package/dist/types/ichigo/VBindingsInit.d.ts +5 -0
- package/dist/types/ichigo/VNode.d.ts +11 -1
- package/dist/types/ichigo/VNodeInit.d.ts +5 -0
- package/dist/types/ichigo/util/ReactiveProxy.d.ts +6 -5
- package/dist/types/ichigo/util/VLogManager.d.ts +15 -0
- package/dist/types/ichigo/util/VLogger.d.ts +31 -0
- package/package.json +2 -2
package/dist/ichigo.umd.js
CHANGED
@@ -7050,34 +7050,46 @@
|
|
7050
7050
|
*/
|
7051
7051
|
class ReactiveProxy {
|
7052
7052
|
/**
|
7053
|
-
* A WeakMap to store the
|
7054
|
-
* This
|
7053
|
+
* A WeakMap to store the proxy for each target object and path combination.
|
7054
|
+
* This prevents creating multiple proxies for the same object accessed from different paths.
|
7055
7055
|
*/
|
7056
|
-
static
|
7056
|
+
static proxyCache = new WeakMap();
|
7057
7057
|
/**
|
7058
7058
|
* Creates a reactive proxy for the given object.
|
7059
7059
|
* The proxy will call the onChange callback whenever a property is modified.
|
7060
7060
|
*
|
7061
7061
|
* @param target The object to make reactive.
|
7062
|
-
* @param onChange Callback function to call when the object changes. Receives the changed
|
7062
|
+
* @param onChange Callback function to call when the object changes. Receives the full path of the changed property.
|
7063
|
+
* @param path The current path in the object tree (used internally for nested objects).
|
7063
7064
|
* @returns A reactive proxy of the target object.
|
7064
7065
|
*/
|
7065
|
-
static create(target, onChange) {
|
7066
|
+
static create(target, onChange, path = '') {
|
7066
7067
|
// If the target is not an object or is null, return it as-is
|
7067
7068
|
if (typeof target !== 'object' || target === null) {
|
7068
7069
|
return target;
|
7069
7070
|
}
|
7070
|
-
//
|
7071
|
-
|
7072
|
-
|
7071
|
+
// Check if we already have a proxy for this target with this path
|
7072
|
+
let pathMap = this.proxyCache.get(target);
|
7073
|
+
if (pathMap) {
|
7074
|
+
const existingProxy = pathMap.get(path);
|
7075
|
+
if (existingProxy) {
|
7076
|
+
return existingProxy;
|
7077
|
+
}
|
7078
|
+
}
|
7079
|
+
else {
|
7080
|
+
pathMap = new Map();
|
7081
|
+
this.proxyCache.set(target, pathMap);
|
7073
7082
|
}
|
7074
|
-
// Create the proxy
|
7083
|
+
// Create the proxy with path captured in closure
|
7075
7084
|
const proxy = new Proxy(target, {
|
7076
7085
|
get(obj, key) {
|
7077
7086
|
const value = Reflect.get(obj, key);
|
7078
7087
|
// If the value is an object or array, make it reactive too
|
7079
7088
|
if (typeof value === 'object' && value !== null) {
|
7080
|
-
|
7089
|
+
// Build the nested path
|
7090
|
+
const keyStr = String(key);
|
7091
|
+
const nestedPath = path ? (Array.isArray(obj) ? `${path}[${keyStr}]` : `${path}.${keyStr}`) : keyStr;
|
7092
|
+
return ReactiveProxy.create(value, onChange, nestedPath);
|
7081
7093
|
}
|
7082
7094
|
// For arrays, intercept mutation methods
|
7083
7095
|
if (Array.isArray(obj) && typeof value === 'function') {
|
@@ -7085,7 +7097,7 @@
|
|
7085
7097
|
if (arrayMutationMethods.includes(key)) {
|
7086
7098
|
return function (...args) {
|
7087
7099
|
const result = value.apply(this, args);
|
7088
|
-
onChange();
|
7100
|
+
onChange(path || undefined);
|
7089
7101
|
return result;
|
7090
7102
|
};
|
7091
7103
|
}
|
@@ -7097,18 +7109,22 @@
|
|
7097
7109
|
const result = Reflect.set(obj, key, value);
|
7098
7110
|
// Only trigger onChange if the value actually changed
|
7099
7111
|
if (oldValue !== value) {
|
7100
|
-
|
7112
|
+
const keyStr = String(key);
|
7113
|
+
const fullPath = path ? (Array.isArray(obj) ? `${path}[${keyStr}]` : `${path}.${keyStr}`) : keyStr;
|
7114
|
+
onChange(fullPath);
|
7101
7115
|
}
|
7102
7116
|
return result;
|
7103
7117
|
},
|
7104
7118
|
deleteProperty(obj, key) {
|
7105
7119
|
const result = Reflect.deleteProperty(obj, key);
|
7106
|
-
|
7120
|
+
const keyStr = String(key);
|
7121
|
+
const fullPath = path ? (Array.isArray(obj) ? `${path}[${keyStr}]` : `${path}.${keyStr}`) : keyStr;
|
7122
|
+
onChange(fullPath);
|
7107
7123
|
return result;
|
7108
7124
|
}
|
7109
7125
|
});
|
7110
|
-
//
|
7111
|
-
|
7126
|
+
// Cache the proxy for this path
|
7127
|
+
pathMap.set(path, proxy);
|
7112
7128
|
return proxy;
|
7113
7129
|
}
|
7114
7130
|
/**
|
@@ -7118,7 +7134,7 @@
|
|
7118
7134
|
* @returns True if the object is a reactive proxy, false otherwise.
|
7119
7135
|
*/
|
7120
7136
|
static isReactive(obj) {
|
7121
|
-
return this.
|
7137
|
+
return this.proxyCache.has(obj);
|
7122
7138
|
}
|
7123
7139
|
/**
|
7124
7140
|
* Unwraps a reactive proxy to get the original object.
|
@@ -7153,10 +7169,18 @@
|
|
7153
7169
|
* The change tracker, if any.
|
7154
7170
|
*/
|
7155
7171
|
#onChange;
|
7172
|
+
/**
|
7173
|
+
* The logger instance.
|
7174
|
+
*/
|
7175
|
+
#logger;
|
7156
7176
|
/**
|
7157
7177
|
* The set of changed identifiers.
|
7158
7178
|
*/
|
7159
7179
|
#changes = new Set();
|
7180
|
+
/**
|
7181
|
+
* Cache for array lengths to detect length changes when the same object reference is used.
|
7182
|
+
*/
|
7183
|
+
#lengthCache = new Map();
|
7160
7184
|
/**
|
7161
7185
|
* Creates a new instance of VBindings.
|
7162
7186
|
* @param parent The parent bindings, if any.
|
@@ -7164,6 +7188,10 @@
|
|
7164
7188
|
constructor(args = {}) {
|
7165
7189
|
this.#parent = args.parent;
|
7166
7190
|
this.#onChange = args.onChange;
|
7191
|
+
this.#logger = args.vApplication?.logManager.getLogger('VBindings');
|
7192
|
+
if (this.#logger?.isDebugEnabled) {
|
7193
|
+
this.#logger.debug(`VBindings created. Parent: ${this.#parent ? 'yes' : 'no'}`);
|
7194
|
+
}
|
7167
7195
|
this.#local = new Proxy({}, {
|
7168
7196
|
get: (obj, key) => {
|
7169
7197
|
if (Reflect.has(obj, key)) {
|
@@ -7184,14 +7212,37 @@
|
|
7184
7212
|
let newValue = value;
|
7185
7213
|
if (typeof value === 'object' && value !== null) {
|
7186
7214
|
// Wrap objects/arrays with reactive proxy, tracking the root key
|
7187
|
-
newValue = ReactiveProxy.create(value, () => {
|
7188
|
-
|
7189
|
-
|
7190
|
-
|
7215
|
+
newValue = ReactiveProxy.create(value, (changedPath) => {
|
7216
|
+
let path = '';
|
7217
|
+
for (const part of changedPath?.split('.') || []) {
|
7218
|
+
path = path ? `${path}.${part}` : part;
|
7219
|
+
this.#logger?.debug(`Binding changed: ${path}`);
|
7220
|
+
this.#changes.add(path);
|
7221
|
+
}
|
7222
|
+
this.#onChange?.(changedPath);
|
7223
|
+
}, key);
|
7191
7224
|
}
|
7192
7225
|
const oldValue = Reflect.get(target, key);
|
7193
7226
|
const result = Reflect.set(target, key, newValue);
|
7194
|
-
|
7227
|
+
// Detect changes
|
7228
|
+
let hasChanged = oldValue !== newValue;
|
7229
|
+
// Special handling for arrays: check length changes even if same object reference
|
7230
|
+
if (Array.isArray(newValue)) {
|
7231
|
+
const cachedLength = this.#lengthCache.get(key);
|
7232
|
+
const currentLength = newValue.length;
|
7233
|
+
if (!hasChanged && cachedLength !== undefined && cachedLength !== currentLength) {
|
7234
|
+
hasChanged = true;
|
7235
|
+
}
|
7236
|
+
this.#lengthCache.set(key, currentLength);
|
7237
|
+
}
|
7238
|
+
if (hasChanged) {
|
7239
|
+
if (this.#logger?.isDebugEnabled) {
|
7240
|
+
const oldValueString = typeof oldValue === 'string' ? `"${oldValue}"` : JSON.stringify(oldValue) || 'undefined';
|
7241
|
+
const newValueString = typeof newValue === 'string' ? `"${newValue}"` : JSON.stringify(newValue) || 'undefined';
|
7242
|
+
const oldValuePreview = oldValueString.length > 100 ? `${oldValueString.substring(0, 100)}...` : oldValueString;
|
7243
|
+
const newValuePreview = newValueString.length > 100 ? `${newValueString.substring(0, 100)}...` : newValueString;
|
7244
|
+
this.#logger.debug(`Binding set on ${target === obj ? 'local' : 'parent'}: ${key}: ${oldValuePreview} -> ${newValuePreview}`);
|
7245
|
+
}
|
7195
7246
|
this.#changes.add(key);
|
7196
7247
|
this.#onChange?.(key);
|
7197
7248
|
}
|
@@ -7199,6 +7250,7 @@
|
|
7199
7250
|
},
|
7200
7251
|
deleteProperty: (obj, key) => {
|
7201
7252
|
const result = Reflect.deleteProperty(obj, key);
|
7253
|
+
this.#logger?.debug(`Binding deleted: ${key}`);
|
7202
7254
|
this.#changes.add(key);
|
7203
7255
|
this.#onChange?.(key);
|
7204
7256
|
return result;
|
@@ -7547,6 +7599,11 @@
|
|
7547
7599
|
* The data bindings associated with this virtual node, if any.
|
7548
7600
|
*/
|
7549
7601
|
#bindings;
|
7602
|
+
/**
|
7603
|
+
* The initial set of identifiers that this node depends on.
|
7604
|
+
* This is optional and may be undefined if there are no dependent identifiers.
|
7605
|
+
*/
|
7606
|
+
#initDependentIdentifiers;
|
7550
7607
|
/**
|
7551
7608
|
* An evaluator for text nodes that contain expressions in {{...}}.
|
7552
7609
|
* This is used to dynamically update the text content based on data bindings.
|
@@ -7591,6 +7648,7 @@
|
|
7591
7648
|
this.#nodeName = args.node.nodeName;
|
7592
7649
|
this.#parentVNode = args.parentVNode;
|
7593
7650
|
this.#bindings = args.bindings;
|
7651
|
+
this.#initDependentIdentifiers = args.dependentIdentifiers;
|
7594
7652
|
this.#parentVNode?.addChild(this);
|
7595
7653
|
// If the node is a text node, check for expressions and create a text evaluator
|
7596
7654
|
if (this.#nodeType === Node.TEXT_NODE) {
|
@@ -7742,6 +7800,8 @@
|
|
7742
7800
|
}
|
7743
7801
|
// Collect identifiers from text evaluator and directives
|
7744
7802
|
const ids = [];
|
7803
|
+
// Include initial dependent identifiers, if any
|
7804
|
+
ids.push(...this.#initDependentIdentifiers ?? []);
|
7745
7805
|
// If this is a text node with a text evaluator, include its identifiers
|
7746
7806
|
if (this.#textEvaluator) {
|
7747
7807
|
ids.push(...this.#textEvaluator.identifiers);
|
@@ -7773,6 +7833,29 @@
|
|
7773
7833
|
this.#preparableIdentifiers = preparableIdentifiers.length === 0 ? [] : [...new Set(preparableIdentifiers)];
|
7774
7834
|
return this.#preparableIdentifiers;
|
7775
7835
|
}
|
7836
|
+
/**
|
7837
|
+
* The DOM path of this virtual node.
|
7838
|
+
* This is a string representation of the path from the root to this node,
|
7839
|
+
* using the node names and their indices among siblings with the same name.
|
7840
|
+
* For example: "DIV[0]/SPAN[1]/#text[0]"
|
7841
|
+
* @return The DOM path as a string.
|
7842
|
+
*/
|
7843
|
+
get domPath() {
|
7844
|
+
const path = [];
|
7845
|
+
let node = this;
|
7846
|
+
while (node) {
|
7847
|
+
if (node.parentVNode && node.parentVNode.childVNodes) {
|
7848
|
+
const siblings = node.parentVNode.childVNodes.filter(v => v.nodeName === node?.nodeName);
|
7849
|
+
const index = siblings.indexOf(node);
|
7850
|
+
path.unshift(`${node.nodeName}[${index}]`);
|
7851
|
+
}
|
7852
|
+
else {
|
7853
|
+
path.unshift(node.nodeName);
|
7854
|
+
}
|
7855
|
+
node = node.parentVNode;
|
7856
|
+
}
|
7857
|
+
return path.join('/');
|
7858
|
+
}
|
7776
7859
|
/**
|
7777
7860
|
* Updates the virtual node and its children based on the current bindings.
|
7778
7861
|
* This method evaluates any expressions in text nodes and applies effectors from directives.
|
@@ -7869,24 +7952,46 @@
|
|
7869
7952
|
/**
|
7870
7953
|
* Adds a dependent virtual node that relies on this node's bindings.
|
7871
7954
|
* @param dependent The dependent virtual node to add.
|
7955
|
+
* @param dependentIdentifiers The identifiers that the dependent node relies on.
|
7956
|
+
* If not provided, the dependent node's own identifiers will be used.
|
7872
7957
|
* @returns A list of closers to unregister the dependency, or undefined if no dependency was added.
|
7873
7958
|
*/
|
7874
|
-
addDependent(dependent) {
|
7959
|
+
addDependent(dependent, dependentIdentifiers = undefined) {
|
7875
7960
|
// List of closers to unregister the dependency
|
7876
7961
|
const closers = [];
|
7877
|
-
//
|
7878
|
-
|
7879
|
-
|
7880
|
-
|
7962
|
+
// If dependent identifiers are not provided, use the dependent node's own identifiers
|
7963
|
+
if (!dependentIdentifiers) {
|
7964
|
+
dependentIdentifiers = [...dependent.dependentIdentifiers];
|
7965
|
+
}
|
7966
|
+
// Prepare alternative identifiers by stripping array indices (e.g., "items[0]" -> "items")
|
7967
|
+
const allDeps = new Set();
|
7968
|
+
dependentIdentifiers.forEach(id => {
|
7969
|
+
allDeps.add(id);
|
7970
|
+
const idx = id.indexOf('[');
|
7971
|
+
if (idx !== -1) {
|
7972
|
+
allDeps.add(id.substring(0, idx));
|
7973
|
+
}
|
7974
|
+
});
|
7975
|
+
// Get this node's identifiers
|
7976
|
+
const thisIds = [...this.preparableIdentifiers];
|
7977
|
+
if (this.#bindings) {
|
7978
|
+
thisIds.push(...this.#bindings?.raw ? Object.keys(this.#bindings.raw) : []);
|
7881
7979
|
}
|
7882
7980
|
// If the dependent node has an identifier in this node's identifiers, add it as a dependency
|
7883
|
-
if (
|
7981
|
+
if ([...allDeps].some(id => thisIds.includes(id))) {
|
7884
7982
|
// If the dependencies list is not initialized, create it
|
7885
7983
|
if (!this.#dependents) {
|
7886
7984
|
this.#dependents = [];
|
7887
7985
|
}
|
7888
7986
|
// Add the dependent node to the list
|
7889
7987
|
this.#dependents.push(dependent);
|
7988
|
+
// Remove the matched identifiers from the dependent node's identifiers to avoid duplicate dependencies
|
7989
|
+
thisIds.forEach(id => {
|
7990
|
+
const idx = dependentIdentifiers.indexOf(id);
|
7991
|
+
if (idx !== -1) {
|
7992
|
+
dependentIdentifiers.splice(idx, 1);
|
7993
|
+
}
|
7994
|
+
});
|
7890
7995
|
// Create a closer to unregister the dependency
|
7891
7996
|
closers.push({
|
7892
7997
|
close: () => {
|
@@ -7899,7 +8004,7 @@
|
|
7899
8004
|
});
|
7900
8005
|
}
|
7901
8006
|
// Recursively add the dependency to the parent node, if any
|
7902
|
-
this.#parentVNode?.addDependent(dependent)?.forEach(closer => closers.push(closer));
|
8007
|
+
this.#parentVNode?.addDependent(dependent, dependentIdentifiers)?.forEach(closer => closers.push(closer));
|
7903
8008
|
// Return a closer to unregister the dependency
|
7904
8009
|
return closers.length > 0 ? closers : undefined;
|
7905
8010
|
}
|
@@ -8571,7 +8676,8 @@
|
|
8571
8676
|
node: clone,
|
8572
8677
|
vApplication: this.#vNode.vApplication,
|
8573
8678
|
parentVNode: this.#vNode.parentVNode,
|
8574
|
-
bindings
|
8679
|
+
bindings,
|
8680
|
+
dependentIdentifiers: [`${this.#sourceName}[${context.index}]`]
|
8575
8681
|
});
|
8576
8682
|
return vNode;
|
8577
8683
|
}
|
@@ -8858,10 +8964,13 @@
|
|
8858
8964
|
}
|
8859
8965
|
// .number modifier: convert to number
|
8860
8966
|
if (this.#modifiers.has('number')) {
|
8861
|
-
|
8862
|
-
|
8863
|
-
|
8864
|
-
|
8967
|
+
// Skip conversion if the value is empty string
|
8968
|
+
if (result !== '') {
|
8969
|
+
const parsed = Number(result);
|
8970
|
+
// Only convert if it's a valid number
|
8971
|
+
if (!isNaN(parsed)) {
|
8972
|
+
result = parsed;
|
8973
|
+
}
|
8865
8974
|
}
|
8866
8975
|
}
|
8867
8976
|
return result;
|
@@ -9389,49 +9498,105 @@
|
|
9389
9498
|
})(LogLevel || (LogLevel = {}));
|
9390
9499
|
|
9391
9500
|
// Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
|
9501
|
+
/**
|
9502
|
+
* A simple logger class for virtual applications.
|
9503
|
+
*/
|
9392
9504
|
class VLogger {
|
9505
|
+
/** The name of the logger. */
|
9393
9506
|
#name;
|
9507
|
+
/** The log manager instance. */
|
9394
9508
|
#logManager;
|
9395
9509
|
constructor(name, logManager) {
|
9396
9510
|
this.#name = name;
|
9397
9511
|
this.#logManager = logManager;
|
9398
9512
|
}
|
9513
|
+
/**
|
9514
|
+
* Indicates whether the debug level is enabled.
|
9515
|
+
*/
|
9516
|
+
get isDebugEnabled() {
|
9517
|
+
return [LogLevel.DEBUG].includes(this.#logManager.logLevel);
|
9518
|
+
}
|
9519
|
+
/**
|
9520
|
+
* Indicates whether the info level is enabled.
|
9521
|
+
*/
|
9522
|
+
get isInfoEnabled() {
|
9523
|
+
return [LogLevel.DEBUG, LogLevel.INFO].includes(this.#logManager.logLevel);
|
9524
|
+
}
|
9525
|
+
/**
|
9526
|
+
* Indicates whether the warn level is enabled.
|
9527
|
+
*/
|
9528
|
+
get isWarnEnabled() {
|
9529
|
+
return [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN].includes(this.#logManager.logLevel);
|
9530
|
+
}
|
9531
|
+
/**
|
9532
|
+
* Logs a debug message.
|
9533
|
+
* @param message The message to log.
|
9534
|
+
*/
|
9399
9535
|
debug(message) {
|
9400
|
-
if (!
|
9536
|
+
if (!this.isDebugEnabled) {
|
9401
9537
|
return;
|
9402
9538
|
}
|
9403
9539
|
console.debug(`[${this.#name}] ${LogLevel.DEBUG}: ${message}`);
|
9404
9540
|
}
|
9541
|
+
/**
|
9542
|
+
* Logs an info message.
|
9543
|
+
* @param message The message to log.
|
9544
|
+
*/
|
9405
9545
|
info(message) {
|
9406
|
-
if (!
|
9546
|
+
if (!this.isInfoEnabled) {
|
9407
9547
|
return;
|
9408
9548
|
}
|
9409
9549
|
console.info(`[${this.#name}] ${LogLevel.INFO}: ${message}`);
|
9410
9550
|
}
|
9551
|
+
/**
|
9552
|
+
* Logs a warn message.
|
9553
|
+
* @param message The message to log.
|
9554
|
+
*/
|
9411
9555
|
warn(message) {
|
9412
|
-
if (!
|
9556
|
+
if (!this.isWarnEnabled) {
|
9413
9557
|
return;
|
9414
9558
|
}
|
9415
9559
|
console.warn(`[${this.#name}] ${LogLevel.WARN}: ${message}`);
|
9416
9560
|
}
|
9561
|
+
/**
|
9562
|
+
* Logs an error message.
|
9563
|
+
* @param message The message to log.
|
9564
|
+
*/
|
9417
9565
|
error(message) {
|
9418
9566
|
console.error(`[${this.#name}] ${LogLevel.ERROR}: ${message}`);
|
9419
9567
|
}
|
9420
9568
|
}
|
9421
9569
|
|
9422
9570
|
// Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
|
9571
|
+
/**
|
9572
|
+
* Manages loggers and their log levels.
|
9573
|
+
*/
|
9423
9574
|
class VLogManager {
|
9575
|
+
/** The current log level. */
|
9424
9576
|
#logLevel;
|
9577
|
+
/** A map of logger instances by name. */
|
9425
9578
|
#loggers = new Map();
|
9426
9579
|
constructor(logLevel = LogLevel.INFO) {
|
9427
9580
|
this.#logLevel = logLevel;
|
9428
9581
|
}
|
9582
|
+
/**
|
9583
|
+
* Sets the log level for all loggers.
|
9584
|
+
* @param level The log level to set.
|
9585
|
+
*/
|
9429
9586
|
set logLevel(level) {
|
9430
9587
|
this.#logLevel = level;
|
9431
9588
|
}
|
9589
|
+
/**
|
9590
|
+
* Gets the current log level.
|
9591
|
+
*/
|
9432
9592
|
get logLevel() {
|
9433
9593
|
return this.#logLevel;
|
9434
9594
|
}
|
9595
|
+
/**
|
9596
|
+
* Gets a logger by name, creating it if it doesn't exist.
|
9597
|
+
* @param name The name of the logger.
|
9598
|
+
* @returns The logger instance.
|
9599
|
+
*/
|
9435
9600
|
getLogger(name) {
|
9436
9601
|
if (this.#loggers.has(name)) {
|
9437
9602
|
return this.#loggers.get(name);
|
@@ -9601,9 +9766,10 @@
|
|
9601
9766
|
#initializeBindings() {
|
9602
9767
|
// Create bindings with change tracking
|
9603
9768
|
this.#bindings = new VBindings({
|
9604
|
-
onChange: (
|
9769
|
+
onChange: (identifier) => {
|
9605
9770
|
this.#scheduleUpdate();
|
9606
|
-
}
|
9771
|
+
},
|
9772
|
+
vApplication: this
|
9607
9773
|
});
|
9608
9774
|
// Inject utility methods into bindings
|
9609
9775
|
this.#bindings.set('$nextTick', (callback) => this.#nextTick(callback));
|
@@ -9665,6 +9831,15 @@
|
|
9665
9831
|
}
|
9666
9832
|
const computed = new Set();
|
9667
9833
|
const processing = new Set();
|
9834
|
+
// Gather all changed identifiers, including parent properties for array items
|
9835
|
+
const allChanges = new Set();
|
9836
|
+
this.#bindings?.changes.forEach(id => {
|
9837
|
+
allChanges.add(id);
|
9838
|
+
const idx = id.indexOf('[');
|
9839
|
+
if (idx !== -1) {
|
9840
|
+
allChanges.add(id.substring(0, idx));
|
9841
|
+
}
|
9842
|
+
});
|
9668
9843
|
// Helper function to recursively compute a property
|
9669
9844
|
const compute = (key) => {
|
9670
9845
|
// Skip if already computed in this update cycle
|
@@ -9680,7 +9855,7 @@
|
|
9680
9855
|
// Get the dependencies for this computed property
|
9681
9856
|
const deps = this.#computedDependencies[key] || [];
|
9682
9857
|
// If none of the dependencies have changed, skip recomputation
|
9683
|
-
if (!deps.some(dep =>
|
9858
|
+
if (!deps.some(dep => allChanges.has(dep))) {
|
9684
9859
|
computed.add(key);
|
9685
9860
|
return;
|
9686
9861
|
}
|
@@ -9698,7 +9873,7 @@
|
|
9698
9873
|
// Track if the computed value actually changed
|
9699
9874
|
if (oldValue !== newValue) {
|
9700
9875
|
this.#bindings?.set(key, newValue);
|
9701
|
-
|
9876
|
+
allChanges.add(key);
|
9702
9877
|
}
|
9703
9878
|
}
|
9704
9879
|
catch (error) {
|
@@ -9710,7 +9885,7 @@
|
|
9710
9885
|
// Find all computed properties that need to be recomputed
|
9711
9886
|
for (const [key, deps] of Object.entries(this.#computedDependencies)) {
|
9712
9887
|
// Check if any dependency has changed
|
9713
|
-
if (deps.some(dep =>
|
9888
|
+
if (deps.some(dep => allChanges.has(dep))) {
|
9714
9889
|
compute(key);
|
9715
9890
|
}
|
9716
9891
|
}
|