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