1"use strict"; 2/* 3 * Copyright (c) 2022-2025 Huawei Device Co., Ltd. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16Object.defineProperty(exports, "__esModule", { value: true }); 17exports.observableProxy = exports.observableProxyArray = exports.ObservableHandler = exports.Observed = exports.getObservableTarget = void 0; 18const OBSERVABLE_TARGET = "__proxy_observable_target__"; 19function getObservableTarget(proxy) { 20 var _a; 21 return (_a = getPropertyValue(OBSERVABLE_TARGET, proxy)) !== null && _a !== void 0 ? _a : proxy; 22} 23exports.getObservableTarget = getObservableTarget; 24function getPropertyValue(name, object) { 25 return object[name]; 26} 27/** 28 * Data class decorator that makes all child fields trackable. 29 */ 30function Observed(constructorFunction) { 31 constructorFunction.prototype[OBSERVED] = true; 32} 33exports.Observed = Observed; 34const OBSERVED = "__ObservedByArkUI__"; 35function isObserved(value) { 36 return value[OBSERVED] === true; 37} 38/** @internal */ 39class ObservableHandler { 40 constructor(parent, observed = false) { 41 this.parents = new Set(); 42 this.children = new Map(); 43 this.observables = new Set(); 44 this._modified = false; 45 this.observed = observed; 46 if (parent) 47 this.addParent(parent); 48 } 49 onAccess() { 50 var _a; 51 if (this.observables.size > 0) { 52 const it = this.observables.keys(); 53 while (true) { 54 const result = it.next(); 55 if (result.done) 56 break; 57 (_a = result.value) === null || _a === void 0 ? void 0 : _a.onAccess(); 58 } 59 } 60 } 61 onModify() { 62 const set = new Set(); 63 this.collect(true, set); 64 set.forEach((handler) => { 65 var _a; 66 handler._modified = true; 67 if (handler.observables.size > 0) { 68 const it = handler.observables.keys(); 69 while (true) { 70 const result = it.next(); 71 if (result.done) 72 break; 73 (_a = result.value) === null || _a === void 0 ? void 0 : _a.onModify(); 74 } 75 } 76 }); 77 } 78 static dropModified(value) { 79 const handler = ObservableHandler.findIfObject(value); 80 if (handler === undefined) 81 return false; 82 const result = handler._modified; 83 handler._modified = false; 84 return result; 85 } 86 /** Adds the specified `observable` to the handler corresponding to the given `value`. */ 87 static attach(value, observable) { 88 const handler = ObservableHandler.findIfObject(value); 89 if (handler) 90 handler.observables.add(observable); 91 } 92 /** Deletes the specified `observable` from the handler corresponding to the given `value`. */ 93 static detach(value, observable) { 94 const handler = ObservableHandler.findIfObject(value); 95 if (handler) 96 handler.observables.delete(observable); 97 } 98 /** @returns the handler corresponding to the given `value` if it was installed */ 99 static findIfObject(value) { 100 const handlers = ObservableHandler.handlers; 101 return handlers !== undefined && value instanceof Object ? handlers.get(getObservableTarget(value)) : undefined; 102 } 103 /** 104 * @param value - any non-null object including arrays 105 * @returns an observable handler or `undefined` if it is not installed 106 */ 107 static find(value) { 108 const handlers = ObservableHandler.handlers; 109 return handlers ? handlers.get(getObservableTarget(value)) : undefined; 110 } 111 /** 112 * @param value - any non-null object including arrays 113 * @param observable - a handler to install on this object 114 * @throws an error if observable handler cannot be installed 115 */ 116 static installOn(value, observable) { 117 let handlers = ObservableHandler.handlers; 118 if (handlers === undefined) { 119 handlers = new WeakMap(); 120 ObservableHandler.handlers = handlers; 121 } 122 observable 123 ? handlers.set(getObservableTarget(value), observable) 124 : handlers.delete(getObservableTarget(value)); 125 } 126 addParent(parent) { 127 var _a; 128 const count = (_a = parent.children.get(this)) !== null && _a !== void 0 ? _a : 0; 129 parent.children.set(this, count + 1); 130 this.parents.add(parent); 131 } 132 removeParent(parent) { 133 var _a; 134 const count = (_a = parent.children.get(this)) !== null && _a !== void 0 ? _a : 0; 135 if (count > 1) { 136 parent.children.set(this, count - 1); 137 } 138 else if (count == 1) { 139 parent.children.delete(this); 140 this.parents.delete(parent); 141 } 142 } 143 removeChild(value) { 144 const child = ObservableHandler.findIfObject(value); 145 if (child) 146 child.removeParent(this); 147 } 148 collect(all, guards = new Set()) { 149 if (guards.has(this)) 150 return guards; // already collected 151 guards.add(this); // handler is already guarded 152 this.parents.forEach(handler => handler.collect(all, guards)); 153 if (all) 154 this.children.forEach((_count, handler) => handler.collect(all, guards)); 155 return guards; 156 } 157 static contains(observable, guards) { 158 if (observable.observed) 159 return true; 160 if (guards === undefined) 161 guards = new Set(); // create if needed 162 else if (guards.has(observable)) 163 return false; // already checked 164 guards.add(observable); // handler is already guarded 165 for (const it of observable.parents.keys()) { 166 if (ObservableHandler.contains(it, guards)) 167 return true; 168 } 169 return false; 170 } 171} 172exports.ObservableHandler = ObservableHandler; 173ObservableHandler.handlers = undefined; 174/** @internal */ 175function observableProxyArray(...value) { 176 return observableProxy(value); 177} 178exports.observableProxyArray = observableProxyArray; 179/** @internal */ 180function observableProxy(value, parent, observed, strict = true) { 181 if (value instanceof ObservableHandler) 182 return value; // do not proxy a marker itself 183 if (value === null || !(value instanceof Object)) 184 return value; // only non-null object can be observable 185 const observable = ObservableHandler.find(value); 186 if (observable) { 187 if (parent) { 188 if (strict) 189 observable.addParent(parent); 190 if (observed === undefined) 191 observed = ObservableHandler.contains(parent); 192 } 193 if (observed) { 194 if (Array.isArray(value)) { 195 for (let index = 0; index < value.length; index++) { 196 value[index] = observableProxy(value[index], observable, observed, false); 197 } 198 } 199 else { 200 proxyFields(value, false, observable); 201 } 202 } 203 return value; 204 } 205 if (Array.isArray(value)) { 206 const handler = new ObservableHandler(parent); 207 const array = proxyChildrenOnly(value, handler, observed); 208 copyWithinObservable(array); 209 fillObservable(array); 210 popObservable(array); 211 pushObservable(array); 212 reverseObservable(array); 213 shiftObservable(array); 214 sortObservable(array); 215 spliceObservable(array); 216 unshiftObservable(array); 217 return proxyObject(array, handler); 218 } 219 if (value instanceof Date) { 220 const valueAsAny = value; 221 const handler = new ObservableHandler(parent); 222 const setMethods = new Set([ 223 "setFullYear", "setMonth", "setDate", "setHours", "setMinutes", "setSeconds", 224 "setMilliseconds", "setTime", "setUTCFullYear", "setUTCMonth", "setUTCDate", 225 "setUTCHours", "setUTCMinutes", "setUTCSeconds", "setUTCMilliseconds" 226 ]); 227 setMethods.forEach((method) => { 228 const originalMethod = method + 'Original'; 229 if (valueAsAny[originalMethod] !== undefined) { 230 return; 231 } 232 valueAsAny[originalMethod] = valueAsAny[method]; 233 valueAsAny[method] = function (...args) { 234 var _a; 235 (_a = ObservableHandler.find(this)) === null || _a === void 0 ? void 0 : _a.onModify(); 236 return this[originalMethod](...args); 237 }; 238 }); 239 return proxyObject(value, handler); 240 } 241 // TODO: support set/map 242 const handler = new ObservableHandler(parent, isObserved(value)); 243 if (handler.observed || observed) 244 proxyFields(value, true, handler); 245 return proxyObject(value, handler); 246} 247exports.observableProxy = observableProxy; 248function proxyObject(value, observable) { 249 ObservableHandler.installOn(value, observable); 250 return new Proxy(value, { 251 get(target, property, receiver) { 252 var _a; 253 if (property == OBSERVABLE_TARGET) 254 return target; 255 const value = Reflect.get(target, property, receiver); 256 (_a = ObservableHandler.find(target)) === null || _a === void 0 ? void 0 : _a.onAccess(); 257 return typeof value == "function" 258 ? value.bind(target) 259 : value; 260 }, 261 set(target, property, value, receiver) { 262 const old = Reflect.get(target, property, receiver); 263 if (value === old) 264 return true; 265 const observable = ObservableHandler.find(target); 266 if (observable) { 267 observable.onModify(); 268 observable.removeChild(old); 269 const observed = ObservableHandler.contains(observable); 270 if (observed || Array.isArray(target)) { 271 value = observableProxy(value, observable, observed); 272 } 273 } 274 return Reflect.set(target, property, value, receiver); 275 }, 276 deleteProperty(target, property) { 277 var _a; 278 (_a = ObservableHandler.find(target)) === null || _a === void 0 ? void 0 : _a.onModify(); 279 delete target[property]; 280 return true; 281 }, 282 }); 283} 284function proxyFields(value, strict, parent) { 285 for (const name of Object.getOwnPropertyNames(value)) { 286 const descriptor = Object.getOwnPropertyDescriptor(value, name); 287 if (descriptor === null || descriptor === void 0 ? void 0 : descriptor.writable) 288 value[name] = observableProxy(value[name], parent, true, strict); 289 } 290} 291function proxyChildrenOnly(array, parent, observed) { 292 if (observed === undefined) 293 observed = ObservableHandler.contains(parent); 294 return array.map(it => observableProxy(it, parent, observed)); 295} 296function copyWithinObservable(array) { 297 if (array.copyWithinOriginal === undefined) { 298 array.copyWithinOriginal = array.copyWithin; 299 array.copyWithin = function (target, start, end) { 300 const observable = ObservableHandler.find(this); 301 observable === null || observable === void 0 ? void 0 : observable.onModify(); 302 return this.copyWithinOriginal(target, start, end); 303 }; 304 } 305} 306function fillObservable(array) { 307 if (array.fillOriginal === undefined) { 308 array.fillOriginal = array.fill; 309 array.fill = function (value, start, end) { 310 const observable = ObservableHandler.find(this); 311 observable === null || observable === void 0 ? void 0 : observable.onModify(); 312 if (observable) 313 value = observableProxy(value, observable); 314 return this.fillOriginal(value, start, end); 315 }; 316 } 317} 318function popObservable(array) { 319 if (array.popOriginal === undefined) { 320 array.popOriginal = array.pop; 321 array.pop = function (...args) { 322 const observable = ObservableHandler.find(this); 323 observable === null || observable === void 0 ? void 0 : observable.onModify(); 324 const result = this.popOriginal(...args); 325 if (observable) 326 observable.removeChild(result); 327 return result; 328 }; 329 } 330} 331function pushObservable(array) { 332 if (array.pushOriginal === undefined) { 333 array.pushOriginal = array.push; 334 array.push = function (...args) { 335 const observable = ObservableHandler.find(this); 336 observable === null || observable === void 0 ? void 0 : observable.onModify(); 337 if (observable) 338 args = proxyChildrenOnly(args, observable); 339 return this.pushOriginal(...args); 340 }; 341 } 342} 343function reverseObservable(array) { 344 if (array.reverseOriginal === undefined) { 345 array.reverseOriginal = array.reverse; 346 array.reverse = function () { 347 const observable = ObservableHandler.find(this); 348 observable === null || observable === void 0 ? void 0 : observable.onModify(); 349 return this.reverseOriginal(); 350 }; 351 } 352} 353function shiftObservable(array) { 354 if (array.shiftOriginal === undefined) { 355 array.shiftOriginal = array.shift; 356 array.shift = function (...args) { 357 const observable = ObservableHandler.find(this); 358 observable === null || observable === void 0 ? void 0 : observable.onModify(); 359 const result = this.shiftOriginal(...args); 360 if (observable) 361 observable.removeChild(result); 362 return result; 363 }; 364 } 365} 366function sortObservable(array) { 367 if (array.sortOriginal === undefined) { 368 array.sortOriginal = array.sort; 369 array.sort = function (compareFn) { 370 const observable = ObservableHandler.find(this); 371 observable === null || observable === void 0 ? void 0 : observable.onModify(); 372 return this.sortOriginal(compareFn); 373 }; 374 } 375} 376function spliceObservable(array) { 377 if (array.spliceOriginal === undefined) { 378 array.spliceOriginal = array.splice; 379 array.splice = function (start, deleteCount, ...items) { 380 const observable = ObservableHandler.find(this); 381 observable === null || observable === void 0 ? void 0 : observable.onModify(); 382 if (observable) 383 items = proxyChildrenOnly(items, observable); 384 if (deleteCount === undefined) 385 deleteCount = array.length; 386 const result = this.spliceOriginal(start, deleteCount, ...items); 387 if (observable && Array.isArray(result)) { 388 result.forEach(it => observable.removeChild(it)); 389 } 390 return result; 391 }; 392 } 393} 394function unshiftObservable(array) { 395 if (array.unshiftOriginal === undefined) { 396 array.unshiftOriginal = array.unshift; 397 array.unshift = function (...items) { 398 const observable = ObservableHandler.find(this); 399 observable === null || observable === void 0 ? void 0 : observable.onModify(); 400 if (observable) 401 items = proxyChildrenOnly(items, observable); 402 return this.unshiftOriginal(...items); 403 }; 404 } 405} 406//# sourceMappingURL=observable.js.map