Problem Statement:
The Neo.state.Provider's data_ config, intended to support deep merging of data from class-level definitions and instance-level overrides, was not functioning as expected. Initial attempts to leverage the merge: 'deep' descriptor strategy resulted in only instance-level data being present, or various TypeError exceptions during testing due to incompatibilities between the createHierarchicalDataProxy and the Siesta testing framework's deep comparison utilities.
Root Cause & Analysis:
Initial Deep Merge Failure: The primary reason for the data_ config not deep-merging was that its value property in the descriptor was set to null. When Neo.setupClass invoked Neo.mergeConfig for data_, null was passed as the defaultValue. Since null is not an object, the deep merge strategy's condition (defaultValueType === 'Object' && instanceValueType === 'Object') was never met, causing Neo.mergeConfig to default to a replace strategy, effectively discarding class-level data.
Proxy Introspection Issues: After addressing the core merge issue, the createHierarchicalDataProxy encountered several TypeError exceptions when subjected to Siesta's deep comparison (t.isDeeplyStrict) and other introspection methods. These errors stemmed from the proxy's default behavior not exposing its dynamically managed properties in a way that external tools could enumerate or inspect. Specifically:
- Accessing internal properties like
__REFADR__ (used by Siesta) or symbol properties.
- Attempting to enumerate properties (e.g.,
Object.keys()) on the proxy's empty internal target object.
- Incorrectly handling nested objects during deep comparison, leading to
config.get is not a function errors when Siesta tried to inspect raw objects instead of nested proxies.
Solution & Changes Implemented:
Enable data_ Deep Merge (Core Fix):
src/state/Provider.mjs: Changed data_.value from null to {}. This ensures that Neo.mergeConfig receives a valid empty object as defaultValue, allowing the merge: 'deep' strategy to correctly combine class-level and instance-level data.
Enhance createHierarchicalDataProxy for Robust Introspection & Compatibility:
src/state/createHierarchicalDataProxy.mjs:
get trap: Modified to explicitly handle symbol properties, __REFADR__, inspect, and then by using Reflect.get(currentTarget, property). This prevents errors when external tools or internal mechanisms try to access these non-data properties directly on the proxy.
set trap: Modified to explicitly handle symbol properties and __REFADR__ by using Reflect.set(currentTarget, property, value).
ownKeys trap: Implemented to correctly report the top-level data keys managed by the State.Provider by calling rootProvider.getTopLevelDataKeys(path). This allows introspection methods like Object.keys() and for...in loops to function correctly.
getOwnPropertyDescriptor trap: Implemented to provide accurate property descriptors. Crucially, for nested objects, it now returns a new createNestedProxy as the value in the descriptor, enabling deep comparison tools to recursively traverse the proxied data structure.
Refine State.Provider's Data Processing:
src/state/Provider.mjs:
processDataObject: Adjusted the recursive processing condition to Neo.typeOf(value) === 'Object'. This ensures that only plain JavaScript objects are recursively processed, preventing Config instances or other Neo.mjs classes from being incorrectly re-processed, which could corrupt the internal #dataConfigs map.
getTopLevelDataKeys: Added a public method to State.Provider to expose the top-level data keys from its private #dataConfigs map, allowing the proxy's ownKeys trap to function without directly accessing private fields.
Adapt Testing Utilities:
test/siesta/tests/state/Provider.mjs:
proxyToObject helper: Reverted to a more robust implementation that recursively converts the proxy into a plain JavaScript object for t.isDeeplyStrict comparisons. This helper now correctly handles null/undefined values and uses Neo.typeOf(value) === 'Object' for accurate plain object detection.
t.isDeeplyStrict calls: Modified to use the proxyToObject helper, ensuring that the deep comparison is performed on a plain object representation of the proxied data.
- New Test Case: Added a test case for multi-level
State.Provider class extension deep merging to verify that data_ configs are correctly deep-merged across multiple levels of State.Provider class extensions.
Overall Impact:
The Neo.state.Provider's data_ config now correctly supports deep merging of class-level and instance-level data. The createHierarchicalDataProxy is significantly more robust, compatible with standard JavaScript introspection methods, and fully testable with deep comparison utilities like Siesta's t.isDeeplyStrict. All relevant tests are now passing, confirming the correct behavior of the State.Provider's reactive data system.
Problem Statement: The
Neo.state.Provider'sdata_config, intended to support deep merging of data from class-level definitions and instance-level overrides, was not functioning as expected. Initial attempts to leverage themerge: 'deep'descriptor strategy resulted in only instance-level data being present, or variousTypeErrorexceptions during testing due to incompatibilities between thecreateHierarchicalDataProxyand the Siesta testing framework's deep comparison utilities.Root Cause & Analysis:
Initial Deep Merge Failure: The primary reason for the
data_config not deep-merging was that itsvalueproperty in the descriptor was set tonull. WhenNeo.setupClassinvokedNeo.mergeConfigfordata_,nullwas passed as thedefaultValue. Sincenullis not an object, thedeepmerge strategy's condition (defaultValueType === 'Object' && instanceValueType === 'Object') was never met, causingNeo.mergeConfigto default to areplacestrategy, effectively discarding class-level data.Proxy Introspection Issues: After addressing the core merge issue, the
createHierarchicalDataProxyencountered severalTypeErrorexceptions when subjected to Siesta's deep comparison (t.isDeeplyStrict) and other introspection methods. These errors stemmed from the proxy's default behavior not exposing its dynamically managed properties in a way that external tools could enumerate or inspect. Specifically:__REFADR__(used by Siesta) orsymbolproperties.Object.keys()) on the proxy's empty internaltargetobject.config.get is not a functionerrors when Siesta tried to inspect raw objects instead of nested proxies.Solution & Changes Implemented:
Enable
data_Deep Merge (Core Fix):src/state/Provider.mjs: Changeddata_.valuefromnullto{}. This ensures thatNeo.mergeConfigreceives a valid empty object asdefaultValue, allowing themerge: 'deep'strategy to correctly combine class-level and instance-leveldata.Enhance
createHierarchicalDataProxyfor Robust Introspection & Compatibility:src/state/createHierarchicalDataProxy.mjs:gettrap: Modified to explicitly handlesymbolproperties,__REFADR__,inspect, andthenby usingReflect.get(currentTarget, property). This prevents errors when external tools or internal mechanisms try to access these non-data properties directly on the proxy.settrap: Modified to explicitly handlesymbolproperties and__REFADR__by usingReflect.set(currentTarget, property, value).ownKeystrap: Implemented to correctly report the top-level data keys managed by theState.Providerby callingrootProvider.getTopLevelDataKeys(path). This allows introspection methods likeObject.keys()andfor...inloops to function correctly.getOwnPropertyDescriptortrap: Implemented to provide accurate property descriptors. Crucially, for nested objects, it now returns a newcreateNestedProxyas thevaluein the descriptor, enabling deep comparison tools to recursively traverse the proxied data structure.Refine
State.Provider's Data Processing:src/state/Provider.mjs:processDataObject: Adjusted the recursive processing condition toNeo.typeOf(value) === 'Object'. This ensures that only plain JavaScript objects are recursively processed, preventingConfiginstances or other Neo.mjs classes from being incorrectly re-processed, which could corrupt the internal#dataConfigsmap.getTopLevelDataKeys: Added a public method toState.Providerto expose the top-level data keys from its private#dataConfigsmap, allowing the proxy'sownKeystrap to function without directly accessing private fields.Adapt Testing Utilities:
test/siesta/tests/state/Provider.mjs:proxyToObjecthelper: Reverted to a more robust implementation that recursively converts the proxy into a plain JavaScript object fort.isDeeplyStrictcomparisons. This helper now correctly handlesnull/undefinedvalues and usesNeo.typeOf(value) === 'Object'for accurate plain object detection.t.isDeeplyStrictcalls: Modified to use theproxyToObjecthelper, ensuring that the deep comparison is performed on a plain object representation of the proxied data.State.Providerclass extension deep merging to verify thatdata_configs are correctly deep-merged across multiple levels ofState.Providerclass extensions.Overall Impact: The
Neo.state.Provider'sdata_config now correctly supports deep merging of class-level and instance-level data. ThecreateHierarchicalDataProxyis significantly more robust, compatible with standard JavaScript introspection methods, and fully testable with deep comparison utilities like Siesta'st.isDeeplyStrict. All relevant tests are now passing, confirming the correct behavior of theState.Provider's reactive data system.