{"version":3,"file":"WorkerPoolManager.js","names":["options: WorkerPoolOptions","newRenderOptions: WorkerRenderingOptions","resolvedThemes: ThemeRegistrationResolved[]","taskPromises: Promise<void>[]","task: SetRenderOptionsWorkerTask","resolvedLanguages: ResolvedLanguage[]","initPromises: Promise<unknown>[]","managedWorker: ManagedWorker","task: InitializeWorkerTask","task: RenderFileTask | RenderDiffTask","worker: ManagedWorker | undefined"],"sources":["../../src/worker/WorkerPoolManager.ts"],"sourcesContent":["import LRUMapPkg from 'lru_map';\n\nimport { DEFAULT_THEMES } from '../constants';\nimport { getResolvedLanguages } from '../highlighter/languages/getResolvedLanguages';\nimport { hasResolvedLanguages } from '../highlighter/languages/hasResolvedLanguages';\nimport { resolveLanguages } from '../highlighter/languages/resolveLanguages';\nimport { getSharedHighlighter } from '../highlighter/shared_highlighter';\nimport { attachResolvedThemes } from '../highlighter/themes/attachResolvedThemes';\nimport { getResolvedThemes } from '../highlighter/themes/getResolvedThemes';\nimport { hasResolvedThemes } from '../highlighter/themes/hasResolvedThemes';\nimport { resolveThemes } from '../highlighter/themes/resolveThemes';\nimport type {\n  DiffsHighlighter,\n  FileContents,\n  FileDiffMetadata,\n  HighlighterTypes,\n  HunkExpansionRegion,\n  RenderDiffOptions,\n  RenderDiffResult,\n  RenderFileOptions,\n  RenderFileResult,\n  SupportedLanguages,\n  ThemedDiffResult,\n  ThemedFileResult,\n  ThemeRegistrationResolved,\n} from '../types';\nimport { areDiffRenderOptionsEqual } from '../utils/areDiffRenderOptionsEqual';\nimport { areFilesEqual } from '../utils/areFilesEqual';\nimport { areThemesEqual } from '../utils/areThemesEqual';\nimport {\n  getCustomExtensionsMap,\n  getCustomExtensionsVersion,\n  getFiletypeFromFileName,\n} from '../utils/getFiletypeFromFileName';\nimport { getThemes } from '../utils/getThemes';\nimport { isDiffPlainText } from '../utils/isDiffPlainText';\nimport { isFilePlainText } from '../utils/isFilePlainText';\nimport { renderDiffWithHighlighter } from '../utils/renderDiffWithHighlighter';\nimport { renderFileWithHighlighter } from '../utils/renderFileWithHighlighter';\nimport type {\n  AllWorkerTasks,\n  DiffRendererInstance,\n  FileRendererInstance,\n  InitializeWorkerRequest,\n  InitializeWorkerTask,\n  RenderDiffRequest,\n  RenderDiffTask,\n  RenderFileRequest,\n  RenderFileTask,\n  ResolvedLanguage,\n  SetRenderOptionsWorkerTask,\n  SubmitRequest,\n  WorkerInitializationRenderOptions,\n  WorkerPoolOptions,\n  WorkerRenderingOptions,\n  WorkerRequestId,\n  WorkerResponse,\n  WorkerStats,\n} from './types';\n\nconst IGNORE_RESPONSE = Symbol('IGNORE_RESPONSE');\n\nclass WorkerPoolTerminatedError extends Error {\n  constructor() {\n    super('WorkerPoolManager: operation canceled because the pool terminated');\n  }\n}\n\ninterface GetCachesResult {\n  fileCache: LRUMapPkg.LRUMap<string, RenderFileResult>;\n  diffCache: LRUMapPkg.LRUMap<string, RenderDiffResult>;\n}\n\ninterface ManagedWorker {\n  worker: Worker;\n  request_id: string | undefined;\n  initialized: boolean;\n  langs: Set<SupportedLanguages>;\n  customExtensionsVersion: number;\n}\n\ninterface ThemeSubscriber {\n  rerender(): void;\n}\n\nexport class WorkerPoolManager {\n  private highlighter: DiffsHighlighter | undefined;\n  private readonly preferredHighlighter: HighlighterTypes;\n  private renderOptions: WorkerRenderingOptions;\n  private initialized: Promise<void> | boolean = false;\n  private workers: ManagedWorker[] = [];\n  private taskQueue = new Map<\n    FileRendererInstance | DiffRendererInstance,\n    RenderDiffTask | RenderFileTask\n  >();\n  private pendingTasks = new Map<WorkerRequestId, AllWorkerTasks>();\n  private nextRequestId = 0;\n  private themeSubscribers = new Set<ThemeSubscriber>();\n  private workersFailed = false;\n  private instanceRequestMap = new Map<\n    FileRendererInstance | DiffRendererInstance,\n    string\n  >();\n  private statSubscribers = new Set<(stats: WorkerStats) => unknown>();\n  private fileCache: LRUMapPkg.LRUMap<string, RenderFileResult>;\n  private diffCache: LRUMapPkg.LRUMap<string, RenderDiffResult>;\n  private _queuedBroadcast: number | undefined;\n  // Incremented on terminate so async lifecycle work can identify stale results.\n  private lifecycleGeneration = 0;\n\n  constructor(\n    private options: WorkerPoolOptions,\n    {\n      langs,\n      theme = DEFAULT_THEMES,\n      useTokenTransformer = false,\n      lineDiffType = 'word-alt',\n      maxLineDiffLength = 1000,\n      tokenizeMaxLineLength = 1000,\n      preferredHighlighter = 'shiki-js',\n    }: WorkerInitializationRenderOptions\n  ) {\n    this.preferredHighlighter = preferredHighlighter;\n    this.renderOptions = {\n      theme,\n      useTokenTransformer,\n      lineDiffType,\n      maxLineDiffLength,\n      tokenizeMaxLineLength,\n    };\n    this.fileCache = new LRUMapPkg.LRUMap(options.totalASTLRUCacheSize ?? 100);\n    this.diffCache = new LRUMapPkg.LRUMap(options.totalASTLRUCacheSize ?? 100);\n    this.queueInitialization(langs);\n  }\n\n  isWorkingPool(): boolean {\n    return !this.workersFailed;\n  }\n\n  getFileResultCache(file: FileContents): RenderFileResult | undefined {\n    return file.cacheKey != null\n      ? this.fileCache.get(file.cacheKey)\n      : undefined;\n  }\n\n  getDiffResultCache(diff: FileDiffMetadata): RenderDiffResult | undefined {\n    return diff.cacheKey != null\n      ? this.diffCache.get(diff.cacheKey)\n      : undefined;\n  }\n\n  inspectCaches(): GetCachesResult {\n    const { fileCache, diffCache } = this;\n    return { fileCache, diffCache };\n  }\n\n  evictFileFromCache(cacheKey: string): boolean {\n    try {\n      return this.fileCache.delete(cacheKey) !== undefined;\n    } finally {\n      this.queueBroadcastStateChanges();\n    }\n  }\n\n  evictDiffFromCache(cacheKey: string): boolean {\n    try {\n      return this.diffCache.delete(cacheKey) !== undefined;\n    } finally {\n      this.queueBroadcastStateChanges();\n    }\n  }\n\n  async setRenderOptions({\n    theme = DEFAULT_THEMES,\n    useTokenTransformer = false,\n    lineDiffType = 'word-alt',\n    maxLineDiffLength = 1000,\n    tokenizeMaxLineLength = 1000,\n  }: Partial<WorkerRenderingOptions>): Promise<void> {\n    const { lifecycleGeneration } = this;\n    try {\n      const newRenderOptions: WorkerRenderingOptions = {\n        theme,\n        useTokenTransformer,\n        lineDiffType,\n        maxLineDiffLength,\n        tokenizeMaxLineLength,\n      };\n      if (!this.isInitialized()) {\n        await this.initialize();\n      }\n      if (\n        !this.isCurrentLifecycle(lifecycleGeneration) ||\n        areDiffRenderOptionsEqual(newRenderOptions, this.renderOptions)\n      ) {\n        return;\n      }\n\n      const themeNames = getThemes(theme);\n      let resolvedThemes: ThemeRegistrationResolved[] = [];\n      if (!areThemesEqual(newRenderOptions.theme, this.renderOptions.theme)) {\n        if (hasResolvedThemes(themeNames)) {\n          resolvedThemes = getResolvedThemes(themeNames);\n        } else {\n          resolvedThemes = await resolveThemes(themeNames);\n        }\n      }\n\n      if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n        return;\n      }\n\n      if (this.highlighter != null) {\n        attachResolvedThemes(resolvedThemes, this.highlighter);\n        await this.setRenderOptionsOnWorkers(newRenderOptions, resolvedThemes);\n      } else {\n        const [highlighter] = await Promise.all([\n          getSharedHighlighter({\n            themes: themeNames,\n            langs: ['text'],\n            preferredHighlighter: this.preferredHighlighter,\n          }),\n          this.setRenderOptionsOnWorkers(newRenderOptions, resolvedThemes),\n        ]);\n        if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n          return;\n        }\n        this.highlighter = highlighter;\n      }\n\n      if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n        return;\n      }\n\n      this.renderOptions = newRenderOptions;\n      this.diffCache.clear();\n      this.fileCache.clear();\n\n      for (const instance of this.themeSubscribers) {\n        instance.rerender();\n      }\n    } catch (error) {\n      if (\n        error instanceof WorkerPoolTerminatedError ||\n        !this.isCurrentLifecycle(lifecycleGeneration)\n      ) {\n        return;\n      }\n      throw error;\n    }\n  }\n\n  getFileRenderOptions(): RenderFileOptions {\n    const { tokenizeMaxLineLength, theme, useTokenTransformer } =\n      this.renderOptions;\n    return { theme, useTokenTransformer, tokenizeMaxLineLength };\n  }\n\n  getDiffRenderOptions(): RenderDiffOptions {\n    return { ...this.renderOptions };\n  }\n\n  private async setRenderOptionsOnWorkers(\n    renderOptions: WorkerRenderingOptions,\n    resolvedThemes: ThemeRegistrationResolved[]\n  ): Promise<void> {\n    if (this.workersFailed) {\n      return;\n    }\n    if (!this.isInitialized()) {\n      await this.initialize();\n    }\n    const taskPromises: Promise<void>[] = [];\n    for (const managedWorker of this.workers) {\n      if (!managedWorker.initialized) {\n        console.log({ managedWorker });\n        throw new Error(\n          'setRenderOptionsOnWorkers: Somehow we have an uninitialized worker'\n        );\n      }\n      taskPromises.push(\n        new Promise<void>((resolve, reject) => {\n          const id = this.generateRequestId();\n          const task: SetRenderOptionsWorkerTask = {\n            type: 'set-render-options',\n            id,\n            request: {\n              type: 'set-render-options',\n              id,\n              renderOptions,\n              resolvedThemes,\n            },\n            resolve,\n            reject,\n            requestStart: Date.now(),\n          };\n          // NOTE(amadeus): We intentionally ignore the normal pending requests\n          // infra because these tasks should technically interrupt the normal\n          // flow and should be processed by the worker when ready immediately\n          this.pendingTasks.set(id, task);\n          managedWorker.worker.postMessage(task.request);\n        })\n      );\n    }\n    await Promise.all(taskPromises);\n  }\n\n  subscribeToThemeChanges(instance: ThemeSubscriber): () => void {\n    this.themeSubscribers.add(instance);\n    this.queueBroadcastStateChanges();\n    return () => {\n      this.unsubscribeToThemeChanges(instance);\n      this.queueBroadcastStateChanges();\n    };\n  }\n\n  unsubscribeToThemeChanges(instance: ThemeSubscriber): void {\n    this.themeSubscribers.delete(instance);\n    this.queueBroadcastStateChanges();\n  }\n\n  subscribeToStatChanges(\n    callback: (stats: WorkerStats) => unknown\n  ): () => void {\n    this.statSubscribers.add(callback);\n    callback(this.getStats());\n    return () => {\n      this.statSubscribers.delete(callback);\n    };\n  }\n\n  private queueBroadcastStateChanges() {\n    if (this._queuedBroadcast != null) return;\n    this._queuedBroadcast = requestAnimationFrame(this._broadcastStateChanges);\n  }\n\n  private _broadcastStateChanges = () => {\n    if (this._queuedBroadcast != null) {\n      cancelAnimationFrame(this._queuedBroadcast);\n      this._queuedBroadcast = undefined;\n    }\n    const stats = this.getStats();\n    for (const callback of this.statSubscribers) {\n      callback(stats);\n    }\n  };\n\n  cleanUpPendingTasks(\n    instance: FileRendererInstance | DiffRendererInstance\n  ): void {\n    this.taskQueue.delete(instance);\n    const requestId = this.instanceRequestMap.get(instance);\n    if (requestId != null) {\n      this.pendingTasks.delete(requestId);\n      this.instanceRequestMap.delete(instance);\n    }\n    this.queueBroadcastStateChanges();\n  }\n\n  isInitialized(): boolean {\n    return this.initialized === true;\n  }\n\n  async initialize(languages: SupportedLanguages[] = []): Promise<void> {\n    if (this.initialized === true) {\n      return;\n    } else if (this.initialized === false) {\n      const { lifecycleGeneration } = this;\n      this.initialized = new Promise((resolve, reject) => {\n        void (async () => {\n          try {\n            const themes = getThemes(this.renderOptions.theme);\n            let resolvedThemes: ThemeRegistrationResolved[] = [];\n            if (hasResolvedThemes(themes)) {\n              resolvedThemes = getResolvedThemes(themes);\n            } else {\n              resolvedThemes = await resolveThemes(themes);\n            }\n            if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n              resolve();\n              return;\n            }\n\n            let resolvedLanguages: ResolvedLanguage[] = [];\n            if (hasResolvedLanguages(languages)) {\n              resolvedLanguages = getResolvedLanguages(languages);\n            } else {\n              resolvedLanguages = await resolveLanguages(languages);\n            }\n            if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n              resolve();\n              return;\n            }\n\n            const [highlighter] = await Promise.all([\n              getSharedHighlighter({\n                themes,\n                langs: ['text', ...languages],\n                preferredHighlighter: this.preferredHighlighter,\n              }),\n              this.initializeWorkers(resolvedThemes, resolvedLanguages),\n            ]);\n\n            if (!this.isCurrentLifecycle(lifecycleGeneration)) {\n              this.terminateWorkers();\n              resolve();\n              return;\n            }\n            this.highlighter = highlighter;\n            this.initialized = true;\n            this.diffCache.clear();\n            this.fileCache.clear();\n            this.drainQueue();\n            this.queueBroadcastStateChanges();\n            resolve();\n          } catch (e) {\n            if (\n              e instanceof WorkerPoolTerminatedError ||\n              !this.isCurrentLifecycle(lifecycleGeneration)\n            ) {\n              resolve();\n              return;\n            }\n            this.initialized = false;\n            this.workersFailed = true;\n            this.queueBroadcastStateChanges();\n            reject(e);\n          }\n        })();\n      });\n      this.queueBroadcastStateChanges();\n    } else {\n      return this.initialized;\n    }\n  }\n\n  private async initializeWorkers(\n    resolvedThemes: ThemeRegistrationResolved[],\n    resolvedLanguages: ResolvedLanguage[]\n  ): Promise<void> {\n    this.workersFailed = false;\n    const initPromises: Promise<unknown>[] = [];\n    const customExtensionVersion = getCustomExtensionsVersion();\n    const customExtensionMap =\n      customExtensionVersion > 0 ? getCustomExtensionsMap() : undefined;\n    if (this.workers.length > 0) {\n      this.terminateWorkers();\n    }\n    for (let i = 0; i < (this.options.poolSize ?? 8); i++) {\n      const worker = this.options.workerFactory();\n      const managedWorker: ManagedWorker = {\n        worker,\n        request_id: undefined,\n        initialized: false,\n        langs: new Set(['text', ...resolvedLanguages.map(({ name }) => name)]),\n        customExtensionsVersion: 0,\n      };\n      worker.addEventListener(\n        'message',\n        (event: MessageEvent<WorkerResponse>) => {\n          this.handleWorkerMessage(managedWorker, event.data);\n        }\n      );\n      worker.addEventListener('error', (error) =>\n        console.error('Worker error:', error, managedWorker)\n      );\n      this.workers.push(managedWorker);\n      initPromises.push(\n        new Promise<void>((resolve, reject) => {\n          const id = this.generateRequestId();\n          const task: InitializeWorkerTask = {\n            type: 'initialize',\n            id,\n            request: {\n              type: 'initialize',\n              id,\n              renderOptions: this.renderOptions,\n              preferredHighlighter: this.preferredHighlighter,\n              resolvedThemes,\n              resolvedLanguages,\n              customExtensionsVersion:\n                customExtensionMap != null ? customExtensionVersion : undefined,\n              customExtensionMap,\n            },\n            resolve() {\n              managedWorker.initialized = true;\n              resolve();\n            },\n            reject,\n            requestStart: Date.now(),\n          };\n          this.pendingTasks.set(id, task);\n          this.executeTask(managedWorker, task);\n        })\n      );\n    }\n    await Promise.all(initPromises);\n  }\n\n  private drainQueue = () => {\n    this._queuedDrain = undefined;\n    // If we are initializing or things got cancelled while initializing, we\n    // should not attempt to drain the queue\n    if (this.initialized !== true || this.taskQueue.size === 0) {\n      return;\n    }\n    for (const [instance, task] of this.taskQueue) {\n      // If we have a request in progress for the same instance, we should wait\n      // for it to finish\n      if (this.instanceRequestMap.has(instance)) {\n        continue;\n      }\n      const langs = getLangsFromTask(task);\n      const availableWorker = this.getAvailableWorker(langs);\n      if (availableWorker == null) {\n        break;\n      }\n      this.assignWorkerToTask(task, availableWorker);\n      void this.resolveLanguagesAndExecuteTask(availableWorker, task, langs);\n    }\n    this.queueBroadcastStateChanges();\n  };\n\n  highlightFileAST(instance: FileRendererInstance, file: FileContents): void {\n    if (isFilePlainText(file)) {\n      return;\n    }\n    // If we already have a task in progress for this same file content, we\n    // should drop it\n    for (const tasks of [this.taskQueue, this.pendingTasks.values()]) {\n      for (const task of tasks) {\n        if (\n          'instance' in task &&\n          task.instance === instance &&\n          task.request.type === 'file' &&\n          areFilesEqual(file, task.request.file)\n        ) {\n          return;\n        }\n      }\n    }\n    this.submitTask(instance, { type: 'file', file });\n  }\n\n  getPlainFileAST(\n    file: FileContents,\n    startingLine: number,\n    totalLines: number,\n    lines?: string[]\n  ): ThemedFileResult | undefined {\n    if (this.highlighter == null) {\n      this.queueInitialization();\n      return undefined;\n    }\n    return renderFileWithHighlighter(\n      file,\n      this.highlighter,\n      this.renderOptions,\n      { forcePlainText: true, startingLine, totalLines, lines }\n    );\n  }\n\n  highlightDiffAST(\n    instance: DiffRendererInstance,\n    diff: FileDiffMetadata\n  ): void {\n    if (isDiffPlainText(diff)) {\n      return;\n    }\n    // If we already have a task in progress for this same diff content, we\n    // should ignore executing it again\n    for (const tasks of [this.taskQueue, this.pendingTasks.values()]) {\n      for (const task of tasks) {\n        if (\n          'instance' in task &&\n          task.instance === instance &&\n          task.request.type === 'diff' &&\n          task.request.diff === diff\n        ) {\n          return;\n        }\n      }\n    }\n    this.submitTask(instance, { type: 'diff', diff });\n  }\n\n  getPlainDiffAST(\n    diff: FileDiffMetadata,\n    startingLine: number,\n    totalLines: number,\n    expandedHunks?: Map<number, HunkExpansionRegion> | true,\n    collapsedContextThreshold?: number\n  ): ThemedDiffResult | undefined {\n    return this.highlighter != null\n      ? renderDiffWithHighlighter(diff, this.highlighter, this.renderOptions, {\n          forcePlainText: true,\n          startingLine,\n          totalLines,\n          expandedHunks,\n          collapsedContextThreshold,\n        })\n      : undefined;\n  }\n\n  terminate(): void {\n    this.lifecycleGeneration++;\n    this.cancelPendingAsyncWorkerTasks();\n    this.terminateWorkers();\n    this.fileCache.clear();\n    this.diffCache.clear();\n    this.instanceRequestMap.clear();\n    this.taskQueue.clear();\n    this.pendingTasks.clear();\n    this.highlighter = undefined;\n    this.initialized = false;\n    this.workersFailed = false;\n    this.queueBroadcastStateChanges();\n  }\n\n  private isCurrentLifecycle(lifecycleGeneration: number): boolean {\n    return this.lifecycleGeneration === lifecycleGeneration;\n  }\n\n  private queueInitialization(languages?: SupportedLanguages[]): void {\n    void this.initialize(languages).catch((error) => {\n      console.error(error);\n    });\n  }\n\n  private cancelPendingAsyncWorkerTasks(): void {\n    const error = new WorkerPoolTerminatedError();\n    for (const task of this.pendingTasks.values()) {\n      if ('reject' in task) {\n        task.reject(error);\n      }\n    }\n  }\n\n  private terminateWorkers() {\n    for (const managedWorker of this.workers) {\n      managedWorker.worker.terminate();\n    }\n    this.workers.length = 0;\n  }\n\n  getStats(): WorkerStats {\n    return {\n      managerState: (() => {\n        if (this.initialized === false) {\n          return 'waiting';\n        }\n        if (this.initialized !== true) {\n          return 'initializing';\n        }\n        return 'initialized';\n      })(),\n      totalWorkers: this.workers.length,\n      workersFailed: this.workersFailed,\n      busyWorkers: this.workers.filter((w) => w.request_id != null).length,\n      queuedTasks: this.taskQueue.size,\n      pendingTasks: this.pendingTasks.size,\n      themeSubscribers: this.themeSubscribers.size,\n      fileCacheSize: this.fileCache.size,\n      diffCacheSize: this.diffCache.size,\n    };\n  }\n\n  private submitTask(\n    instance: FileRendererInstance,\n    request: Omit<RenderFileRequest, 'id'>\n  ): void;\n  private submitTask(\n    instance: DiffRendererInstance,\n    request: Omit<RenderDiffRequest, 'id'>\n  ): void;\n  private submitTask(\n    instance: FileRendererInstance | DiffRendererInstance,\n    request: SubmitRequest\n  ): void {\n    if (this.initialized === false) {\n      this.queueInitialization();\n    }\n\n    const id = this.generateRequestId();\n    const requestStart = Date.now();\n    const task: RenderFileTask | RenderDiffTask = (() => {\n      switch (request.type) {\n        case 'file':\n          return {\n            type: 'file',\n            id,\n            request: { ...request, id },\n            instance: instance as FileRendererInstance,\n            requestStart,\n          };\n        case 'diff':\n          return {\n            type: 'diff',\n            id,\n            request: { ...request, id },\n            instance: instance as DiffRendererInstance,\n            requestStart,\n          };\n      }\n    })();\n\n    this.taskQueue.set(instance, task);\n    this.queueDrain();\n  }\n\n  private async resolveLanguagesAndExecuteTask(\n    availableWorker: ManagedWorker,\n    task: RenderFileTask | RenderDiffTask,\n    langs: SupportedLanguages[]\n  ): Promise<void> {\n    try {\n      // Add resolved languages if required\n      const workerMissingLangs = langs.filter(\n        (lang) => !availableWorker.langs.has(lang)\n      );\n\n      if (workerMissingLangs.length > 0) {\n        if (hasResolvedLanguages(workerMissingLangs)) {\n          task.request.resolvedLanguages =\n            getResolvedLanguages(workerMissingLangs);\n        } else {\n          task.request.resolvedLanguages =\n            await resolveLanguages(workerMissingLangs);\n        }\n      }\n      this.executeTask(availableWorker, task);\n    } catch {\n      this.cleanWorkerAndTask(availableWorker, task);\n    }\n  }\n\n  private handleWorkerMessage(\n    managedWorker: ManagedWorker,\n    response: WorkerResponse\n  ): void {\n    const task = this.pendingTasks.get(response.id);\n    try {\n      if (task == null) {\n        // If we can't find a task for this response, it probably means the\n        // component has been unmounted, so we should silently ignore it\n        throw IGNORE_RESPONSE;\n      } else if (response.type === 'error') {\n        const error = new Error(response.error);\n        if (response.stack) {\n          error.stack = response.stack;\n        }\n        if ('reject' in task) {\n          task.reject(error);\n        } else {\n          task.instance.onHighlightError(error);\n        }\n        throw error;\n      } else {\n        // If we've gotten a newer request from the same instance, we should\n        // ignore this response either because it's out of order or because we\n        // have a newer more important request\n        if (\n          'instance' in task &&\n          this.instanceRequestMap.get(task.instance) !== response.id\n        ) {\n          throw IGNORE_RESPONSE;\n        }\n        switch (response.requestType) {\n          case 'initialize':\n            if (task.type !== 'initialize') {\n              throw new Error('handleWorkerMessage: task/response dont match');\n            }\n            this.syncCustomExtensionVersion(managedWorker, task.request);\n            task.resolve();\n            break;\n          case 'set-render-options':\n            if (task.type !== 'set-render-options') {\n              throw new Error('handleWorkerMessage: task/response dont match');\n            }\n            task.resolve();\n            break;\n          case 'file': {\n            if (task.type !== 'file') {\n              throw new Error('handleWorkerMessage: task/response dont match');\n            }\n            const { result, options } = response;\n            const { instance, request } = task;\n            this.syncCustomExtensionVersion(managedWorker, request);\n            if (request.file.cacheKey != null) {\n              this.fileCache.set(request.file.cacheKey, { result, options });\n            }\n            instance.onHighlightSuccess(request.file, result, options);\n            break;\n          }\n          case 'diff': {\n            if (task.type !== 'diff') {\n              throw new Error('handleWorkerMessage: task/response dont match');\n            }\n            const { result, options } = response;\n            const { instance, request } = task;\n            this.syncCustomExtensionVersion(managedWorker, request);\n            if (request.diff.cacheKey != null) {\n              this.diffCache.set(request.diff.cacheKey, { result, options });\n            }\n            instance.onHighlightSuccess(request.diff, result, options);\n            break;\n          }\n        }\n      }\n    } catch (error) {\n      if (error !== IGNORE_RESPONSE) {\n        console.error(error, task, response);\n      }\n    }\n\n    this.cleanWorkerAndTask(managedWorker, task);\n    this.queueBroadcastStateChanges();\n    if (this.taskQueue.size > 0) {\n      // We queue drain so that potentially multiple workers can free up\n      // allowing for better language matches if possible\n      this.queueDrain();\n    }\n  }\n\n  private _queuedDrain: Promise<void> | undefined;\n  private queueDrain() {\n    if (this._queuedDrain != null) return;\n    this._queuedDrain = Promise.resolve().then(this.drainQueue);\n    this.queueBroadcastStateChanges();\n  }\n\n  private assignWorkerToTask(\n    task: AllWorkerTasks,\n    managedWorker: ManagedWorker\n  ) {\n    managedWorker.request_id = task.id;\n    if ('instance' in task) {\n      this.taskQueue.delete(task.instance);\n      this.instanceRequestMap.set(task.instance, task.id);\n    }\n    this.pendingTasks.set(task.id, task);\n  }\n\n  private cleanWorkerAndTask(\n    managedWorker: ManagedWorker,\n    task?: AllWorkerTasks\n  ) {\n    managedWorker.request_id = undefined;\n    if (task != null) {\n      if ('instance' in task) {\n        this.instanceRequestMap.delete(task.instance);\n      }\n      this.pendingTasks.delete(task.id);\n    }\n  }\n\n  private executeTask(\n    managedWorker: ManagedWorker,\n    task: AllWorkerTasks\n  ): void {\n    if (shouldSyncCustomExtensions(task.request)) {\n      this.maybeAttachCustomExtensions(managedWorker, task.request);\n    }\n    this.assignWorkerToTask(task, managedWorker);\n    for (const lang of getLangsFromTask(task)) {\n      managedWorker.langs.add(lang);\n    }\n    try {\n      managedWorker.worker.postMessage(task.request);\n    } catch (error) {\n      this.cleanWorkerAndTask(managedWorker, task);\n      console.error('Failed to post message to worker:', error);\n      if ('instance' in task) {\n        task.instance.onHighlightError(error);\n      } else if ('reject' in task) {\n        task.reject(error as Error);\n      }\n    }\n    this.queueBroadcastStateChanges();\n  }\n\n  private maybeAttachCustomExtensions(\n    managedWorker: ManagedWorker,\n    request: InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest\n  ): void {\n    if (request.customExtensionsVersion != null) {\n      return;\n    }\n    const version = getCustomExtensionsVersion();\n    if (managedWorker.customExtensionsVersion >= version) {\n      return;\n    }\n    request.customExtensionsVersion = version;\n    request.customExtensionMap = getCustomExtensionsMap();\n  }\n\n  private syncCustomExtensionVersion(\n    managedWorker: ManagedWorker,\n    request: InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest\n  ): void {\n    if (request.customExtensionsVersion == null) {\n      return;\n    }\n    managedWorker.customExtensionsVersion = request.customExtensionsVersion;\n  }\n\n  private getAvailableWorker(\n    langs: SupportedLanguages[]\n  ): ManagedWorker | undefined {\n    let worker: ManagedWorker | undefined;\n    for (const managedWorker of this.workers) {\n      if (managedWorker.request_id != null || !managedWorker.initialized) {\n        continue;\n      }\n      worker = managedWorker;\n      if (langs.length === 0) {\n        break;\n      }\n      let hasEveryLang = true;\n      for (const lang of langs) {\n        if (!managedWorker.langs.has(lang)) {\n          hasEveryLang = false;\n          break;\n        }\n      }\n      if (hasEveryLang) {\n        break;\n      }\n    }\n    return worker;\n  }\n\n  private generateRequestId(): WorkerRequestId {\n    return `req_${++this.nextRequestId}`;\n  }\n}\n\nfunction shouldSyncCustomExtensions(\n  request: AllWorkerTasks['request']\n): request is InitializeWorkerRequest | RenderFileRequest | RenderDiffRequest {\n  return (\n    request.type === 'initialize' ||\n    request.type === 'file' ||\n    request.type === 'diff'\n  );\n}\n\nfunction getLangsFromTask(task: AllWorkerTasks): SupportedLanguages[] {\n  const langs = new Set<SupportedLanguages>();\n  if (task.type === 'initialize' || task.type === 'set-render-options') {\n    return [];\n  }\n  switch (task.type) {\n    case 'file': {\n      langs.add(\n        task.request.file.lang ??\n          getFiletypeFromFileName(task.request.file.name)\n      );\n      break;\n    }\n    case 'diff': {\n      langs.add(\n        task.request.diff.lang ??\n          getFiletypeFromFileName(task.request.diff.name)\n      );\n      langs.add(\n        task.request.diff.lang ??\n          getFiletypeFromFileName(task.request.diff.prevName ?? '-')\n      );\n      break;\n    }\n  }\n  langs.delete('text');\n  return Array.from(langs);\n}\n"],"mappings":"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"}