mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-24 06:14:01 +08:00
Accept readline's dispose, instanceof, dynamic streams and non-string prompts
rl[Symbol.dispose]() closes the interface, as Node's dispose method calls close(). rl instanceof readline.Interface and readlinePromises.Interface answer which module created the interface: rl.create's mode marks readline/promises interfaces, and the fallback declarations declare both Interface classes as values. In JavaScript, createInterface's input and output can be checked-dynamic values (a function's result, an object's member, an element that may be undefined): rl.inputFromDyn and rl.outputFromDyn unbox the stream they hold, an output of null or undefined means no output, and other values are refused. In JavaScript, question() and setPrompt() accept a value that is not a string. When such a prompt is written, an interface that is not a terminal throws ERR_INVALID_ARG_TYPE as Node's kWriteToOutput does, and the readline/promises question rejects with it; the question stays pending and the previous prompt returns when it is answered. A terminal interface writes String(value) of a primitive. Corpus 5876-5878 match Node v24.22.0.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -58997,7 +58997,9 @@
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59029,7 +59031,9 @@
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59257,7 +59261,9 @@
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59318,7 +59324,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59350,7 +59358,9 @@
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59433,7 +59443,8 @@
|
||||
"confidence": "high",
|
||||
"source": "compiler-dedicated:readline.createInterface",
|
||||
"tests": [
|
||||
"tests/corpus/1475-readline-closed-stdin.ts"
|
||||
"tests/corpus/1475-readline-closed-stdin.ts",
|
||||
"tests/corpus/5878-readline-dynamic-streams.mjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59572,7 +59583,8 @@
|
||||
"confidence": "high",
|
||||
"source": "compiler-dedicated:rl.close",
|
||||
"tests": [
|
||||
"tests/corpus/1475-readline-closed-stdin.ts"
|
||||
"tests/corpus/1475-readline-closed-stdin.ts",
|
||||
"tests/corpus/5878-readline-dynamic-streams.mjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59602,7 +59614,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59632,7 +59646,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59662,7 +59678,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59692,7 +59710,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59722,7 +59742,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59752,7 +59774,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59782,7 +59806,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59812,7 +59838,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59932,7 +59960,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59962,7 +59992,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59992,7 +60024,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -60050,7 +60084,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -60080,7 +60116,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82445,7 +82445,10 @@
|
||||
"tests/corpus/5791-readline-keypress-interfaces.cjs",
|
||||
"tests/corpus/5792-readline-keypress-listeners.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs",
|
||||
"tests/corpus/5878-readline-dynamic-streams.mjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "chapter"
|
||||
@@ -82499,7 +82502,9 @@
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -82530,7 +82535,9 @@
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -82750,7 +82757,9 @@
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -82832,7 +82841,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -82863,7 +82874,9 @@
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5872-readline-positional-create-interface.mjs",
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs"
|
||||
"tests/corpus/5873-readline-promises-namespace.cjs",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -82943,7 +82956,8 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-dedicated:readline.createInterface",
|
||||
"tests": [
|
||||
"tests/corpus/1475-readline-closed-stdin.ts"
|
||||
"tests/corpus/1475-readline-closed-stdin.ts",
|
||||
"tests/corpus/5878-readline-dynamic-streams.mjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83099,7 +83113,8 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-dedicated:rl.close",
|
||||
"tests": [
|
||||
"tests/corpus/1475-readline-closed-stdin.ts"
|
||||
"tests/corpus/1475-readline-closed-stdin.ts",
|
||||
"tests/corpus/5878-readline-dynamic-streams.mjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83128,7 +83143,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83157,7 +83174,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83186,7 +83205,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83215,7 +83236,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83244,7 +83267,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83273,7 +83298,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83302,7 +83329,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83331,7 +83360,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83447,7 +83478,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83476,7 +83509,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83505,7 +83540,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83561,7 +83598,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83590,7 +83629,9 @@
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
"tests/corpus/5299-readline-stdin.ts",
|
||||
"tests/corpus/5876-readline-dispose-instanceof.ts",
|
||||
"tests/corpus/5877-readline-question-query.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
|
||||
@@ -189,13 +189,13 @@
|
||||
{"chapter": "events", "symbols": ["CustomEvent", "detail"], "status": "partial", "evidence": ["tests/corpus/5130-event-target-dispatch.js", "tests/corpus/5132-event-target-typed.ts"]},
|
||||
{"chapter": "events", "symbols": ["events.errorMonitor"], "status": "partial", "evidence": ["tests/corpus/5134-events-error-monitor.ts", "tests/corpus/5136-events-error-monitor-string-name.ts", "tests/corpus/5137-events-error-monitor-computed-name.js", "tests/corpus/5138-events-error-monitor-shared-listener.ts"]},
|
||||
{"chapter": "events", "symbols": ["events.addAbortListener"], "status": "partial", "evidence": ["tests/corpus/5133-abort-signal-event-target.ts"]},
|
||||
{"chapter": "readline", "symbols": ["readlinePromises.Interface", "readlinePromises.createInterface", "rl.question"], "status": "partial", "evidence": ["tests/corpus/5295-readline-promises-question.ts", "tests/corpus/5296-readline-lines.ts", "tests/corpus/5298-readline-abort.cjs", "tests/corpus/5299-readline-stdin.ts", "tests/corpus/5872-readline-positional-create-interface.mjs", "tests/corpus/5873-readline-promises-namespace.cjs"]},
|
||||
{"chapter": "readline", "symbols": ["readlinePromises.Interface", "readlinePromises.createInterface", "rl.question"], "status": "partial", "evidence": ["tests/corpus/5295-readline-promises-question.ts", "tests/corpus/5296-readline-lines.ts", "tests/corpus/5298-readline-abort.cjs", "tests/corpus/5299-readline-stdin.ts", "tests/corpus/5872-readline-positional-create-interface.mjs", "tests/corpus/5873-readline-promises-namespace.cjs", "tests/corpus/5876-readline-dispose-instanceof.ts", "tests/corpus/5877-readline-question-query.cjs"]},
|
||||
{"chapter": "readline", "symbols": ["'history'", "rl.line"], "status": "partial", "evidence": ["tests/corpus/5780-readline-history-options.ts", "tests/corpus/5781-readline-history-options.cjs"]},
|
||||
{"chapter": "readline", "symbols": ["'SIGINT'", "'SIGTSTP'", "'SIGCONT'", "line", "cursor", "completer"], "status": "partial", "evidence": ["tests/corpus/5782-readline-terminal-editing.ts", "tests/corpus/5783-readline-terminal-completion.cjs", "tests/corpus/5784-readline-terminal-promises.ts", "tests/corpus/5785-readline-terminal-keys.ts", "tests/corpus/5786-readline-terminal-history.cjs"]},
|
||||
{"chapter": "readline", "symbols": ["readline.cursorTo", "readline.moveCursor", "readline.clearLine", "readline.clearScreenDown", "rl.getCursorPos"], "status": "partial", "evidence": ["tests/corpus/5787-readline-cursor-helpers.cjs", "tests/corpus/5789-readline-cursor-pos.cjs"]},
|
||||
{"chapter": "readline", "symbols": ["readlinePromises.Readline", "rl.clearLine", "rl.clearScreenDown", "rl.commit", "rl.cursorTo", "rl.moveCursor", "rl.rollback"], "status": "partial", "evidence": ["tests/corpus/5788-readline-promises-readline.ts", "tests/corpus/5789-readline-cursor-pos.cjs"]},
|
||||
{"chapter": "readline", "symbols": ["readline.emitKeypressEvents"], "status": "partial", "evidence": ["tests/corpus/5790-readline-keypress-events.ts", "tests/corpus/5791-readline-keypress-interfaces.cjs", "tests/corpus/5792-readline-keypress-listeners.ts"]},
|
||||
{"chapter": "readline", "symbols": ["readline.Interface", "rl.pause", "rl.resume", "rl.prompt", "rl.setPrompt", "rl.getPrompt", "rl.write", "rl", "'close'", "'error'", "'line'", "'pause'", "'resume'"], "status": "partial", "evidence": ["tests/corpus/5295-readline-promises-question.ts", "tests/corpus/5296-readline-lines.ts", "tests/corpus/5297-readline-callback-events.cjs", "tests/corpus/5299-readline-stdin.ts"]},
|
||||
{"chapter": "readline", "symbols": ["readline.Interface", "rl.pause", "rl.resume", "rl.prompt", "rl.setPrompt", "rl.getPrompt", "rl.write", "rl", "'close'", "'error'", "'line'", "'pause'", "'resume'"], "status": "partial", "evidence": ["tests/corpus/5295-readline-promises-question.ts", "tests/corpus/5296-readline-lines.ts", "tests/corpus/5297-readline-callback-events.cjs", "tests/corpus/5299-readline-stdin.ts", "tests/corpus/5876-readline-dispose-instanceof.ts", "tests/corpus/5877-readline-question-query.cjs"]},
|
||||
{"chapter": "stream", "symbols": ["stream.addAbortSignal"], "status": "partial", "evidence": ["tests/corpus/5300-stream-add-abort-signal.ts", "tests/corpus/5301-stream-add-abort-signal-errors.cjs"]},
|
||||
{"chapter": "stream", "symbols": ["readable.map", "readable.filter", "readable.forEach", "readable.toArray", "readable.some", "readable.find", "readable.every", "readable.flatMap", "readable.drop", "readable.take", "readable.reduce"], "status": "partial", "evidence": ["tests/corpus/5335-stream-operators-values.ts", "tests/corpus/5336-stream-operators-promise-timing.ts", "tests/corpus/5337-stream-operators-concurrency.ts", "tests/corpus/5338-stream-operators-checks.cjs"]},
|
||||
{"chapter": "stream", "symbols": ["stream.Readable.from"], "status": "partial", "evidence": ["tests/corpus/5340-readable-from-options.ts", "tests/corpus/5341-readable-from-dynamic.cjs", "tests/corpus/5420-readable-from-generators.ts", "tests/corpus/5421-readable-from-generators.cjs", "tests/corpus/5610-readable-from-sets-and-views.ts", "tests/corpus/5611-readable-from-class-iterables.ts", "tests/corpus/5612-readable-from-iterables.cjs"]},
|
||||
@@ -338,7 +338,7 @@
|
||||
{ "chapter": "fs", "signatures": ["`filehandle[Symbol.asyncDispose]()`"], "status": "partial", "evidence": ["tests/corpus/2844-node-disposable-resources.ts"] },
|
||||
{ "chapter": "fs", "signatures": ["`fsPromises.realpath(path[, options])`"], "status": "partial", "evidence": ["tests/corpus/2940-fsp-realpath.ts"] },
|
||||
{ "chapter": "fs", "signatures": ["`fsPromises.readdir(path[, options])`"], "status": "partial", "evidence": ["tests/corpus/2942-fsp-readdir-dirent.ts"] },
|
||||
{ "chapter": "readline", "signatures": ["`rl[Symbol.dispose]()`"], "status": "partial", "evidence": ["tests/corpus/2844-node-disposable-resources.ts"] },
|
||||
{ "chapter": "readline", "signatures": ["`rl[Symbol.dispose]()`"], "status": "partial", "evidence": ["tests/corpus/2844-node-disposable-resources.ts", "tests/corpus/5876-readline-dispose-instanceof.ts"] },
|
||||
{ "chapter": "timers", "signatures": ["`immediate[Symbol.dispose]()`", "`timeout[Symbol.dispose]()`"], "status": "partial", "evidence": ["tests/corpus/2844-node-disposable-resources.ts"] },
|
||||
{ "chapter": "stream", "signatures": ["`readable[Symbol.asyncIterator]()`", "`readable.iterator([options])`"], "status": "partial", "evidence": ["tests/corpus/1746-stream-for-await.ts", "tests/corpus/2861-readable-iterator-close.ts"] },
|
||||
{ "chapter": "stream", "signatures": ["`writable.destroy([error])`"], "status": "partial", "evidence": ["tests/corpus/2897-child-stdin-destroy.ts"] },
|
||||
@@ -405,7 +405,7 @@
|
||||
{ "symbols": ["dgram.createSocket", "socket.address", "socket.bind", "socket.close", "socket.connect", "socket.ref", "socket.remoteAddress", "socket.send", "socket.unref"], "status": "partial", "evidence": ["tests/corpus/2597-dgram-send-ladders.cjs"] },
|
||||
{ "symbols": ["net.SocketAddress", "net.BlockList"], "status": "partial", "evidence": ["tests/corpus/5659-net-blocklist-socketaddress.ts", "tests/fixtures/node-types/net-blocklist.ts"] },
|
||||
{ "symbols": ["SocketAddress.parse", "BlockList.isBlockList", "blockList.addAddress", "blockList.addAddresses", "blockList.addCIDR", "blockList.addCIDRs", "blockList.addRange", "blockList.addSubnet", "blockList.check", "blockList.clear", "blockList.fromJSON", "blockList.removeAddress", "blockList.removeCIDR", "blockList.removeRange", "blockList.removeSubnet", "blockList.toJSON"], "status": "supported", "evidence": ["tests/corpus/5659-net-blocklist-socketaddress.ts", "tests/fixtures/node-types/net-blocklist.ts"] },
|
||||
{ "symbols": ["readline.createInterface", "rl.close"], "status": "partial", "evidence": ["tests/corpus/1475-readline-closed-stdin.ts"] },
|
||||
{ "symbols": ["readline.createInterface", "rl.close"], "status": "partial", "evidence": ["tests/corpus/1475-readline-closed-stdin.ts", "tests/corpus/5878-readline-dynamic-streams.mjs"] },
|
||||
{ "symbols": ["test", "it", "test.skip", "test.todo", "test.only", "describe", "suite", "before", "after", "beforeEach", "afterEach", "context.test", "context.skip", "context.todo", "context.assert"], "status": "partial", "evidence": ["tests/harness/node-test.test.ts"] },
|
||||
{ "symbols": ["http.request", "http.get", "http.createServer", "https.request", "https.get", "https.createServer", "request.end", "request.destroy", "request.setTimeout", "request.write", "response.end", "response.getHeader", "response.hasHeader", "response.removeHeader", "response.setHeader", "response.write", "response.writeHead", "headersTimeout", "requestTimeout", "timeout", "keepAliveTimeout", "keepAliveTimeoutBuffer"], "status": "partial", "evidence": ["tests/corpus/1780-http-res-surface.ts", "tests/corpus/2672-http-request-response-callback.ts", "tests/corpus/2689-http-server-timeout-properties.ts"] }
|
||||
]
|
||||
|
||||
+8
-2
@@ -3839,7 +3839,10 @@ declare module "readline" {
|
||||
meta?: boolean;
|
||||
shift?: boolean;
|
||||
}
|
||||
export interface Interface extends Disposable {
|
||||
export class Interface implements Disposable {
|
||||
/* createInterface creates interfaces; instanceof tells the two
|
||||
* modules' classes apart. */
|
||||
protected constructor();
|
||||
/* terminal says whether the interface edits lines as a terminal;
|
||||
* line and cursor are the terminal's (line is '' otherwise). history
|
||||
* is the history option itself (or the interface's own empty array);
|
||||
@@ -3954,7 +3957,10 @@ declare module "readline/promises" {
|
||||
/* The readline Interface with the promise question: it resolves with
|
||||
* the next line, rejects with an AbortError when options.signal aborts,
|
||||
* and with ERR_USE_AFTER_CLOSE after close(). */
|
||||
export interface Interface extends Disposable {
|
||||
export class Interface implements Disposable {
|
||||
/* createInterface creates interfaces; instanceof tells the two
|
||||
* modules' classes apart. */
|
||||
protected constructor();
|
||||
/* terminal says whether the interface edits lines as a terminal;
|
||||
* line and cursor are the terminal's (line is '' otherwise). history
|
||||
* is the history option itself (or the interface's own empty array);
|
||||
|
||||
@@ -82,12 +82,19 @@ export function emitIoLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue
|
||||
}
|
||||
// A signal that is not an AbortSignal throws ERR_INVALID_ARG_TYPE.
|
||||
if (mode & 8) host.emitPendingCheck();
|
||||
// readline/promises interfaces are instances of that module's class.
|
||||
if (mode & 32) {
|
||||
host.declare(`declare void @scr_rl_mark_promises(double)`);
|
||||
B.line(`call void @scr_rl_mark_promises(double ${t})`);
|
||||
}
|
||||
return { name: t, type: e.type };
|
||||
}
|
||||
if (e.fn === "rl.question") {
|
||||
if (e.fn === "rl.question" || e.fn === "rl.questionDyn") {
|
||||
// The answer callback MOVES into the interface's registry; throws
|
||||
// Node's use-after-close error (the may-throw seed).
|
||||
// Node's use-after-close error (the may-throw seed), and with a
|
||||
// checked-dynamic query that is not a string, ERR_INVALID_ARG_TYPE.
|
||||
host.usesTimers = true;
|
||||
const fn = e.fn === "rl.question" ? "scr_rl_question" : "scr_rl_question_dyn";
|
||||
const cbT = e.args[2]!.type;
|
||||
if (cbT.kind !== "func")
|
||||
throw new InternalCompilerError("llvm emitter bug: rl.question callback not a func");
|
||||
@@ -100,29 +107,37 @@ export function emitIoLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue
|
||||
? "scr_rl_answer_thunk_dyn"
|
||||
: "scr_rl_answer_thunk_str";
|
||||
host.declare(`declare void @${adapter}(ptr, ptr)`);
|
||||
host.declare(`declare void @scr_rl_question(double, ptr, ptr, ptr)`);
|
||||
host.declare(`declare void @${fn}(double, ptr, ptr, ptr)`);
|
||||
B.line(
|
||||
`call void @scr_rl_question(double ${args[0]!.name}, ptr ${args[1]!.name}, ptr ${args[2]!.name}, ptr @${adapter})`,
|
||||
`call void @${fn}(double ${args[0]!.name}, ptr ${args[1]!.name}, ptr ${args[2]!.name}, ptr @${adapter})`,
|
||||
);
|
||||
host.emitPendingCheck();
|
||||
return { name: "", type: e.type };
|
||||
}
|
||||
if (e.fn === "rl.questionAsync" || e.fn === "rl.questionAsyncSignal") {
|
||||
// The promise question never throws: closed interfaces and aborted
|
||||
// signals reject the answered promise (+1).
|
||||
if (
|
||||
e.fn === "rl.questionAsync" ||
|
||||
e.fn === "rl.questionAsyncSignal" ||
|
||||
e.fn === "rl.questionAsyncDyn" ||
|
||||
e.fn === "rl.questionAsyncSignalDyn"
|
||||
) {
|
||||
// The promise question never throws: closed interfaces, aborted
|
||||
// signals and queries that are not strings reject the answered
|
||||
// promise (+1).
|
||||
host.usesTimers = true;
|
||||
const args = e.args.map((a) => host.emitExpr(a));
|
||||
const dyn = e.fn === "rl.questionAsyncDyn" || e.fn === "rl.questionAsyncSignalDyn";
|
||||
const fn = dyn ? "scr_rl_question_promise_dyn" : "scr_rl_question_promise";
|
||||
let signal = "null";
|
||||
let add = "null";
|
||||
if (e.fn === "rl.questionAsyncSignal") {
|
||||
if (e.fn === "rl.questionAsyncSignal" || e.fn === "rl.questionAsyncSignalDyn") {
|
||||
signal = args[2]!.name;
|
||||
host.declare(`declare ptr @scr_events_add_abort_listener(ptr, ptr)`);
|
||||
add = "@scr_events_add_abort_listener";
|
||||
}
|
||||
host.declare(`declare ptr @scr_rl_question_promise(double, ptr, ptr, ptr)`);
|
||||
host.declare(`declare ptr @${fn}(double, ptr, ptr, ptr)`);
|
||||
const t = B.tmp();
|
||||
B.line(
|
||||
`${t} = call ptr @scr_rl_question_promise(double ${args[0]!.name}, ptr ${args[1]!.name}, ptr ${signal}, ptr ${add})`,
|
||||
`${t} = call ptr @${fn}(double ${args[0]!.name}, ptr ${args[1]!.name}, ptr ${signal}, ptr ${add})`,
|
||||
);
|
||||
return host.own({ name: t, type: e.type });
|
||||
}
|
||||
|
||||
@@ -1023,6 +1023,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"rl.prompt": "scr_rl_prompt",
|
||||
"rl.promptWith": "scr_rl_prompt_with",
|
||||
"rl.setPrompt": "scr_rl_set_prompt",
|
||||
"rl.setPromptDyn": "scr_rl_set_prompt_dyn",
|
||||
"rl.getPrompt": "scr_rl_get_prompt",
|
||||
"rl.write": "scr_rl_write",
|
||||
"rl.writeKey": "scr_rl_write_key",
|
||||
@@ -1038,6 +1039,9 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"rl.terminal": "scr_rl_terminal",
|
||||
"rl.line": "scr_rl_line",
|
||||
"rl.isTerminal": "scr_rl_is_terminal",
|
||||
"rl.isPromises": "scr_rl_is_promises",
|
||||
"rl.inputFromDyn": "scr_stream_rl_from_dyn",
|
||||
"rl.outputFromDyn": "scr_stream_rl_from_dyn",
|
||||
"rl.cursor": "scr_rl_cursor",
|
||||
"rl.seq": "scr_rl_seq",
|
||||
"rl.moves": "scr_rl_moves",
|
||||
|
||||
@@ -18,6 +18,7 @@ import { READLINE_DOCUMENTED_OPTIONS, fenceOrDropOptionKey } from "../surfaces.j
|
||||
import { streamSidesOf } from "../lower-stream.js";
|
||||
import type { IrLibFn } from "../../../ir/builtin-signatures.js";
|
||||
import { voidizedCallback } from "../lower-server.js";
|
||||
import { builtinImportOf } from "./module-bindings.js";
|
||||
|
||||
/** The ambient module declaring `d`, or null. */
|
||||
function ambientModuleOf(d: ts.Node): string | null {
|
||||
@@ -270,6 +271,80 @@ function lowerReadlineUiCall(
|
||||
return { kind: "seqExpr", stmts, result: recv, type: F64, loc };
|
||||
}
|
||||
|
||||
/** The module ("readline" or "readline/promises") whose Interface class
|
||||
* `node` names, or null. */
|
||||
function interfaceClassModule(lowerer: Lowerer, node: ts.Expression): string | null {
|
||||
const builtin = ts.isIdentifier(node)
|
||||
? builtinImportOf(lowerer, node)
|
||||
: ts.isPropertyAccessExpression(node) && !node.questionDotToken
|
||||
? lowerer.builtinMemberOf(node)
|
||||
: null;
|
||||
if (builtin?.member !== "Interface") return null;
|
||||
return builtin.module === "readline" || builtin.module === "readline/promises"
|
||||
? builtin.module
|
||||
: null;
|
||||
}
|
||||
|
||||
/** `rl instanceof readline.Interface` and `rl instanceof
|
||||
* readlinePromises.Interface` over a readline Interface: an interface is
|
||||
* an instance of the class of the module that created it (Node's
|
||||
* readline/promises Interface does not extend node:readline's). Null for
|
||||
* other instanceof tests. */
|
||||
export function lowerReadlineInstanceof(
|
||||
lowerer: Lowerer,
|
||||
expr: ts.BinaryExpression,
|
||||
): IrExpr | null {
|
||||
const module = interfaceClassModule(lowerer, expr.right);
|
||||
if (module === null || !isReadlineTyped(lowerer, expr.left)) return null;
|
||||
const loc = locOf(expr);
|
||||
const recv = lowerer.lowerExprExpecting(expr.left, F64);
|
||||
const isPromises: IrExpr = {
|
||||
kind: "libCall",
|
||||
fn: "rl.isPromises",
|
||||
args: [recv],
|
||||
type: BOOL,
|
||||
loc,
|
||||
};
|
||||
if (module === "readline/promises") return isPromises;
|
||||
return { kind: "unary", op: "!", operand: isPromises, type: BOOL, loc };
|
||||
}
|
||||
|
||||
/** `rl[Symbol.dispose]()` on a readline Interface: Node's dispose method
|
||||
* calls close(), which does nothing on a closed interface. Null for other
|
||||
* element-access calls. */
|
||||
export function lowerReadlineDisposeCall(lowerer: Lowerer, call: ts.CallExpression): IrExpr | null {
|
||||
const access = call.expression;
|
||||
if (!ts.isElementAccessExpression(access) || access.questionDotToken || call.questionDotToken) {
|
||||
return null;
|
||||
}
|
||||
let key = access.argumentExpression;
|
||||
while (ts.isParenthesizedExpression(key)) key = key.expression;
|
||||
if (
|
||||
!ts.isPropertyAccessExpression(key) ||
|
||||
lowerer.stdlibGlobalMember(key, "Symbol") !== "dispose"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (!isReadlineTyped(lowerer, access.expression)) return null;
|
||||
if (call.arguments.length > 0) {
|
||||
lowerer.noLowering("readline.Interface[Symbol.dispose] with arguments", call);
|
||||
}
|
||||
const recv = lowerer.lowerExprExpecting(access.expression, F64);
|
||||
return { kind: "libCall", fn: "rl.close", args: [recv], type: VOID, loc: locOf(call) };
|
||||
}
|
||||
|
||||
/** A question query or setPrompt argument: the string, or for a value
|
||||
* that need not be one (JavaScript), the checked-dynamic value, which the
|
||||
* runtime writes the way Node's kWriteToOutput does (dyn: true). */
|
||||
function lowerPromptArg(lowerer: Lowerer, node: ts.Expression): { value: IrExpr; dyn: boolean } {
|
||||
if (lowerer.typeOf(node).flags & ts.TypeFlags.StringLike) {
|
||||
return { value: lowerer.lowerExprExpecting(node, STRING), dyn: false };
|
||||
}
|
||||
const value = lowerer.lowerExpr(node);
|
||||
if (value.type.kind === "string") return { value, dyn: false };
|
||||
return { value: lowerer.coerceInto(node, value, DYN), dyn: true };
|
||||
}
|
||||
|
||||
/** Method calls on readline Interface receivers (both modules):
|
||||
* - question(query, cb) — the callback form; question(query, { signal }?)
|
||||
* — the promise form (readline/promises);
|
||||
@@ -303,18 +378,20 @@ export function lowerReadlineMethodCall(
|
||||
args.length <= 2
|
||||
) {
|
||||
const recv = receiver();
|
||||
const query = lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
const query = lowerPromptArg(lowerer, args[0]!);
|
||||
const signal = questionSignal(lowerer, args[1]);
|
||||
const type = { kind: "promise", inner: STRING } as const;
|
||||
if (signal === null) {
|
||||
return { kind: "libCall", fn: "rl.questionAsync", args: [recv, query], type, loc };
|
||||
const fn = query.dyn ? "rl.questionAsyncDyn" : "rl.questionAsync";
|
||||
return { kind: "libCall", fn, args: [recv, query.value], type, loc };
|
||||
}
|
||||
const sig = lowerer.lowerExprExpecting(signal, DYN);
|
||||
return { kind: "libCall", fn: "rl.questionAsyncSignal", args: [recv, query, sig], type, loc };
|
||||
const fn = query.dyn ? "rl.questionAsyncSignalDyn" : "rl.questionAsyncSignal";
|
||||
return { kind: "libCall", fn, args: [recv, query.value, sig], type, loc };
|
||||
}
|
||||
if (name === "question" && args.length === 2) {
|
||||
const recv = receiver();
|
||||
const query = lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
const query = lowerPromptArg(lowerer, args[0]!);
|
||||
const cb = lowerer.lowerExpr(args[1]!);
|
||||
if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > 1) {
|
||||
lowerer.unsupported(
|
||||
@@ -331,7 +408,8 @@ export function lowerReadlineMethodCall(
|
||||
`question callbacks whose parameter is not 'string' (got '${lowerer.fmt(param)}')`,
|
||||
);
|
||||
}
|
||||
return { kind: "libCall", fn: "rl.question", args: [recv, query, cb], type: VOID, loc };
|
||||
const fn = query.dyn ? "rl.questionDyn" : "rl.question";
|
||||
return { kind: "libCall", fn, args: [recv, query.value, cb], type: VOID, loc };
|
||||
}
|
||||
const simple: Record<string, "rl.close" | "rl.pause" | "rl.resume" | "rl.prompt"> = {
|
||||
close: "rl.close",
|
||||
@@ -366,8 +444,9 @@ export function lowerReadlineMethodCall(
|
||||
}
|
||||
if (name === "setPrompt" && args.length === 1) {
|
||||
const recv = receiver();
|
||||
const prompt = lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
return { kind: "libCall", fn: "rl.setPrompt", args: [recv, prompt], type: VOID, loc };
|
||||
const prompt = lowerPromptArg(lowerer, args[0]!);
|
||||
const fn = prompt.dyn ? "rl.setPromptDyn" : "rl.setPrompt";
|
||||
return { kind: "libCall", fn, args: [recv, prompt.value], type: VOID, loc };
|
||||
}
|
||||
if (name === "getPrompt" && args.length === 0) {
|
||||
return { kind: "libCall", fn: "rl.getPrompt", args: [receiver()], type: STRING, loc };
|
||||
@@ -871,6 +950,25 @@ export function lowerReadlineCursorCall(
|
||||
return { kind: "seqExpr", stmts, result, type: BOOL, loc };
|
||||
}
|
||||
|
||||
/** A checked-dynamic input or output (a JavaScript function's result, an
|
||||
* object's member, an element that may be undefined) as the stream it
|
||||
* holds: the runtime refuses a value that is not a stream with that
|
||||
* side, and answers null for an output of null or undefined. Other
|
||||
* values pass through. */
|
||||
function streamFromDyn(lowerer: Lowerer, node: ts.Expression, output: boolean): IrExpr {
|
||||
let v = lowerer.lowerExpr(node);
|
||||
if (v.type.kind === "union") v = lowerer.coerceInto(node, v, DYN);
|
||||
if (v.type.kind !== "dyn") return v;
|
||||
const loc = locOf(node);
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: output ? "rl.outputFromDyn" : "rl.inputFromDyn",
|
||||
args: [v, boolLit(output, loc)],
|
||||
type: { kind: "object", className: output ? "%Writable" : "%Readable" },
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
/** The interface's input: process.stdin (null) or a readable stream. */
|
||||
function lowerInput(lowerer: Lowerer, node: ts.Expression): IrExpr | null {
|
||||
if (
|
||||
@@ -879,7 +977,7 @@ function lowerInput(lowerer: Lowerer, node: ts.Expression): IrExpr | null {
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const v = lowerer.lowerExpr(node);
|
||||
const v = streamFromDyn(lowerer, node, false);
|
||||
const info = v.type.kind === "object" ? lowerer.classes.get(v.type.className) : undefined;
|
||||
const sides = streamSidesOf(lowerer, info);
|
||||
if (sides !== "r" && sides !== "rw") {
|
||||
@@ -904,7 +1002,7 @@ function lowerOutput(lowerer: Lowerer, node: ts.Expression): IrExpr | "stdout" |
|
||||
return "none";
|
||||
}
|
||||
if (ts.isIdentifier(node) && node.text === "undefined") return "none";
|
||||
const v = lowerer.lowerExpr(node);
|
||||
const v = streamFromDyn(lowerer, node, true);
|
||||
const info = v.type.kind === "object" ? lowerer.classes.get(v.type.className) : undefined;
|
||||
const sides = streamSidesOf(lowerer, info);
|
||||
if (sides !== "w" && sides !== "rw") {
|
||||
@@ -923,8 +1021,8 @@ function lowerOutput(lowerer: Lowerer, node: ts.Expression): IrExpr | "stdout" |
|
||||
* stream; the output process.stdout, a writable stream, or absent
|
||||
* (prompts are then not written). rl.create's arguments: the mode literal
|
||||
* (1 stream input, 2 stream output, 4 stdout output, 8 signal, 16
|
||||
* prompt), then the input and output streams when present, crlfDelay,
|
||||
* the prompt and the signal when present. An interface that can be a
|
||||
* prompt, 32 node:readline/promises), then the input and output streams
|
||||
* when present, crlfDelay, the prompt and the signal when present. An interface that can be a
|
||||
* terminal one (a `terminal` option that is not false, or none with
|
||||
* process.stdout as the output, which is a terminal when it is a TTY)
|
||||
* goes through rl.terminal with the completer. */
|
||||
@@ -1107,7 +1205,8 @@ export function lowerReadlineCreateCall(
|
||||
(typeof output === "object" ? 2 : 0) |
|
||||
(output === "stdout" ? 4 : 0) |
|
||||
(signal !== null ? 8 : 0) |
|
||||
(prompt !== null ? 16 : 0);
|
||||
(prompt !== null ? 16 : 0) |
|
||||
(promises ? 32 : 0);
|
||||
const args: IrExpr[] = [numLit(mode, loc)];
|
||||
if (input !== null) args.push(input);
|
||||
if (typeof output === "object") args.push(output);
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
import { cjsExportCallIgnoresThis, cjsInFileExportGlobalOf } from "./cjs-exports-object.js";
|
||||
import { lowerGenMethodCall } from "./lower-generators.js";
|
||||
import { lowerClassSymbolMethodCall } from "./symbol-methods.js";
|
||||
import { lowerReadlineDisposeCall } from "./builtins/readline.js";
|
||||
import { errorToStringCall } from "./error-methods.js";
|
||||
import { lowerClassPrototypeAssign, lowerClassPrototypeDescriptors } from "./class-prototypes.js";
|
||||
import {
|
||||
@@ -1648,6 +1649,8 @@ export function lowerCall(lowerer: Lowerer, expr: ts.CallExpression): IrExpr {
|
||||
if (timersDispose) return timersDispose;
|
||||
const symbolMethod = lowerClassSymbolMethodCall(lowerer, expr);
|
||||
if (symbolMethod) return symbolMethod;
|
||||
const readlineDispose = lowerReadlineDisposeCall(lowerer, expr);
|
||||
if (readlineDispose) return readlineDispose;
|
||||
lowerer.fenceUnsupportedFetchConstructorMember(expr.expression);
|
||||
lowerer.fenceStaticResponseMember(expr.expression, "call");
|
||||
lowerer.fenceStaticHeadersMember(expr.expression, "call");
|
||||
|
||||
@@ -9,7 +9,7 @@ import { lowerUnionEquality, tagEqualityMayMissAlias } from "./strict-equality.j
|
||||
import { isOptionalProcessStreamProperty } from "./builtins/process.js";
|
||||
import { lowerWorkerMetadata } from "./builtins/workers.js";
|
||||
import { esmImportBindingKind } from "./builtins/module-bindings.js";
|
||||
import { lowerReadlineProperty } from "./builtins/readline.js";
|
||||
import { lowerReadlineInstanceof, lowerReadlineProperty } from "./builtins/readline.js";
|
||||
import { lowerBlobInstanceof } from "./builtins/blob.js";
|
||||
import { lowerWebAssemblyTypeof, refuseWebAssemblyUse } from "./builtins/webassembly.js";
|
||||
import { lowerKeyObjectInstanceof } from "./builtins/key-object.js";
|
||||
@@ -12816,6 +12816,8 @@ export function lowerInstanceOf(lowerer: Lowerer, expr: ts.BinaryExpression, loc
|
||||
if (keyObject) return keyObject;
|
||||
const netAddress = lowerNetAddressInstanceof(lowerer, expr);
|
||||
if (netAddress) return netAddress;
|
||||
const readlineInterface = lowerReadlineInstanceof(lowerer, expr);
|
||||
if (readlineInterface) return readlineInterface;
|
||||
}
|
||||
if (lowerer.isStdlibGlobal(expr.right, "SharedArrayBuffer")) {
|
||||
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
||||
|
||||
@@ -435,6 +435,7 @@ export const MAY_THROW_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"https.requestFn",
|
||||
"https.requestFnCb",
|
||||
"rl.question",
|
||||
"rl.questionDyn",
|
||||
"rl.checkOptions",
|
||||
"rl.checkCompleter",
|
||||
"readable.fromArr",
|
||||
@@ -457,6 +458,11 @@ export const MAY_THROW_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"rl.resume",
|
||||
"rl.prompt",
|
||||
"rl.promptWith",
|
||||
"rl.setPromptDyn",
|
||||
"rl.getPrompt",
|
||||
"rl.cursorPos",
|
||||
"rl.inputFromDyn",
|
||||
"rl.outputFromDyn",
|
||||
"rl.write",
|
||||
"rl.writeKey",
|
||||
"rl.cursor",
|
||||
|
||||
@@ -3959,6 +3959,14 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
argTypes: [F64, STRING, DYN],
|
||||
result: { kind: "promise", inner: STRING },
|
||||
},
|
||||
/** The question forms with a checked-dynamic query (a JavaScript value
|
||||
* that need not be a string). */
|
||||
"rl.questionDyn": { argTypes: [F64, DYN, null], result: VOID },
|
||||
"rl.questionAsyncDyn": { argTypes: [F64, DYN], result: { kind: "promise", inner: STRING } },
|
||||
"rl.questionAsyncSignalDyn": {
|
||||
argTypes: [F64, DYN, DYN],
|
||||
result: { kind: "promise", inner: STRING },
|
||||
},
|
||||
"rl.close": { argTypes: [F64], result: VOID },
|
||||
"rl.pause": { argTypes: [F64], result: VOID },
|
||||
"rl.resume": { argTypes: [F64], result: VOID },
|
||||
@@ -3966,6 +3974,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
/** prompt(preserveCursor): [handle, preserveCursor] */
|
||||
"rl.promptWith": { argTypes: [F64, BOOL], result: VOID },
|
||||
"rl.setPrompt": { argTypes: [F64, STRING], result: VOID },
|
||||
"rl.setPromptDyn": { argTypes: [F64, DYN], result: VOID },
|
||||
"rl.getPrompt": { argTypes: [F64], result: STRING },
|
||||
"rl.write": { argTypes: [F64, STRING], result: VOID },
|
||||
/** write(data, key): [handle, hasData, data, hasKey, hasName, name,
|
||||
@@ -3998,6 +4007,14 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
"rl.terminal": { argTypes: [F64, F64, BOOL, DYN, BOOL], result: VOID },
|
||||
"rl.line": { argTypes: [F64], result: STRING },
|
||||
"rl.isTerminal": { argTypes: [F64], result: BOOL },
|
||||
"rl.isPromises": { argTypes: [F64], result: BOOL },
|
||||
/** createInterface's input or output as a checked-dynamic value:
|
||||
* [value, output] — the stream; refused when it is not one. */
|
||||
"rl.inputFromDyn": { argTypes: [DYN, BOOL], result: { kind: "object", className: "%Readable" } },
|
||||
"rl.outputFromDyn": {
|
||||
argTypes: [DYN, BOOL],
|
||||
result: { kind: "object", className: "%Writable" },
|
||||
},
|
||||
"rl.cursor": { argTypes: [F64], result: F64 },
|
||||
/** readline.cursorTo / moveCursor / clearLine / clearScreenDown: rl.seq
|
||||
* [kind (0-3), the first and second numbers, whether the second is
|
||||
|
||||
@@ -192,6 +192,8 @@ const STREAM_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"fileHandle.createWriteStream",
|
||||
"fs.createReadStream",
|
||||
"fs.createWriteStream",
|
||||
"rl.inputFromDyn",
|
||||
"rl.outputFromDyn",
|
||||
"sp.finished",
|
||||
"sp.pipeline",
|
||||
"sp.pipelineStages",
|
||||
@@ -663,12 +665,16 @@ const PROCESS_EVENT_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"rl.question",
|
||||
"rl.questionAsync",
|
||||
"rl.questionAsyncSignal",
|
||||
"rl.questionDyn",
|
||||
"rl.questionAsyncDyn",
|
||||
"rl.questionAsyncSignalDyn",
|
||||
"rl.close",
|
||||
"rl.pause",
|
||||
"rl.resume",
|
||||
"rl.prompt",
|
||||
"rl.promptWith",
|
||||
"rl.setPrompt",
|
||||
"rl.setPromptDyn",
|
||||
"rl.getPrompt",
|
||||
"rl.write",
|
||||
"rl.writeKey",
|
||||
@@ -685,6 +691,7 @@ const PROCESS_EVENT_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"rl.terminal",
|
||||
"rl.line",
|
||||
"rl.isTerminal",
|
||||
"rl.isPromises",
|
||||
"rl.cursor",
|
||||
"rl.seq",
|
||||
"rl.moves",
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5876-readline-dispose-instanceof.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5877-readline-question-query.cjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5878-readline-dynamic-streams.mjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -185,11 +185,28 @@ void scr_rl_do_resume(ScrRl *rl) {
|
||||
}
|
||||
}
|
||||
|
||||
bool scr_rl_prompt_writable(ScrRl *rl) {
|
||||
if (!rl->prompt_value) return true;
|
||||
scr_dyn_arg_type_fail("stringToWrite", "of type string", rl->prompt_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void scr_rl_prompt_now(ScrRl *rl) {
|
||||
if (rl->paused) scr_rl_do_resume(rl);
|
||||
if (scr_exc_pending()) return;
|
||||
if (rl->tty) scr_rl_tty_ops->prompt(rl, false);
|
||||
else scr_rl_write_out(rl, rl->prompt);
|
||||
else if (scr_rl_prompt_writable(rl)) scr_rl_write_out(rl, rl->prompt);
|
||||
}
|
||||
|
||||
/* setPrompt(this[kOldPrompt]) when a question is answered or cancelled */
|
||||
static void scr_rl_restore_prompt(ScrRl *rl) {
|
||||
if (!rl->old_prompt) return;
|
||||
scr_str_release(rl->prompt);
|
||||
rl->prompt = rl->old_prompt;
|
||||
rl->old_prompt = NULL;
|
||||
if (rl->prompt_value) scr_dyn_release(rl->prompt_value);
|
||||
rl->prompt_value = rl->old_prompt_value;
|
||||
rl->old_prompt_value = NULL;
|
||||
}
|
||||
|
||||
/* ── close ─────────────────────────────────────────────────────────── */
|
||||
@@ -251,11 +268,7 @@ void scr_rl_on_line(ScrRl *rl, ScrStr *line) {
|
||||
ScrClosure *cb = rl->q_cb;
|
||||
ScrRlLineFn fn = rl->q_fn;
|
||||
rl->q_cb = NULL;
|
||||
if (rl->old_prompt) {
|
||||
scr_str_release(rl->prompt);
|
||||
rl->prompt = rl->old_prompt;
|
||||
rl->old_prompt = NULL;
|
||||
}
|
||||
scr_rl_restore_prompt(rl);
|
||||
fn(cb, line);
|
||||
scr_closure_release(cb);
|
||||
return;
|
||||
@@ -619,6 +632,8 @@ static void scr_rl_cleanup_atexit(void) {
|
||||
if (rl->out) rl->io->release(rl->out);
|
||||
scr_str_release(rl->prompt);
|
||||
if (rl->old_prompt) scr_str_release(rl->old_prompt);
|
||||
if (rl->prompt_value) scr_dyn_release(rl->prompt_value);
|
||||
if (rl->old_prompt_value) scr_dyn_release(rl->old_prompt_value);
|
||||
free(rl->line_buf);
|
||||
free(rl->parked);
|
||||
free(rl);
|
||||
@@ -748,7 +763,58 @@ void scr_rl_question(double id, ScrStr *query, ScrClosure *cb /*moves*/, ScrRlLi
|
||||
return;
|
||||
}
|
||||
rl->old_prompt = rl->prompt;
|
||||
rl->old_prompt_value = rl->prompt_value;
|
||||
rl->prompt = scr_str_retain(query);
|
||||
rl->prompt_value = NULL;
|
||||
rl->q_cb = cb;
|
||||
rl->q_fn = fn;
|
||||
scr_rl_prompt_now(rl);
|
||||
}
|
||||
|
||||
/* A prompt that is not a string: String(value) for a primitive, which is
|
||||
* what the terminal's line refresh and getCursorPos concatenate. An
|
||||
* object's valueOf/toString would run on every refresh, and a symbol
|
||||
* throws there, so a terminal interface refuses them (false, the refusal
|
||||
* pending); another interface only writes the prompt, which throws for
|
||||
* any value that is not a string, so it keeps ''. +1. */
|
||||
static bool scr_rl_prompt_text(ScrRl *rl, ScrDyn *value, ScrStr **out) {
|
||||
if (scr_rl_primitive_prompt(value)) {
|
||||
*out = scr_dyn_string_coerce(value);
|
||||
return true;
|
||||
}
|
||||
if (!rl->tty) {
|
||||
*out = scr_str_new("", 0);
|
||||
return true;
|
||||
}
|
||||
static const char m[] =
|
||||
"a terminal readline prompt that is an object or a symbol has no lowering";
|
||||
scr_throw_error_msg_code(SCR_ERR_ERROR, m, sizeof m - 1, "SC2020");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* question(query, cb) with a checked-dynamic query: a string goes through
|
||||
* scr_rl_question. Another value becomes the prompt, the callback is
|
||||
* kept, and the prompt write throws ERR_INVALID_ARG_TYPE on an interface
|
||||
* that is not a terminal (the question stays pending, as in Node). */
|
||||
void scr_rl_question_dyn(double id, ScrDyn *query, ScrClosure *cb /*moves*/, ScrRlLineFn fn) {
|
||||
if (query->kind == SCR_DYN_STR) {
|
||||
scr_rl_question(id, query->v.str, cb, fn);
|
||||
return;
|
||||
}
|
||||
ScrRl *rl = scr_rl_find(id);
|
||||
if (!rl || rl->closed || rl->q_cb) {
|
||||
scr_rl_question(id, NULL, cb, fn); /* the closed throw, or the re-prompt */
|
||||
return;
|
||||
}
|
||||
ScrStr *text;
|
||||
if (!scr_rl_prompt_text(rl, query, &text)) {
|
||||
scr_closure_release(cb);
|
||||
return;
|
||||
}
|
||||
rl->old_prompt = rl->prompt;
|
||||
rl->old_prompt_value = rl->prompt_value;
|
||||
rl->prompt = text;
|
||||
rl->prompt_value = scr_dyn_retain(query);
|
||||
rl->q_cb = cb;
|
||||
rl->q_fn = fn;
|
||||
scr_rl_prompt_now(rl);
|
||||
@@ -857,11 +923,7 @@ static ScrDyn *scr_rl_ask_abort_thunk(ScrClosure *clo, ScrDyn *const *args, size
|
||||
if (rl && rl->q_cb) {
|
||||
scr_closure_release(rl->q_cb);
|
||||
rl->q_cb = NULL;
|
||||
if (rl->old_prompt) {
|
||||
scr_str_release(rl->prompt);
|
||||
rl->prompt = rl->old_prompt;
|
||||
rl->old_prompt = NULL;
|
||||
}
|
||||
scr_rl_restore_prompt(rl);
|
||||
if (rl->tty) {
|
||||
scr_rl_tty_ops->clear_line(rl);
|
||||
} else {
|
||||
@@ -877,8 +939,11 @@ static ScrDyn *scr_rl_ask_abort_thunk(ScrClosure *clo, ScrDyn *const *args, size
|
||||
|
||||
/* readline/promises question(query, { signal }): rejects with
|
||||
* ERR_USE_AFTER_CLOSE on a closed interface and with an AbortError when
|
||||
* the signal is (or becomes) aborted. */
|
||||
ScrPromise *scr_rl_question_promise(double id, ScrStr *query, ScrDyn *signal, ScrAbortAddFn add) {
|
||||
* the signal is (or becomes) aborted. A checked-dynamic query (query_dyn,
|
||||
* query NULL) that is not a string rejects with what question() throws,
|
||||
* as Node's promise executor does. */
|
||||
static ScrPromise *scr_rl_ask(double id, ScrStr *query, ScrDyn *query_dyn, ScrDyn *signal,
|
||||
ScrAbortAddFn add) {
|
||||
RlAsk *a = calloc(1, sizeof *a);
|
||||
if (!a) scr_rl_oom();
|
||||
a->rc = 1;
|
||||
@@ -928,10 +993,29 @@ ScrPromise *scr_rl_question_promise(double id, ScrStr *query, ScrDyn *signal, Sc
|
||||
scr_box_set_ref(cb->caps[0], a); /* the creation reference moves here */
|
||||
cb->caps[1] = scr_box_new_obj(&scr_rl_dyn_retain_v, &scr_rl_dyn_release_v, NULL);
|
||||
scr_box_set_ref(cb->caps[1], dispose); /* moves (NULL without a signal) */
|
||||
scr_rl_question(id, query, cb, &scr_rl_ask_answer);
|
||||
if (!query_dyn) {
|
||||
scr_rl_question(id, query, cb, &scr_rl_ask_answer);
|
||||
return result;
|
||||
}
|
||||
scr_rl_ask_retain_v(a);
|
||||
scr_rl_question_dyn(id, query_dyn, cb, &scr_rl_ask_answer);
|
||||
if (scr_exc_pending() && !a->settled) {
|
||||
a->settled = true;
|
||||
scr_promise_reject_pending(a->p);
|
||||
}
|
||||
scr_rl_ask_release_v(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
ScrPromise *scr_rl_question_promise(double id, ScrStr *query, ScrDyn *signal, ScrAbortAddFn add) {
|
||||
return scr_rl_ask(id, query, NULL, signal, add);
|
||||
}
|
||||
|
||||
ScrPromise *scr_rl_question_promise_dyn(double id, ScrDyn *query, ScrDyn *signal,
|
||||
ScrAbortAddFn add) {
|
||||
return scr_rl_ask(id, NULL, query, signal, add);
|
||||
}
|
||||
|
||||
void scr_rl_close(double id) {
|
||||
ScrRl *rl = scr_rl_find(id);
|
||||
if (rl) scr_rl_settle_close(rl); /* twice is a no-op, like Node */
|
||||
@@ -985,13 +1069,48 @@ void scr_rl_set_prompt(double id, ScrStr *prompt) {
|
||||
if (!rl) return;
|
||||
scr_str_release(rl->prompt);
|
||||
rl->prompt = scr_str_retain(prompt);
|
||||
if (rl->prompt_value) scr_dyn_release(rl->prompt_value);
|
||||
rl->prompt_value = NULL;
|
||||
}
|
||||
|
||||
/* setPrompt(value) with a checked-dynamic value; borrows. */
|
||||
void scr_rl_set_prompt_dyn(double id, ScrDyn *prompt) {
|
||||
if (prompt->kind == SCR_DYN_STR) {
|
||||
scr_rl_set_prompt(id, prompt->v.str);
|
||||
return;
|
||||
}
|
||||
ScrRl *rl = scr_rl_find(id);
|
||||
if (!rl) return;
|
||||
ScrStr *text;
|
||||
if (!scr_rl_prompt_text(rl, prompt, &text)) return;
|
||||
scr_str_release(rl->prompt);
|
||||
rl->prompt = text;
|
||||
if (rl->prompt_value) scr_dyn_release(rl->prompt_value);
|
||||
rl->prompt_value = scr_dyn_retain(prompt);
|
||||
}
|
||||
|
||||
ScrStr *scr_rl_get_prompt(double id) {
|
||||
ScrRl *rl = scr_rl_find(id);
|
||||
if (rl && rl->prompt_value) {
|
||||
static const char m[] = "rl.getPrompt() of a prompt that is not a string has no lowering";
|
||||
scr_throw_error_msg_code(SCR_ERR_ERROR, m, sizeof m - 1, "SC2020");
|
||||
return scr_str_new("", 0);
|
||||
}
|
||||
return rl ? scr_str_retain(rl->prompt) : scr_str_new("", 0);
|
||||
}
|
||||
|
||||
/* Called after a node:readline/promises interface opens: its Interface
|
||||
* class is not node:readline's, which instanceof tells apart. */
|
||||
void scr_rl_mark_promises(double id) {
|
||||
ScrRl *rl = scr_rl_find(id);
|
||||
if (rl) rl->promises = true;
|
||||
}
|
||||
|
||||
bool scr_rl_is_promises(double id) {
|
||||
ScrRl *rl = scr_rl_find(id);
|
||||
return rl && rl->promises;
|
||||
}
|
||||
|
||||
bool scr_rl_is_terminal(double id) {
|
||||
ScrRl *rl = scr_rl_find(id);
|
||||
return rl && rl->tty;
|
||||
|
||||
@@ -47,13 +47,22 @@ void scr_rl_input_error(double id, ScrError *err);
|
||||
void scr_rl_question(double id, ScrStr *query, ScrClosure *cb /*moves*/,
|
||||
void (*fn)(ScrClosure *, ScrStr *));
|
||||
ScrPromise *scr_rl_question_promise(double id, ScrStr *query, ScrDyn *signal, ScrAbortAddFn add);
|
||||
/* The same with a checked-dynamic query (borrowed). A query that is not a
|
||||
* string makes the prompt write throw ERR_INVALID_ARG_TYPE (the promise
|
||||
* form rejects with it) unless the interface is a terminal; the question
|
||||
* stays pending, as in Node. */
|
||||
void scr_rl_question_dyn(double id, ScrDyn *query, ScrClosure *cb /*moves*/,
|
||||
void (*fn)(ScrClosure *, ScrStr *));
|
||||
ScrPromise *scr_rl_question_promise_dyn(double id, ScrDyn *query, ScrDyn *signal,
|
||||
ScrAbortAddFn add);
|
||||
void scr_rl_close(double id);
|
||||
void scr_rl_pause(double id); /* may throw ERR_USE_AFTER_CLOSE */
|
||||
void scr_rl_resume(double id); /* may throw ERR_USE_AFTER_CLOSE */
|
||||
void scr_rl_prompt(double id); /* may throw ERR_USE_AFTER_CLOSE */
|
||||
void scr_rl_prompt_with(double id, bool preserve); /* prompt(preserveCursor); may throw */
|
||||
void scr_rl_set_prompt(double id, ScrStr *prompt);
|
||||
ScrStr *scr_rl_get_prompt(double id); /* +1 */
|
||||
void scr_rl_set_prompt_dyn(double id, ScrDyn *prompt); /* borrows; may throw */
|
||||
ScrStr *scr_rl_get_prompt(double id); /* +1; may throw (a prompt that is not a string) */
|
||||
void scr_rl_write(double id, ScrStr *data); /* may throw ERR_USE_AFTER_CLOSE */
|
||||
void scr_rl_on(double id, double ev, ScrClosure *cb /*moves*/, void *fn, bool once);
|
||||
/* createInterface's history and editing options, in Node's order: the
|
||||
@@ -83,6 +92,8 @@ double scr_rl_num_prop(double id, double which);
|
||||
* when there is a completer (the inspect unit links only then). */
|
||||
void scr_rl_terminal(double id, double mode, bool on, ScrDyn *completer, bool promises,
|
||||
ScrStr *(*inspect)(ScrDyn *, double, double));
|
||||
void scr_rl_mark_promises(double id);
|
||||
bool scr_rl_is_promises(double id);
|
||||
bool scr_rl_is_terminal(double id);
|
||||
/* readline.cursorTo / moveCursor / clearLine / clearScreenDown: the data
|
||||
* written (+1; kind 0-3), after the argument checks (may throw), and
|
||||
@@ -135,6 +146,9 @@ void scr_rl_error_thunk_dyn(ScrClosure *cb, ScrError *err);
|
||||
* the input stream or NULL for process.stdin; out the output stream or
|
||||
* NULL (out_stdout picks process.stdout). Both borrowed, like prompt
|
||||
* (NULL: the default "> "). */
|
||||
/* A checked-dynamic input or output as its stream (+1; NULL for an output
|
||||
* of null or undefined); refuses other values (may throw). */
|
||||
ScrStream *scr_stream_rl_from_dyn(const ScrDyn *value, bool output);
|
||||
double scr_stream_rl_open(ScrStream *in, ScrStream *out, bool out_stdout, double crlf_delay,
|
||||
ScrStr *prompt, ScrDyn *signal, ScrAbortAddFn add);
|
||||
void scr_stdin_data_thunk0(ScrClosure *cb, ScrBytes *chunk);
|
||||
|
||||
@@ -56,11 +56,17 @@ typedef struct ScrRl {
|
||||
bool stdin_in; /* reads process.stdin through the shared consumer */
|
||||
bool dead; /* stdin only: created after stdin ended */
|
||||
bool paused;
|
||||
bool promises; /* created by node:readline/promises */
|
||||
const ScrRlIo *io; /* the stream side, for a stream input or output */
|
||||
void *in; /* the input stream (+1), or NULL */
|
||||
void *out; /* the output stream (+1), or NULL */
|
||||
bool out_stdout;
|
||||
ScrStr *prompt, *old_prompt;
|
||||
/* A prompt that is not a string (setPrompt or question with another
|
||||
* value), +1, or NULL; prompt then holds String(value) for a terminal
|
||||
* interface. Writing it the way Node's kWriteToOutput does throws
|
||||
* ERR_INVALID_ARG_TYPE. old_prompt_value is old_prompt's. */
|
||||
ScrDyn *prompt_value, *old_prompt_value;
|
||||
ScrClosure *q_cb; /* the pending question's callback (owned) */
|
||||
ScrRlLineFn q_fn;
|
||||
/* the last promise question (kQuestionReject), which the terminal's
|
||||
@@ -114,6 +120,14 @@ ScrArr *scr_rl_history_of(ScrRl *rl); /* borrowed; created on first use */
|
||||
void scr_rl_emit(ScrRl *rl, int ev, ScrStr *line, ScrError *err);
|
||||
void scr_rl_emit_history(ScrRl *rl);
|
||||
void scr_rl_write_out(ScrRl *rl, ScrStr *text);
|
||||
/* kWriteToOutput(prompt)'s validateString: false with ERR_INVALID_ARG_TYPE
|
||||
* pending when the prompt is not a string. */
|
||||
bool scr_rl_prompt_writable(ScrRl *rl);
|
||||
/* A prompt value whose String() the interface keeps in prompt. */
|
||||
static inline bool scr_rl_primitive_prompt(const ScrDyn *v) {
|
||||
return v->kind == SCR_DYN_NULL || v->kind == SCR_DYN_UNDEF || v->kind == SCR_DYN_BOOL ||
|
||||
v->kind == SCR_DYN_NUM || v->kind == SCR_DYN_BIGINT;
|
||||
}
|
||||
/* kOnLine: a pending question takes the line, else 'line'. Moves line. */
|
||||
void scr_rl_on_line(ScrRl *rl, ScrStr *line);
|
||||
void scr_rl_do_pause(ScrRl *rl);
|
||||
|
||||
@@ -2255,7 +2255,7 @@ static void tty_prompt(ScrRl *rl, bool preserve) {
|
||||
bool dumb = term && term->len == 4 && memcmp(term->data, "dumb", 4) == 0;
|
||||
if (term) scr_str_release(term);
|
||||
if (dumb) {
|
||||
scr_rl_write_out(rl, rl->prompt);
|
||||
if (scr_rl_prompt_writable(rl)) scr_rl_write_out(rl, rl->prompt);
|
||||
return;
|
||||
}
|
||||
if (!preserve) t->cursor = 0;
|
||||
@@ -2333,6 +2333,12 @@ double scr_rl_cursor_pos(double id, double which) {
|
||||
if (rl->tty) {
|
||||
pos = tty_cursor_pos(rl);
|
||||
} else {
|
||||
if (rl->prompt_value && !scr_rl_primitive_prompt(rl->prompt_value)) {
|
||||
static const char m[] =
|
||||
"rl.getCursorPos() with a prompt that is an object or a symbol has no lowering";
|
||||
scr_throw_error_msg_code(SCR_ERR_ERROR, m, sizeof m - 1, "SC2020");
|
||||
return 0;
|
||||
}
|
||||
ScrRlTty probe = {0};
|
||||
probe.columns = INFINITY;
|
||||
rl->tty = &probe;
|
||||
|
||||
@@ -4728,6 +4728,31 @@ static void scr_stream_rl_hooks_free(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* createInterface's input or output given as a checked-dynamic value (a
|
||||
* JavaScript function's result, an object's member): the stream (+1).
|
||||
* An output of null or undefined is NULL (no output). A value that is
|
||||
* not a stream with a readable (input) or writable (output) side is
|
||||
* refused. */
|
||||
ScrStream *scr_stream_rl_from_dyn(const ScrDyn *value, bool output) {
|
||||
if (output && (value->kind == SCR_DYN_NULL || value->kind == SCR_DYN_UNDEF)) return NULL;
|
||||
if (value->kind == SCR_DYN_TYPED_REF && value->v.typed_ref.type_key_len > 7 &&
|
||||
!memcmp(value->v.typed_ref.type_key, "object:", 7)) {
|
||||
ScrStream *stream = scr_dyn_typed_ref_unbox(value);
|
||||
const ScrVt *vt = stream->vt;
|
||||
bool duplex = vt == &scr_duplex_vt || vt == &scr_transform_vt || vt == &scr_passthrough_vt ||
|
||||
scr_vt_within(vt, &scr_duplex_vt);
|
||||
bool ok = duplex || (output ? vt == &scr_writable_vt || scr_vt_within(vt, &scr_writable_vt)
|
||||
: vt == &scr_readable_vt || scr_vt_within(vt, &scr_readable_vt));
|
||||
if (ok) return stream;
|
||||
value->v.typed_ref.release(stream);
|
||||
}
|
||||
static const char in_m[] = "createInterface with an input that is not a readable stream has no lowering";
|
||||
static const char out_m[] = "createInterface with an output that is not a writable stream has no lowering";
|
||||
if (output) scr_throw_error_msg_code(SCR_ERR_ERROR, out_m, sizeof out_m - 1, "SC2020");
|
||||
else scr_throw_error_msg_code(SCR_ERR_ERROR, in_m, sizeof in_m - 1, "SC2020");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double scr_stream_rl_open(ScrStream *in, ScrStream *out, bool out_stdout, double crlf_delay,
|
||||
ScrStr *prompt, ScrDyn *signal, ScrAbortAddFn add) {
|
||||
double id = scr_rl_open(&scr_stream_rl_io, in ? scr_stream_retain(in) : NULL,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// rl[Symbol.dispose]() closes the interface as close() does, and a second
|
||||
// call does nothing. instanceof tells node:readline's Interface from
|
||||
// node:readline/promises' one.
|
||||
import * as readline from "node:readline";
|
||||
import * as readlinePromises from "node:readline/promises";
|
||||
import { PassThrough } from "node:stream";
|
||||
|
||||
const rl = readline.createInterface({ input: new PassThrough() });
|
||||
rl.on("close", () => console.log("close 1"));
|
||||
rl[Symbol.dispose]();
|
||||
console.log("disposed");
|
||||
rl[Symbol.dispose]();
|
||||
rl.close();
|
||||
console.log("after second dispose");
|
||||
|
||||
const prl = readlinePromises.createInterface({ input: new PassThrough() });
|
||||
prl.on("close", () => console.log("close 2"));
|
||||
console.log(rl instanceof readline.Interface, rl instanceof readlinePromises.Interface);
|
||||
console.log(prl instanceof readline.Interface, prl instanceof readlinePromises.Interface);
|
||||
prl[Symbol.dispose]();
|
||||
|
||||
function make(): readline.Interface {
|
||||
return readline.createInterface({ input: new PassThrough() });
|
||||
}
|
||||
const made = make();
|
||||
made.on("close", () => console.log("close 3"));
|
||||
made[Symbol.dispose]();
|
||||
console.log("done");
|
||||
@@ -0,0 +1,51 @@
|
||||
// question() with a query that is not a string: a non-terminal interface
|
||||
// throws (callback form) or rejects (readline/promises) with Node's
|
||||
// ERR_INVALID_ARG_TYPE from the output write, and the question stays
|
||||
// pending; a terminal interface writes the query as a string.
|
||||
const readline = require("readline");
|
||||
const readlinePromises = require("readline/promises");
|
||||
const { PassThrough } = require("stream");
|
||||
|
||||
function capture(name) {
|
||||
const output = new PassThrough();
|
||||
output.on("data", (d) => console.log(name, JSON.stringify(String(d))));
|
||||
return output;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const input = new PassThrough();
|
||||
const rl = readline.createInterface({ input, output: capture("out") });
|
||||
rl.on("line", (line) => console.log("line", line));
|
||||
try {
|
||||
rl.question(42, (answer) => console.log("answer", answer));
|
||||
} catch (e) {
|
||||
console.log(e.name, e.code, e.message);
|
||||
}
|
||||
try {
|
||||
rl.prompt();
|
||||
} catch (e) {
|
||||
console.log("prompt", e.code, e.message);
|
||||
}
|
||||
input.write("first\nsecond\n");
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
console.log("prompt after", JSON.stringify(rl.getPrompt()));
|
||||
rl.close();
|
||||
|
||||
for (const query of [undefined, null, { a: 1 }, [1, 2], true]) {
|
||||
const prl = readlinePromises.createInterface({ input: new PassThrough() });
|
||||
try {
|
||||
await prl.question(query);
|
||||
} catch (e) {
|
||||
console.log("rejected", e.code, e.message);
|
||||
}
|
||||
prl.close();
|
||||
}
|
||||
|
||||
const tin = new PassThrough();
|
||||
const trl = readline.createInterface({ input: tin, output: capture("tty"), terminal: true });
|
||||
trl.question(7, (answer) => console.log("tty answer", answer));
|
||||
tin.write("x\r");
|
||||
await new Promise((resolve) => setImmediate(resolve));
|
||||
trl.close();
|
||||
}
|
||||
main();
|
||||
@@ -0,0 +1,41 @@
|
||||
// createInterface over an input or output that is a function's result,
|
||||
// an object's member or an array element.
|
||||
import * as readline from "node:readline";
|
||||
import * as readlinePromises from "node:readline/promises";
|
||||
import { PassThrough, Readable, Writable } from "node:stream";
|
||||
|
||||
function source(lines) {
|
||||
return Readable.from([lines.join("\n") + "\n"]);
|
||||
}
|
||||
const streams = { input: source(["a", "b"]), output: new PassThrough() };
|
||||
streams.output.on("data", (d) => console.log("out", JSON.stringify(String(d))));
|
||||
|
||||
const rl1 = readline.createInterface({ input: source(["one", "two"]) });
|
||||
for await (const line of rl1) console.log("rl1", line);
|
||||
|
||||
const rl2 = readline.createInterface({ input: streams.input, output: streams.output, prompt: "? " });
|
||||
rl2.prompt();
|
||||
for await (const line of rl2) console.log("rl2", line);
|
||||
|
||||
const inputs = [source(["x"]), source(["y"])];
|
||||
for (const [i, input] of inputs.entries()) {
|
||||
const rl = readline.createInterface(input);
|
||||
rl.on("line", (line) => console.log("rl3", i, line));
|
||||
await new Promise((resolve) => {
|
||||
rl.on("close", () => resolve(undefined));
|
||||
});
|
||||
}
|
||||
|
||||
const sink = new Writable({
|
||||
write(chunk, _encoding, callback) {
|
||||
console.log("sink", JSON.stringify(String(chunk)));
|
||||
callback();
|
||||
},
|
||||
});
|
||||
const make = () => ({ input: new PassThrough(), output: sink });
|
||||
const opts = make();
|
||||
const prl = readlinePromises.createInterface(opts.input, opts.output);
|
||||
const answer = prl.question("name? ");
|
||||
opts.input.write("ada\n");
|
||||
console.log("answer", await answer);
|
||||
prl.close();
|
||||
@@ -2129,6 +2129,9 @@
|
||||
"5873-readline-promises-namespace.cjs": 2023,
|
||||
"5874-worker-stdio-streams.ts": 2839,
|
||||
"5875-worker-stdout-pipe.cjs": 2740,
|
||||
"5876-readline-dispose-instanceof.ts": 2465,
|
||||
"5877-readline-question-query.cjs": 3233,
|
||||
"5878-readline-dynamic-streams.mjs": 2925,
|
||||
"5879-worker-stdin.ts": 3158,
|
||||
"5880-emitter-leak-warning-delivery.ts": 3418,
|
||||
"5881-inspect-weak-collections.ts": 2615,
|
||||
@@ -5218,6 +5221,9 @@
|
||||
"5873-readline-promises-namespace.cjs": 3811,
|
||||
"5874-worker-stdio-streams.ts": 27801,
|
||||
"5875-worker-stdout-pipe.cjs": 31391,
|
||||
"5876-readline-dispose-instanceof.ts": 29361,
|
||||
"5877-readline-question-query.cjs": 170940,
|
||||
"5878-readline-dynamic-streams.mjs": 25503,
|
||||
"5879-worker-stdin.ts": 33232,
|
||||
"5880-emitter-leak-warning-delivery.ts": 4319,
|
||||
"5881-inspect-weak-collections.ts": 2915,
|
||||
|
||||
Reference in New Issue
Block a user