Skip to content

node:buffer: validate Blob object URL creation like Node #2982

Description

@andrewtdiz

Summary

Node's Blob object URL surface rejects non-Blob inputs to URL.createObjectURL() and returns blob:nodedata:<uuid> identifiers for valid Blob values. Perry's object URL helper currently maps invalid handles to an empty string and generates monotonic 32-hex identifiers without UUID separators.

Node.js behavior

Observed with Node v25.9.0:

const { Blob, resolveObjectURL } = require("node:buffer");

URL.createObjectURL("x");
// TypeError ERR_INVALID_ARG_TYPE: The "obj" argument must be an instance of Blob

URL.createObjectURL({});
// TypeError ERR_INVALID_ARG_TYPE: The "obj" argument must be an instance of Blob

const b = new Blob(["hi"]);
const id = URL.createObjectURL(b);
console.log(id);
// blob:nodedata:<uuid>, e.g. blob:nodedata:50e4632e-6844-422a-b2d5-68227d49b179

console.log(resolveObjectURL(id)?.size); // 2
URL.revokeObjectURL(id);
console.log(resolveObjectURL(id));       // undefined

URL.revokeObjectURL(nonString) and resolveObjectURL(nonStringOrUnknown) are lenient and return undefined, but createObjectURL validates that the object is a Blob.

Current Perry behavior

crates/perry-stdlib/src/fetch_blob.rs implements the object URL registry:

  • js_url_create_object_url(blob_handle) calls handle_id(blob_handle) and returns an empty string when the id is 0; it does not throw for strings, objects, or other non-Blob values.
  • Valid ids are formatted with format!("blob:nodedata:{:032x}", n), so generated ids are monotonic fixed-width hex strings rather than Node's UUID-shaped blob:nodedata:<uuid> strings.
  • resolveObjectURL/revokeObjectURL leniency broadly matches Node for bad or unknown URLs, so the validation gap is specific to creation.

Suggested test surface

  • URL.createObjectURL("x") and URL.createObjectURL({}) throw TypeError with code ERR_INVALID_ARG_TYPE.
  • URL.createObjectURL(new Blob(["hi"])) returns a string matching ^blob:nodedata:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$.
  • resolveObjectURL(id) returns a Blob with the original size/type, and returns undefined after revoke.
  • revokeObjectURL(123) and resolveObjectURL(123) remain lenient.

Scope / non-goals

This is scoped to Blob object URL validation and identifier shape. It does not require changing Blob/File constructor coercion or object URL lifetime semantics beyond existing revoke behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions