Skip to content

node:buffer: coerce Blob and File constructor inputs like Node #2959

Description

@andrewtdiz

Summary

The existing node:buffer Blob/File implementation covers basic string parts, binary parts, metadata, and object URLs, but the constructors do not apply Node/WHATWG coercion rules for non-binary parts or File metadata. Direct new Blob(...) / new File(...) behavior diverges even though the surface exists.

Node Behavior

Local Node v25.9.0 probe:

(async () => {
  const { Blob, File } = require("node:buffer");
  const parts = [123, true, false, null, undefined, { toString(){ return "obj"; } }, ["a", "b"]];
  const b = new Blob(parts);
  console.log("blob text", JSON.stringify(await b.text()));
  console.log("blob size", b.size);
  const f = new File(parts, 123, { type: "TEXT/PLAIN;Charset=UTF-8", lastModified: "42.9" });
  console.log("file name", JSON.stringify(f.name));
  console.log("file type", JSON.stringify(f.type));
  console.log("file lm", f.lastModified);
  console.log("file text", JSON.stringify(await f.text()));
})();

Observed Node behavior:

  • new Blob(parts).text() resolves to "123truefalsenullundefinedobja,b" and size is 31.
  • Non-Blob/non-BufferSource parts are stringified, including arrays via normal array ToString (["a", "b"] becomes "a,b"), not recursively flattened as raw parts.
  • new File(parts, 123, ...) coerces the name to "123".
  • type: "TEXT/PLAIN;Charset=UTF-8" is normalized to "text/plain;charset=utf-8".
  • lastModified: "42.9" is coerced to numeric 42.9.

Perry Behavior

Current source evidence:

  • crates/perry-codegen/src/lower_call/builtin.rs lowers new Blob(parts, opts) to js_blob_new(parts, type) and new File(parts, name, opts) to js_file_new(parts, name, type, lastModified).
  • crates/perry-stdlib/src/fetch_blob.rs::append_blob_part_bytes() only handles strings, Buffer/Uint8Array, Blob handles, and arrays. The comment says anything else is silently dropped, and the array path recurses into elements, so ["a", "b"] becomes "ab" rather than Node's "a,b" stringification.
  • js_file_new() only reads name and type when the inputs are heap string tags; non-string names become "" instead of ToString(name).
  • js_blob_new() / js_file_new() store type as provided rather than applying Blob type normalization.
  • js_file_new() treats lastModified.is_nan() as “use Date.now()”. NaN-boxed non-number values such as a string option take that path instead of ToNumber(lastModified).

Suggested PR Cut

Batch this with related issues in:
node:buffer: Blob and File constructor coercion parity cut

Good batch candidates:

  • no other open node:buffer Blob/File coercion leaf is currently known; keep this as a focused PR unless another constructor-coercion issue appears

Do not batch with:

Acceptance

  • parity/regression test proves non-binary Blob parts use Node-compatible ToString / USVString behavior, including arrays becoming comma-joined strings
  • parity/regression test proves File name coercion matches Node for non-string names
  • Blob/File type normalization matches Node for valid mixed-case MIME strings and invalid characters
  • lastModified uses Node-compatible numeric coercion and only defaults to current time when the option is absent
  • existing Blob/File binary parts, metadata, text/arrayBuffer behavior, and object URL fixtures continue passing
  • related known-failure/docs/manifest entries updated if touched

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