{"version":3,"file":"index.cjs","names":[],"sources":["../lib/reducer/helpers.ts","../lib/reducer/abortController.ts","../lib/reducer/queryQueue.ts","../lib/reducer/request.ts","../lib/reducer/storage.ts","../lib/reducer/index.ts","../lib/hooks/createCdeebeeHooks.ts","../lib/hooks/selectors.ts"],"sourcesContent":["import { type WritableDraft } from '@reduxjs/toolkit';\nimport { type CdeebeeSettings, type CdeebeeModule, CdeebeeValueList } from './types';\n\nexport function checkModule<T = unknown>(settings: CdeebeeSettings<T> | WritableDraft<CdeebeeSettings<T>>, module: CdeebeeModule, result: () => void) {\n  if (settings.modules.includes(module)) {\n    result();\n  }\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n  return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nexport function mergeDeepRight<T>(\n  left: T,\n  right: Partial<T> | Record<string, unknown>\n): T {\n  if (!isRecord(left) || !isRecord(right)) {\n    return right as T;\n  }\n\n  const result = { ...left } as Record<string, unknown>;\n  const rightRecord = right as Record<string, unknown>;\n\n  for (const key in rightRecord) {\n    if (Object.prototype.hasOwnProperty.call(rightRecord, key)) {\n      const leftValue = result[key];\n      const rightValue = rightRecord[key];\n\n      if (\n        isRecord(leftValue) &&\n        isRecord(rightValue) &&\n        !Array.isArray(leftValue) &&\n        !Array.isArray(rightValue)\n      ) {\n        result[key] = mergeDeepRight(leftValue, rightValue);\n      } else {\n        result[key] = rightValue;\n      }\n    }\n  }\n\n  return result as T;\n}\n\nexport function omit<T extends Record<string, unknown>>(keys: string[], obj: T): Omit<T, keyof T> {\n  const result = { ...obj };\n  for (const key of keys) {\n    delete result[key];\n  }\n  return result as Omit<T, keyof T>;\n}\n\n/**\n * Extract primary key values from API response data per list.\n * Handles responses with format: { listName: { data: [...], primaryKey: 'id' } }\n * Returns a map of listName -> array of IDs.\n */\nexport function extractLastResultIdList(response: unknown): Record<string, string[]> {\n  if (!isRecord(response)) {\n    return {};\n  }\n\n  const result: Record<string, string[]> = {};\n\n  for (const listName of Object.keys(response)) {\n    const value = response[listName];\n\n    if (\n      isRecord(value) &&\n      Array.isArray(value.data) &&\n      typeof value.primaryKey === 'string'\n    ) {\n      const primaryKey = value.primaryKey;\n      const idList: string[] = [];\n\n      for (const item of value.data) {\n        if (isRecord(item) && primaryKey in item) {\n          idList.push(String(item[primaryKey]));\n        }\n      }\n\n      result[listName] = idList;\n    }\n  }\n\n  return result;\n}\n\nexport function batchingUpdate<T extends Record<string, unknown>>(\n  state: T,\n  valueList: CdeebeeValueList<T>\n): void {\n  for (let i = 0; i < valueList.length; i++) {\n    const item = valueList[i] as { key: readonly (string | number)[]; value: unknown };\n    const path = item.key;\n    const value = item.value;\n\n    if (path.length === 0) {\n      continue;\n    }\n\n    let current: Record<string, unknown> | unknown[] = state as Record<string, unknown>;\n\n    for (let j = 0; j < path.length - 1; j++) {\n      const pathKey = path[j];\n\n      if (Array.isArray(current)) {\n        const index = typeof pathKey === 'number' ? pathKey : Number(pathKey);\n        if (!(index in current) || !isRecord(current[index])) {\n          current[index] = {};\n        }\n        current = current[index] as Record<string, unknown>;\n      } else {\n        const key = String(pathKey);\n        if (!(key in current)) {\n          const nextIsNumeric = typeof path[j + 1] === 'number' || (!isNaN(Number(path[j + 1])) && String(Number(path[j + 1])) === String(path[j + 1]));\n          current[key] = nextIsNumeric ? [] : {};\n        }\n        const next = current[key];\n        current = (Array.isArray(next) ? next : (isRecord(next) ? next : {})) as Record<string, unknown> | unknown[];\n      }\n    }\n\n    const lastKey = path[path.length - 1];\n    if (Array.isArray(current)) {\n      const index = typeof lastKey === 'number' ? lastKey : Number(lastKey);\n      current[index] = value;\n    } else {\n      current[String(lastKey)] = value;\n    }\n  }\n}\n","interface RequestController {\n  requestId: string;\n  controller: AbortController;\n  api: string;\n}\n\nclass AbortControllerStore {\n  private byRequestId = new Map<string, RequestController>();\n  private byApi = new Map<string, Set<string>>();\n\n  add(api: string, requestId: string, controller: AbortController): void {\n    const item: RequestController = { requestId, controller, api };\n    this.byRequestId.set(requestId, item);\n\n    if (!this.byApi.has(api)) {\n      this.byApi.set(api, new Set());\n    }\n    this.byApi.get(api)!.add(requestId);\n  }\n\n  delete(requestId: string): void {\n    const item = this.byRequestId.get(requestId);\n    if (!item) return;\n\n    this.byRequestId.delete(requestId);\n    const apiSet = this.byApi.get(item.api);\n    if (apiSet) {\n      apiSet.delete(requestId);\n      if (apiSet.size === 0) {\n        this.byApi.delete(item.api);\n      }\n    }\n  }\n\n  abortAllForApi(api: string, excludeRequestId: string): void {\n    const requestIDList = this.byApi.get(api);\n    if (!requestIDList) return;\n\n    const toAbort = Array.from(requestIDList).filter(id => id !== excludeRequestId);\n\n    for (const requestId of toAbort) {\n      const item = this.byRequestId.get(requestId);\n      if (item) {\n        item.controller.abort();\n        this.delete(requestId);\n      }\n    }\n  }\n}\n\nconst abortStore = new AbortControllerStore();\n\nexport function abortQuery(api: string, currentRequestId: string): void {\n  abortStore.abortAllForApi(api, currentRequestId);\n}\n\nexport function abortManager(signal: AbortSignal, api: string, requestId: string) {\n  const controller = new AbortController();\n\n  const cleanup = () => {\n    abortStore.delete(requestId);\n  };\n\n  signal.addEventListener('abort', () => {\n    controller.abort();\n    cleanup();\n  });\n\n  return {\n    controller,\n    init: () => abortStore.add(api, requestId, controller),\n    drop: cleanup,\n  };\n}\n","class QueryQueue {\n  private currentPromise: Promise<unknown> = Promise.resolve();\n  private queueLength = 0;\n\n  async enqueue<T>(task: () => Promise<T>): Promise<T> {\n    this.queueLength++;\n\n    const previousPromise = this.currentPromise;\n\n    this.currentPromise = previousPromise\n      .then(() => task(), () => task())\n      .finally(() => {\n        this.queueLength--;\n      });\n\n    return this.currentPromise as Promise<T>;\n  }\n\n  getQueueLength(): number {\n    return this.queueLength;\n  }\n\n  clear(): void {\n    this.queueLength = 0;\n    this.currentPromise = Promise.resolve();\n  }\n}\n\nexport const queryQueue = new QueryQueue();\n\n","import { createAsyncThunk } from '@reduxjs/toolkit';\nimport { checkModule } from './helpers';\nimport { abortManager } from './abortController';\nimport { queryQueue } from './queryQueue';\nimport { type CdeebeeState, type CdeebeeRequestOptions } from './types';\n\nexport const request = createAsyncThunk(\n  'cdeebee/request',\n  async (options: CdeebeeRequestOptions<unknown>, { rejectWithValue, getState, requestId, signal }) => {\n    const startedAt = new Date().toUTCString();\n    const { cdeebee: { settings } } = getState() as { cdeebee: CdeebeeState<unknown> };\n\n    const abort = abortManager(signal, options.api, requestId);\n    const withCallback = options.onResult && typeof options.onResult === 'function';\n\n    checkModule(settings, 'cancelation', abort.init);\n\n    const executeRequest = async () => {\n      try {\n        const { method = 'POST', body, headers = {} } = options;\n        const baseHeaders = typeof settings.mergeWithHeaders === 'function'\n          ? settings.mergeWithHeaders()\n          : (settings.mergeWithHeaders ?? {});\n\n        const extraHeaders: Record<string, string> = { ...baseHeaders, ...headers };\n\n        const baseData = typeof settings.mergeWithData === 'function'\n          ? settings.mergeWithData()\n          : (settings.mergeWithData ?? {});\n\n        const b = { ...baseData, ...(body ?? {}) };\n        let requestData: FormData | string = JSON.stringify(b);\n        const isFormData = !!options.files;\n\n        // handling files\n        if (options.files) {\n          const formData = new FormData();\n          const fileKey = options.fileKey || settings.fileKey;\n          const bodyKey = options.bodyKey || settings.bodyKey;\n\n          for (let i = 0; i < options.files.length; i += 1) {\n            if (fileKey) {\n              formData.append(fileKey, options.files[i]);\n            }\n          }\n\n          if (bodyKey) {\n            formData.append(bodyKey, requestData);\n          }\n          requestData = formData;\n        }\n        // [end] handling files\n\n        const fetchHeaders: Record<string, string> = {\n          'ui-request-id': requestId,\n          ...(isFormData ? {} : { 'Content-Type': 'application/json' }),\n          ...extraHeaders,\n        };\n\n        const isGet = method === 'GET';\n\n        const response = await fetch(options.api, {\n          method,\n          headers: fetchHeaders,\n          signal: abort.controller.signal,\n          ...(isGet ? {} : { body: requestData }),\n        });\n\n        checkModule(settings, 'cancelation', abort.drop);\n\n        let result: unknown;\n        const responseType = options.responseType || 'json';\n\n        if (responseType === 'text') {\n          result = await response.text();\n        } else if (responseType === 'blob') {\n          result = await response.blob();\n        } else {\n          // default: json\n          result = await response.json();\n        }\n\n        if (!response.ok) {\n          if (withCallback) options.onResult!(result);\n          return rejectWithValue({ status: response.status, statusText: response.statusText, data: result });\n        }\n\n        if (withCallback) options.onResult!(result);\n        return { result, startedAt, endedAt: new Date().toUTCString() };\n      } catch (error) {\n        checkModule(settings, 'cancelation', abort.drop);\n\n        if (withCallback) options.onResult!(error);\n\n        if (error instanceof Error && error.name === 'AbortError') {\n          return rejectWithValue({ message: 'Request was cancelled', cancelled: true });\n        }\n\n        return rejectWithValue({ message: error instanceof Error ? error.message : 'Unknown error occurred' });\n      }\n    };\n\n    if (settings.modules.includes('queryQueue')) {\n      return queryQueue.enqueue(executeRequest);\n    }\n\n    return executeRequest();\n  },\n);\n\n","import { type CdeebeeListStrategy, type CdeebeeState } from './types';\nimport { isRecord, mergeDeepRight, omit } from './helpers';\n\ntype ResponseValue = Record<string, unknown>;\n\ntype IResponse = Record<string, ResponseValue>;\n\ntype StorageData = Record<string, unknown>;\n\nfunction isDataWithPrimaryKey(value: unknown): value is { data: unknown[]; primaryKey: string } {\n  return (\n    isRecord(value) &&\n    Array.isArray(value.data) &&\n    typeof value.primaryKey === 'string'\n  );\n}\nfunction normalizeDataWithPrimaryKey(data: unknown[], primaryKey: string): Record<string, unknown> {\n  const result: Record<string, unknown> = {};\n\n  for (const item of data) {\n    if (isRecord(item) && primaryKey in item) {\n      const key = String(item[primaryKey]);\n      result[key] = item;\n    }\n  }\n\n  return result;\n}\n\nfunction applyStrategy(\n  existingValue: StorageData,\n  newValue: StorageData | ResponseValue,\n  strategy: string,\n  key: string\n): ResponseValue {\n  if (strategy === 'replace') {\n    return newValue as ResponseValue;\n  } else if (strategy === 'merge') {\n    return mergeDeepRight(existingValue, newValue as StorageData) as ResponseValue;\n  } else if (strategy === 'skip') {\n    return existingValue as ResponseValue;\n  } else {\n    console.warn(`Cdeebee: Unknown strategy \"${strategy}\" for key \"${key}\". Skipping normalization.`);\n    return mergeDeepRight(existingValue, newValue as StorageData) as ResponseValue;\n  }\n}\n\nexport function defaultNormalize<T>(\n  cdeebee: CdeebeeState<T>,\n  response: IResponse,\n  strategyList: CdeebeeListStrategy<T>\n): Record<string, ResponseValue> {\n  const keyList = Object.keys(response);\n  const currentStorage = isRecord(cdeebee.storage) ? (cdeebee.storage as Record<string, unknown>) : {};\n\n  const result = { ...currentStorage } as Record<string, ResponseValue>;\n  const keyListToOmit = new Set<string>();\n\n  for (const key of keyList) {\n    const responseValue = response[key];\n\n    if (responseValue === null || responseValue === undefined || typeof responseValue === 'string') {\n      keyListToOmit.add(key);\n      continue;\n    }\n\n    const strategy = strategyList[key as keyof T] ?? 'merge';\n\n    // For 'skip' strategy, if key doesn't exist in storage, skip it entirely\n    if (strategy === 'skip' && !(key in currentStorage)) {\n      continue;\n    }\n\n    const existingValue = key in currentStorage ? (currentStorage[key] as StorageData) : {};\n\n    if (isDataWithPrimaryKey(responseValue)) {\n      const normalizedValue = normalizeDataWithPrimaryKey(responseValue.data, responseValue.primaryKey);\n      result[key] = applyStrategy(existingValue, normalizedValue, strategy, key);\n      continue;\n    }\n\n    if (isRecord(responseValue)) {\n      result[key] = applyStrategy(existingValue, responseValue as StorageData, strategy, key);\n    } else {\n      result[key] = responseValue;\n    }\n  }\n\n  return keyListToOmit.size > 0 ? omit(Array.from(keyListToOmit), result) : result;\n}\n","import { createSlice, current, type PayloadAction } from '@reduxjs/toolkit';\n\nimport { type CdeebeeSettings, type CdeebeeState, type CdeebeeValueList, type CdeebeeListStrategy } from './types';\nimport { checkModule, mergeDeepRight, batchingUpdate, extractLastResultIdList } from './helpers';\nimport { abortQuery } from './abortController';\nimport { request } from './request';\nimport { defaultNormalize } from './storage';\n\nconst initialState: CdeebeeState<unknown> = {\n  settings: {\n    modules: ['history', 'listener', 'storage', 'cancelation'],\n    fileKey: 'file',\n    bodyKey: 'value',\n    listStrategy: {},\n    mergeWithData: {},\n    mergeWithHeaders: {},\n  },\n  storage: {},\n  request: {\n    active: [],\n    errors: {},\n    done: {},\n    lastResultIdList: {},\n  },\n};\n\nexport const factory = <T>(settings: CdeebeeSettings<T>, storage?: T) => {\n  const slice = createSlice({\n    name: 'cdeebee',\n    initialState: mergeDeepRight(initialState as CdeebeeState<T>, { settings, storage: storage ?? {} }) as CdeebeeState<T>,\n    reducers: {\n      set(state, action: { payload: CdeebeeValueList<T> }) {\n        // Directly mutate state.storage using Immer Draft\n        // This is more performant than creating a new object\n        // Immer will track changes and create minimal updates\n        batchingUpdate(state.storage as Record<string, unknown>, action.payload);\n      },\n      historyClear(state, action: PayloadAction<string | undefined>) {\n        const api = action.payload;\n\n        if (api) {\n          delete state.request.done[api];\n          delete state.request.errors[api];\n        } else {\n          state.request.done = {};\n          state.request.errors = {};\n        }\n      }\n    },\n    extraReducers: builder => {\n      builder\n        .addCase(request.pending, (state, action) => {\n          const api = action.meta.arg.api;\n          const requestId = action.meta.requestId;\n\n          if (action.meta.arg.historyClear) {\n            checkModule(state.settings, 'history', () => {\n              delete state.request.done[api];\n              delete state.request.errors[api];\n            });\n          }\n\n          checkModule(state.settings, 'cancelation', () => {\n            abortQuery(api, requestId);\n          });\n          checkModule(state.settings, 'listener', () => {\n            state.request.active.push({ api, requestId });\n          });\n        })\n        .addCase(request.fulfilled, (state, action) => {\n          const requestId = action.meta.requestId;\n          const api = action.meta.arg.api;\n\n          checkModule(state.settings, 'listener', () => {\n            state.request.active = state.request.active.filter(q => !(q.api === api && q.requestId === requestId));\n          });\n          checkModule(state.settings, 'history', () => {\n            if (!state.request.done[api]) state.request.done[api] = [];\n            state.request.done[api].push({ api, request: action.payload, requestId });\n            const max = state.settings.maxHistorySize;\n            if (max && state.request.done[api].length > max) {\n              state.request.done[api] = state.request.done[api].slice(-max);\n            }\n          });\n          checkModule(state.settings, 'storage', () => {\n            if (action.meta.arg.ignore) {\n              return;\n            }\n\n            const strategyList = (action.meta.arg.listStrategy ?? state.settings.listStrategy ?? {}) as CdeebeeListStrategy<T>;\n            const normalize = (action.meta.arg.normalize ?? state.settings.normalize ?? defaultNormalize) as NonNullable<CdeebeeSettings<T>['normalize']>;\n\n            const currentState = current(state) as CdeebeeState<T>;\n            const normalizedData = normalize(currentState, action.payload.result, strategyList);\n\n            // Normalize already handles merge/replace/skip and preserves keys not in response\n            // Simply apply the result\n            (state.storage as any) = normalizedData;\n\n            // Extract and store result IDs for filtering per list\n            state.request.lastResultIdList[api] = extractLastResultIdList(action.payload.result);\n          });\n        })\n        .addCase(request.rejected, (state, action) => {\n          const requestId = action.meta.requestId;\n          const api = action.meta.arg.api;\n\n          checkModule(state.settings, 'listener', () => {\n            state.request.active = state.request.active.filter(q => !(q.api === api && q.requestId === requestId));\n          });\n          checkModule(state.settings, 'history', () => {\n            if (!state.request.errors[api]) state.request.errors[api] = [];\n            state.request.errors[api].push({ requestId: requestId, api, request: action.error });\n            const max = state.settings.maxHistorySize;\n            if (max && state.request.errors[api].length > max) {\n              state.request.errors[api] = state.request.errors[api].slice(-max);\n            }\n          });\n        });\n    },\n  });\n\n  return slice;\n};\n","import { useSelector } from 'react-redux';\nimport { type CdeebeeState, type CdeebeeHistoryState } from '../reducer/types';\n\nconst EMPTY_ARRAY: readonly never[] = [];\nconst EMPTY_HISTORY: readonly CdeebeeHistoryState[] = EMPTY_ARRAY;\nconst EMPTY_ID_LIST: readonly string[] = EMPTY_ARRAY;\n\n/**\n * Generic hook factory that creates a selector hook for cdeebee state.\n * This allows the hooks to work with any Redux root state structure.\n *\n * @template RootState - The shape of the Redux root state\n * @template Storage - The shape of the cdeebee storage\n * @param selectCdeebee - Function to select the cdeebee slice from root state\n * @returns An object containing all cdeebee hooks\n */\nexport function createCdeebeeHooks<RootState, Storage>(\n  selectCdeebee: (state: RootState) => CdeebeeState<Storage>\n) {\n  /**\n   * Check if any of the specified APIs are currently loading.\n   *\n   * @param apiList - Array of API endpoints to check\n   * @returns true if any of the APIs are currently active/loading\n   *\n   * @example\n   * const isLoading = useLoading(['/api/forums', '/api/threads']);\n   * if (isLoading) return <Spinner />;\n   */\n  function useLoading(apiList: string[]): boolean {\n    return useSelector((state: RootState) => {\n      const cdeebee = selectCdeebee(state);\n      return cdeebee.request.active.some(q => apiList.includes(q.api));\n    });\n  }\n\n  /**\n   * Get the successful request history for a specific API endpoint.\n   *\n   * @param api - The API endpoint\n   * @returns Array of successful request history entries\n   *\n   * @example\n   * const history = useRequestHistory('/api/forums');\n   * console.log(`Made ${history.length} successful requests`);\n   */\n  function useRequestHistory(api: string) {\n    return useSelector((state: RootState) => {\n      const cdeebee = selectCdeebee(state);\n      return cdeebee.request.done[api] ?? EMPTY_HISTORY;\n    });\n  }\n\n  /**\n   * Get the error history for a specific API endpoint.\n   *\n   * @param api - The API endpoint\n   * @returns Array of error history entries\n   *\n   * @example\n   * const errors = useRequestErrors('/api/forums');\n   * if (errors.length > 0) {\n   *   console.error('Last error:', errors[errors.length - 1]);\n   * }\n   */\n  function useRequestErrors(api: string) {\n    return useSelector((state: RootState) => {\n      const cdeebee = selectCdeebee(state);\n      return cdeebee.request.errors[api] ?? EMPTY_HISTORY;\n    });\n  }\n\n  /**\n   * Get a specific list from storage with full type safety.\n   *\n   * @param listName - The name of the list in storage\n   * @returns The list data\n   *\n   * @example\n   * const forums = useStorageList('forumList');\n   * const forumArray = Object.values(forums);\n   */\n  function useStorageList<K extends keyof Storage>(listName: K): Storage[K] {\n    return useSelector((state: RootState) => {\n      const cdeebee = selectCdeebee(state);\n      return cdeebee.storage[listName];\n    });\n  }\n\n  /**\n   * Get the entire cdeebee storage.\n   *\n   * @returns The complete storage object\n   *\n   * @example\n   * const storage = useStorage();\n   * console.log(Object.keys(storage)); // ['forumList', 'threadList', ...]\n   */\n  function useStorage(): Storage {\n    return useSelector((state: RootState) => {\n      const cdeebee = selectCdeebee(state);\n      return cdeebee.storage;\n    });\n  }\n\n  /**\n   * Check if any request is currently loading (across all APIs).\n   *\n   * @returns true if any request is active\n   *\n   * @example\n   * const isAnythingLoading = useIsLoading();\n   * if (isAnythingLoading) return <GlobalSpinner />;\n   */\n  function useIsLoading(): boolean {\n    return useSelector((state: RootState) => {\n      const cdeebee = selectCdeebee(state);\n      return cdeebee.request.active.length > 0;\n    });\n  }\n\n  /**\n   * Get the list of IDs returned by the last successful request to an API for a specific list.\n   * Useful for filtering storage data to show only results from a specific request.\n   *\n   * @param api - The API endpoint\n   * @param listName - The name of the list in storage (typed from Storage)\n   * @returns Array of primary key IDs from the last response for that list\n   *\n   * @example\n   * const productList = useStorageList('productList');\n   * const lastIDList = useLastResultIdList('/api/search', 'productList');\n   * const displayResults = lastIDList.map(id => productList[id]).filter(Boolean);\n   */\n  function useLastResultIdList<K extends keyof Storage>(api: string, listName: K): string[] {\n    return useSelector((state: RootState) => {\n      const cdeebee = selectCdeebee(state);\n      return cdeebee.request.lastResultIdList[api]?.[listName as string] ?? EMPTY_ID_LIST;\n    });\n  }\n\n  return {\n    useLoading,\n    useRequestHistory,\n    useRequestErrors,\n    useStorageList,\n    useStorage,\n    useIsLoading,\n    useLastResultIdList,\n  };\n}\n","import { useSelector } from 'react-redux';\nimport { type CdeebeeState, type CdeebeeHistoryState } from '../reducer/types';\n\nconst EMPTY_ARRAY: readonly never[] = [];\nconst EMPTY_HISTORY: readonly CdeebeeHistoryState[] = EMPTY_ARRAY;\nconst EMPTY_ID_LIST: readonly string[] = EMPTY_ARRAY;\n\n/**\n * Standalone hook that can be used without createCdeebeeHooks.\n * Assumes the cdeebee slice is at state.cdeebee.\n *\n * @param apiList - Array of API endpoints to check\n * @returns true if any of the APIs are currently active/loading\n *\n * @example\n * const isLoading = useLoading(['/api/forums', '/api/threads']);\n */\nexport function useLoading<Storage = unknown>(apiList: string[]): boolean {\n  return useSelector((state: { cdeebee: CdeebeeState<Storage> }) => {\n    return state.cdeebee.request.active.some(q => apiList.includes(q.api));\n  });\n}\n\n/**\n * Standalone hook that can be used without createCdeebeeHooks.\n * Assumes the cdeebee slice is at state.cdeebee.\n *\n * @param api - The API endpoint\n * @returns Array of successful request history entries\n */\nexport function useRequestHistory<Storage = unknown>(api: string) {\n  return useSelector((state: { cdeebee: CdeebeeState<Storage> }) => {\n    return state.cdeebee.request.done[api] ?? EMPTY_HISTORY;\n  });\n}\n\n/**\n * Standalone hook that can be used without createCdeebeeHooks.\n * Assumes the cdeebee slice is at state.cdeebee.\n *\n * @param api - The API endpoint\n * @returns Array of error history entries\n */\nexport function useRequestErrors<Storage = unknown>(api: string) {\n  return useSelector((state: { cdeebee: CdeebeeState<Storage> }) => {\n    return state.cdeebee.request.errors[api] ?? EMPTY_HISTORY;\n  });\n}\n\n/**\n * Standalone hook that can be used without createCdeebeeHooks.\n * Assumes the cdeebee slice is at state.cdeebee.\n *\n * @param listName - The name of the list in storage\n * @returns The list data\n */\nexport function useStorageList<Storage, K extends keyof Storage>(listName: K): Storage[K] {\n  return useSelector((state: { cdeebee: CdeebeeState<Storage> }) => {\n    return state.cdeebee.storage[listName];\n  });\n}\n\n/**\n * Standalone hook that can be used without createCdeebeeHooks.\n * Assumes the cdeebee slice is at state.cdeebee.\n *\n * @returns The complete storage object\n */\nexport function useStorage<Storage>(): Storage {\n  return useSelector((state: { cdeebee: CdeebeeState<Storage> }) => {\n    return state.cdeebee.storage;\n  });\n}\n\n/**\n * Standalone hook that can be used without createCdeebeeHooks.\n * Assumes the cdeebee slice is at state.cdeebee.\n *\n * @returns true if any request is active\n */\nexport function useIsLoading<Storage = unknown>(): boolean {\n  return useSelector((state: { cdeebee: CdeebeeState<Storage> }) => {\n    return state.cdeebee.request.active.length > 0;\n  });\n}\n\n/**\n * Standalone hook that can be used without createCdeebeeHooks.\n * Assumes the cdeebee slice is at state.cdeebee.\n *\n * Get the list of IDs returned by the last successful request to an API for a specific list.\n * Useful for filtering storage data to show only results from a specific request.\n *\n * @param api - The API endpoint\n * @param listName - The name of the list in storage (typed from Storage)\n * @returns Array of primary key IDs from the last response for that list\n *\n * @example\n * const productList = useStorageList('productList');\n * const lastIDList = useLastResultIdList('/api/search', 'productList');\n * const displayResults = lastIDList.map(id => productList[id]).filter(Boolean);\n */\nexport function useLastResultIdList<Storage, K extends keyof Storage>(api: string, listName: K): string[] {\n  return useSelector((state: { cdeebee: CdeebeeState<Storage> }) => {\n    return state.cdeebee.request.lastResultIdList[api]?.[listName as string] ?? EMPTY_ID_LIST;\n  });\n}\n"],"mappings":"8HAGA,SAAgB,EAAyB,EAAkE,EAAuB,EAAoB,CAChJ,EAAS,QAAQ,SAAS,EAAO,EACnC,GAAQ,CAIZ,SAAgB,EAAS,EAAkD,CACzE,OAAyB,OAAO,GAAU,YAAnC,GAA+C,CAAC,MAAM,QAAQ,EAAM,CAG7E,SAAgB,EACd,EACA,EACG,CACH,GAAI,CAAC,EAAS,EAAK,EAAI,CAAC,EAAS,EAAM,CACrC,OAAO,EAGT,IAAM,EAAS,CAAE,GAAG,EAAM,CACpB,EAAc,EAEpB,IAAK,IAAM,KAAO,EAChB,GAAI,OAAO,UAAU,eAAe,KAAK,EAAa,EAAI,CAAE,CAC1D,IAAM,EAAY,EAAO,GACnB,EAAa,EAAY,GAG7B,EAAS,EAAU,EACnB,EAAS,EAAW,EACpB,CAAC,MAAM,QAAQ,EAAU,EACzB,CAAC,MAAM,QAAQ,EAAW,CAE1B,EAAO,GAAO,EAAe,EAAW,EAAW,CAEnD,EAAO,GAAO,EAKpB,OAAO,EAGT,SAAgB,EAAwC,EAAgB,EAA0B,CAChG,IAAM,EAAS,CAAE,GAAG,EAAK,CACzB,IAAK,IAAM,KAAO,EAChB,OAAO,EAAO,GAEhB,OAAO,EAQT,SAAgB,EAAwB,EAA6C,CACnF,GAAI,CAAC,EAAS,EAAS,CACrB,MAAO,EAAE,CAGX,IAAM,EAAmC,EAAE,CAE3C,IAAK,IAAM,KAAY,OAAO,KAAK,EAAS,CAAE,CAC5C,IAAM,EAAQ,EAAS,GAEvB,GACE,EAAS,EAAM,EACf,MAAM,QAAQ,EAAM,KAAK,EACzB,OAAO,EAAM,YAAe,SAC5B,CACA,IAAM,EAAa,EAAM,WACnB,EAAmB,EAAE,CAE3B,IAAK,IAAM,KAAQ,EAAM,KACnB,EAAS,EAAK,EAAI,KAAc,GAClC,EAAO,KAAK,OAAO,EAAK,GAAY,CAAC,CAIzC,EAAO,GAAY,GAIvB,OAAO,EAGT,SAAgB,EACd,EACA,EACM,CACN,IAAK,IAAI,EAAI,EAAG,EAAI,EAAU,OAAQ,IAAK,CACzC,IAAM,EAAO,EAAU,GACjB,EAAO,EAAK,IACZ,EAAQ,EAAK,MAEnB,GAAI,EAAK,SAAW,EAClB,SAGF,IAAI,EAA+C,EAEnD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAS,EAAG,IAAK,CACxC,IAAM,EAAU,EAAK,GAErB,GAAI,MAAM,QAAQ,EAAQ,CAAE,CAC1B,IAAM,EAAQ,OAAO,GAAY,SAAW,EAAU,OAAO,EAAQ,EACjE,EAAE,KAAS,IAAY,CAAC,EAAS,EAAQ,GAAO,IAClD,EAAQ,GAAS,EAAE,EAErB,EAAU,EAAQ,OACb,CACL,IAAM,EAAM,OAAO,EAAQ,CAC3B,GAAI,EAAE,KAAO,GAAU,CACrB,IAAM,EAAgB,OAAO,EAAK,EAAI,IAAO,UAAa,CAAC,MAAM,OAAO,EAAK,EAAI,GAAG,CAAC,EAAI,OAAO,OAAO,EAAK,EAAI,GAAG,CAAC,GAAK,OAAO,EAAK,EAAI,GAAG,CAC5I,EAAQ,GAAO,EAAgB,EAAE,CAAG,EAAE,CAExC,IAAM,EAAO,EAAQ,GACrB,EAAW,MAAM,QAAQ,EAAK,EAAW,EAAS,EAAK,CAAtB,EAAgC,EAAE,EAIvE,IAAM,EAAU,EAAK,EAAK,OAAS,GACnC,GAAI,MAAM,QAAQ,EAAQ,CAAE,CAC1B,IAAM,EAAQ,OAAO,GAAY,SAAW,EAAU,OAAO,EAAQ,CACrE,EAAQ,GAAS,OAEjB,EAAQ,OAAO,EAAQ,EAAI,GC/EjC,IAAM,EAAa,IA5CnB,KAA2B,gCACH,IAAI,eACV,IAAI,IAEpB,IAAI,EAAa,EAAmB,EAAmC,CACrE,IAAM,EAA0B,CAAE,YAAW,aAAY,MAAK,CAC9D,KAAK,YAAY,IAAI,EAAW,EAAK,CAEhC,KAAK,MAAM,IAAI,EAAI,EACtB,KAAK,MAAM,IAAI,EAAK,IAAI,IAAM,CAEhC,KAAK,MAAM,IAAI,EAAI,CAAE,IAAI,EAAU,CAGrC,OAAO,EAAyB,CAC9B,IAAM,EAAO,KAAK,YAAY,IAAI,EAAU,CAC5C,GAAI,CAAC,EAAM,OAEX,KAAK,YAAY,OAAO,EAAU,CAClC,IAAM,EAAS,KAAK,MAAM,IAAI,EAAK,IAAI,CACnC,IACF,EAAO,OAAO,EAAU,CACpB,EAAO,OAAS,GAClB,KAAK,MAAM,OAAO,EAAK,IAAI,EAKjC,eAAe,EAAa,EAAgC,CAC1D,IAAM,EAAgB,KAAK,MAAM,IAAI,EAAI,CACzC,GAAI,CAAC,EAAe,OAEpB,IAAM,EAAU,MAAM,KAAK,EAAc,CAAC,OAAO,GAAM,IAAO,EAAiB,CAE/E,IAAK,IAAM,KAAa,EAAS,CAC/B,IAAM,EAAO,KAAK,YAAY,IAAI,EAAU,CACxC,IACF,EAAK,WAAW,OAAO,CACvB,KAAK,OAAO,EAAU,KAQ9B,SAAgB,EAAW,EAAa,EAAgC,CACtE,EAAW,eAAe,EAAK,EAAiB,CAGlD,SAAgB,EAAa,EAAqB,EAAa,EAAmB,CAChF,IAAM,EAAa,IAAI,gBAEjB,MAAgB,CACpB,EAAW,OAAO,EAAU,EAQ9B,OALA,EAAO,iBAAiB,YAAe,CACrC,EAAW,OAAO,CAClB,GAAS,EACT,CAEK,CACL,aACA,SAAY,EAAW,IAAI,EAAK,EAAW,EAAW,CACtD,KAAM,EACP,CC5CH,IAAa,EAAa,IA5B1B,KAAiB,mCAC4B,QAAQ,SAAS,kBACtC,EAEtB,MAAM,QAAW,EAAoC,CAWnD,MAVA,MAAK,cAIL,KAAK,eAFmB,KAAK,eAG1B,SAAW,GAAM,KAAQ,GAAM,CAAC,CAChC,YAAc,CACb,KAAK,eACL,CAEG,KAAK,eAGd,gBAAyB,CACvB,OAAO,KAAK,YAGd,OAAc,CACZ,KAAK,YAAc,EACnB,KAAK,eAAiB,QAAQ,SAAS,GClB9B,GAAA,EAAA,EAAA,kBACX,kBACA,MAAO,EAAyC,CAAE,kBAAiB,WAAU,YAAW,YAAa,CACnG,IAAM,EAAY,IAAI,MAAM,CAAC,aAAa,CACpC,CAAE,QAAS,CAAE,aAAe,GAAU,CAEtC,EAAQ,EAAa,EAAQ,EAAQ,IAAK,EAAU,CACpD,EAAe,EAAQ,UAAY,OAAO,EAAQ,UAAa,WAErE,EAAY,EAAU,cAAe,EAAM,KAAK,CAEhD,IAAM,EAAiB,SAAY,CACjC,GAAI,CACF,GAAM,CAAE,SAAS,OAAQ,OAAM,UAAU,EAAE,EAAK,EAK1C,EAAuC,CAAE,GAJ3B,OAAO,EAAS,kBAAqB,WACrD,EAAS,kBAAkB,CAC1B,EAAS,kBAAoB,EAAE,CAE2B,GAAG,EAAS,CAMrE,EAAI,CAAE,GAJK,OAAO,EAAS,eAAkB,WAC/C,EAAS,eAAe,CACvB,EAAS,eAAiB,EAAE,CAER,GAAI,GAAQ,EAAE,CAAG,CACtC,EAAiC,KAAK,UAAU,EAAE,CAChD,EAAa,CAAC,CAAC,EAAQ,MAG7B,GAAI,EAAQ,MAAO,CACjB,IAAM,EAAW,IAAI,SACf,EAAU,EAAQ,SAAW,EAAS,QACtC,EAAU,EAAQ,SAAW,EAAS,QAE5C,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,MAAM,OAAQ,GAAK,EACzC,GACF,EAAS,OAAO,EAAS,EAAQ,MAAM,GAAG,CAI1C,GACF,EAAS,OAAO,EAAS,EAAY,CAEvC,EAAc,EAIhB,IAAM,EAAuC,CAC3C,gBAAiB,EACjB,GAAI,EAAa,EAAE,CAAG,CAAE,eAAgB,mBAAoB,CAC5D,GAAG,EACJ,CAEK,EAAQ,IAAW,MAEnB,EAAW,MAAM,MAAM,EAAQ,IAAK,CACxC,SACA,QAAS,EACT,OAAQ,EAAM,WAAW,OACzB,GAAI,EAAQ,EAAE,CAAG,CAAE,KAAM,EAAa,CACvC,CAAC,CAEF,EAAY,EAAU,cAAe,EAAM,KAAK,CAEhD,IAAI,EACE,EAAe,EAAQ,cAAgB,OAiB7C,MAfA,CAME,EANE,IAAiB,OACV,MAAM,EAAS,MAAM,CACrB,IAAiB,OACjB,MAAM,EAAS,MAAM,CAGrB,MAAM,EAAS,MAAM,CAG3B,EAAS,IAKV,GAAc,EAAQ,SAAU,EAAO,CACpC,CAAE,SAAQ,YAAW,QAAS,IAAI,MAAM,CAAC,aAAa,CAAE,GALzD,GAAc,EAAQ,SAAU,EAAO,CACpC,EAAgB,CAAE,OAAQ,EAAS,OAAQ,WAAY,EAAS,WAAY,KAAM,EAAQ,CAAC,QAK7F,EAAO,CASd,OARA,EAAY,EAAU,cAAe,EAAM,KAAK,CAE5C,GAAc,EAAQ,SAAU,EAAM,CAEtC,aAAiB,OAAS,EAAM,OAAS,aACpC,EAAgB,CAAE,QAAS,wBAAyB,UAAW,GAAM,CAAC,CAGxE,EAAgB,CAAE,QAAS,aAAiB,MAAQ,EAAM,QAAU,yBAA0B,CAAC,GAQ1G,OAJI,EAAS,QAAQ,SAAS,aAAa,CAClC,EAAW,QAAQ,EAAe,CAGpC,GAAgB,EAE1B,CCnGD,SAAS,EAAqB,EAAkE,CAC9F,OACE,EAAS,EAAM,EACf,MAAM,QAAQ,EAAM,KAAK,EACzB,OAAO,EAAM,YAAe,SAGhC,SAAS,EAA4B,EAAiB,EAA6C,CACjG,IAAM,EAAkC,EAAE,CAE1C,IAAK,IAAM,KAAQ,EACjB,GAAI,EAAS,EAAK,EAAI,KAAc,EAAM,CACxC,IAAM,EAAM,OAAO,EAAK,GAAY,CACpC,EAAO,GAAO,EAIlB,OAAO,EAGT,SAAS,EACP,EACA,EACA,EACA,EACe,CASb,OARE,IAAa,UACR,EACE,IAAa,QACf,EAAe,EAAe,EAAwB,CACpD,IAAa,OACf,GAEP,QAAQ,KAAK,8BAA8B,EAAS,aAAa,EAAI,4BAA4B,CAC1F,EAAe,EAAe,EAAwB,EAIjE,SAAgB,EACd,EACA,EACA,EAC+B,CAC/B,IAAM,EAAU,OAAO,KAAK,EAAS,CAC/B,EAAiB,EAAS,EAAQ,QAAQ,CAAI,EAAQ,QAAsC,EAAE,CAE9F,EAAS,CAAE,GAAG,EAAgB,CAC9B,EAAgB,IAAI,IAE1B,IAAK,IAAM,KAAO,EAAS,CACzB,IAAM,EAAgB,EAAS,GAE/B,GAAI,GAAkB,MAAuC,OAAO,GAAkB,SAAU,CAC9F,EAAc,IAAI,EAAI,CACtB,SAGF,IAAM,EAAW,EAAa,IAAmB,QAGjD,GAAI,IAAa,QAAU,EAAE,KAAO,GAClC,SAGF,IAAM,EAAgB,KAAO,EAAkB,EAAe,GAAuB,EAAE,CAEvF,GAAI,EAAqB,EAAc,CAAE,CAEvC,EAAO,GAAO,EAAc,EADJ,EAA4B,EAAc,KAAM,EAAc,WAAW,CACrC,EAAU,EAAI,CAC1E,SAGE,EAAS,EAAc,CACzB,EAAO,GAAO,EAAc,EAAe,EAA8B,EAAU,EAAI,CAEvF,EAAO,GAAO,EAIlB,OAAO,EAAc,KAAO,EAAI,EAAK,MAAM,KAAK,EAAc,CAAE,EAAO,CAAG,EChF5E,IAAM,EAAsC,CAC1C,SAAU,CACR,QAAS,CAAC,UAAW,WAAY,UAAW,cAAc,CAC1D,QAAS,OACT,QAAS,QACT,aAAc,EAAE,CAChB,cAAe,EAAE,CACjB,iBAAkB,EAAE,CACrB,CACD,QAAS,EAAE,CACX,QAAS,CACP,OAAQ,EAAE,CACV,OAAQ,EAAE,CACV,KAAM,EAAE,CACR,iBAAkB,EAAE,CACrB,CACF,CAEY,GAAc,EAA8B,KAgGvD,EAAA,EAAA,aA/F0B,CACxB,KAAM,UACN,aAAc,EAAe,EAAiC,CAAE,WAAU,QAAS,GAAW,EAAE,CAAE,CAAC,CACnG,SAAU,CACR,IAAI,EAAO,EAA0C,CAInD,EAAe,EAAM,QAAoC,EAAO,QAAQ,EAE1E,aAAa,EAAO,EAA2C,CAC7D,IAAM,EAAM,EAAO,QAEf,GACF,OAAO,EAAM,QAAQ,KAAK,GAC1B,OAAO,EAAM,QAAQ,OAAO,KAE5B,EAAM,QAAQ,KAAO,EAAE,CACvB,EAAM,QAAQ,OAAS,EAAE,GAG9B,CACD,cAAe,GAAW,CACxB,EACG,QAAQ,EAAQ,SAAU,EAAO,IAAW,CAC3C,IAAM,EAAM,EAAO,KAAK,IAAI,IACtB,EAAY,EAAO,KAAK,UAE1B,EAAO,KAAK,IAAI,cAClB,EAAY,EAAM,SAAU,cAAiB,CAC3C,OAAO,EAAM,QAAQ,KAAK,GAC1B,OAAO,EAAM,QAAQ,OAAO,IAC5B,CAGJ,EAAY,EAAM,SAAU,kBAAqB,CAC/C,EAAW,EAAK,EAAU,EAC1B,CACF,EAAY,EAAM,SAAU,eAAkB,CAC5C,EAAM,QAAQ,OAAO,KAAK,CAAE,MAAK,YAAW,CAAC,EAC7C,EACF,CACD,QAAQ,EAAQ,WAAY,EAAO,IAAW,CAC7C,IAAM,EAAY,EAAO,KAAK,UACxB,EAAM,EAAO,KAAK,IAAI,IAE5B,EAAY,EAAM,SAAU,eAAkB,CAC5C,EAAM,QAAQ,OAAS,EAAM,QAAQ,OAAO,OAAO,GAAK,EAAE,EAAE,MAAQ,GAAO,EAAE,YAAc,GAAW,EACtG,CACF,EAAY,EAAM,SAAU,cAAiB,CACtC,EAAM,QAAQ,KAAK,KAAM,EAAM,QAAQ,KAAK,GAAO,EAAE,EAC1D,EAAM,QAAQ,KAAK,GAAK,KAAK,CAAE,MAAK,QAAS,EAAO,QAAS,YAAW,CAAC,CACzE,IAAM,EAAM,EAAM,SAAS,eACvB,GAAO,EAAM,QAAQ,KAAK,GAAK,OAAS,IAC1C,EAAM,QAAQ,KAAK,GAAO,EAAM,QAAQ,KAAK,GAAK,MAAM,CAAC,EAAI,GAE/D,CACF,EAAY,EAAM,SAAU,cAAiB,CAC3C,GAAI,EAAO,KAAK,IAAI,OAClB,OAGF,IAAM,EAAgB,EAAO,KAAK,IAAI,cAAgB,EAAM,SAAS,cAAgB,EAAE,CAQtF,EAAM,SAPY,EAAO,KAAK,IAAI,WAAa,EAAM,SAAS,WAAa,IAAA,EAAA,EAAA,SAE/C,EAAM,CACY,EAAO,QAAQ,OAAQ,EAAa,CAOnF,EAAM,QAAQ,iBAAiB,GAAO,EAAwB,EAAO,QAAQ,OAAO,EACpF,EACF,CACD,QAAQ,EAAQ,UAAW,EAAO,IAAW,CAC5C,IAAM,EAAY,EAAO,KAAK,UACxB,EAAM,EAAO,KAAK,IAAI,IAE5B,EAAY,EAAM,SAAU,eAAkB,CAC5C,EAAM,QAAQ,OAAS,EAAM,QAAQ,OAAO,OAAO,GAAK,EAAE,EAAE,MAAQ,GAAO,EAAE,YAAc,GAAW,EACtG,CACF,EAAY,EAAM,SAAU,cAAiB,CACtC,EAAM,QAAQ,OAAO,KAAM,EAAM,QAAQ,OAAO,GAAO,EAAE,EAC9D,EAAM,QAAQ,OAAO,GAAK,KAAK,CAAa,YAAW,MAAK,QAAS,EAAO,MAAO,CAAC,CACpF,IAAM,EAAM,EAAM,SAAS,eACvB,GAAO,EAAM,QAAQ,OAAO,GAAK,OAAS,IAC5C,EAAM,QAAQ,OAAO,GAAO,EAAM,QAAQ,OAAO,GAAK,MAAM,CAAC,EAAI,GAEnE,EACF,EAEP,CAAC,CCrHE,EAAgC,EAAE,CAClC,EAAgD,EAChD,EAAmC,EAWzC,SAAgB,EACd,EACA,CAWA,SAAS,EAAW,EAA4B,CAC9C,OAAA,EAAA,EAAA,aAAoB,GACF,EAAc,EAAM,CACrB,QAAQ,OAAO,KAAK,GAAK,EAAQ,SAAS,EAAE,IAAI,CAAC,CAChE,CAaJ,SAAS,EAAkB,EAAa,CACtC,OAAA,EAAA,EAAA,aAAoB,GACF,EAAc,EAAM,CACrB,QAAQ,KAAK,IAAQ,EACpC,CAeJ,SAAS,EAAiB,EAAa,CACrC,OAAA,EAAA,EAAA,aAAoB,GACF,EAAc,EAAM,CACrB,QAAQ,OAAO,IAAQ,EACtC,CAaJ,SAAS,EAAwC,EAAyB,CACxE,OAAA,EAAA,EAAA,aAAoB,GACF,EAAc,EAAM,CACrB,QAAQ,GACvB,CAYJ,SAAS,GAAsB,CAC7B,OAAA,EAAA,EAAA,aAAoB,GACF,EAAc,EAAM,CACrB,QACf,CAYJ,SAAS,GAAwB,CAC/B,OAAA,EAAA,EAAA,aAAoB,GACF,EAAc,EAAM,CACrB,QAAQ,OAAO,OAAS,EACvC,CAgBJ,SAAS,EAA6C,EAAa,EAAuB,CACxF,OAAA,EAAA,EAAA,aAAoB,GACF,EAAc,EAAM,CACrB,QAAQ,iBAAiB,KAAO,IAAuB,EACtE,CAGJ,MAAO,CACL,aACA,oBACA,mBACA,iBACA,aACA,eACA,sBACD,CClJH,IAAM,EAAgC,EAAE,CAClC,EAAgD,EAChD,EAAmC,EAYzC,SAAgB,EAA8B,EAA4B,CACxE,OAAA,EAAA,EAAA,aAAoB,GACX,EAAM,QAAQ,QAAQ,OAAO,KAAK,GAAK,EAAQ,SAAS,EAAE,IAAI,CAAC,CACtE,CAUJ,SAAgB,EAAqC,EAAa,CAChE,OAAA,EAAA,EAAA,aAAoB,GACX,EAAM,QAAQ,QAAQ,KAAK,IAAQ,EAC1C,CAUJ,SAAgB,EAAoC,EAAa,CAC/D,OAAA,EAAA,EAAA,aAAoB,GACX,EAAM,QAAQ,QAAQ,OAAO,IAAQ,EAC5C,CAUJ,SAAgB,EAAiD,EAAyB,CACxF,OAAA,EAAA,EAAA,aAAoB,GACX,EAAM,QAAQ,QAAQ,GAC7B,CASJ,SAAgB,GAA+B,CAC7C,OAAA,EAAA,EAAA,aAAoB,GACX,EAAM,QAAQ,QACrB,CASJ,SAAgB,GAA2C,CACzD,OAAA,EAAA,EAAA,aAAoB,GACX,EAAM,QAAQ,QAAQ,OAAO,OAAS,EAC7C,CAmBJ,SAAgB,EAAsD,EAAa,EAAuB,CACxG,OAAA,EAAA,EAAA,aAAoB,GACX,EAAM,QAAQ,QAAQ,iBAAiB,KAAO,IAAuB,EAC5E"}