UNPKG

@trpc/server

Version:

The tRPC server library

242 lines (238 loc) • 8.29 kB
'use strict'; var createDeferred = require('./createDeferred.js'); var disposable = require('./disposable.js'); function _ts_add_disposable_resource(env, value, async) { if (value !== null && value !== void 0) { if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); var dispose, inner; { if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); dispose = value[Symbol.asyncDispose]; } if (dispose === void 0) { if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); dispose = value[Symbol.dispose]; inner = dispose; } if (typeof dispose !== "function") throw new TypeError("Object not disposable."); if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; env.stack.push({ value: value, dispose: dispose, async: async }); } else { env.stack.push({ async: true }); } return value; } function _ts_dispose_resources(env) { var _SuppressedError = typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; return (_ts_dispose_resources = function _ts_dispose_resources(env) { function fail(e) { env.error = env.hasError ? new _SuppressedError(e, env.error, "An error was suppressed during disposal.") : e; env.hasError = true; } var r, s = 0; function next() { while(r = env.stack.pop()){ try { if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next); if (r.dispose) { var result = r.dispose.call(r.value); if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); }); } else s |= 1; } catch (e) { fail(e); } } if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); if (env.hasError) throw env.error; } return next(); })(env); } function createManagedIterator(iterable, onResult) { const iterator = iterable[Symbol.asyncIterator](); let state = 'idle'; function cleanup() { state = 'done'; onResult = ()=>{ // noop }; } function pull() { if (state !== 'idle') { return; } state = 'pending'; const next = iterator.next(); next.then((result)=>{ if (result.done) { state = 'done'; onResult({ status: 'return', value: result.value }); cleanup(); return; } state = 'idle'; onResult({ status: 'yield', value: result.value }); }).catch((cause)=>{ onResult({ status: 'error', error: cause }); cleanup(); }); } return { pull, destroy: async ()=>{ cleanup(); await iterator.return?.(); } }; } /** * Creates a new async iterable that merges multiple async iterables into a single stream. * Values from the input iterables are yielded in the order they resolve, similar to Promise.race(). * * New iterables can be added dynamically using the returned {@link MergedAsyncIterables.add} method, even after iteration has started. * * If any of the input iterables throws an error, that error will be propagated through the merged stream. * Other iterables will not continue to be processed. * * @template TYield The type of values yielded by the input iterables */ function mergeAsyncIterables() { let state = 'idle'; let flushSignal = createDeferred.createDeferred(); /** * used while {@link state} is `idle` */ const iterables = []; /** * used while {@link state} is `pending` */ const iterators = new Set(); const buffer = []; function initIterable(iterable) { if (state !== 'pending') { // shouldn't happen return; } const iterator = createManagedIterator(iterable, (result)=>{ if (state !== 'pending') { // shouldn't happen return; } switch(result.status){ case 'yield': buffer.push([ iterator, result ]); break; case 'return': iterators.delete(iterator); break; case 'error': buffer.push([ iterator, result ]); iterators.delete(iterator); break; } flushSignal.resolve(); }); iterators.add(iterator); iterator.pull(); } return { add (iterable) { switch(state){ case 'idle': iterables.push(iterable); break; case 'pending': initIterable(iterable); break; } }, async *[Symbol.asyncIterator] () { const env = { stack: [], error: void 0, hasError: false }; try { if (state !== 'idle') { throw new Error('Cannot iterate twice'); } state = 'pending'; const _finally = _ts_add_disposable_resource(env, disposable.makeAsyncResource({}, async ()=>{ state = 'done'; const errors = []; await Promise.all(Array.from(iterators.values()).map(async (it)=>{ try { await it.destroy(); } catch (cause) { errors.push(cause); } })); buffer.length = 0; iterators.clear(); flushSignal.resolve(); if (errors.length > 0) { throw new AggregateError(errors); } }), true); ; while(iterables.length > 0){ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion initIterable(iterables.shift()); } while(iterators.size > 0){ await flushSignal.promise; while(buffer.length > 0){ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const [iterator, result] = buffer.shift(); switch(result.status){ case 'yield': yield result.value; iterator.pull(); break; case 'error': throw result.error; } } flushSignal = createDeferred.createDeferred(); } } catch (e) { env.error = e; env.hasError = true; } finally{ const result = _ts_dispose_resources(env); if (result) await result; } } }; } exports.mergeAsyncIterables = mergeAsyncIterables;