Extract Web API abstraction into spark_web package#25
Merged
kevin-sakemaer merged 17 commits intomainfrom Feb 12, 2026
Merged
Conversation
…N naming Introduces `spark_web`, a cross-platform Web API layer that mirrors MDN naming exactly (Element, Node, Document, Window, etc.) so developers can rely on MDN docs. On the browser, types wrap real `package:web` objects. On the server, types are no-ops or provide Dart-native fallbacks (Storage backed by Map, Crypto via dart:math, btoa/atob via dart:convert). Key changes: - New `packages/spark_web/` package with abstract interfaces, browser implementations (wrapping package:web), and server implementations - Migrated spark_framework to use spark_web instead of the old stubs.dart noSuchMethod pattern — provides real type safety - vdom_web.dart continues using package:web directly for performance (browser-only, behind conditional export) - Deleted stubs.dart (563 lines of noSuchMethod-based stubs) - Updated js_callback_stub, query_stubs, extensions, and tests https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
- Fix innerHTML/outerHTML to handle JSAny <-> String conversion - Fix hidden property to handle JSAny? <-> bool conversion - Fix MutationObserverInit to pass non-nullable bool params with defaults - Fix Console type ($Console in package:web) and use top-level console - Fix setTimeout/setInterval to pass delay as JSAny? - Export createMutationObserver/createEvent from spark_web barrel - Remove Request/Response from hide list (no longer in spark_web) - Remove unused imports (dart:js_interop, dart:async) https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
Adds the Clipboard API matching MDN (navigator.clipboard): - readText() → Future<String> - writeText(data) → Future<void> Browser impl wraps package:web Clipboard via navigator.clipboard. Server impl stores text in memory (useful for testing). https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
README includes usage examples, architecture overview, and a comprehensive API coverage table tracking implemented vs TODO MDN Web APIs organized by category (DOM Core, Elements, CSS, Window, Networking, Observers, etc.). https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
Adds all missing event types: - FocusEvent, WheelEvent, PointerEvent, TouchEvent, DragEvent - AnimationEvent, TransitionEvent, CustomEvent Adds all missing HTML element types: - HTMLCanvasElement, HTMLMediaElement, HTMLVideoElement, HTMLAudioElement - HTMLDialogElement, HTMLDetailsElement, HTMLSlotElement, HTMLIFrameElement - HTMLTableElement, HTMLTableSectionElement, HTMLTableRowElement, HTMLTableCellElement - HTMLHeadingElement, HTMLUListElement, HTMLOListElement, HTMLLIElement - HTMLPreElement, HTMLHRElement, HTMLBRElement - HTMLProgressElement, HTMLMeterElement, HTMLOutputElement All types include abstract interfaces, server stubs, and browser implementations wrapping package:web. DOM Core and DOM Elements sections are now 100% complete. https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
- Delete js_callback_web.dart / js_callback_stub.dart — inline browser event listener logic directly into vdom_web.dart - Delete query_stubs.dart / query_web.dart — unused top-level query functions superseded by WebComponent.query() instance methods - Delete adopted_styles_web.dart / adopted_styles_stub.dart — replace with cross-platform code using spark_web's CSSStyleSheet API - Delete orphaned html/query_stubs.dart - Add createCSSStyleSheet() factory to spark_web for constructable stylesheets on both server and browser - Move stylesheet caching into web_component.dart using spark_web types - Update adopted_styles_test to import from web_component.dart 7 files deleted, net -195 lines. All 342 tests pass. https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
…ransfer abstractions Replace all dynamic return types (except the intentional `raw` escape hatch) with fully typed alternatives: - Add Touch, TouchList, DataTransfer abstract interfaces to core.dart - Add BrowserTouch, BrowserTouchList, BrowserDataTransfer wrappers - Add ServerTouch, ServerTouchList, ServerDataTransfer no-op impls - TouchEvent.touches/targetTouches/changedTouches now return TouchList - DragEvent.dataTransfer now returns DataTransfer? - CustomEvent.detail now returns Object? instead of dynamic - Console methods now take Object? instead of dynamic - History.state/pushState/replaceState now use Object? - CustomElementRegistry.define/get now use Object/Object? - HTMLCanvasElement.getContext returns Object?, toDataURL quality is num? The only remaining `dynamic` in spark_web is on the `raw` getter — the intentional escape hatch to the underlying platform object. https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
…nd Canvas - Add ElementDefinitionOptions class for CustomElementRegistry.define() with extends_ property (maps to JS 'extends' keyword) - Add RenderingContext base type and CanvasRenderingContext2D with full typed API (drawing, paths, text, styles, transforms, pixel manipulation) - Add TextMetrics and ImageData abstract types - HTMLCanvasElement.getContext now returns RenderingContext? instead of Object? - Browser implementations wrap package:web natives with proper type conversions - History.state, Console, CustomEvent.detail remain Object? as they are inherently "any" in the web spec https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
The contextId parameter is now a CanvasContextType enum instead of a free-form string. The enum currently supports canvas2d, with comments guiding contributors on how to add webgl, webgl2, webgpu, etc. https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
Provides query() and queryAll() stubs that user @component classes can mix in. This lets the user write `query('#my-el')` in their base class without IDE errors. The generated code inherits the real implementations from WebComponent, so the stubs are never called at runtime. Usage: @component(tag: 'my-widget') class MyWidget with Queryable { Element render() => div(onClick: (_) { final el = query('#greeting'); }, ['Click']); } https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
Users no longer need `with Queryable` or any boilerplate. The top-level query() and queryAll() functions serve as stubs for the user's @component source file. In the generated code, WebComponent.query() (the instance method) naturally shadows the top-level function. Usage — zero ceremony: @component(tag: 'my-widget') class MyWidget { Element render() => button(onClick: (_) { final el = query('#greeting'); // just works }, ['Click']); } https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
The generator now emits: import 'package:spark_framework/spark.dart' hide query, queryAll; This ensures the generated class resolves query() and queryAll() to the inherited WebComponent instance methods instead of the top-level stubs. User base files still see the stubs for IDE type-checking. https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
Both spark and spark_generator depend on spark_web, so changes in packages/spark_web/ should trigger their test jobs. https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
The openapi tests create temp projects that depend on spark_framework via path. Since spark_framework now depends on spark_web (also a local package), the temp pubspecs need a dependency_overrides entry pointing to spark_web's local path so dart pub get can resolve it. https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR extracts the Web API abstraction layer from
sparkinto a new standalonespark_webpackage. This provides a server-safe Web API that mirrors the MDN Web API with identical naming, working on both the Dart VM (server) and dart2js/dart2wasm (browser).Key Changes
New
spark_webpackage:core.dart):EventTarget,Event,MouseEvent,KeyboardEvent,InputEvent,FocusEvent,WheelEvent,DragEvent,PointerEvent,TouchEvent,AnimationEvent,TransitionEvent,CustomEvent,MutationObserver,MutationRecorddom.dart):Node,Element,HTMLElement, and 40+ specific element types (input, button, canvas, video, audio, dialog, table, etc.)window.dart):Window,Document,Console,Navigator,Storage,Location,History,Crypto,Performance,CustomElementRegistrycanvas.dart):CanvasRenderingContext2Dwith full 2D drawing APIcss.dart):CSSStyleDeclaration,CSSStyleSheet,DOMTokenListcollections.dart):NodeList,NamedNodeMap,DOMTokenListBrowser implementation (
browser/):package:webtypes with comprehensive type checking and conversionwrapNode(),wrapElement(),wrapEvent()utilities for converting native objectsServer implementation (
server/):Storagebacked byMapfor localStorage/sessionStorageCrypto.randomUUID()viadart:mathConsolebacked byprint()Updated
sparkpackage:stubs.dartwithspark_webimportsstubs.dart,adopted_styles_web.dart,adopted_styles_stub.dart,js_callback_web.dart,js_callback_stub.dart,query_stubs.dart,query_web.dartweb_component.dartto usespark_webinstead of conditional importsvdom_web.dartto importpackage:webdirectly (no longer needs stubs)extensions.dartto usespark_webspark_component.dartto pass native DOM objects to VDOM enginepubspec.yamlto depend onspark_webspark_webin change detectionImplementation Details
dart:js_interopavailability to detect platform (browser vs server)package:webobjects; server implementation provides sensible defaultswindowanddocumentavailable as top-level exports inspark_web.dart.rawproperty on all types provides access to underlying native objects on browser (null on server)https://claude.ai/code/session_01S4gf6evvE9dAHPDFt6e8pa