@microsoft/fast-html 1.0.0-alpha.34 → 1.0.0-alpha.35

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.
@@ -1,4 +1,4 @@
1
- import { __awaiter, __decorate, __metadata } from "tslib";
1
+ import { __decorate, __metadata } from "tslib";
2
2
  import { attr, children, elements, FAST, FASTElement, FASTElementDefinition, fastElementRegistry, HydratableElementController, ref, repeat, slotted, ViewTemplate, when, } from "@microsoft/fast-element";
3
3
  import "@microsoft/fast-element/install-hydratable-view-templates.js";
4
4
  import { ObserverMap } from "./observer-map.js";
@@ -23,7 +23,7 @@ class TemplateElement extends FASTElement {
23
23
  static config(callbacks) {
24
24
  TemplateElement.lifecycleCallbacks = callbacks;
25
25
  // Pass the hydration-specific callbacks to HydratableElementController
26
- HydratableElementController.config(Object.assign({}, callbacks));
26
+ HydratableElementController.config({ ...callbacks });
27
27
  return this;
28
28
  }
29
29
  /**
@@ -68,7 +68,7 @@ class TemplateElement extends FASTElement {
68
68
  return;
69
69
  }
70
70
  this.schema = new Schema(name);
71
- FASTElementDefinition.registerAsync(name).then((value) => __awaiter(this, void 0, void 0, function* () {
71
+ FASTElementDefinition.registerAsync(name).then(async (value) => {
72
72
  var _a, _b, _c, _d, _e, _f, _g;
73
73
  (_b = (_a = TemplateElement.lifecycleCallbacks).elementDidRegister) === null || _b === void 0 ? void 0 : _b.call(_a, name);
74
74
  if (!((_c = TemplateElement.elementOptions) === null || _c === void 0 ? void 0 : _c[name])) {
@@ -84,7 +84,7 @@ class TemplateElement extends FASTElement {
84
84
  (_f = (_e = TemplateElement.lifecycleCallbacks).templateWillUpdate) === null || _f === void 0 ? void 0 : _f.call(_e, name);
85
85
  const innerHTML = transformInnerHTML(this.innerHTML);
86
86
  // Cache paths during template processing (pass undefined if observerMap is not available)
87
- const { strings, values } = yield this.resolveStringsAndValues(null, innerHTML, false, null, 0, this.schema, this.observerMap);
87
+ const { strings, values } = await this.resolveStringsAndValues(null, innerHTML, false, null, 0, this.schema, this.observerMap);
88
88
  // Define the root properties cached in the observer map as observable (only if observerMap exists)
89
89
  (_g = this.observerMap) === null || _g === void 0 ? void 0 : _g.defineProperties();
90
90
  if (registeredFastElement) {
@@ -102,7 +102,7 @@ class TemplateElement extends FASTElement {
102
102
  else {
103
103
  throw FAST.error(2000 /* Message.noTemplateProvided */, { name: this.name });
104
104
  }
105
- }));
105
+ });
106
106
  }
107
107
  /**
108
108
  * Resolve strings and values from an innerHTML string
@@ -110,17 +110,15 @@ class TemplateElement extends FASTElement {
110
110
  * @param self - Indicates that this should refer to itself instead of a property when creating bindings.
111
111
  * @param observerMap - ObserverMap instance for caching binding paths (optional).
112
112
  */
113
- resolveStringsAndValues(rootPropertyName, innerHTML, self = false, parentContext, level, schema, observerMap) {
114
- return __awaiter(this, void 0, void 0, function* () {
115
- const strings = [];
116
- const values = []; // these can be bindings, directives, etc.
117
- yield this.resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self, parentContext, level, schema, observerMap);
118
- strings.raw = strings.map(value => String.raw({ raw: value }));
119
- return {
120
- strings,
121
- values,
122
- };
123
- });
113
+ async resolveStringsAndValues(rootPropertyName, innerHTML, self = false, parentContext, level, schema, observerMap) {
114
+ const strings = [];
115
+ const values = []; // these can be bindings, directives, etc.
116
+ await this.resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self, parentContext, level, schema, observerMap);
117
+ strings.raw = strings.map(value => String.raw({ raw: value }));
118
+ return {
119
+ strings,
120
+ values,
121
+ };
124
122
  }
125
123
  /**
126
124
  * Resolve a template or behavior
@@ -138,27 +136,25 @@ class TemplateElement extends FASTElement {
138
136
  * @param self - Indicates that this should refer to itself instead of a property when creating bindings.
139
137
  * @param observerMap - ObserverMap instance for caching binding paths (optional).
140
138
  */
141
- resolveTemplateDirective(rootPropertyName, behaviorConfig, externalValues, innerHTML, self = false, parentContext, level, schema, observerMap) {
142
- return __awaiter(this, void 0, void 0, function* () {
143
- switch (behaviorConfig.name) {
144
- case "when": {
145
- const expressionChain = getExpressionChain(behaviorConfig.value);
146
- const whenLogic = resolveWhen(rootPropertyName, expressionChain, parentContext, level, schema);
147
- const { strings, values } = yield this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), self, parentContext, level, schema, observerMap);
148
- externalValues.push(when(whenLogic, this.resolveTemplateOrBehavior(strings, values)));
149
- break;
150
- }
151
- case "repeat": {
152
- const valueAttr = behaviorConfig.value.split(" "); // syntax {{x in y}}
153
- const updatedLevel = level + 1;
154
- rootPropertyName = getRootPropertyName(rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name);
155
- const binding = bindingResolver(null, rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name, schema, valueAttr[0], level);
156
- const { strings, values } = yield this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), true, valueAttr[0], updatedLevel, schema, observerMap);
157
- externalValues.push(repeat((x, c) => binding(x, c), this.resolveTemplateOrBehavior(strings, values)));
158
- break;
159
- }
139
+ async resolveTemplateDirective(rootPropertyName, behaviorConfig, externalValues, innerHTML, self = false, parentContext, level, schema, observerMap) {
140
+ switch (behaviorConfig.name) {
141
+ case "when": {
142
+ const expressionChain = getExpressionChain(behaviorConfig.value);
143
+ const whenLogic = resolveWhen(rootPropertyName, expressionChain, parentContext, level, schema);
144
+ const { strings, values } = await this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), self, parentContext, level, schema, observerMap);
145
+ externalValues.push(when(whenLogic, this.resolveTemplateOrBehavior(strings, values)));
146
+ break;
160
147
  }
161
- });
148
+ case "repeat": {
149
+ const valueAttr = behaviorConfig.value.split(" "); // syntax {{x in y}}
150
+ const updatedLevel = level + 1;
151
+ rootPropertyName = getRootPropertyName(rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name);
152
+ const binding = bindingResolver(null, rootPropertyName, valueAttr[2], parentContext, behaviorConfig.name, schema, valueAttr[0], level);
153
+ const { strings, values } = await this.resolveStringsAndValues(rootPropertyName, innerHTML.slice(behaviorConfig.openingTagEndIndex, behaviorConfig.closingTagStartIndex), true, valueAttr[0], updatedLevel, schema, observerMap);
154
+ externalValues.push(repeat((x, c) => binding(x, c), this.resolveTemplateOrBehavior(strings, values)));
155
+ break;
156
+ }
157
+ }
162
158
  }
163
159
  /**
164
160
  * Resolve a template directive
@@ -166,36 +162,34 @@ class TemplateElement extends FASTElement {
166
162
  * @param propName - The property name to pass to the directive.
167
163
  * @param externalValues - The interpreted values from the parent.
168
164
  */
169
- resolveAttributeDirective(name, propName, externalValues) {
170
- return __awaiter(this, void 0, void 0, function* () {
171
- switch (name) {
172
- case "children": {
173
- externalValues.push(children(propName));
174
- break;
175
- }
176
- case "slotted": {
177
- const parts = propName.trim().split(" filter ");
178
- const slottedOption = {
179
- property: parts[0],
180
- };
181
- if (parts[1]) {
182
- if (parts[1].startsWith("elements(")) {
183
- let params = parts[1].replace("elements(", "");
184
- params = params.substring(0, params.lastIndexOf(")"));
185
- Object.assign(slottedOption, {
186
- filter: elements(params || undefined),
187
- });
188
- }
165
+ async resolveAttributeDirective(name, propName, externalValues) {
166
+ switch (name) {
167
+ case "children": {
168
+ externalValues.push(children(propName));
169
+ break;
170
+ }
171
+ case "slotted": {
172
+ const parts = propName.trim().split(" filter ");
173
+ const slottedOption = {
174
+ property: parts[0],
175
+ };
176
+ if (parts[1]) {
177
+ if (parts[1].startsWith("elements(")) {
178
+ let params = parts[1].replace("elements(", "");
179
+ params = params.substring(0, params.lastIndexOf(")"));
180
+ Object.assign(slottedOption, {
181
+ filter: elements(params || undefined),
182
+ });
189
183
  }
190
- externalValues.push(slotted(slottedOption));
191
- break;
192
- }
193
- case "ref": {
194
- externalValues.push(ref(propName));
195
- break;
196
184
  }
185
+ externalValues.push(slotted(slottedOption));
186
+ break;
197
187
  }
198
- });
188
+ case "ref": {
189
+ externalValues.push(ref(propName));
190
+ break;
191
+ }
192
+ }
199
193
  }
200
194
  /**
201
195
  * Resolver of a data binding
@@ -206,60 +200,58 @@ class TemplateElement extends FASTElement {
206
200
  * @param behaviorConfig - The binding behavior configuration object.
207
201
  * @param observerMap - ObserverMap instance for caching binding paths (optional).
208
202
  */
209
- resolveDataBinding(rootPropertyName, innerHTML, strings, values, self = false, behaviorConfig, parentContext, level, schema, observerMap) {
210
- return __awaiter(this, void 0, void 0, function* () {
211
- switch (behaviorConfig.subtype) {
212
- case "content": {
213
- strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
214
- const type = "access";
215
- const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
203
+ async resolveDataBinding(rootPropertyName, innerHTML, strings, values, self = false, behaviorConfig, parentContext, level, schema, observerMap) {
204
+ switch (behaviorConfig.subtype) {
205
+ case "content": {
206
+ strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
207
+ const type = "access";
208
+ const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
209
+ rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
210
+ const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
211
+ const contentBinding = (x, c) => binding(x, c);
212
+ values.push(contentBinding);
213
+ await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
214
+ break;
215
+ }
216
+ case "attribute": {
217
+ strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
218
+ if (behaviorConfig.aspect === "@") {
219
+ const bindingHTML = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
220
+ const openingParenthesis = bindingHTML.indexOf("(");
221
+ const closingParenthesis = bindingHTML.indexOf(")");
222
+ const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex -
223
+ (closingParenthesis - openingParenthesis) -
224
+ 1);
225
+ const type = "event";
216
226
  rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
227
+ const arg = bindingHTML.slice(openingParenthesis + 1, closingParenthesis);
217
228
  const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
218
- const contentBinding = (x, c) => binding(x, c);
219
- values.push(contentBinding);
220
- yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
221
- break;
222
- }
223
- case "attribute": {
224
- strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex));
225
- if (behaviorConfig.aspect === "@") {
226
- const bindingHTML = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
227
- const openingParenthesis = bindingHTML.indexOf("(");
228
- const closingParenthesis = bindingHTML.indexOf(")");
229
- const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex -
230
- (closingParenthesis - openingParenthesis) -
231
- 1);
232
- const type = "event";
233
- rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
234
- const arg = bindingHTML.slice(openingParenthesis + 1, closingParenthesis);
235
- const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
236
- const attributeBinding = (x, c) => binding(x, c).bind(x)(...(arg === "e" ? [c.event] : []), ...(arg !== "e" && arg !== ""
237
- ? [
238
- bindingResolver(strings.join(""), rootPropertyName, arg, parentContext, type, schema, parentContext, level)(x, c),
239
- ]
240
- : []));
241
- values.push(attributeBinding);
242
- }
243
- else {
244
- const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
245
- const type = "access";
246
- rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
247
- const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
248
- const attributeBinding = (x, c) => binding(x, c);
249
- values.push(attributeBinding);
250
- }
251
- yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
252
- break;
229
+ const attributeBinding = (x, c) => binding(x, c).bind(x)(...(arg === "e" ? [c.event] : []), ...(arg !== "e" && arg !== ""
230
+ ? [
231
+ bindingResolver(strings.join(""), rootPropertyName, arg, parentContext, type, schema, parentContext, level)(x, c),
232
+ ]
233
+ : []));
234
+ values.push(attributeBinding);
253
235
  }
254
- case "attributeDirective": {
255
- strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex - behaviorConfig.name.length - 4));
236
+ else {
256
237
  const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
257
- yield this.resolveAttributeDirective(behaviorConfig.name, propName, values);
258
- yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex + 1, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
259
- break;
238
+ const type = "access";
239
+ rootPropertyName = getRootPropertyName(rootPropertyName, propName, parentContext, type);
240
+ const binding = bindingResolver(strings.join(""), rootPropertyName, propName, parentContext, type, schema, parentContext, level);
241
+ const attributeBinding = (x, c) => binding(x, c);
242
+ values.push(attributeBinding);
260
243
  }
244
+ await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
245
+ break;
261
246
  }
262
- });
247
+ case "attributeDirective": {
248
+ strings.push(innerHTML.slice(0, behaviorConfig.openingStartIndex - behaviorConfig.name.length - 4));
249
+ const propName = innerHTML.slice(behaviorConfig.openingEndIndex, behaviorConfig.closingStartIndex);
250
+ await this.resolveAttributeDirective(behaviorConfig.name, propName, values);
251
+ await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingEndIndex + 1, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
252
+ break;
253
+ }
254
+ }
263
255
  }
264
256
  /**
265
257
  * Resolver of the innerHTML string
@@ -269,27 +261,25 @@ class TemplateElement extends FASTElement {
269
261
  * @param self - Indicates that this should refer to itself instead of a property when creating bindings.
270
262
  * @param observerMap - ObserverMap instance for caching binding paths (optional).
271
263
  */
272
- resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self = false, parentContext, level, schema, observerMap) {
273
- return __awaiter(this, void 0, void 0, function* () {
274
- const behaviorConfig = getNextBehavior(innerHTML);
275
- if (behaviorConfig === null) {
276
- strings.push(innerHTML);
277
- }
278
- else {
279
- switch (behaviorConfig.type) {
280
- case "dataBinding": {
281
- yield this.resolveDataBinding(rootPropertyName, innerHTML, strings, values, self, behaviorConfig, parentContext, level, schema, observerMap);
282
- break;
283
- }
284
- case "templateDirective": {
285
- strings.push(innerHTML.slice(0, behaviorConfig.openingTagStartIndex));
286
- yield this.resolveTemplateDirective(rootPropertyName, behaviorConfig, values, innerHTML, self, parentContext, level, schema, observerMap);
287
- yield this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingTagEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
288
- break;
289
- }
264
+ async resolveInnerHTML(rootPropertyName, innerHTML, strings, values, self = false, parentContext, level, schema, observerMap) {
265
+ const behaviorConfig = getNextBehavior(innerHTML);
266
+ if (behaviorConfig === null) {
267
+ strings.push(innerHTML);
268
+ }
269
+ else {
270
+ switch (behaviorConfig.type) {
271
+ case "dataBinding": {
272
+ await this.resolveDataBinding(rootPropertyName, innerHTML, strings, values, self, behaviorConfig, parentContext, level, schema, observerMap);
273
+ break;
274
+ }
275
+ case "templateDirective": {
276
+ strings.push(innerHTML.slice(0, behaviorConfig.openingTagStartIndex));
277
+ await this.resolveTemplateDirective(rootPropertyName, behaviorConfig, values, innerHTML, self, parentContext, level, schema, observerMap);
278
+ await this.resolveInnerHTML(rootPropertyName, innerHTML.slice(behaviorConfig.closingTagEndIndex, innerHTML.length), strings, values, self, parentContext, level, schema, observerMap);
279
+ break;
290
280
  }
291
281
  }
292
- });
282
+ }
293
283
  }
294
284
  }
295
285
  /**
@@ -28,7 +28,10 @@ const ComparisonOperator = {
28
28
  NOT: "!",
29
29
  NOT_EQUALS: "!=",
30
30
  };
31
- const Operator = Object.assign(Object.assign({}, LogicalOperator), ComparisonOperator);
31
+ const Operator = {
32
+ ...LogicalOperator,
33
+ ...ComparisonOperator,
34
+ };
32
35
  /**
33
36
  * A map of proxied objects
34
37
  */
@@ -141,7 +144,11 @@ function getAttributeDataBindingConfig(innerHTML, config) {
141
144
  firstCharOfAttribute === ":"
142
145
  ? firstCharOfAttribute
143
146
  : null;
144
- return Object.assign(Object.assign({}, config), { subtype: "attribute", aspect });
147
+ return {
148
+ ...config,
149
+ subtype: "attribute",
150
+ aspect,
151
+ };
145
152
  }
146
153
  /**
147
154
  * Get the attribute directive binding config
@@ -154,7 +161,11 @@ function getAttributeDirectiveDataBindingConfig(innerHTML, config) {
154
161
  const lastItem = splitInnerHTML[splitInnerHTML.length - 1];
155
162
  const equals = lastItem.indexOf("=");
156
163
  const name = lastItem.slice(2, equals);
157
- return Object.assign(Object.assign({}, config), { subtype: "attributeDirective", name: name });
164
+ return {
165
+ ...config,
166
+ subtype: "attributeDirective",
167
+ name: name,
168
+ };
158
169
  }
159
170
  /**
160
171
  * Get the content data binding config
@@ -162,7 +173,10 @@ function getAttributeDirectiveDataBindingConfig(innerHTML, config) {
162
173
  * @returns ContentDataBindingBehaviorConfig
163
174
  */
164
175
  function getContentDataBindingConfig(config) {
165
- return Object.assign(Object.assign({}, config), { subtype: "content" });
176
+ return {
177
+ ...config,
178
+ subtype: "content",
179
+ };
166
180
  }
167
181
  /**
168
182
  * Finds the next data binding in innerHTML and determines its type and indices
@@ -403,7 +417,10 @@ function evaluatePartsInExpressionChain(parts, operator) {
403
417
  while (current.next) {
404
418
  current = current.next;
405
419
  }
406
- current.next = Object.assign({ operator }, nextPart);
420
+ current.next = {
421
+ operator,
422
+ ...nextPart,
423
+ };
407
424
  }
408
425
  }
409
426
  return firstPart;
@@ -536,7 +553,16 @@ function resolveExpression(x, c, level, contextPath, expression, rootSchema) {
536
553
  return resolvedLeft < resolvedRight;
537
554
  }
538
555
  default: {
539
- return resolvedLeft;
556
+ if (typeof resolvedLeft === "boolean") {
557
+ return resolvedLeft;
558
+ }
559
+ if (typeof resolvedLeft === "number") {
560
+ return resolvedLeft !== 0;
561
+ }
562
+ if (typeof resolvedLeft === "string") {
563
+ return resolvedLeft.length > 0;
564
+ }
565
+ return !!resolvedLeft;
540
566
  }
541
567
  }
542
568
  }
@@ -882,10 +908,14 @@ function notifyObservables(targetObject) {
882
908
  * @returns Proxy object
883
909
  */
884
910
  export function assignProxy(schema, rootSchema, target, rootProperty, object) {
885
- if (object.$isProxy === undefined) {
911
+ if (!object.$isProxy) {
886
912
  // Create a proxy for the object that triggers Observable.notify on mutations
887
913
  const proxy = new Proxy(object, {
888
914
  set: (obj, prop, value) => {
915
+ const currentValue = obj[prop];
916
+ if (deepEqual(currentValue, value)) {
917
+ return true;
918
+ }
889
919
  obj[prop] = assignObservables(schema, rootSchema, value, target, rootProperty);
890
920
  notifyObservables(proxy);
891
921
  return true;
@@ -970,6 +1000,49 @@ function getAttributeName(previousString) {
970
1000
  }
971
1001
  return attributeName;
972
1002
  }
1003
+ /**
1004
+ * Determine if an object has an observable accessor for a backing field
1005
+ * @param object - The object to check
1006
+ * @param backingField - The backing field name
1007
+ * @returns True if the object has an observable accessor for the backing field, false otherwise
1008
+ */
1009
+ function hasObservableAccessor(object, backingField) {
1010
+ const accessors = Observable.getAccessors(object);
1011
+ if (!accessors) {
1012
+ return false;
1013
+ }
1014
+ return accessors.some((accessor) => accessor.name === backingField);
1015
+ }
1016
+ /**
1017
+ * Determine if a key should be skipped during deep comparison
1018
+ *
1019
+ * @param object - The object to check
1020
+ * @param key - The key to evaluate
1021
+ * @returns True if the key should be skipped during comparison, false otherwise
1022
+ */
1023
+ function shouldSkipKey(object, key) {
1024
+ if (key[0] !== "_" || key === "_") {
1025
+ return false;
1026
+ }
1027
+ return hasObservableAccessor(object, key.slice(1));
1028
+ }
1029
+ /**
1030
+ * Get comparable keys from an object, excluding those that should be skipped
1031
+ *
1032
+ * @param object - The object to extract keys from
1033
+ * @returns An array of keys that should be compared
1034
+ */
1035
+ function getComparableKeys(object) {
1036
+ const hasOwn = Object.prototype.hasOwnProperty;
1037
+ const keys = [];
1038
+ for (const key in object) {
1039
+ if (!hasOwn.call(object, key) || shouldSkipKey(object, key)) {
1040
+ continue;
1041
+ }
1042
+ keys.push(key);
1043
+ }
1044
+ return keys;
1045
+ }
973
1046
  /**
974
1047
  * Deeply compares two objects for equality.
975
1048
  *
@@ -1007,22 +1080,17 @@ export function deepEqual(obj1, obj2) {
1007
1080
  return true;
1008
1081
  }
1009
1082
  const hasOwn = Object.prototype.hasOwnProperty;
1010
- let keyCount = 0;
1011
- for (const key in obj1) {
1012
- if (hasOwn.call(obj1, key)) {
1013
- keyCount++;
1014
- if (!hasOwn.call(obj2, key) || !deepEqual(obj1[key], obj2[key])) {
1015
- return false;
1016
- }
1017
- }
1083
+ const obj1Keys = getComparableKeys(obj1);
1084
+ const obj2Keys = getComparableKeys(obj2);
1085
+ if (obj1Keys.length !== obj2Keys.length) {
1086
+ return false;
1018
1087
  }
1019
- let obj2KeyCount = 0;
1020
- for (const key in obj2) {
1021
- if (hasOwn.call(obj2, key)) {
1022
- obj2KeyCount++;
1088
+ for (const key of obj1Keys) {
1089
+ if (!hasOwn.call(obj2, key) || !deepEqual(obj1[key], obj2[key])) {
1090
+ return false;
1023
1091
  }
1024
1092
  }
1025
- return keyCount === obj2KeyCount;
1093
+ return true;
1026
1094
  }
1027
1095
  /**
1028
1096
  * Deeply merges the source object into the target object.
@@ -1048,7 +1116,7 @@ export function deepMerge(target, source) {
1048
1116
  const isSourceArray = Array.isArray(sourceValue);
1049
1117
  if (isSourceArray) {
1050
1118
  const isTargetArray = Array.isArray(targetValue);
1051
- const clonedItems = sourceValue.map((item) => item && typeof item === "object" ? Object.assign({}, item) : item);
1119
+ const clonedItems = sourceValue.map((item) => item && typeof item === "object" ? { ...item } : item);
1052
1120
  if (isTargetArray) {
1053
1121
  // Use splice to maintain observable array tracking
1054
1122
  targetValue.splice(0, targetValue.length, ...clonedItems);