mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-07 08:33:50 +08:00
Add readline/promises, stream.addAbortSignal and the stream/promises options (#983)
This commit is contained in:
@@ -52,6 +52,7 @@ A declaration in the standard library does not guarantee that an API compiles. U
|
||||
|
||||
- **Text encoding:** `TextEncoder` and `TextDecoder` can be stored in fields, arguments, return values, arrays, and closures. Encoding supports UTF-8, including `encodeInto` into a Uint8Array or Buffer view. Codec properties expose the canonical encoding and decoder flags. Decoding accepts recognized WHATWG labels, including runtime labels and omitted or undefined encodings. Literal constructor options support `fatal` and `ignoreBOM`; literal decode options support streaming UTF-8, UTF-16, and single-byte encodings. Other legacy streaming encodings, runtime options objects, inspection, JSON conversion, and module-scope declarations using `var` or lacking an initializer are unsupported. Use initialized `let` or `const` bindings for stored codecs.
|
||||
- **EventEmitter:** Literal event names use fixed argument tuples. Computed string names require a constant prefix or suffix that excludes `error`, meta events, and stream-internal events; their arguments must match the exact arity and support checked conversion. Unrestricted names, symbols, names that may reach internal events, and typed `listeners()`/`rawListeners()` results for names with possible computed registrations are unsupported.
|
||||
- **Streams and readline:** Stream chunks are Buffers or strings. Object-mode streams of other values, `Readable.from` over arrays of other values, the `Readable` iterator helpers (`map`, `filter`, `toArray`, and the others), `stream.compose`, and generator stages in `pipeline` are unsupported. `pipeline` from `node:stream/promises` accepts a trailing `{ signal, end }` object, and `finished` accepts `readable`, `writable`, `error`, `cleanup`, and `signal` options, with boolean literals for the flags. Stream constructors accept a `signal` option in an options object literal, and `stream.addAbortSignal` accepts any stream. `node:readline` and `node:readline/promises` interfaces read `process.stdin` or a readable stream without terminal mode: `question`, `prompt`, `write`, `pause`, `resume`, `'line'`/`'close'`/`'pause'`/`'resume'`/`'error'` listeners, and `for await` compile. Terminal mode, `completer`, history options, and chained listener registration are unsupported.
|
||||
- **CommonJS metadata:** The module graph is fixed at build time. Cache deletion or reloading, metadata writes, `module.paths` mutation, and `require.extensions` are unsupported. Read-only access to `module.id`, `filename`, `path`, `paths`, `loaded`, `isPreloading`, `parent`, `children`, `require.main`, and `require.cache` lookup/enumeration compiles.
|
||||
- **ESM metadata:** Native `import.meta.url`, `filename`, `dirname`, and `main` support dot access, statically known keys, and destructuring selected fields. Storing or mutating the metadata object, rest bindings, runtime keys, and stored `resolve` callbacks are unsupported. Native `import.meta.resolve()` requires a literal specifier. Module instances distinguished by URL query strings or fragments are unsupported.
|
||||
- **Date:** Date unions, year/month constructors, setters, statically typed TypeScript identity comparisons, thrown Date values, and locale/string formatters are unsupported. Zero-argument and single number/string constructors, storage and passing, `getTime`, `valueOf`, `toISOString`, calendar getters, `getTimezoneOffset`, and single-string `Date.parse` compile. JavaScript Dates retain identity through checked storage and support Date-copy construction and JSON serialization. See [Dates and timezones](#dates-and-timezones) for parsing restrictions.
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
"schemaVersion": 3,
|
||||
"nodeVersion": "24.15.0",
|
||||
"nodeCommit": "848430679556aed0bd073f2bc263331ad84fa119",
|
||||
"artifactVersion": "442e4b7745b74b802a16",
|
||||
"artifactVersion": "5bc8e72822dd7065df84",
|
||||
"rowCount": 3662
|
||||
}
|
||||
|
||||
@@ -599,9 +599,9 @@
|
||||
"apiEntries": 43,
|
||||
"static": {
|
||||
"supported": 0,
|
||||
"partial": 4,
|
||||
"partial": 21,
|
||||
"refused": 0,
|
||||
"not-implemented": 39,
|
||||
"not-implemented": 22,
|
||||
"by-design": 0,
|
||||
"unreviewed": 0,
|
||||
"not-applicable": 0
|
||||
@@ -644,9 +644,9 @@
|
||||
"apiEntries": 137,
|
||||
"static": {
|
||||
"supported": 0,
|
||||
"partial": 36,
|
||||
"partial": 38,
|
||||
"refused": 0,
|
||||
"not-implemented": 101,
|
||||
"not-implemented": 99,
|
||||
"by-design": 0,
|
||||
"unreviewed": 0,
|
||||
"not-applicable": 0
|
||||
@@ -55258,9 +55258,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55281,9 +55281,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55465,9 +55465,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55511,9 +55511,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55534,9 +55534,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55764,9 +55764,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55787,9 +55787,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55810,9 +55810,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55833,9 +55833,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55856,9 +55856,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55879,9 +55879,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55902,9 +55902,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -55994,9 +55994,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -56017,9 +56017,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -56040,9 +56040,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -56086,9 +56086,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -56109,9 +56109,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -58110,9 +58110,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented static async-iteration subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -58593,9 +58593,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented static async-iteration subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"tiers": {
|
||||
"static": {
|
||||
"replace-refusal": 40,
|
||||
"verify-gap": 2296,
|
||||
"audit-partial": 852,
|
||||
"verify-gap": 2277,
|
||||
"audit-partial": 871,
|
||||
"classify": 2,
|
||||
"implement": 80
|
||||
}
|
||||
@@ -54988,12 +54988,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.readlinePromises.Interface",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55013,12 +55018,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl.question",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55213,12 +55223,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.readlinePromises.createInterface",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55263,12 +55278,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.readline.Interface",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55288,12 +55308,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl.question",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55523,7 +55548,10 @@
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl",
|
||||
"tests": [
|
||||
"tests/corpus/2844-node-disposable-resources.ts"
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -55544,12 +55572,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl.pause",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55569,12 +55602,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl.prompt",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55594,12 +55632,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl.resume",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55619,12 +55662,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl.setPrompt",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55644,12 +55692,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl.getPrompt",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55669,12 +55722,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl.write",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55694,12 +55752,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.rl",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55794,12 +55857,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.'close'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55819,12 +55887,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.'error'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55844,12 +55917,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.'line'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55894,12 +55972,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.'pause'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -55919,12 +56002,17 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:readline",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:readline.'resume'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -57552,7 +57640,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -57581,7 +57671,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -57610,7 +57702,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -57639,7 +57733,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -57668,7 +57764,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -57697,7 +57795,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58043,12 +58143,15 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:stream",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:stream.stream.addAbortSignal",
|
||||
"tests": [
|
||||
"tests/corpus/5300-stream-add-abort-signal.ts",
|
||||
"tests/corpus/5301-stream-add-abort-signal-errors.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -58076,7 +58179,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58130,7 +58235,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58159,7 +58266,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58188,7 +58297,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58217,7 +58328,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58246,7 +58359,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58592,12 +58707,15 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:stream",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:stream.stream.addAbortSignal",
|
||||
"tests": [
|
||||
"tests/corpus/5300-stream-add-abort-signal.ts",
|
||||
"tests/corpus/5301-stream-add-abort-signal-errors.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -58625,7 +58743,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58704,7 +58824,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58760,7 +58882,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58789,7 +58913,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58818,7 +58944,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -58872,7 +59000,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59381,7 +59511,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59410,7 +59542,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59439,7 +59573,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59468,7 +59604,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59497,7 +59635,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59526,7 +59666,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59555,7 +59697,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59584,7 +59728,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -59613,7 +59759,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -60848,7 +60996,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -61002,7 +61152,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -61031,7 +61183,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,9 +626,9 @@
|
||||
"apiEntries": 43,
|
||||
"static": {
|
||||
"supported": 0,
|
||||
"partial": 4,
|
||||
"partial": 21,
|
||||
"refused": 0,
|
||||
"not-implemented": 39,
|
||||
"not-implemented": 22,
|
||||
"by-design": 0,
|
||||
"unreviewed": 0,
|
||||
"not-applicable": 0
|
||||
@@ -701,9 +701,9 @@
|
||||
"apiEntries": 137,
|
||||
"static": {
|
||||
"supported": 0,
|
||||
"partial": 36,
|
||||
"partial": 38,
|
||||
"refused": 0,
|
||||
"not-implemented": 101,
|
||||
"not-implemented": 99,
|
||||
"by-design": 0,
|
||||
"unreviewed": 0,
|
||||
"not-applicable": 0
|
||||
@@ -75896,7 +75896,11 @@
|
||||
"evidence": "derived:descendants",
|
||||
"tests": [
|
||||
"tests/corpus/1475-readline-closed-stdin.ts",
|
||||
"tests/corpus/2844-node-disposable-resources.ts"
|
||||
"tests/corpus/5295-readline-promises-question.ts",
|
||||
"tests/corpus/5296-readline-lines.ts",
|
||||
"tests/corpus/5297-readline-callback-events.cjs",
|
||||
"tests/corpus/5298-readline-abort.cjs",
|
||||
"tests/corpus/5299-readline-stdin.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "chapter"
|
||||
@@ -75942,8 +75946,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.readlinePromises.Interface",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -75965,8 +75975,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl.question",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76149,8 +76165,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.readlinePromises.createInterface",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76218,8 +76240,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.readline.Interface",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76241,8 +76269,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl.question",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76480,7 +76514,10 @@
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl",
|
||||
"tests": [
|
||||
"tests/corpus/2844-node-disposable-resources.ts"
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -76503,8 +76540,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl.pause",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76526,8 +76569,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl.prompt",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76549,8 +76598,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl.resume",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76572,8 +76627,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl.setPrompt",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76595,8 +76656,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl.getPrompt",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76618,8 +76685,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl.write",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76641,8 +76714,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.rl",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76733,8 +76812,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.'close'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76756,8 +76841,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.'error'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76779,8 +76870,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.'line'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76825,8 +76922,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.'pause'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -76848,8 +76951,14 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:readline"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:readline.'resume'",
|
||||
"tests": [
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -79642,7 +79751,11 @@
|
||||
"tests/corpus/2893-child-stdin-duplex.ts",
|
||||
"tests/corpus/2894-child-stdin-backpressure.ts",
|
||||
"tests/corpus/2895-child-stdin-write-after-end.ts",
|
||||
"tests/corpus/2897-child-stdin-destroy.ts"
|
||||
"tests/corpus/2897-child-stdin-destroy.ts",
|
||||
"tests/corpus/5300-stream-add-abort-signal.ts",
|
||||
"tests/corpus/5301-stream-add-abort-signal-errors.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "chapter"
|
||||
@@ -79762,7 +79875,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -79790,7 +79905,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -79818,7 +79935,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -79869,7 +79988,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -79897,7 +80018,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -79925,7 +80048,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -80247,8 +80372,12 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:stream"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:stream.stream.addAbortSignal",
|
||||
"tests": [
|
||||
"tests/corpus/5300-stream-add-abort-signal.ts",
|
||||
"tests/corpus/5301-stream-add-abort-signal-errors.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -80275,7 +80404,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -80326,7 +80457,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -80354,7 +80487,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -80405,7 +80540,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
@@ -80433,7 +80570,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
@@ -80461,7 +80600,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
@@ -80783,8 +80924,12 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:stream"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:stream.stream.addAbortSignal",
|
||||
"tests": [
|
||||
"tests/corpus/5300-stream-add-abort-signal.ts",
|
||||
"tests/corpus/5301-stream-add-abort-signal-errors.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -80811,7 +80956,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
@@ -80908,7 +81055,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -80962,7 +81111,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -80990,7 +81141,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81018,7 +81171,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81069,7 +81224,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81633,7 +81790,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81661,7 +81820,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81689,7 +81850,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81717,7 +81880,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81745,7 +81910,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81773,7 +81940,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81801,7 +81970,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81829,7 +82000,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -81857,7 +82030,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83204,7 +83379,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
@@ -83462,7 +83639,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
@@ -83490,7 +83669,9 @@
|
||||
"tests": [
|
||||
"tests/corpus/1685-stream-readable-basics.ts",
|
||||
"tests/corpus/1688-stream-writable-basics.ts",
|
||||
"tests/corpus/1814-stream-pipeline.cjs"
|
||||
"tests/corpus/1814-stream-pipeline.cjs",
|
||||
"tests/corpus/5302-stream-promises-options.ts",
|
||||
"tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
|
||||
@@ -170,6 +170,9 @@
|
||||
{"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"]},
|
||||
{"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": "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": "worker_threads", "symbols": ["Worker"], "status": "partial", "evidence": ["tests/corpus/native-worker-messages.ts", "tests/corpus/native-worker-commonjs.cjs", "tests/corpus/native-worker-environment.ts", "tests/corpus/native-worker-roots/main.ts"]},
|
||||
{"chapter": "worker_threads", "symbols": ["MessagePort", "parentPort", "worker_threads.workerData", "isMainThread", "threadId"], "status": "partial", "evidence": ["tests/corpus/native-worker-commonjs.cjs", "tests/corpus/native-worker-port-lifetime.ts", "tests/corpus/native-worker-references.ts"]},
|
||||
{"chapter": "worker_threads", "symbols": ["worker.postMessage", "port.postMessage"], "status": "partial", "evidence": ["tests/corpus/native-worker-message-graphs.ts", "tests/corpus/native-worker-shared-views.ts", "tests/corpus/native-worker-clone-errors.ts", "tests/corpus/native-worker-delayed-messages.ts"]},
|
||||
@@ -314,7 +317,7 @@
|
||||
{ "symbols": ["AsyncLocalStorage", "asyncLocalStorage.run", "asyncLocalStorage.exit", "asyncLocalStorage.getStore", "asyncLocalStorage.enterWith", "asyncLocalStorage.disable"], "status": "partial", "evidence": ["tests/corpus/2213-async-local-storage.cjs"] },
|
||||
{ "symbols": ["StringDecoder", "stringDecoder.write", "stringDecoder.end"], "status": "partial", "evidence": ["tests/corpus/1662-string-decoder-encodings.ts", "tests/corpus/node24-string-decoder.ts"] },
|
||||
{ "symbols": ["setImmediate", "setInterval", "setTimeout", "clearImmediate", "clearInterval", "clearTimeout", "timersPromises.setTimeout", "timersPromises.setImmediate", "timersPromises.setInterval", "immediate.hasRef", "immediate.ref", "immediate.unref", "timeout.hasRef", "timeout.ref", "timeout.unref", "timeout.refresh"], "status": "partial", "evidence": ["tests/corpus/1804-timers-module.ts", "tests/corpus/1805-timer-callback-args.ts", "tests/corpus/2093-timers-promises.ts", "tests/corpus/2852-timers-promises-interval.ts"] },
|
||||
{ "symbols": ["stream.finished", "stream.pipeline", "stream.getDefaultHighWaterMark", "readable.push", "readable.unshift", "readable.read", "readable.pause", "readable.resume", "readable.isPaused", "readable.setEncoding", "readable.pipe", "readable.unpipe", "readable.destroy", "writable.write", "writable.end", "writable.cork", "writable.uncork", "writable.setDefaultEncoding"], "status": "partial", "evidence": ["tests/corpus/1685-stream-readable-basics.ts", "tests/corpus/1688-stream-writable-basics.ts", "tests/corpus/1814-stream-pipeline.cjs"] },
|
||||
{ "symbols": ["stream.finished", "stream.pipeline", "stream.getDefaultHighWaterMark", "readable.push", "readable.unshift", "readable.read", "readable.pause", "readable.resume", "readable.isPaused", "readable.setEncoding", "readable.pipe", "readable.unpipe", "readable.destroy", "writable.write", "writable.end", "writable.cork", "writable.uncork", "writable.setDefaultEncoding"], "status": "partial", "evidence": ["tests/corpus/1685-stream-readable-basics.ts", "tests/corpus/1688-stream-writable-basics.ts", "tests/corpus/1814-stream-pipeline.cjs", "tests/corpus/5302-stream-promises-options.ts", "tests/corpus/5303-stream-finished-callback-opts.cjs"] },
|
||||
{ "symbols": ["net.createServer", "net.connect", "net.createConnection", "net.getDefaultAutoSelectFamilyAttemptTimeout", "net.setDefaultAutoSelectFamilyAttemptTimeout", "server.address", "server.close", "server.listen", "socket.destroy", "socket.end", "socket.pause", "socket.resume", "socket.setEncoding", "socket.setTimeout", "socket.write"], "status": "partial", "evidence": ["tests/corpus/2500-net-autosel-timeout.ts", "tests/corpus/2596-net-arg-ladders.cjs"] },
|
||||
{ "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": ["readline.createInterface", "rl.close"], "status": "partial", "evidence": ["tests/corpus/1475-readline-closed-stdin.ts"] },
|
||||
|
||||
+106
-16
@@ -3033,29 +3033,82 @@ declare module "node:querystring" {
|
||||
* on("close", cb). An open interface keeps the process alive until
|
||||
* close()/EOF, Node's semantics; lines split on \n and \r\n. */
|
||||
declare module "readline" {
|
||||
/* The non-terminal Interface over process.stdin or a stream input
|
||||
* (scr_readline.c). Listener registrations are statements: chaining
|
||||
* on()/once() results has no lowering. */
|
||||
export interface Interface extends Disposable {
|
||||
question(query: string, callback: (answer: string) => void): void;
|
||||
close(): void;
|
||||
on(event: "close", listener: () => void): void;
|
||||
pause(): this;
|
||||
resume(): this;
|
||||
prompt(preserveCursor?: boolean): void;
|
||||
setPrompt(prompt: string): void;
|
||||
getPrompt(): string;
|
||||
write(data: string): void;
|
||||
on(event: "line", listener: (input: string) => void): this;
|
||||
on(event: "close" | "pause" | "resume", listener: () => void): this;
|
||||
on(event: "error", listener: (err: Error) => void): this;
|
||||
once(event: "line", listener: (input: string) => void): this;
|
||||
once(event: "close" | "pause" | "resume", listener: () => void): this;
|
||||
once(event: "error", listener: (err: Error) => void): this;
|
||||
addListener(event: "line", listener: (input: string) => void): this;
|
||||
addListener(event: "close" | "pause" | "resume", listener: () => void): this;
|
||||
addListener(event: "error", listener: (err: Error) => void): this;
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<string>;
|
||||
[Symbol.dispose](): void;
|
||||
}
|
||||
export function createInterface(options: {
|
||||
export interface ReadLineOptions {
|
||||
input: unknown;
|
||||
output?: unknown;
|
||||
terminal?: boolean;
|
||||
/* crlfDelay: Infinity is accepted — the lowered splitter already
|
||||
* holds a trailing \r for the next chunk, Node's Infinity behavior;
|
||||
* completer and the history knobs fence by name (no interactive
|
||||
* terminal). The options-record stance (see http.RequestOptions)
|
||||
* admits every key; the walk decides. */
|
||||
/* Node's crlfDelay: a \n arriving within this many ms (at least 100)
|
||||
* after a line ended by \r is dropped. completer and the history
|
||||
* knobs fence by name (no interactive terminal). The options-record
|
||||
* stance (see http.RequestOptions) admits every key; the walk
|
||||
* decides. */
|
||||
crlfDelay?: number;
|
||||
completer?: unknown;
|
||||
signal?: AbortSignal;
|
||||
[option: string]: unknown;
|
||||
}): Interface;
|
||||
}
|
||||
export function createInterface(options: ReadLineOptions): Interface;
|
||||
export function createInterface(input: unknown, output?: unknown): Interface;
|
||||
}
|
||||
declare module "node:readline" {
|
||||
export * from "readline";
|
||||
}
|
||||
declare module "readline/promises" {
|
||||
import { ReadLineOptions } from "readline";
|
||||
/* 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 {
|
||||
question(query: string, options?: { signal?: AbortSignal }): Promise<string>;
|
||||
close(): void;
|
||||
pause(): this;
|
||||
resume(): this;
|
||||
prompt(preserveCursor?: boolean): void;
|
||||
setPrompt(prompt: string): void;
|
||||
getPrompt(): string;
|
||||
write(data: string): void;
|
||||
on(event: "line", listener: (input: string) => void): this;
|
||||
on(event: "close" | "pause" | "resume", listener: () => void): this;
|
||||
on(event: "error", listener: (err: Error) => void): this;
|
||||
once(event: "line", listener: (input: string) => void): this;
|
||||
once(event: "close" | "pause" | "resume", listener: () => void): this;
|
||||
once(event: "error", listener: (err: Error) => void): this;
|
||||
addListener(event: "line", listener: (input: string) => void): this;
|
||||
addListener(event: "close" | "pause" | "resume", listener: () => void): this;
|
||||
addListener(event: "error", listener: (err: Error) => void): this;
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<string>;
|
||||
[Symbol.dispose](): void;
|
||||
}
|
||||
export function createInterface(options: ReadLineOptions): Interface;
|
||||
export function createInterface(input: unknown, output?: unknown): Interface;
|
||||
}
|
||||
declare module "node:readline/promises" {
|
||||
export * from "readline/promises";
|
||||
}
|
||||
|
||||
/* node:zlib — the one-shot zlib/raw/gzip codecs lower for string/Buffer
|
||||
* inputs with Node's default options; libz links only into zlib-using
|
||||
@@ -4731,7 +4784,7 @@ declare module "stream" {
|
||||
emitClose?: boolean;
|
||||
captureRejections?: boolean;
|
||||
defaultEncoding?: string;
|
||||
signal?: unknown;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
interface WritableOptions {
|
||||
highWaterMark?: number;
|
||||
@@ -4751,7 +4804,7 @@ declare module "stream" {
|
||||
autoDestroy?: boolean;
|
||||
emitClose?: boolean;
|
||||
captureRejections?: boolean;
|
||||
signal?: unknown;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
interface DuplexOptions {
|
||||
highWaterMark?: number;
|
||||
@@ -4780,7 +4833,7 @@ declare module "stream" {
|
||||
writable?: boolean;
|
||||
defaultEncoding?: string;
|
||||
writev?(this: Duplex, chunks: unknown[], callback: (error?: Error | null) => void): void;
|
||||
signal?: unknown;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
interface TransformOptions {
|
||||
highWaterMark?: number;
|
||||
@@ -4816,7 +4869,7 @@ declare module "stream" {
|
||||
readable?: boolean;
|
||||
writable?: boolean;
|
||||
defaultEncoding?: string;
|
||||
signal?: unknown;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
|
||||
class Readable extends EventEmitter {
|
||||
@@ -5060,15 +5113,34 @@ declare module "stream" {
|
||||
constructor(opts?: TransformOptions);
|
||||
}
|
||||
|
||||
/* addAbortSignal(signal, stream): the stream, destroyed with an
|
||||
* AbortError (cause: signal.reason) when the signal aborts. */
|
||||
function addAbortSignal<T extends Readable | Writable>(signal: AbortSignal, stream: T): T;
|
||||
/* Declared so requires typecheck; each fences at its use site. */
|
||||
function pipeline(...streams: unknown[]): unknown;
|
||||
/* finished's options: readable/writable choose which ends count,
|
||||
* error: false ignores 'error' events, signal aborts the wait. */
|
||||
interface FinishedOptions {
|
||||
readable?: boolean;
|
||||
writable?: boolean;
|
||||
error?: boolean;
|
||||
signal?: AbortSignal;
|
||||
}
|
||||
function finished(stream: unknown, callback: (err?: Error | null) => void): () => void;
|
||||
function finished(
|
||||
stream: unknown,
|
||||
options: FinishedOptions | null,
|
||||
callback: (err?: Error | null) => void,
|
||||
): () => void;
|
||||
/* Folds to the TARGET platform's default at compile time (win32 16384,
|
||||
* else 65536 — Node's own state.js split); literal argument only. */
|
||||
function getDefaultHighWaterMark(objectMode: boolean): number;
|
||||
namespace promises {
|
||||
function pipeline(...streams: unknown[]): Promise<void>;
|
||||
function finished(stream: unknown): Promise<void>;
|
||||
function finished(
|
||||
stream: unknown,
|
||||
options?: FinishedOptions & { cleanup?: boolean },
|
||||
): Promise<void>;
|
||||
}
|
||||
class Stream extends EventEmitter {}
|
||||
|
||||
@@ -5079,12 +5151,19 @@ declare module "stream" {
|
||||
Transform,
|
||||
PassThrough,
|
||||
Stream,
|
||||
addAbortSignal,
|
||||
pipeline,
|
||||
finished,
|
||||
getDefaultHighWaterMark,
|
||||
promises,
|
||||
};
|
||||
export type { ReadableOptions, WritableOptions, DuplexOptions, TransformOptions };
|
||||
export type {
|
||||
ReadableOptions,
|
||||
WritableOptions,
|
||||
DuplexOptions,
|
||||
TransformOptions,
|
||||
FinishedOptions,
|
||||
};
|
||||
}
|
||||
declare module "node:stream" {
|
||||
import stream = require("stream");
|
||||
@@ -5094,9 +5173,20 @@ declare module "stream/promises" {
|
||||
/* The promise forms of finished/pipeline: a void promise the stream's
|
||||
* terminal point settles (rejected with the error, or
|
||||
* ERR_STREAM_PREMATURE_CLOSE on an early close). Stream arguments are
|
||||
* the lowered surface — iterables/generators/functions fence per site. */
|
||||
* the lowered surface — iterables/generators/functions fence per site.
|
||||
* pipeline's trailing { signal, end } object and finished's options
|
||||
* (readable, writable, error, cleanup, signal) are supported. */
|
||||
function pipeline(...streams: unknown[]): Promise<void>;
|
||||
function finished(stream: unknown): Promise<void>;
|
||||
function finished(
|
||||
stream: unknown,
|
||||
options?: {
|
||||
readable?: boolean;
|
||||
writable?: boolean;
|
||||
error?: boolean;
|
||||
cleanup?: boolean;
|
||||
signal?: AbortSignal;
|
||||
} | null,
|
||||
): Promise<void>;
|
||||
export { pipeline, finished };
|
||||
}
|
||||
declare module "node:stream/promises" {
|
||||
|
||||
@@ -36,13 +36,43 @@ export function emitAssertInspectLibCall(host: LlvmEmitterContext, e: LibCallExp
|
||||
|
||||
export function emitIoLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue {
|
||||
const B = host.B;
|
||||
if (e.fn === "rl.create") {
|
||||
if (e.fn === "rl.create" || e.fn === "rl.createSignal") {
|
||||
// readline interface handles are runtime IDs (doubles); an open
|
||||
// interface holds the loop.
|
||||
// interface on stdin holds the loop. The mode literal says which
|
||||
// optional arguments follow (lowerReadlineCreateCall): a stream input
|
||||
// or output goes through scr_stream.c's bridge.
|
||||
host.usesTimers = true;
|
||||
host.declare(`declare double @scr_rl_create()`);
|
||||
const modeArg = e.args[0]!;
|
||||
if (modeArg.kind !== "numLit") {
|
||||
throw new InternalCompilerError("llvm emitter bug: rl.create mode is not a literal");
|
||||
}
|
||||
const mode = modeArg.value;
|
||||
let i = 1;
|
||||
const input = mode & 1 ? host.emitExpr(e.args[i++]!).name : "null";
|
||||
const output = mode & 2 ? host.emitExpr(e.args[i++]!).name : "null";
|
||||
const crlf = host.emitExpr(e.args[i++]!).name;
|
||||
const prompt = mode & 16 ? host.emitExpr(e.args[i++]!).name : "null";
|
||||
const signal = mode & 8 ? host.emitExpr(e.args[i++]!).name : "null";
|
||||
let add = "null";
|
||||
if (mode & 8) {
|
||||
host.declare(`declare ptr @scr_events_add_abort_listener(ptr, ptr)`);
|
||||
add = "@scr_events_add_abort_listener";
|
||||
}
|
||||
const stdout = mode & 4 ? "true" : "false";
|
||||
const t = B.tmp();
|
||||
B.line(`${t} = call double @scr_rl_create()`);
|
||||
if (mode & 3) {
|
||||
host.declare(`declare double @scr_stream_rl_open(ptr, ptr, i1, double, ptr, ptr, ptr)`);
|
||||
B.line(
|
||||
`${t} = call double @scr_stream_rl_open(ptr ${input}, ptr ${output}, i1 ${stdout}, double ${crlf}, ptr ${prompt}, ptr ${signal}, ptr ${add})`,
|
||||
);
|
||||
} else {
|
||||
host.declare(`declare double @scr_rl_open(ptr, ptr, ptr, i1, double, ptr, ptr, ptr)`);
|
||||
B.line(
|
||||
`${t} = call double @scr_rl_open(ptr null, ptr null, ptr null, i1 ${stdout}, double ${crlf}, ptr ${prompt}, ptr ${signal}, ptr ${add})`,
|
||||
);
|
||||
}
|
||||
// A signal that is not an AbortSignal throws ERR_INVALID_ARG_TYPE.
|
||||
if (mode & 8) host.emitPendingCheck();
|
||||
return { name: t, type: e.type };
|
||||
}
|
||||
if (e.fn === "rl.question") {
|
||||
@@ -54,7 +84,12 @@ export function emitIoLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue
|
||||
throw new InternalCompilerError("llvm emitter bug: rl.question callback not a func");
|
||||
const args = e.args.map((a) => host.emitExpr(a));
|
||||
host.moveTemp(args[2]!);
|
||||
const adapter = cbT.params.length === 0 ? "scr_rl_answer_thunk0" : "scr_rl_answer_thunk_str";
|
||||
const adapter =
|
||||
cbT.params.length === 0
|
||||
? "scr_rl_answer_thunk0"
|
||||
: cbT.params[0]!.kind === "dyn"
|
||||
? "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)`);
|
||||
B.line(
|
||||
@@ -63,20 +98,53 @@ export function emitIoLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue
|
||||
host.emitPendingCheck();
|
||||
return { name: "", type: e.type };
|
||||
}
|
||||
if (e.fn === "rl.close") {
|
||||
const id = host.emitExpr(e.args[0]!);
|
||||
host.declare(`declare void @scr_rl_close(double)`);
|
||||
B.line(`call void @scr_rl_close(double ${id.name})`);
|
||||
return { name: "", type: e.type };
|
||||
}
|
||||
if (e.fn === "rl.onClose") {
|
||||
// The close listener MOVES into the interface's registry.
|
||||
if (e.fn === "rl.questionAsync" || e.fn === "rl.questionAsyncSignal") {
|
||||
// The promise question never throws: closed interfaces and aborted
|
||||
// signals reject the answered promise (+1).
|
||||
host.usesTimers = true;
|
||||
const args = e.args.map((a) => host.emitExpr(a));
|
||||
let signal = "null";
|
||||
let add = "null";
|
||||
if (e.fn === "rl.questionAsyncSignal") {
|
||||
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)`);
|
||||
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})`,
|
||||
);
|
||||
return host.own({ name: t, type: e.type });
|
||||
}
|
||||
if (e.fn === "rl.on") {
|
||||
// The listener MOVES into the interface's registry; the adapter is
|
||||
// picked by event and parameter count ('line': the text, 'error': the
|
||||
// error, the rest none).
|
||||
host.usesTimers = true;
|
||||
const cbT = e.args[2]!.type;
|
||||
const evArg = e.args[1]!;
|
||||
if (cbT.kind !== "func" || evArg.kind !== "numLit") {
|
||||
throw new InternalCompilerError("llvm emitter bug: rl.on operands");
|
||||
}
|
||||
const id = host.emitExpr(e.args[0]!);
|
||||
const cb = host.emitExpr(e.args[1]!);
|
||||
const ev = host.emitExpr(evArg);
|
||||
const cb = host.emitExpr(e.args[2]!);
|
||||
const once = host.emitExpr(e.args[3]!);
|
||||
host.moveTemp(cb);
|
||||
host.declare(`declare void @scr_rl_on_close(double, ptr)`);
|
||||
B.line(`call void @scr_rl_on_close(double ${id.name}, ptr ${cb.name})`);
|
||||
let adapter = "null";
|
||||
if (evArg.value === 0 || evArg.value === 4) {
|
||||
const param = cbT.params[0];
|
||||
const kind =
|
||||
param === undefined ? "0" : param.kind === "dyn" ? "_dyn" : evArg.value === 0 ? "_str" : "";
|
||||
const name = evArg.value === 0 ? `scr_rl_answer_thunk${kind}` : `scr_rl_error_thunk${kind}`;
|
||||
host.declare(`declare void @${name}(ptr, ptr)`);
|
||||
adapter = `@${name}`;
|
||||
}
|
||||
host.declare(`declare void @scr_rl_on(double, double, ptr, ptr, i1)`);
|
||||
B.line(
|
||||
`call void @scr_rl_on(double ${id.name}, double ${ev.name}, ptr ${cb.name}, ptr ${adapter}, i1 ${once.name})`,
|
||||
);
|
||||
return { name: "", type: e.type };
|
||||
}
|
||||
return host.emitGenericLibCall(e);
|
||||
@@ -315,7 +383,12 @@ export function emitGenericLibCall(host: LlvmEmitterContext, e: LibCallExpr): Ll
|
||||
export function emitLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue {
|
||||
if (USES_TIMERS_LIB_FNS.has(e.fn)) host.usesTimers = true;
|
||||
if (e.fn === "process.nextTick") return host.emitAsyncContextLibCall(e);
|
||||
if (e.fn === "sp.pipeline") return host.emitStreamLibCall(e);
|
||||
if (
|
||||
e.fn === "sp.pipeline" ||
|
||||
e.fn.startsWith("sp.pipelineOpts") ||
|
||||
e.fn.startsWith("sp.finishedOpts")
|
||||
)
|
||||
return host.emitStreamLibCall(e);
|
||||
if (e.fn === "bigint.parse" && e.args[0]?.kind === "strLit") {
|
||||
// Literals (`123n`) and constant BigInt("...") texts are immortal
|
||||
// statics instead of a parse at every evaluation.
|
||||
|
||||
@@ -853,6 +853,18 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"readable.resume": "scr_stream_resume",
|
||||
"readable.isPaused": "scr_stream_is_paused",
|
||||
"readable.setEncoding": "scr_stream_set_encoding",
|
||||
// node:readline's plain method calls (scr_readline.c): borrowed
|
||||
// operands; getPrompt answers +1, iterNext a +1 promise.
|
||||
"rl.close": "scr_rl_close",
|
||||
"rl.pause": "scr_rl_pause",
|
||||
"rl.resume": "scr_rl_resume",
|
||||
"rl.prompt": "scr_rl_prompt",
|
||||
"rl.setPrompt": "scr_rl_set_prompt",
|
||||
"rl.getPrompt": "scr_rl_get_prompt",
|
||||
"rl.write": "scr_rl_write",
|
||||
"rl.iterOpen": "scr_rl_iter_open",
|
||||
"rl.iterNext": "scr_rl_iter_next",
|
||||
"rl.iterReturn": "scr_rl_iter_return",
|
||||
"readable.nextChunk": "scr_stream_next_chunk",
|
||||
"readable.nextChunkDyn": "scr_stream_next_chunk_dyn",
|
||||
"readable.fromArr": "scr_stream_from_arr",
|
||||
@@ -1242,6 +1254,12 @@ export const USES_TIMERS_LIB_FNS = new Set<string>([
|
||||
"stream.pipelineDyn",
|
||||
"sp.finished",
|
||||
"sp.pipeline",
|
||||
"sp.pipelineOpts",
|
||||
"sp.pipelineOptsSignal",
|
||||
"sp.finishedOpts",
|
||||
"sp.finishedOptsSignal",
|
||||
"stream.finishedOpts",
|
||||
"stream.finishedOptsSignal",
|
||||
"sc.text",
|
||||
"sc.json",
|
||||
"sc.buffer",
|
||||
|
||||
@@ -13,6 +13,29 @@ const STREAM_CANONICAL_CBS: Record<string, ("r" | "w" | "f" | "d" | "t" | "l")[]
|
||||
|
||||
export function emitStreamLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue {
|
||||
const B = host.B;
|
||||
if (e.fn === "stream.addAbortSignal" || e.fn === "stream.addAbortSignalFn") {
|
||||
// stream.addAbortSignal: [stream, signal] — the constructors' signal
|
||||
// option chained over the fresh stream (a falsy signal is ignored);
|
||||
// stream.addAbortSignalFn: [signal, stream] — the module function, in
|
||||
// source order. Both answer the stream +1, or NULL with
|
||||
// ERR_INVALID_ARG_TYPE pending; addAbortListener comes from the
|
||||
// event-target unit the call links.
|
||||
host.usesTimers = true;
|
||||
const option = e.fn === "stream.addAbortSignal";
|
||||
const first = host.emitExpr(e.args[0]!);
|
||||
const second = host.emitExpr(e.args[1]!);
|
||||
const stream = option ? first : second;
|
||||
const signal = option ? second : first;
|
||||
host.declare(`declare ptr @scr_events_add_abort_listener(ptr, ptr)`);
|
||||
host.declare(`declare ptr @scr_stream_add_abort_signal(ptr, ptr, i1, ptr)`);
|
||||
const t = B.tmp();
|
||||
B.line(
|
||||
`${t} = call ptr @scr_stream_add_abort_signal(ptr ${stream.name}, ptr ${signal.name}, i1 ${option}, ptr @scr_events_add_abort_listener)`,
|
||||
);
|
||||
const out = host.own({ name: t, type: e.type });
|
||||
host.emitPendingCheck();
|
||||
return out;
|
||||
}
|
||||
if (e.fn === "stream.onData") {
|
||||
// The callback MOVES into the stream's listener registry; the
|
||||
// adapter is per callback shape — runtime-provided for the
|
||||
@@ -500,6 +523,95 @@ export function emitStreamLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlV
|
||||
host.emitPendingCheck();
|
||||
return out;
|
||||
}
|
||||
if (e.fn === "sp.pipelineOpts" || e.fn === "sp.pipelineOptsSignal") {
|
||||
// pipeline(count, s1..sn, end[, signal]) settling a void promise.
|
||||
const countArg = e.args[0]!;
|
||||
if (countArg.kind !== "numLit")
|
||||
throw new InternalCompilerError(`llvm emitter bug: ${e.fn} count not a literal`);
|
||||
const n = countArg.value;
|
||||
const args = e.args.map((a) => host.emitExpr(a));
|
||||
const arr = B.slot();
|
||||
B.entryAllocas.push(`${arr} = alloca [${n} x ptr]`);
|
||||
for (let i = 0; i < n; i++) {
|
||||
const p = B.tmp();
|
||||
B.line(`${p} = getelementptr inbounds [${n} x ptr], ptr ${arr}, i64 0, i64 ${i}`);
|
||||
B.line(`store ptr ${args[1 + i]!.name}, ptr ${p}`);
|
||||
}
|
||||
const end = args[1 + n]!.name;
|
||||
let signal = "null";
|
||||
let add = "null";
|
||||
if (e.fn === "sp.pipelineOptsSignal") {
|
||||
signal = args[2 + n]!.name;
|
||||
host.declare(`declare ptr @scr_events_add_abort_listener(ptr, ptr)`);
|
||||
add = "@scr_events_add_abort_listener";
|
||||
}
|
||||
const t = B.tmp();
|
||||
host.declare(`declare ptr @scr_sp_pipeline_opts(double, ptr, i1, ptr, ptr)`);
|
||||
B.line(
|
||||
`${t} = call ptr @scr_sp_pipeline_opts(double ${f64Lit(n)}, ptr ${arr}, i1 ${end}, ptr ${signal}, ptr ${add})`,
|
||||
);
|
||||
const out = host.own({ name: t, type: e.type });
|
||||
host.emitPendingCheck();
|
||||
return out;
|
||||
}
|
||||
if (e.fn === "sp.finishedOpts" || e.fn === "sp.finishedOptsSignal") {
|
||||
// finished(s, options) → a pending void promise (+1).
|
||||
const args = e.args.map((a) => host.emitExpr(a));
|
||||
let signal = "null";
|
||||
let add = "null";
|
||||
if (e.fn === "sp.finishedOptsSignal") {
|
||||
signal = args[5]!.name;
|
||||
host.declare(`declare ptr @scr_events_add_abort_listener(ptr, ptr)`);
|
||||
add = "@scr_events_add_abort_listener";
|
||||
}
|
||||
const t = B.tmp();
|
||||
host.declare(`declare ptr @scr_sp_finished_opts(ptr, double, double, i1, i1, ptr, ptr)`);
|
||||
B.line(
|
||||
`${t} = call ptr @scr_sp_finished_opts(ptr ${args[0]!.name}, double ${args[1]!.name}, double ${args[2]!.name}, i1 ${args[3]!.name}, i1 ${args[4]!.name}, ptr ${signal}, ptr ${add})`,
|
||||
);
|
||||
return host.own({ name: t, type: e.type });
|
||||
}
|
||||
if (e.fn === "stream.finishedOpts" || e.fn === "stream.finishedOptsSignal") {
|
||||
// finished(s, options, cb) → the +1 cleanup closure; typed callbacks
|
||||
// ride the "e" thunk, checked-dynamic ones the runtime's own.
|
||||
const args = e.args.map((a) => host.emitExpr(a));
|
||||
const cbIdx = e.args.length - 1;
|
||||
let signal = "null";
|
||||
let add = "null";
|
||||
if (e.fn === "stream.finishedOptsSignal") {
|
||||
signal = args[4]!.name;
|
||||
host.declare(`declare ptr @scr_events_add_abort_listener(ptr, ptr)`);
|
||||
add = "@scr_events_add_abort_listener";
|
||||
}
|
||||
const head = `ptr ${args[0]!.name}`;
|
||||
const flags = `double ${args[1]!.name}, double ${args[2]!.name}, i1 ${args[3]!.name}, ptr ${signal}, ptr ${add}`;
|
||||
const t = B.tmp();
|
||||
const cbT = e.args[cbIdx]!.type;
|
||||
if (cbT.kind === "dyn") {
|
||||
host.declare(
|
||||
`declare ptr @scr_stream_finished_opts_dyn(ptr, ptr, double, double, i1, ptr, ptr)`,
|
||||
);
|
||||
B.line(
|
||||
`${t} = call ptr @scr_stream_finished_opts_dyn(${head}, ptr ${args[cbIdx]!.name}, ${flags})`,
|
||||
);
|
||||
} else {
|
||||
if (cbT.kind !== "func")
|
||||
throw new InternalCompilerError(
|
||||
"llvm emitter bug: stream.finishedOpts callback not a func",
|
||||
);
|
||||
host.moveTemp(args[cbIdx]!);
|
||||
const thunk = host.streamCbThunkFor("e", cbT);
|
||||
host.declare(
|
||||
`declare ptr @scr_stream_finished_opts(ptr, ptr, ptr, double, double, i1, ptr, ptr)`,
|
||||
);
|
||||
B.line(
|
||||
`${t} = call ptr @scr_stream_finished_opts(${head}, ptr ${args[cbIdx]!.name}, ptr @${thunk}, ${flags})`,
|
||||
);
|
||||
}
|
||||
const out = host.own({ name: t, type: e.type });
|
||||
host.emitPendingCheck();
|
||||
return out;
|
||||
}
|
||||
if (e.fn === "stream.pipeline" || e.fn === "stream.pipelineDyn") {
|
||||
// pipeline(count, s1..sn, cb): the destination answers +1. The
|
||||
// stream list rides a stack array (the C compound literal).
|
||||
|
||||
@@ -21,6 +21,7 @@ export const SUPPORTED_BUILTIN_MODULES = [
|
||||
"string_decoder",
|
||||
"querystring",
|
||||
"readline",
|
||||
"readline/promises",
|
||||
"http2",
|
||||
"assert",
|
||||
"assert/strict",
|
||||
|
||||
@@ -181,7 +181,10 @@ export function lowerBuiltinModuleCall(
|
||||
const lowered = lowerDiagnosticsChannelCall(lowerer, expr, bi, loc);
|
||||
if (lowered) return lowered;
|
||||
}
|
||||
if (bi.module === "readline" && bi.member === "createInterface") {
|
||||
if (
|
||||
(bi.module === "readline" || bi.module === "readline/promises") &&
|
||||
bi.member === "createInterface"
|
||||
) {
|
||||
return lowerReadlineCreateCall(lowerer, expr, loc);
|
||||
}
|
||||
if (bi.module === "fs" && bi.member === "readFileSync" && expr.arguments.length === 2) {
|
||||
|
||||
@@ -1,39 +1,91 @@
|
||||
import * as ts from "../../ts7/adapter.js";
|
||||
import { type Lowerer } from "../lowerer.js";
|
||||
import { locOf } from "../../program.js";
|
||||
import { F64, type IrExpr, type SrcLoc, STRING, VOID } from "../../../ir/ir.js";
|
||||
import { DYN, F64, type IrExpr, type SrcLoc, STRING, VOID } from "../../../ir/ir.js";
|
||||
import { boolLit, numLit } from "../../../ir/build.js";
|
||||
import { READLINE_DOCUMENTED_OPTIONS, fenceOrDropOptionKey } from "../surfaces.js";
|
||||
import { streamSidesOf } from "../lower-stream.js";
|
||||
|
||||
/** True when `node`'s checker type is readline's Interface (stdlib
|
||||
* provenance + the enclosing "readline" ambient module — the name alone
|
||||
* is too generic). The interface maps to an f64 handle, so the IR type
|
||||
* cannot discriminate it from a plain number. */
|
||||
function isReadlineTyped(lowerer: Lowerer, node: ts.Expression): boolean {
|
||||
/** The ambient module declaring `d`, or null. */
|
||||
function ambientModuleOf(d: ts.Node): string | null {
|
||||
let up: ts.Node | undefined = d.parent;
|
||||
while (up) {
|
||||
if (ts.isModuleDeclaration(up) && ts.isStringLiteral(up.name)) return up.name.text;
|
||||
up = up.parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isReadlineModule(spec: string | null): boolean {
|
||||
return (
|
||||
spec === "readline" ||
|
||||
spec === "node:readline" ||
|
||||
spec === "readline/promises" ||
|
||||
spec === "node:readline/promises"
|
||||
);
|
||||
}
|
||||
|
||||
/** True when `node`'s checker type is a readline Interface (stdlib
|
||||
* provenance + the enclosing "readline" or "readline/promises" ambient
|
||||
* module — the name alone is too generic). The interface maps to an f64
|
||||
* handle, so the IR type cannot discriminate it from a plain number. */
|
||||
export function isReadlineTyped(lowerer: Lowerer, node: ts.Expression): boolean {
|
||||
const t = lowerer.checker.getTypeAtLocation(node);
|
||||
const sym = t.getAliasSymbol() ?? t.getSymbol();
|
||||
if (sym?.name !== "Interface") return false;
|
||||
return lowerer.checker.declarationsOf(sym).some((d) => {
|
||||
if (!ts.isClassDeclaration(d) && !ts.isInterfaceDeclaration(d)) return false;
|
||||
if (!lowerer.isStdlibFile(d.getSourceFile())) return false;
|
||||
let up: ts.Node | undefined = d.parent;
|
||||
while (up) {
|
||||
if (ts.isModuleDeclaration(up) && ts.isStringLiteral(up.name)) {
|
||||
return up.name.text === "readline" || up.name.text === "node:readline";
|
||||
}
|
||||
up = up.parent;
|
||||
}
|
||||
return false;
|
||||
return isReadlineModule(ambientModuleOf(d));
|
||||
});
|
||||
}
|
||||
|
||||
/** Method calls on readline Interface receivers: `rl.question(query, cb)`
|
||||
* writes the query to stdout and delivers the next stdin line's text to
|
||||
* the callback (one (answer: string) parameter, or none); `rl.close()`
|
||||
* fires the 'close' listeners synchronously (Node's inline emit) and
|
||||
* releases the loop; `rl.on("close", cb)` registers a zero-arg
|
||||
* listener. Everything else the lib declares (on("line"), prompt,
|
||||
* setPrompt, ...) fences member-qualified. Null for non-Interface
|
||||
* receivers. */
|
||||
/** The runtime's listener event numbers (scr_readline.c). */
|
||||
const RL_EVENTS: Record<string, number> = { line: 0, close: 1, pause: 2, resume: 3, error: 4 };
|
||||
|
||||
/** True when the call's resolved signature answers a Promise — the
|
||||
* readline/promises question. */
|
||||
function returnsPromise(lowerer: Lowerer, call: ts.CallExpression): boolean {
|
||||
const t = lowerer.checker.getTypeAtLocation(call);
|
||||
return (t.getSymbol()?.name ?? "") === "Promise";
|
||||
}
|
||||
|
||||
/** The `{ signal }` options argument of the promise question: the signal
|
||||
* expression, or null when absent. Other members fence by name. */
|
||||
function questionSignal(lowerer: Lowerer, node: ts.Expression | undefined): ts.Expression | null {
|
||||
if (node === undefined) return null;
|
||||
if (!ts.isObjectLiteralExpression(node)) {
|
||||
lowerer.noLowering(
|
||||
"readline/promises question options that are not an object literal",
|
||||
node,
|
||||
"write the options inline: question(query, { signal })",
|
||||
);
|
||||
}
|
||||
let signal: ts.Expression | null = null;
|
||||
for (const p of node.properties) {
|
||||
if (ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "signal") {
|
||||
signal = p.initializer;
|
||||
continue;
|
||||
}
|
||||
lowerer.noLowering(
|
||||
"readline/promises question with this option",
|
||||
p,
|
||||
"signal is the supported question option",
|
||||
);
|
||||
}
|
||||
return signal;
|
||||
}
|
||||
|
||||
/** Method calls on readline Interface receivers (both modules):
|
||||
* - question(query, cb) — the callback form; question(query, { signal }?)
|
||||
* — the promise form (readline/promises);
|
||||
* - close(), pause(), resume(), prompt(), setPrompt(p), getPrompt(),
|
||||
* write(data);
|
||||
* - on/once/addListener for 'line' ((input: string) or no parameters),
|
||||
* 'close'/'pause'/'resume' (no parameters) and 'error' ((err: Error) or
|
||||
* no parameters), as statements.
|
||||
* Everything else the lib declares fences member-qualified. Null for
|
||||
* non-Interface receivers. */
|
||||
export function lowerReadlineMethodCall(
|
||||
lowerer: Lowerer,
|
||||
call: ts.CallExpression,
|
||||
@@ -44,159 +96,314 @@ export function lowerReadlineMethodCall(
|
||||
if (!lowerer.isStdlibMember(access)) return null;
|
||||
const name = access.name.text;
|
||||
const loc = locOf(call);
|
||||
if (name === "question" && call.arguments.length === 2) {
|
||||
const receiver = lowerer.lowerExprExpecting(access.expression, F64);
|
||||
const query = lowerer.lowerExprExpecting(call.arguments[0]!, STRING);
|
||||
const cb = lowerer.lowerExpr(call.arguments[1]!);
|
||||
const args = call.arguments;
|
||||
const receiver = (): IrExpr => lowerer.lowerExprExpecting(access.expression, F64);
|
||||
if (
|
||||
name === "question" &&
|
||||
returnsPromise(lowerer, call) &&
|
||||
args.length >= 1 &&
|
||||
args.length <= 2
|
||||
) {
|
||||
const recv = receiver();
|
||||
const query = lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
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 sig = lowerer.lowerExprExpecting(signal, DYN);
|
||||
return { kind: "libCall", fn: "rl.questionAsyncSignal", args: [recv, query, sig], type, loc };
|
||||
}
|
||||
if (name === "question" && args.length === 2) {
|
||||
const recv = receiver();
|
||||
const query = lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
const cb = lowerer.lowerExpr(args[1]!);
|
||||
if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > 1) {
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
call.arguments[1]!,
|
||||
args[1]!,
|
||||
"question callbacks with more than one parameter or a return value",
|
||||
);
|
||||
}
|
||||
const param = cb.type.params[0];
|
||||
if (param !== undefined && param.kind !== "string") {
|
||||
if (param !== undefined && param.kind !== "string" && param.kind !== "dyn") {
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
call.arguments[1]!,
|
||||
args[1]!,
|
||||
`question callbacks whose parameter is not 'string' (got '${lowerer.fmt(param)}')`,
|
||||
);
|
||||
}
|
||||
return { kind: "libCall", fn: "rl.question", args: [receiver, query, cb], type: VOID, loc };
|
||||
return { kind: "libCall", fn: "rl.question", args: [recv, query, cb], type: VOID, loc };
|
||||
}
|
||||
if (name === "close" && call.arguments.length === 0) {
|
||||
const receiver = lowerer.lowerExprExpecting(access.expression, F64);
|
||||
return { kind: "libCall", fn: "rl.close", args: [receiver], type: VOID, loc };
|
||||
}
|
||||
if (name === "on" && call.arguments.length === 2) {
|
||||
const evT = lowerer.typeOf(call.arguments[0]!);
|
||||
const event = evT.isStringLiteralType() ? evT.value : null;
|
||||
if (event !== "close") {
|
||||
const simple: Record<string, "rl.close" | "rl.pause" | "rl.resume" | "rl.prompt"> = {
|
||||
close: "rl.close",
|
||||
pause: "rl.pause",
|
||||
resume: "rl.resume",
|
||||
prompt: "rl.prompt",
|
||||
};
|
||||
const simpleFn = simple[name];
|
||||
if (simpleFn !== undefined && (args.length === 0 || (name === "prompt" && args.length === 1))) {
|
||||
if ((name === "pause" || name === "resume") && !ts.isExpressionStatement(call.parent)) {
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
call,
|
||||
`using the result of readline ${name}() (call it as its own statement)`,
|
||||
);
|
||||
}
|
||||
// prompt(preserveCursor) only moves the cursor of a terminal
|
||||
// interface: a boolean literal is accepted and has no effect here.
|
||||
const flag = args[0];
|
||||
if (
|
||||
flag !== undefined &&
|
||||
flag.kind !== ts.SyntaxKind.TrueKeyword &&
|
||||
flag.kind !== ts.SyntaxKind.FalseKeyword
|
||||
) {
|
||||
lowerer.noLowering(
|
||||
`readline.Interface.on(${event === null ? "non-literal event" : `"${event}"`}, ...)`,
|
||||
call.arguments[0]!,
|
||||
'"close" is the supported readline event (question(query, cb) is the line consumer)',
|
||||
"readline prompt() with a non-literal preserveCursor",
|
||||
flag,
|
||||
"preserveCursor only affects terminal interfaces — pass a boolean literal or omit it",
|
||||
);
|
||||
}
|
||||
return { kind: "libCall", fn: simpleFn, args: [receiver()], type: VOID, loc };
|
||||
}
|
||||
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 };
|
||||
}
|
||||
if (name === "getPrompt" && args.length === 0) {
|
||||
return { kind: "libCall", fn: "rl.getPrompt", args: [receiver()], type: STRING, loc };
|
||||
}
|
||||
if (name === "write" && args.length === 1) {
|
||||
const recv = receiver();
|
||||
const data = lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
return { kind: "libCall", fn: "rl.write", args: [recv, data], type: VOID, loc };
|
||||
}
|
||||
if ((name === "on" || name === "once" || name === "addListener") && args.length === 2) {
|
||||
const evT = lowerer.typeOf(args[0]!);
|
||||
const event = evT.isStringLiteralType() ? evT.value : null;
|
||||
const ev = event === null ? undefined : RL_EVENTS[event];
|
||||
if (ev === undefined) {
|
||||
lowerer.noLowering(
|
||||
`readline.Interface.${name}(${event === null ? "non-literal event" : `"${event}"`}, ...)`,
|
||||
args[0]!,
|
||||
'"line", "close", "pause", "resume" and "error" are the supported readline events',
|
||||
);
|
||||
}
|
||||
if (!ts.isExpressionStatement(call.parent)) {
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
call,
|
||||
"chaining readline listener registration (the result is void here — register each listener as its own statement)",
|
||||
"chaining readline listener registration (register each listener as its own statement)",
|
||||
);
|
||||
}
|
||||
const receiver = lowerer.lowerExprExpecting(access.expression, F64);
|
||||
const cb = lowerer.lowerExpr(call.arguments[1]!);
|
||||
if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > 0) {
|
||||
const recv = receiver();
|
||||
const cb = lowerer.lowerExpr(args[1]!);
|
||||
const want = event === "line" ? "string" : event === "error" ? "error" : null;
|
||||
const param = cb.type.kind === "func" ? cb.type.params[0] : undefined;
|
||||
const paramOk =
|
||||
param === undefined ||
|
||||
(want !== null && param.kind === "dyn") ||
|
||||
(want === "string" && param.kind === "string") ||
|
||||
(want === "error" && param.kind === "object" && param.className === "Error");
|
||||
if (
|
||||
cb.type.kind !== "func" ||
|
||||
cb.type.ret.kind !== "void" ||
|
||||
cb.type.params.length > (want === null ? 0 : 1) ||
|
||||
!paramOk
|
||||
) {
|
||||
lowerer.unsupported(
|
||||
"SC1090",
|
||||
call.arguments[1]!,
|
||||
"close listeners with parameters or a return value (use ())",
|
||||
args[1]!,
|
||||
want === "string"
|
||||
? "'line' listeners other than (input: string) => void"
|
||||
: want === "error"
|
||||
? "'error' listeners other than (err: Error) => void"
|
||||
: `'${event}' listeners with parameters or a return value (use ())`,
|
||||
);
|
||||
}
|
||||
return { kind: "libCall", fn: "rl.onClose", args: [receiver, cb], type: VOID, loc };
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "rl.on",
|
||||
args: [recv, numLit(ev, loc), cb, boolLit(name === "once", loc)],
|
||||
type: VOID,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
lowerer.noLowering(
|
||||
`readline.Interface.${name}`,
|
||||
call,
|
||||
'question(query, cb), close(), and on("close", cb) are the supported Interface members',
|
||||
"question, close, pause, resume, prompt, setPrompt, getPrompt, write, for await, and on/once for 'line', 'close', 'pause', 'resume' and 'error' are the supported Interface members",
|
||||
lowerer.checker.getSymbolAtLocation(access.name),
|
||||
);
|
||||
}
|
||||
|
||||
// readline.createInterface({ input: process.stdin, output:
|
||||
// process.stdout }): exactly that options shape — the runtime reads
|
||||
// fd 0 and writes prompts to stdout, so any OTHER stream would be a
|
||||
// lie. `terminal` is accepted only as the literal false (the pipe
|
||||
// behavior this implements); other members fence by name.
|
||||
/** The interface's input: process.stdin (null) or a readable stream. */
|
||||
function lowerInput(lowerer: Lowerer, node: ts.Expression): IrExpr | null {
|
||||
if (
|
||||
ts.isPropertyAccessExpression(node) &&
|
||||
lowerer.stdlibGlobalMember(node, "process") === "stdin"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const v = lowerer.lowerExpr(node);
|
||||
const info = v.type.kind === "object" ? lowerer.classes.get(v.type.className) : undefined;
|
||||
const sides = streamSidesOf(lowerer, info);
|
||||
if (sides !== "r" && sides !== "rw") {
|
||||
lowerer.noLowering(
|
||||
"createInterface with this input",
|
||||
node,
|
||||
"process.stdin and readable streams are the supported inputs",
|
||||
);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/** The interface's output: "stdout", a writable stream, or "none". */
|
||||
function lowerOutput(lowerer: Lowerer, node: ts.Expression): IrExpr | "stdout" | "none" {
|
||||
if (
|
||||
ts.isPropertyAccessExpression(node) &&
|
||||
lowerer.stdlibGlobalMember(node, "process") === "stdout"
|
||||
) {
|
||||
return "stdout";
|
||||
}
|
||||
if (node.kind === ts.SyntaxKind.NullKeyword) {
|
||||
return "none";
|
||||
}
|
||||
if (ts.isIdentifier(node) && node.text === "undefined") return "none";
|
||||
const v = lowerer.lowerExpr(node);
|
||||
const info = v.type.kind === "object" ? lowerer.classes.get(v.type.className) : undefined;
|
||||
const sides = streamSidesOf(lowerer, info);
|
||||
if (sides !== "w" && sides !== "rw") {
|
||||
lowerer.noLowering(
|
||||
"createInterface with this output",
|
||||
node,
|
||||
"process.stdout and writable streams are the supported outputs",
|
||||
);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/** createInterface({ input, output?, terminal?: false, crlfDelay?,
|
||||
* prompt?, signal? }) or createInterface(input, output?), from
|
||||
* node:readline or node:readline/promises. The input is process.stdin
|
||||
* or a readable 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. */
|
||||
export function lowerReadlineCreateCall(
|
||||
lowerer: Lowerer,
|
||||
expr: ts.CallExpression,
|
||||
loc: SrcLoc,
|
||||
): IrExpr {
|
||||
const optsNode = expr.arguments.length === 1 ? expr.arguments[0] : undefined;
|
||||
if (!optsNode || !ts.isObjectLiteralExpression(optsNode)) {
|
||||
let input: IrExpr | null = null;
|
||||
let output: IrExpr | "stdout" | "none" = "none";
|
||||
let crlf: IrExpr = numLit(0, loc);
|
||||
let prompt: IrExpr | null = null;
|
||||
let signal: IrExpr | null = null;
|
||||
const optsNode = expr.arguments[0];
|
||||
if (optsNode === undefined) {
|
||||
lowerer.noLowering(
|
||||
"createInterface with this argument shape",
|
||||
"createInterface without arguments",
|
||||
expr,
|
||||
"the supported form is createInterface({ input: process.stdin, output: process.stdout })",
|
||||
"pass { input: process.stdin, output: process.stdout } or createInterface(input, output)",
|
||||
);
|
||||
}
|
||||
let sawInput = false;
|
||||
for (const p of optsNode.properties) {
|
||||
if (!ts.isPropertyAssignment(p) || !ts.isIdentifier(p.name)) {
|
||||
if (!ts.isObjectLiteralExpression(optsNode)) {
|
||||
if (expr.arguments.length > 2) {
|
||||
lowerer.noLowering(
|
||||
"createInterface with this options shape",
|
||||
p,
|
||||
"spreads, computed keys, and shorthand options have no lowering — write each member inline",
|
||||
"createInterface with a completer or terminal argument",
|
||||
expr.arguments[2]!,
|
||||
"the positional form createInterface(input, output) is supported (terminal line editing has no lowering)",
|
||||
);
|
||||
}
|
||||
const member = p.name.text;
|
||||
const streamOf = (node: ts.Expression): string | null =>
|
||||
ts.isPropertyAccessExpression(node) ? lowerer.stdlibGlobalMember(node, "process") : null;
|
||||
if (member === "input") {
|
||||
if (streamOf(p.initializer) !== "stdin") {
|
||||
lowerer.noLowering(
|
||||
"createInterface with a non-stdin input",
|
||||
p.initializer,
|
||||
"process.stdin is the one supported input stream",
|
||||
);
|
||||
}
|
||||
sawInput = true;
|
||||
} else if (member === "output") {
|
||||
if (streamOf(p.initializer) !== "stdout") {
|
||||
lowerer.noLowering(
|
||||
"createInterface with a non-stdout output",
|
||||
p.initializer,
|
||||
"process.stdout is the one supported output stream",
|
||||
);
|
||||
}
|
||||
} else if (member === "terminal") {
|
||||
const t = lowerer.typeOf(p.initializer);
|
||||
if (!(t.flags & ts.TypeFlags.BooleanLiteral) || lowerer.checker.typeToString(t) !== "false") {
|
||||
lowerer.noLowering(
|
||||
"createInterface with terminal enabled",
|
||||
p.initializer,
|
||||
"terminal line editing has no lowering — the pipe behavior is what compiles",
|
||||
);
|
||||
}
|
||||
} else if (member === "crlfDelay") {
|
||||
// Infinity states the lowered behavior: the splitter holds a
|
||||
// trailing \r until the next chunk decides \r\n vs \r, with no
|
||||
// time limit (scr_readline.c) — Node's crlfDelay: Infinity.
|
||||
// Finite delays would need a timer the splitter does not have.
|
||||
if (!ts.isIdentifier(p.initializer) || p.initializer.text !== "Infinity") {
|
||||
lowerer.noLowering(
|
||||
"createInterface with a finite crlfDelay",
|
||||
p.initializer,
|
||||
"the lowered splitter always joins \\r\\n across chunks (Node's crlfDelay: Infinity) — Infinity is the accepted value",
|
||||
);
|
||||
}
|
||||
} else if (member === "completer") {
|
||||
input = lowerInput(lowerer, optsNode);
|
||||
if (expr.arguments[1] !== undefined) output = lowerOutput(lowerer, expr.arguments[1]);
|
||||
} else {
|
||||
if (expr.arguments.length > 1) {
|
||||
lowerer.noLowering(
|
||||
"createInterface with a completer",
|
||||
p,
|
||||
"tab completion needs an interactive terminal — the lowered interface reads piped lines (terminal: false)",
|
||||
"createInterface with more than one argument",
|
||||
expr.arguments[1]!,
|
||||
"pass every option in the one options object",
|
||||
);
|
||||
} else {
|
||||
fenceOrDropOptionKey(
|
||||
lowerer,
|
||||
p,
|
||||
member,
|
||||
"createInterface",
|
||||
READLINE_DOCUMENTED_OPTIONS,
|
||||
"input, output, terminal: false, and crlfDelay: Infinity are the supported options",
|
||||
}
|
||||
let sawInput = false;
|
||||
for (const p of optsNode.properties) {
|
||||
if (!ts.isPropertyAssignment(p) || !ts.isIdentifier(p.name)) {
|
||||
lowerer.noLowering(
|
||||
"createInterface with this options shape",
|
||||
p,
|
||||
"spreads, computed keys, and shorthand options have no lowering — write each member inline",
|
||||
);
|
||||
}
|
||||
const member = p.name.text;
|
||||
if (member === "input") {
|
||||
input = lowerInput(lowerer, p.initializer);
|
||||
sawInput = true;
|
||||
} else if (member === "output") {
|
||||
output = lowerOutput(lowerer, p.initializer);
|
||||
} else if (member === "terminal") {
|
||||
const t = lowerer.typeOf(p.initializer);
|
||||
if (
|
||||
!(t.flags & ts.TypeFlags.BooleanLiteral) ||
|
||||
lowerer.checker.typeToString(t) !== "false"
|
||||
) {
|
||||
lowerer.noLowering(
|
||||
"createInterface with terminal enabled",
|
||||
p.initializer,
|
||||
"terminal line editing has no lowering — the pipe behavior is what compiles",
|
||||
);
|
||||
}
|
||||
} else if (member === "crlfDelay") {
|
||||
crlf = lowerer.lowerExprExpecting(p.initializer, F64);
|
||||
} else if (member === "prompt") {
|
||||
prompt = lowerer.lowerExprExpecting(p.initializer, STRING);
|
||||
} else if (member === "signal") {
|
||||
signal = lowerer.lowerExprExpecting(p.initializer, DYN);
|
||||
} else if (member === "completer") {
|
||||
lowerer.noLowering(
|
||||
"createInterface with a completer",
|
||||
p,
|
||||
"tab completion needs an interactive terminal — the lowered interface reads piped lines (terminal: false)",
|
||||
);
|
||||
} else {
|
||||
fenceOrDropOptionKey(
|
||||
lowerer,
|
||||
p,
|
||||
member,
|
||||
"createInterface",
|
||||
READLINE_DOCUMENTED_OPTIONS,
|
||||
"input, output, terminal: false, crlfDelay, prompt and signal are the supported options",
|
||||
);
|
||||
// An undocumented key, dropped like Node drops it.
|
||||
}
|
||||
}
|
||||
if (!sawInput) {
|
||||
lowerer.noLowering(
|
||||
"createInterface without an input stream",
|
||||
optsNode,
|
||||
"pass { input: process.stdin, output: process.stdout }",
|
||||
);
|
||||
// An undocumented key, dropped like Node drops it.
|
||||
}
|
||||
}
|
||||
if (!sawInput) {
|
||||
lowerer.noLowering(
|
||||
"createInterface without an input stream",
|
||||
optsNode,
|
||||
"pass { input: process.stdin, output: process.stdout }",
|
||||
);
|
||||
}
|
||||
return { kind: "libCall", fn: "rl.create", args: [], type: F64, loc };
|
||||
const mode =
|
||||
(input !== null ? 1 : 0) |
|
||||
(typeof output === "object" ? 2 : 0) |
|
||||
(output === "stdout" ? 4 : 0) |
|
||||
(signal !== null ? 8 : 0) |
|
||||
(prompt !== null ? 16 : 0);
|
||||
const args: IrExpr[] = [numLit(mode, loc)];
|
||||
if (input !== null) args.push(input);
|
||||
if (typeof output === "object") args.push(output);
|
||||
args.push(crlf);
|
||||
if (prompt !== null) args.push(prompt);
|
||||
if (signal !== null) args.push(signal);
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: signal !== null ? "rl.createSignal" : "rl.create",
|
||||
args,
|
||||
type: F64,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,13 +7,23 @@ import { dynUndefinedExpr } from "../../ir/build.js";
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import { InternalCompilerError } from "../../errors.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
import { BOOL, DYN, type IrExpr, type IrStmt, type IrType, VOID } from "../../ir/ir.js";
|
||||
import {
|
||||
BOOL,
|
||||
DYN,
|
||||
F64,
|
||||
type IrExpr,
|
||||
type IrStmt,
|
||||
type IrType,
|
||||
STRING,
|
||||
VOID,
|
||||
} from "../../ir/ir.js";
|
||||
import { locOf } from "../program.js";
|
||||
import { extractIteratorValue } from "./lower-generators.js";
|
||||
import { streamSidesOf } from "./lower-stream.js";
|
||||
import { staticReadableStreamElementType } from "./lower-stdlib.js";
|
||||
import { genResultRecord } from "../type-mapper.js";
|
||||
import { sideEffectFreeOptionValue } from "./surfaces.js";
|
||||
import { isReadlineTyped } from "./builtins/readline.js";
|
||||
|
||||
interface NodeReadablePlan {
|
||||
source: ts.Expression;
|
||||
@@ -828,7 +838,143 @@ function lowerClassAsyncIterator(
|
||||
/** Claims the built-in static async-iterator families. Typed async
|
||||
* generators and process.stdin retain their specialized drivers; this is
|
||||
* the common stream protocol path. */
|
||||
/** The readline Interface a for-await iterates: the interface itself or
|
||||
* an explicit rl[Symbol.asyncIterator]() call. */
|
||||
function readlinePlan(lowerer: Lowerer, expression: ts.Expression): ts.Expression | null {
|
||||
if (isReadlineTyped(lowerer, expression)) return expression;
|
||||
const call = symbolAsyncIteratorCall(lowerer, expression);
|
||||
if (call === null) return null;
|
||||
const source = (call.expression as ts.PropertyAccessExpression | ts.ElementAccessExpression)
|
||||
.expression;
|
||||
return isReadlineTyped(lowerer, source) ? source : null;
|
||||
}
|
||||
|
||||
/** for await (const line of rl): the interface's one async iterator
|
||||
* (events.on over 'line' — scr_readline.c). Each line binds as a string;
|
||||
* an abrupt completion while the body owns a line calls return(), which
|
||||
* stops the iterator listening while queued lines stay readable. */
|
||||
function lowerReadlineLines(
|
||||
lowerer: Lowerer,
|
||||
stmt: ts.ForOfStatement,
|
||||
source: ts.Expression,
|
||||
): IrStmt {
|
||||
const binding = checkForAwaitBinding(lowerer, stmt);
|
||||
const loc = locOf(stmt);
|
||||
lowerer.scopes.push(new Map());
|
||||
try {
|
||||
const rl = lowerer.declareHiddenLocal("%faReadline", F64);
|
||||
const active = lowerer.declareHiddenLocal("%faActive", BOOL);
|
||||
active.mutable = true;
|
||||
const promiseT: IrType = { kind: "promise", inner: DYN };
|
||||
const promise = lowerer.declareHiddenLocal("%rlNext", promiseT);
|
||||
const value = lowerer.declareHiddenLocal("%rlValue", DYN);
|
||||
const line = lowerer.declareLocal(
|
||||
binding.declaration.name,
|
||||
binding.declaration.name.text,
|
||||
STRING,
|
||||
binding.mutable,
|
||||
);
|
||||
const rlRef = (): IrExpr => ({ kind: "varRef", localId: rl.id, type: F64, loc });
|
||||
const valueRef = (): IrExpr => ({ kind: "varRef", localId: value.id, type: DYN, loc });
|
||||
const body = lowerer.inCtl("loop", () => lowerer.lowerScopedBlock(stmt.statement));
|
||||
const loop: IrStmt = {
|
||||
kind: "while",
|
||||
cond: { kind: "boolLit", value: true, type: BOOL, loc },
|
||||
body: [
|
||||
{
|
||||
kind: "assign",
|
||||
localId: active.id,
|
||||
value: { kind: "boolLit", value: false, type: BOOL, loc },
|
||||
loc,
|
||||
},
|
||||
{
|
||||
kind: "varDecl",
|
||||
localId: promise.id,
|
||||
init: { kind: "libCall", fn: "rl.iterNext", args: [rlRef()], type: promiseT, loc },
|
||||
loc,
|
||||
},
|
||||
{
|
||||
kind: "varDecl",
|
||||
localId: value.id,
|
||||
init: {
|
||||
kind: "awaitExpr",
|
||||
value: { kind: "varRef", localId: promise.id, type: promiseT, loc },
|
||||
type: DYN,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
},
|
||||
{
|
||||
kind: "if",
|
||||
cond: { kind: "dynTest", test: "undefined", value: valueRef(), type: BOOL, loc },
|
||||
then: [{ kind: "break", loc }],
|
||||
else_: null,
|
||||
loc,
|
||||
},
|
||||
{
|
||||
kind: "varDecl",
|
||||
localId: line.id,
|
||||
init: { kind: "dynCheck", value: valueRef(), type: STRING, loc },
|
||||
loc,
|
||||
},
|
||||
{
|
||||
kind: "assign",
|
||||
localId: active.id,
|
||||
value: { kind: "boolLit", value: true, type: BOOL, loc },
|
||||
loc,
|
||||
},
|
||||
...body,
|
||||
],
|
||||
loc,
|
||||
};
|
||||
return {
|
||||
kind: "block",
|
||||
body: [
|
||||
{ kind: "varDecl", localId: rl.id, init: lowerer.lowerExprExpecting(source, F64), loc },
|
||||
{
|
||||
kind: "exprStmt",
|
||||
expr: { kind: "libCall", fn: "rl.iterOpen", args: [rlRef()], type: VOID, loc },
|
||||
loc,
|
||||
},
|
||||
{
|
||||
kind: "varDecl",
|
||||
localId: active.id,
|
||||
init: { kind: "boolLit", value: false, type: BOOL, loc },
|
||||
loc,
|
||||
},
|
||||
{
|
||||
kind: "tryCatch",
|
||||
tryBody: [loop],
|
||||
catchBody: null,
|
||||
catchLocalId: null,
|
||||
finallyBody: [
|
||||
{
|
||||
kind: "if",
|
||||
cond: boolRef(active.id, loc),
|
||||
then: [
|
||||
{
|
||||
kind: "exprStmt",
|
||||
expr: { kind: "libCall", fn: "rl.iterReturn", args: [rlRef()], type: VOID, loc },
|
||||
loc,
|
||||
},
|
||||
],
|
||||
else_: null,
|
||||
loc,
|
||||
},
|
||||
],
|
||||
loc,
|
||||
},
|
||||
],
|
||||
loc,
|
||||
};
|
||||
} finally {
|
||||
lowerer.scopes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
export function lowerForAwaitBuiltin(lowerer: Lowerer, stmt: ts.ForOfStatement): IrStmt | null {
|
||||
const readline = readlinePlan(lowerer, stmt.expression);
|
||||
if (readline) return lowerReadlineLines(lowerer, stmt, readline);
|
||||
const node = nodeReadablePlan(lowerer, stmt.expression);
|
||||
if (node) return lowerNodeReadable(lowerer, stmt, node);
|
||||
const web = webReadablePlan(lowerer, stmt.expression);
|
||||
|
||||
@@ -37,6 +37,7 @@ import { newFnCtx, own } from "./lowerer.js";
|
||||
import { appendImplicitUndefinedReturn } from "./function-returns.js";
|
||||
import { bufEncoding, knownBufEncoding } from "./containers/bytes.js";
|
||||
import { tryLowerExpression } from "./expressions/try-lower-expression.js";
|
||||
import { sideEffectFreeOptionValue } from "./surfaces.js";
|
||||
import {
|
||||
BOOL,
|
||||
DYN,
|
||||
@@ -60,6 +61,29 @@ import {
|
||||
|
||||
const BYTES = BYTES_U8;
|
||||
|
||||
/** An expression that only reads bindings and properties (no calls,
|
||||
* assignments or construction): reading it a step later observes the
|
||||
* same values. */
|
||||
function pureRead(node: ts.Expression): boolean {
|
||||
if (
|
||||
ts.isParenthesizedExpression(node) ||
|
||||
ts.isNonNullExpression(node) ||
|
||||
ts.isAsExpression(node)
|
||||
) {
|
||||
return pureRead(node.expression);
|
||||
}
|
||||
if (ts.isIdentifier(node) || node.kind === ts.SyntaxKind.ThisKeyword) return true;
|
||||
if (ts.isPropertyAccessExpression(node)) return pureRead(node.expression);
|
||||
if (ts.isElementAccessExpression(node)) {
|
||||
return (
|
||||
(ts.isStringLiteral(node.argumentExpression) ||
|
||||
ts.isNumericLiteral(node.argumentExpression)) &&
|
||||
pureRead(node.expression)
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** The stream sides of a receiver class: the nearest stream-class
|
||||
* ancestor's, or null off the stream hierarchy. */
|
||||
export function streamSidesOf(
|
||||
@@ -334,6 +358,10 @@ interface StreamOptions {
|
||||
* as a follow-up readable.pushEncoding — push(string) decodes through
|
||||
* it (Node's Buffer.from(chunk, state.defaultEncoding)). */
|
||||
pushEncoding: string | null;
|
||||
/** The `signal` option's value: applied with stream.addAbortSignal
|
||||
* right after construction (Node's constructors call addAbortSignal
|
||||
* once the state exists), or null when absent. */
|
||||
signal: IrExpr | null;
|
||||
/** Present callbacks in canonical order with their flag bits. */
|
||||
flags: number;
|
||||
cbs: IrExpr[];
|
||||
@@ -559,6 +587,7 @@ function parseStreamOptions(
|
||||
encoding: null,
|
||||
badEncoding: null,
|
||||
pushEncoding: null,
|
||||
signal: null,
|
||||
flags: 0,
|
||||
cbs: [],
|
||||
};
|
||||
@@ -635,6 +664,11 @@ function parseStreamOptions(
|
||||
present.set(name, lowerStreamCallback(lowerer, name, fnNode, thisType, tuple));
|
||||
continue;
|
||||
}
|
||||
// `{ signal }` shorthand: the binding is the value.
|
||||
if (name === "signal" && ts.isShorthandPropertyAssignment(prop)) {
|
||||
if (ts.isIdentifier(prop.name)) out.signal = lowerer.lowerExprExpecting(prop.name, DYN);
|
||||
continue;
|
||||
}
|
||||
// Scalar options.
|
||||
if (!ts.isPropertyAssignment(prop)) {
|
||||
lowerer.unsupported(
|
||||
@@ -729,13 +763,22 @@ function parseStreamOptions(
|
||||
"utf8 (the default) is the supported write-side string encoding",
|
||||
);
|
||||
break;
|
||||
case "signal":
|
||||
lowerer.noLowering(
|
||||
`the ${ctorName} option 'signal'`,
|
||||
prop,
|
||||
"AbortSignal-driven destruction is not lowered yet",
|
||||
);
|
||||
case "signal": {
|
||||
// `if (options.signal) addAbortSignal(options.signal, this)`:
|
||||
// applied over the fresh stream, so the value is read after the
|
||||
// constructor — only values whose evaluation has no effects.
|
||||
if (value.kind === ts.SyntaxKind.NullKeyword) break;
|
||||
if (ts.isIdentifier(value) && value.text === "undefined") break;
|
||||
if (!sideEffectFreeOptionValue(value) && !pureRead(value)) {
|
||||
lowerer.noLowering(
|
||||
`the ${ctorName} option 'signal' with this value`,
|
||||
prop,
|
||||
"pass a variable or property read (const signal = controller.signal) — the signal is read after construction",
|
||||
);
|
||||
}
|
||||
out.signal = lowerer.lowerExprExpecting(value, DYN);
|
||||
break;
|
||||
}
|
||||
case "captureRejections": {
|
||||
// `false` is the default — admit the literal; the rejection-
|
||||
// capturing mode has no lowering (async listeners' promises are
|
||||
@@ -824,6 +867,7 @@ function streamCtorArgs(
|
||||
encoding: string | null;
|
||||
badEncoding: string | null;
|
||||
pushEncoding: string | null;
|
||||
signal: IrExpr | null;
|
||||
} {
|
||||
const shape = streamCtorShape(cls);
|
||||
const o = parseStreamOptions(
|
||||
@@ -852,6 +896,7 @@ function streamCtorArgs(
|
||||
encoding: o.encoding,
|
||||
badEncoding: o.badEncoding,
|
||||
pushEncoding: o.pushEncoding,
|
||||
signal: o.signal,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -907,13 +952,23 @@ export function lowerStreamNew(lowerer: Lowerer, expr: ts.NewExpression, info: C
|
||||
loc,
|
||||
};
|
||||
}
|
||||
if (parsed.encoding === null) return built;
|
||||
// { encoding }: chain a setEncoding over the fresh stream (the operand
|
||||
// temp releases with the frame; setEncoding answers the receiver +1).
|
||||
if (parsed.encoding !== null) {
|
||||
// { encoding }: chain a setEncoding over the fresh stream (the operand
|
||||
// temp releases with the frame; setEncoding answers the receiver +1).
|
||||
built = {
|
||||
kind: "libCall",
|
||||
fn: "readable.setEncoding",
|
||||
args: [built, strLit(parsed.encoding, loc)],
|
||||
type: thisType,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
if (parsed.signal === null) return built;
|
||||
// { signal }: addAbortSignal over the fresh stream (answers it +1).
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "readable.setEncoding",
|
||||
args: [built, strLit(parsed.encoding, loc)],
|
||||
fn: "stream.addAbortSignal",
|
||||
args: [built, parsed.signal],
|
||||
type: thisType,
|
||||
loc,
|
||||
};
|
||||
@@ -1178,6 +1233,19 @@ export function lowerStreamSuperCall(
|
||||
loc,
|
||||
});
|
||||
}
|
||||
if (parsed.signal !== null) {
|
||||
out.push({
|
||||
kind: "exprStmt",
|
||||
expr: {
|
||||
kind: "libCall",
|
||||
fn: "stream.addAbortSignal",
|
||||
args: [thisRef(), parsed.signal],
|
||||
type: thisType,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1423,7 +1491,7 @@ const PIPELINE_STAGE_EXPECTED =
|
||||
function lowerStreamArg(
|
||||
lowerer: Lowerer,
|
||||
node: ts.Expression,
|
||||
what: "finished" | "pipeline",
|
||||
what: "finished" | "pipeline" | "addAbortSignal",
|
||||
): IrExpr {
|
||||
const v = lowerer.lowerExpr(node);
|
||||
const info = v.type.kind === "object" ? lowerer.classes.get(v.type.className) : undefined;
|
||||
@@ -1563,6 +1631,113 @@ function lowerEosCallback(
|
||||
);
|
||||
}
|
||||
|
||||
/** finished()'s options literal: readable/writable/error (and cleanup
|
||||
* for the promise form) as boolean literals, signal as any expression.
|
||||
* readable/writable answer -1 when absent (Node's default: whether the
|
||||
* stream has that side). */
|
||||
function eosOptions(
|
||||
lowerer: Lowerer,
|
||||
node: ts.Expression,
|
||||
promiseForm: boolean,
|
||||
): { readable: number; writable: number; error: boolean; cleanup: boolean; signal: IrExpr | null } {
|
||||
const out = {
|
||||
readable: -1,
|
||||
writable: -1,
|
||||
error: true,
|
||||
cleanup: false,
|
||||
signal: null as IrExpr | null,
|
||||
};
|
||||
if (!ts.isObjectLiteralExpression(node)) {
|
||||
lowerer.noLowering(
|
||||
"finished options that are not an object literal",
|
||||
node,
|
||||
"write the options inline: finished(stream, { readable, writable, error, signal })",
|
||||
);
|
||||
}
|
||||
const literal = (p: ts.PropertyAssignment, key: string): boolean => {
|
||||
if (p.initializer.kind === ts.SyntaxKind.TrueKeyword) return true;
|
||||
if (p.initializer.kind === ts.SyntaxKind.FalseKeyword) return false;
|
||||
lowerer.noLowering(
|
||||
`the finished option '${key}' with a non-literal value`,
|
||||
p.initializer,
|
||||
"pass true or false",
|
||||
);
|
||||
};
|
||||
for (const p of node.properties) {
|
||||
const key =
|
||||
(ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) && ts.isIdentifier(p.name)
|
||||
? p.name.text
|
||||
: null;
|
||||
if (key === "signal") {
|
||||
out.signal = ts.isShorthandPropertyAssignment(p)
|
||||
? lowerer.lowerExprExpecting(p.name as ts.Identifier, DYN)
|
||||
: lowerer.lowerExprExpecting((p as ts.PropertyAssignment).initializer, DYN);
|
||||
continue;
|
||||
}
|
||||
if (key !== null && ts.isPropertyAssignment(p)) {
|
||||
if (key === "readable") out.readable = literal(p, key) ? 1 : 0;
|
||||
else if (key === "writable") out.writable = literal(p, key) ? 1 : 0;
|
||||
else if (key === "error") out.error = literal(p, key);
|
||||
else if (key === "cleanup" && promiseForm) out.cleanup = literal(p, key);
|
||||
else {
|
||||
lowerer.noLowering(
|
||||
`the finished option '${key}'`,
|
||||
p,
|
||||
promiseForm
|
||||
? "readable, writable, error, cleanup and signal are the supported options"
|
||||
: "readable, writable, error and signal are the supported options",
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
lowerer.noLowering(
|
||||
"finished options with this member",
|
||||
p,
|
||||
"write each option as a plain property",
|
||||
);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** pipeline()'s trailing options literal (the promise form): signal as
|
||||
* any expression, end as a boolean literal. */
|
||||
function pipelineOptions(
|
||||
lowerer: Lowerer,
|
||||
node: ts.ObjectLiteralExpression,
|
||||
): { end: boolean; signal: IrExpr | null } {
|
||||
const out = { end: true, signal: null as IrExpr | null };
|
||||
for (const p of node.properties) {
|
||||
const key =
|
||||
(ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) && ts.isIdentifier(p.name)
|
||||
? p.name.text
|
||||
: null;
|
||||
if (key === "signal") {
|
||||
out.signal = ts.isShorthandPropertyAssignment(p)
|
||||
? lowerer.lowerExprExpecting(p.name as ts.Identifier, DYN)
|
||||
: lowerer.lowerExprExpecting((p as ts.PropertyAssignment).initializer, DYN);
|
||||
continue;
|
||||
}
|
||||
if (key === "end" && ts.isPropertyAssignment(p)) {
|
||||
if (p.initializer.kind === ts.SyntaxKind.TrueKeyword) continue;
|
||||
if (p.initializer.kind === ts.SyntaxKind.FalseKeyword) {
|
||||
out.end = false;
|
||||
continue;
|
||||
}
|
||||
lowerer.noLowering(
|
||||
"the pipeline option 'end' with a non-literal value",
|
||||
p.initializer,
|
||||
"pass true or false",
|
||||
);
|
||||
}
|
||||
lowerer.noLowering(
|
||||
key === null ? "pipeline options with this member" : `the pipeline option '${key}'`,
|
||||
p,
|
||||
"signal and end are the supported pipeline options",
|
||||
);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** `finished(...)` / `pipeline(...)` / `getDefaultHighWaterMark(...)`
|
||||
* imported from node:stream (named imports, destructured requires, and
|
||||
* namespace-member calls all land here). Null for members this spoke
|
||||
@@ -1580,13 +1755,11 @@ export function lowerStreamModuleCall(
|
||||
// ERR_STREAM_PREMATURE_CLOSE — the callback form's status, boxed
|
||||
// through the promise instead). The options argument has no lowering.
|
||||
if (bi.member === "finished") {
|
||||
if (args.length !== 1) {
|
||||
if (args.length !== 1 && args.length !== 2) {
|
||||
lowerer.noLowering(
|
||||
`stream/promises.finished with ${args.length} arguments`,
|
||||
call,
|
||||
args.length === 2
|
||||
? "the options argument has no lowering yet — the one-argument form is supported"
|
||||
: "the supported form is finished(stream)",
|
||||
"the supported forms are finished(stream) and finished(stream, options)",
|
||||
);
|
||||
}
|
||||
let recv: IrExpr;
|
||||
@@ -1596,6 +1769,24 @@ export function lowerStreamModuleCall(
|
||||
if (e instanceof StreamArgTypeThrow) return e.expr;
|
||||
throw e;
|
||||
}
|
||||
const optsNode = args[1];
|
||||
if (optsNode !== undefined && optsNode.kind !== ts.SyntaxKind.NullKeyword) {
|
||||
const o = eosOptions(lowerer, optsNode, true);
|
||||
const head: IrExpr[] = [
|
||||
recv,
|
||||
numLit(o.readable, loc),
|
||||
numLit(o.writable, loc),
|
||||
boolLit(o.error, loc),
|
||||
boolLit(o.cleanup, loc),
|
||||
];
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: o.signal ? "sp.finishedOptsSignal" : "sp.finishedOpts",
|
||||
args: o.signal ? [...head, o.signal] : head,
|
||||
type: { kind: "promise", inner: VOID },
|
||||
loc,
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "sp.finished",
|
||||
@@ -1616,13 +1807,29 @@ export function lowerStreamModuleCall(
|
||||
"the supported form is pipeline(source, ...streams)",
|
||||
);
|
||||
}
|
||||
const lastArg = args[args.length - 1]!;
|
||||
const opts =
|
||||
ts.isObjectLiteralExpression(lastArg) && args.length >= 3
|
||||
? pipelineOptions(lowerer, lastArg)
|
||||
: null;
|
||||
const stageArgs = opts ? args.slice(0, -1) : args;
|
||||
let streams: IrExpr[];
|
||||
try {
|
||||
streams = args.map((a) => lowerStreamArg(lowerer, a, "pipeline"));
|
||||
streams = stageArgs.map((a) => lowerStreamArg(lowerer, a, "pipeline"));
|
||||
} catch (e) {
|
||||
if (e instanceof StreamArgTypeThrow) return e.expr;
|
||||
throw e;
|
||||
}
|
||||
if (opts) {
|
||||
const head = [numLit(streams.length, loc), ...streams, boolLit(opts.end, loc)];
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: opts.signal ? "sp.pipelineOptsSignal" : "sp.pipelineOpts",
|
||||
args: opts.signal ? [...head, opts.signal] : head,
|
||||
type: { kind: "promise", inner: VOID },
|
||||
loc,
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "sp.pipeline",
|
||||
@@ -1703,14 +1910,39 @@ export function lowerStreamModuleCall(
|
||||
);
|
||||
}
|
||||
|
||||
if (bi.member === "finished") {
|
||||
if (bi.member === "addAbortSignal") {
|
||||
// addAbortSignal(signal, stream) → the stream: destroyed with an
|
||||
// AbortError when the signal aborts (now, if it already has).
|
||||
if (args.length !== 2) {
|
||||
lowerer.noLowering(
|
||||
`addAbortSignal with ${args.length} arguments`,
|
||||
call,
|
||||
"the supported form is addAbortSignal(signal, stream)",
|
||||
);
|
||||
}
|
||||
const signal = lowerer.lowerExprExpecting(args[0]!, DYN);
|
||||
let stream: IrExpr;
|
||||
try {
|
||||
stream = lowerStreamArg(lowerer, args[1]!, "addAbortSignal");
|
||||
} catch (e) {
|
||||
if (e instanceof StreamArgTypeThrow) return e.expr;
|
||||
throw e;
|
||||
}
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: "stream.addAbortSignalFn",
|
||||
args: [signal, stream],
|
||||
type: stream.type,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
if (bi.member === "finished") {
|
||||
if (args.length !== 2 && args.length !== 3) {
|
||||
lowerer.noLowering(
|
||||
`finished with ${args.length} arguments`,
|
||||
call,
|
||||
args.length === 3
|
||||
? "the options argument has no lowering yet — the two-argument form is supported"
|
||||
: "the supported form is finished(stream, callback)",
|
||||
"the supported forms are finished(stream, callback) and finished(stream, options, callback)",
|
||||
);
|
||||
}
|
||||
let recv: IrExpr;
|
||||
@@ -1720,7 +1952,24 @@ export function lowerStreamModuleCall(
|
||||
if (e instanceof StreamArgTypeThrow) return e.expr;
|
||||
throw e;
|
||||
}
|
||||
const { cb, dyn } = lowerEosCallback(lowerer, "finished", args[1]!, recv.type);
|
||||
if (args.length === 3 && args[1]!.kind !== ts.SyntaxKind.NullKeyword) {
|
||||
const o = eosOptions(lowerer, args[1]!, false);
|
||||
const { cb } = lowerEosCallback(lowerer, "finished", args[2]!, recv.type);
|
||||
const head: IrExpr[] = [
|
||||
recv,
|
||||
numLit(o.readable, loc),
|
||||
numLit(o.writable, loc),
|
||||
boolLit(o.error, loc),
|
||||
];
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: o.signal ? "stream.finishedOptsSignal" : "stream.finishedOpts",
|
||||
args: o.signal ? [...head, o.signal, cb] : [...head, cb],
|
||||
type: funcOf([], VOID),
|
||||
loc,
|
||||
};
|
||||
}
|
||||
const { cb, dyn } = lowerEosCallback(lowerer, "finished", args[args.length - 1]!, recv.type);
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: dyn ? "stream.finishedDyn" : "stream.finished",
|
||||
|
||||
@@ -1617,6 +1617,11 @@ export const BUILTIN_MODULE_FNS: Record<
|
||||
readline: {
|
||||
createInterface: { fn: "rl.create", params: [], result: F64 },
|
||||
},
|
||||
// node:readline/promises: the same Interface with the promise question
|
||||
// (lowerReadlineMethodCall tells the forms apart by the call's result).
|
||||
"readline/promises": {
|
||||
createInterface: { fn: "rl.create", params: [], result: F64 },
|
||||
},
|
||||
// node:net and node:http lower ENTIRELY through the server spoke
|
||||
// (lower-server.ts — every call shape is special-cased: closures, the
|
||||
// optional connect host, writeHead's literal headers); the member list
|
||||
|
||||
@@ -1928,11 +1928,10 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null {
|
||||
) {
|
||||
return { kind: "object", className: RUNTIME_EMITTER_CLASS };
|
||||
}
|
||||
// readline.Interface: the interface value is an f64 handle into the
|
||||
// runtime's registry (the Timeout-id precedent). The NAME is generic,
|
||||
// so the provenance check adds the enclosing ambient module, like
|
||||
// net.Server (@types/node also declares Interface in
|
||||
// readline/promises — that one stays unmapped and fences).
|
||||
// readline.Interface and readline/promises.Interface: the interface
|
||||
// value is an f64 handle into the runtime's registry (the Timeout-id
|
||||
// precedent). The NAME is generic, so the provenance check adds the
|
||||
// enclosing ambient module, like net.Server.
|
||||
if (
|
||||
psym?.name === "Interface" &&
|
||||
checker
|
||||
@@ -1941,7 +1940,8 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null {
|
||||
(d) =>
|
||||
(ts.isInterfaceDeclaration(d) || ts.isClassDeclaration(d)) &&
|
||||
ctx.isStdlibFile(d.getSourceFile()) &&
|
||||
isDeclaredInAmbientModule(d, "readline"),
|
||||
(isDeclaredInAmbientModule(d, "readline") ||
|
||||
isDeclaredInAmbientModule(d, "readline/promises")),
|
||||
)
|
||||
) {
|
||||
return F64;
|
||||
|
||||
@@ -374,6 +374,15 @@ export const MAY_THROW_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"https.requestFn",
|
||||
"https.requestFnCb",
|
||||
"rl.question",
|
||||
"rl.createSignal",
|
||||
"stream.finishedOpts",
|
||||
"stream.finishedOptsSignal",
|
||||
"stream.addAbortSignal",
|
||||
"stream.addAbortSignalFn",
|
||||
"rl.pause",
|
||||
"rl.resume",
|
||||
"rl.prompt",
|
||||
"rl.write",
|
||||
|
||||
"json.parse",
|
||||
"json.parseReviver",
|
||||
|
||||
@@ -3018,6 +3018,14 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
* propagates the first error by destroying the rest, calls cb after the
|
||||
* last 'close'; answers the destination +1. The Dyn twins take the
|
||||
* callback as a checked-dynamic VALUE (mustCall wrappers). */
|
||||
/** stream.addAbortSignal: [stream, signal] — the constructors' signal
|
||||
* option, chained over the fresh stream (falsy signals are ignored);
|
||||
* stream.addAbortSignalFn: [signal, stream] — addAbortSignal(signal,
|
||||
* stream). Both answer the stream (+1) and throw ERR_INVALID_ARG_TYPE
|
||||
* for a value that is not an AbortSignal; they link the event-target
|
||||
* unit (addAbortListener). */
|
||||
"stream.addAbortSignal": { argTypes: [null, DYN], result: VOID },
|
||||
"stream.addAbortSignalFn": { argTypes: [DYN, null], result: VOID },
|
||||
"stream.finished": { argTypes: [null, null], result: VOID },
|
||||
"stream.finishedDyn": { argTypes: [null, DYN], result: VOID },
|
||||
"stream.pipeline": { argTypes: [F64], result: VOID },
|
||||
@@ -3028,6 +3036,25 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
* chaining/destroyer semantics settling a void promise (fulfilled on a
|
||||
* clean finish, rejected with the finish status otherwise). */
|
||||
"sp.finished": { argTypes: [null], result: { kind: "promise", inner: VOID } },
|
||||
/** The options forms: finished(s, { readable, writable, error, cleanup,
|
||||
* signal }) as [s, readable, writable, error, cleanup, signal?] —
|
||||
* readable/writable -1 (absent), 0 or 1 — and pipeline(...streams,
|
||||
* { signal, end }) as [count, s1..sn, end, signal?]. The Signal forms
|
||||
* link the event-target unit (addAbortListener). The callback form
|
||||
* stream.finishedOpts: [s, readable, writable, error, signal?, cb] →
|
||||
* the cleanup closure. */
|
||||
"sp.finishedOpts": {
|
||||
argTypes: [null, F64, F64, BOOL, BOOL],
|
||||
result: { kind: "promise", inner: VOID },
|
||||
},
|
||||
"sp.finishedOptsSignal": {
|
||||
argTypes: [null, F64, F64, BOOL, BOOL, DYN],
|
||||
result: { kind: "promise", inner: VOID },
|
||||
},
|
||||
"sp.pipelineOpts": { argTypes: [F64], result: { kind: "promise", inner: VOID } },
|
||||
"sp.pipelineOptsSignal": { argTypes: [F64], result: { kind: "promise", inner: VOID } },
|
||||
"stream.finishedOpts": { argTypes: [null, F64, F64, BOOL, null], result: VOID },
|
||||
"stream.finishedOptsSignal": { argTypes: [null, F64, F64, BOOL, DYN, null], result: VOID },
|
||||
"sp.pipeline": { argTypes: [F64], result: { kind: "promise", inner: VOID } },
|
||||
/** node:stream/consumers — the promise consumers over the readable
|
||||
* machinery: (s) → a pending promise settled at the terminal point
|
||||
@@ -3293,27 +3320,46 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
"strdec.next": { argTypes: [STRING, F64, BYTES_U8], result: F64 },
|
||||
"strdec.end": { argTypes: [STRING, F64], result: STRING },
|
||||
// node:readline: the interface handle is f64; the callbacks' func types
|
||||
// are program-dependent (zero-param or (answer: string) — the emitter
|
||||
// picks the adapter), so those slots are null like child.onExit's.
|
||||
/** node:readline's question/close slice (scr_readline.c, linked under
|
||||
* the events gate — these fns imply moduleUsesProcessEvents). The
|
||||
* interface value is an f64 handle (the Timeout-id precedent).
|
||||
* rl.create: [] → f64 — createInterface({ input: process.stdin,
|
||||
* output: process.stdout }), registering the unit's shared stdin
|
||||
* consumer (an OPEN interface keeps the loop alive until close/EOF,
|
||||
* Node's semantics). rl.question: [handle, query, cb] — writes the
|
||||
* query to stdout (Node writes under pipes too) and delivers the next
|
||||
* line's text through a backend-picked adapter (zero-param or
|
||||
* (answer: string)); THROWS Node's "readline was closed" on a closed
|
||||
* interface (may-throw). rl.close: [handle] — fires 'close' listeners
|
||||
* SYNCHRONOUSLY (Node's inline emit) and detaches the consumer (the
|
||||
* loop stops waiting on fd 0). rl.onClose: [handle, cb] — a zero-arg
|
||||
* listener (moves); stdin EOF closes every open interface with the
|
||||
* buffered partial line DISCARDED, like Node. */
|
||||
"rl.create": { argTypes: [], result: F64 },
|
||||
// are program-dependent (the emitter picks the adapter by parameter
|
||||
// count), so those slots are null like child.onExit's.
|
||||
/** node:readline and node:readline/promises (scr_readline.c, linked
|
||||
* under the events gate — these fns imply moduleUsesProcessEvents; a
|
||||
* stream input or output adds scr_stream.c's bridge). The interface
|
||||
* value is an f64 handle (the Timeout-id precedent); an open interface
|
||||
* on stdin keeps the loop alive until close/EOF.
|
||||
* rl.create: [mode, input?, output?, crlfDelay, prompt?] → f64, mode
|
||||
* bits 1 stream input, 2 stream output, 4 stdout output, 16 prompt
|
||||
* (lowerReadlineCreateCall); rl.createSignal: the same plus the
|
||||
* trailing AbortSignal (mode bit 8), which links the event-target unit
|
||||
* for addAbortListener. rl.question: [handle, query, cb] — the callback
|
||||
* form; THROWS ERR_USE_AFTER_CLOSE on a closed interface (may-throw).
|
||||
* rl.questionAsync(Signal): [handle, query, signal?] → promise<string>,
|
||||
* rejecting instead of throwing. rl.close/pause/resume/prompt:
|
||||
* [handle] (pause/resume/prompt throw ERR_USE_AFTER_CLOSE when
|
||||
* closed); rl.setPrompt/getPrompt; rl.write: [handle, data] (throws
|
||||
* when closed). rl.on: [handle, event, cb, once] — the event number
|
||||
* (0 'line', 1 'close', 2 'pause', 3 'resume', 4 'error') and the
|
||||
* listener (moves). rl.iterOpen/iterNext/iterReturn: the async
|
||||
* iterator — iterNext answers promise<dyn> of the line or undefined. */
|
||||
"rl.create": { argTypes: [F64], result: F64 },
|
||||
"rl.createSignal": { argTypes: [F64], result: F64 },
|
||||
"rl.question": { argTypes: [F64, STRING, null], result: VOID },
|
||||
"rl.questionAsync": { argTypes: [F64, STRING], result: { kind: "promise", inner: STRING } },
|
||||
"rl.questionAsyncSignal": {
|
||||
argTypes: [F64, STRING, DYN],
|
||||
result: { kind: "promise", inner: STRING },
|
||||
},
|
||||
"rl.close": { argTypes: [F64], result: VOID },
|
||||
"rl.onClose": { argTypes: [F64, null], result: VOID },
|
||||
"rl.pause": { argTypes: [F64], result: VOID },
|
||||
"rl.resume": { argTypes: [F64], result: VOID },
|
||||
"rl.prompt": { argTypes: [F64], result: VOID },
|
||||
"rl.setPrompt": { argTypes: [F64, STRING], result: VOID },
|
||||
"rl.getPrompt": { argTypes: [F64], result: STRING },
|
||||
"rl.write": { argTypes: [F64, STRING], result: VOID },
|
||||
"rl.on": { argTypes: [F64, F64, null, BOOL], result: VOID },
|
||||
"rl.iterOpen": { argTypes: [F64], result: VOID },
|
||||
"rl.iterNext": { argTypes: [F64], result: { kind: "promise", inner: DYN } },
|
||||
"rl.iterReturn": { argTypes: [F64], result: VOID },
|
||||
/** node:timers/promises — the promisified pair (scr_async.c, beside
|
||||
* the timer heap they ride): tp.setTimeout: [ms] → a pending void
|
||||
* promise a one-shot heap timer fulfills (the loop's timer phase, FIFO
|
||||
|
||||
@@ -164,6 +164,18 @@ function scanRuntimeFeatures(mod: IrModule, stopAt?: keyof RuntimeFeatures): Run
|
||||
features.legacyTextDecoder = true;
|
||||
if (fn.startsWith("fetch.")) features.fetch = true;
|
||||
if (fn.startsWith("event.") || fn === "events.addAbortListener") features.eventTarget = true;
|
||||
// Signal-taking readline entry points call addAbortListener.
|
||||
if (
|
||||
fn === "rl.createSignal" ||
|
||||
fn === "rl.questionAsyncSignal" ||
|
||||
fn === "stream.addAbortSignal" ||
|
||||
fn === "stream.addAbortSignalFn" ||
|
||||
fn === "sp.finishedOptsSignal" ||
|
||||
fn === "sp.pipelineOptsSignal" ||
|
||||
fn === "stream.finishedOptsSignal"
|
||||
) {
|
||||
features.eventTarget = true;
|
||||
}
|
||||
if (PROCESS_EVENT_LIB_FNS.has(fn)) features.processEvents = true;
|
||||
if (fn.startsWith("emitter.")) features.emitter = true;
|
||||
if (
|
||||
@@ -180,7 +192,12 @@ function scanRuntimeFeatures(mod: IrModule, stopAt?: keyof RuntimeFeatures): Run
|
||||
fn === "stream.errored" ||
|
||||
fn === "sp.finished" ||
|
||||
fn === "sp.pipeline" ||
|
||||
fn === "console.bind"
|
||||
fn === "console.bind" ||
|
||||
fn === "stream.addAbortSignal" ||
|
||||
fn === "stream.addAbortSignalFn" ||
|
||||
fn.startsWith("sp.finishedOpts") ||
|
||||
fn.startsWith("sp.pipelineOpts") ||
|
||||
fn.startsWith("stream.finishedOpts")
|
||||
)
|
||||
features.stream = true;
|
||||
if (fn.startsWith("zlib.")) features.zlib = true;
|
||||
@@ -426,9 +443,21 @@ const PROCESS_EVENT_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
// node:readline rides the stdin unit (scr_readline.c links beside
|
||||
// scr_events.c under the same gate).
|
||||
"rl.create",
|
||||
"rl.createSignal",
|
||||
"rl.question",
|
||||
"rl.questionAsync",
|
||||
"rl.questionAsyncSignal",
|
||||
"rl.close",
|
||||
"rl.onClose",
|
||||
"rl.pause",
|
||||
"rl.resume",
|
||||
"rl.prompt",
|
||||
"rl.setPrompt",
|
||||
"rl.getPrompt",
|
||||
"rl.write",
|
||||
"rl.on",
|
||||
"rl.iterOpen",
|
||||
"rl.iterNext",
|
||||
"rl.iterReturn",
|
||||
]);
|
||||
|
||||
/** True when the module uses the process-events surface — the link switch
|
||||
|
||||
@@ -3960,11 +3960,15 @@ function validateFunction(
|
||||
e.fn === "stream.pipeline" ||
|
||||
e.fn === "stream.pipelineDyn" ||
|
||||
e.fn === "sp.pipeline" ||
|
||||
e.fn === "sp.pipelineOpts" ||
|
||||
e.fn === "sp.pipelineOptsSignal" ||
|
||||
e.fn === "writable.write" ||
|
||||
e.fn === "writable.writeStr" ||
|
||||
e.fn === "writable.writeU" ||
|
||||
e.fn === "writable.end" ||
|
||||
e.fn === "readable.unpipe";
|
||||
e.fn === "readable.unpipe" ||
|
||||
e.fn === "rl.create" ||
|
||||
e.fn === "rl.createSignal";
|
||||
if (variadic ? e.args.length < sig.argTypes.length : e.args.length !== sig.argTypes.length) {
|
||||
err(`libCall ${e.fn}: ${e.args.length} args, expected ${sig.argTypes.length}`, e.loc);
|
||||
}
|
||||
@@ -5163,7 +5167,11 @@ function validateFunction(
|
||||
e.fn === "stream.finished" ||
|
||||
e.fn === "stream.finishedDyn" ||
|
||||
e.fn === "stream.pipeline" ||
|
||||
e.fn === "stream.pipelineDyn"
|
||||
e.fn === "stream.pipelineDyn" ||
|
||||
e.fn === "stream.addAbortSignal" ||
|
||||
e.fn === "stream.addAbortSignalFn" ||
|
||||
e.fn === "stream.finishedOpts" ||
|
||||
e.fn === "stream.finishedOptsSignal"
|
||||
) {
|
||||
// Receiver/result: stream-hierarchy objects (a class whose base
|
||||
// chain reaches a runtime stream class). Constructors have no
|
||||
@@ -5179,6 +5187,15 @@ function validateFunction(
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (e.fn === "stream.addAbortSignal" || e.fn === "stream.addAbortSignalFn") {
|
||||
// [stream, signal] (the option) or [signal, stream] (the module
|
||||
// function): the result is the stream argument's type.
|
||||
const s = e.args[e.fn === "stream.addAbortSignal" ? 0 : 1];
|
||||
if (!isStreamObject(s?.type) || !typeEquals(e.type, s!.type)) {
|
||||
err(`libCall ${e.fn} must take and return a stream-hierarchy object`, e.loc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (e.fn.endsWith(".new")) {
|
||||
if (!isStreamObject(e.type)) {
|
||||
err(`libCall ${e.fn} must return a stream class, got ${e.type.kind}`, e.loc);
|
||||
@@ -5210,6 +5227,20 @@ function validateFunction(
|
||||
if (e.type.kind !== "void") err(`libCall ${e.fn} must be void`, e.loc);
|
||||
return true;
|
||||
}
|
||||
if (e.fn === "stream.finishedOpts" || e.fn === "stream.finishedOptsSignal") {
|
||||
// (recv, readable, writable, error[, signal], cb) → the cleanup
|
||||
// closure; the callback is a func or a checked-dynamic value.
|
||||
if (!isStreamObject(e.args[0]?.type)) {
|
||||
err(`libCall ${e.fn} receiver must be a stream-hierarchy object`, e.loc);
|
||||
}
|
||||
const cbK = e.args[e.args.length - 1]?.type.kind;
|
||||
if (cbK !== "func" && cbK !== "dyn")
|
||||
err(`libCall ${e.fn} callback must be a func or dyn`, e.loc);
|
||||
if (e.type.kind !== "func") {
|
||||
err(`libCall ${e.fn} must return the cleanup closure, got ${e.type.kind}`, e.loc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (e.fn === "stream.finished" || e.fn === "stream.finishedDyn") {
|
||||
// (recv, cb) → the cleanup closure.
|
||||
if (!isStreamObject(e.args[0]?.type)) {
|
||||
|
||||
@@ -2049,6 +2049,19 @@
|
||||
"name": "readline.createInterface",
|
||||
"status": "static"
|
||||
},
|
||||
{
|
||||
"id": "node-builtin.readline.promises",
|
||||
"kind": "node-builtin",
|
||||
"name": "readline/promises",
|
||||
"status": "static",
|
||||
"note": "recognized module (bare and node:-prefixed specifiers)"
|
||||
},
|
||||
{
|
||||
"id": "node-builtin.readline.promises.createInterface",
|
||||
"kind": "node-builtin",
|
||||
"name": "readline/promises.createInterface",
|
||||
"status": "static"
|
||||
},
|
||||
{
|
||||
"id": "node-builtin.stream",
|
||||
"kind": "node-builtin",
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5295-readline-promises-question.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5296-readline-lines.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5297-readline-callback-events.cjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5298-readline-abort.cjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5299-readline-stdin.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5300-stream-add-abort-signal.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5301-stream-add-abort-signal-errors.cjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5302-stream-promises-options.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5303-stream-finished-callback-opts.cjs"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/fixtures/node-types/readline-promises.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+923
-176
File diff suppressed because it is too large
Load Diff
@@ -2992,6 +2992,10 @@ extern SCR_TL void (*scr_emitter_on_hook)(ScrEmitter *em, ScrStr *name);
|
||||
* are called by compiler-emitted completion-callback closures. */
|
||||
typedef struct ScrStreamState ScrStreamState;
|
||||
typedef struct ScrBytes ScrBytes; /* full definition below (C11 repeat) */
|
||||
/* events.addAbortListener (scr_events_add_abort_listener): passed to the
|
||||
* signal-taking stream and readline entry points by programs that link
|
||||
* the event-target unit. */
|
||||
typedef ScrDyn *(*ScrAbortAddFn)(ScrDyn *signal, ScrDyn *listener);
|
||||
|
||||
typedef struct ScrStream {
|
||||
size_t rc;
|
||||
@@ -3227,6 +3231,30 @@ ScrStream *scr_stream_destroy(ScrStream *s, ScrError *err); /* recv +1 */
|
||||
* the iterator's internal error consumer suppresses an unhandled crash when
|
||||
* no user error listener exists. Receiver +1. */
|
||||
ScrStream *scr_stream_iterator_close(ScrStream *s);
|
||||
/* stream.addAbortSignal(signal, s) and the constructors' signal option
|
||||
* (from_option: a falsy signal is ignored). Borrows; answers s +1, or
|
||||
* NULL with ERR_INVALID_ARG_TYPE pending. */
|
||||
ScrStream *scr_stream_add_abort_signal(ScrStream *s, ScrDyn *signal, bool from_option,
|
||||
ScrAbortAddFn add);
|
||||
/* finished(s, options[, cb]) — Node's eos with its listeners and state
|
||||
* predicates: readable/writable are -1 (the default), 0 or 1; on_error is
|
||||
* options.error !== false; signal is NULL or borrowed (ERR_INVALID_ARG_TYPE
|
||||
* pending and NULL answered for a non-signal). The callback forms answer
|
||||
* the +1 cleanup closure (cb moves; the typed form takes its invoke
|
||||
* adapter); the promise form rejects instead of throwing. */
|
||||
ScrClosure *scr_stream_finished_opts(ScrStream *s, ScrClosure *cb, ScrStreamErrInv inv,
|
||||
double readable, double writable, bool on_error,
|
||||
ScrDyn *signal, ScrAbortAddFn add);
|
||||
ScrClosure *scr_stream_finished_opts_dyn(ScrStream *s, ScrDyn *cb, double readable,
|
||||
double writable, bool on_error, ScrDyn *signal,
|
||||
ScrAbortAddFn add);
|
||||
ScrPromise *scr_sp_finished_opts(ScrStream *s, double readable, double writable, bool on_error,
|
||||
bool cleanup, ScrDyn *signal, ScrAbortAddFn add);
|
||||
/* stream/promises pipeline(...streams, { signal, end }): end false leaves
|
||||
* the destination open; a non-signal value rejects with
|
||||
* ERR_INVALID_ARG_TYPE. Streams borrowed. */
|
||||
ScrPromise *scr_sp_pipeline_opts(double n, ScrStream **streams, bool end, ScrDyn *signal,
|
||||
ScrAbortAddFn add);
|
||||
double scr_stream_prop(ScrStream *s, const char *name);
|
||||
ScrError *scr_stream_errored(ScrStream *s); /* +1 or NULL */
|
||||
|
||||
@@ -6494,22 +6522,59 @@ bool scr_stdin_pending(void);
|
||||
void scr_stdin_remove_data(ScrClosure *cb);
|
||||
bool scr_stdin_ended(void);
|
||||
|
||||
/* ── node:readline (scr_readline.c, linked under the events gate) ──────
|
||||
* The question/close slice, Node's semantics pinned under pipes (the file
|
||||
* header has the full model): create answers the f64 interface handle;
|
||||
* question writes the query to stdout and delivers the next line's text
|
||||
* through the adapter (fn(cb, +1 answer)) — throwing Node's
|
||||
* "readline was closed" on a closed interface (may-throw); close fires
|
||||
* the 'close' listeners SYNCHRONOUSLY (Node's inline emit) and detaches
|
||||
* the stdin consumer; onClose registers a zero-arg listener (moves).
|
||||
* Answer adapters: thunk0 ignores the line, thunk_str passes it. */
|
||||
double scr_rl_create(void);
|
||||
void scr_rl_question(double id, const ScrStr *query, ScrClosure *cb /*moves*/,
|
||||
/* ── node:readline and node:readline/promises (scr_readline.c, linked
|
||||
* under the events gate) ─────────────────────────────────────────────
|
||||
* The non-terminal Interface (the file header has the model). Interfaces
|
||||
* are f64 handles. A stream input or output goes through ScrRlIo, which
|
||||
* scr_stream.c provides (scr_stream_rl_open), so stdin-only programs
|
||||
* never link the stream unit; the stream side feeds input through
|
||||
* scr_rl_input_*. Signal-taking entry points get addAbortListener as
|
||||
* `add` (scr_events_add_abort_listener), passed only by programs that
|
||||
* link the event-target unit. Listener event numbers: 0 'line',
|
||||
* 1 'close', 2 'pause', 3 'resume', 4 'error'. */
|
||||
typedef struct ScrRlIo {
|
||||
void (*pause)(void *s);
|
||||
void (*resume)(void *s);
|
||||
void (*detach)(void *s, double id); /* removes the interface's input listeners */
|
||||
void (*write)(void *s, ScrStr *text); /* borrows text */
|
||||
void (*release)(void *s);
|
||||
} ScrRlIo;
|
||||
double scr_rl_open(const ScrRlIo *io, void *in /*+1 or NULL: stdin*/, void *out /*+1 or NULL*/,
|
||||
bool out_stdout, double crlf_delay, ScrStr *prompt, ScrDyn *signal,
|
||||
ScrAbortAddFn add);
|
||||
void scr_rl_input_bytes(double id, ScrBytes *chunk);
|
||||
void scr_rl_input_str(double id, ScrStr *chunk);
|
||||
void scr_rl_input_end(double id);
|
||||
void scr_rl_input_error(double id, ScrError *err);
|
||||
/* question: throws ERR_USE_AFTER_CLOSE on a closed interface (may-throw);
|
||||
* fn(cb, +1 answer). The promise form never throws. */
|
||||
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);
|
||||
void scr_rl_close(double id);
|
||||
void scr_rl_on_close(double id, ScrClosure *cb /*moves*/);
|
||||
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_set_prompt(double id, ScrStr *prompt);
|
||||
ScrStr *scr_rl_get_prompt(double id); /* +1 */
|
||||
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);
|
||||
void scr_rl_iter_open(double id);
|
||||
ScrPromise *scr_rl_iter_next(double id); /* +1 promise<dyn>: the line, or undefined */
|
||||
void scr_rl_iter_return(double id);
|
||||
/* Listener adapters: line/answer (fn(cb, +1 text)) and error listeners. */
|
||||
void scr_rl_answer_thunk0(ScrClosure *cb, ScrStr *answer);
|
||||
void scr_rl_answer_thunk_str(ScrClosure *cb, ScrStr *answer);
|
||||
void scr_rl_error_thunk(ScrClosure *cb, ScrError *err);
|
||||
void scr_rl_error_thunk0(ScrClosure *cb, ScrError *err);
|
||||
void scr_rl_answer_thunk_dyn(ScrClosure *cb, ScrStr *answer);
|
||||
void scr_rl_error_thunk_dyn(ScrClosure *cb, ScrError *err);
|
||||
/* createInterface with a stream input and/or output (scr_stream.c): in is
|
||||
* 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 "> "). */
|
||||
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);
|
||||
void scr_stdin_data_thunk_bytes(ScrClosure *cb, ScrBytes *chunk);
|
||||
/* The loop-side registration (scr_async.c, always linked) and the
|
||||
|
||||
@@ -2097,12 +2097,40 @@ void scr_stream_finished_dyn_inv(ScrClosure *cb, ScrStream *s, ScrError *err) {
|
||||
typedef struct {
|
||||
size_t rc;
|
||||
size_t n, closed;
|
||||
size_t watched; /* stages with a watcher: all, or all but the last (end: false) */
|
||||
ScrStream **streams; /* owned */
|
||||
ScrClosure *cb; /* owned; NULL once fired */
|
||||
ScrStreamErrInv inv;
|
||||
ScrError *err; /* first error, owned */
|
||||
ScrDyn *dispose; /* the signal listener's disposable (+1), or NULL */
|
||||
/* contexts holding a disposable: it holds the abort listener, which
|
||||
* holds the context, so exit releases what a pipeline that never
|
||||
* finished still holds */
|
||||
void *live_prev, *live_next;
|
||||
} ScrStreamPipeCtx;
|
||||
|
||||
static SCR_TL ScrStreamPipeCtx *scr_pipe_live = NULL;
|
||||
|
||||
static void scr_pipe_unlink(ScrStreamPipeCtx *ctx) {
|
||||
ScrStreamPipeCtx *prev = ctx->live_prev, *next = ctx->live_next;
|
||||
if (prev) prev->live_next = next;
|
||||
else if (scr_pipe_live == ctx) scr_pipe_live = next;
|
||||
if (next) next->live_prev = prev;
|
||||
ctx->live_prev = ctx->live_next = NULL;
|
||||
}
|
||||
|
||||
static void scr_pipe_atexit(void) {
|
||||
while (scr_pipe_live) {
|
||||
ScrStreamPipeCtx *ctx = scr_pipe_live;
|
||||
scr_pipe_unlink(ctx);
|
||||
if (ctx->dispose) {
|
||||
ScrDyn *d = ctx->dispose;
|
||||
ctx->dispose = NULL;
|
||||
scr_dyn_release(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void *scr_stream_pipectx_retain_v(void *p) {
|
||||
((ScrStreamPipeCtx *)p)->rc++;
|
||||
return p;
|
||||
@@ -2115,24 +2143,38 @@ static void scr_stream_pipectx_release_v(void *p) {
|
||||
free(ctx->streams);
|
||||
if (ctx->cb) scr_closure_release(ctx->cb);
|
||||
if (ctx->err) scr_error_release(ctx->err);
|
||||
if (ctx->dispose) scr_dyn_release(ctx->dispose);
|
||||
scr_pipe_unlink(ctx);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void scr_stream_pipectx_dispose(ScrStreamPipeCtx *ctx) {
|
||||
if (!ctx->dispose) return;
|
||||
scr_pipe_unlink(ctx);
|
||||
ScrDyn *d = ctx->dispose;
|
||||
ctx->dispose = NULL;
|
||||
ScrDyn *r = scr_dyn_call(d, NULL, 0, "dispose");
|
||||
if (r) scr_dyn_release(r);
|
||||
scr_dyn_release(d);
|
||||
}
|
||||
|
||||
static void scr_stream_pipeline_watch_inv(ScrClosure *cb, ScrStream *s, ScrError *err) {
|
||||
ScrStreamPipeCtx *ctx = scr_box_get_ref(cb->caps[0]); /* +1 */
|
||||
ctx->closed++;
|
||||
if (err != NULL && ctx->err == NULL) {
|
||||
ctx->err = scr_error_retain(err);
|
||||
scr_stream_pipectx_dispose(ctx);
|
||||
/* Node's pipeline destroyer: every other stream goes down with the
|
||||
* SAME error, pipeline order (each emits 'error' then 'close'). */
|
||||
for (size_t i = 0; i < ctx->n; i++) {
|
||||
for (size_t i = 0; i < ctx->watched; i++) {
|
||||
if (ctx->streams[i] != s) {
|
||||
ScrStream *chain = scr_stream_destroy(ctx->streams[i], ctx->err);
|
||||
scr_stream_release(chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx->closed == ctx->n && ctx->cb != NULL) {
|
||||
if (ctx->closed == ctx->watched && ctx->cb != NULL) {
|
||||
scr_stream_pipectx_dispose(ctx);
|
||||
ScrClosure *ucb = ctx->cb;
|
||||
ctx->cb = NULL;
|
||||
ctx->inv(ucb, ctx->streams[ctx->n - 1], ctx->err);
|
||||
@@ -2154,31 +2196,90 @@ ScrStream *scr_stream_pipeline_dyn(double n, ScrStream **streams, ScrDyn *cb) {
|
||||
return scr_stream_pipeline(n, streams, c, &scr_stream_finished_dyn_inv);
|
||||
}
|
||||
|
||||
static ScrError *scr_stream_abort_error(ScrDyn *signal);
|
||||
static ScrDyn *scr_stream_signal_prop(ScrDyn *signal, const char *key);
|
||||
static void *scr_stream_dyn_retain_v(void *p);
|
||||
static void scr_stream_dyn_release_v(void *p);
|
||||
|
||||
/* pipeline's signal: an abort destroys every watched stage with
|
||||
* new AbortError(undefined, { cause: signal.reason }) (finishImpl's
|
||||
* destroyers); the callback follows the last close. caps[0] the context,
|
||||
* caps[1] the signal. */
|
||||
static ScrDyn *scr_stream_pipeline_abort_thunk(ScrClosure *clo, ScrDyn *const *args, size_t argc) {
|
||||
(void)args;
|
||||
(void)argc;
|
||||
ScrStreamPipeCtx *ctx = scr_box_get_ref(clo->caps[0]); /* +1 */
|
||||
ScrDyn *signal = scr_box_get_ref(clo->caps[1]); /* +1 */
|
||||
if (ctx->err == NULL && ctx->cb != NULL) {
|
||||
ctx->err = scr_stream_abort_error(signal);
|
||||
scr_stream_pipectx_dispose(ctx);
|
||||
for (size_t i = 0; i < ctx->watched; i++) {
|
||||
ScrStream *chain = scr_stream_destroy(ctx->streams[i], ctx->err);
|
||||
scr_stream_release(chain);
|
||||
}
|
||||
}
|
||||
scr_dyn_release(signal);
|
||||
scr_stream_pipectx_release_v(ctx);
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
|
||||
static ScrStream *scr_stream_pipeline_ex(size_t n, ScrStream **streams, ScrClosure *cb,
|
||||
ScrStreamErrInv inv, bool end, ScrDyn *signal,
|
||||
ScrAbortAddFn add);
|
||||
|
||||
ScrStream *scr_stream_pipeline(double n_d, ScrStream **streams /*borrowed*/,
|
||||
ScrClosure *cb /*moves*/, ScrStreamErrInv inv) {
|
||||
size_t n = (size_t)n_d;
|
||||
return scr_stream_pipeline_ex((size_t)n_d, streams, cb, inv, true, NULL, NULL);
|
||||
}
|
||||
|
||||
static ScrStream *scr_stream_pipeline_ex(size_t n, ScrStream **streams, ScrClosure *cb,
|
||||
ScrStreamErrInv inv, bool end, ScrDyn *signal,
|
||||
ScrAbortAddFn add) {
|
||||
ScrStreamPipeCtx *ctx = calloc(1, sizeof *ctx);
|
||||
if (!ctx) scr_stream_oom();
|
||||
ctx->rc = 1;
|
||||
ctx->n = n;
|
||||
ctx->watched = end ? n : n - 1;
|
||||
ctx->streams = calloc(n, sizeof *ctx->streams);
|
||||
if (!ctx->streams) scr_stream_oom();
|
||||
for (size_t i = 0; i < n; i++) ctx->streams[i] = scr_stream_retain(streams[i]);
|
||||
ctx->cb = cb;
|
||||
ctx->inv = inv;
|
||||
/* Chain the pipes first (end: true — Node's pipeline forwards EOF). */
|
||||
/* Chain the pipes first (Node's pipeline forwards EOF; end: false
|
||||
* leaves the destination open). */
|
||||
for (size_t i = 0; i + 1 < n; i++) {
|
||||
ScrStream *d = scr_stream_pipe(streams[i], streams[i + 1], true);
|
||||
ScrStream *d = scr_stream_pipe(streams[i], streams[i + 1], end || i + 2 < n);
|
||||
scr_stream_release(d);
|
||||
}
|
||||
/* One watcher per stream: the terminal statuses drive error propagation
|
||||
* and the final callback (after the LAST 'close'). */
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
/* One watcher per stream (the destination is not watched under end:
|
||||
* false): the terminal statuses drive error propagation and the final
|
||||
* callback (after the LAST 'close'). */
|
||||
for (size_t i = 0; i < ctx->watched; i++) {
|
||||
ScrClosure *w = scr_closure_new((void *)&scr_stream_pipeline_watch_inv, 1);
|
||||
w->caps[0] = scr_box_new_obj(&scr_stream_pipectx_retain_v, &scr_stream_pipectx_release_v, NULL);
|
||||
scr_box_set_ref(w->caps[0], scr_stream_pipectx_retain_v(ctx));
|
||||
scr_stream_fin_add(streams[i]->st, w, &scr_stream_pipeline_watch_inv);
|
||||
}
|
||||
if (signal) {
|
||||
ScrClosure *clo = scr_closure_new((void *)&scr_stream_pipeline_abort_thunk, 2);
|
||||
clo->caps[0] = scr_box_new_obj(&scr_stream_pipectx_retain_v, &scr_stream_pipectx_release_v, NULL);
|
||||
scr_box_set_ref(clo->caps[0], scr_stream_pipectx_retain_v(ctx));
|
||||
clo->caps[1] = scr_box_new_obj(&scr_stream_dyn_retain_v, &scr_stream_dyn_release_v, NULL);
|
||||
scr_box_set_ref(clo->caps[1], scr_dyn_retain(signal));
|
||||
ScrDyn *listener = scr_dyn_new_func(clo, &scr_stream_pipeline_abort_thunk, 0, "()", "abort");
|
||||
ctx->dispose = add(signal, listener);
|
||||
scr_dyn_release(listener);
|
||||
if (ctx->dispose) {
|
||||
static SCR_TL bool registered = false;
|
||||
if (!registered) {
|
||||
registered = true;
|
||||
scr_atexit(scr_pipe_atexit);
|
||||
}
|
||||
ctx->live_next = scr_pipe_live;
|
||||
if (scr_pipe_live) scr_pipe_live->live_prev = ctx;
|
||||
scr_pipe_live = ctx;
|
||||
}
|
||||
}
|
||||
scr_stream_pipectx_release_v(ctx); /* the boxes own it now */
|
||||
return scr_stream_retain(streams[n - 1]);
|
||||
}
|
||||
@@ -2226,6 +2327,32 @@ ScrPromise *scr_sp_pipeline(double n, ScrStream **streams) {
|
||||
return p;
|
||||
}
|
||||
|
||||
/* pipeline(...streams, { signal, end }): end false leaves the destination
|
||||
* open (the promise settles once the stages before it finish); signal is
|
||||
* NULL or borrowed — a value that is not an AbortSignal rejects with
|
||||
* ERR_INVALID_ARG_TYPE before anything is piped. */
|
||||
ScrPromise *scr_sp_pipeline_opts(double n, ScrStream **streams, bool end, ScrDyn *signal,
|
||||
ScrAbortAddFn add) {
|
||||
ScrPromise *p = scr_promise_new();
|
||||
if (signal && scr_dyn_is_nullish(signal)) signal = NULL;
|
||||
if (signal) {
|
||||
ScrDyn *aborted = NULL;
|
||||
if (signal->kind == SCR_DYN_OBJ || signal->kind == SCR_DYN_HANDLE) {
|
||||
aborted = scr_stream_signal_prop(signal, "aborted");
|
||||
}
|
||||
if (!aborted) {
|
||||
scr_dyn_prop_type_fail("options.signal", "an instance of AbortSignal", signal);
|
||||
scr_promise_reject_pending(p);
|
||||
return p;
|
||||
}
|
||||
scr_dyn_release(aborted);
|
||||
}
|
||||
ScrStream *dst = scr_stream_pipeline_ex((size_t)n, streams, scr_sp_watcher(p),
|
||||
&scr_sp_settle_inv, end, signal, add);
|
||||
scr_stream_release(dst);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* ── node:stream/consumers ────────────────────────────────────────────
|
||||
* text/json/buffer/arrayBuffer over the readable machinery: a native 'data' listener
|
||||
* accumulates every chunk (Buffer chunks as-is; string chunks — an
|
||||
@@ -2369,6 +2496,671 @@ ScrPromise *scr_sc_json(ScrStream *s) { return scr_sc_consume(s, SCR_SC_JSON); }
|
||||
ScrPromise *scr_sc_buffer(ScrStream *s) { return scr_sc_consume(s, SCR_SC_BUFFER); }
|
||||
ScrPromise *scr_sc_array_buffer(ScrStream *s) { return scr_sc_consume(s, SCR_SC_ARRAY_BUFFER); }
|
||||
|
||||
/* ── stream.addAbortSignal and the constructors' signal option ─────────
|
||||
* lib/internal/streams/add-abort-signal.js: the signal must be an object
|
||||
* with an `aborted` property (ERR_INVALID_ARG_TYPE otherwise). An aborted
|
||||
* signal destroys the stream now with new AbortError(undefined, { cause:
|
||||
* signal.reason }); otherwise an abort listener does it later, and the
|
||||
* listener is removed when the stream finishes (eos — whose own 'error'
|
||||
* listener also marks the stream's errors handled). The signal's
|
||||
* properties are read through the generic checked-dynamic getters, and
|
||||
* addAbortListener arrives as `add` from the event-target unit, so this
|
||||
* unit links without it. */
|
||||
|
||||
static ScrDyn *scr_stream_signal_prop(ScrDyn *signal, const char *key) {
|
||||
size_t n = strlen(key);
|
||||
if (signal->kind == SCR_DYN_OBJ) {
|
||||
ScrDyn *v = scr_dyn_obj_get(signal, key, n);
|
||||
return v ? scr_dyn_retain(v) : NULL;
|
||||
}
|
||||
ScrStr *k = scr_str_new(key, n);
|
||||
ScrDyn *v = scr_dyn_handle_key_get(signal, k);
|
||||
scr_str_release(k);
|
||||
return v;
|
||||
}
|
||||
|
||||
static ScrError *scr_stream_abort_error(ScrDyn *signal) {
|
||||
ScrStr *message = scr_str_new("The operation was aborted", 25);
|
||||
ScrError *e = scr_error_new(SCR_ERR_ERROR, message);
|
||||
scr_str_release(message);
|
||||
scr_str_release(e->name);
|
||||
e->name = scr_str_new("AbortError", 10);
|
||||
/* AbortError assigns code, then name: own enumerable properties */
|
||||
scr_error_set_code(e, "ABORT_ERR");
|
||||
e->name_present = true;
|
||||
e->name_enumerable = true;
|
||||
ScrDyn *reason = scr_stream_signal_prop(signal, "reason");
|
||||
scr_error_define_cause(e, reason ? reason : scr_dyn_undefined());
|
||||
if (reason) scr_dyn_release(reason);
|
||||
return e;
|
||||
}
|
||||
|
||||
static void scr_stream_abort_now(ScrStream *s, ScrDyn *signal) {
|
||||
ScrError *e = scr_stream_abort_error(signal);
|
||||
scr_stream_release(scr_stream_destroy(s, e));
|
||||
scr_error_release(e);
|
||||
}
|
||||
|
||||
/* onAbort: caps[0] the stream, caps[1] the signal. */
|
||||
static ScrDyn *scr_stream_abort_thunk(ScrClosure *clo, ScrDyn *const *args, size_t argc) {
|
||||
(void)args;
|
||||
(void)argc;
|
||||
ScrStream *s = scr_box_get_ref(clo->caps[0]); /* +1, NULL after exit teardown */
|
||||
ScrDyn *signal = scr_box_get_ref(clo->caps[1]); /* +1 */
|
||||
if (s) scr_stream_abort_now(s, signal);
|
||||
scr_dyn_release(signal);
|
||||
if (s) scr_stream_release(s);
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
|
||||
/* A pending abort hook: the listener closure (borrowed) holds the stream,
|
||||
* and the stream's watcher holds the listener through the disposable — a
|
||||
* cycle the stream's finish breaks. Hooks still pending at exit drop the
|
||||
* listener's stream so the stream can be freed. */
|
||||
typedef struct ScrAbortHook {
|
||||
ScrClosure *listener; /* borrowed */
|
||||
struct ScrAbortHook *prev, *next;
|
||||
} ScrAbortHook;
|
||||
|
||||
static SCR_TL ScrAbortHook *scr_abort_hooks = NULL;
|
||||
|
||||
static void scr_abort_hook_unlink(ScrAbortHook *h) {
|
||||
if (h->prev) h->prev->next = h->next;
|
||||
else if (scr_abort_hooks == h) scr_abort_hooks = h->next;
|
||||
if (h->next) h->next->prev = h->prev;
|
||||
free(h);
|
||||
}
|
||||
|
||||
static void scr_abort_hooks_atexit(void) {
|
||||
while (scr_abort_hooks) {
|
||||
ScrAbortHook *h = scr_abort_hooks;
|
||||
scr_box_set_ref(h->listener->caps[0], NULL);
|
||||
scr_abort_hook_unlink(h);
|
||||
}
|
||||
}
|
||||
|
||||
static void *scr_stream_ptr_keep_v(void *p) { return p; }
|
||||
static void scr_stream_ptr_drop_v(void *p) { (void)p; }
|
||||
|
||||
/* The eos callback: the disposable (caps[0]) removes the abort listener;
|
||||
* caps[1] is the pending hook. */
|
||||
static void scr_stream_abort_dispose_inv(ScrClosure *cb, ScrStream *s, ScrError *err) {
|
||||
(void)s;
|
||||
(void)err;
|
||||
ScrAbortHook *h = scr_box_get_ref(cb->caps[1]);
|
||||
if (h) {
|
||||
scr_box_set_ref(cb->caps[1], NULL);
|
||||
scr_abort_hook_unlink(h);
|
||||
}
|
||||
ScrDyn *dispose = scr_box_get_ref(cb->caps[0]); /* +1 */
|
||||
ScrDyn *r = scr_dyn_call(dispose, NULL, 0, "dispose");
|
||||
if (r) scr_dyn_release(r);
|
||||
scr_dyn_release(dispose);
|
||||
}
|
||||
|
||||
static void *scr_stream_dyn_retain_v(void *p) { return p ? scr_dyn_retain(p) : NULL; }
|
||||
static void scr_stream_dyn_release_v(void *p) { scr_dyn_release(p); }
|
||||
|
||||
/* addAbortSignal(signal, stream), or the `signal` option (from_option:
|
||||
* a falsy value is ignored, Node's `if (options.signal)`). Borrows both;
|
||||
* answers the stream +1, or NULL with ERR_INVALID_ARG_TYPE pending. */
|
||||
ScrStream *scr_stream_add_abort_signal(ScrStream *s, ScrDyn *signal, bool from_option,
|
||||
ScrAbortAddFn add) {
|
||||
if (from_option && (signal == NULL || !scr_dyn_truthy(signal))) return scr_stream_retain(s);
|
||||
if (signal && signal->kind == SCR_DYN_NULL) {
|
||||
/* typeof null is 'object': the `in` test itself throws */
|
||||
static const char m[] = "Cannot use 'in' operator to search for 'aborted' in null";
|
||||
scr_throw_error_msg(SCR_ERR_TYPE, m, sizeof m - 1);
|
||||
return NULL;
|
||||
}
|
||||
ScrDyn *aborted = NULL;
|
||||
if (signal && (signal->kind == SCR_DYN_OBJ || signal->kind == SCR_DYN_HANDLE)) {
|
||||
aborted = scr_stream_signal_prop(signal, "aborted");
|
||||
}
|
||||
if (aborted == NULL) {
|
||||
scr_dyn_arg_type_fail("signal", "an instance of AbortSignal",
|
||||
signal ? signal : scr_dyn_undefined());
|
||||
return NULL;
|
||||
}
|
||||
bool is_aborted = scr_dyn_truthy(aborted);
|
||||
scr_dyn_release(aborted);
|
||||
if (is_aborted) {
|
||||
scr_stream_abort_now(s, signal);
|
||||
return scr_stream_retain(s);
|
||||
}
|
||||
ScrClosure *clo = scr_closure_new((void *)&scr_stream_abort_thunk, 2);
|
||||
clo->caps[0] = scr_box_new_obj(&scr_stream_retain_v, &scr_stream_release_v, &scr_stream_trace);
|
||||
scr_box_set_ref(clo->caps[0], scr_stream_retain(s));
|
||||
clo->caps[1] = scr_box_new_obj(&scr_stream_dyn_retain_v, &scr_stream_dyn_release_v, NULL);
|
||||
scr_box_set_ref(clo->caps[1], scr_dyn_retain(signal));
|
||||
ScrDyn *listener = scr_dyn_new_func(clo, &scr_stream_abort_thunk, 0, "()", "onAbort");
|
||||
ScrDyn *dispose = add(signal, listener);
|
||||
scr_dyn_release(listener);
|
||||
if (dispose == NULL) return NULL;
|
||||
static SCR_TL bool registered = false;
|
||||
if (!registered) {
|
||||
registered = true;
|
||||
scr_atexit(scr_abort_hooks_atexit);
|
||||
}
|
||||
ScrAbortHook *h = calloc(1, sizeof *h);
|
||||
if (!h) scr_stream_oom();
|
||||
h->listener = clo;
|
||||
h->next = scr_abort_hooks;
|
||||
if (scr_abort_hooks) scr_abort_hooks->prev = h;
|
||||
scr_abort_hooks = h;
|
||||
ScrClosure *w = scr_closure_new((void *)&scr_stream_abort_dispose_inv, 2);
|
||||
w->caps[0] = scr_box_new_obj(&scr_stream_dyn_retain_v, &scr_stream_dyn_release_v, NULL);
|
||||
scr_box_set_ref(w->caps[0], dispose); /* moves */
|
||||
w->caps[1] = scr_box_new_obj(&scr_stream_ptr_keep_v, &scr_stream_ptr_drop_v, NULL);
|
||||
scr_box_set_ref(w->caps[1], h);
|
||||
scr_closure_release(scr_stream_finished(s, w, &scr_stream_abort_dispose_inv));
|
||||
return scr_stream_retain(s);
|
||||
}
|
||||
|
||||
/* ── node:readline over stream inputs and outputs ─────────────────────
|
||||
* The Interface's stream side (scr_readline.c has the model): 'data',
|
||||
* 'end' and 'error' listeners on the input, registered in that order like
|
||||
* Node's constructor ('error' first there; it is never observable before
|
||||
* the others), removed again when the interface closes, and prompt
|
||||
* writes to the output. The input resumes at creation (input.resume()). */
|
||||
|
||||
typedef struct ScrRlHook {
|
||||
ScrStream *s; /* borrowed: the interface holds the stream */
|
||||
double id;
|
||||
ScrClosure *data, *end, *error; /* borrowed: the emitter owns them */
|
||||
struct ScrRlHook *next;
|
||||
} ScrRlHook;
|
||||
|
||||
static SCR_TL ScrRlHook *scr_rl_hooks = NULL;
|
||||
|
||||
static double scr_stream_rl_id(ScrClosure *cb) { return scr_box_get_f64(cb->caps[0]); }
|
||||
|
||||
static void scr_stream_rl_data(ScrClosure *cb, void *b, void *str) {
|
||||
if (b != NULL) scr_rl_input_bytes(scr_stream_rl_id(cb), (ScrBytes *)b);
|
||||
else if (str != NULL) scr_rl_input_str(scr_stream_rl_id(cb), (ScrStr *)str);
|
||||
}
|
||||
|
||||
static void scr_stream_rl_end(ScrClosure *cb) { scr_rl_input_end(scr_stream_rl_id(cb)); }
|
||||
|
||||
static void scr_stream_rl_error(ScrClosure *cb, void *err) {
|
||||
scr_rl_input_error(scr_stream_rl_id(cb), (ScrError *)err);
|
||||
}
|
||||
|
||||
static ScrClosure *scr_stream_rl_listener(void *fn, double id) {
|
||||
ScrClosure *c = scr_closure_new(fn, 1);
|
||||
c->caps[0] = scr_box_new(SCR_BOX_F64);
|
||||
scr_box_set_f64(c->caps[0], id);
|
||||
return c;
|
||||
}
|
||||
|
||||
static void scr_stream_rl_on(ScrStream *s, const char *name, ScrClosure *cb, ScrEeInvoke inv) {
|
||||
ScrStr *n = scr_str_new(name, strlen(name));
|
||||
scr_emitter_release(scr_emitter_on((ScrEmitter *)s, n, cb, inv, false, false));
|
||||
scr_str_release(n);
|
||||
}
|
||||
|
||||
static void scr_stream_rl_off(ScrStream *s, const char *name, ScrClosure *cb) {
|
||||
ScrStr *n = scr_str_new(name, strlen(name));
|
||||
scr_emitter_release(scr_emitter_off((ScrEmitter *)s, n, cb));
|
||||
scr_str_release(n);
|
||||
}
|
||||
|
||||
static void scr_stream_rl_pause(void *s) { scr_stream_release(scr_stream_pause(s)); }
|
||||
static void scr_stream_rl_resume(void *s) { scr_stream_release(scr_stream_resume(s)); }
|
||||
static void scr_stream_rl_write(void *s, ScrStr *text) { scr_stream_write_str(s, text, NULL); }
|
||||
static void scr_stream_rl_release(void *s) { scr_stream_release(s); }
|
||||
|
||||
static void scr_stream_rl_detach(void *s, double id) {
|
||||
for (ScrRlHook **at = &scr_rl_hooks; *at; at = &(*at)->next) {
|
||||
ScrRlHook *h = *at;
|
||||
if (h->s != s || h->id != id) continue;
|
||||
*at = h->next;
|
||||
scr_stream_rl_off(h->s, "data", h->data);
|
||||
scr_stream_rl_off(h->s, "error", h->error);
|
||||
scr_stream_rl_off(h->s, "end", h->end);
|
||||
free(h);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static const ScrRlIo scr_stream_rl_io = {
|
||||
&scr_stream_rl_pause, &scr_stream_rl_resume, &scr_stream_rl_detach,
|
||||
&scr_stream_rl_write, &scr_stream_rl_release,
|
||||
};
|
||||
|
||||
static void scr_stream_rl_hooks_free(void) {
|
||||
while (scr_rl_hooks) {
|
||||
ScrRlHook *h = scr_rl_hooks;
|
||||
scr_rl_hooks = h->next;
|
||||
free(h);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
out ? scr_stream_retain(out) : NULL, out_stdout, crlf_delay, prompt,
|
||||
signal, add);
|
||||
if (in == NULL) return id;
|
||||
static SCR_TL bool registered = false;
|
||||
if (!registered) {
|
||||
registered = true;
|
||||
scr_atexit(scr_stream_rl_hooks_free);
|
||||
}
|
||||
ScrRlHook *h = calloc(1, sizeof *h);
|
||||
if (!h) scr_stream_oom();
|
||||
h->s = in;
|
||||
h->id = id;
|
||||
h->error = scr_stream_rl_listener((void *)&scr_stream_rl_error, id);
|
||||
h->data = scr_stream_rl_listener((void *)&scr_stream_rl_data, id);
|
||||
h->end = scr_stream_rl_listener((void *)&scr_stream_rl_end, id);
|
||||
h->next = scr_rl_hooks;
|
||||
scr_rl_hooks = h;
|
||||
scr_stream_rl_on(in, "error", h->error, scr_ee_inv_fixed1);
|
||||
scr_stream_rl_on(in, "data", h->data, scr_ee_inv_fixed2);
|
||||
scr_stream_rl_on(in, "end", h->end, scr_ee_inv_fixed0);
|
||||
scr_stream_rl_resume(in);
|
||||
return id;
|
||||
}
|
||||
|
||||
/* ── finished(stream, options) (lib/internal/streams/end-of-stream.js) ──
|
||||
* The options form registers Node's own listeners on the stream — 'end',
|
||||
* 'finish', 'error' (unless options.error is false) and 'close', in that
|
||||
* order — and decides with Node's predicates over the stream state:
|
||||
* options.readable/writable (default: whether the stream has that side)
|
||||
* choose which ends count, and willEmitClose (autoDestroy, emitClose,
|
||||
* not yet closed, and both sides counted) defers the callback to 'close'.
|
||||
* options.signal: an abort removes the listeners and calls back with
|
||||
* new AbortError(undefined, { cause: signal.reason }). The callback runs
|
||||
* once; the answered cleanup closure removes the listeners. */
|
||||
|
||||
typedef struct ScrEos {
|
||||
size_t rc;
|
||||
/* Borrowed: the stream's listeners hold the context, so the context
|
||||
* must not hold the stream; every closure that can run after the
|
||||
* listeners are gone (ticks, the abort listener, cleanup) holds the
|
||||
* stream itself. */
|
||||
ScrStream *s;
|
||||
bool pending; /* the callback has not run */
|
||||
ScrClosure *cb; /* owned until it runs or cleanup drops it (callback form) */
|
||||
ScrStreamErrInv inv;
|
||||
bool readable, writable;
|
||||
bool will_emit_close;
|
||||
bool readable_finished, writable_finished;
|
||||
bool on_error;
|
||||
ScrClosure *l_end, *l_finish, *l_error, *l_close; /* emitter-owned */
|
||||
ScrDyn *dispose; /* the signal listener's disposable (+1) */
|
||||
ScrDyn *signal; /* +1 */
|
||||
ScrPromise *promise; /* the promise form (+1) */
|
||||
bool auto_cleanup;
|
||||
struct ScrEos *live_prev, *live_next; /* pending contexts, released at exit */
|
||||
} ScrEos;
|
||||
|
||||
static SCR_TL ScrEos *scr_eos_live = NULL;
|
||||
|
||||
static void scr_eos_unlink(ScrEos *e) {
|
||||
if (e->live_prev) e->live_prev->live_next = e->live_next;
|
||||
else if (scr_eos_live == e) scr_eos_live = e->live_next;
|
||||
if (e->live_next) e->live_next->live_prev = e->live_prev;
|
||||
e->live_prev = e->live_next = NULL;
|
||||
}
|
||||
|
||||
/* The callback and promise a context still holds: dropped once it
|
||||
* settles, is cleaned up, or the program exits. */
|
||||
static void scr_eos_drop(ScrEos *e) {
|
||||
e->pending = false;
|
||||
scr_eos_unlink(e);
|
||||
if (e->cb) {
|
||||
scr_closure_release(e->cb);
|
||||
e->cb = NULL;
|
||||
}
|
||||
if (e->promise) {
|
||||
scr_promise_release(e->promise);
|
||||
e->promise = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void *scr_eos_retain_v(void *p);
|
||||
static void scr_eos_release_v(void *p);
|
||||
|
||||
/* At exit the disposable goes too: it holds the abort listener, which
|
||||
* holds this context. */
|
||||
static void scr_eos_atexit(void) {
|
||||
while (scr_eos_live) {
|
||||
ScrEos *e = scr_eos_retain_v(scr_eos_live);
|
||||
ScrDyn *d = e->dispose;
|
||||
e->dispose = NULL;
|
||||
scr_eos_drop(e);
|
||||
if (d) scr_dyn_release(d);
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void *scr_eos_retain_v(void *p) {
|
||||
((ScrEos *)p)->rc++;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void scr_eos_release_v(void *p) {
|
||||
ScrEos *e = p;
|
||||
if (--e->rc > 0) return;
|
||||
if (e->cb) scr_closure_release(e->cb);
|
||||
if (e->dispose) scr_dyn_release(e->dispose);
|
||||
if (e->signal) scr_dyn_release(e->signal);
|
||||
if (e->promise) scr_promise_release(e->promise);
|
||||
scr_eos_unlink(e);
|
||||
free(e);
|
||||
}
|
||||
|
||||
/* A closure over the context; `keep` also holds the stream. */
|
||||
static ScrClosure *scr_eos_closure_k(void *fn, ScrEos *e, bool keep) {
|
||||
ScrClosure *c = scr_closure_new(fn, keep ? 2 : 1);
|
||||
c->caps[0] = scr_box_new_obj(&scr_eos_retain_v, &scr_eos_release_v, NULL);
|
||||
scr_box_set_ref(c->caps[0], scr_eos_retain_v(e));
|
||||
if (keep) {
|
||||
c->caps[1] = scr_box_new_obj(&scr_stream_retain_v, &scr_stream_release_v, &scr_stream_trace);
|
||||
scr_box_set_ref(c->caps[1], scr_stream_retain(e->s));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static ScrClosure *scr_eos_closure(void *fn, ScrEos *e) { return scr_eos_closure_k(fn, e, false); }
|
||||
|
||||
static ScrEos *scr_eos_of(ScrClosure *c) { return scr_box_get_ref(c->caps[0]); /* +1 */ }
|
||||
|
||||
/* Node's predicates (internal/streams/utils.js) over the state. */
|
||||
static bool scr_eos_closed(const ScrStreamState *st) { return st->close_scheduled || st->close_emitted; }
|
||||
static bool scr_eos_readable_prop(const ScrStreamState *st) {
|
||||
return st->has_r && !st->destroyed && !st->error_emitted && !st->r.end_emitted;
|
||||
}
|
||||
static bool scr_eos_writable_prop(const ScrStreamState *st) {
|
||||
return st->has_w && !st->destroyed && !st->errored && !st->w.ending;
|
||||
}
|
||||
static bool scr_eos_readable_finished(const ScrStreamState *st, bool strict) {
|
||||
if (!st->has_r || st->errored) return false;
|
||||
return st->r.end_emitted || (!strict && st->r.ended && st->r.length == 0);
|
||||
}
|
||||
static bool scr_eos_writable_finished(const ScrStreamState *st, bool strict) {
|
||||
if (!st->has_w) return false;
|
||||
if (st->w.finished) return true;
|
||||
if (st->errored) return false;
|
||||
return !strict && st->w.ending && st->w.length == 0;
|
||||
}
|
||||
/* isReadable/isWritable: -1 for null (no such property), else 0/1. */
|
||||
static int scr_eos_is_readable(const ScrStreamState *st) {
|
||||
if (!st->has_r) return -1;
|
||||
if (st->destroyed) return 0;
|
||||
return scr_eos_readable_prop(st) && !st->r.end_emitted;
|
||||
}
|
||||
static int scr_eos_is_writable(const ScrStreamState *st) {
|
||||
if (!st->has_w) return -1;
|
||||
if (st->destroyed) return 0;
|
||||
return scr_eos_writable_prop(st) && !st->w.ending;
|
||||
}
|
||||
|
||||
static void scr_eos_unlisten(ScrEos *e) {
|
||||
ScrStream *s = e->s;
|
||||
if (e->l_end) scr_stream_rl_off(s, "end", e->l_end);
|
||||
if (e->l_finish) scr_stream_rl_off(s, "finish", e->l_finish);
|
||||
if (e->l_error) scr_stream_rl_off(s, "error", e->l_error);
|
||||
if (e->l_close) scr_stream_rl_off(s, "close", e->l_close);
|
||||
e->l_end = e->l_finish = e->l_error = e->l_close = NULL;
|
||||
}
|
||||
|
||||
static void scr_eos_dispose_signal(ScrEos *e) {
|
||||
if (!e->dispose) return;
|
||||
ScrDyn *d = e->dispose;
|
||||
e->dispose = NULL;
|
||||
ScrDyn *r = scr_dyn_call(d, NULL, 0, "dispose");
|
||||
if (r) scr_dyn_release(r);
|
||||
scr_dyn_release(d);
|
||||
}
|
||||
|
||||
static void scr_eos_settle(ScrEos *e, ScrError *err);
|
||||
|
||||
/* callback.call(stream, err): once; borrows err. */
|
||||
static void scr_eos_fire(ScrEos *e, ScrError *err) {
|
||||
if (!e->pending) return;
|
||||
scr_eos_dispose_signal(e);
|
||||
scr_eos_settle(e, err);
|
||||
}
|
||||
|
||||
/* Runs the callback (or settles the promise) and drops them. */
|
||||
static void scr_eos_settle(ScrEos *e, ScrError *err) {
|
||||
scr_eos_retain_v(e);
|
||||
ScrClosure *cb = e->cb;
|
||||
ScrPromise *p = e->promise;
|
||||
e->cb = NULL;
|
||||
e->promise = NULL;
|
||||
e->pending = false;
|
||||
scr_eos_unlink(e);
|
||||
if (p) {
|
||||
if (e->auto_cleanup) scr_eos_unlisten(e);
|
||||
if (err != NULL) {
|
||||
scr_throw_obj(scr_error_retain(err), &scr_error_retain_v, &scr_error_release_v,
|
||||
scr_error_trace_arg());
|
||||
scr_promise_reject_pending(p);
|
||||
} else {
|
||||
scr_promise_fulfill_void(p);
|
||||
}
|
||||
scr_promise_release(p);
|
||||
} else if (cb) {
|
||||
e->inv(cb, e->s, err);
|
||||
scr_closure_release(cb);
|
||||
}
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
|
||||
static void scr_eos_premature(ScrEos *e) {
|
||||
ScrError *err = scr_stream_mkerr("ERR_STREAM_PREMATURE_CLOSE", "Premature close");
|
||||
scr_eos_fire(e, err);
|
||||
scr_error_release(err);
|
||||
}
|
||||
|
||||
static void scr_eos_onfinish(ScrClosure *c) {
|
||||
ScrEos *e = scr_eos_of(c);
|
||||
ScrStreamState *st = e->s->st;
|
||||
e->writable_finished = true;
|
||||
if (st->destroyed) e->will_emit_close = false;
|
||||
if (!(e->will_emit_close && (!scr_eos_readable_prop(st) || e->readable))) {
|
||||
if (!e->readable || e->readable_finished) scr_eos_fire(e, NULL);
|
||||
}
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
|
||||
static void scr_eos_onend(ScrClosure *c) {
|
||||
ScrEos *e = scr_eos_of(c);
|
||||
ScrStreamState *st = e->s->st;
|
||||
e->readable_finished = true;
|
||||
if (st->destroyed) e->will_emit_close = false;
|
||||
if (!(e->will_emit_close && (!scr_eos_writable_prop(st) || e->writable))) {
|
||||
if (!e->writable || e->writable_finished) scr_eos_fire(e, NULL);
|
||||
}
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
|
||||
static void scr_eos_onerror(ScrClosure *c, void *err) {
|
||||
ScrEos *e = scr_eos_of(c);
|
||||
scr_eos_fire(e, (ScrError *)err);
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
|
||||
static void scr_eos_onclose(ScrClosure *c) {
|
||||
ScrEos *e = scr_eos_of(c);
|
||||
ScrStreamState *st = e->s->st;
|
||||
if (st->errored) {
|
||||
scr_eos_fire(e, st->errored);
|
||||
} else if (e->readable && !e->readable_finished && st->has_r &&
|
||||
!scr_eos_readable_finished(st, false)) {
|
||||
scr_eos_premature(e);
|
||||
} else if (e->writable && !e->writable_finished && !scr_eos_writable_finished(st, false)) {
|
||||
scr_eos_premature(e);
|
||||
} else {
|
||||
scr_eos_fire(e, NULL);
|
||||
}
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
|
||||
static void scr_eos_onclosed(ScrClosure *c) {
|
||||
ScrEos *e = scr_eos_of(c);
|
||||
ScrStreamState *st = e->s->st;
|
||||
scr_eos_fire(e, st->errored);
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
|
||||
/* abort: the listeners go (cleanup keeps the callback for this call),
|
||||
* then the callback runs with the AbortError. */
|
||||
static void scr_eos_abort(ScrEos *e) {
|
||||
if (!e->pending) return;
|
||||
scr_eos_unlisten(e);
|
||||
scr_eos_dispose_signal(e);
|
||||
ScrError *err = scr_stream_abort_error(e->signal);
|
||||
scr_eos_settle(e, err);
|
||||
scr_error_release(err);
|
||||
}
|
||||
|
||||
static void scr_eos_abort_tick(ScrClosure *c) {
|
||||
ScrEos *e = scr_eos_of(c);
|
||||
scr_eos_abort(e);
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
|
||||
static ScrDyn *scr_eos_abort_thunk(ScrClosure *c, ScrDyn *const *args, size_t argc) {
|
||||
(void)args;
|
||||
(void)argc;
|
||||
ScrEos *e = scr_eos_of(c);
|
||||
scr_eos_abort(e);
|
||||
scr_eos_release_v(e);
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
|
||||
static void scr_eos_cleanup_fn(ScrClosure *c) {
|
||||
ScrEos *e = scr_eos_of(c);
|
||||
scr_eos_unlisten(e);
|
||||
scr_eos_dispose_signal(e);
|
||||
if (e->pending) scr_eos_drop(e);
|
||||
scr_eos_release_v(e);
|
||||
}
|
||||
|
||||
/* readable/writable: -1 for the default; on_error: options.error !==
|
||||
* false; signal: NULL when absent (borrowed). cb moves. Answers the +1
|
||||
* cleanup closure, or NULL with ERR_INVALID_ARG_TYPE pending for a signal
|
||||
* that is not an AbortSignal. */
|
||||
static ScrClosure *scr_stream_eos_impl(ScrStream *s, ScrClosure *cb, ScrStreamErrInv inv,
|
||||
ScrPromise *p, bool auto_cleanup, int readable,
|
||||
int writable, bool on_error, ScrDyn *signal,
|
||||
ScrAbortAddFn add) {
|
||||
ScrStreamState *st = s->st;
|
||||
static SCR_TL bool registered = false;
|
||||
if (!registered) {
|
||||
registered = true;
|
||||
scr_atexit(scr_eos_atexit);
|
||||
}
|
||||
if (signal && !scr_dyn_is_nullish(signal)) {
|
||||
ScrDyn *aborted = NULL;
|
||||
if (signal->kind == SCR_DYN_OBJ || signal->kind == SCR_DYN_HANDLE) {
|
||||
aborted = scr_stream_signal_prop(signal, "aborted");
|
||||
}
|
||||
if (!aborted) {
|
||||
if (cb) scr_closure_release(cb);
|
||||
scr_dyn_prop_type_fail("options.signal", "an instance of AbortSignal", signal);
|
||||
return NULL;
|
||||
}
|
||||
scr_dyn_release(aborted);
|
||||
} else {
|
||||
signal = NULL;
|
||||
}
|
||||
ScrEos *e = calloc(1, sizeof *e);
|
||||
if (!e) scr_stream_oom();
|
||||
e->rc = 1;
|
||||
e->s = s;
|
||||
e->pending = true;
|
||||
e->cb = cb;
|
||||
e->inv = inv;
|
||||
e->promise = p ? scr_promise_retain(p) : NULL;
|
||||
e->auto_cleanup = auto_cleanup;
|
||||
e->live_next = scr_eos_live;
|
||||
if (scr_eos_live) scr_eos_live->live_prev = e;
|
||||
scr_eos_live = e;
|
||||
e->readable = readable < 0 ? st->has_r : readable != 0;
|
||||
e->writable = writable < 0 ? st->has_w : writable != 0;
|
||||
e->on_error = on_error;
|
||||
e->will_emit_close = st->auto_destroy && st->emit_close && !scr_eos_closed(st) &&
|
||||
st->has_r == e->readable && st->has_w == e->writable;
|
||||
e->writable_finished = scr_eos_writable_finished(st, false);
|
||||
e->readable_finished = scr_eos_readable_finished(st, false);
|
||||
e->l_end = scr_eos_closure((void *)&scr_eos_onend, e);
|
||||
e->l_finish = scr_eos_closure((void *)&scr_eos_onfinish, e);
|
||||
e->l_close = scr_eos_closure((void *)&scr_eos_onclose, e);
|
||||
scr_stream_rl_on(s, "end", e->l_end, scr_ee_inv_fixed0);
|
||||
scr_stream_rl_on(s, "finish", e->l_finish, scr_ee_inv_fixed0);
|
||||
if (on_error) {
|
||||
e->l_error = scr_eos_closure((void *)&scr_eos_onerror, e);
|
||||
scr_stream_rl_on(s, "error", e->l_error, scr_ee_inv_fixed1);
|
||||
}
|
||||
scr_stream_rl_on(s, "close", e->l_close, scr_ee_inv_fixed0);
|
||||
bool closed = scr_eos_closed(st);
|
||||
if (closed) {
|
||||
scr_next_tick(scr_eos_closure_k((void *)&scr_eos_onclose, e, true));
|
||||
} else if (st->error_emitted) {
|
||||
if (!e->will_emit_close) scr_next_tick(scr_eos_closure_k((void *)&scr_eos_onclosed, e, true));
|
||||
} else if (!e->readable && (!e->will_emit_close || scr_eos_is_readable(st) == 1) &&
|
||||
(e->writable_finished || scr_eos_is_writable(st) == 0) &&
|
||||
(!st->has_w || (!st->w.writing && st->w.head == st->w.n))) {
|
||||
scr_next_tick(scr_eos_closure_k((void *)&scr_eos_onclosed, e, true));
|
||||
} else if (!e->writable && (!e->will_emit_close || scr_eos_is_writable(st) == 1) &&
|
||||
(e->readable_finished || scr_eos_is_readable(st) == 0)) {
|
||||
scr_next_tick(scr_eos_closure_k((void *)&scr_eos_onclosed, e, true));
|
||||
}
|
||||
if (signal && !closed) {
|
||||
e->signal = scr_dyn_retain(signal);
|
||||
ScrDyn *aborted = scr_stream_signal_prop(signal, "aborted");
|
||||
bool is_aborted = aborted && scr_dyn_truthy(aborted);
|
||||
if (aborted) scr_dyn_release(aborted);
|
||||
if (is_aborted) {
|
||||
scr_next_tick(scr_eos_closure_k((void *)&scr_eos_abort_tick, e, true));
|
||||
} else {
|
||||
ScrClosure *clo = scr_eos_closure_k((void *)&scr_eos_abort_thunk, e, true);
|
||||
ScrDyn *listener = scr_dyn_new_func(clo, &scr_eos_abort_thunk, 0, "()", "abort");
|
||||
e->dispose = add(signal, listener);
|
||||
scr_dyn_release(listener);
|
||||
}
|
||||
}
|
||||
ScrClosure *cleanup = scr_eos_closure_k((void *)&scr_eos_cleanup_fn, e, true);
|
||||
scr_eos_release_v(e);
|
||||
return cleanup;
|
||||
}
|
||||
|
||||
ScrClosure *scr_stream_finished_opts(ScrStream *s, ScrClosure *cb /*moves*/, ScrStreamErrInv inv,
|
||||
double readable, double writable, bool on_error,
|
||||
ScrDyn *signal, ScrAbortAddFn add) {
|
||||
return scr_stream_eos_impl(s, cb, inv, NULL, false, (int)readable, (int)writable, on_error,
|
||||
signal, add);
|
||||
}
|
||||
|
||||
ScrClosure *scr_stream_finished_opts_dyn(ScrStream *s, ScrDyn *cb, double readable,
|
||||
double writable, bool on_error, ScrDyn *signal,
|
||||
ScrAbortAddFn add) {
|
||||
ScrClosure *c = scr_stream_dynopt_clo(cb, (void *)&scr_stream_finished_dyn_inv);
|
||||
return scr_stream_eos_impl(s, c, &scr_stream_finished_dyn_inv, NULL, false, (int)readable,
|
||||
(int)writable, on_error, signal, add);
|
||||
}
|
||||
|
||||
/* stream/promises finished(stream, options): cleanup removes the
|
||||
* listeners once the promise settles. */
|
||||
ScrPromise *scr_sp_finished_opts(ScrStream *s, double readable, double writable, bool on_error,
|
||||
bool cleanup, ScrDyn *signal, ScrAbortAddFn add) {
|
||||
ScrPromise *p = scr_promise_new();
|
||||
ScrClosure *cleanup_fn = scr_stream_eos_impl(s, NULL, NULL, p, cleanup, (int)readable,
|
||||
(int)writable, on_error, signal, add);
|
||||
if (cleanup_fn == NULL) {
|
||||
scr_promise_reject_pending(p);
|
||||
return p;
|
||||
}
|
||||
scr_closure_release(cleanup_fn);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* ── node:console Console instances over writable streams ─────────────
|
||||
* Node's kWriteToConsole: with ignoreErrors (the default) one shared noop
|
||||
* 'error' listener brackets the write when the stream has none, and the
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// node:readline/promises question() over a stream input and output: the
|
||||
// prompt is written to the output, the next line answers, a second
|
||||
// question while one is pending writes the first prompt again and never
|
||||
// settles, close() leaves a pending question unsettled, and a question on
|
||||
// a closed interface rejects with ERR_USE_AFTER_CLOSE. Without an output
|
||||
// nothing is written. setPrompt/getPrompt/prompt manage the prompt, and
|
||||
// write() feeds text as if it were input.
|
||||
import * as readline from "node:readline/promises";
|
||||
import { PassThrough, Readable } from "node:stream";
|
||||
|
||||
const out = new PassThrough();
|
||||
out.on("data", (c: Buffer) => console.log("output:", JSON.stringify(c.toString())));
|
||||
|
||||
const rl = readline.createInterface({ input: Readable.from(["alpha\nbeta\n"]), output: out });
|
||||
const first = rl.question("first? ");
|
||||
const second = rl.question("second? ");
|
||||
first.then((answer: string) => console.log("first answer:", answer));
|
||||
second.then((answer: string) => console.log("second answer (never):", answer));
|
||||
rl.on("line", (line: string) => console.log("line:", line));
|
||||
rl.on("pause", () => console.log("pause"));
|
||||
rl.on("close", () => console.log("close"));
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
const quiet = readline.createInterface({ input: new Readable({ read() {} }) });
|
||||
quiet.question("not written ").then((answer: string) => console.log("never:", answer));
|
||||
quiet.close();
|
||||
try {
|
||||
await quiet.question("again ");
|
||||
} catch (e) {
|
||||
console.log("after close:", (e as Error & { code?: string }).code, (e as Error).message);
|
||||
}
|
||||
|
||||
const prompter = readline.createInterface({
|
||||
input: new Readable({ read() {} }),
|
||||
output: out,
|
||||
prompt: "$ ",
|
||||
});
|
||||
console.log("prompt:", JSON.stringify(prompter.getPrompt()));
|
||||
prompter.prompt();
|
||||
prompter.setPrompt("> ");
|
||||
prompter.prompt(true);
|
||||
prompter.on("line", (line: string) => console.log("typed:", line));
|
||||
prompter.write("one\ntwo");
|
||||
prompter.write("\n");
|
||||
const typed = prompter.question("q? ");
|
||||
prompter.write("answer\n");
|
||||
console.log("answered:", await typed);
|
||||
console.log("prompt restored:", JSON.stringify(prompter.getPrompt()));
|
||||
prompter.close();
|
||||
@@ -0,0 +1,43 @@
|
||||
// Line splitting and async iteration on node:readline/promises: \n, \r\n
|
||||
// and a lone \r end lines, a \n that arrives in the next chunk after a
|
||||
// \r is dropped (within crlfDelay), UTF-8 sequences split across chunks
|
||||
// decode whole, and the unfinished last line is emitted at the end of
|
||||
// input. for await reads the same lines; after a break the lines already
|
||||
// queued are still read by the next loop. More than 1024 queued lines
|
||||
// pause the input until the loop catches up.
|
||||
import { createInterface } from "node:readline/promises";
|
||||
import { Readable } from "node:stream";
|
||||
|
||||
const src = new Readable({ read() {} });
|
||||
const rl = createInterface({ input: src, crlfDelay: Infinity });
|
||||
rl.on("line", (line: string) => console.log("line", JSON.stringify(line)));
|
||||
rl.on("close", () => console.log("closed"));
|
||||
for (const chunk of ["one\r", "\ntwo\r\nthree\rfour", "\n", "cafÃ", "©\nlast"]) {
|
||||
src.push(Buffer.from(chunk, "latin1"));
|
||||
}
|
||||
src.push(null);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
const lines = createInterface({ input: Readable.from(["1\n2\n3\n4"]) });
|
||||
for await (const line of lines) {
|
||||
console.log("first loop", line);
|
||||
break;
|
||||
}
|
||||
for await (const line of lines) console.log("second loop", line);
|
||||
console.log("loops done");
|
||||
|
||||
const many: string[] = [];
|
||||
for (let i = 0; i < 1500; i++) many.push(`row ${i}`);
|
||||
const big = createInterface({ input: Readable.from([many.join("\n") + "\n"]) });
|
||||
let pauses = 0;
|
||||
let resumes = 0;
|
||||
big.on("pause", () => pauses++);
|
||||
big.on("resume", () => resumes++);
|
||||
let count = 0;
|
||||
let lastLine = "";
|
||||
for await (const line of big) {
|
||||
count++;
|
||||
lastLine = line;
|
||||
if (count === 1) await new Promise<void>((resolve) => setTimeout(resolve, 5));
|
||||
}
|
||||
console.log("read", count, lastLine, "pauses", pauses, "resumes", resumes);
|
||||
@@ -0,0 +1,32 @@
|
||||
// The callback node:readline Interface from CommonJS: 'line' listeners
|
||||
// (on and once) in registration order, a question answered by the next
|
||||
// line while other lines go to 'line', 'pause' before 'close' when the
|
||||
// input ends, listeners added after 'close' never running, and a question
|
||||
// after close throwing ERR_USE_AFTER_CLOSE. An error on the input reaches
|
||||
// the interface's 'error' listener.
|
||||
'use strict';
|
||||
const readline = require('node:readline');
|
||||
const { Readable } = require('node:stream');
|
||||
|
||||
const rl = readline.createInterface({ input: Readable.from(['a\nb\nc\nd']) });
|
||||
rl.on('line', (line) => console.log('on', line));
|
||||
rl.once('line', (line) => console.log('once', line));
|
||||
rl.question('', (answer) => console.log('answer', answer));
|
||||
rl.on('pause', () => console.log('pause'));
|
||||
rl.on('close', () => {
|
||||
console.log('close');
|
||||
rl.on('close', () => console.log('never'));
|
||||
try {
|
||||
rl.question('again', () => {});
|
||||
} catch (e) {
|
||||
console.log('question after close:', e.code, e.message);
|
||||
}
|
||||
rl.close();
|
||||
});
|
||||
|
||||
const broken = new Readable({ read() {} });
|
||||
const rl2 = readline.createInterface({ input: broken });
|
||||
rl2.on('error', (err) => console.log('interface error:', err.message));
|
||||
rl2.on('line', (line) => console.log('rl2 line', line));
|
||||
broken.push('ok\n');
|
||||
setTimeout(() => broken.destroy(new Error('input failed')), 5);
|
||||
@@ -0,0 +1,46 @@
|
||||
// AbortSignal with node:readline/promises: an aborted question rejects
|
||||
// with an AbortError whose cause is the signal's reason and writes
|
||||
// clearLine's "\r\n"; an already aborted signal rejects before the prompt
|
||||
// is written. The createInterface signal option closes the interface when
|
||||
// it aborts, on the next tick when it is already aborted.
|
||||
'use strict';
|
||||
const { createInterface } = require('node:readline/promises');
|
||||
const { PassThrough, Readable } = require('node:stream');
|
||||
|
||||
async function main() {
|
||||
const out = new PassThrough();
|
||||
out.on('data', (c) => console.log('output:', JSON.stringify(c.toString())));
|
||||
const rl = createInterface({ input: new Readable({ read() {} }), output: out });
|
||||
const ac = new AbortController();
|
||||
const pending = rl.question('name? ', { signal: ac.signal });
|
||||
ac.abort();
|
||||
try {
|
||||
await pending;
|
||||
} catch (e) {
|
||||
console.log(e.name, e.code, e.message, e.cause instanceof Error, e.cause.name);
|
||||
}
|
||||
const pre = new AbortController();
|
||||
pre.abort('why');
|
||||
try {
|
||||
await rl.question('again? ', { signal: pre.signal });
|
||||
} catch (e) {
|
||||
console.log('pre-aborted:', e.name, e.cause);
|
||||
}
|
||||
const answered = new AbortController();
|
||||
rl.write('');
|
||||
const later = rl.question('third? ', { signal: answered.signal });
|
||||
rl.write('yes\n');
|
||||
console.log('answered:', await later);
|
||||
answered.abort();
|
||||
const closer = new AbortController();
|
||||
const rl2 = createInterface({ input: new Readable({ read() {} }), signal: closer.signal });
|
||||
rl2.on('close', () => console.log('rl2 closed by abort'));
|
||||
closer.abort();
|
||||
const early = new AbortController();
|
||||
early.abort();
|
||||
const rl3 = createInterface({ input: new Readable({ read() {} }), signal: early.signal });
|
||||
rl3.on('close', () => console.log('rl3 closed on the next tick'));
|
||||
console.log('rl3 created');
|
||||
rl.close();
|
||||
}
|
||||
main();
|
||||
@@ -0,0 +1,37 @@
|
||||
// node:readline/promises over process.stdin: the program re-runs itself
|
||||
// with piped input and prints the child's output. The first line answers
|
||||
// the question (its prompt goes to stdout), the 'line' listener receives
|
||||
// the rest, the trailing line without a newline included, and 'close'
|
||||
// follows the end of input. A second child reads every line with for
|
||||
// await, and a callback interface reports \r\n-terminated lines.
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { createInterface } from "node:readline/promises";
|
||||
import * as readlineCb from "node:readline";
|
||||
|
||||
const mode = process.argv[2];
|
||||
if (mode === "question") {
|
||||
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
||||
const answer = rl.question("name? ");
|
||||
const rest: string[] = [];
|
||||
rl.on("line", (line: string) => rest.push(line));
|
||||
rl.on("close", () => console.log("closed after", rest.join(",")));
|
||||
console.log(`\nhello ${await answer}`);
|
||||
} else if (mode === "lines") {
|
||||
const rl = createInterface({ input: process.stdin });
|
||||
let n = 0;
|
||||
for await (const line of rl) console.log(++n, line);
|
||||
console.log("done");
|
||||
} else if (mode === "callback") {
|
||||
const rl = readlineCb.createInterface({ input: process.stdin });
|
||||
rl.on("line", (line: string) => console.log("line:", line));
|
||||
rl.on("close", () => console.log("closed"));
|
||||
} else {
|
||||
for (const [child, input] of [
|
||||
["question", "Ada\nblue\nx\nz"],
|
||||
["lines", "a\n\nb\r\nc"],
|
||||
["callback", "first\r\nsecond\nthird"],
|
||||
]) {
|
||||
const stdout = execFileSync(process.execPath, [process.argv[1]!, child!], { input, encoding: "utf8" });
|
||||
console.log(child, JSON.stringify(stdout));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// stream.addAbortSignal and the constructors' signal option: an abort
|
||||
// destroys the stream with an AbortError (no unhandled error: the
|
||||
// listener eos registers handles it), an already aborted signal destroys
|
||||
// it during construction, the subclass super({ signal }) form applies it
|
||||
// too, and an abort after the stream finished changes nothing.
|
||||
import { addAbortSignal, Readable, Writable } from "node:stream";
|
||||
|
||||
const ac = new AbortController();
|
||||
const r = addAbortSignal(ac.signal, new Readable({ read() {} }));
|
||||
r.on("close", () => console.log("close", r.destroyed, r.errored?.name));
|
||||
ac.abort();
|
||||
console.log("after abort", r.destroyed);
|
||||
const pre = new AbortController();
|
||||
pre.abort("x");
|
||||
const signal = pre.signal;
|
||||
const w = new Writable({ write(_c, _e, cb) { cb(); }, signal });
|
||||
console.log("pre", w.destroyed, w.errored?.name);
|
||||
w.on("error", (e: Error) => console.log("w error", e.name, e.message));
|
||||
w.on("close", () => console.log("w close"));
|
||||
const ok = new Readable({ read() { this.push("a"); this.push(null); } });
|
||||
const ac2 = new AbortController();
|
||||
addAbortSignal(ac2.signal, ok).resume();
|
||||
ok.on("close", () => { ac2.abort(); console.log("ok close, abort after finish", ok.errored); });
|
||||
class Sub extends Readable {
|
||||
constructor(s: AbortSignal) { super({ signal: s }); }
|
||||
_read() {}
|
||||
}
|
||||
const ac3 = new AbortController();
|
||||
const sub = new Sub(ac3.signal);
|
||||
sub.on("error", (e: Error) => console.log("sub error", e.name));
|
||||
ac3.abort();
|
||||
@@ -0,0 +1,16 @@
|
||||
// addAbortSignal's argument checks from CommonJS: Node's
|
||||
// ERR_INVALID_ARG_TYPE messages for values that are not AbortSignals or
|
||||
// streams (null fails the in-operator check), a falsy signal option is
|
||||
// ignored, and the AbortError carries the abort reason as its cause.
|
||||
const { addAbortSignal, Readable } = require("node:stream");
|
||||
const r = new Readable({ read() {} });
|
||||
for (const bad of [{}, null, 1, "x", undefined]) {
|
||||
try { addAbortSignal(bad, r); console.log("accepted", bad); } catch (e) { console.log(e.code, e.message); }
|
||||
}
|
||||
try { addAbortSignal(new AbortController().signal, {}); } catch (e) { console.log(e.code, e.message); }
|
||||
const ac = new AbortController();
|
||||
ac.abort(new Error("reason here"));
|
||||
const s = new Readable({ read() {}, signal: ac.signal });
|
||||
s.on("error", (e) => console.log("error", e.name, e.cause.message, e.code));
|
||||
const t = new Readable({ read() {}, signal: false });
|
||||
console.log("falsy signal ignored", t.destroyed);
|
||||
@@ -0,0 +1,87 @@
|
||||
// stream/promises options: pipeline(..., { signal }) destroys every stage
|
||||
// with an AbortError in pipeline order and rejects after the last close
|
||||
// (an already aborted signal too), and { end: false } settles when the
|
||||
// source is done while the destination stays open. finished(stream,
|
||||
// options): readable/writable choose which end counts, error: false
|
||||
// waits for close, signal rejects without destroying the stream, and
|
||||
// cleanup removes the listeners. The callback form takes the options.
|
||||
import { Duplex, PassThrough, Readable, Writable, finished as finishedCb } from "node:stream";
|
||||
import { finished, pipeline } from "node:stream/promises";
|
||||
|
||||
{
|
||||
const ac = new AbortController();
|
||||
const src = new Readable({ read() {} });
|
||||
const mid = new PassThrough();
|
||||
const dst = new Writable({ write(c, _e, cb) { console.log("write", c.toString()); cb(); } });
|
||||
src.on("error", (e: Error) => console.log("src error", e.name));
|
||||
src.on("close", () => console.log("src close"));
|
||||
mid.on("error", (e: Error) => console.log("mid error", e.name));
|
||||
mid.on("close", () => console.log("mid close"));
|
||||
dst.on("error", (e: Error) => console.log("dst error", e.name));
|
||||
dst.on("close", () => console.log("dst close"));
|
||||
const p = pipeline(src, mid, dst, { signal: ac.signal });
|
||||
src.push("a");
|
||||
setTimeout(() => ac.abort(), 5);
|
||||
try { await p; } catch (e) { console.log("rejected", (e as Error).name); }
|
||||
}
|
||||
{
|
||||
const ac = new AbortController();
|
||||
ac.abort();
|
||||
const src = Readable.from(["x"]);
|
||||
const dst = new Writable({ write(_c, _e, cb) { cb(); } });
|
||||
try { await pipeline(src, dst, { signal: ac.signal }); } catch (e) { console.log("pre", (e as Error).name, src.destroyed, dst.destroyed); }
|
||||
}
|
||||
{
|
||||
const chunks: string[] = [];
|
||||
const dst = new Writable({ write(c, _e, cb) { chunks.push(c.toString()); cb(); } });
|
||||
dst.on("finish", () => console.log("dst finish"));
|
||||
await pipeline(Readable.from(["1", "2"]), dst, { end: false });
|
||||
console.log("resolved end:false", chunks.join(""), dst.writableEnded, dst.destroyed);
|
||||
dst.end("3");
|
||||
await new Promise<void>((r) => setTimeout(r, 5));
|
||||
console.log("after manual end", chunks.join(""));
|
||||
}
|
||||
console.log("done");
|
||||
|
||||
{
|
||||
const d = new Duplex({ read() {}, write(_c, _e, cb) { cb(); } });
|
||||
d.on("finish", () => console.log("d finish"));
|
||||
d.on("close", () => console.log("d close"));
|
||||
const p = finished(d, { readable: false }).then(() => console.log("d settled (readable: false)"));
|
||||
d.end("x");
|
||||
await p;
|
||||
}
|
||||
{
|
||||
const d = new PassThrough();
|
||||
d.on("end", () => console.log("pt end"));
|
||||
const p = finished(d, { writable: false }).then(() => console.log("pt settled (writable: false)"));
|
||||
d.resume();
|
||||
d.end("y");
|
||||
await p;
|
||||
}
|
||||
{
|
||||
const r = new Readable({ read() {} });
|
||||
r.on("error", (e: Error) => console.log("r error event", e.message));
|
||||
r.on("close", () => console.log("r close"));
|
||||
const p = finished(r, { error: false }).catch((e) => console.log("r rejected", (e as Error).message));
|
||||
r.destroy(new Error("boom"));
|
||||
await p;
|
||||
}
|
||||
{
|
||||
const ac = new AbortController();
|
||||
const r = new Readable({ read() {} });
|
||||
const p = finished(r, { signal: ac.signal }).catch((e) => console.log("aborted", (e as Error).name, r.destroyed));
|
||||
ac.abort();
|
||||
await p;
|
||||
}
|
||||
{
|
||||
const r = Readable.from(["a"]);
|
||||
r.resume();
|
||||
await finished(r, { cleanup: true });
|
||||
console.log("cleanup listeners", r.listenerCount("end"), r.listenerCount("close"));
|
||||
}
|
||||
{
|
||||
const w = new Writable({ write(_c, _e, cb) { cb(); } });
|
||||
finishedCb(w, { writable: true }, (err?: Error | null) => console.log("callback form", err));
|
||||
w.end("z");
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// finished(stream, options, callback) from CommonJS: the callback runs
|
||||
// once with the error or no argument, `this` unused; readable: false on a
|
||||
// Duplex settles at 'finish'; error: false leaves the 'error' event to the
|
||||
// stream's own listeners and reports at close; the returned cleanup
|
||||
// function removes the listeners so the callback never runs; signal
|
||||
// aborts the wait with an AbortError; a non-signal value throws
|
||||
// ERR_INVALID_ARG_TYPE.
|
||||
'use strict';
|
||||
const { Duplex, Readable, finished } = require('node:stream');
|
||||
|
||||
const d = new Duplex({ read() {}, write(c, e, cb) { cb(); } });
|
||||
d.on('finish', () => console.log('finish'));
|
||||
finished(d, { readable: false }, (err) => console.log('duplex settled', err));
|
||||
d.end('x');
|
||||
|
||||
const r = new Readable({ read() {} });
|
||||
r.on('error', (e) => console.log('error event', e.message));
|
||||
finished(r, { error: false }, (err) => console.log('error:false settled', err.message));
|
||||
r.destroy(new Error('bad'));
|
||||
|
||||
const quiet = new Readable({ read() {} });
|
||||
const cleanup = finished(quiet, null, (err) => console.log('never', err));
|
||||
cleanup();
|
||||
quiet.destroy();
|
||||
|
||||
const ac = new AbortController();
|
||||
const waiting = new Readable({ read() {} });
|
||||
finished(waiting, { signal: ac.signal }, (err) => console.log('aborted', err.name, err.cause.name, waiting.destroyed));
|
||||
ac.abort();
|
||||
|
||||
try {
|
||||
finished(new Readable({ read() {} }), { signal: {} }, () => {});
|
||||
} catch (e) {
|
||||
console.log(e.code, e.message);
|
||||
}
|
||||
@@ -46,9 +46,9 @@ fs.watch("/tmp", { persistent: false }, () => {});
|
||||
dns.lookup("localhost", { family: 4, hints: 32 }, () => {});
|
||||
dns.lookup("localhost", { family: 4, all: true }, () => {});
|
||||
|
||||
// readline: a finite crlfDelay is not the lowered splitter; completer
|
||||
// readline: history only exists for terminal interfaces; completer
|
||||
// needs an interactive terminal.
|
||||
readline.createInterface({ input: process.stdin, output: process.stdout, crlfDelay: 100 });
|
||||
readline.createInterface({ input: process.stdin, output: process.stdout, historySize: 10 });
|
||||
readline.createInterface({ input: process.stdin, output: process.stdout, completer: (l: string) => [[l], l] });
|
||||
|
||||
// A bare __proto__: value entry is not an ordinary undocumented key: it
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// readline/promises under @types/node: the Interface over process.stdin,
|
||||
// a question answered through write() with the prompt on stdout, 'line'
|
||||
// for text with no pending question, close(), and the rejection of a
|
||||
// question asked after close.
|
||||
import { createInterface } from "node:readline/promises";
|
||||
|
||||
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
||||
rl.on("line", (line: string) => console.log("line:", line));
|
||||
rl.on("close", () => console.log("closed"));
|
||||
const answer = rl.question("name? ");
|
||||
rl.write("Ada\nextra\n");
|
||||
console.log("\nname:", await answer);
|
||||
rl.close();
|
||||
try {
|
||||
await rl.question("again? ");
|
||||
} catch (err) {
|
||||
console.log("after close:", (err as Error & { code?: string }).code);
|
||||
}
|
||||
@@ -106,18 +106,18 @@ options-record.ts:47:38 - error SC2020: 'lookup option 'all'' is part of the sta
|
||||
|
||||
hint: the all-addresses callback shape has no lowering — the lowered callback is (err, address, family) over one IPv4 answer
|
||||
|
||||
options-record.ts:51:85 - error SC2020: 'createInterface with a finite crlfDelay' is part of the standard library types but has no scriptc lowering yet
|
||||
options-record.ts:51:74 - error SC2020: 'createInterface option 'historySize'' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
50 | // needs an interactive terminal.
|
||||
51 | readline.createInterface({ input: process.stdin, output: process.stdout, crlfDelay: 100 });
|
||||
| ^~~
|
||||
51 | readline.createInterface({ input: process.stdin, output: process.stdout, historySize: 10 });
|
||||
| ^~~~~~~~~~~~~~~
|
||||
52 | readline.createInterface({ input: process.stdin, output: process.stdout, completer: (l: string) => [[l], l] });
|
||||
|
||||
hint: the lowered splitter always joins \r\n across chunks (Node's crlfDelay: Infinity) — Infinity is the accepted value
|
||||
hint: input, output, terminal: false, crlfDelay, prompt and signal are the supported options
|
||||
|
||||
options-record.ts:52:74 - error SC2020: 'createInterface with a completer' is part of the standard library types but has no scriptc lowering yet
|
||||
|
||||
51 | readline.createInterface({ input: process.stdin, output: process.stdout, crlfDelay: 100 });
|
||||
51 | readline.createInterface({ input: process.stdin, output: process.stdout, historySize: 10 });
|
||||
52 | readline.createInterface({ input: process.stdin, output: process.stdout, completer: (l: string) => [[l], l] });
|
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
53 |
|
||||
|
||||
@@ -126,6 +126,24 @@ test("node-types: util.styleText supports stored calls and standard streams unde
|
||||
expect(comparableStderr(native.stderr)).toBe(node.stderr);
|
||||
});
|
||||
|
||||
test("node-types: readline/promises over stdin compiles under @types/node", async () => {
|
||||
const outDir = outDirFor("readline-promises");
|
||||
const entry = join(nodeTypesDir, "readline-promises.ts");
|
||||
const result = await compile(entry, {
|
||||
outPath: join(outDir, "readline-promises"),
|
||||
outDir,
|
||||
sanitize,
|
||||
});
|
||||
expect(result.ok, !result.ok ? JSON.stringify(result.diagnostics, null, 2) : "").toBe(true);
|
||||
if (!result.ok) return;
|
||||
const [native, node] = await Promise.all([
|
||||
execFileAsync(result.binaryPath),
|
||||
execFileAsync(process.execPath, ["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", entry]),
|
||||
]);
|
||||
expect(native.stdout).toBe(node.stdout);
|
||||
expect(comparableStderr(native.stderr)).toBe(node.stderr);
|
||||
});
|
||||
|
||||
function outDirFor(name: string): string {
|
||||
return mkdtempSync(join(tmpdir(), `scriptc-config-${name}-`));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user