Skip to content

feat(util/stream): util.inherits + stream prototype scaffold (express unblocked)#984

Merged
proggeramlug merged 1 commit into
mainfrom
worktree-agent-a925319b98cf0344f
May 18, 2026
Merged

feat(util/stream): util.inherits + stream prototype scaffold (express unblocked)#984
proggeramlug merged 1 commit into
mainfrom
worktree-agent-a925319b98cf0344f

Conversation

@proggeramlug
Copy link
Copy Markdown
Contributor

Summary

  • Express compile crashed at util.inherits(SendStream, Stream) inside node_modules/send/index.js:173Stream.prototype was undefined because the V8-fallback node:stream shim returned a plain { Readable, Writable, ... } namespace, not the legacy Stream constructor Node ships.
  • Rewrite the node:stream shim around a real Stream class with its own .prototype and EventEmitter-shaped instance methods. Attach Readable/Writable/Duplex/Transform/PassThrough/pipeline/finished as static properties, then export default Stream + export const __perry_commonjs = true so require('stream') returns the class itself (matching Node).
  • Harden util.inherits(ctor, superCtor) in the node:util shim: input validation, TypeError when superCtor.prototype is undefined (matches Node's message), and Object.defineProperty(ctor, 'super_', ...) for the legacy chaining pattern.
  • Add property("stream", "prototype") to the API manifest so the Compile-time error for unimplemented Node / Web APIs #463 unimplemented-API gate accepts Stream.prototype reads.

Verification

import express from 'express' — previously crashed with

TypeError: Object prototype may only be an Object or null: undefined
    at Object.inherits (node:util:14:52)
    at send/index.js:356:6

After this PR: typeof express === 'function'.

test-files/test_util_inherits.ts (V8-routed via .js fixture, mirrors the express/send pattern):

typeof Stream: function
typeof Stream.prototype: object
typeof Stream.Readable: function
typeof Stream.Readable.prototype: object
SendStream.super_ === Stream: true
Bar instanceof Foo: true
Bar.greet(): hello
Bar.super_ === Foo: true

Test plan

  • cargo build --release -p perry-runtime -p perry-stdlib -p perry-jsruntime -p perry
  • cargo fmt --all
  • test-files/test_util_inherits.ts — all 8 assertions pass
  • Express reproducer (import express from 'express') compiles + runs + prints function
  • CI: lint / cargo-test / parity / compile-smoke / api-docs-drift / security-audit

Express compile blocked at `util.inherits(SendStream, Stream)` because
`require('stream')` returned a null-proto namespace and `Stream.prototype`
was undefined. The V8-fallback `node:stream` shim is now a real `Stream`
class with sub-classes attached as static properties + `__perry_commonjs`
so `require()` returns the class itself; `util.inherits` now defines
`super_` and validates inputs.

- crates/perry-jsruntime/src/modules.rs: rewrite `node:stream` shim
  around a legacy `Stream` class with `.prototype`, EventEmitter-shaped
  instance methods, and `Readable`/`Writable`/`Duplex`/`Transform`/
  `PassThrough`/`pipeline`/`finished` as statics. Add
  `export const __perry_commonjs = true;` so the CJS require() shim
  returns the class. Split the previously-unreachable `"stream/web"`
  match arm. Harden `util.inherits` to throw `TypeError` on missing
  prototype and set `ctor.super_`.
- crates/perry-api-manifest/src/entries.rs: register
  `property("stream", "prototype")` so the #463 unimplemented-API
  gate accepts `Stream.prototype` reads.
- test-files/test_util_inherits.ts (+ V8 fixture): exercises
  Stream.prototype, Stream.Readable.prototype, util.inherits with
  prototype-chain method resolution, and the `super_` static.

Verified: `import express from 'express'; console.log(typeof express)`
now prints `function` (was `TypeError: Object prototype may only be an
Object or null: undefined`).
@proggeramlug proggeramlug merged commit db85162 into main May 18, 2026
5 of 9 checks passed
@proggeramlug proggeramlug deleted the worktree-agent-a925319b98cf0344f branch May 18, 2026 03:22
proggeramlug added a commit that referenced this pull request May 18, 2026
PRs #966, #967, #982, #984 added entries to perry-api-manifest::API_MANIFEST
(stream EventEmitter instance methods, stream.prototype, lodash
inRange/max/maxBy/mean/meanBy/min/minBy/random/sum/sumBy/tail) without
rerunning ./scripts/regen_api_docs.sh. Drift has been blocking the
api-docs-drift gate on every subsequent PR; unblocking the next batch.

Coverage moves from 870 → 898 entries across 71 modules. Mechanical
regen, no manifest edits.
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