Generated by
scripts/gen_parity_gaps.pyfromdocs/runtime-parity.md(the API inventory) reconciled against Perry's coverage sources. Do not edit by hand — re-run the script to refresh.
This is a structured gap analysis comparing the public Node.js API surface
against the APIs Perry can dispatch. Coverage is derived from four sources:
the unimplemented-API gate manifest (crates/perry-api-manifest/src/entries.rs,
method/property rows), compound Expr::* HIR variants
(crates/perry-hir/src/ir/), js_* FFI exports across perry-runtime /
perry-stdlib / perry-ext-*, and module-gated method-dispatch literals.
Behavioral status. This list counts individual API surface gaps, not behavioral pass rate. Measured against Node's own test suite (
scripts/node_suite_run.pyvstest-parity/node_suite_baseline.json), Perry's runtime passes ~97%; overall Node.js/TypeScript compatibility is around 95%. Heavily-used modules (fs,http/https/http2,net/tls,crypto,stream,events,child_process,worker_threads,process,zlib) are real, not stubs.
Across 49 node:* modules: 2222 covered / 296 gap of 2518 catalogued APIs.
Web / global APIs and Bun-only APIs are tracked separately in
runtime-parity.md; their coverage is curated, not recomputed here.
| Module | Covered | Gap | Total |
|---|---|---|---|
node:perf_hooks |
17 | 39 | 56 |
node:http2 |
68 | 34 | 102 |
node:test |
59 | 34 | 93 |
node:util |
84 | 21 | 105 |
node:tls |
35 | 18 | 53 |
node:v8 |
41 | 17 | 58 |
node:process |
106 | 12 | 118 |
node:stream/web |
58 | 10 | 68 |
node:inspector |
10 | 9 | 19 |
node:module |
41 | 9 | 50 |
node:timers |
8 | 9 | 17 |
node:url |
40 | 9 | 49 |
node:fs |
174 | 8 | 182 |
node:readline/promises |
0 | 7 | 7 |
node:assert |
21 | 6 | 27 |
node:buffer |
102 | 6 | 108 |
node:cluster |
29 | 6 | 35 |
node:events |
35 | 6 | 41 |
node:trace_events |
0 | 6 | 6 |
node:crypto |
133 | 5 | 138 |
node:tty |
15 | 4 | 19 |
node:https |
21 | 3 | 24 |
node:child_process |
35 | 2 | 37 |
node:readline |
27 | 2 | 29 |
node:sqlite |
50 | 2 | 52 |
node:timers/promises |
3 | 2 | 5 |
node:worker_threads |
62 | 2 | 64 |
node:async_hooks |
28 | 1 | 29 |
node:console |
22 | 1 | 23 |
node:dgram |
27 | 1 | 28 |
node:fs/promises |
60 | 1 | 61 |
node:http |
140 | 1 | 141 |
node:net |
77 | 1 | 78 |
node:stream |
80 | 1 | 81 |
node:zlib |
90 | 1 | 91 |
node:diagnostics_channel |
30 | 0 | 30 |
node:dns |
53 | 0 | 53 |
node:dns/promises |
21 | 0 | 21 |
node:domain |
10 | 0 | 10 |
node:os |
209 | 0 | 209 |
node:path |
16 | 0 | 16 |
node:punycode |
8 | 0 | 8 |
node:querystring |
7 | 0 | 7 |
node:repl |
17 | 0 | 17 |
node:stream/consumers |
6 | 0 | 6 |
node:stream/promises |
3 | 0 | 3 |
node:string_decoder |
6 | 0 | 6 |
node:vm |
32 | 0 | 32 |
node:wasi |
6 | 0 | 6 |
| Total | 2222 | 296 | 2518 |
Only modules with at least one remaining gap are listed, in descending gap-size order. Modules omitted here have zero catalogued gaps.
Covered: 17 · Gap: 39
performance.clearMarks([name])performance.clearMeasures([name])performance.clearResourceTimings([name])performance.getEntries()performance.getEntriesByName(name[, type])performance.getEntriesByType(type)performance.eventLoopUtilization([util1[, util2]])performance.setResourceTimingBufferSize(maxSize)performance.markResourceTiming(...)performance.toJSON()performance.nodeTimingperformance.timeOriginentry.entryTypeentry.flagsentry.kindnodeStartv8StartenvironmentbootstrapCompleteloopStartloopExitidleTimeuvMetricsInfonew PerformanceObserver(callback)PerformanceObserver.supportedEntryTypeslist.getEntries()list.getEntriesByName(name[, type])list.getEntriesByType(type)histogram.meanhistogram.stddevhistogram.percentileBigInt(percentile)histogram.reset()histogram.enable()histogram.disable()histogram[Symbol.dispose]()histogram.record(val)histogram.recordDelta()histogram.add(other)perf_hooks.eventLoopUtilization([util1[, util2]])
Covered: 68 · Gap: 34
session.originSetserverSession.altsvc(alt, originOrStream)stream.idstream.sentInfoHeadersstream.sentTrailersserverStream.pushAllowedhttp2Server[Symbol.asyncDispose]()http2Server.timeouthttp2Server.updateSettings([settings])request.authorityrequest.completerequest.httpVersionrequest.rawHeadersrequest.rawTrailersrequest.schemerequest.trailersresponse.addTrailers(headers)response.appendHeader(name, value)response.createPushResponse(headers, callback)response.finishedresponse.getHeader(name)response.getHeaderNames()response.hasHeader(name)response.removeHeader(name)response.reqresponse.sendDateresponse.setHeader(name, value)response.statusCoderesponse.statusMessageresponse.writableEndedresponse.write(chunk[, encoding][, callback])response.writeContinue()response.writeEarlyHints(hints)response.writeHead(statusCode[, statusMessage][, headers])
Covered: 59 · Gap: 34
t.runOnly(shouldRunOnlyTests)t.waitFor(condition[, options])t.fullNamet.filePatht.passedt.attemptt.workerIds.fullNames.filePaths.passeds.attemptmock.module(specifier[, options])mock.accessesmock.accessCount()mock.resetAccesses()tapdotjunitlcov'test:start''test:plan''test:pass''test:fail''test:complete''test:diagnostic''test:coverage''test:enqueue''test:dequeue''test:watch:drained''test:watch:restarted''test:stderr''test:stdout''test:summary''test:interrupted'
Covered: 84 · Gap: 21
MIMEType.prototype.paramsutil.inspect.customutil.inspect.defaultOptionsutil.inspect.stylesutil.inspect.colorsutil.promisify.customutil.isArray(object)util.isBoolean(object)util.isBuffer(object)util.isError(object)util.isFunction(object)util.isNull(object)util.isNullOrUndefined(object)util.isNumber(object)util.isObject(object)util.isPrimitive(object)util.isString(object)util.isSymbol(object)util.isUndefined(object)util.print(...args)util.puts(...args)
Covered: 35 · Gap: 18
tls.createSecurePair([context][, isServer][, requestCert][, rejectUnauthorized][, options])server.addContext(hostname, context)tlsSocket.localAddresstlsSocket.localPorttlsSocket.remoteAddresstlsSocket.remoteFamilytlsSocket.remotePorttlsSocket.disableRenegotiation()tlsSocket.enableTrace()tlsSocket.getEphemeralKeyInfo()tlsSocket.getFinished()tlsSocket.getPeerFinished()tlsSocket.getPeerX509Certificate()tlsSocket.getSharedSigalgs()tlsSocket.getTLSTicket()tlsSocket.getX509Certificate()tlsSocket.renegotiate(options, callback)tlsSocket.setKeyCert(context)
Covered: 41 · Gap: 17
v8.startHeapProfile([options])new Serializer()transferArrayBuffer(id, arrayBuffer)_writeHostObject(object)_getDataCloneError(message)_getSharedArrayBufferId(sab)_setTreatArrayBufferViewsAsHostObjects(flag)new Deserializer(buffer)transferArrayBuffer(id, arrayBuffer)getWireFormatVersion()_readHostObject()v8.DefaultSerializerv8.DefaultDeserializernew GCProfiler()[Symbol.dispose]()[Symbol.dispose]()[Symbol.asyncDispose]()
Covered: 106 · Gap: 12
process.mainModuleprocess.features.uvprocess.noDeprecationprocess.throwDeprecationprocess.traceDeprecationprocess.traceProcessWarnings'uncaughtExceptionMonitor''unhandledRejection''rejectionHandled''workerMessage''SIGWINCH''SIGBREAK'
Covered: 58 · Gap: 10
new ReadableStream([underlyingSource[, strategy]])new ReadableStreamDefaultReader(stream)new ReadableStreamBYOBReader(stream)new WritableStream([underlyingSink[, strategy]])new WritableStreamDefaultWriter(stream)new TransformStream([transformer[, writableStrategy[, readableStrategy]]])new ByteLengthQueuingStrategy(init)new CountQueuingStrategy(init)new TextEncoderStream()new TextDecoderStream([encoding[, options]])
Covered: 10 · Gap: 9
inspector.Network.requestWillBeSent(params)inspector.Network.responseReceived(params)inspector.Network.dataReceived(params)inspector.Network.dataSent(params)inspector.Network.loadingFinished(params)inspector.Network.loadingFailed(params)inspector.Network.webSocketCreated(params)inspector.Network.webSocketHandshakeResponseReceived(params)inspector.Network.webSocketClosed(params)
Covered: 41 · Gap: 9
Module.constants.compileCacheStatusmodule.childrenmodule.idmodule.loadedmodule.parentmodule.isPreloadingsourceMap.payloadsourceMap.findEntry(lineOffset, columnOffset)sourceMap.findOrigin(lineNumber, columnNumber)
Covered: 8 · Gap: 9
immediate.unref()immediate.hasRef()immediate[Symbol.dispose]()timeout.unref()timeout.hasRef()timeout.refresh()timeout.close()timeout[Symbol.toPrimitive]()timeout[Symbol.dispose]()
Covered: 40 · Gap: 9
url.toJSON()new URLSearchParams()new URLSearchParams(string)new URLSearchParams(obj)new URLSearchParams(iterable)params.entries()params.keys()params.values()params[Symbol.iterator]()
Covered: 174 · Gap: 8
fs.realpath.native(path[, options], callback)fs.realpathSync.native(path[, options])stats.devstats.inostats.nlinkstats.rdevstats.sizestats.blksize
Covered: 0 · Gap: 7
readlinePromises.createInterface(options)rl.clearLine(dir)rl.clearScreenDown()rl.cursorTo(x[, y])rl.moveCursor(dx, dy)rl.commit()rl.rollback()
Covered: 21 · Gap: 6
assert.CallTrackertracker.calls(fn[, exact])tracker.getCalls(fn)tracker.report()tracker.reset([fn])tracker.verify()
Covered: 102 · Gap: 6
Buffer.allocUnsafeSlow(size)Buffer.poolSizebuf[index]buf.parentnew buffer.Blob([sources[, options]])new buffer.File(sources, fileName[, options])
Covered: 29 · Gap: 6
'message''setup'worker.idworker.send(message[, sendHandle[, options]][, callback])'message''error'
Covered: 35 · Gap: 6
EventEmitter.prototype[Symbol.for('nodejs.rejection')]()Event.prototype.composedPath()Event.prototype.initEvent(type, bubbles, cancelable)Event.prototype.preventDefault()Event.prototype.stopImmediatePropagation()Event.prototype.stopPropagation()
Covered: 0 · Gap: 6
trace_events.createTracing(options)trace_events.getEnabledCategories()tracing.categoriestracing.enabledtracing.enable()tracing.disable()
Covered: 133 · Gap: 5
crypto.setEngine(engine[, flags])crypto.fipsKeyObject.from(key)new X509Certificate(buffer)x509.ca
Covered: 15 · Gap: 4
readStream.isTTYreadStream.fdwriteStream.isTTYwriteStream.fd
Covered: 21 · Gap: 3
agent.keepSocketAlive(socket)agent.reuseSocket(socket, request)server[Symbol.asyncDispose]()
Covered: 35 · Gap: 2
child_process.ChildProcesschild_process.Stream
Covered: 27 · Gap: 2
rl[Symbol.dispose]()rl.cursor
Covered: 50 · Gap: 2
db.serialize([dbName])db.deserialize(buffer[, options])
Covered: 3 · Gap: 2
scheduler.wait(delay[, options])scheduler.yield()
Covered: 62 · Gap: 2
new Worker(filename[, options])worker[Symbol.asyncDispose]()
Covered: 28 · Gap: 1
new AsyncLocalStorage()
Covered: 22 · Gap: 1
new Console(stdout[, stderr][, ignoreErrors])
Covered: 27 · Gap: 1
socket[Symbol.asyncDispose]()
Covered: 60 · Gap: 1
filehandle.fd
Covered: 140 · Gap: 1
server[Symbol.asyncDispose]()
Covered: 77 · Gap: 1
server[Symbol.asyncDispose]()
Covered: 80 · Gap: 1
writable.writableAborted
Covered: 90 · Gap: 1
zlib.bytesRead
- Coverage = dispatchable, not byte-for-byte. A manifest/FFI match means Perry can dispatch the call, not that every option/overload matches Node.
- Module-gated dispatch. Method-name string literals only count for
modules that have a real implementation (a manifest entry or a
js_<module>_*FFI export), so stub files naming methods in error strings don't read as covered. - Manual coverage overrides. A few APIs are implemented in generic,
non-module-named dispatchers (e.g.
KeyObjectproperty access inperry-runtime/src/object/field_get_set.rs). These are credited via an auditedMANUAL_COVERAGEtable in the script. - Constants & events are credited as a block when the module exposes
constants/codesor anon/emitsurface, rather than per-leaf. class Xdeclaration rows are excluded from counts.