{"version":3,"file":"inflight-cache.mjs","sourceRoot":"","sources":["../src/inflight-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n  JsonRpcMiddleware,\n  MiddlewareContext,\n} from '@metamask/json-rpc-engine/v2';\nimport { createDeferredPromise } from '@metamask/utils';\nimport type { Json, JsonRpcRequest } from '@metamask/utils';\n\nimport { projectLogger, createModuleLogger } from './logging-utils';\nimport { cacheIdentifierForRequest } from './utils/cache';\n\ntype RequestHandler = [\n  (result: Readonly<Json>) => void,\n  (error: unknown) => void,\n];\n\ntype InflightRequest = {\n  [cacheId: string]: RequestHandler[];\n};\n\nconst log = createModuleLogger(projectLogger, 'inflight-cache');\n\n/**\n * Creates a middleware that caches inflight requests.\n * If a request is already in flight, the middleware will wait for the request to complete\n * and then return the result.\n *\n * @returns A middleware that caches inflight requests.\n */\nexport function createInflightCacheMiddleware(): JsonRpcMiddleware<\n  JsonRpcRequest,\n  Json,\n  MiddlewareContext<{ skipCache: boolean }>\n> {\n  const inflightRequests: InflightRequest = {};\n\n  return async ({ request, context, next }) => {\n    if (context.get('skipCache')) {\n      return next();\n    }\n\n    const cacheId: string | null = cacheIdentifierForRequest(request);\n    if (!cacheId) {\n      log('Request is not cacheable, proceeding. req = %o', request);\n      return next();\n    }\n\n    // check for matching requests\n    let activeRequestHandlers: RequestHandler[] = inflightRequests[cacheId];\n    // if found, wait for the active request to be handled\n    if (activeRequestHandlers) {\n      // setup the response listener and wait for it to be called\n      // it will handle copying the result and request fields\n      log(\n        'Running %i handler(s) for request %o',\n        activeRequestHandlers.length,\n        request,\n      );\n      return await createActiveRequestHandler(activeRequestHandlers);\n    }\n\n    // setup response handler array for subsequent requests\n    activeRequestHandlers = [];\n    inflightRequests[cacheId] = activeRequestHandlers;\n    // allow request to be handled normally\n    log('Carrying original request forward %o', request);\n    try {\n      const result = (await next()) as Readonly<Json>;\n      log(\n        'Running %i collected handler(s) for successful request %o',\n        activeRequestHandlers.length,\n        request,\n      );\n      runRequestHandlers({ result }, activeRequestHandlers);\n      return result;\n    } catch (error) {\n      log(\n        'Running %i collected handler(s) for failed request %o',\n        activeRequestHandlers.length,\n        request,\n      );\n      runRequestHandlers({ error }, activeRequestHandlers);\n      throw error;\n    } finally {\n      delete inflightRequests[cacheId];\n    }\n  };\n}\n\n/**\n * Creates a new request handler for the active request.\n *\n * @param activeRequestHandlers - The active request handlers.\n * @returns A promise that resolves to the result of the request.\n */\nfunction createActiveRequestHandler(\n  activeRequestHandlers: RequestHandler[],\n): Promise<Readonly<Json>> {\n  const { resolve, promise, reject } = createDeferredPromise<Readonly<Json>>();\n  activeRequestHandlers.push([\n    (result: Readonly<Json>): void => resolve(result),\n    (error: unknown): void => reject(error),\n  ]);\n  return promise;\n}\n\n/**\n * Runs the request handlers for the given result or error.\n *\n * @param resultOrError - The result or error of the request.\n * @param activeRequestHandlers - The active request handlers.\n */\nfunction runRequestHandlers(\n  resultOrError: { result: Readonly<Json> } | { error: unknown },\n  activeRequestHandlers: RequestHandler[],\n): void {\n  // use setTimeout so we can handle the original request first\n  setTimeout(() => {\n    activeRequestHandlers.forEach(([onSuccess, onError]) => {\n      if ('result' in resultOrError) {\n        onSuccess(resultOrError.result);\n      } else {\n        onError(resultOrError.error);\n      }\n    });\n  });\n}\n"]}