Skip to content

run wavedash dev with local sdk - #56

Open
The64thRealm wants to merge 2 commits into
mainfrom
dev-local-sdk
Open

run wavedash dev with local sdk#56
The64thRealm wants to merge 2 commits into
mainfrom
dev-local-sdk

Conversation

@The64thRealm

Copy link
Copy Markdown
Contributor

For me to run wavedash dev with my local js-sdk without needing to bump the pin / bump the sdk-js version / do weird shenanigans with my files

The64thRealm and others added 2 commits August 14, 2026 12:57
The dev shell hardcodes cdn.jsdelivr.net/npm/@wvdsh/sdk-js@<pinned version>,
so trying an SDK change under `wavedash dev` meant publishing it first. This
points the injected script tag at a local bundle served from the dev server's
own origin, which COEP accepts without the CORP header a cross-origin build
would need.

Takes the bundle or any directory holding it, so an sdk-js checkout and its
dist/ both work, and resolves at startup so a wrong path fails immediately
rather than as a broken script tag once the game is already loading. The file
is read per request, so rebuilding sdk-js only needs a page reload.

With no flag the pinned CDN url is built exactly as before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repo is public, so a test named for a hidden flag documents the flag it
is hiding. What it uniquely covered — which commands carry --upload-source,
and that godot-plugin is its only value — is not worth advertising that in
the test names.

`upload_source_parses_the_plugin_and_defaults_to_the_cli` still covers
parsing the value and defaulting to the CLI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Aug 14, 2026

Copy link
Copy Markdown

Claude finished @The64thRealm's task in 4m 17s —— View job


Code review: --sdk-js local SDK support

  • Gather context (PR diff vs origin/main, src/dev/{mod,server}.rs, src/main.rs)
  • Review changed code for correctness, reuse, and simplification
  • Verify findings and post inline comments

Summary

Reviewed all 3 files (+88/−53). The core mechanism is sound:

  • resolve_sdk_js's dir/file candidate order and use of transpose() are correct.
  • Switching to a same-origin /__wavedash/sdk.js genuinely satisfies the COEP require-corp policy without needing CORP headers, and the leftover crossorigin="anonymous" on a same-origin script is harmless.
  • The per-request fs::read matches the existing blocking-read pattern in serve_file.
  • No other call site of the CDN URL was missed — only src/dev/server.rs references it, and ServeConfig's single construction site is already updated.

Caveat: I could not run cargo test (the sandbox blocked the command), so compilation was verified by inspection only.

Findings (3, all low severity — posted as inline comments)

  1. src/dev/server.rs:39 — Serving the bundle at /__wavedash/sdk.js makes its relative //# sourceMappingURL=inject.global.js.map resolve to /__wavedash/inject.global.js.map, which 404s via handle_static. The local sdk-js build stays unmapped in devtools, defeating part of the flag's purpose. Fix this →
  2. src/dev/server.rs:558 — A read failure returns a text/plain 500 the page can't surface: window.Wavedash is undefined, so dev.js's initGate throws a TypeError on window.Wavedash.gameLoaded inside its DOMContentLoaded listener and the boot overlay never mounts. Reloading mid-rebuild (exactly the workflow the doc comment invites) gives a black page with the reason only in the network tab. Fix this →
  3. src/main.rs:942 — The PR deletes upload_source_is_hidden_and_only_offers_the_godot_plugin, which is unrelated to --sdk-js. Nothing now asserts that --upload-source stays hide-set or that wavedash dev restricts it to godot-plugin (the surviving test only covers build push). Renaming rather than deleting keeps the guard. Fix this →

No blocking issues — the feature works as intended for its stated purpose.
• Branch: dev-local-sdk

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

Comment thread src/dev/server.rs
}

/// Same-origin, which COEP allows without the CORP a cross-origin build needs.
const LOCAL_SDK_URL: &str = "/__wavedash/sdk.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Serving the bundle under a renamed URL breaks its sourcemap, which is exactly what you want when debugging a local sdk-js build. A dev dist/inject.global.js ends with //# sourceMappingURL=inject.global.js.map; the browser resolves that relative to /__wavedash/sdk.js, requests /__wavedash/inject.global.js.map, which falls through to handle_static and 404s — so SDK frames stay minified/unmapped in devtools.

Cheapest fix: serve any sibling *.map next to cfg.sdk_js from /__wavedash/<name> (or route the bundle at /__wavedash/inject.global.js and serve the map alongside it).

Comment thread src/dev/server.rs
};
match std::fs::read(path) {
Ok(bytes) => respond(StatusCode::OK, "text/javascript; charset=utf-8", None, bytes),
Err(e) => respond(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The read-failure path fails silently from the user's point of view. The doc comment invites the workflow "rebuild sdk-js, reload the page", so reloading mid-rebuild (file momentarily missing, or read back truncated) is a realistic race. In either case window.Wavedash never gets defined, and dev.js's initGate starts with if (window.Wavedash.gameLoaded) — it throws TypeError inside the DOMContentLoaded listener, so the boot overlay never mounts and no message is shown. The developer gets a black page and has to find the 500 in the network tab.

Consider responding with JS that surfaces the reason through the existing gate, e.g. body window.__wavedashBootError = "could not read <path>: <err>"; with Content-Type: text/javascript (status 200 or 500 — the script body is what the gate can act on).

Comment thread src/main.rs
);
}

#[test]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR deletes upload_source_is_hidden_and_only_offers_the_godot_plugin, which is unrelated to --sdk-js and leaves a coverage hole: nothing now asserts that --upload-source is hide-set, nor that wavedash dev's copy restricts values to godot-plugin (the surviving upload_source_parses_the_plugin_and_defaults_to_the_cli only exercises build push). Concretely, dropping hide = true on either arg, or adding a new UploadSource variant, now passes CI silently.

If the concern is the test name advertising a hidden flag, renaming it (e.g. every_build_creating_command_agrees_on_its_source_arg) keeps the guard without the signal.

@The64thRealm
The64thRealm deleted the dev-local-sdk branch August 14, 2026 20:36
@The64thRealm
The64thRealm restored the dev-local-sdk branch August 14, 2026 20:39
@The64thRealm The64thRealm reopened this Aug 14, 2026
@claude

claude Bot commented Aug 14, 2026

Copy link
Copy Markdown

Claude finished @The64thRealm's task in 4m 18s —— View job


Code review: --sdk-js local SDK support

  • Gather context (diff vs origin/main, src/dev/{mod,server}.rs, dev.js, shell.html, src/main.rs, Cargo.toml)
  • Review changed code for correctness, reuse, and simplification
  • Verify findings and post inline comments

Summary

Re-reviewed all 3 files (+88/−53) after the reopen. The mechanism holds up:

  • resolve_sdk_js's candidate order handles both forms a developer would actually type (--sdk-js ~/code/sdk-js and --sdk-js ~/code/sdk-js/dist) without a dist/dist mishap, and .map(...).transpose()? is the right shape for fail-at-startup.
  • Serving from same-origin /__wavedash/sdk.js is the correct answer to COEP require-corp — no CORP header needed, and the leftover crossorigin="anonymous" is harmless on a same-origin script (credentials are omitted, which is fine since handle_sdk_js doesn't read cookies).
  • respond already sets cache-control: no-store on every response (src/dev/server.rs:602), so the "rebuild sdk-js, reload" workflow genuinely picks up the new bytes — the per-request fs::read isn't defeated by browser caching.
  • Per-request blocking fs::read matches the existing serve_file pattern.
  • cdn_inject_url is the only CDN reference and ServeConfig's single construction site is updated — no missed call sites.

Caveat: cargo test and cargo build are blocked by the sandbox in this run, so compilation and the two new/updated tests were verified by inspection only (imports check out: Path is in scope in server.rs:10, PathBuf in main.rs:33).

Findings

New this pass — posted inline:

  1. src/dev/mod.rs:60 (test coverage)resolve_sdk_js is the only new branching logic and has no test, while the thinner inject_url switch got one. tempfile is already a dev-dependency with the tempfile::tempdir() pattern at src/config.rs:1026, so a four-assert test is cheap. The dist case matters most: reordering the candidate vec! would silently start resolving dist/dist/inject.global.js. Fix this →
  2. src/dev/server.rs:41 (simplification)inject_url never reads the path, only whether it's Some; Option<&Path> is a boolean in a path's clothing, threaded through shell and inject_sdk just to reach the match. Storing the resolved URL as a ServeConfig field collapses three signature changes and four call-site edits, and moves the decision next to resolve_sdk_js. Fix this →

Still open from the previous pass (existing inline threads, not re-posted):

  1. src/dev/server.rs:39 — the renamed URL breaks a relative sourceMappingURL: /__wavedash/inject.global.js.map falls through to handle_static and 404s, so SDK frames stay unmapped. Unverified this run — I have no network access to check whether the sdk-js build emits a sourceMappingURL comment; if it doesn't, this is a non-issue. Serving any sibling *.map would make it moot either way.
  2. src/dev/server.rs:558confirmed: on read failure the text/plain 500 is invisible to the developer. window.Wavedash stays undefined, and dev.js:30 opens with if (window.Wavedash.gameLoaded), so initGate throws a TypeError inside its DOMContentLoaded listener and the overlay never mounts — black page, reason only in the network tab. Worth noting this fragility predates the PR (a CDN load failure does the same), but --sdk-js turns a rare case into a routine one: the doc comment on line 551 explicitly invites reloading during a rebuild.
  3. src/main.rs:942 — the deleted upload_source_is_hidden_and_only_offers_the_godot_plugin test. fa55fac's message gives a deliberate rationale (a public repo shouldn't name a hidden flag in a test name), which is a fair call to make. Two things weaken it though: the surviving test's body already contains --upload-source and its godot-plugin value, so the flag is equally discoverable either way; and wavedash dev's copy of the arg now has zero coverage — dropping hide = true from it, or adding a UploadSource variant, passes CI silently. Your call, but a rename would have kept the guard at the same disclosure cost.

No blocking issues — the feature does what it says.

• Branch: dev-local-sdk

Comment thread src/dev/mod.rs

const DEFAULT_CONFIG: &str = "./wavedash.toml";

fn resolve_sdk_js(path: PathBuf) -> Result<PathBuf> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

resolve_sdk_js is the one piece of new logic here with real branching — dir → dist/inject.global.js<dir>/inject.global.js → direct-file passthrough → error — and it's the piece a user hits first and most often (--sdk-js ~/code/sdk-js vs --sdk-js ~/code/sdk-js/dist vs a direct path). It has no test, while the much thinner inject_url switch got one (a_local_bundle_is_served_from_this_origin).

tempfile is already a dev-dependency and src/config.rs:1026 has the tempfile::tempdir() pattern to copy, so this is cheap:

#[test]
fn a_local_sdk_js_resolves_from_a_repo_root_a_dist_dir_or_a_direct_file() {
    let dir = tempfile::tempdir().expect("temp dir");
    let root = dir.path();
    let dist = root.join("dist");
    std::fs::create_dir(&dist).unwrap();
    let bundle = dist.join("inject.global.js");
    std::fs::write(&bundle, "//").unwrap();

    // The repo root a developer actually types.
    assert_eq!(resolve_sdk_js(root.to_path_buf()).unwrap(), bundle);
    // The dist dir — must not become dist/dist/inject.global.js.
    assert_eq!(resolve_sdk_js(dist.clone()).unwrap(), bundle);
    // A direct path is taken as-is.
    assert_eq!(resolve_sdk_js(bundle.clone()).unwrap(), bundle);
    // Nothing to serve is an error, not a 500 at request time.
    assert!(resolve_sdk_js(root.join("nope")).is_err());
}

The dist case is the one worth locking down: the candidate order is what keeps it from resolving dist/dist/inject.global.js, and reordering the vec! would silently break it.

Comment thread src/dev/server.rs
/// Same-origin, which COEP allows without the CORP a cross-origin build needs.
const LOCAL_SDK_URL: &str = "/__wavedash/sdk.js";

fn inject_url(sdk_js: Option<&Path>) -> String {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

inject_url never reads the path — only whether it's Some. That makes Option<&Path> a boolean in a path's clothing, and it's threaded through shell and inject_sdk purely to reach this match, which is what forced three signature changes and four call-site edits in this diff.

Since ServeConfig is built once and the URL is a pure function of sdk_js, resolving it at construction collapses all of that:

// in ServeConfig
/// The SDK bundle URL injected into every page — local when `sdk_js` is set.
pub inject_url: String,

Then shell(&cfg.inject_url, ...) / inject_sdk(&cfg.inject_url, ...) take a &str, inject_url(sdk_js) disappears, and the "is there a local build" decision happens once next to resolve_sdk_js instead of on every request. It also removes the mild misdirection for the next reader, who currently has to follow Option<&Path> two frames down to learn the path never lands in the URL.

Not worth blocking on — but if you'd rather keep the current shape, fn inject_url(local: bool) at least says what the parameter is doing.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant