Skip to content

Add a framework-agnostic data layer to the web core library#631

Draft
jacobsimionato wants to merge 12 commits intogoogle:mainfrom
jacobsimionato:data-layer-1
Draft

Add a framework-agnostic data layer to the web core library#631
jacobsimionato wants to merge 12 commits intogoogle:mainfrom
jacobsimionato:data-layer-1

Conversation

@jacobsimionato
Copy link
Collaborator

No description provided.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces a framework-agnostic data layer for the web core library, aligning it with the v0.9 specification and the Flutter implementation. It features a reactive DataModel with JSON Pointer support, a MessageProcessor for protocol handling, and Zod-based schema validation for components. The architecture is well-decoupled, allowing for easier integration with different web frameworks. However, the relaxation of TypeScript strictness in tsconfig.json is a concern, and there are potential edge cases in the data model's path resolution and array handling that should be addressed to ensure robustness.


/* Linting */
"strict": true,
"strict": false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Disabling strict mode in a core library is a significant regression in type safety. It is highly recommended to keep strict: true to catch potential null/undefined errors and ensure better code quality, especially for a library intended to be used across different frameworks.

Suggested change
"strict": false,
"strict": true,

Comment on lines +66 to +71
if (current[segment] === undefined || current[segment] === null) {
const nextSegment = (i < segments.length - 1) ? segments[i + 1] : lastSegment;
current[segment] = /^\d+$/.test(nextSegment) ? [] : {};
}
current = current[segment];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The set method does not handle cases where an intermediate path segment exists but is a primitive value (e.g., a string or number). If the path traversal encounters a primitive, subsequent property access or assignment will fail or behave unexpectedly. It would be safer to check if current[segment] is an object before proceeding or to overwrite it if the path implies a nested structure.


if (value === undefined) {
if (Array.isArray(current)) {
current[parseInt(lastSegment, 10)] = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Setting an array element to undefined using an index assignment creates a "hole" in the array and does not change its length. If the intention is to remove the element from the array (consistent with delete for objects), Array.prototype.splice() should be used instead.

Suggested change
current[parseInt(lastSegment, 10)] = undefined;
current.splice(parseInt(lastSegment, 10), 1);

}
} else if (msg.updateDataModel) {
const payload = msg.updateDataModel;
const path = payload.path || '/';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The routeMessage method uses if/else if blocks to process updateComponents and updateDataModel. This means that if a single message object contains both keys, only the first one encountered will be processed. While the protocol might currently favor one command per message, it's more robust to process all present commands by using separate if blocks.

Suggested change
const path = payload.path || '/';
}
if (msg.updateDataModel) {

}

private parsePath(path: string): string[] {
return path.split('/').filter(p => p.length > 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The parsePath method uses a simple split on /, which does not support standard JSON Pointer escaping (e.g., ~1 for / and ~0 for ~). If the data model is intended to be fully compliant with JSON Pointer (RFC 6901), these escape sequences should be handled during path parsing.

Comment on lines +181 to +183
if (component && component !== existing.type) {
console.warn(`Attempting to change type of component ${id} from ${existing.type} to ${component}. Ignoring new type.`);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When a component already exists, the code warns and ignores a type change. However, if the server intentionally changed the component type for a specific ID, simply updating the properties of the old type might lead to an invalid state or rendering errors. A more robust approach would be to recreate the component if the type discriminator changes.

Renames the 'A2uiModel' class to 'SurfaceGroupModel' and its corresponding
files to better reflect its role in managing groups of surfaces.
Updates all internal references and tests accordingly.
…d ComponentsModel

Refactors SurfaceGroupModel and ComponentsModel to accept instances via `addSurface`
and `addComponent` respectively, improving encapsulation. Implements an observable
pattern for action handling in SurfaceModel and SurfaceGroupModel, removing the
ActionHandler dependency from the SurfaceModel constructor.
Renames ComponentsModel to SurfaceComponentsModel to better reflect its
scope and relationship with SurfaceModel. Updates all references and exports.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant

Comments