@microsoft/fast-element 2.10.4 → 3.0.0-rc.2

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.
Files changed (211) hide show
  1. package/CHANGELOG.md +52 -2
  2. package/README.md +244 -1
  3. package/dist/arrays/arrays.api.json +2621 -0
  4. package/dist/context/context.api.json +13 -13
  5. package/dist/declarative/declarative.api.json +8483 -0
  6. package/dist/di/di.api.json +16 -16
  7. package/dist/dts/__test__/helpers.d.ts +6 -0
  8. package/dist/dts/array-observer.d.ts +2 -0
  9. package/dist/dts/arrays.d.ts +2 -0
  10. package/dist/dts/attr.d.ts +1 -0
  11. package/dist/dts/binding/binding.d.ts +15 -5
  12. package/dist/dts/binding/one-time.d.ts +1 -1
  13. package/dist/dts/binding/one-way.d.ts +1 -1
  14. package/dist/dts/binding/signal.d.ts +6 -6
  15. package/dist/dts/binding/two-way.d.ts +2 -1
  16. package/dist/dts/binding.d.ts +7 -0
  17. package/dist/dts/components/attributes.d.ts +1 -4
  18. package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
  19. package/dist/dts/components/element-controller.d.ts +80 -114
  20. package/dist/dts/components/element-hydration.d.ts +1 -1
  21. package/dist/dts/components/enable-hydration.d.ts +54 -0
  22. package/dist/dts/components/fast-definitions.d.ts +98 -46
  23. package/dist/dts/components/fast-element.d.ts +43 -16
  24. package/dist/dts/components/hydration-tracker.d.ts +83 -0
  25. package/dist/dts/components/hydration.d.ts +23 -53
  26. package/dist/dts/components/schema.d.ts +205 -0
  27. package/dist/dts/context.d.ts +13 -13
  28. package/dist/dts/css.d.ts +3 -0
  29. package/dist/dts/debug.d.ts +5 -1
  30. package/dist/dts/declarative/attribute-map.d.ts +58 -0
  31. package/dist/dts/declarative/debug.d.ts +4 -0
  32. package/dist/dts/declarative/index.d.ts +14 -0
  33. package/dist/dts/declarative/interfaces.d.ts +8 -0
  34. package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
  35. package/dist/dts/declarative/observer-map.d.ts +89 -0
  36. package/dist/dts/declarative/runtime.d.ts +5 -0
  37. package/dist/dts/declarative/syntax.d.ts +21 -0
  38. package/dist/dts/declarative/template-bridge.d.ts +33 -0
  39. package/dist/dts/declarative/template-parser.d.ts +98 -0
  40. package/dist/dts/declarative/template.d.ts +10 -0
  41. package/dist/dts/declarative/utilities.d.ts +358 -0
  42. package/dist/dts/di/di.d.ts +7 -7
  43. package/dist/dts/directives/children.d.ts +2 -0
  44. package/dist/dts/directives/node-observation.d.ts +2 -0
  45. package/dist/dts/directives/ref.d.ts +2 -0
  46. package/dist/dts/directives/repeat.d.ts +4 -0
  47. package/dist/dts/directives/slotted.d.ts +2 -0
  48. package/dist/dts/directives/when.d.ts +3 -0
  49. package/dist/dts/dom-policy.d.ts +23 -5
  50. package/dist/dts/dom.d.ts +4 -16
  51. package/dist/dts/html.d.ts +5 -0
  52. package/dist/dts/hydration/diagnostics.d.ts +93 -0
  53. package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
  54. package/dist/dts/hydration/messages.d.ts +62 -0
  55. package/dist/dts/hydration/runtime.d.ts +7 -0
  56. package/dist/dts/hydration/target-builder.d.ts +40 -12
  57. package/dist/dts/hydration.d.ts +18 -0
  58. package/dist/dts/index.d.ts +42 -42
  59. package/dist/dts/index.debug.d.ts +0 -1
  60. package/dist/dts/index.rollup.debug.d.ts +0 -1
  61. package/dist/dts/interfaces.d.ts +2 -49
  62. package/dist/dts/observable.d.ts +3 -6
  63. package/dist/dts/observation/arrays.d.ts +1 -1
  64. package/dist/dts/observation/observable.d.ts +3 -3
  65. package/dist/dts/observation/update-queue.d.ts +1 -1
  66. package/dist/dts/platform.d.ts +45 -8
  67. package/dist/dts/registry.d.ts +1 -0
  68. package/dist/dts/render.d.ts +7 -0
  69. package/dist/dts/schema.d.ts +1 -0
  70. package/dist/dts/state/exports.d.ts +1 -1
  71. package/dist/dts/state/state.d.ts +2 -2
  72. package/dist/dts/styles/css-directive.d.ts +5 -12
  73. package/dist/dts/styles/css.d.ts +5 -7
  74. package/dist/dts/styles/element-styles.d.ts +0 -10
  75. package/dist/dts/styles.d.ts +6 -0
  76. package/dist/dts/templating/compiler.d.ts +1 -1
  77. package/dist/dts/templating/html-binding-directive.d.ts +10 -2
  78. package/dist/dts/templating/html-directive.d.ts +19 -1
  79. package/dist/dts/templating/hydration-view.d.ts +130 -0
  80. package/dist/dts/templating/render.d.ts +1 -1
  81. package/dist/dts/templating/repeat.d.ts +1 -1
  82. package/dist/dts/templating/template.d.ts +15 -7
  83. package/dist/dts/templating/view.d.ts +25 -102
  84. package/dist/dts/templating.d.ts +10 -0
  85. package/dist/dts/testing/exports.d.ts +2 -2
  86. package/dist/dts/testing/fakes.d.ts +4 -4
  87. package/dist/dts/updates.d.ts +1 -0
  88. package/dist/dts/volatile.d.ts +2 -0
  89. package/dist/esm/__test__/helpers.js +22 -0
  90. package/dist/esm/__test__/setup-node.js +18 -0
  91. package/dist/esm/array-observer.js +1 -0
  92. package/dist/esm/arrays.js +1 -0
  93. package/dist/esm/attr.js +1 -0
  94. package/dist/esm/binding/normalize.js +1 -1
  95. package/dist/esm/binding/signal.js +4 -4
  96. package/dist/esm/binding/two-way.js +3 -3
  97. package/dist/esm/binding.js +4 -0
  98. package/dist/esm/components/attributes.js +18 -11
  99. package/dist/esm/components/definition-schema-transforms.js +23 -0
  100. package/dist/esm/components/element-controller.js +206 -270
  101. package/dist/esm/components/element-hydration.js +1 -1
  102. package/dist/esm/components/enable-hydration.js +124 -0
  103. package/dist/esm/components/fast-definitions.js +219 -56
  104. package/dist/esm/components/fast-element.js +18 -27
  105. package/dist/esm/components/hydration-tracker.js +122 -0
  106. package/dist/esm/components/hydration.js +137 -140
  107. package/dist/esm/components/schema.js +253 -0
  108. package/dist/esm/context.js +6 -6
  109. package/dist/esm/css.js +3 -0
  110. package/dist/esm/debug.js +27 -26
  111. package/dist/esm/declarative/attribute-map.js +122 -0
  112. package/dist/esm/declarative/debug.js +4 -0
  113. package/dist/esm/declarative/index.js +4 -0
  114. package/dist/esm/declarative/interfaces.js +9 -0
  115. package/dist/esm/declarative/observer-map-utilities.js +565 -0
  116. package/dist/esm/declarative/observer-map.js +216 -0
  117. package/dist/esm/declarative/runtime.js +14 -0
  118. package/dist/esm/declarative/syntax.js +36 -0
  119. package/dist/esm/declarative/template-bridge.js +160 -0
  120. package/dist/esm/declarative/template-parser.js +306 -0
  121. package/dist/esm/declarative/template.js +143 -0
  122. package/dist/esm/declarative/utilities.js +1069 -0
  123. package/dist/esm/di/di.js +8 -9
  124. package/dist/esm/directives/children.js +1 -0
  125. package/dist/esm/directives/node-observation.js +1 -0
  126. package/dist/esm/directives/ref.js +1 -0
  127. package/dist/esm/directives/repeat.js +1 -0
  128. package/dist/esm/directives/slotted.js +1 -0
  129. package/dist/esm/directives/when.js +1 -0
  130. package/dist/esm/dom-policy.js +35 -6
  131. package/dist/esm/dom.js +1 -1
  132. package/dist/esm/html.js +2 -0
  133. package/dist/esm/hydration/diagnostics.js +50 -0
  134. package/dist/esm/hydration/hydration-debugger.js +112 -0
  135. package/dist/esm/hydration/messages.js +84 -0
  136. package/dist/esm/hydration/runtime.js +33 -0
  137. package/dist/esm/hydration/target-builder.js +144 -91
  138. package/dist/esm/hydration.js +6 -0
  139. package/dist/esm/index.debug.js +2 -1
  140. package/dist/esm/index.js +38 -29
  141. package/dist/esm/index.rollup.debug.js +3 -2
  142. package/dist/esm/index.rollup.js +1 -1
  143. package/dist/esm/interfaces.js +2 -45
  144. package/dist/esm/metadata.js +2 -8
  145. package/dist/esm/observable.js +1 -4
  146. package/dist/esm/observation/arrays.js +1 -1
  147. package/dist/esm/observation/notifier.js +2 -4
  148. package/dist/esm/observation/observable.js +5 -5
  149. package/dist/esm/observation/update-queue.js +47 -58
  150. package/dist/esm/platform.js +31 -30
  151. package/dist/esm/registry.js +1 -0
  152. package/dist/esm/render.js +1 -0
  153. package/dist/esm/schema.js +1 -0
  154. package/dist/esm/state/exports.js +1 -1
  155. package/dist/esm/styles/css-directive.js +1 -2
  156. package/dist/esm/styles/css.js +15 -56
  157. package/dist/esm/styles/element-styles.js +69 -15
  158. package/dist/esm/styles.js +2 -0
  159. package/dist/esm/templating/html-binding-directive.js +11 -9
  160. package/dist/esm/templating/hydration-view.js +228 -0
  161. package/dist/esm/templating/render.js +39 -18
  162. package/dist/esm/templating/repeat.js +69 -33
  163. package/dist/esm/templating/template.js +7 -7
  164. package/dist/esm/templating/view.js +25 -234
  165. package/dist/esm/templating.js +7 -0
  166. package/dist/esm/testing/exports.js +2 -2
  167. package/dist/esm/testing/fixture.js +2 -2
  168. package/dist/esm/testing/timeout.js +2 -2
  169. package/dist/esm/updates.js +1 -0
  170. package/dist/esm/volatile.js +1 -0
  171. package/dist/fast-element.api.json +14389 -11138
  172. package/dist/fast-element.d.ts +3651 -809
  173. package/dist/fast-element.debug.js +5666 -4722
  174. package/dist/fast-element.debug.min.js +2 -2
  175. package/dist/fast-element.js +5394 -4381
  176. package/dist/fast-element.min.js +2 -2
  177. package/dist/fast-element.untrimmed.d.ts +923 -472
  178. package/dist/hydration/hydration.api.json +6460 -0
  179. package/dist/styles/styles.api.json +2672 -0
  180. package/package.json +165 -45
  181. package/ARCHITECTURE_FASTELEMENT.md +0 -63
  182. package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +0 -36
  183. package/ARCHITECTURE_INTRO.md +0 -10
  184. package/ARCHITECTURE_OVERVIEW.md +0 -52
  185. package/ARCHITECTURE_UPDATES.md +0 -11
  186. package/CHANGELOG.json +0 -2275
  187. package/DESIGN.md +0 -510
  188. package/api-extractor.context.json +0 -14
  189. package/api-extractor.di.json +0 -14
  190. package/biome.json +0 -4
  191. package/dist/dts/components/install-hydration.d.ts +0 -1
  192. package/dist/dts/pending-task.d.ts +0 -32
  193. package/dist/dts/styles/css-binding-directive.d.ts +0 -60
  194. package/dist/dts/templating/install-hydratable-view-templates.d.ts +0 -1
  195. package/dist/esm/components/install-hydration.js +0 -3
  196. package/dist/esm/pending-task.js +0 -28
  197. package/dist/esm/polyfills.js +0 -60
  198. package/dist/esm/styles/css-binding-directive.js +0 -76
  199. package/dist/esm/templating/install-hydratable-view-templates.js +0 -23
  200. package/docs/ACKNOWLEDGEMENTS.md +0 -12
  201. package/docs/api-report.api.md +0 -1122
  202. package/docs/context/api-report.api.md +0 -69
  203. package/docs/di/api-report.api.md +0 -315
  204. package/docs/fast-element-2-changes.md +0 -15
  205. package/playwright.config.ts +0 -26
  206. package/scripts/run-api-extractor.js +0 -51
  207. package/test/index.html +0 -11
  208. package/test/main.ts +0 -104
  209. package/test/vite.config.ts +0 -19
  210. package/tsconfig.api-extractor.json +0 -6
  211. /package/dist/dts/{polyfills.d.ts → __test__/setup-node.d.ts} +0 -0
@@ -0,0 +1,565 @@
1
+ import { defsPropertyName, refPropertyName, schemaRegistry, } from "../components/schema.js";
2
+ import { Observable } from "../observation/observable.js";
3
+ /**
4
+ * A map of proxied objects.
5
+ */
6
+ const objectTargetsMap = new WeakMap();
7
+ /**
8
+ * A map of arrays being observed.
9
+ */
10
+ const observedArraysMap = new WeakSet();
11
+ /**
12
+ * Determines the data type of the provided data
13
+ * @param data - The data to analyze
14
+ * @returns "array" for arrays, "object" for non-null objects, "primitive" for other types
15
+ */
16
+ function getDataType(data) {
17
+ if (Array.isArray(data))
18
+ return "array";
19
+ if (typeof data === "object" && data !== null)
20
+ return "object";
21
+ return "primitive";
22
+ }
23
+ /**
24
+ * Get properties from an anyOf array
25
+ * @param anyOf - The anyOf array in a JSON schema
26
+ * @returns The array item matching a ref if it exists
27
+ */
28
+ function getSchemaPropertiesFromAnyOf(anyOf) {
29
+ let propertiesFromAnyOf = null;
30
+ for (const anyOfItem of anyOf) {
31
+ if (anyOfItem[refPropertyName]) {
32
+ const splitRef = anyOfItem[refPropertyName].split("/");
33
+ const customElement = splitRef.slice(-2)[0];
34
+ const attributeName = splitRef.slice(-1)[0].slice(0, -5);
35
+ if (schemaRegistry.has(customElement)) {
36
+ const customElementSchemaMap = schemaRegistry.get(customElement);
37
+ propertiesFromAnyOf = customElementSchemaMap.get(attributeName);
38
+ }
39
+ }
40
+ }
41
+ return propertiesFromAnyOf;
42
+ }
43
+ /**
44
+ * Gets a properties definition if one exists
45
+ * @param schema - The JSON schema to get properties from
46
+ * @returns A JSON schema with properties or null
47
+ */
48
+ function getSchemaProperties(schema) {
49
+ if (schema === null || schema === void 0 ? void 0 : schema.properties) {
50
+ return schema.properties;
51
+ }
52
+ else if (schema === null || schema === void 0 ? void 0 : schema.anyOf) {
53
+ return getSchemaPropertiesFromAnyOf(schema.anyOf);
54
+ }
55
+ return null;
56
+ }
57
+ /**
58
+ * Checks whether a schema node (or any of its descendants) has observation enabled.
59
+ * Returns false only when the node and ALL its descendants are stamped with `$observe: false`.
60
+ */
61
+ function hasObservedSchemaDescendant(schema) {
62
+ if ((schema === null || schema === void 0 ? void 0 : schema.$observe) !== false) {
63
+ return true;
64
+ }
65
+ const props = getSchemaProperties(schema);
66
+ if (!props) {
67
+ return false;
68
+ }
69
+ return Object.keys(props).some(k => hasObservedSchemaDescendant(props[k]));
70
+ }
71
+ /**
72
+ * Checks whether a schema node is fully excluded from observation.
73
+ * A node is excluded when it is stamped `$observe: false` AND none of
74
+ * its descendants are observed (no re-included leaves beneath it).
75
+ */
76
+ function isSchemaExcluded(schema) {
77
+ return (schema === null || schema === void 0 ? void 0 : schema.$observe) === false && !hasObservedSchemaDescendant(schema);
78
+ }
79
+ function defineObservableProperty(targetObject, key, schema, rootSchema, target, rootProperty) {
80
+ if (!Observable.getAccessors(targetObject).some(accessor => accessor.name === key)) {
81
+ const field = `_${key}`;
82
+ const callback = `${key}Changed`;
83
+ const accessor = {
84
+ name: key,
85
+ getValue(source) {
86
+ Observable.track(source, key);
87
+ return source[field];
88
+ },
89
+ setValue(source, value) {
90
+ const oldValue = source[field];
91
+ const newValue = assignObservables(schema, rootSchema, value, target, rootProperty);
92
+ if (oldValue !== newValue) {
93
+ source[field] = newValue;
94
+ const changeCallback = source[callback];
95
+ if (typeof changeCallback === "function") {
96
+ changeCallback.call(source, oldValue, newValue);
97
+ }
98
+ Observable.notify(source, key);
99
+ }
100
+ },
101
+ };
102
+ Observable.defineProperty(targetObject, accessor);
103
+ }
104
+ }
105
+ function findArrayItemDef(schema) {
106
+ var _a;
107
+ const items = schema.items;
108
+ return (_a = findDef(schema)) !== null && _a !== void 0 ? _a : (items === undefined ? null : findDef(items));
109
+ }
110
+ /**
111
+ * Assigns Observable properties to items in an array and sets up change notifications
112
+ * @param proxiedData - The array data to make observable
113
+ * @param schema - The schema defining the structure of array items
114
+ * @param rootSchema - The root schema for the entire data structure
115
+ * @param target - The target element
116
+ * @param rootProperty - The root property name
117
+ * @returns The array with observable properties and change notifications
118
+ */
119
+ function assignObservablesToArray(proxiedData, schema, rootSchema, target, rootProperty) {
120
+ const schemaProperties = getSchemaProperties(schema);
121
+ // If the schema has no properties, the array contains primitives (e.g. string[])
122
+ // — observe the array for changes but skip per-item proxying.
123
+ const data = schemaProperties
124
+ ? proxiedData.map((item) => {
125
+ const originalItem = Object.assign({}, item);
126
+ assignProxyToItemsInArray(item, originalItem, schema, rootSchema, target, rootProperty);
127
+ return Object.assign(item, originalItem);
128
+ })
129
+ : proxiedData;
130
+ if (!observedArraysMap.has(data)) {
131
+ observedArraysMap.add(data);
132
+ Observable.getNotifier(data).subscribe({
133
+ handleChange(subject, args) {
134
+ args.forEach((arg) => {
135
+ if (arg.addedCount > 0) {
136
+ if (schemaProperties) {
137
+ for (let i = arg.addedCount - 1; i >= 0; i--) {
138
+ const item = subject[arg.index + i];
139
+ const originalItem = Object.assign({}, item);
140
+ assignProxyToItemsInArray(item, originalItem, schema, rootSchema, target, rootProperty);
141
+ Object.assign(item, originalItem);
142
+ }
143
+ }
144
+ // Notify observers of the target object's root property
145
+ Observable.notify(target, rootProperty);
146
+ }
147
+ });
148
+ },
149
+ });
150
+ }
151
+ if (schemaProperties !== null) {
152
+ return data;
153
+ }
154
+ // For primitive arrays, wrap in a Proxy so that direct index assignment
155
+ // (e.g. arr[0] = value) triggers FAST's splice-based change tracking and
156
+ // keeps repeat directives in sync. Object arrays are not wrapped because
157
+ // their items are individually proxied, and FAST's own push/splice/etc.
158
+ // already carry splice records — double-wrapping would produce duplicate
159
+ // splice notifications.
160
+ return new Proxy(data, {
161
+ set: (arr, prop, value) => {
162
+ const idx = typeof prop === "string" ? Number(prop) : NaN;
163
+ if (typeof prop !== "symbol" && Number.isInteger(idx) && idx >= 0) {
164
+ // splice() replaces the item in-place and creates the splice
165
+ // record that FAST's ArrayObserver delivers to repeat directives.
166
+ Array.prototype.splice.call(arr, idx, 1, value);
167
+ }
168
+ else {
169
+ arr[prop] = value;
170
+ }
171
+ return true;
172
+ },
173
+ });
174
+ }
175
+ /**
176
+ * Extracts the definition name from a JSON Schema $ref property
177
+ * @param defName - The $ref string (e.g., "#/$defs/MyType")
178
+ * @returns The definition name (e.g., "MyType")
179
+ */
180
+ function getDefFromRef(defName) {
181
+ const splitName = defName.split("/");
182
+ return splitName[splitName.length - 1];
183
+ }
184
+ /**
185
+ * Find a definition
186
+ * This may exist as a $ref at the root or as a $ref in any anyOf or not at all
187
+ * if the Observer Map has not been enabled on a child component
188
+ * @param schema - The JSON schema to find the ref in
189
+ * @returns The definition or null
190
+ * @public
191
+ */
192
+ export function findDef(schema) {
193
+ const defStartingString = "#/$defs";
194
+ if (schema[refPropertyName] &&
195
+ schema[refPropertyName].startsWith(defStartingString)) {
196
+ return getDefFromRef(schema[refPropertyName]);
197
+ }
198
+ if (schema.anyOf) {
199
+ const index = schema.anyOf.findIndex((anyOfItem) => {
200
+ return (!!anyOfItem[refPropertyName] &&
201
+ anyOfItem[refPropertyName].startsWith(defStartingString));
202
+ });
203
+ if (index > -1) {
204
+ const ref = schema.anyOf[index][refPropertyName];
205
+ if (ref.startsWith(defStartingString)) {
206
+ return getDefFromRef(ref);
207
+ }
208
+ }
209
+ }
210
+ return null;
211
+ }
212
+ /**
213
+ * Assign observables to data
214
+ * @param schema - The schema
215
+ * @param rootSchema - The root schema mapping to the root property
216
+ * @param data - The data
217
+ * @param target - The target custom element
218
+ * @param rootProperty - The root property
219
+ * @returns
220
+ * @public
221
+ */
222
+ export function assignObservables(schema, rootSchema, data, target, rootProperty) {
223
+ var _a;
224
+ const dataType = getDataType(data);
225
+ let proxiedData = data;
226
+ switch (dataType) {
227
+ case "array": {
228
+ const context = findArrayItemDef(schema);
229
+ if (context) {
230
+ proxiedData = assignObservablesToArray(proxiedData, (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context], rootSchema, target, rootProperty);
231
+ }
232
+ else {
233
+ // Primitive array (items have no schema $ref): wrap in a proxy so that
234
+ // direct index assignments (e.g. arr[0] = value) use FAST's splice-based
235
+ // change tracking and keep repeat directives in sync.
236
+ proxiedData = assignObservablesToArray(proxiedData, schema, rootSchema, target, rootProperty);
237
+ }
238
+ break;
239
+ }
240
+ case "object": {
241
+ proxiedData = assignProxyToItemsInObject(target, rootProperty, proxiedData, schema, rootSchema);
242
+ break;
243
+ }
244
+ }
245
+ return proxiedData;
246
+ }
247
+ /**
248
+ * Assign a proxy to items in an array
249
+ * @param proxiableItem - The array item to proxy
250
+ * @param originalItem - The original array item
251
+ * @param schema - The schema mapping to the items in the array
252
+ * @param rootSchema - The root schema assigned to the root property
253
+ */
254
+ function assignProxyToItemsInArray(proxiableItem, originalItem, schema, rootSchema, target, rootProperty) {
255
+ const schemaProperties = getSchemaProperties(schema);
256
+ getObjectProperties(proxiableItem, schemaProperties).forEach(key => {
257
+ const childSchema = schemaProperties[key];
258
+ // Skip properties fully excluded from observation
259
+ if (isSchemaExcluded(childSchema)) {
260
+ return;
261
+ }
262
+ // Initialize the property as undefined if it doesn't exist
263
+ if (!(key in originalItem)) {
264
+ originalItem[key] = undefined;
265
+ }
266
+ // Assign the proxy first
267
+ originalItem[key] = assignProxyToItemsInObject(proxiableItem, key, originalItem[key], schemaProperties[key], rootSchema);
268
+ // Then make the property observable
269
+ defineObservableProperty(proxiableItem, key, childSchema, rootSchema, target, rootProperty);
270
+ });
271
+ }
272
+ /**
273
+ * Get an objects properties as agreed upon between the schema and data
274
+ * @param data - The data
275
+ * @param schemaProperties - The schema properties
276
+ * @returns A list of strings the schema properties enumerate (includes properties not present in data)
277
+ */
278
+ function getObjectProperties(data, schemaProperties) {
279
+ const dataKeys = Object.keys(data);
280
+ const schemaPropertyKeys = Object.keys(schemaProperties !== null && schemaProperties !== void 0 ? schemaProperties : {});
281
+ // Return all schema properties that are either in the data or in the schema
282
+ // This ensures properties defined in schema but missing from data get initialized
283
+ const allKeys = new Set([...dataKeys, ...schemaPropertyKeys]);
284
+ return Array.from(allKeys).filter(function (key) {
285
+ return schemaPropertyKeys.indexOf(key) !== -1;
286
+ });
287
+ }
288
+ /**
289
+ * Assign a proxy to items in an object
290
+ * @param target - The target custom element
291
+ * @param rootProperty - The root property
292
+ * @param data - The data to proxy
293
+ * @param schema - The schema for the data
294
+ * @param rootSchema - The root schema for the root property
295
+ * @returns a Proxy
296
+ */
297
+ function assignProxyToItemsInObject(target, rootProperty, data, schema, rootSchema) {
298
+ var _a;
299
+ const type = getDataType(data);
300
+ const schemaProperties = getSchemaProperties(schema);
301
+ let proxiedData = data;
302
+ if (type === "object" && schemaProperties) {
303
+ // navigate through all items in the object
304
+ getObjectProperties(proxiedData, schemaProperties).forEach(property => {
305
+ const childSchema = schemaProperties[property];
306
+ // Skip properties fully excluded from observation
307
+ if (isSchemaExcluded(childSchema)) {
308
+ return;
309
+ }
310
+ proxiedData[property] = assignProxyToItemsInObject(target, rootProperty, proxiedData[property], schemaProperties[property], rootSchema);
311
+ });
312
+ // Assign a Proxy unless this level is fully excluded from observation
313
+ if (!isSchemaExcluded(schema)) {
314
+ proxiedData = assignProxy(schema, rootSchema, target, rootProperty, proxiedData);
315
+ // Add this target to the object's target list
316
+ addTargetToObject(proxiedData, target, rootProperty);
317
+ }
318
+ }
319
+ else if (type === "array") {
320
+ const context = findArrayItemDef(schema);
321
+ if (context) {
322
+ const definition = (_a = rootSchema[defsPropertyName]) === null || _a === void 0 ? void 0 : _a[context];
323
+ if ((definition === null || definition === void 0 ? void 0 : definition.type) === "object") {
324
+ proxiedData = assignObservablesToArray(proxiedData, definition, rootSchema, target, rootProperty);
325
+ }
326
+ }
327
+ }
328
+ return proxiedData;
329
+ }
330
+ /**
331
+ * Add a target to an object's target list
332
+ * @param object - The object to associate with the target
333
+ * @param target - The target custom element
334
+ * @param rootProperty - The root property name
335
+ */
336
+ function addTargetToObject(object, target, rootProperty) {
337
+ if (!objectTargetsMap.has(object)) {
338
+ objectTargetsMap.set(object, []);
339
+ }
340
+ const targets = objectTargetsMap.get(object);
341
+ targets.push({ target, rootProperty });
342
+ }
343
+ /**
344
+ * Get all targets for an object
345
+ * @param object - The object to get targets for
346
+ * @returns Array of target info objects
347
+ */
348
+ function getTargetsForObject(object) {
349
+ return objectTargetsMap.get(object) || [];
350
+ }
351
+ /**
352
+ * Notify any observables mapped to the object
353
+ * @param targetObject The object that is mapped to a target and rootProperty
354
+ */
355
+ function notifyObservables(targetObject) {
356
+ getTargetsForObject(targetObject).forEach((targetItem) => {
357
+ // Trigger notification for property changes
358
+ Observable.notify(targetItem.target, targetItem.rootProperty);
359
+ });
360
+ }
361
+ /**
362
+ * Assign a proxy to an object
363
+ * @param schema - The current schema
364
+ * @param rootSchema - The root schema for the root property
365
+ * @param target - The target custom element
366
+ * @param rootProperty - The root property
367
+ * @param object - The object to assign the proxy to
368
+ * @returns Proxy object
369
+ * @public
370
+ */
371
+ export function assignProxy(schema, rootSchema, target, rootProperty, object) {
372
+ if (!object.$isProxy) {
373
+ const schemaProperties = getSchemaProperties(schema);
374
+ // Create a proxy for the object that triggers Observable.notify on mutations
375
+ const proxy = new Proxy(object, {
376
+ set: (obj, prop, value) => {
377
+ const currentValue = obj[prop];
378
+ if (deepEqual(currentValue, value)) {
379
+ return true;
380
+ }
381
+ const propName = typeof prop === "string" ? prop : String(prop);
382
+ const childSchema = schemaProperties === null || schemaProperties === void 0 ? void 0 : schemaProperties[propName];
383
+ // If the property is fully excluded, assign without proxying or notifying
384
+ if (isSchemaExcluded(childSchema)) {
385
+ obj[prop] = value;
386
+ return true;
387
+ }
388
+ obj[prop] = assignObservables(childSchema !== null && childSchema !== void 0 ? childSchema : schema, rootSchema, value, target, rootProperty);
389
+ notifyObservables(proxy);
390
+ return true;
391
+ },
392
+ get: (target, key) => {
393
+ if (key !== "$isProxy") {
394
+ return target[key];
395
+ }
396
+ return true;
397
+ },
398
+ deleteProperty: (obj, prop) => {
399
+ if (prop in obj) {
400
+ const propName = typeof prop === "string" ? prop : String(prop);
401
+ const childSchema = schemaProperties === null || schemaProperties === void 0 ? void 0 : schemaProperties[propName];
402
+ delete obj[prop];
403
+ // Only suppress notification if fully excluded
404
+ if (isSchemaExcluded(childSchema)) {
405
+ return true;
406
+ }
407
+ notifyObservables(proxy);
408
+ return true;
409
+ }
410
+ return false;
411
+ },
412
+ });
413
+ return proxy;
414
+ }
415
+ return object;
416
+ }
417
+ /**
418
+ * Determine if an object has an observable accessor for a backing field
419
+ * @param object - The object to check
420
+ * @param backingField - The backing field name
421
+ * @returns True if the object has an observable accessor for the backing field, false otherwise
422
+ */
423
+ function hasObservableAccessor(object, backingField) {
424
+ const accessors = Observable.getAccessors(object);
425
+ if (!accessors) {
426
+ return false;
427
+ }
428
+ return accessors.some((accessor) => accessor.name === backingField);
429
+ }
430
+ /**
431
+ * Determine if a key should be skipped during deep comparison
432
+ *
433
+ * @param object - The object to check
434
+ * @param key - The key to evaluate
435
+ * @returns True if the key should be skipped during comparison, false otherwise
436
+ */
437
+ function shouldSkipKey(object, key) {
438
+ if (key[0] !== "_" || key === "_") {
439
+ return false;
440
+ }
441
+ return hasObservableAccessor(object, key.slice(1));
442
+ }
443
+ /**
444
+ * Get comparable keys from an object, excluding those that should be skipped
445
+ *
446
+ * @param object - The object to extract keys from
447
+ * @returns An array of keys that should be compared
448
+ */
449
+ function getComparableKeys(object) {
450
+ const hasOwn = Object.prototype.hasOwnProperty;
451
+ const keys = [];
452
+ for (const key in object) {
453
+ if (!hasOwn.call(object, key) || shouldSkipKey(object, key)) {
454
+ continue;
455
+ }
456
+ keys.push(key);
457
+ }
458
+ return keys;
459
+ }
460
+ /**
461
+ * Deeply compares two objects for equality.
462
+ *
463
+ * @param obj1 - First object to compare
464
+ * @param obj2 - Second object to compare
465
+ * @returns True if the objects are deeply equal, false otherwise
466
+ * @public
467
+ */
468
+ export function deepEqual(obj1, obj2) {
469
+ if (Object.is(obj1, obj2)) {
470
+ return true;
471
+ }
472
+ if (obj1 == null || obj2 == null) {
473
+ return false;
474
+ }
475
+ const type1 = typeof obj1;
476
+ const type2 = typeof obj2;
477
+ if (type1 !== type2 || type1 !== "object") {
478
+ return false;
479
+ }
480
+ const isArray1 = Array.isArray(obj1);
481
+ const isArray2 = Array.isArray(obj2);
482
+ if (isArray1 !== isArray2) {
483
+ return false;
484
+ }
485
+ if (isArray1) {
486
+ const len = obj1.length;
487
+ if (len !== obj2.length) {
488
+ return false;
489
+ }
490
+ for (let i = 0; i < len; i++) {
491
+ if (!deepEqual(obj1[i], obj2[i])) {
492
+ return false;
493
+ }
494
+ }
495
+ return true;
496
+ }
497
+ const hasOwn = Object.prototype.hasOwnProperty;
498
+ const obj1Keys = getComparableKeys(obj1);
499
+ const obj2Keys = getComparableKeys(obj2);
500
+ if (obj1Keys.length !== obj2Keys.length) {
501
+ return false;
502
+ }
503
+ for (const key of obj1Keys) {
504
+ if (!hasOwn.call(obj2, key) || !deepEqual(obj1[key], obj2[key])) {
505
+ return false;
506
+ }
507
+ }
508
+ return true;
509
+ }
510
+ /**
511
+ * Checks if a value is a plain object (not an array, null, or other type).
512
+ *
513
+ * @param value - The value to check
514
+ * @returns True if the value is a plain object, false otherwise
515
+ * @public
516
+ */
517
+ export function isPlainObject(value) {
518
+ return !!value && typeof value === "object" && !Array.isArray(value);
519
+ }
520
+ /**
521
+ * Deeply merges the source object into the target object.
522
+ *
523
+ * @param target - The target object to merge into
524
+ * @param source - The source object to merge from
525
+ * @returns boolean indicating whether changes were made
526
+ * @public
527
+ */
528
+ export function deepMerge(target, source) {
529
+ const hasOwn = Object.prototype.hasOwnProperty;
530
+ let hasChanges = false;
531
+ for (const key in source) {
532
+ if (!hasOwn.call(source, key)) {
533
+ continue;
534
+ }
535
+ const sourceValue = source[key];
536
+ if (sourceValue === void 0) {
537
+ continue;
538
+ }
539
+ const targetValue = target[key];
540
+ if (deepEqual(targetValue, sourceValue)) {
541
+ continue;
542
+ }
543
+ hasChanges = true;
544
+ if (Array.isArray(sourceValue)) {
545
+ const clonedItems = sourceValue.map((item) => isPlainObject(item) ? Object.assign({}, item) : item);
546
+ target[key] = clonedItems;
547
+ continue;
548
+ }
549
+ if (isPlainObject(sourceValue)) {
550
+ const targetIsObject = isPlainObject(targetValue);
551
+ const nextTarget = targetIsObject ? Object.assign({}, targetValue) : {};
552
+ const nestedChanged = deepMerge(nextTarget, sourceValue);
553
+ if (!targetIsObject) {
554
+ target[key] = nextTarget;
555
+ continue;
556
+ }
557
+ if (nestedChanged) {
558
+ target[key] = nextTarget;
559
+ }
560
+ continue;
561
+ }
562
+ target[key] = sourceValue;
563
+ }
564
+ return hasChanges;
565
+ }