UNPKG

@strapi/data-transfer

Version:

Data transfer capabilities for Strapi

60 lines (57 loc) 2.15 kB
import { once } from 'node:events'; import { finished } from 'node:stream/promises'; /** * Async helper for application code that `await`s sequential writes to a `Writable`. * * 1. Waits until `writable.write` invokes its callback (chunk accepted / `_write` finished). * 2. If `write()` returned `false` **and** `writable.writableNeedDrain` is still true after the * callback, waits for `'drain'`. * * We check both: the return value tells us backpressure was signaled; `writableNeedDrain` avoids * awaiting `'drain'` when it already fired before we subscribed (would otherwise hang forever). * * While waiting for `'drain'`, we also race {@link finished} so destroying the writable (e.g. abort) * cannot leave this promise pending forever. */ async function write(writable, chunk) { let flushed = true; await new Promise((resolve, reject)=>{ let settled = false; const finish = (fn)=>{ if (settled) { return; } settled = true; writable.off('error', onError); fn(); }; const onError = (err)=>{ finish(()=>reject(err)); }; writable.once('error', onError); flushed = writable.write(chunk, (err)=>{ if (err) { // Do not reject or remove `error` here: Node may emit `error` after this callback, and // clearing the listener first would leave that emission unhandled. setImmediate(()=>{ if (!settled) { finish(()=>reject(err)); } }); return; } finish(()=>resolve()); }); }); if (!flushed && writable.writableNeedDrain) { // Without `finished`, awaiting only `drain` can hang forever if the writable is destroyed first. await Promise.race([ once(writable, 'drain'), finished(writable, { readable: false, writable: true }) ]); } } export { write }; //# sourceMappingURL=writable-async-write.mjs.map