synapse-storage 4.0.1 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,25 @@
1
1
  import { FetchBaseQueryArgs, RequestDefinition } from '../types/api.interface';
2
2
  import { QueryOptions, QueryResult } from '../types/query.interface';
3
+ /**
4
+ * Собирает абсолютный URL запроса из `path` и `baseUrl`.
5
+ *
6
+ * Важно: сохраняется поведение конкатенации (`baseUrl + path`), а не
7
+ * резолвинг через `new URL(path, baseUrl)`. Резолвинг отбросил бы префикс
8
+ * базы для абсолютных путей (например, `new URL('/api/x', 'http://host/_api')`
9
+ * даёт `http://host/api/x`, теряя `/_api`), что сломало бы проксирование.
10
+ *
11
+ * Покрываемые кейсы:
12
+ * - абсолютный `path` (`http(s)://...`) → используется как есть;
13
+ * - относительный `path` + абсолютный `baseUrl` → конкатенация даёт абсолютный URL;
14
+ * - относительный `path` + относительный `baseUrl` (браузер, напр. `/_api`) →
15
+ * результат резолвится относительно `window.location.origin`, чтобы
16
+ * `new URL(...)` не падал с `Invalid URL` (same-origin сохраняется).
17
+ *
18
+ * @param path Путь запроса
19
+ * @param baseUrl Базовый URL клиента (может быть абсолютным или относительным)
20
+ * @returns Готовый объект URL
21
+ */
22
+ export declare function buildRequestUrl(path: string, baseUrl: string): URL;
3
23
  /**
4
24
  * Создает базовый fetch-клиент для запросов с поддержкой файлов
5
25
  * @param options Настройки базового клиента
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-base-query.d.ts","sourceRoot":"","sources":["../../../src/api/utils/fetch-base-query.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,kBAAkB,EAAE,iBAAiB,EAAkB,MAAM,wBAAwB,CAAA;AAC1G,OAAO,EAAsB,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAqFxF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,IAGlE,aAAa,EAAE,aAAa,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS,KAAK,GAAG,KAAK,EAC7F,MAAM,iBAAiB,CAAC,aAAa,CAAC,EACtC,cAAc,YAAY,YAAK,EAC/B,SAAS,OAAO,KACf,OAAO,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAsG1C"}
1
+ {"version":3,"file":"fetch-base-query.d.ts","sourceRoot":"","sources":["../../../src/api/utils/fetch-base-query.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,kBAAkB,EAAE,iBAAiB,EAAkB,MAAM,wBAAwB,CAAA;AAC1G,OAAO,EAAsB,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAqFxF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,GAAG,CAqBlE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,IAGlE,aAAa,EAAE,aAAa,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS,KAAK,GAAG,KAAK,EAC7F,MAAM,iBAAiB,CAAC,aAAa,CAAC,EACtC,cAAc,YAAY,YAAK,EAC/B,SAAS,OAAO,KACf,OAAO,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAsG1C"}
@@ -136,6 +136,42 @@ import { getFileMetadataFromHeaders, getResponseFormatForMimeType, isFileRespons
136
136
  };
137
137
  }
138
138
  }
139
+ /**
140
+ * Собирает абсолютный URL запроса из `path` и `baseUrl`.
141
+ *
142
+ * Важно: сохраняется поведение конкатенации (`baseUrl + path`), а не
143
+ * резолвинг через `new URL(path, baseUrl)`. Резолвинг отбросил бы префикс
144
+ * базы для абсолютных путей (например, `new URL('/api/x', 'http://host/_api')`
145
+ * даёт `http://host/api/x`, теряя `/_api`), что сломало бы проксирование.
146
+ *
147
+ * Покрываемые кейсы:
148
+ * - абсолютный `path` (`http(s)://...`) → используется как есть;
149
+ * - относительный `path` + абсолютный `baseUrl` → конкатенация даёт абсолютный URL;
150
+ * - относительный `path` + относительный `baseUrl` (браузер, напр. `/_api`) →
151
+ * результат резолвится относительно `window.location.origin`, чтобы
152
+ * `new URL(...)` не падал с `Invalid URL` (same-origin сохраняется).
153
+ *
154
+ * @param path Путь запроса
155
+ * @param baseUrl Базовый URL клиента (может быть абсолютным или относительным)
156
+ * @returns Готовый объект URL
157
+ */ function buildRequestUrl(path, baseUrl) {
158
+ // Путь уже абсолютный — база не нужна.
159
+ if (/^https?:\/\//i.test(path)) {
160
+ return new URL(path);
161
+ }
162
+ const combined = `${baseUrl}${path}`;
163
+ // Конкатенация уже дала абсолютный URL (абсолютный baseUrl).
164
+ if (/^https?:\/\//i.test(combined)) {
165
+ return new URL(combined);
166
+ }
167
+ // Относительный результат — резолвим относительно origin'а в браузере.
168
+ if (typeof window !== 'undefined' && window.location?.origin) {
169
+ return new URL(combined, window.location.origin);
170
+ }
171
+ // Сервер с относительной базой: явный resolve невозможен. Пробуем как есть —
172
+ // даст понятную ошибку, если база действительно относительна.
173
+ return new URL(combined);
174
+ }
139
175
  /**
140
176
  * Создает базовый fetch-клиент для запросов с поддержкой файлов
141
177
  * @param options Настройки базового клиента
@@ -148,7 +184,7 @@ import { getFileMetadataFromHeaders, getResponseFormatForMimeType, isFileRespons
148
184
  // Определяем формат ответа с приоритетом от options
149
185
  const responseFormat = optResponseFormat || reqResponseFormat;
150
186
  // Строим URL с учетом api параметров
151
- const url = new URL(path.startsWith('http') ? path : `${baseUrl}${path}`);
187
+ const url = buildRequestUrl(path, baseUrl);
152
188
  // Добавляем query-параметры в URL
153
189
  if (query) {
154
190
  Object.entries(query).forEach(([key, value])=>{
@@ -237,6 +273,6 @@ import { getFileMetadataFromHeaders, getResponseFormatForMimeType, isFileRespons
237
273
  };
238
274
  }
239
275
 
240
- export { fetchBaseQuery };
276
+ export { buildRequestUrl, fetchBaseQuery };
241
277
 
242
278
  //# sourceMappingURL=fetch-base-query.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api/utils/fetch-base-query.js","sources":["../../../src/api/utils/fetch-base-query.ts"],"sourcesContent":["import { handleCallbackError, logError } from '../../_utils/error-handling.util'\nimport { ApiContext, FetchBaseQueryArgs, RequestDefinition, ResponseFormat } from '../types/api.interface'\nimport { FileDownloadResult, QueryOptions, QueryResult } from '../types/query.interface'\nimport { getFileMetadataFromHeaders, getResponseFormatForMimeType, isFileResponse } from './file-utils'\n\n/**\n * Извлекает данные из response в зависимости от формата\n * @param response Объект Response\n * @param format Формат ответа\n * @returns Объект с данными или ошибкой\n */\nasync function getResponseData<T, E extends Error>(response: Response, format?: ResponseFormat): Promise<{ data?: T; error?: E; fileMetadata?: FileDownloadResult }> {\n let responseFormat = format\n const contentType = response.headers.get('content-type') || ''\n\n // Если формат не указан, пытаемся определить его из MIME-типа\n if (!responseFormat && contentType) {\n // Проверка, является ли ответ файлом на основе заголовков\n if (isFileResponse(response.headers)) {\n responseFormat = ResponseFormat.Blob\n } else {\n responseFormat = getResponseFormatForMimeType(contentType)\n }\n }\n\n // Если формат всё ещё не определен, используем JSON по умолчанию\n if (!responseFormat) {\n responseFormat = ResponseFormat.Json\n }\n\n try {\n // Получение метаданных файла, если формат указывает на файл\n let fileMetadata: any\n if (responseFormat === ResponseFormat.Blob || responseFormat === ResponseFormat.ArrayBuffer) {\n fileMetadata = getFileMetadataFromHeaders(response.headers)\n }\n\n // Обработка данных в зависимости от формата\n switch (responseFormat) {\n case ResponseFormat.Json: {\n // Пробуем получить JSON-данные\n try {\n const data = await response.json()\n return response.ok ? { data: data as T, fileMetadata } : { error: data as E, fileMetadata }\n } catch (error) {\n // Если не удалось разобрать JSON, возвращаем текст\n const text = await response.text()\n return response.ok ? { data: text as unknown as T, fileMetadata } : { error: text as unknown as E, fileMetadata }\n }\n }\n\n case ResponseFormat.Text: {\n const text = await response.text()\n return response.ok ? { data: text as unknown as T, fileMetadata } : { error: text as unknown as E, fileMetadata }\n }\n\n case ResponseFormat.Blob: {\n const blob = await response.blob()\n return response.ok ? { data: blob as unknown as T, fileMetadata } : { error: blob as unknown as E, fileMetadata }\n }\n\n case ResponseFormat.ArrayBuffer: {\n const buffer = await response.arrayBuffer()\n return response.ok ? { data: buffer as unknown as T, fileMetadata } : { error: buffer as unknown as E, fileMetadata }\n }\n\n case ResponseFormat.FormData: {\n const formData = await response.formData()\n return response.ok ? { data: formData as unknown as T, fileMetadata } : { error: formData as unknown as E, fileMetadata }\n }\n\n case ResponseFormat.Raw: {\n return response.ok ? { data: response as unknown as T, fileMetadata } : { error: response as unknown as E, fileMetadata }\n }\n\n default:\n // Если формат неизвестен, возвращаем blob как наиболее универсальный\n // eslint-disable-next-line no-case-declarations\n const blob = await response.blob()\n return response.ok ? { data: blob as unknown as T, fileMetadata } : { error: blob as unknown as E, fileMetadata }\n }\n } catch (err) {\n handleCallbackError(`fetchBaseQuery: error extracting response data (format: ${responseFormat})`, err)\n return response.ok ? { data: undefined } : { error: err as E }\n }\n}\n\n/**\n * Создает базовый fetch-клиент для запросов с поддержкой файлов\n * @param options Настройки базового клиента\n * @returns Функция для выполнения запросов\n */\nexport function fetchBaseQuery(options: Omit<FetchBaseQueryArgs, 'prepareHeaders'>) {\n const { baseUrl, timeout = 30000, fetchFn = fetch, credentials = 'same-origin' } = options\n\n return async <RequestResult, RequestParams extends Record<string, any>, E extends Error = Error>(\n args: RequestDefinition<RequestParams>,\n queryOptions: QueryOptions = {},\n headers: Headers,\n ): Promise<QueryResult<RequestResult, E>> => {\n const { path, method, body, query, responseFormat: reqResponseFormat } = args\n\n const { signal, timeout: requestTimeout = timeout, responseFormat: optResponseFormat } = queryOptions\n\n // Определяем формат ответа с приоритетом от options\n const responseFormat = optResponseFormat || reqResponseFormat\n\n // Строим URL с учетом api параметров\n const url = new URL(path.startsWith('http') ? path : `${baseUrl}${path}`)\n\n // Добавляем query-параметры в URL\n if (query) {\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n if (Array.isArray(value)) {\n value.forEach((item) => url.searchParams.append(key, String(item)))\n } else {\n url.searchParams.append(key, String(value))\n }\n }\n })\n }\n\n // Если body это объект, конвертируем в JSON и устанавливаем Content-Type\n let serializedBody: string | FormData | Blob | undefined\n if (body !== undefined) {\n if (body instanceof FormData || body instanceof Blob) {\n serializedBody = body\n } else if (typeof body === 'object' && body !== null) {\n try {\n serializedBody = JSON.stringify(body)\n if (!headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json')\n }\n } catch (error) {\n logError('fetchBaseQuery: error serializing request body', error)\n serializedBody = String(body)\n }\n } else {\n serializedBody = String(body)\n }\n }\n\n // Создаем таймаут если указан\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n const timeoutPromise = new Promise<never>((_, reject) => {\n if (requestTimeout) {\n timeoutId = globalThis.setTimeout(() => {\n reject(new Error(`Превышено время ожидания запроса (${requestTimeout}мс)`))\n }, requestTimeout)\n }\n })\n\n try {\n // Выполняем запрос\n const fetchPromise = fetchFn(url.toString(), {\n method,\n headers,\n body: serializedBody,\n signal,\n credentials,\n })\n\n // Используем Promise.race для обработки таймаута\n const response = await Promise.race([fetchPromise, timeoutPromise])\n\n // Обрабатываем ответ\n const { data, error, fileMetadata } = await getResponseData<RequestResult, E>(response, responseFormat as ResponseFormat)\n\n // Формируем результат запроса\n const result: QueryResult<RequestResult, E> = {\n data,\n error,\n ok: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n fileDownloadResult: fileMetadata,\n }\n\n return result\n } catch (err) {\n // Обрабатываем ошибки сети или таймаута\n const error = err as Error\n handleCallbackError('fetchBaseQuery: request execution error', error)\n\n // Формируем результат с ошибкой\n return {\n error: error as E,\n ok: false,\n status: 0,\n statusText: error.message,\n headers: new Headers(),\n }\n } finally {\n // Очищаем таймер в любом случае\n if (timeoutId) {\n globalThis.clearTimeout(timeoutId)\n }\n }\n }\n}\n"],"names":["handleCallbackError","logError","ResponseFormat","getFileMetadataFromHeaders","getResponseFormatForMimeType","isFileResponse","getResponseData","response","format","responseFormat","contentType","fileMetadata","data","error","text","blob","buffer","formData","err","undefined","fetchBaseQuery","options","baseUrl","timeout","fetchFn","fetch","credentials","args","queryOptions","headers","path","method","body","query","reqResponseFormat","signal","requestTimeout","optResponseFormat","url","URL","Object","key","value","Array","item","String","serializedBody","FormData","Blob","JSON","timeoutId","timeoutPromise","Promise","_","reject","globalThis","Error","fetchPromise","result","Headers"],"mappings":"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"}
1
+ {"version":3,"file":"api/utils/fetch-base-query.js","sources":["../../../src/api/utils/fetch-base-query.ts"],"sourcesContent":["import { handleCallbackError, logError } from '../../_utils/error-handling.util'\nimport { ApiContext, FetchBaseQueryArgs, RequestDefinition, ResponseFormat } from '../types/api.interface'\nimport { FileDownloadResult, QueryOptions, QueryResult } from '../types/query.interface'\nimport { getFileMetadataFromHeaders, getResponseFormatForMimeType, isFileResponse } from './file-utils'\n\n/**\n * Извлекает данные из response в зависимости от формата\n * @param response Объект Response\n * @param format Формат ответа\n * @returns Объект с данными или ошибкой\n */\nasync function getResponseData<T, E extends Error>(response: Response, format?: ResponseFormat): Promise<{ data?: T; error?: E; fileMetadata?: FileDownloadResult }> {\n let responseFormat = format\n const contentType = response.headers.get('content-type') || ''\n\n // Если формат не указан, пытаемся определить его из MIME-типа\n if (!responseFormat && contentType) {\n // Проверка, является ли ответ файлом на основе заголовков\n if (isFileResponse(response.headers)) {\n responseFormat = ResponseFormat.Blob\n } else {\n responseFormat = getResponseFormatForMimeType(contentType)\n }\n }\n\n // Если формат всё ещё не определен, используем JSON по умолчанию\n if (!responseFormat) {\n responseFormat = ResponseFormat.Json\n }\n\n try {\n // Получение метаданных файла, если формат указывает на файл\n let fileMetadata: any\n if (responseFormat === ResponseFormat.Blob || responseFormat === ResponseFormat.ArrayBuffer) {\n fileMetadata = getFileMetadataFromHeaders(response.headers)\n }\n\n // Обработка данных в зависимости от формата\n switch (responseFormat) {\n case ResponseFormat.Json: {\n // Пробуем получить JSON-данные\n try {\n const data = await response.json()\n return response.ok ? { data: data as T, fileMetadata } : { error: data as E, fileMetadata }\n } catch (error) {\n // Если не удалось разобрать JSON, возвращаем текст\n const text = await response.text()\n return response.ok ? { data: text as unknown as T, fileMetadata } : { error: text as unknown as E, fileMetadata }\n }\n }\n\n case ResponseFormat.Text: {\n const text = await response.text()\n return response.ok ? { data: text as unknown as T, fileMetadata } : { error: text as unknown as E, fileMetadata }\n }\n\n case ResponseFormat.Blob: {\n const blob = await response.blob()\n return response.ok ? { data: blob as unknown as T, fileMetadata } : { error: blob as unknown as E, fileMetadata }\n }\n\n case ResponseFormat.ArrayBuffer: {\n const buffer = await response.arrayBuffer()\n return response.ok ? { data: buffer as unknown as T, fileMetadata } : { error: buffer as unknown as E, fileMetadata }\n }\n\n case ResponseFormat.FormData: {\n const formData = await response.formData()\n return response.ok ? { data: formData as unknown as T, fileMetadata } : { error: formData as unknown as E, fileMetadata }\n }\n\n case ResponseFormat.Raw: {\n return response.ok ? { data: response as unknown as T, fileMetadata } : { error: response as unknown as E, fileMetadata }\n }\n\n default:\n // Если формат неизвестен, возвращаем blob как наиболее универсальный\n // eslint-disable-next-line no-case-declarations\n const blob = await response.blob()\n return response.ok ? { data: blob as unknown as T, fileMetadata } : { error: blob as unknown as E, fileMetadata }\n }\n } catch (err) {\n handleCallbackError(`fetchBaseQuery: error extracting response data (format: ${responseFormat})`, err)\n return response.ok ? { data: undefined } : { error: err as E }\n }\n}\n\n/**\n * Собирает абсолютный URL запроса из `path` и `baseUrl`.\n *\n * Важно: сохраняется поведение конкатенации (`baseUrl + path`), а не\n * резолвинг через `new URL(path, baseUrl)`. Резолвинг отбросил бы префикс\n * базы для абсолютных путей (например, `new URL('/api/x', 'http://host/_api')`\n * даёт `http://host/api/x`, теряя `/_api`), что сломало бы проксирование.\n *\n * Покрываемые кейсы:\n * - абсолютный `path` (`http(s)://...`) → используется как есть;\n * - относительный `path` + абсолютный `baseUrl` → конкатенация даёт абсолютный URL;\n * - относительный `path` + относительный `baseUrl` (браузер, напр. `/_api`) →\n * результат резолвится относительно `window.location.origin`, чтобы\n * `new URL(...)` не падал с `Invalid URL` (same-origin сохраняется).\n *\n * @param path Путь запроса\n * @param baseUrl Базовый URL клиента (может быть абсолютным или относительным)\n * @returns Готовый объект URL\n */\nexport function buildRequestUrl(path: string, baseUrl: string): URL {\n // Путь уже абсолютный — база не нужна.\n if (/^https?:\\/\\//i.test(path)) {\n return new URL(path)\n }\n\n const combined = `${baseUrl}${path}`\n\n // Конкатенация уже дала абсолютный URL (абсолютный baseUrl).\n if (/^https?:\\/\\//i.test(combined)) {\n return new URL(combined)\n }\n\n // Относительный результат — резолвим относительно origin'а в браузере.\n if (typeof window !== 'undefined' && window.location?.origin) {\n return new URL(combined, window.location.origin)\n }\n\n // Сервер с относительной базой: явный resolve невозможен. Пробуем как есть —\n // даст понятную ошибку, если база действительно относительна.\n return new URL(combined)\n}\n\n/**\n * Создает базовый fetch-клиент для запросов с поддержкой файлов\n * @param options Настройки базового клиента\n * @returns Функция для выполнения запросов\n */\nexport function fetchBaseQuery(options: Omit<FetchBaseQueryArgs, 'prepareHeaders'>) {\n const { baseUrl, timeout = 30000, fetchFn = fetch, credentials = 'same-origin' } = options\n\n return async <RequestResult, RequestParams extends Record<string, any>, E extends Error = Error>(\n args: RequestDefinition<RequestParams>,\n queryOptions: QueryOptions = {},\n headers: Headers,\n ): Promise<QueryResult<RequestResult, E>> => {\n const { path, method, body, query, responseFormat: reqResponseFormat } = args\n\n const { signal, timeout: requestTimeout = timeout, responseFormat: optResponseFormat } = queryOptions\n\n // Определяем формат ответа с приоритетом от options\n const responseFormat = optResponseFormat || reqResponseFormat\n\n // Строим URL с учетом api параметров\n const url = buildRequestUrl(path, baseUrl)\n\n // Добавляем query-параметры в URL\n if (query) {\n Object.entries(query).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n if (Array.isArray(value)) {\n value.forEach((item) => url.searchParams.append(key, String(item)))\n } else {\n url.searchParams.append(key, String(value))\n }\n }\n })\n }\n\n // Если body это объект, конвертируем в JSON и устанавливаем Content-Type\n let serializedBody: string | FormData | Blob | undefined\n if (body !== undefined) {\n if (body instanceof FormData || body instanceof Blob) {\n serializedBody = body\n } else if (typeof body === 'object' && body !== null) {\n try {\n serializedBody = JSON.stringify(body)\n if (!headers.has('Content-Type')) {\n headers.set('Content-Type', 'application/json')\n }\n } catch (error) {\n logError('fetchBaseQuery: error serializing request body', error)\n serializedBody = String(body)\n }\n } else {\n serializedBody = String(body)\n }\n }\n\n // Создаем таймаут если указан\n let timeoutId: ReturnType<typeof setTimeout> | undefined\n const timeoutPromise = new Promise<never>((_, reject) => {\n if (requestTimeout) {\n timeoutId = globalThis.setTimeout(() => {\n reject(new Error(`Превышено время ожидания запроса (${requestTimeout}мс)`))\n }, requestTimeout)\n }\n })\n\n try {\n // Выполняем запрос\n const fetchPromise = fetchFn(url.toString(), {\n method,\n headers,\n body: serializedBody,\n signal,\n credentials,\n })\n\n // Используем Promise.race для обработки таймаута\n const response = await Promise.race([fetchPromise, timeoutPromise])\n\n // Обрабатываем ответ\n const { data, error, fileMetadata } = await getResponseData<RequestResult, E>(response, responseFormat as ResponseFormat)\n\n // Формируем результат запроса\n const result: QueryResult<RequestResult, E> = {\n data,\n error,\n ok: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n fileDownloadResult: fileMetadata,\n }\n\n return result\n } catch (err) {\n // Обрабатываем ошибки сети или таймаута\n const error = err as Error\n handleCallbackError('fetchBaseQuery: request execution error', error)\n\n // Формируем результат с ошибкой\n return {\n error: error as E,\n ok: false,\n status: 0,\n statusText: error.message,\n headers: new Headers(),\n }\n } finally {\n // Очищаем таймер в любом случае\n if (timeoutId) {\n globalThis.clearTimeout(timeoutId)\n }\n }\n }\n}\n"],"names":["handleCallbackError","logError","ResponseFormat","getFileMetadataFromHeaders","getResponseFormatForMimeType","isFileResponse","getResponseData","response","format","responseFormat","contentType","fileMetadata","data","error","text","blob","buffer","formData","err","undefined","buildRequestUrl","path","baseUrl","URL","combined","window","fetchBaseQuery","options","timeout","fetchFn","fetch","credentials","args","queryOptions","headers","method","body","query","reqResponseFormat","signal","requestTimeout","optResponseFormat","url","Object","key","value","Array","item","String","serializedBody","FormData","Blob","JSON","timeoutId","timeoutPromise","Promise","_","reject","globalThis","Error","fetchPromise","result","Headers"],"mappings":"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"}
@@ -13,12 +13,27 @@ export declare class IndexedDBManager {
13
13
  private initPromise;
14
14
  private storeNames;
15
15
  private dbVersion;
16
+ private opQueue;
16
17
  private constructor();
17
18
  static getInstance(dbName: string, dbVersion?: number, logger?: ILogger): IndexedDBManager;
18
19
  initialize(): Promise<IDBDatabase>;
19
20
  private autoDetectAndOpen;
20
21
  private detectCurrentVersion;
21
22
  ensureStoreExists(storeName: string): Promise<IDBDatabase>;
23
+ /**
24
+ * Ставит операцию изменения схемы в последовательную очередь, чтобы создания
25
+ * сторов на одной БД не перекрывались между await-точками. Очередь не должна
26
+ * "застревать" из-за упавшей операции — ошибку прокидываем вызывающему, но
27
+ * следующий элемент очереди стартует независимо от результата предыдущего.
28
+ */
29
+ private enqueue;
30
+ /**
31
+ * Идемпотентно гарантирует наличие переданных сторов. Никогда не обращается к
32
+ * this.db после await — работает с локальной ссылкой, возвращённой initialize()
33
+ * / openDatabase(), поэтому параллельное обнуление this.db (другим клиентом
34
+ * или onversionchange) не приводит к чтению свойств у null.
35
+ */
36
+ private ensureStoresInternal;
22
37
  private openDatabase;
23
38
  closeDatabase(): void;
24
39
  deleteDatabase(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"indexed-DB.service.d.ts","sourceRoot":"","sources":["../../../../src/core/storage/adapters/indexed-DB.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"indexed-DB.service.d.ts","sourceRoot":"","sources":["../../../../src/core/storage/adapters/indexed-DB.service.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,12 @@ class IndexedDBManager {
20
20
  initPromise = null;
21
21
  storeNames = new Set();
22
22
  dbVersion;
23
+ // Последовательная очередь операций изменения схемы (создание сторов).
24
+ // На одну БД (singleton по dbName) обычно инициализируется сразу несколько
25
+ // ApiClient-ов (comments, posts, reactions, ...). Без сериализации их
26
+ // ensureStoreExists перекрывались: один обнулял this.db между close() и
27
+ // переоткрытием, второй в этот момент читал this.db.objectStoreNames → null.
28
+ opQueue = Promise.resolve();
23
29
  constructor(dbName, dbVersion, logger){
24
30
  this.dbName = dbName;
25
31
  this.logger = logger;
@@ -70,24 +76,43 @@ class IndexedDBManager {
70
76
  });
71
77
  }
72
78
  async ensureStoreExists(storeName) {
73
- await this.initialize();
74
- if (this.db.objectStoreNames.contains(storeName)) {
75
- this.storeNames.add(storeName);
76
- return this.db;
79
+ return this.enqueue(()=>this.ensureStoresInternal([
80
+ storeName
81
+ ]));
82
+ }
83
+ /**
84
+ * Ставит операцию изменения схемы в последовательную очередь, чтобы создания
85
+ * сторов на одной БД не перекрывались между await-точками. Очередь не должна
86
+ * "застревать" из-за упавшей операции — ошибку прокидываем вызывающему, но
87
+ * следующий элемент очереди стартует независимо от результата предыдущего.
88
+ */ enqueue(operation) {
89
+ const result = this.opQueue.then(operation, operation);
90
+ this.opQueue = result.then(()=>undefined, ()=>undefined);
91
+ return result;
92
+ }
93
+ /**
94
+ * Идемпотентно гарантирует наличие переданных сторов. Никогда не обращается к
95
+ * this.db после await — работает с локальной ссылкой, возвращённой initialize()
96
+ * / openDatabase(), поэтому параллельное обнуление this.db (другим клиентом
97
+ * или onversionchange) не приводит к чтению свойств у null.
98
+ */ async ensureStoresInternal(storeNames) {
99
+ let db = await this.initialize();
100
+ const missingStores = storeNames.filter((name)=>!db.objectStoreNames.contains(name));
101
+ if (missingStores.length === 0) {
102
+ for (const name of storeNames)this.storeNames.add(name);
103
+ return db;
77
104
  }
78
- this.logger?.debug(`Store "${storeName}" not found, upgrading database`, {
105
+ this.logger?.debug(`Создание недостающих хранилищ: ${missingStores.join(', ')}`, {
79
106
  dbName: this.dbName,
80
- currentStores: Array.from(this.db.objectStoreNames)
107
+ currentStores: Array.from(db.objectStoreNames)
81
108
  });
82
- this.db.close();
109
+ db.close();
83
110
  this.db = null;
84
111
  this.dbVersion++;
85
- this.initPromise = this.openDatabase([
86
- storeName
87
- ]);
88
- const newDb = await this.initPromise;
89
- this.storeNames.add(storeName);
90
- return newDb;
112
+ this.initPromise = this.openDatabase(missingStores);
113
+ db = await this.initPromise;
114
+ for (const name of storeNames)this.storeNames.add(name);
115
+ return db;
91
116
  }
92
117
  async openDatabase(newStores = []) {
93
118
  return new Promise((resolve, reject)=>{
@@ -160,26 +185,7 @@ class IndexedDBManager {
160
185
  });
161
186
  }
162
187
  async ensureStoresExist(storeNames) {
163
- // Сначала инициализируем базу
164
- await this.initialize();
165
- // Проверяем, какие хранилища уже существуют
166
- const missingStores = storeNames.filter((name)=>!this.db.objectStoreNames.contains(name));
167
- // Если все хранилища уже существуют, просто возвращаем базу
168
- if (missingStores.length === 0) {
169
- return this.db;
170
- }
171
- // Иначе нам нужно обновить базу для создания новых хранилищ
172
- this.logger?.debug(`Создание недостающих хранилищ: ${missingStores.join(', ')}`, {
173
- dbName: this.dbName,
174
- currentStores: Array.from(this.db.objectStoreNames)
175
- });
176
- // Закрываем текущее соединение
177
- this.db.close();
178
- this.db = null;
179
- // Увеличиваем версию один раз для всех новых хранилищ
180
- this.dbVersion++;
181
- this.initPromise = this.openDatabase(missingStores);
182
- return this.initPromise;
188
+ return this.enqueue(()=>this.ensureStoresInternal(storeNames));
183
189
  }
184
190
  // Метод для получения текущей версии
185
191
  getCurrentVersion() {
@@ -1 +1 @@
1
- {"version":3,"file":"core/storage/adapters/indexed-DB.service.js","sources":["../../../../src/core/storage/adapters/indexed-DB.service.ts"],"sourcesContent":["import { IAsyncPluginExecutor } from '../modules/plugin/plugin.interface'\nimport { SingletonMixin } from '../modules/singleton/mixin.util'\nimport { ConfigureAsyncMiddlewares, IEventEmitter, ILogger, IndexedDBStorageConfig, StorageType } from '../storage.interface'\nimport { StorageKey, StorageKeyType } from '../utils/storage-key'\nimport { AsyncBaseStorage } from './async-base-storage.service'\nimport { getValueByPath, parsePath, setValueByPath } from './path.utils'\n\nexport interface IndexedDBConfig {\n dbName?: string\n}\n\n// Управляет соединением с базой данных\nexport class IndexedDBManager {\n private static instances = new Map<string, IndexedDBManager>()\n private db: IDBDatabase | null = null\n private initPromise: Promise<IDBDatabase> | null = null\n private storeNames: Set<string> = new Set()\n private dbVersion: number\n\n private constructor(\n private readonly dbName: string,\n dbVersion: number,\n private readonly logger?: ILogger,\n ) {\n this.dbVersion = dbVersion\n }\n\n static getInstance(dbName: string, dbVersion: number = 1, logger?: ILogger): IndexedDBManager {\n if (!IndexedDBManager.instances.has(dbName)) {\n IndexedDBManager.instances.set(dbName, new IndexedDBManager(dbName, dbVersion, logger))\n }\n\n const instance = IndexedDBManager.instances.get(dbName)!\n\n // Update version if higher version is requested\n if (dbVersion > instance.dbVersion) {\n instance.dbVersion = dbVersion\n }\n\n return instance\n }\n\n async initialize(): Promise<IDBDatabase> {\n if (this.db) {\n return this.db\n }\n\n if (!this.initPromise) {\n this.initPromise = this.autoDetectAndOpen()\n }\n\n return this.initPromise\n }\n\n private async autoDetectAndOpen(): Promise<IDBDatabase> {\n // Auto-detect the current DB version to avoid VersionError\n const currentVersion = await this.detectCurrentVersion()\n if (currentVersion > this.dbVersion) {\n this.logger?.debug(`Auto-detected higher DB version: ${currentVersion} (requested: ${this.dbVersion})`)\n this.dbVersion = currentVersion\n }\n return this.openDatabase()\n }\n\n private detectCurrentVersion(): Promise<number> {\n return new Promise<number>((resolve) => {\n try {\n const request = indexedDB.open(this.dbName)\n request.onsuccess = () => {\n const version = request.result.version\n request.result.close()\n resolve(version)\n }\n request.onerror = () => resolve(0)\n } catch {\n resolve(0)\n }\n })\n }\n\n async ensureStoreExists(storeName: string): Promise<IDBDatabase> {\n await this.initialize()\n\n if (this.db!.objectStoreNames.contains(storeName)) {\n this.storeNames.add(storeName)\n return this.db!\n }\n\n this.logger?.debug(`Store \"${storeName}\" not found, upgrading database`, {\n dbName: this.dbName,\n currentStores: Array.from(this.db!.objectStoreNames),\n })\n\n this.db!.close()\n this.db = null\n\n this.dbVersion++\n this.initPromise = this.openDatabase([storeName])\n\n const newDb = await this.initPromise\n this.storeNames.add(storeName)\n return newDb\n }\n\n private async openDatabase(newStores: string[] = []): Promise<IDBDatabase> {\n return new Promise<IDBDatabase>((resolve, reject) => {\n this.logger?.debug(`Opening database \"${this.dbName}\" with version ${this.dbVersion}`)\n\n const request = indexedDB.open(this.dbName, this.dbVersion)\n\n request.onerror = () => {\n this.logger?.error(`Failed to open database \"${this.dbName}\"`, { error: request.error })\n reject(request.error)\n }\n\n request.onblocked = () => {\n this.logger?.warn(`Database \"${this.dbName}\" upgrade blocked by another connection. Close other tabs or connections.`)\n reject(new Error(`Database \"${this.dbName}\" upgrade blocked — close other tabs using this database`))\n }\n\n request.onsuccess = () => {\n this.db = request.result\n\n // Auto-close when another connection requests a version upgrade\n this.db.onversionchange = () => {\n this.db?.close()\n this.db = null\n this.initPromise = null\n }\n\n // Add existing stores to our set\n for (let i = 0; i < this.db.objectStoreNames.length; i++) {\n this.storeNames.add(this.db.objectStoreNames[i])\n }\n\n this.logger?.debug(`Database \"${this.dbName}\" opened successfully`, {\n version: this.db.version,\n stores: Array.from(this.db.objectStoreNames),\n })\n\n resolve(this.db)\n }\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result\n this.logger?.debug(`Upgrading database \"${this.dbName}\" to version ${this.dbVersion}`)\n\n // Create new stores that don't exist yet\n for (const storeName of newStores) {\n if (!db.objectStoreNames.contains(storeName)) {\n this.logger?.debug(`Creating store \"${storeName}\"`)\n db.createObjectStore(storeName)\n }\n }\n }\n })\n }\n\n closeDatabase(): void {\n if (this.db) {\n this.db.close()\n this.db = null\n this.initPromise = null\n }\n }\n\n async deleteDatabase(): Promise<void> {\n this.closeDatabase()\n\n return new Promise<void>((resolve, reject) => {\n const request = indexedDB.deleteDatabase(this.dbName)\n request.onsuccess = () => {\n this.logger?.debug(`Database \"${this.dbName}\" deleted successfully`)\n IndexedDBManager.instances.delete(this.dbName)\n this.storeNames.clear()\n resolve()\n }\n request.onerror = () => {\n this.logger?.error(`Failed to delete database \"${this.dbName}\"`, { error: request.error })\n reject(request.error)\n }\n })\n }\n async ensureStoresExist(storeNames: string[]): Promise<IDBDatabase> {\n // Сначала инициализируем базу\n await this.initialize()\n\n // Проверяем, какие хранилища уже существуют\n const missingStores = storeNames.filter((name) => !this.db!.objectStoreNames.contains(name))\n\n // Если все хранилища уже существуют, просто возвращаем базу\n if (missingStores.length === 0) {\n return this.db!\n }\n\n // Иначе нам нужно обновить базу для создания новых хранилищ\n this.logger?.debug(`Создание недостающих хранилищ: ${missingStores.join(', ')}`, {\n dbName: this.dbName,\n currentStores: Array.from(this.db!.objectStoreNames),\n })\n\n // Закрываем текущее соединение\n this.db!.close()\n this.db = null\n\n // Увеличиваем версию один раз для всех новых хранилищ\n this.dbVersion++\n this.initPromise = this.openDatabase(missingStores)\n\n return this.initPromise\n }\n\n // Метод для получения текущей версии\n getCurrentVersion(): number {\n return this.dbVersion\n }\n}\n\nexport class IndexedDBStorage<T extends Record<string, any>> extends AsyncBaseStorage<T> {\n protected static readonly STORAGE_TYPE: StorageType = 'indexedDB'\n readonly type: StorageType = 'indexedDB'\n\n private readonly DB_NAME: string\n private readonly STORE_NAME: string\n private dbManager: IndexedDBManager\n\n constructor(config: IndexedDBStorageConfig<T>, pluginExecutor?: IAsyncPluginExecutor, eventEmitter?: IEventEmitter, logger?: ILogger) {\n super(config, pluginExecutor, eventEmitter, logger)\n\n this.DB_NAME = config.options?.dbName || 'app_storage'\n this.STORE_NAME = config.name\n\n // Get database manager instance (version is auto-detected internally)\n this.dbManager = IndexedDBManager.getInstance(this.DB_NAME, 1, logger)\n }\n\n static create<T extends Record<string, any>>(\n config: IndexedDBStorageConfig,\n pluginExecutor?: IAsyncPluginExecutor,\n eventEmitter?: IEventEmitter,\n logger?: ILogger,\n ): IndexedDBStorage<T> {\n return SingletonMixin.handleSingletonCreation(\n config,\n this.STORAGE_TYPE,\n (finalConfig) => new IndexedDBStorage<T>(finalConfig as IndexedDBStorageConfig<T>, pluginExecutor, eventEmitter, logger),\n logger,\n )\n }\n\n protected async doInitialize(): Promise<this> {\n try {\n this.logger?.debug(`Initializing IndexedDB storage \"${this.STORE_NAME}\"`)\n\n // Создаем store в базе данных\n await this.dbManager.ensureStoreExists(this.STORE_NAME)\n\n // Проверяем, что хранилище доступно\n const db = await this.dbManager.initialize()\n if (!db.objectStoreNames.contains(this.STORE_NAME)) {\n throw new Error(`Store \"${this.STORE_NAME}\" not found after initialization`)\n }\n\n // Инициализируем middleware\n this.initializeMiddlewares()\n\n // Инициализируем с middleware\n await this.initializeWithMiddlewares()\n\n this.logger?.debug(`IndexedDB storage \"${this.STORE_NAME}\" initialized successfully`)\n return this\n } catch (error) {\n this.logger?.error(`Ошибка инициализации IndexedDB \"${this.name}\"`, { error })\n throw error\n }\n }\n\n static async createStorages<S extends Record<string, any>>(\n dbName: string,\n configs: {\n [K in keyof S]: {\n name: string\n initialState?: S[K]\n middlewares?: ConfigureAsyncMiddlewares\n pluginExecutor?: IAsyncPluginExecutor\n eventEmitter?: IEventEmitter\n }\n },\n logger?: ILogger,\n ): Promise<{ [K in keyof S]: IndexedDBStorage<S[K]> }> {\n // Используем единый IndexedDBManager (версия определяется автоматически)\n const dbManager = IndexedDBManager.getInstance(dbName, 1, logger)\n\n // Получаем имена всех хранилищ, которые нужно создать\n const storeNames = Object.values(configs).map((config) => config.name)\n\n // Предварительно создаем все хранилища в рамках одной операции\n await dbManager.ensureStoresExist(storeNames)\n\n // Создаем и инициализируем все хранилища\n const result: Record<string, IndexedDBStorage<any>> = {}\n\n for (const [key, config] of Object.entries(configs)) {\n const storage = new IndexedDBStorage(\n {\n ...config,\n options: { dbName },\n },\n config.pluginExecutor,\n config.eventEmitter,\n logger,\n )\n\n // Инициализируем хранилище\n result[key] = await storage.initialize()\n }\n\n return result as { [K in keyof S]: IndexedDBStorage<S[K]> }\n }\n\n // ─── IndexedDB-specific API ────────────────────────────────────────────────\n\n /**\n * Выполняет операцию в рамках IDB-транзакции.\n * Обёртка для низкоуровневого IDB transaction API.\n *\n * @param mode - Режим транзакции ('readonly' | 'readwrite')\n * @param fn - Callback, получающий IDBObjectStore. Возвращает результат операции.\n * @returns Promise с результатом callback-а\n */\n async transaction<R>(mode: IDBTransactionMode, fn: (store: IDBObjectStore) => IDBRequest<R> | R): Promise<R> {\n const store = await this.getObjectStore(mode)\n\n return new Promise<R>((resolve, reject) => {\n const tx = store.transaction\n\n tx.onerror = () => reject(tx.error)\n tx.onabort = () => reject(tx.error || new Error('Transaction aborted'))\n\n try {\n const result = fn(store)\n\n if (result instanceof IDBRequest) {\n result.onsuccess = () => resolve(result.result)\n result.onerror = () => reject(result.error)\n } else {\n // Sync result — resolve when transaction completes\n tx.oncomplete = () => resolve(result)\n }\n } catch (error) {\n reject(error)\n }\n })\n }\n\n /**\n * Текущая версия базы данных.\n */\n get dbVersion(): number {\n return this.dbManager.getCurrentVersion()\n }\n\n /**\n * Имя базы данных.\n */\n get dbName(): string {\n return this.DB_NAME\n }\n\n /**\n * Имя object store в IndexedDB.\n */\n get storeName(): string {\n return this.STORE_NAME\n }\n\n // ─── Private helpers ───────────────────────────────────────────────────────\n\n private async getTransaction(mode: IDBTransactionMode = 'readonly'): Promise<IDBTransaction> {\n try {\n // Ensure database is open and our store exists\n const db = await this.dbManager.ensureStoreExists(this.STORE_NAME)\n\n // Проверяем существование хранилища перед созданием транзакции\n if (!db.objectStoreNames.contains(this.STORE_NAME)) {\n // Попытка исправить проблему - закрываем и снова открываем\n this.logger?.warn(`Object store \"${this.STORE_NAME}\" not found, attempting to repair`)\n\n db.close()\n this.dbManager.closeDatabase()\n\n // Пробуем заново создать хранилище с инкрементом версии\n const newDb = await this.dbManager.ensureStoreExists(this.STORE_NAME)\n\n if (!newDb.objectStoreNames.contains(this.STORE_NAME)) {\n throw new Error(`Object store \"${this.STORE_NAME}\" still doesn't exist after repair attempt`)\n }\n\n return newDb.transaction(this.STORE_NAME, mode)\n }\n\n return db.transaction(this.STORE_NAME, mode)\n } catch (error) {\n this.logger?.error(`Failed to create transaction for store \"${this.STORE_NAME}\"`, { error })\n throw error\n }\n }\n\n private async getObjectStore(mode: IDBTransactionMode = 'readonly'): Promise<IDBObjectStore> {\n const transaction = await this.getTransaction(mode)\n return transaction.objectStore(this.STORE_NAME)\n }\n\n protected async doGet(key: StorageKeyType): Promise<any> {\n const store = await this.getObjectStore()\n\n // Для пустого ключа возвращаем все состояние\n if (key === '') {\n return new Promise((resolve, reject) => {\n const request = store.getAll()\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n const allValues = request.result\n const allKeys = store.getAllKeys()\n\n allKeys.onsuccess = () => {\n const state = allKeys.result.reduce(\n (acc, k, index) => {\n if (k !== 'root') {\n acc[k as string] = allValues[index]\n }\n return acc\n },\n {} as Record<string, any>,\n )\n resolve(state)\n }\n allKeys.onerror = () => reject(allKeys.error)\n }\n })\n }\n\n // Проверяем, является ли ключ \"сырым\"\n if (key instanceof StorageKey && key.isUnparseable()) {\n return new Promise((resolve, reject) => {\n const request = store.get(key.valueOf())\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(request.result)\n })\n }\n\n // Для вложенного пути\n const parts = parsePath(key)\n if (parts.length > 1) {\n const rootKey = parts[0]\n return new Promise((resolve, reject) => {\n const request = store.get(rootKey)\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n const rootValue = request.result\n if (!rootValue) {\n resolve(undefined)\n return\n }\n const value = getValueByPath(rootValue, parts.slice(1).join('.'))\n resolve(value)\n }\n })\n }\n\n // Для простого ключа\n return new Promise((resolve, reject) => {\n const request = store.get(parts[0])\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(request.result)\n })\n }\n\n protected async doSet(key: StorageKeyType, value: any): Promise<void> {\n // Для пустого ключа устанавливаем все состояние\n if (key === '') {\n const store = await this.getObjectStore('readwrite')\n return new Promise((resolve, reject) => {\n const tx = store.transaction\n\n tx.oncomplete = () => {\n resolve()\n }\n\n tx.onerror = () => {\n reject(tx.error)\n }\n\n const clearRequest = store.clear()\n\n clearRequest.onsuccess = () => {\n const entries = Object.entries(value)\n for (const [entryKey, entryValue] of entries) {\n store.put(entryValue, entryKey)\n }\n }\n\n clearRequest.onerror = () => {\n reject(clearRequest.error)\n }\n })\n }\n\n const store = await this.getObjectStore('readwrite')\n\n // Для \"сырого\" ключа\n if (key instanceof StorageKey && key.isUnparseable()) {\n await this.putValueInStore(store, key.valueOf(), value)\n return\n }\n\n // Для вложенного пути\n const parts = parsePath(key)\n if (parts.length > 1) {\n const rootKey = parts[0]\n return new Promise((resolve, reject) => {\n const request = store.get(rootKey)\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n const rootValue = request.result || {}\n const updatedValue = setValueByPath(rootValue, parts.slice(1).join('.'), value)\n const putRequest = store.put(updatedValue, rootKey)\n putRequest.onerror = () => reject(putRequest.error)\n putRequest.onsuccess = () => resolve()\n }\n })\n }\n\n // Для простого ключа\n await this.putValueInStore(store, parts[0], value)\n }\n\n private async putValueInStore(store: IDBObjectStore, key: StorageKeyType, value: any): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = store.put(value, key.valueOf())\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve()\n })\n }\n\n protected async doUpdate(updates: Array<{ key: string | StorageKey; value: any }>): Promise<void> {\n // Группируем обновления\n const updatesByRoot = new Map<string, Array<{ path: string[]; value: any }>>()\n const rawUpdates: Array<{ key: string; value: any }> = []\n\n // Разделяем обновления на \"сырые\" и обычные\n for (const { key, value } of updates) {\n if (key instanceof StorageKey && key.isUnparseable()) {\n rawUpdates.push({ key: key.valueOf(), value })\n continue\n }\n\n const parts = parsePath(key)\n const rootKey = parts[0]\n const path = parts.slice(1)\n\n if (!updatesByRoot.has(rootKey)) {\n updatesByRoot.set(rootKey, [])\n }\n updatesByRoot.get(rootKey)!.push({ path, value })\n }\n\n // Одна транзакция на весь doUpdate — атомарность\n const transaction = await this.getTransaction('readwrite')\n const store = transaction.objectStore(this.STORE_NAME)\n\n return new Promise<void>((resolve, reject) => {\n transaction.oncomplete = () => resolve()\n transaction.onerror = () => {\n this.logger?.error('Error during update:', { error: transaction.error })\n reject(transaction.error)\n }\n transaction.onabort = () => {\n this.logger?.error('Update transaction aborted:', { error: transaction.error })\n reject(transaction.error || new Error('Transaction aborted'))\n }\n\n // Обрабатываем \"сырые\" обновления\n for (const { key, value } of rawUpdates) {\n store.put(value, key)\n }\n\n // Обрабатываем сгруппированные обновления\n // Для каждого rootKey: читаем текущее значение, применяем все path-обновления, записываем обратно\n const rootKeys = Array.from(updatesByRoot.keys())\n\n if (rootKeys.length === 0) {\n // Нет сгруппированных обновлений — транзакция завершится сама\n return\n }\n\n for (const rootKey of rootKeys) {\n const getRequest = store.get(rootKey)\n\n getRequest.onsuccess = () => {\n const rootValue = getRequest.result || {}\n let updatedValue = { ...rootValue }\n const pathUpdates = updatesByRoot.get(rootKey)!\n\n for (const { path, value } of pathUpdates) {\n if (path.length === 0) {\n updatedValue = value\n } else {\n updatedValue = setValueByPath(updatedValue, path.join('.'), value)\n }\n }\n\n store.put(updatedValue, rootKey)\n }\n }\n })\n }\n\n protected async doDelete(key: StorageKeyType): Promise<boolean> {\n const store = await this.getObjectStore('readwrite')\n\n // Для \"сырого\" ключа\n if (key instanceof StorageKey && key.isUnparseable()) {\n return new Promise((resolve, reject) => {\n const request = store.delete(key.valueOf())\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(true)\n })\n }\n\n const parts = parsePath(key)\n\n // Для простого ключа\n if (parts.length === 1) {\n return new Promise((resolve, reject) => {\n const request = store.delete(parts[0])\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(true)\n })\n }\n\n // Для вложенного пути\n const rootKey = parts[0]\n return new Promise((resolve, reject) => {\n const getRequest = store.get(rootKey)\n getRequest.onerror = () => reject(getRequest.error)\n getRequest.onsuccess = () => {\n const rootValue = getRequest.result\n if (!rootValue) {\n resolve(false)\n return\n }\n\n const parent = getValueByPath(rootValue, parts.slice(0, -1).join('.'))\n const lastKey = parts[parts.length - 1]\n\n if (!parent || !(lastKey in parent)) {\n resolve(false)\n return\n }\n\n if (Array.isArray(parent)) {\n const index = parseInt(lastKey, 10)\n if (!isNaN(index)) {\n parent.splice(index, 1)\n } else {\n // @ts-ignore\n delete parent[lastKey]\n }\n } else {\n delete parent[lastKey]\n }\n\n const putRequest = store.put(rootValue, rootKey)\n putRequest.onerror = () => reject(putRequest.error)\n putRequest.onsuccess = () => resolve(true)\n }\n })\n }\n\n protected async doClear(): Promise<void> {\n const store = await this.getObjectStore('readwrite')\n return new Promise((resolve, reject) => {\n const request = store.clear()\n request.onsuccess = () => resolve()\n request.onerror = () => reject(request.error)\n })\n }\n\n protected async doKeys(): Promise<string[]> {\n const store = await this.getObjectStore()\n const request = store.getAllKeys()\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n resolve(request.result as string[])\n }\n request.onerror = () => reject(request.error)\n })\n }\n\n protected async doHas(key: StorageKeyType): Promise<boolean> {\n const value = await this.doGet(key)\n return value !== undefined\n }\n\n /**\n * Override performCleanup: persistent storage should NOT clear data on destroy.\n * Only clean up middleware and runtime resources, not persisted data.\n */\n protected async performCleanup(): Promise<void> {\n await this.pluginExecutor?.executeOnClear()\n await this.doDestroy()\n }\n\n protected async doDestroy(): Promise<void> {\n // Persistent storage: do NOT clear data on destroy.\n // Only release runtime resources. Data persists across component lifecycles.\n }\n}\n"],"names":["SingletonMixin","StorageKey","AsyncBaseStorage","getValueByPath","parsePath","setValueByPath","IndexedDBManager","Map","Set","dbName","dbVersion","logger","instance","currentVersion","Promise","resolve","request","indexedDB","version","storeName","Array","newDb","newStores","reject","Error","i","event","db","storeNames","missingStores","name","IndexedDBStorage","config","pluginExecutor","eventEmitter","finalConfig","error","configs","dbManager","Object","result","key","storage","mode","fn","store","tx","IDBRequest","transaction","allValues","allKeys","state","acc","k","index","parts","rootKey","rootValue","undefined","value","clearRequest","entries","entryKey","entryValue","updatedValue","putRequest","updates","updatesByRoot","rawUpdates","path","rootKeys","getRequest","pathUpdates","parent","lastKey","parseInt","isNaN"],"mappings":"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"}
1
+ {"version":3,"file":"core/storage/adapters/indexed-DB.service.js","sources":["../../../../src/core/storage/adapters/indexed-DB.service.ts"],"sourcesContent":["import { IAsyncPluginExecutor } from '../modules/plugin/plugin.interface'\nimport { SingletonMixin } from '../modules/singleton/mixin.util'\nimport { ConfigureAsyncMiddlewares, IEventEmitter, ILogger, IndexedDBStorageConfig, StorageType } from '../storage.interface'\nimport { StorageKey, StorageKeyType } from '../utils/storage-key'\nimport { AsyncBaseStorage } from './async-base-storage.service'\nimport { getValueByPath, parsePath, setValueByPath } from './path.utils'\n\nexport interface IndexedDBConfig {\n dbName?: string\n}\n\n// Управляет соединением с базой данных\nexport class IndexedDBManager {\n private static instances = new Map<string, IndexedDBManager>()\n private db: IDBDatabase | null = null\n private initPromise: Promise<IDBDatabase> | null = null\n private storeNames: Set<string> = new Set()\n private dbVersion: number\n // Последовательная очередь операций изменения схемы (создание сторов).\n // На одну БД (singleton по dbName) обычно инициализируется сразу несколько\n // ApiClient-ов (comments, posts, reactions, ...). Без сериализации их\n // ensureStoreExists перекрывались: один обнулял this.db между close() и\n // переоткрытием, второй в этот момент читал this.db.objectStoreNames → null.\n private opQueue: Promise<unknown> = Promise.resolve()\n\n private constructor(\n private readonly dbName: string,\n dbVersion: number,\n private readonly logger?: ILogger,\n ) {\n this.dbVersion = dbVersion\n }\n\n static getInstance(dbName: string, dbVersion: number = 1, logger?: ILogger): IndexedDBManager {\n if (!IndexedDBManager.instances.has(dbName)) {\n IndexedDBManager.instances.set(dbName, new IndexedDBManager(dbName, dbVersion, logger))\n }\n\n const instance = IndexedDBManager.instances.get(dbName)!\n\n // Update version if higher version is requested\n if (dbVersion > instance.dbVersion) {\n instance.dbVersion = dbVersion\n }\n\n return instance\n }\n\n async initialize(): Promise<IDBDatabase> {\n if (this.db) {\n return this.db\n }\n\n if (!this.initPromise) {\n this.initPromise = this.autoDetectAndOpen()\n }\n\n return this.initPromise\n }\n\n private async autoDetectAndOpen(): Promise<IDBDatabase> {\n // Auto-detect the current DB version to avoid VersionError\n const currentVersion = await this.detectCurrentVersion()\n if (currentVersion > this.dbVersion) {\n this.logger?.debug(`Auto-detected higher DB version: ${currentVersion} (requested: ${this.dbVersion})`)\n this.dbVersion = currentVersion\n }\n return this.openDatabase()\n }\n\n private detectCurrentVersion(): Promise<number> {\n return new Promise<number>((resolve) => {\n try {\n const request = indexedDB.open(this.dbName)\n request.onsuccess = () => {\n const version = request.result.version\n request.result.close()\n resolve(version)\n }\n request.onerror = () => resolve(0)\n } catch {\n resolve(0)\n }\n })\n }\n\n async ensureStoreExists(storeName: string): Promise<IDBDatabase> {\n return this.enqueue(() => this.ensureStoresInternal([storeName]))\n }\n\n /**\n * Ставит операцию изменения схемы в последовательную очередь, чтобы создания\n * сторов на одной БД не перекрывались между await-точками. Очередь не должна\n * \"застревать\" из-за упавшей операции — ошибку прокидываем вызывающему, но\n * следующий элемент очереди стартует независимо от результата предыдущего.\n */\n private enqueue<R>(operation: () => Promise<R>): Promise<R> {\n const result = this.opQueue.then(operation, operation)\n this.opQueue = result.then(\n () => undefined,\n () => undefined,\n )\n return result\n }\n\n /**\n * Идемпотентно гарантирует наличие переданных сторов. Никогда не обращается к\n * this.db после await — работает с локальной ссылкой, возвращённой initialize()\n * / openDatabase(), поэтому параллельное обнуление this.db (другим клиентом\n * или onversionchange) не приводит к чтению свойств у null.\n */\n private async ensureStoresInternal(storeNames: string[]): Promise<IDBDatabase> {\n let db = await this.initialize()\n\n const missingStores = storeNames.filter((name) => !db.objectStoreNames.contains(name))\n if (missingStores.length === 0) {\n for (const name of storeNames) this.storeNames.add(name)\n return db\n }\n\n this.logger?.debug(`Создание недостающих хранилищ: ${missingStores.join(', ')}`, {\n dbName: this.dbName,\n currentStores: Array.from(db.objectStoreNames),\n })\n\n db.close()\n this.db = null\n\n this.dbVersion++\n this.initPromise = this.openDatabase(missingStores)\n db = await this.initPromise\n\n for (const name of storeNames) this.storeNames.add(name)\n return db\n }\n\n private async openDatabase(newStores: string[] = []): Promise<IDBDatabase> {\n return new Promise<IDBDatabase>((resolve, reject) => {\n this.logger?.debug(`Opening database \"${this.dbName}\" with version ${this.dbVersion}`)\n\n const request = indexedDB.open(this.dbName, this.dbVersion)\n\n request.onerror = () => {\n this.logger?.error(`Failed to open database \"${this.dbName}\"`, { error: request.error })\n reject(request.error)\n }\n\n request.onblocked = () => {\n this.logger?.warn(`Database \"${this.dbName}\" upgrade blocked by another connection. Close other tabs or connections.`)\n reject(new Error(`Database \"${this.dbName}\" upgrade blocked — close other tabs using this database`))\n }\n\n request.onsuccess = () => {\n this.db = request.result\n\n // Auto-close when another connection requests a version upgrade\n this.db.onversionchange = () => {\n this.db?.close()\n this.db = null\n this.initPromise = null\n }\n\n // Add existing stores to our set\n for (let i = 0; i < this.db.objectStoreNames.length; i++) {\n this.storeNames.add(this.db.objectStoreNames[i])\n }\n\n this.logger?.debug(`Database \"${this.dbName}\" opened successfully`, {\n version: this.db.version,\n stores: Array.from(this.db.objectStoreNames),\n })\n\n resolve(this.db)\n }\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result\n this.logger?.debug(`Upgrading database \"${this.dbName}\" to version ${this.dbVersion}`)\n\n // Create new stores that don't exist yet\n for (const storeName of newStores) {\n if (!db.objectStoreNames.contains(storeName)) {\n this.logger?.debug(`Creating store \"${storeName}\"`)\n db.createObjectStore(storeName)\n }\n }\n }\n })\n }\n\n closeDatabase(): void {\n if (this.db) {\n this.db.close()\n this.db = null\n this.initPromise = null\n }\n }\n\n async deleteDatabase(): Promise<void> {\n this.closeDatabase()\n\n return new Promise<void>((resolve, reject) => {\n const request = indexedDB.deleteDatabase(this.dbName)\n request.onsuccess = () => {\n this.logger?.debug(`Database \"${this.dbName}\" deleted successfully`)\n IndexedDBManager.instances.delete(this.dbName)\n this.storeNames.clear()\n resolve()\n }\n request.onerror = () => {\n this.logger?.error(`Failed to delete database \"${this.dbName}\"`, { error: request.error })\n reject(request.error)\n }\n })\n }\n async ensureStoresExist(storeNames: string[]): Promise<IDBDatabase> {\n return this.enqueue(() => this.ensureStoresInternal(storeNames))\n }\n\n // Метод для получения текущей версии\n getCurrentVersion(): number {\n return this.dbVersion\n }\n}\n\nexport class IndexedDBStorage<T extends Record<string, any>> extends AsyncBaseStorage<T> {\n protected static readonly STORAGE_TYPE: StorageType = 'indexedDB'\n readonly type: StorageType = 'indexedDB'\n\n private readonly DB_NAME: string\n private readonly STORE_NAME: string\n private dbManager: IndexedDBManager\n\n constructor(config: IndexedDBStorageConfig<T>, pluginExecutor?: IAsyncPluginExecutor, eventEmitter?: IEventEmitter, logger?: ILogger) {\n super(config, pluginExecutor, eventEmitter, logger)\n\n this.DB_NAME = config.options?.dbName || 'app_storage'\n this.STORE_NAME = config.name\n\n // Get database manager instance (version is auto-detected internally)\n this.dbManager = IndexedDBManager.getInstance(this.DB_NAME, 1, logger)\n }\n\n static create<T extends Record<string, any>>(\n config: IndexedDBStorageConfig,\n pluginExecutor?: IAsyncPluginExecutor,\n eventEmitter?: IEventEmitter,\n logger?: ILogger,\n ): IndexedDBStorage<T> {\n return SingletonMixin.handleSingletonCreation(\n config,\n this.STORAGE_TYPE,\n (finalConfig) => new IndexedDBStorage<T>(finalConfig as IndexedDBStorageConfig<T>, pluginExecutor, eventEmitter, logger),\n logger,\n )\n }\n\n protected async doInitialize(): Promise<this> {\n try {\n this.logger?.debug(`Initializing IndexedDB storage \"${this.STORE_NAME}\"`)\n\n // Создаем store в базе данных\n await this.dbManager.ensureStoreExists(this.STORE_NAME)\n\n // Проверяем, что хранилище доступно\n const db = await this.dbManager.initialize()\n if (!db.objectStoreNames.contains(this.STORE_NAME)) {\n throw new Error(`Store \"${this.STORE_NAME}\" not found after initialization`)\n }\n\n // Инициализируем middleware\n this.initializeMiddlewares()\n\n // Инициализируем с middleware\n await this.initializeWithMiddlewares()\n\n this.logger?.debug(`IndexedDB storage \"${this.STORE_NAME}\" initialized successfully`)\n return this\n } catch (error) {\n this.logger?.error(`Ошибка инициализации IndexedDB \"${this.name}\"`, { error })\n throw error\n }\n }\n\n static async createStorages<S extends Record<string, any>>(\n dbName: string,\n configs: {\n [K in keyof S]: {\n name: string\n initialState?: S[K]\n middlewares?: ConfigureAsyncMiddlewares\n pluginExecutor?: IAsyncPluginExecutor\n eventEmitter?: IEventEmitter\n }\n },\n logger?: ILogger,\n ): Promise<{ [K in keyof S]: IndexedDBStorage<S[K]> }> {\n // Используем единый IndexedDBManager (версия определяется автоматически)\n const dbManager = IndexedDBManager.getInstance(dbName, 1, logger)\n\n // Получаем имена всех хранилищ, которые нужно создать\n const storeNames = Object.values(configs).map((config) => config.name)\n\n // Предварительно создаем все хранилища в рамках одной операции\n await dbManager.ensureStoresExist(storeNames)\n\n // Создаем и инициализируем все хранилища\n const result: Record<string, IndexedDBStorage<any>> = {}\n\n for (const [key, config] of Object.entries(configs)) {\n const storage = new IndexedDBStorage(\n {\n ...config,\n options: { dbName },\n },\n config.pluginExecutor,\n config.eventEmitter,\n logger,\n )\n\n // Инициализируем хранилище\n result[key] = await storage.initialize()\n }\n\n return result as { [K in keyof S]: IndexedDBStorage<S[K]> }\n }\n\n // ─── IndexedDB-specific API ────────────────────────────────────────────────\n\n /**\n * Выполняет операцию в рамках IDB-транзакции.\n * Обёртка для низкоуровневого IDB transaction API.\n *\n * @param mode - Режим транзакции ('readonly' | 'readwrite')\n * @param fn - Callback, получающий IDBObjectStore. Возвращает результат операции.\n * @returns Promise с результатом callback-а\n */\n async transaction<R>(mode: IDBTransactionMode, fn: (store: IDBObjectStore) => IDBRequest<R> | R): Promise<R> {\n const store = await this.getObjectStore(mode)\n\n return new Promise<R>((resolve, reject) => {\n const tx = store.transaction\n\n tx.onerror = () => reject(tx.error)\n tx.onabort = () => reject(tx.error || new Error('Transaction aborted'))\n\n try {\n const result = fn(store)\n\n if (result instanceof IDBRequest) {\n result.onsuccess = () => resolve(result.result)\n result.onerror = () => reject(result.error)\n } else {\n // Sync result — resolve when transaction completes\n tx.oncomplete = () => resolve(result)\n }\n } catch (error) {\n reject(error)\n }\n })\n }\n\n /**\n * Текущая версия базы данных.\n */\n get dbVersion(): number {\n return this.dbManager.getCurrentVersion()\n }\n\n /**\n * Имя базы данных.\n */\n get dbName(): string {\n return this.DB_NAME\n }\n\n /**\n * Имя object store в IndexedDB.\n */\n get storeName(): string {\n return this.STORE_NAME\n }\n\n // ─── Private helpers ───────────────────────────────────────────────────────\n\n private async getTransaction(mode: IDBTransactionMode = 'readonly'): Promise<IDBTransaction> {\n try {\n // Ensure database is open and our store exists\n const db = await this.dbManager.ensureStoreExists(this.STORE_NAME)\n\n // Проверяем существование хранилища перед созданием транзакции\n if (!db.objectStoreNames.contains(this.STORE_NAME)) {\n // Попытка исправить проблему - закрываем и снова открываем\n this.logger?.warn(`Object store \"${this.STORE_NAME}\" not found, attempting to repair`)\n\n db.close()\n this.dbManager.closeDatabase()\n\n // Пробуем заново создать хранилище с инкрементом версии\n const newDb = await this.dbManager.ensureStoreExists(this.STORE_NAME)\n\n if (!newDb.objectStoreNames.contains(this.STORE_NAME)) {\n throw new Error(`Object store \"${this.STORE_NAME}\" still doesn't exist after repair attempt`)\n }\n\n return newDb.transaction(this.STORE_NAME, mode)\n }\n\n return db.transaction(this.STORE_NAME, mode)\n } catch (error) {\n this.logger?.error(`Failed to create transaction for store \"${this.STORE_NAME}\"`, { error })\n throw error\n }\n }\n\n private async getObjectStore(mode: IDBTransactionMode = 'readonly'): Promise<IDBObjectStore> {\n const transaction = await this.getTransaction(mode)\n return transaction.objectStore(this.STORE_NAME)\n }\n\n protected async doGet(key: StorageKeyType): Promise<any> {\n const store = await this.getObjectStore()\n\n // Для пустого ключа возвращаем все состояние\n if (key === '') {\n return new Promise((resolve, reject) => {\n const request = store.getAll()\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n const allValues = request.result\n const allKeys = store.getAllKeys()\n\n allKeys.onsuccess = () => {\n const state = allKeys.result.reduce(\n (acc, k, index) => {\n if (k !== 'root') {\n acc[k as string] = allValues[index]\n }\n return acc\n },\n {} as Record<string, any>,\n )\n resolve(state)\n }\n allKeys.onerror = () => reject(allKeys.error)\n }\n })\n }\n\n // Проверяем, является ли ключ \"сырым\"\n if (key instanceof StorageKey && key.isUnparseable()) {\n return new Promise((resolve, reject) => {\n const request = store.get(key.valueOf())\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(request.result)\n })\n }\n\n // Для вложенного пути\n const parts = parsePath(key)\n if (parts.length > 1) {\n const rootKey = parts[0]\n return new Promise((resolve, reject) => {\n const request = store.get(rootKey)\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n const rootValue = request.result\n if (!rootValue) {\n resolve(undefined)\n return\n }\n const value = getValueByPath(rootValue, parts.slice(1).join('.'))\n resolve(value)\n }\n })\n }\n\n // Для простого ключа\n return new Promise((resolve, reject) => {\n const request = store.get(parts[0])\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(request.result)\n })\n }\n\n protected async doSet(key: StorageKeyType, value: any): Promise<void> {\n // Для пустого ключа устанавливаем все состояние\n if (key === '') {\n const store = await this.getObjectStore('readwrite')\n return new Promise((resolve, reject) => {\n const tx = store.transaction\n\n tx.oncomplete = () => {\n resolve()\n }\n\n tx.onerror = () => {\n reject(tx.error)\n }\n\n const clearRequest = store.clear()\n\n clearRequest.onsuccess = () => {\n const entries = Object.entries(value)\n for (const [entryKey, entryValue] of entries) {\n store.put(entryValue, entryKey)\n }\n }\n\n clearRequest.onerror = () => {\n reject(clearRequest.error)\n }\n })\n }\n\n const store = await this.getObjectStore('readwrite')\n\n // Для \"сырого\" ключа\n if (key instanceof StorageKey && key.isUnparseable()) {\n await this.putValueInStore(store, key.valueOf(), value)\n return\n }\n\n // Для вложенного пути\n const parts = parsePath(key)\n if (parts.length > 1) {\n const rootKey = parts[0]\n return new Promise((resolve, reject) => {\n const request = store.get(rootKey)\n request.onerror = () => reject(request.error)\n request.onsuccess = () => {\n const rootValue = request.result || {}\n const updatedValue = setValueByPath(rootValue, parts.slice(1).join('.'), value)\n const putRequest = store.put(updatedValue, rootKey)\n putRequest.onerror = () => reject(putRequest.error)\n putRequest.onsuccess = () => resolve()\n }\n })\n }\n\n // Для простого ключа\n await this.putValueInStore(store, parts[0], value)\n }\n\n private async putValueInStore(store: IDBObjectStore, key: StorageKeyType, value: any): Promise<void> {\n return new Promise((resolve, reject) => {\n const request = store.put(value, key.valueOf())\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve()\n })\n }\n\n protected async doUpdate(updates: Array<{ key: string | StorageKey; value: any }>): Promise<void> {\n // Группируем обновления\n const updatesByRoot = new Map<string, Array<{ path: string[]; value: any }>>()\n const rawUpdates: Array<{ key: string; value: any }> = []\n\n // Разделяем обновления на \"сырые\" и обычные\n for (const { key, value } of updates) {\n if (key instanceof StorageKey && key.isUnparseable()) {\n rawUpdates.push({ key: key.valueOf(), value })\n continue\n }\n\n const parts = parsePath(key)\n const rootKey = parts[0]\n const path = parts.slice(1)\n\n if (!updatesByRoot.has(rootKey)) {\n updatesByRoot.set(rootKey, [])\n }\n updatesByRoot.get(rootKey)!.push({ path, value })\n }\n\n // Одна транзакция на весь doUpdate — атомарность\n const transaction = await this.getTransaction('readwrite')\n const store = transaction.objectStore(this.STORE_NAME)\n\n return new Promise<void>((resolve, reject) => {\n transaction.oncomplete = () => resolve()\n transaction.onerror = () => {\n this.logger?.error('Error during update:', { error: transaction.error })\n reject(transaction.error)\n }\n transaction.onabort = () => {\n this.logger?.error('Update transaction aborted:', { error: transaction.error })\n reject(transaction.error || new Error('Transaction aborted'))\n }\n\n // Обрабатываем \"сырые\" обновления\n for (const { key, value } of rawUpdates) {\n store.put(value, key)\n }\n\n // Обрабатываем сгруппированные обновления\n // Для каждого rootKey: читаем текущее значение, применяем все path-обновления, записываем обратно\n const rootKeys = Array.from(updatesByRoot.keys())\n\n if (rootKeys.length === 0) {\n // Нет сгруппированных обновлений — транзакция завершится сама\n return\n }\n\n for (const rootKey of rootKeys) {\n const getRequest = store.get(rootKey)\n\n getRequest.onsuccess = () => {\n const rootValue = getRequest.result || {}\n let updatedValue = { ...rootValue }\n const pathUpdates = updatesByRoot.get(rootKey)!\n\n for (const { path, value } of pathUpdates) {\n if (path.length === 0) {\n updatedValue = value\n } else {\n updatedValue = setValueByPath(updatedValue, path.join('.'), value)\n }\n }\n\n store.put(updatedValue, rootKey)\n }\n }\n })\n }\n\n protected async doDelete(key: StorageKeyType): Promise<boolean> {\n const store = await this.getObjectStore('readwrite')\n\n // Для \"сырого\" ключа\n if (key instanceof StorageKey && key.isUnparseable()) {\n return new Promise((resolve, reject) => {\n const request = store.delete(key.valueOf())\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(true)\n })\n }\n\n const parts = parsePath(key)\n\n // Для простого ключа\n if (parts.length === 1) {\n return new Promise((resolve, reject) => {\n const request = store.delete(parts[0])\n request.onerror = () => reject(request.error)\n request.onsuccess = () => resolve(true)\n })\n }\n\n // Для вложенного пути\n const rootKey = parts[0]\n return new Promise((resolve, reject) => {\n const getRequest = store.get(rootKey)\n getRequest.onerror = () => reject(getRequest.error)\n getRequest.onsuccess = () => {\n const rootValue = getRequest.result\n if (!rootValue) {\n resolve(false)\n return\n }\n\n const parent = getValueByPath(rootValue, parts.slice(0, -1).join('.'))\n const lastKey = parts[parts.length - 1]\n\n if (!parent || !(lastKey in parent)) {\n resolve(false)\n return\n }\n\n if (Array.isArray(parent)) {\n const index = parseInt(lastKey, 10)\n if (!isNaN(index)) {\n parent.splice(index, 1)\n } else {\n // @ts-ignore\n delete parent[lastKey]\n }\n } else {\n delete parent[lastKey]\n }\n\n const putRequest = store.put(rootValue, rootKey)\n putRequest.onerror = () => reject(putRequest.error)\n putRequest.onsuccess = () => resolve(true)\n }\n })\n }\n\n protected async doClear(): Promise<void> {\n const store = await this.getObjectStore('readwrite')\n return new Promise((resolve, reject) => {\n const request = store.clear()\n request.onsuccess = () => resolve()\n request.onerror = () => reject(request.error)\n })\n }\n\n protected async doKeys(): Promise<string[]> {\n const store = await this.getObjectStore()\n const request = store.getAllKeys()\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n resolve(request.result as string[])\n }\n request.onerror = () => reject(request.error)\n })\n }\n\n protected async doHas(key: StorageKeyType): Promise<boolean> {\n const value = await this.doGet(key)\n return value !== undefined\n }\n\n /**\n * Override performCleanup: persistent storage should NOT clear data on destroy.\n * Only clean up middleware and runtime resources, not persisted data.\n */\n protected async performCleanup(): Promise<void> {\n await this.pluginExecutor?.executeOnClear()\n await this.doDestroy()\n }\n\n protected async doDestroy(): Promise<void> {\n // Persistent storage: do NOT clear data on destroy.\n // Only release runtime resources. Data persists across component lifecycles.\n }\n}\n"],"names":["SingletonMixin","StorageKey","AsyncBaseStorage","getValueByPath","parsePath","setValueByPath","IndexedDBManager","Map","Set","Promise","dbName","dbVersion","logger","instance","currentVersion","resolve","request","indexedDB","version","storeName","operation","result","undefined","storeNames","db","missingStores","name","Array","newStores","reject","Error","i","event","IndexedDBStorage","config","pluginExecutor","eventEmitter","finalConfig","error","configs","dbManager","Object","key","storage","mode","fn","store","tx","IDBRequest","newDb","transaction","allValues","allKeys","state","acc","k","index","parts","rootKey","rootValue","value","clearRequest","entries","entryKey","entryValue","updatedValue","putRequest","updates","updatesByRoot","rawUpdates","path","rootKeys","getRequest","pathUpdates","parent","lastKey","parseInt","isNaN"],"mappings":"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"}
@@ -17,5 +17,20 @@ export declare function useSelector<T>(selector: SelectorAPI<T>, options: UseSel
17
17
  data: T;
18
18
  isLoading: boolean;
19
19
  };
20
+ export declare function useSelector<T>(selector: SelectorAPI<T>, options: UseSelectorOptions<T> & {
21
+ withLoading?: false;
22
+ }): T;
23
+ /**
24
+ * Изоляция ре-рендеров для keyed-map стора (паттерн «equals»). Подписка идёт на
25
+ * ВЕСЬ map (`selector`), но `equals` сравнивает только срез по `key` → компонент
26
+ * ре-рендерится лишь когда меняется его `map[key]` по ссылке. Работает, т.к.
27
+ * иммутабельные мутации по одному ключу не трогают ссылку чужих срезов.
28
+ * Гранулярность живёт в месте вызова, а селекторы остаются плоскими (без фабрик
29
+ * с `Map<key, selector>`).
30
+ *
31
+ * `fallback` ОБЯЗАН быть стабильной ссылкой (module-level константа), иначе
32
+ * `?? fallback` будет давать новый объект каждый тик и рвать нижестоящие `useMemo`.
33
+ */
34
+ export declare function useKeyedSliceSelector<V>(selector: SelectorAPI<Record<string, V>>, key: string, fallback: V): V;
20
35
  export {};
21
36
  //# sourceMappingURL=useSelector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSelector.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useSelector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,UAAU,kBAAkB,CAAC,CAAC;IAC5B,6DAA6D;IAC7D,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAA;IAChC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAC3D,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAA"}
1
+ {"version":3,"file":"useSelector.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/useSelector.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,UAAU,kBAAkB,CAAC,CAAC;IAC5B,6DAA6D;IAC7D,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAA;IAChC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;AAC3D,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAA;AACjJ,wBAAgB,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,KAAK,CAAA;CAAE,GAAG,CAAC,CAAA;AAwDrH;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAK9G"}
@@ -50,7 +50,23 @@ function useSelector(selector, options) {
50
50
  }
51
51
  return value;
52
52
  }
53
+ /**
54
+ * Изоляция ре-рендеров для keyed-map стора (паттерн «equals»). Подписка идёт на
55
+ * ВЕСЬ map (`selector`), но `equals` сравнивает только срез по `key` → компонент
56
+ * ре-рендерится лишь когда меняется его `map[key]` по ссылке. Работает, т.к.
57
+ * иммутабельные мутации по одному ключу не трогают ссылку чужих срезов.
58
+ * Гранулярность живёт в месте вызова, а селекторы остаются плоскими (без фабрик
59
+ * с `Map<key, selector>`).
60
+ *
61
+ * `fallback` ОБЯЗАН быть стабильной ссылкой (module-level константа), иначе
62
+ * `?? fallback` будет давать новый объект каждый тик и рвать нижестоящие `useMemo`.
63
+ */ function useKeyedSliceSelector(selector, key, fallback) {
64
+ const map = useSelector(selector, {
65
+ equals: (a, b)=>a[key] === b[key]
66
+ });
67
+ return map[key] ?? fallback;
68
+ }
53
69
 
54
- export { useSelector };
70
+ export { useKeyedSliceSelector, useSelector };
55
71
 
56
72
  //# sourceMappingURL=useSelector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"react/hooks/useSelector.js","sources":["../../../src/react/hooks/useSelector.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { SelectorAPI } from '../../core'\n\ninterface UseSelectorOptions<T> {\n /** Функция сравнения для предотвращения лишних ререндеров */\n equals?: (a: T, b: T) => boolean\n /** Включать ли статус загрузки в возвращаемый результат */\n withLoading?: boolean\n}\n\n/**\n * Хук для использования селекторов в компонентах React.\n * Использует useSyncExternalStore для корректной работы в Concurrent Mode.\n * Подписывается напрямую через selector.subscribe() — без глобального реестра.\n */\nexport function useSelector<T>(selector: SelectorAPI<T>): T\nexport function useSelector<T>(selector: SelectorAPI<T>, options: UseSelectorOptions<T> & { withLoading: true }): { data: T; isLoading: boolean }\nexport function useSelector<T>(selector: SelectorAPI<T>, options?: UseSelectorOptions<T>): { data: T; isLoading: boolean } | T {\n const equalsRef = useRef(options?.equals)\n\n // Кеш для мемоизации результата getSnapshot (предотвращает лишние ререндеры)\n const cachedRef = useRef<T | undefined>(undefined)\n\n const subscribe = useCallback(\n (onStoreChange: VoidFunction) => {\n return selector.subscribe({\n notify: () => {\n onStoreChange()\n },\n })\n },\n [selector],\n )\n\n const getSnapshot = useCallback((): T => {\n const value = selector.selectSync()\n\n // Если есть пользовательская функция сравнения — мемоизируем\n const equals = equalsRef.current\n if (equals && cachedRef.current !== undefined && equals(cachedRef.current, value)) {\n return cachedRef.current\n }\n\n cachedRef.current = value\n return value\n }, [selector])\n\n const value = useSyncExternalStore(subscribe, getSnapshot)\n\n // Подписка на статус готовности storage (используется только при withLoading)\n const subscribeToStatus = useCallback(\n (onStoreChange: VoidFunction) => {\n return selector.onSourceStatusChange(() => {\n onStoreChange()\n })\n },\n [selector],\n )\n\n const getStatusSnapshot = useCallback((): boolean => {\n return !selector.isSourceReady()\n }, [selector])\n\n const isLoading = useSyncExternalStore(subscribeToStatus, getStatusSnapshot)\n\n if (options?.withLoading) {\n return { data: value, isLoading }\n }\n\n return value\n}\n"],"names":["useCallback","useRef","useSyncExternalStore","useSelector","selector","options","equalsRef","cachedRef","undefined","subscribe","onStoreChange","getSnapshot","value","equals","subscribeToStatus","getStatusSnapshot","isLoading"],"mappings":";;;AAAiE;AAkB1D,SAASG,WAAWA,CAAIC,QAAwB,EAAEC,OAA+B;IACtF,MAAMC,YAAYL,MAAMA,CAACI,SAAS;IAElC,6EAA6E;IAC7E,MAAME,YAAYN,MAAMA,CAAgBO;IAExC,MAAMC,YAAYT,WAAWA,CAC3B,CAACU;QACC,OAAON,SAAS,SAAS,CAAC;YACxB,QAAQ;gBACNM;YACF;QACF;IACF,GACA;QAACN;KAAS;IAGZ,MAAMO,cAAcX,WAAWA,CAAC;QAC9B,MAAMY,QAAQR,SAAS,UAAU;QAEjC,6DAA6D;QAC7D,MAAMS,SAASP,UAAU,OAAO;QAChC,IAAIO,UAAUN,UAAU,OAAO,KAAKC,aAAaK,OAAON,UAAU,OAAO,EAAEK,QAAQ;YACjF,OAAOL,UAAU,OAAO;QAC1B;QAEAA,UAAU,OAAO,GAAGK;QACpB,OAAOA;IACT,GAAG;QAACR;KAAS;IAEb,MAAMQ,QAAQV,oBAAoBA,CAACO,WAAWE;IAE9C,8EAA8E;IAC9E,MAAMG,oBAAoBd,WAAWA,CACnC,CAACU;QACC,OAAON,SAAS,oBAAoB,CAAC;YACnCM;QACF;IACF,GACA;QAACN;KAAS;IAGZ,MAAMW,oBAAoBf,WAAWA,CAAC;QACpC,OAAO,CAACI,SAAS,aAAa;IAChC,GAAG;QAACA;KAAS;IAEb,MAAMY,YAAYd,oBAAoBA,CAACY,mBAAmBC;IAE1D,IAAIV,SAAS,aAAa;QACxB,OAAO;YAAE,MAAMO;YAAOI;QAAU;IAClC;IAEA,OAAOJ;AACT"}
1
+ {"version":3,"file":"react/hooks/useSelector.js","sources":["../../../src/react/hooks/useSelector.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { SelectorAPI } from '../../core'\n\ninterface UseSelectorOptions<T> {\n /** Функция сравнения для предотвращения лишних ререндеров */\n equals?: (a: T, b: T) => boolean\n /** Включать ли статус загрузки в возвращаемый результат */\n withLoading?: boolean\n}\n\n/**\n * Хук для использования селекторов в компонентах React.\n * Использует useSyncExternalStore для корректной работы в Concurrent Mode.\n * Подписывается напрямую через selector.subscribe() — без глобального реестра.\n */\nexport function useSelector<T>(selector: SelectorAPI<T>): T\nexport function useSelector<T>(selector: SelectorAPI<T>, options: UseSelectorOptions<T> & { withLoading: true }): { data: T; isLoading: boolean }\nexport function useSelector<T>(selector: SelectorAPI<T>, options: UseSelectorOptions<T> & { withLoading?: false }): T\nexport function useSelector<T>(selector: SelectorAPI<T>, options?: UseSelectorOptions<T>): { data: T; isLoading: boolean } | T {\n const equalsRef = useRef(options?.equals)\n\n // Кеш для мемоизации результата getSnapshot (предотвращает лишние ререндеры)\n const cachedRef = useRef<T | undefined>(undefined)\n\n const subscribe = useCallback(\n (onStoreChange: VoidFunction) => {\n return selector.subscribe({\n notify: () => {\n onStoreChange()\n },\n })\n },\n [selector],\n )\n\n const getSnapshot = useCallback((): T => {\n const value = selector.selectSync()\n\n // Если есть пользовательская функция сравнения — мемоизируем\n const equals = equalsRef.current\n if (equals && cachedRef.current !== undefined && equals(cachedRef.current, value)) {\n return cachedRef.current\n }\n\n cachedRef.current = value\n return value\n }, [selector])\n\n const value = useSyncExternalStore(subscribe, getSnapshot)\n\n // Подписка на статус готовности storage (используется только при withLoading)\n const subscribeToStatus = useCallback(\n (onStoreChange: VoidFunction) => {\n return selector.onSourceStatusChange(() => {\n onStoreChange()\n })\n },\n [selector],\n )\n\n const getStatusSnapshot = useCallback((): boolean => {\n return !selector.isSourceReady()\n }, [selector])\n\n const isLoading = useSyncExternalStore(subscribeToStatus, getStatusSnapshot)\n\n if (options?.withLoading) {\n return { data: value, isLoading }\n }\n\n return value\n}\n\n/**\n * Изоляция ре-рендеров для keyed-map стора (паттерн «equals»). Подписка идёт на\n * ВЕСЬ map (`selector`), но `equals` сравнивает только срез по `key` → компонент\n * ре-рендерится лишь когда меняется его `map[key]` по ссылке. Работает, т.к.\n * иммутабельные мутации по одному ключу не трогают ссылку чужих срезов.\n * Гранулярность живёт в месте вызова, а селекторы остаются плоскими (без фабрик\n * с `Map<key, selector>`).\n *\n * `fallback` ОБЯЗАН быть стабильной ссылкой (module-level константа), иначе\n * `?? fallback` будет давать новый объект каждый тик и рвать нижестоящие `useMemo`.\n */\nexport function useKeyedSliceSelector<V>(selector: SelectorAPI<Record<string, V>>, key: string, fallback: V): V {\n const map = useSelector(selector, {\n equals: (a, b) => a[key] === b[key],\n })\n return map[key] ?? fallback\n}\n"],"names":["useCallback","useRef","useSyncExternalStore","useSelector","selector","options","equalsRef","cachedRef","undefined","subscribe","onStoreChange","getSnapshot","value","equals","subscribeToStatus","getStatusSnapshot","isLoading","useKeyedSliceSelector","key","fallback","map","a","b"],"mappings":";;;AAAiE;AAmB1D,SAASG,WAAWA,CAAIC,QAAwB,EAAEC,OAA+B;IACtF,MAAMC,YAAYL,MAAMA,CAACI,SAAS;IAElC,6EAA6E;IAC7E,MAAME,YAAYN,MAAMA,CAAgBO;IAExC,MAAMC,YAAYT,WAAWA,CAC3B,CAACU;QACC,OAAON,SAAS,SAAS,CAAC;YACxB,QAAQ;gBACNM;YACF;QACF;IACF,GACA;QAACN;KAAS;IAGZ,MAAMO,cAAcX,WAAWA,CAAC;QAC9B,MAAMY,QAAQR,SAAS,UAAU;QAEjC,6DAA6D;QAC7D,MAAMS,SAASP,UAAU,OAAO;QAChC,IAAIO,UAAUN,UAAU,OAAO,KAAKC,aAAaK,OAAON,UAAU,OAAO,EAAEK,QAAQ;YACjF,OAAOL,UAAU,OAAO;QAC1B;QAEAA,UAAU,OAAO,GAAGK;QACpB,OAAOA;IACT,GAAG;QAACR;KAAS;IAEb,MAAMQ,QAAQV,oBAAoBA,CAACO,WAAWE;IAE9C,8EAA8E;IAC9E,MAAMG,oBAAoBd,WAAWA,CACnC,CAACU;QACC,OAAON,SAAS,oBAAoB,CAAC;YACnCM;QACF;IACF,GACA;QAACN;KAAS;IAGZ,MAAMW,oBAAoBf,WAAWA,CAAC;QACpC,OAAO,CAACI,SAAS,aAAa;IAChC,GAAG;QAACA;KAAS;IAEb,MAAMY,YAAYd,oBAAoBA,CAACY,mBAAmBC;IAE1D,IAAIV,SAAS,aAAa;QACxB,OAAO;YAAE,MAAMO;YAAOI;QAAU;IAClC;IAEA,OAAOJ;AACT;AAEA;;;;;;;;;;CAUC,GACM,SAASK,qBAAqBA,CAAIb,QAAwC,EAAEc,GAAW,EAAEC,QAAW;IACzG,MAAMC,MAAMjB,WAAWA,CAACC,UAAU;QAChC,QAAQ,CAACiB,GAAGC,IAAMD,CAAC,CAACH,IAAI,KAAKI,CAAC,CAACJ,IAAI;IACrC;IACA,OAAOE,GAAG,CAACF,IAAI,IAAIC;AACrB"}