| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0ab8f26 commit 2a3babb
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,6 +58,7 @@ const { | |||
| 58 | 58 | kResolvedPromise, | |
| 59 | 59 | clampHWM, | |
| 60 | 60 | convertChunks, | |
| 61 | + getWriterSignal, | ||
| 61 | 62 | getMinCursor, | |
| 62 | 63 | hasProtocol, | |
| 63 | 64 | onSignalAbort, | |
@@ -526,41 +527,41 @@ class BroadcastWriter { | |||
| 526 | 527 | return this.#isClosedOrAborted() ? null : this.#broadcast[kGetDesiredSize](); | |
| 527 | 528 | } | |
| 528 | 529 | ||
| 529 | - #canUseWriteFastPath(options) { | ||
| 530 | - return !options?.signal && !this.#isClosed() && !this.#aborted && | ||
| 530 | + #canUseWriteFastPath(signal) { | ||
| 531 | + return !signal && !this.#isClosed() && !this.#aborted && | ||
| 531 | 532 | this.#broadcast[kCanWrite](); | |
| 532 | 533 | } | |
| 533 | 534 | ||
| 534 | 535 | write(chunk, options) { | |
| 536 | + const signal = getWriterSignal(options); | ||
| 535 | 537 | // Fast path: no signal, writer open, buffer has space | |
| 536 | - if (this.#canUseWriteFastPath(options)) { | ||
| 538 | + if (this.#canUseWriteFastPath(signal)) { | ||
| 537 | 539 | const converted = toUint8Array(chunk); | |
| 538 | 540 | this.#broadcast[kWrite]([converted]); | |
| 539 | 541 | this.#totalBytes += TypedArrayPrototypeGetByteLength(converted); | |
| 540 | 542 | return kResolvedPromise; | |
| 541 | 543 | } | |
| 542 | - return this.#writevSlow([chunk], options); | ||
| 544 | + return this.#writevSlow([chunk], signal); | ||
| 543 | 545 | } | |
| 544 | 546 | ||
| 545 | 547 | writev(chunks, options) { | |
| 546 | 548 | if (!ArrayIsArray(chunks)) { | |
| 547 | 549 | throw new ERR_INVALID_ARG_TYPE('chunks', 'Array', chunks); | |
| 548 | 550 | } | |
| 551 | + const signal = getWriterSignal(options); | ||
| 549 | 552 | // Fast path: no signal, writer open, buffer has space | |
| 550 | - if (this.#canUseWriteFastPath(options)) { | ||
| 553 | + if (this.#canUseWriteFastPath(signal)) { | ||
| 551 | 554 | const converted = convertChunks(chunks); | |
| 552 | 555 | this.#broadcast[kWrite](converted); | |
| 553 | 556 | for (let i = 0; i < converted.length; i++) { | |
| 554 | 557 | this.#totalBytes += TypedArrayPrototypeGetByteLength(converted[i]); | |
| 555 | 558 | } | |
| 556 | 559 | return kResolvedPromise; | |
| 557 | 560 | } | |
| 558 | - return this.#writevSlow(chunks, options); | ||
| 561 | + return this.#writevSlow(chunks, signal); | ||
| 559 | 562 | } | |
| 560 | 563 | ||
| 561 | - async #writevSlow(chunks, options) { | ||
| 562 | - const signal = options?.signal; | ||
| 563 | - | ||
| 564 | + async #writevSlow(chunks, signal) { | ||
| 564 | 565 | // Check for pre-aborted | |
| 565 | 566 | signal?.throwIfAborted(); | |
| 566 | 567 | ||
@@ -623,6 +624,7 @@ class BroadcastWriter { | |||
| 623 | 624 | ||
| 624 | 625 | // end() is synchronous internally - signal accepted for interface compliance. | |
| 625 | 626 | end(options) { | |
| 627 | + getWriterSignal(options); | ||
| 626 | 628 | if (this.#isClosed()) return this.#closed; | |
| 627 | 629 | this.#closed = PromiseResolve(this.#totalBytes); | |
| 628 | 630 | this.#broadcast[kEnd](); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -61,6 +61,7 @@ const { | |||
| 61 | 61 | } = require('internal/streams/iter/types'); | |
| 62 | 62 | ||
| 63 | 63 | const { | |
| 64 | + getWriterSignal, | ||
| 64 | 65 | validateBackpressure, | |
| 65 | 66 | toUint8Array, | |
| 66 | 67 | } = require('internal/streams/iter/utils'); | |
@@ -572,10 +573,11 @@ function fromWritable(writable, options = kNullPrototype) { | |||
| 572 | 573 | // as 'error' events caught by our generic error handler, rejecting | |
| 573 | 574 | // the next pending operation rather than the already-resolved one. | |
| 574 | 575 | // | |
| 575 | - // The options.signal parameter from the Writer interface is ignored. | ||
| 576 | - // Classic stream.Writable has no per-write abort signal support; | ||
| 577 | - // cancellation should be handled at the pipeline level instead. | ||
| 578 | - write(chunk) { | ||
| 576 | + // The options.signal parameter from the Writer interface is validated but | ||
| 577 | + // otherwise ignored. Classic stream.Writable has no per-write abort signal | ||
| 578 | + // support; cancellation should be handled at the pipeline level instead. | ||
| 579 | + write(chunk, options) { | ||
| 580 | + getWriterSignal(options); | ||
| 579 | 581 | if (!isWritable()) { | |
| 580 | 582 | return PromiseReject(new ERR_STREAM_WRITE_AFTER_END()); | |
| 581 | 583 | } | |
@@ -617,10 +619,11 @@ function fromWritable(writable, options = kNullPrototype) { | |||
| 617 | 619 | return PromiseResolve(); | |
| 618 | 620 | }, | |
| 619 | 621 | ||
| 620 | - writev(chunks) { | ||
| 622 | + writev(chunks, options) { | ||
| 621 | 623 | if (!ArrayIsArray(chunks)) { | |
| 622 | 624 | throw new ERR_INVALID_ARG_TYPE('chunks', 'Array', chunks); | |
| 623 | 625 | } | |
| 626 | + getWriterSignal(options); | ||
| 624 | 627 | if (!isWritable()) { | |
| 625 | 628 | return PromiseReject(new ERR_STREAM_WRITE_AFTER_END()); | |
| 626 | 629 | } | |
@@ -666,8 +669,10 @@ function fromWritable(writable, options = kNullPrototype) { | |||
| 666 | 669 | return -1; | |
| 667 | 670 | }, | |
| 668 | 671 | ||
| 669 | - // options.signal is ignored for the same reason as write(). | ||
| 670 | - end() { | ||
| 672 | + // options.signal is validated but otherwise ignored for the same reason as | ||
| 673 | + // write(). | ||
| 674 | + end(options) { | ||
| 675 | + getWriterSignal(options); | ||
| 671 | 676 | if ((writable.writableFinished ?? false) || | |
| 672 | 677 | (writable.destroyed ?? false)) { | |
| 673 | 678 | cleanup(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -42,6 +42,7 @@ const { | |||
| 42 | 42 | onSignalAbort, | |
| 43 | 43 | toUint8Array, | |
| 44 | 44 | convertChunks, | |
| 45 | + getWriterSignal, | ||
| 45 | 46 | parsePullArgs, | |
| 46 | 47 | validateBackpressure, | |
| 47 | 48 | } = require('internal/streams/iter/utils'); | |
@@ -565,26 +566,28 @@ class PushWriter { | |||
| 565 | 566 | } | |
| 566 | 567 | ||
| 567 | 568 | write(chunk, options) { | |
| 568 | - if (!options?.signal && this.#queue.canWriteSync()) { | ||
| 569 | + const signal = getWriterSignal(options); | ||
| 570 | + if (!signal && this.#queue.canWriteSync()) { | ||
| 569 | 571 | const bytes = toUint8Array(chunk); | |
| 570 | 572 | this.#queue.writeSync([bytes]); | |
| 571 | 573 | return kResolvedPromise; | |
| 572 | 574 | } | |
| 573 | 575 | const bytes = toUint8Array(chunk); | |
| 574 | - return this.#queue.writeAsync([bytes], options?.signal); | ||
| 576 | + return this.#queue.writeAsync([bytes], signal); | ||
| 575 | 577 | } | |
| 576 | 578 | ||
| 577 | 579 | writev(chunks, options) { | |
| 578 | 580 | if (!ArrayIsArray(chunks)) { | |
| 579 | 581 | throw new ERR_INVALID_ARG_TYPE('chunks', 'Array', chunks); | |
| 580 | 582 | } | |
| 581 | - if (!options?.signal && this.#queue.canWriteSync()) { | ||
| 583 | + const signal = getWriterSignal(options); | ||
| 584 | + if (!signal && this.#queue.canWriteSync()) { | ||
| 582 | 585 | const bytes = convertChunks(chunks); | |
| 583 | 586 | this.#queue.writeSync(bytes); | |
| 584 | 587 | return kResolvedPromise; | |
| 585 | 588 | } | |
| 586 | 589 | const bytes = convertChunks(chunks); | |
| 587 | - return this.#queue.writeAsync(bytes, options?.signal); | ||
| 590 | + return this.#queue.writeAsync(bytes, signal); | ||
| 588 | 591 | } | |
| 589 | 592 | ||
| 590 | 593 | writeSync(chunk) { | |
@@ -601,6 +604,7 @@ class PushWriter { | |||
| 601 | 604 | } | |
| 602 | 605 | ||
| 603 | 606 | end(options) { | |
| 607 | + getWriterSignal(options); | ||
| 604 | 608 | const result = this.#queue.end(); | |
| 605 | 609 | if (result === -2) { | |
| 606 | 610 | // Errored: reject with stored error | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -35,7 +35,10 @@ const { isError } = require('internal/util'); | |||
| 35 | 35 | ||
| 36 | 36 | const { isSharedArrayBuffer, isUint8Array } = require('internal/util/types'); | |
| 37 | 37 | ||
| 38 | - const { validateOneOf } = require('internal/validators'); | ||
| 38 | + const { | ||
| 39 | + validateAbortSignal, | ||
| 40 | + validateOneOf, | ||
| 41 | + } = require('internal/validators'); | ||
| 39 | 42 | ||
| 40 | 43 | // Cached resolved promise to avoid allocating a new one on every sync fast-path. | |
| 41 | 44 | const kResolvedPromise = PromiseResolve(); | |
@@ -267,6 +270,17 @@ function convertChunks(chunks) { | |||
| 267 | 270 | return result; | |
| 268 | 271 | } | |
| 269 | 272 | ||
| 273 | + /** | ||
| 274 | + * Validate Writer options and return options.signal. | ||
| 275 | + * @param {object|undefined} options | ||
| 276 | + * @returns {AbortSignal|undefined} | ||
| 277 | + */ | ||
| 278 | + function getWriterSignal(options) { | ||
| 279 | + const signal = options?.signal; | ||
| 280 | + validateAbortSignal(signal, 'options.signal'); | ||
| 281 | + return signal; | ||
| 282 | + } | ||
| 283 | + | ||
| 270 | 284 | /** | |
| 271 | 285 | * Wrap a caught value as an Error, converting non-Error values. | |
| 272 | 286 | * @param {unknown} error | |
@@ -378,6 +392,7 @@ module.exports = { | |||
| 378 | 392 | clampHWM, | |
| 379 | 393 | concatBytes, | |
| 380 | 394 | convertChunks, | |
| 395 | + getWriterSignal, | ||
| 381 | 396 | getMinCursor, | |
| 382 | 397 | hasProtocol, | |
| 383 | 398 | isPullOptions, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,8 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | 4 | const common = require('../common'); | |
| 5 | 5 | const assert = require('assert'); | |
| 6 | + const { Writable } = require('stream'); | ||
| 6 | 7 | const { | |
| 7 | - from, fromSync, pull, pullSync, pipeTo, | ||
| 8 | + from, fromSync, pull, pullSync, pipeTo, fromWritable, | ||
| 8 | 9 | push, duplex, broadcast, Broadcast, share, shareSync, | |
| 9 | 10 | Share, SyncShare, | |
| 10 | 11 | bytes, bytesSync, text, textSync, | |
@@ -42,6 +43,19 @@ assert.throws(() => push({ signal: {} }), { code: 'ERR_INVALID_ARG_TYPE' }); | |||
| 42 | 43 | assert.throws(() => push(42, {}), { code: 'ERR_INVALID_ARG_TYPE' }); | |
| 43 | 44 | assert.throws(() => push('bad', {}), { code: 'ERR_INVALID_ARG_TYPE' }); | |
| 44 | 45 | ||
| 46 | + // Writer options.signal must be AbortSignal | ||
| 47 | + { | ||
| 48 | + const { writer } = push(); | ||
| 49 | + const badOptions = { signal: 'bad' }; | ||
| 50 | + assert.throws(() => writer.write('a', badOptions), | ||
| 51 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 52 | + assert.throws(() => writer.writev(['b'], badOptions), | ||
| 53 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 54 | + assert.throws(() => writer.end(badOptions), | ||
| 55 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 56 | + writer.endSync(); | ||
| 57 | + } | ||
| 58 | + | ||
| 45 | 59 | // Writer.writev requires array | |
| 46 | 60 | { | |
| 47 | 61 | const { writer } = push(); | |
@@ -147,6 +161,19 @@ assert.throws(() => broadcast({ highWaterMark: Number.MAX_SAFE_INTEGER + 1 }), | |||
| 147 | 161 | assert.throws(() => broadcast({ signal: {} }), { code: 'ERR_INVALID_ARG_TYPE' }); | |
| 148 | 162 | assert.throws(() => broadcast({ backpressure: 'bad' }), { code: 'ERR_INVALID_ARG_VALUE' }); | |
| 149 | 163 | ||
| 164 | + // BroadcastWriter options.signal must be AbortSignal | ||
| 165 | + { | ||
| 166 | + const { writer } = broadcast(); | ||
| 167 | + const badOptions = { signal: 'bad' }; | ||
| 168 | + assert.throws(() => writer.write('a', badOptions), | ||
| 169 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 170 | + assert.throws(() => writer.writev(['b'], badOptions), | ||
| 171 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 172 | + assert.throws(() => writer.end(badOptions), | ||
| 173 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 174 | + writer.endSync(); | ||
| 175 | + } | ||
| 176 | + | ||
| 150 | 177 | // BroadcastWriter.writev requires array | |
| 151 | 178 | { | |
| 152 | 179 | const { writer } = broadcast(); | |
@@ -160,6 +187,24 @@ assert.throws(() => broadcast({ backpressure: 'bad' }), { code: 'ERR_INVALID_ARG | |||
| 160 | 187 | // Broadcast.from rejects non-streamable input | |
| 161 | 188 | assert.throws(() => Broadcast.from(42), { code: 'ERR_INVALID_ARG_TYPE' }); | |
| 162 | 189 | ||
| 190 | + // fromWritable Writer options.signal must be AbortSignal | ||
| 191 | + { | ||
| 192 | + const writable = new Writable({ | ||
| 193 | + write(chunk, encoding, callback) { | ||
| 194 | + callback(); | ||
| 195 | + }, | ||
| 196 | + }); | ||
| 197 | + const writer = fromWritable(writable); | ||
| 198 | + const badOptions = { signal: 'bad' }; | ||
| 199 | + assert.throws(() => writer.write('a', badOptions), | ||
| 200 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 201 | + assert.throws(() => writer.writev(['b'], badOptions), | ||
| 202 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 203 | + assert.throws(() => writer.end(badOptions), | ||
| 204 | + { code: 'ERR_INVALID_ARG_TYPE' }); | ||
| 205 | + writable.destroy(); | ||
| 206 | + } | ||
| 207 | + | ||
| 163 | 208 | // ============================================================================= | |
| 164 | 209 | // share() / shareSync() validation | |
| 165 | 210 | // ============================================================================= | |
| Back | FazBrowse Home | New Git URL |
0 commit comments