* Create a stream/iter Writer adapter from a classic Writable (or duck-type). * * Duck-type requirements: write() and on()/off() methods. * Falls back to sensible defaults for missing properties like * writableHighWaterMark, writableLength, writableObjectMode. * @param {object} writable - A cla
(writable, options = kNullPrototype)
| 422 | * @returns {object} A stream/iter Writer adapter. |
| 423 | */ |
| 424 | function fromWritable(writable, options = kNullPrototype) { |
| 425 | if (writable == null || |
| 426 | typeof writable.write !== 'function' || |
| 427 | typeof writable.on !== 'function') { |
| 428 | throw new ERR_INVALID_ARG_TYPE('writable', 'Writable', writable); |
| 429 | } |
| 430 | |
| 431 | validateObject(options, 'options'); |
| 432 | const { |
| 433 | backpressure = 'strict', |
| 434 | } = options; |
| 435 | validateBackpressure(backpressure); |
| 436 | |
| 437 | // The Writer interface is bytes-only. Object-mode Writables expect |
| 438 | // arbitrary JS values, which is incompatible. |
| 439 | if (writable.writableObjectMode) { |
| 440 | throw new ERR_INVALID_STATE( |
| 441 | 'Cannot create a stream/iter Writer from an object-mode Writable'); |
| 442 | } |
| 443 | |
| 444 | // drop-oldest is not supported for classic stream.Writable. The |
| 445 | // Writable's internal buffer stores individual { chunk, encoding, |
| 446 | // callback } entries with no concept of batch boundaries. A writev() |
| 447 | // call fans out into N separate buffer entries, so a subsequent |
| 448 | // drop-oldest eviction could partially tear apart an earlier atomic |
| 449 | // writev batch. The PushWriter avoids this because writev occupies a |
| 450 | // single slot. Supporting drop-oldest here would require either |
| 451 | // accepting partial writev eviction or adding batch tracking to the |
| 452 | // buffer -- neither is acceptable without a deeper rework of Writable |
| 453 | // internals. |
| 454 | if (backpressure === 'drop-oldest') { |
| 455 | throw new ERR_INVALID_ARG_VALUE('options.backpressure', backpressure, |
| 456 | 'drop-oldest is not supported for classic stream.Writable'); |
| 457 | } |
| 458 | |
| 459 | // Return cached adapter if available. Backpressure policy changes writer |
| 460 | // behavior, so cache one adapter per policy. |
| 461 | let cachedByBackpressure = fromWritableCache.get(writable); |
| 462 | if (cachedByBackpressure !== undefined) { |
| 463 | const cached = cachedByBackpressure.get(backpressure); |
| 464 | if (cached !== undefined) return cached; |
| 465 | } else { |
| 466 | cachedByBackpressure = new SafeMap(); |
| 467 | fromWritableCache.set(writable, cachedByBackpressure); |
| 468 | } |
| 469 | |
| 470 | // Fall back to sensible defaults for duck-typed streams that may not |
| 471 | // expose the full stream.Writable property set. |
| 472 | const hwm = writable.writableHighWaterMark ?? 16384; |
| 473 | let totalBytes = 0; |
| 474 | |
| 475 | // Waiters pending on backpressure resolution (block policy only). |
| 476 | // Multiple un-awaited writes can each add a waiter, so this must be |
| 477 | // a list. A single persistent 'drain' listener and 'error' listener |
| 478 | // (installed once lazily) resolve or reject all waiters to avoid |
| 479 | // accumulating per-write listeners on the stream. |
| 480 | let waiters = []; |
| 481 | let listenersInstalled = false; |
no test coverage detected
searching dependent graphs…