{"version":3,"file":"httpBatchLink-CaWjh1oW.mjs","names":["batchLoader: BatchLoader<TKey, TValue>","pendingItems: BatchItem<TKey, TValue>[] | null","dispatchTimer: ReturnType<typeof setTimeout> | null","items: BatchItem<TKey, TValue>[]","groupedItems: BatchItem<TKey, TValue>[][]","batch: Batch<TKey, TValue>","key: TKey","item: BatchItem<TKey, TValue>","signal: AbortSignal","opts: HTTPBatchLinkOptions<TRouter['_def']['_config']['$types']>","type: ProcedureType"],"sources":["../src/internals/dataLoader.ts","../src/internals/signals.ts","../src/links/httpBatchLink.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-non-null-assertion */\n\ntype BatchItem<TKey, TValue> = {\n  aborted: boolean;\n  key: TKey;\n  resolve: ((value: TValue) => void) | null;\n  reject: ((error: Error) => void) | null;\n  batch: Batch<TKey, TValue> | null;\n};\ntype Batch<TKey, TValue> = {\n  items: BatchItem<TKey, TValue>[];\n};\nexport type BatchLoader<TKey, TValue> = {\n  validate: (keys: TKey[]) => boolean;\n  fetch: (keys: TKey[]) => Promise<TValue[] | Promise<TValue>[]>;\n};\n\n/**\n * A function that should never be called unless we messed something up.\n */\nconst throwFatalError = () => {\n  throw new Error(\n    'Something went wrong. Please submit an issue at https://github.com/trpc/trpc/issues/new',\n  );\n};\n\n/**\n * Dataloader that's very inspired by https://github.com/graphql/dataloader\n * Less configuration, no caching, and allows you to cancel requests\n * When cancelling a single fetch the whole batch will be cancelled only when _all_ items are cancelled\n */\nexport function dataLoader<TKey, TValue>(\n  batchLoader: BatchLoader<TKey, TValue>,\n) {\n  let pendingItems: BatchItem<TKey, TValue>[] | null = null;\n  let dispatchTimer: ReturnType<typeof setTimeout> | null = null;\n\n  const destroyTimerAndPendingItems = () => {\n    clearTimeout(dispatchTimer as any);\n    dispatchTimer = null;\n    pendingItems = null;\n  };\n\n  /**\n   * Iterate through the items and split them into groups based on the `batchLoader`'s validate function\n   */\n  function groupItems(items: BatchItem<TKey, TValue>[]) {\n    const groupedItems: BatchItem<TKey, TValue>[][] = [[]];\n    let index = 0;\n    while (true) {\n      const item = items[index];\n      if (!item) {\n        // we're done\n        break;\n      }\n      const lastGroup = groupedItems[groupedItems.length - 1]!;\n\n      if (item.aborted) {\n        // Item was aborted before it was dispatched\n        item.reject?.(new Error('Aborted'));\n        index++;\n        continue;\n      }\n\n      const isValid = batchLoader.validate(\n        lastGroup.concat(item).map((it) => it.key),\n      );\n\n      if (isValid) {\n        lastGroup.push(item);\n        index++;\n        continue;\n      }\n\n      if (lastGroup.length === 0) {\n        item.reject?.(new Error('Input is too big for a single dispatch'));\n        index++;\n        continue;\n      }\n      // Create new group, next iteration will try to add the item to that\n      groupedItems.push([]);\n    }\n    return groupedItems;\n  }\n\n  function dispatch() {\n    const groupedItems = groupItems(pendingItems!);\n    destroyTimerAndPendingItems();\n\n    // Create batches for each group of items\n    for (const items of groupedItems) {\n      if (!items.length) {\n        continue;\n      }\n      const batch: Batch<TKey, TValue> = {\n        items,\n      };\n      for (const item of items) {\n        item.batch = batch;\n      }\n      const promise = batchLoader.fetch(batch.items.map((_item) => _item.key));\n\n      promise\n        .then(async (result) => {\n          await Promise.all(\n            result.map(async (valueOrPromise, index) => {\n              const item = batch.items[index]!;\n              try {\n                const value = await Promise.resolve(valueOrPromise);\n\n                item.resolve?.(value);\n              } catch (cause) {\n                item.reject?.(cause as Error);\n              }\n\n              item.batch = null;\n              item.reject = null;\n              item.resolve = null;\n            }),\n          );\n\n          for (const item of batch.items) {\n            item.reject?.(new Error('Missing result'));\n            item.batch = null;\n          }\n        })\n        .catch((cause) => {\n          for (const item of batch.items) {\n            item.reject?.(cause);\n            item.batch = null;\n          }\n        });\n    }\n  }\n  function load(key: TKey): Promise<TValue> {\n    const item: BatchItem<TKey, TValue> = {\n      aborted: false,\n      key,\n      batch: null,\n      resolve: throwFatalError,\n      reject: throwFatalError,\n    };\n\n    const promise = new Promise<TValue>((resolve, reject) => {\n      item.reject = reject;\n      item.resolve = resolve;\n\n      pendingItems ??= [];\n      pendingItems.push(item);\n    });\n\n    dispatchTimer ??= setTimeout(dispatch);\n\n    return promise;\n  }\n\n  return {\n    load,\n  };\n}\n","import type { Maybe } from '@trpc/server/unstable-core-do-not-import';\n\n/**\n * Like `Promise.all()` but for abort signals\n * - When all signals have been aborted, the merged signal will be aborted\n * - If one signal is `null`, no signal will be aborted\n */\nexport function allAbortSignals(...signals: Maybe<AbortSignal>[]): AbortSignal {\n  const ac = new AbortController();\n\n  const count = signals.length;\n\n  let abortedCount = 0;\n\n  const onAbort = () => {\n    if (++abortedCount === count) {\n      ac.abort();\n    }\n  };\n\n  for (const signal of signals) {\n    if (signal?.aborted) {\n      onAbort();\n    } else {\n      signal?.addEventListener('abort', onAbort, {\n        once: true,\n      });\n    }\n  }\n\n  return ac.signal;\n}\n\n/**\n * Like `Promise.race` but for abort signals\n *\n * Basically, a ponyfill for\n * [`AbortSignal.any`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static).\n */\nexport function raceAbortSignals(\n  ...signals: Maybe<AbortSignal>[]\n): AbortSignal {\n  const ac = new AbortController();\n\n  for (const signal of signals) {\n    if (signal?.aborted) {\n      ac.abort();\n    } else {\n      signal?.addEventListener('abort', () => ac.abort(), { once: true });\n    }\n  }\n\n  return ac.signal;\n}\n\nexport function abortSignalToPromise(signal: AbortSignal): Promise<never> {\n  return new Promise((_, reject) => {\n    if (signal.aborted) {\n      reject(signal.reason);\n      return;\n    }\n    signal.addEventListener(\n      'abort',\n      () => {\n        reject(signal.reason);\n      },\n      { once: true },\n    );\n  });\n}\n","import type { AnyRouter, ProcedureType } from '@trpc/server';\nimport { observable } from '@trpc/server/observable';\nimport { transformResult } from '@trpc/server/unstable-core-do-not-import';\nimport type { BatchLoader } from '../internals/dataLoader';\nimport { dataLoader } from '../internals/dataLoader';\nimport { allAbortSignals } from '../internals/signals';\nimport type { NonEmptyArray } from '../internals/types';\nimport { TRPCClientError } from '../TRPCClientError';\nimport type { HTTPBatchLinkOptions } from './HTTPBatchLinkOptions';\nimport type { HTTPResult } from './internals/httpUtils';\nimport {\n  getUrl,\n  jsonHttpRequester,\n  resolveHTTPLinkOptions,\n} from './internals/httpUtils';\nimport type { Operation, TRPCLink } from './types';\n\n/**\n * @see https://trpc.io/docs/client/links/httpBatchLink\n */\nexport function httpBatchLink<TRouter extends AnyRouter>(\n  opts: HTTPBatchLinkOptions<TRouter['_def']['_config']['$types']>,\n): TRPCLink<TRouter> {\n  const resolvedOpts = resolveHTTPLinkOptions(opts);\n  const maxURLLength = opts.maxURLLength ?? Infinity;\n  const maxItems = opts.maxItems ?? Infinity;\n\n  return () => {\n    const batchLoader = (\n      type: ProcedureType,\n    ): BatchLoader<Operation, HTTPResult> => {\n      return {\n        validate(batchOps) {\n          if (maxURLLength === Infinity && maxItems === Infinity) {\n            // escape hatch for quick calcs\n            return true;\n          }\n          if (batchOps.length > maxItems) {\n            return false;\n          }\n          const path = batchOps.map((op) => op.path).join(',');\n          const inputs = batchOps.map((op) => op.input);\n\n          const url = getUrl({\n            ...resolvedOpts,\n            type,\n            path,\n            inputs,\n            signal: null,\n          });\n\n          return url.length <= maxURLLength;\n        },\n        async fetch(batchOps) {\n          const path = batchOps.map((op) => op.path).join(',');\n          const inputs = batchOps.map((op) => op.input);\n          const signal = allAbortSignals(...batchOps.map((op) => op.signal));\n\n          const res = await jsonHttpRequester({\n            ...resolvedOpts,\n            path,\n            inputs,\n            type,\n            headers() {\n              if (!opts.headers) {\n                return {};\n              }\n              if (typeof opts.headers === 'function') {\n                return opts.headers({\n                  opList: batchOps as NonEmptyArray<Operation>,\n                });\n              }\n              return opts.headers;\n            },\n            signal,\n          });\n          const resJSON = Array.isArray(res.json)\n            ? res.json\n            : batchOps.map(() => res.json);\n          const result = resJSON.map((item) => ({\n            meta: res.meta,\n            json: item,\n          }));\n          return result;\n        },\n      };\n    };\n\n    const query = dataLoader(batchLoader('query'));\n    const mutation = dataLoader(batchLoader('mutation'));\n\n    const loaders = { query, mutation };\n    return ({ op }) => {\n      return observable((observer) => {\n        /* istanbul ignore if -- @preserve */\n        if (op.type === 'subscription') {\n          throw new Error(\n            'Subscriptions are unsupported by `httpLink` - use `httpSubscriptionLink` or `wsLink`',\n          );\n        }\n        const loader = loaders[op.type];\n        const promise = loader.load(op);\n\n        let _res = undefined as HTTPResult | undefined;\n        promise\n          .then((res) => {\n            _res = res;\n            const transformed = transformResult(\n              res.json,\n              resolvedOpts.transformer.output,\n            );\n\n            if (!transformed.ok) {\n              observer.error(\n                TRPCClientError.from(transformed.error, {\n                  meta: res.meta,\n                }),\n              );\n              return;\n            }\n            observer.next({\n              context: res.meta,\n              result: transformed.result,\n            });\n            observer.complete();\n          })\n          .catch((err) => {\n            observer.error(\n              TRPCClientError.from(err, {\n                meta: _res?.meta,\n              }),\n            );\n          });\n\n        return () => {\n          // noop\n        };\n      });\n    };\n  };\n}\n"],"mappings":"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"}