Skip to content

Valdi Web sync - #148

Open
scorsin-oai wants to merge 15 commits into
mainfrom
simon/260814-valdi-web
Open

Valdi Web sync#148
scorsin-oai wants to merge 15 commits into
mainfrom
simon/260814-valdi-web

Conversation

@scorsin-oai

Copy link
Copy Markdown
Collaborator

Description

This change syncs up most of the Valdi web implementation in the OpenAI fork back to upstream.

  • Adds Valdi Web as a complete supported platform, including DOM rendering, layout, styling, text and inputs, images, animations, navigation, gestures, custom views, workers, persistence, and browser lifecycle handling.
  • Introduces valdi install web, giving developers a first-class command to build and launch a Valdi application in the browser. Web also participates in the existing hot-reload workflow.
  • Adds Bazel support for Web applications and reusable npm packages, including web_deps, native-module registration, worker entrypoints, configurable asset inlining, tree shaking, and package metadata.
  • Extends the compiler for Web module generation and loading, lazy module resolution, native-module shims, path collapsing, inline localization, worker registries, and reliable hot-reload source mapping.
  • Aligns Web typography with host applications by inheriting font settings and smoothing and mapping Valdi system font weights appropriately.
  • Adds the cross-platform integration-test module: 137 renderer cases, deterministic snapshot capture, node-output comparison, native image diffing, and an interactive HTML report for comparing Web and native renderers.
    Includes a framework-level SSR prototype with a server DOM, HTML/CSS serialization, routing, streaming responses, and tests.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • Documentation improvement
  • Performance optimization
  • Test improvement
  • Other (please describe)

Testing

  • Tests pass locally (bazel test //...)
  • Added/updated tests for changes (if applicable)
  • Tested on multiple platforms (iOS/Android/Web/macOS as applicable)
  • Manual testing performed (describe below)

Testing Details

Checklist

  • Code follows project style guidelines
  • Documentation updated (if needed)
  • No breaking changes (or documented in description)
  • Commit messages follow conventional format
  • No secrets, API keys, or internal URLs included

Related Issues

Additional Context

…ical-color-palettes

# Conflicts:
#	valdi/src/valdi/runtime/Attributes/AttributesBindingContextImpl.cpp
#	valdi/src/valdi/runtime/Context/ViewNode.cpp
#	valdi/src/valdi/runtime/Context/ViewNode.hpp
#	valdi/src/valdi/runtime/JavaScript/Modules/AttributedTextNativeModuleFactory.cpp
#	valdi/test/runtime/ViewNode_tests.cpp
…pi-versioning

# Conflicts:
#	compiler/compiler/Compiler/Sources/Processors/NativeCodeGenerationManager.swift
…0814-valdi-web

# Conflicts:
#	valdi/src/valdi/runtime/Context/ViewNode.cpp
#	valdi/src/valdi/runtime/Context/ViewNode.hpp
#	valdi/test/runtime/AttributeProcessors_tests.cpp
# Conflicts:
#	src/valdi_modules/src/valdi/web_renderer/src/ValdiWebRuntime.ts
#	valdi/src/valdi/runtime/Context/ViewNode.cpp
#	valdi/src/valdi/runtime/JavaScript/JavaScriptRuntime.cpp
#	valdi/src/valdi/runtime/JavaScript/Modules/AttributedTextNativeModuleFactory.cpp
#	valdi/src/valdi/runtime/Resources/ThemableAsset.cpp
#	valdi/test/integration/Runtime_tests.cpp
#	valdi/test/runtime/AttributeProcessors_tests.cpp
@github-actions

Copy link
Copy Markdown

Sensitive Files Detected

🔧 Build rules — Affects build rules for all Valdi consumers.

This is an automated notice. A maintainer will review after import.

@github-actions github-actions Bot added area/runtime Valdi runtime (C++/native) area/compiler Valdi compiler area/build-system Bazel build rules and config area/docs Documentation area/snap-drawing SnapDrawing library size/XL labels Aug 18, 2026
@github-actions

Copy link
Copy Markdown

📊 PR Size: size/XL

Total changes: 55434 lines (414 files)

Top files changed:

  • apps/integration_test/src/valdi/integration_test_app/src/IntegrationTestCases.tsx: +5589 -0
  • src/valdi_modules/src/valdi/web_renderer/test/WebRendererCore.spec.ts: +5330 -0
  • compiler/companion/src/VersioningValidator.spec.ts: +1934 -0
  • src/valdi_modules/src/valdi/web_renderer/src/elements/ViewElementAttributes.ts: +991 -0
  • apps/integration_test/src/valdi/integration_test_cli/src/htmlReport.ts: +946 -0
  • bzl/valdi/valdi_collapse_web_paths.bzl: +119 -806
  • src/valdi_modules/src/valdi/web_renderer/src/core/ViewNode.ts: +920 -0
  • src/valdi_modules/src/valdi/valdi_ssr/src/dom/ServerDOM.ts: +850 -0
  • src/valdi_modules/src/valdi/persistence/web/PersistentStoreNative.ts: +522 -255
  • compiler/companion/src/VersioningValidator.ts: +750 -0

...and 404 more files

Size calculated as additions + deletions. Labels: XS (<10), S (<50), M (<250), L (<1000), XL (1000+)

Comment on lines +118 to +124
window.parent.postMessage(
{
channel: WEB_DEBUGGER_CHANNEL,
...payload,
},
'*',
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:
The target origin of the window.postMessage() API is set to "*". This could allow for information disclosure due to the possibility of any origin allowed to receive the message.

To resolve this comment:

🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by wildcard-postmessage-configuration.

You can view more details about this finding in the Semgrep AppSec Platform.

relayCompilerEvents: boolean,
): Promise<CommandResult> {
return new Promise((resolve, reject) => {
const child = spawn(command, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Semgrep identified an issue in your code:
Untrusted input might be injected into a command executed by the application, which can lead to a command injection vulnerability. An attacker can execute arbitrary commands, potentially gaining complete control of the system. To prevent this vulnerability, avoid executing OS commands with user input. If this is unavoidable, validate and sanitize the user input, and use safe methods for executing the commands. For more information, see [Command injection prevention for JavaScript ] (https://semgrep.dev/docs/cheat-sheets/javascript-command-injection/).

Dataflow graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>npm_modules/cli/src/commands/hotreload.ts</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L340 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 340] client.query</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L340 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 340] targets</a>"]

            v3["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L340 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 340] await</a>"]

            v4["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L403 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 403] resolveHotreloadTarget</a>"]

            v5["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L403 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 403] await</a>"]

            v6["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L403 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 403] resolvedTarget</a>"]

            v7["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L407 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 407] hotreloadResolvedTarget</a>"]

            v8["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L267 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 267] resolvedTarget</a>"]

            v9["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L283 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 283] await</a>"]

            v10["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L283 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 283] buildOutputs</a>"]

            v11["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L289 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 289] hotreloadCommand</a>"]

            v12["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L311 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 311] buildHotreloadCommand</a>"]

            v13["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L91 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 91] hotreloadCommand</a>"]

            v14["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L310 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 310] runHotreloadWithCompilerEvents</a>"]

            v15["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L148 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 148] command</a>"]
        end
            v2 --> v3
            v3 --> v4
            v4 --> v5
            v5 --> v6
            v6 --> v7
            v7 --> v8
            v8 --> v9
            v9 --> v10
            v10 --> v11
            v11 --> v12
            v12 --> v13
            v13 --> v14
            v14 --> v15
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/Snapchat/Valdi/blob/08a94ae1ba4624b0d6f0d139eb27f2ea4a560068/npm_modules/cli/src/commands/hotreload.ts#L154 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 154] command</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink


Loading

To resolve this comment:

🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by express-child-process.

You can view more details about this finding in the Semgrep AppSec Platform.

@semgrep-code-snapchat

Copy link
Copy Markdown

Semgrep found 15 internal-sensitive-strings findings:

This literal might contain a Snapchat internal reference that should not be committed to open-source repositories.

Fix: Please replace / remove the string to avoid committing it to open-source repositories.

@github-actions

Copy link
Copy Markdown

⚠️ Bazel & CI Test Results

Test Suite Result
macOS: C++ & Platform Tests ✅ success
Valdi Smoke Tests ❌ failure
API Surface Check ✅ success
Snapshot Tests ✅ success
valdi_web Integration Test ❌ failure
Linux: Hotreload Smoke ✅ success
Linux: Registry Validation ❌ failure
Linux: Build & Export ✅ success
Linux: Build Compiler ✅ success
Linux: Module Tests ✅ success
Linux: C++ Tests ❌ failure

Some tests failed. Please check the workflow logs for details.

🚀 Bazel remote cache is now enabled - future builds will be faster!

Workflow: Valdi CI

@clholgat

Copy link
Copy Markdown
Collaborator

Pulled 08a94ae into the public AYAB Valdi update and tested it against another downstream Web consumer.

AYAB builds, all 11 Bazel test targets and 17 browser E2E workflows pass, and simulation works. The second consumer builds and its canonical Valdi/package verification passes with compatibility patches. These are the issues I hit:

1. External paths escape the collapsed output tree

With Valdi consumed through Bzlmod, _dest_native() can receive paths like:

../valdi~/src/valdi_modules/src/valdi/coreutils/src/RuntimeBase.d.ts

The fallback returns that unchanged, so the collapse action writes outside its declared tree artifact.

Patch used in AYAB

2. Current Widgets still uses setColorPalette

InitSemanticColors.ts calls runtime.setColorPalette(), which this branch removes in favor of named palettes.

Widgets compatibility patch

The matching Widgets change needs to land with this, or Valdi needs a temporary compatibility wrapper.

3. PersistentStore changes format without migrating existing data

The previous Web implementation stored:

valdi.PersistentStore.<store-name>

The new implementation stores individual entries under:

valdi.persistence.v1:<scope>:<store-name>:<entry-key>

I couldn't find a fallback reader or migration. Existing preferences and recoverable app state disappear after upgrading.

4. Native-module shim generation and ownership are fragile

I expected @ExportModule plus the Web implementation in web_deps to be sufficient. Instead:

  • Without web_register_native_module_id_overrides, normal module imports remain unresolved.
  • Overrides declared by dependencies aren't reliably propagated into downstream collapsed packages.
  • Declaring an override on an owning module can make the concrete output and generated shim target the same URL, causing a duplicate .finalFile error.

Temporary AYAB override

This needs one ownership model, transitive aggregation, and automatic inference from the exported declaration and Web implementation rather than downstream registration metadata. The duplicate-output diagnostic should also identify the generated shim.

5. The browser-history navigation API has no equivalent replacement

WebNavStack, RouteRegistry, and the previous WebNavigator exposed URL construction, deep-link restoration, popstate, and back/forward navigation.

The new navigation implementation doesn't appear to expose equivalent browser-routing behavior. We currently have to restore the compatibility files downstream.

6. Generated .bin entries need undocumented Webpack configuration

_module_entry_registry.js emits static require() calls for .bin entries. A regular Webpack 5 consumer fails to parse them unless it adds:

{
  test: /\.bin$/,
  type: 'asset/bytes',
}

Either the generated package needs to be bundler-neutral, or the required loader configuration needs to be shipped and documented.

7. Webpack reports a dynamic-require warning

Every build emits:

Critical dependency: require function is used in a way in which dependencies
cannot be statically extracted

This appears to come from passing the raw require into dynamic component resolution. A generated static registry should avoid it.

8. Non-interactive layout overlays block pointer input

Catalog-style pages render normally, but cards underneath an empty absolute layout cannot be clicked. The new LayoutElementClass creates ordinary <div> elements without the previous pointer-events: none default, so an empty full-body layout wins elementFromPoint() and intercepts real pointer input.

The old contract was transparent layout/view nodes with interactive nodes opted back in by onTap, touchEnabled, or hitTest. Restoring that behavior fixes real mouse clicks through overlapping layouts. This needs a renderer test covering an empty overlay and an interactive child.

9. Pointer-transparent layouts also disable text fields and scroll views

After restoring transparent layout containers, the new TextFieldElementClass and ScrollElementClass remain pointer-events: none. Text fields cannot receive focus/input and wheel events cannot reach scroll views.

Hit-testing needs to be owned per element class: transparent layout overlays, pointer-active onTap views, editable text fields, and wheel/touch-scrollable scroll views should be tested together as one contract.

10. onTouch lost desktop mouse support and local coordinates

Widgets Slider uses onTouch as a cross-input drag primitive and calculates its value from element-local event.x. The previous renderer supplied a document-level mouse sequence and local x/y. This PR binds only browser touch events and reports viewport coordinates, so a real mouse drag leaves the slider unchanged.

The local fix restores desktop mouse handling, listener cleanup, and local coordinates while retaining absoluteX/absoluteY, but it has not been rerun yet. This should probably use Pointer Events plus pointer capture, with mouse, touch, drag-outside-bounds, coordinate, and cleanup tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/build-system Bazel build rules and config area/compiler Valdi compiler area/docs Documentation area/runtime Valdi runtime (C++/native) area/snap-drawing SnapDrawing library size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants