UNPKG

@trpc/server

Version:

The tRPC server library

1 lines • 10.8 kB
{"version":3,"file":"observable-kpFFzvou.mjs","names":[],"sources":["../src/observable/observable.ts"],"sourcesContent":["import type {\n Observable,\n Observer,\n OperatorFunction,\n TeardownLogic,\n UnaryFunction,\n Unsubscribable,\n} from './types';\n\n/**\n * The type of the values an observable emits.\n * @public\n */\nexport type inferObservableValue<TObservable> =\n TObservable extends Observable<infer TValue, unknown> ? TValue : never;\n\n/**\n * Whether a value is an observable: an object with `subscribe`.\n * @public\n */\nexport function isObservable(x: unknown): x is Observable<unknown, unknown> {\n return typeof x === 'object' && x !== null && 'subscribe' in x;\n}\n\n/**\n * Creates an observable. `subscribe` runs for each subscriber, and can\n * return a function that cleans up when it unsubscribes.\n * @example\n * ```ts\n * const ticks = observable<number>((observer) => {\n * const timer = setInterval(() => observer.next(Date.now()), 1000);\n * return () => clearInterval(timer);\n * });\n * ```\n * @see https://trpc.io/docs/client/links#creating-a-custom-link\n * @public\n */\nexport function observable<TValue, TError = unknown>(\n subscribe: (observer: Observer<TValue, TError>) => TeardownLogic,\n): Observable<TValue, TError> {\n const self: Observable<TValue, TError> = {\n subscribe(observer) {\n let teardownRef: TeardownLogic | null = null;\n let isDone = false;\n let unsubscribed = false;\n let teardownImmediately = false;\n let subscribing = true;\n let thrownWhileSubscribing = null as { cause: unknown } | null;\n function end(notify: () => void) {\n try {\n notify();\n } catch (cause) {\n // The source hasn't returned its teardown yet; rethrow once it has\n // run.\n if (!subscribing) throw cause;\n thrownWhileSubscribing ??= { cause };\n } finally {\n unsubscribe();\n }\n }\n function unsubscribe() {\n if (teardownRef === null) {\n teardownImmediately = true;\n return;\n }\n if (unsubscribed) {\n return;\n }\n unsubscribed = true;\n\n if (typeof teardownRef === 'function') {\n teardownRef();\n } else if (teardownRef) {\n teardownRef.unsubscribe();\n }\n }\n teardownRef = subscribe({\n next(value) {\n if (isDone) {\n return;\n }\n observer.next?.(value);\n },\n error(err) {\n if (isDone) {\n return;\n }\n isDone = true;\n end(() => observer.error?.(err));\n },\n complete() {\n if (isDone) {\n return;\n }\n isDone = true;\n end(() => observer.complete?.());\n },\n });\n subscribing = false;\n if (teardownImmediately) {\n unsubscribe();\n }\n if (thrownWhileSubscribing) {\n throw thrownWhileSubscribing.cause;\n }\n return {\n unsubscribe,\n };\n },\n pipe(\n ...operations: OperatorFunction<any, any, any, any>[]\n ): Observable<any, any> {\n return operations.reduce(pipeReducer, self);\n },\n };\n return self;\n}\n\nfunction pipeReducer(prev: any, fn: UnaryFunction<any, any>) {\n return fn(prev);\n}\n\n/**\n * Resolves with an observable's first value. Rejects if it errors or\n * completes first. It doesn't unsubscribe after the first value.\n * @internal\n */\nexport function observableToPromise<TValue>(\n observable: Observable<TValue, unknown>,\n) {\n const ac = new AbortController();\n const promise = new Promise<TValue>((resolve, reject) => {\n let isDone = false;\n function onDone() {\n if (isDone) {\n return;\n }\n isDone = true;\n obs$.unsubscribe();\n }\n ac.signal.addEventListener('abort', () => {\n reject(ac.signal.reason);\n });\n const obs$ = observable.subscribe({\n next(data) {\n isDone = true;\n resolve(data);\n onDone();\n },\n error(data) {\n reject(data);\n },\n complete() {\n ac.abort();\n onDone();\n },\n });\n });\n return promise;\n}\n\n/**\n * Turns an observable into an async iterable, like Node's `events.on()`.\n * Values wait in a queue until `next()` reads them. An error rejects the\n * `next()` after the queued values, once. Completion, `return()` and aborting\n * `signal` unsubscribe and end every pending `next()` with `{ done: true }`;\n * anything the observable sends after that is ignored.\n *\n * If unsubscribing throws, `return()` rejects with the error, and completion\n * or an error from the observable rethrows it. On abort, it rejects the\n * `next()` after the queued values instead, as an error would.\n * @internal\n */\nexport function observableToAsyncIterable<TValue>(\n observable: Observable<TValue, unknown>,\n signal: AbortSignal,\n): AsyncIterableIterator<TValue> {\n const values: TValue[] = [];\n const pending: {\n resolve: (result: IteratorResult<TValue>) => void;\n reject: (cause: unknown) => void;\n }[] = [];\n let error: { cause: unknown } | null = null;\n let finished = false;\n let subscription: Unsubscribable | null = null;\n\n /** Unsubscribes once, and returns what the teardown threw. */\n function unsubscribe(): { cause: unknown } | null {\n if (finished) {\n return null;\n }\n finished = true;\n signal.removeEventListener('abort', abort);\n try {\n subscription?.unsubscribe();\n return null;\n } catch (cause) {\n return { cause };\n }\n }\n /** Settles every pending `next()`: the first with `error` if there is one. */\n function settle() {\n const first = error ? pending.shift() : undefined;\n if (error && first) {\n const { cause } = error;\n error = null;\n first.reject(cause);\n }\n for (const promise of pending.splice(0)) {\n promise.resolve({ value: undefined, done: true });\n }\n }\n function finish() {\n const failure = unsubscribe();\n settle();\n if (failure) {\n throw failure.cause;\n }\n }\n function abort() {\n const failure = unsubscribe();\n if (failure) {\n error = failure;\n }\n settle();\n }\n\n subscription = observable.subscribe({\n next(value) {\n if (finished) {\n return;\n }\n const promise = pending.shift();\n if (promise) {\n promise.resolve({ value, done: false });\n } else {\n values.push(value);\n }\n },\n error(cause) {\n if (finished) {\n return;\n }\n error = { cause };\n finish();\n },\n complete: finish,\n });\n if (finished) {\n subscription.unsubscribe();\n } else if (signal.aborted) {\n abort();\n } else {\n signal.addEventListener('abort', abort, { once: true });\n }\n\n const iterator: AsyncIterableIterator<TValue> = {\n [Symbol.asyncIterator]() {\n return iterator;\n },\n async next() {\n if (values.length > 0) {\n return { value: values.shift() as TValue, done: false };\n }\n if (error) {\n const { cause } = error;\n error = null;\n throw cause;\n }\n if (finished) {\n return { value: undefined, done: true };\n }\n return new Promise((resolve, reject) => {\n pending.push({ resolve, reject });\n });\n },\n async return() {\n values.length = 0;\n error = null;\n finish();\n return { value: undefined, done: true };\n },\n };\n return iterator;\n}\n"],"mappings":"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"}