@ryuzaki13/react-foundation-lib 2.23.0 → 2.25.0

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.
Files changed (39) hide show
  1. package/dist/analytical-table/index.js +1 -1
  2. package/dist/chunks/{formatters-D1ob2OkI.js → formatters-DVqfmx3V.js} +288 -278
  3. package/dist/chunks/formatters-DVqfmx3V.js.map +1 -0
  4. package/dist/chunks/{query-client-Gk7lxFz7.js → query-client-Dj_zNE22.js} +243 -154
  5. package/dist/chunks/query-client-Dj_zNE22.js.map +1 -0
  6. package/dist/error-report/index.js +2 -2
  7. package/dist/formatters/formatDurationMinutes.d.ts +7 -0
  8. package/dist/formatters/formatDurationMinutes.d.ts.map +1 -0
  9. package/dist/formatters/index.d.ts +1 -0
  10. package/dist/formatters/index.d.ts.map +1 -1
  11. package/dist/formatters/index.js +5 -5
  12. package/dist/indexed-records/createIndexedRecords.d.ts +8 -0
  13. package/dist/indexed-records/createIndexedRecords.d.ts.map +1 -0
  14. package/dist/indexed-records/getIndexedRecord.d.ts +9 -0
  15. package/dist/indexed-records/getIndexedRecord.d.ts.map +1 -0
  16. package/dist/indexed-records/index.d.ts +4 -0
  17. package/dist/indexed-records/index.d.ts.map +1 -0
  18. package/dist/indexed-records/index.js +22 -0
  19. package/dist/indexed-records/index.js.map +1 -0
  20. package/dist/indexed-records/indexedRecordsTypes.d.ts +12 -0
  21. package/dist/indexed-records/indexedRecordsTypes.d.ts.map +1 -0
  22. package/dist/query-client/createQueryRestoreSchedulerGuard.d.ts +9 -0
  23. package/dist/query-client/createQueryRestoreSchedulerGuard.d.ts.map +1 -0
  24. package/dist/query-client/createQueryRestoreStorage.d.ts +9 -0
  25. package/dist/query-client/createQueryRestoreStorage.d.ts.map +1 -0
  26. package/dist/query-client/index.js +2 -2
  27. package/dist/query-client/persistence.d.ts.map +1 -1
  28. package/dist/query-client/queryMeta.d.ts +13 -0
  29. package/dist/query-client/queryMeta.d.ts.map +1 -1
  30. package/dist/query-client/queryRestoreSchedulerTypes.d.ts +7 -0
  31. package/dist/query-client/queryRestoreSchedulerTypes.d.ts.map +1 -0
  32. package/dist/query-client/queryRestoreStorageTypes.d.ts +13 -0
  33. package/dist/query-client/queryRestoreStorageTypes.d.ts.map +1 -0
  34. package/package.json +5 -1
  35. package/src/formatters/README.mdx +18 -0
  36. package/src/indexed-records/README.md +32 -0
  37. package/src/query-client/README.mdx +55 -7
  38. package/dist/chunks/formatters-D1ob2OkI.js.map +0 -1
  39. package/dist/chunks/query-client-Gk7lxFz7.js.map +0 -1
@@ -475,6 +475,8 @@ Callbacks устанавливаются в `QueryCache` и `MutationCache`. О
475
475
  type AppQueryMeta = {
476
476
  readonly persist?: boolean;
477
477
  readonly sessionScoped?: boolean;
478
+ readonly sessionPersistence?: SessionQueryPersistencePolicy;
479
+ readonly [QUERY_RESTORED_DATA_PARSER]?: QueryRestoredDataParser;
478
480
  } & Record<string, unknown>;
479
481
  ```
480
482
 
@@ -494,6 +496,50 @@ queryOptions({
494
496
  `Record<string, unknown>` разрешает consumer-проекту добавлять собственную metadata. Infrastructure этого модуля читает
495
497
  `persist`, `sessionScoped` и явный `sessionPersistence` contract.
496
498
 
499
+ ### Проверка восстановленного значения
500
+
501
+ `QueryRestoredDataParser` — синхронный callback `(data: unknown) => unknown`.
502
+ Consumer проверяет недоверенную форму своего persisted snapshot и возвращает
503
+ канонические data либо бросает ошибку. Callback задаётся symbol key, а не
504
+ строковым свойством с функцией:
505
+
506
+ ```ts
507
+ import { queryOptions } from "@tanstack/react-query";
508
+ import { createSessionQueryPersistenceMeta, QUERY_RESTORED_DATA_PARSER } from "@ryuzaki13/react-foundation-lib/query-client";
509
+
510
+ queryOptions({
511
+ queryKey: ["dictionary", "scope"],
512
+ queryFn: fetchDictionary,
513
+ meta: {
514
+ ...createSessionQueryPersistenceMeta({ cacheName: "dictionary", maxEntries: 8, refetchOnRestore: false }),
515
+ [QUERY_RESTORED_DATA_PARSER]: parseDictionarySnapshot
516
+ }
517
+ });
518
+ ```
519
+
520
+ Parser вызывается один раз при фактическом чтении пригодной записи из IndexedDB,
521
+ до передачи data в Query. Обычный network read, уже полученные Query data, SSR
522
+ hydration, обход `entries` и garbage collection parser не запускают. Проверка
523
+ network/SSR формы остаётся у соответствующих consumer boundaries.
524
+
525
+ Истёкшие и несовместимые с buster записи удаляет обычный persister без дорогого
526
+ parser. Ошибка parser удаляет только текущий storage key и запускает обычный
527
+ queryFn; ошибка этого network read передаётся Query без подмены. `undefined` и
528
+ Promise-like result не являются допустимым синхронным результатом parser.
529
+
530
+ Parser не должен менять восстановленный объект. Он может вернуть новый
531
+ проверенный неизменяемый снимок; инфраструктура копирует только оболочки
532
+ persisted записи и её state, а записи каталога не обходит и не индексирует.
533
+ Завершённая отмена или смена session scope не разрешает публикацию старого
534
+ restore и не запускает его network fallback.
535
+ Отмена не удаляет рабочую persisted запись. Поздние scheduled timestamp/refetch
536
+ от предыдущего restore не возобновляют отменённую Query и не затрагивают новое
537
+ чтение этой Query, даже если новый ответ сохранил ту же data identity.
538
+
539
+ Symbol metadata сохраняется при `{ ...meta }`. Callback не входит в persisted
540
+ payload и не сериализуется вместе с metadata при SSR. Query options consumer
541
+ вновь подключают его в браузере; symbol key не превращается в строковый API.
542
+
497
543
  ### `persistedQueryMeta`
498
544
 
499
545
  ```ts
@@ -2485,13 +2531,15 @@ Profile tests подтверждают:
2485
2531
 
2486
2532
  ### Query metadata
2487
2533
 
2488
- | Symbol | Вид | Назначение |
2489
- | ----------------------------------- | -------- | -------------------------------------------------------- |
2490
- | `AppQueryMeta` | type | Public/session persistence и consumer metadata. |
2491
- | `SessionQueryPersistencePolicy` | type | Имя и bounded limit session cache. |
2492
- | `persistedQueryMeta` | const | Opt-in public persistence marker. |
2493
- | `sessionScopedQueryMeta` | const | Session reset marker без browser persistence. |
2494
- | `createSessionQueryPersistenceMeta` | function | Явный opt-in bounded persistence текущей server session. |
2534
+ | Symbol | Вид | Назначение |
2535
+ | ----------------------------------- | -------- | ---------------------------------------------------------- |
2536
+ | `AppQueryMeta` | type | Public/session persistence и consumer metadata. |
2537
+ | `SessionQueryPersistencePolicy` | type | Имя и bounded limit session cache. |
2538
+ | `QueryRestoredDataParser` | type | Синхронная проверка restored data перед Query publication. |
2539
+ | `QUERY_RESTORED_DATA_PARSER` | symbol | Несериализуемый opt-in callback key в Query metadata. |
2540
+ | `persistedQueryMeta` | const | Opt-in public persistence marker. |
2541
+ | `sessionScopedQueryMeta` | const | Session reset marker без browser persistence. |
2542
+ | `createSessionQueryPersistenceMeta` | function | Явный opt-in bounded persistence текущей server session. |
2495
2543
 
2496
2544
  ### Persistence project adapter
2497
2545
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"formatters-D1ob2OkI.js","names":[],"sources":["../../src/formatters/strings/formatRussianPlural.ts","../../src/formatters/strings/startsWithIgnoringZeros.ts","../../src/formatters/strings/stripInnerSpaces.ts","../../src/formatters/strings/stripLeadingZeros.ts","../../src/formatters/strings/truncateText.ts","../../src/formatters/boolean/toAbapBoolean.ts","../../src/formatters/formatName.ts","../../src/formatters/number/compact.ts","../../src/formatters/number/isZeroValue.ts","../../src/formatters/number/formatNumber.ts","../../src/formatters/formatFileSize.ts","../../src/formatters/formatPhone.ts","../../src/formatters/date/calendarDate.ts","../../src/formatters/date/calendarPeriod.ts","../../src/formatters/date/dateRange.ts","../../src/formatters/date/educationDate.ts","../../src/formatters/date/formatInstantDateTime.ts","../../src/formatters/date/relativeRanges.ts","../../src/formatters/normalizeLeadingZeros.ts","../../src/formatters/pipeline/dependencies.ts","../../src/formatters/valueState/valueStateRegistry.ts","../../src/formatters/valueState/fixedValueStateResolver.ts","../../src/formatters/valueState/thresholdValueStateResolver.ts","../../src/formatters/pipeline/execute.ts","../../src/formatters/pipeline/normalize.ts","../../src/formatters/pipeline/definitions.ts","../../src/formatters/pipeline/registry.ts","../../src/formatters/pipeline/rekey.ts","../../src/formatters/pipeline/runtime.ts","../../src/formatters/valueState/presentation.ts","../../src/formatters/valueState/resolveValueStateClassName.ts"],"sourcesContent":["/**\n * Полные формы русского существительного для разных количественных категорий.\n * Полные слова позволяют корректно работать не только с окончаниями, но и с\n * формами, у которых меняется основа, например «ребёнок» и «детей».\n */\nexport type RussianPluralForms = {\n\treadonly one: string;\n\treadonly few: string;\n\treadonly many: string;\n\treadonly other?: string;\n};\n\nconst russianPluralRules = new Intl.PluralRules(\"ru-RU\");\n\n/**\n * Выбирает форму русского существительного по количеству.\n *\n * Категория `other` используется для дробных чисел. Если отдельная форма для\n * неё не передана, применяется форма `few`, например «1,5 элемента».\n */\nexport function selectRussianPluralForm(value: number, forms: RussianPluralForms): string {\n\tif (!Number.isFinite(value)) {\n\t\tthrow new RangeError(\"Количество должно быть конечным числом.\");\n\t}\n\n\tswitch (russianPluralRules.select(value)) {\n\t\tcase \"one\":\n\t\t\treturn forms.one;\n\t\tcase \"few\":\n\t\t\treturn forms.few;\n\t\tcase \"many\":\n\t\t\treturn forms.many;\n\t\tdefault:\n\t\t\treturn forms.other ?? forms.few;\n\t}\n}\n\n/**\n * Форматирует количество вместе с подходящей формой русского существительного.\n * Число сохраняется без округления; для отдельного форматирования числа следует\n * использовать `selectRussianPluralForm` вместе с нужным числовым форматтером.\n */\nexport function formatRussianPlural(value: number, forms: RussianPluralForms): string {\n\treturn `${value} ${selectRussianPluralForm(value, forms)}`;\n}\n","export function startsWithIgnoringZeros(value: string, searchText: string): boolean {\n\tconst cleanValue = (value || \"\").toLowerCase().replace(/^0+/, \"\");\n\tconst cleanText = (searchText || \"\").toLowerCase().replace(/^0+/, \"\");\n\treturn cleanValue.startsWith(cleanText);\n}\n","/**\n * Удаляет пробелы и NBSP/узкие NBSP внутри строки.\n * (для случая \"1 234\" или \"1\\u00A0234\").\n */\nexport function stripInnerSpaces(s: string): string {\n\tlet out = \"\";\n\tlet changed = false;\n\n\tfor (let i = 0; i < s.length; i++) {\n\t\tconst c = s.charCodeAt(i);\n\t\t// обычный пробел, NBSP, NNBSP\n\t\tif (c === 32 || c === 160 || c === 8239) {\n\t\t\tchanged = true;\n\t\t\tcontinue;\n\t\t}\n\t\tout += s[i];\n\t}\n\n\treturn changed ? out : s;\n}\n","/**\n * Удаляет ведущие нули из строки.\n * - \"00123\" -> \"123\"\n * - \"0000\" -> \"0\"\n * - \"-0012\" -> \"-12\"\n * - \"\" -> \"0\"\n * - \"abc\" -> \"abc\"\n */\nexport function stripLeadingZeros(input: string): string {\n\tif (input == null) return \"\";\n\n\t// обрезаем пробелы по краям\n\tconst str = input.trim();\n\tif (str === \"\") return \"0\";\n\n\t// проверяем отрицательное число\n\tconst isNegative = str.startsWith(\"-\");\n\tconst core = isNegative ? str.slice(1) : str;\n\n\t// если core не число — вернём как есть\n\tif (!/^\\d+$/.test(core)) {\n\t\treturn str;\n\t}\n\n\t// удаляем ведущие нули\n\tconst normalized = core.replace(/^0+/, \"\");\n\tconst result = normalized === \"\" ? \"0\" : normalized;\n\n\treturn isNegative ? `-${result}` : result;\n}\n","import { normalizeText } from \"./normalizeText\";\n\nexport function truncateText(value: string | null | undefined, maxLength: number) {\n\tconst normalized = normalizeText(value);\n\tif (!normalized) return undefined;\n\n\treturn normalized.length > maxLength ? `${normalized.slice(0, maxLength)}...` : normalized;\n}\n","import { AbapBoolean } from \"../../types\";\n\nexport function toAbapBoolean(value: unknown): AbapBoolean {\n\tif (typeof value === \"string\") {\n\t\tvalue = value !== \"0\";\n\t}\n\treturn value ? \"X\" : \" \";\n}\n","/**\n * Форматирует ФИО в формат \"Фамилия Имя Отчество\"\n * @param fullName Входная строка с ФИО в любом формате\n * @returns Отформатированная строка в формате \"Иванов Иван Иванович\"\n */\nexport function formatFullName(fullName: string): string {\n\tif (!fullName) return \"\";\n\n\t// Нормализация строки: удаление лишних пробелов, приведение к нижнему регистру\n\tconst normalized = fullName\n\t\t.trim()\n\t\t.replace(/\\s+/g, \" \")\n\t\t.replace(/\\s*\\./, \" \") // если после фамилии есть точка...\n\t\t.replace(/\\s*\\.\\s*/g, \". \")\n\t\t.toLowerCase();\n\n\t// Разделение на части\n\tconst parts = normalized.split(\" \");\n\n\tif (parts.length === 0) return \"\";\n\n\t// Обработка фамилии (первая часть)\n\tconst surname = capitalize(parts[0]);\n\n\tif (parts.length === 1) return surname;\n\n\t// Обработка имени (вторая часть)\n\tconst name = parts[1].includes(\".\") ? expandInitial(parts[1]) : capitalize(parts[1]);\n\n\tif (parts.length === 2) return `${surname} ${name}`;\n\n\t// Обработка отчества (третья часть)\n\tconst patronymic = parts[2].includes(\".\") ? expandInitial(parts[2]) : capitalize(parts[2]);\n\n\treturn `${surname} ${name} ${patronymic}`;\n}\n\n/**\n * Форматирует ФИО в формат \"Фамилия И.О.\"\n * @param fullName Входная строка с ФИО в любом формате\n * @returns Отформатированная строка в формате \"Иванов И.И.\"\n */\nexport function formatShortName(fullName: string): string {\n\tif (!fullName) return \"\";\n\n\t// Нормализация строки\n\tconst normalized = fullName\n\t\t.trim()\n\t\t.replace(/\\s+/g, \" \")\n\t\t.replace(/\\s*\\.\\s*/g, \".\")\n\t\t.toLowerCase();\n\n\tconst parts = normalized.split(\" \");\n\n\tif (parts.length === 0) return \"\";\n\n\t// Фамилия всегда с большой буквы\n\tconst surname = capitalize(parts[0]);\n\n\tif (parts.length === 1) return surname;\n\n\t// Инициал имени\n\tconst nameInitial = parts[1].startsWith(\".\") ? parts[1][1] : parts[1][0];\n\n\tif (parts.length === 2) {\n\t\treturn parts[1].includes(\".\") ? `${surname} ${nameInitial.toUpperCase()}.` : `${surname} ${nameInitial.toUpperCase()}.`;\n\t}\n\n\t// Инициал отчества\n\tconst patronymicInitial = parts[2].startsWith(\".\") ? parts[2][1] : parts[2][0];\n\n\treturn `${surname} ${nameInitial.toUpperCase()}.${patronymicInitial.toUpperCase()}.`;\n}\n\nfunction capitalize(str: string): string {\n\tif (!str) return \"\";\n\treturn str[0].toUpperCase() + str.slice(1);\n}\n\nfunction expandInitial(initial: string): string {\n\treturn capitalize(initial);\n\n\t// if (!initial.includes(\".\")) return capitalize(initial);\n\n\t// const letter = initial.replace(/\\./g, \"\").toLowerCase();\n\t// if (!letter) return \"\";\n\n\t// return letter.toUpperCase();\n}\n","import type { NumberFormatCompactOptions, NumberFormatCompactRoundingMode, NumberFormatCompactUnit, NumberFormatPreset } from \"./types\";\n\ntype PlainNumberFormatter = (value: number, preset: NumberFormatPreset) => string;\n\ntype ResolvedCompactNumberOptions = {\n\troundingMode: NumberFormatCompactRoundingMode;\n\tsuffixSeparator: string;\n\tmaxDecimals?: number;\n\tunits: readonly NumberFormatCompactUnit[];\n};\n\nconst DEFAULT_COMPACT_NUMBER_UNITS: readonly NumberFormatCompactUnit[] = [\n\t{ value: 1_000_000_000_000, suffix: \"трлн\" },\n\t{ value: 1_000_000_000, suffix: \"млрд\" },\n\t{ value: 1_000_000, suffix: \"млн\" },\n\t{ value: 1_000, suffix: \"тыс\" }\n];\n\nconst DEFAULT_COMPACT_NUMBER_OPTIONS = {\n\tminCompactValue: 1_000,\n\troundingMode: \"trunc\",\n\tsuffixSeparator: \" \"\n} satisfies Required<Pick<NumberFormatCompactOptions, \"minCompactValue\" | \"roundingMode\" | \"suffixSeparator\">>;\n\nconst DEFAULT_COMPACT_DECIMAL_SEPARATOR = \",\";\n\nfunction applyCompactRounding(value: number, mode: NumberFormatCompactRoundingMode): number {\n\tswitch (mode) {\n\t\tcase \"round\":\n\t\t\treturn Math.round(value);\n\n\t\tcase \"floor\":\n\t\t\treturn Math.floor(value);\n\n\t\tcase \"ceil\":\n\t\t\treturn Math.ceil(value);\n\n\t\tcase \"trunc\":\n\t\t\treturn Math.trunc(value);\n\t}\n}\n\nfunction roundCompactValue(value: number, decimals: number, mode: NumberFormatCompactRoundingMode): number {\n\tif (decimals === 0) return applyCompactRounding(value, mode);\n\n\tconst factor = 10 ** decimals;\n\treturn applyCompactRounding(value * factor, mode) / factor;\n}\n\nfunction getAutoCompactDecimals(normalizedValue: number): number {\n\tif (normalizedValue >= 10) return 0;\n\n\treturn 1;\n}\n\nfunction getEffectiveDecimals(value: number, maxDecimals: number): number {\n\tif (maxDecimals === 0) return 0;\n\n\tconst fixed = value.toFixed(maxDecimals);\n\tconst decimalSeparatorIndex = fixed.indexOf(\".\");\n\n\tif (decimalSeparatorIndex === -1) return 0;\n\n\tconst fraction = fixed.slice(decimalSeparatorIndex + 1).replace(/0+$/, \"\");\n\n\treturn fraction.length;\n}\n\nfunction getCompactUnitIndex(value: number, units: readonly NumberFormatCompactUnit[]): number {\n\treturn units.findIndex((unit) => value >= unit.value);\n}\n\nfunction createPlainCompactPreset(preset: NumberFormatPreset, decimals: number): NumberFormatPreset {\n\treturn {\n\t\tname: preset.name,\n\t\tdecimals,\n\t\tdecimalSeparator: decimals > 0 ? preset.decimalSeparator || DEFAULT_COMPACT_DECIMAL_SEPARATOR : preset.decimalSeparator,\n\t\tgrouping: preset.grouping,\n\t\tgroupingSeparator: preset.groupingSeparator,\n\t\tgroupingSize: preset.groupingSize\n\t};\n}\n\nfunction formatCompactValueByUnit(\n\tabsValue: number,\n\tunitIndex: number,\n\toptions: ResolvedCompactNumberOptions,\n\tpreset: NumberFormatPreset,\n\tformatPlainNumber: PlainNumberFormatter\n): string {\n\tconst unit = options.units[unitIndex];\n\tif (!unit) return formatPlainNumber(absValue, preset);\n\n\tconst normalizedValue = absValue / unit.value;\n\tconst maxDecimals = options.maxDecimals ?? getAutoCompactDecimals(normalizedValue);\n\tconst roundedValue = roundCompactValue(normalizedValue, maxDecimals, options.roundingMode);\n\n\t// Защита от результата вида \"1000 тыс\": переходим на следующую единицу.\n\tif (roundedValue >= 1000 && unitIndex > 0) {\n\t\treturn formatCompactValueByUnit(absValue, unitIndex - 1, options, preset, formatPlainNumber);\n\t}\n\n\tconst decimals = getEffectiveDecimals(roundedValue, maxDecimals);\n\tconst compactPreset = createPlainCompactPreset(preset, decimals);\n\n\treturn `${formatPlainNumber(roundedValue, compactPreset)}${options.suffixSeparator}${unit.suffix}`;\n}\n\nexport function formatCompactNumberValue(args: {\n\tvalue: number;\n\tpreset: NumberFormatPreset;\n\toptions: NumberFormatCompactOptions;\n\tformatPlainNumber: PlainNumberFormatter;\n}): string {\n\tconst {\n\t\tminCompactValue = DEFAULT_COMPACT_NUMBER_OPTIONS.minCompactValue,\n\t\troundingMode = DEFAULT_COMPACT_NUMBER_OPTIONS.roundingMode,\n\t\tsuffixSeparator = DEFAULT_COMPACT_NUMBER_OPTIONS.suffixSeparator,\n\t\tmaxDecimals,\n\t\tunits = DEFAULT_COMPACT_NUMBER_UNITS\n\t} = args.options;\n\n\tconst absValue = Math.abs(args.value);\n\n\tif (absValue < minCompactValue) {\n\t\treturn args.formatPlainNumber(args.value, args.preset);\n\t}\n\n\tconst unitIndex = getCompactUnitIndex(absValue, units);\n\n\tif (unitIndex === -1) {\n\t\treturn args.formatPlainNumber(args.value, args.preset);\n\t}\n\n\tconst sign = args.value < 0 ? \"-\" : \"\";\n\n\treturn `${sign}${formatCompactValueByUnit(\n\t\tabsValue,\n\t\tunitIndex,\n\t\t{\n\t\t\troundingMode,\n\t\t\tsuffixSeparator,\n\t\t\tmaxDecimals,\n\t\t\tunits\n\t\t},\n\t\targs.preset,\n\t\targs.formatPlainNumber\n\t)}`;\n}\n","import { toFiniteNumber } from \"./parseNumber\";\n\n/**\n * Проверяет, что значение семантически эквивалентно нулю и может быть скрыто как пустое.\n */\nexport function isZeroValue(value: unknown): boolean {\n\tconst v = toFiniteNumber(value);\n\n\tif (typeof v === \"number\") {\n\t\treturn v === 0;\n\t}\n\n\tif (typeof v === \"bigint\") {\n\t\treturn v === 0n;\n\t}\n\n\treturn false;\n}\n","import { formatCompactNumberValue } from \"./compact\";\nimport { isZeroValue } from \"./isZeroValue\";\nimport { DEFAULT_NUMBER_PRESET_NAMES, NUMBER_FORMAT_DEFAULTS, resolveNumberPreset } from \"./presets\";\n\nimport type { NumberFormatPreset } from \"./types\";\n\n/** Кеш Intl.NumberFormat по ключу \"decimals:grouping\" */\nconst cache = new Map<string, Intl.NumberFormat>();\n\n/** Усечение дробной части без округления: 1234.789 → 1234.7 (при decimals=1) */\nexport function truncateNumber(num: number, decimals: number): number {\n\tif (decimals === 0) return Math.trunc(num);\n\tconst factor = 10 ** decimals;\n\treturn Math.trunc(num * factor) / factor;\n}\n\nfunction getFormatter(decimals: number, grouping: boolean): Intl.NumberFormat {\n\tconst key = `${decimals}:${grouping ? 1 : 0}`;\n\tlet fmt = cache.get(key);\n\tif (!fmt) {\n\t\tfmt = new Intl.NumberFormat(\"ru-RU\", {\n\t\t\tminimumFractionDigits: decimals,\n\t\t\tmaximumFractionDigits: decimals,\n\t\t\tuseGrouping: grouping\n\t\t});\n\t\tcache.set(key, fmt);\n\t}\n\treturn fmt;\n}\n\n/** Разделители совпадают с ru-RU — можно использовать format() напрямую */\nfunction isStandard(p: NumberFormatPreset): boolean {\n\treturn (\n\t\tp.groupingSize === 3 &&\n\t\t(p.groupingSeparator === NUMBER_FORMAT_DEFAULTS.groupingSeparator || !p.grouping) &&\n\t\t(p.decimalSeparator === \",\" || p.decimals === 0)\n\t);\n}\n\n/**\n * Быстрый путь (99% вызовов): format() напрямую, без постобработки.\n * ru-RU уже даёт неразрывный пробел и запятую — именно то, что нужно.\n */\nfunction formatStandard(num: number, decimals: number, grouping: boolean): string {\n\treturn getFormatter(decimals, grouping).format(num);\n}\n\n/** Кастомные разделители: formatToParts() с подстановкой */\nfunction formatCustomSeparator(num: number, preset: NumberFormatPreset): string {\n\tconst parts = getFormatter(preset.decimals, preset.grouping).formatToParts(num);\n\tlet result = \"\";\n\tfor (const p of parts) {\n\t\tif (p.type === \"group\") result += preset.groupingSeparator;\n\t\telse if (p.type === \"decimal\") result += preset.decimalSeparator;\n\t\telse result += p.value;\n\t}\n\treturn result;\n}\n\n/** Нестандартный размер группы: Intl для округления, ручная группировка */\nfunction formatManualGroup(num: number, preset: NumberFormatPreset): string {\n\tconst raw = getFormatter(preset.decimals, false).format(num);\n\n\t// ru-RU без группировки: \"1234567,89\" — запятая как десятичный разделитель\n\tconst sep = raw.indexOf(\",\");\n\tconst intPart = sep === -1 ? raw : raw.slice(0, sep);\n\tconst fracPart = sep === -1 ? null : raw.slice(sep + 1);\n\n\tconst neg = intPart.startsWith(\"-\");\n\tconst abs = neg ? intPart.slice(1) : intPart;\n\n\tconst size = preset.groupingSize;\n\tconst groups: string[] = [];\n\tfor (let i = abs.length; i > 0; i -= size) {\n\t\tgroups.unshift(abs.slice(Math.max(0, i - size), i));\n\t}\n\n\tlet result = (neg ? \"-\" : \"\") + groups.join(preset.groupingSeparator);\n\tif (fracPart !== null) result += preset.decimalSeparator + fracPart;\n\treturn result;\n}\n\nfunction formatPlainNumber(num: number, preset: NumberFormatPreset): string {\n\t// 99% — стандартный ru-RU формат, format() без постобработки\n\tif (isStandard(preset)) return formatStandard(num, preset.decimals, preset.grouping);\n\n\t// Стандартный размер группы (3), но кастомные разделители\n\tif (preset.groupingSize === 3 || !preset.grouping) return formatCustomSeparator(num, preset);\n\n\t// Нестандартный размер группы\n\treturn formatManualGroup(num, preset);\n}\n\n/**\n * Форматирование числа по предустановке.\n * Оптимизировано для 500+ вызовов: кеш Intl.NumberFormat + быстрый путь через format().\n *\n * Не использовать напрямую! Использовать `formatNumberAs*`\n */\nexport function formatNumber(value: unknown, presetOrName: string | NumberFormatPreset): string {\n\tif (typeof value === \"string\" && value.trim() === \"\") return \"0\";\n\n\tconst num = Number(value);\n\tif (!Number.isFinite(num)) return \"0\";\n\n\tconst preset = resolveNumberPreset(presetOrName);\n\n\t// Усечение без округления — Intl занимается только группировкой и разделителями\n\t// const truncated = truncateNumber(num, preset.decimals);\n\t// NOTE: 30.04.2026 выяснилось, что всё таки должно быть именно округление, а не усечение\n\n\tif (preset.compact) {\n\t\treturn formatCompactNumberValue({\n\t\t\tvalue: num,\n\t\t\tpreset,\n\t\t\toptions: preset.compact,\n\t\t\tformatPlainNumber\n\t\t});\n\t}\n\n\treturn formatPlainNumber(num, preset);\n}\n\n/**\n * Форматирование значения пресетом `integer` (0 знаков после запятой)\n */\nexport function formatNumberAsInteger(value: unknown): string {\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.integer);\n}\n\n/**\n * Форматирование значения пресетом `currency` (0 знаков после запятой)\n */\nexport function formatNumberAsCurrency(value: unknown): string {\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.currency);\n}\n\n/**\n * Форматирование значения пресетом `decimal` (1 знак после запятой)\n */\nexport function formatNumberAsDecimal(value: unknown): string {\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.decimal);\n}\n\n/**\n * Форматирование значения пресетом `decimal-2` (2 знака после запятой)\n */\nexport function formatNumberAsDecimal2(value: unknown): string {\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.decimal2);\n}\n\n/**\n * Форматирование значения пресетом `decimal-3` (3 знака после запятой)\n */\nexport function formatNumberAsDecimal3(value: unknown): string {\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.decimal3);\n}\n\n/**\n * Форматирование значения пресетом `percent` (2 знака после запятой)\n */\nexport function formatNumberAsPercent(value: unknown): string {\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.percent);\n}\n\n/**\n * Форматирование значения пресетом `price` (0 знаков после запятой)\n */\nexport function formatNumberAsPrice(value: unknown): string {\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.price);\n}\n\n/**\n * Форматирование значения пресетом `tonnage` (1 знак после запятой)\n */\nexport function formatNumberAsTonnage(value: unknown): string {\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.tonnage);\n}\n\n/**\n * Форматирование значения пресетом `integer` (0 знаков после запятой)\n *\n * Проверяет, если значение семантически эквивалентно нулю, то возвращается пустая строка.\n */\nexport function formatNumberAsIntegerOrEmpty(value: unknown): string {\n\tif (isZeroValue(value)) return \"\";\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.integer);\n}\n\n/**\n * Форматирование значения пресетом `currency` (0 знаков после запятой)\n *\n * Проверяет, если значение семантически эквивалентно нулю, то возвращается пустая строка.\n */\nexport function formatNumberAsCurrencyOrEmpty(value: unknown): string {\n\tif (isZeroValue(value)) return \"\";\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.currency);\n}\n\n/**\n * Форматирование значения пресетом `decimal` (1 знак после запятой)\n *\n * Проверяет, если значение семантически эквивалентно нулю, то возвращается пустая строка.\n */\nexport function formatNumberAsDecimalOrEmpty(value: unknown): string {\n\tif (isZeroValue(value)) return \"\";\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.decimal);\n}\n\n/**\n * Форматирование значения пресетом `decimal-2` (2 знака после запятой)\n *\n * Проверяет, если значение семантически эквивалентно нулю, то возвращается пустая строка.\n */\nexport function formatNumberAsDecimal2OrEmpty(value: unknown): string {\n\tif (isZeroValue(value)) return \"\";\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.decimal2);\n}\n\n/**\n * Форматирование значения пресетом `decimal-3` (3 знака после запятой)\n *\n * Проверяет, если значение семантически эквивалентно нулю, то возвращается пустая строка.\n */\nexport function formatNumberAsDecimal3OrEmpty(value: unknown): string {\n\tif (isZeroValue(value)) return \"\";\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.decimal3);\n}\n\n/**\n * Форматирование значения пресетом `percent` (2 знака после запятой)\n *\n * Проверяет, если значение семантически эквивалентно нулю, то возвращается пустая строка.\n */\nexport function formatNumberAsPercentOrEmpty(value: unknown): string {\n\tif (isZeroValue(value)) return \"\";\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.percent);\n}\n\n/**\n * Форматирование значения пресетом `price` (0 знаков после запятой)\n *\n * Проверяет, если значение семантически эквивалентно нулю, то возвращается пустая строка.\n */\nexport function formatNumberAsPriceOrEmpty(value: unknown): string {\n\tif (isZeroValue(value)) return \"\";\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.price);\n}\n\n/**\n * Форматирование значения пресетом `tonnage` (1 знак после запятой)\n *\n * Проверяет, если значение семантически эквивалентно нулю, то возвращается пустая строка.\n */\nexport function formatNumberAsTonnageOrEmpty(value: unknown): string {\n\tif (isZeroValue(value)) return \"\";\n\treturn formatNumber(value, DEFAULT_NUMBER_PRESET_NAMES.tonnage);\n}\n\n/** Очистить кеш Intl.NumberFormat */\nexport function clearFormatCache(): void {\n\tcache.clear();\n}\n","import { formatNumber, NUMBER_FORMAT_DEFAULTS, type NumberFormatPreset } from \"./number\";\n\nconst FILE_SIZE_BASE = 1024;\n/** Двоичные единицы соответствуют File.size и серверным лимитам в байтах. */\nconst FILE_SIZE_PRESET: NumberFormatPreset = {\n\t...NUMBER_FORMAT_DEFAULTS,\n\tname: \"file-size\",\n\tdecimals: 0,\n\tcompact: {\n\t\tminCompactValue: 1,\n\t\tmaxDecimals: 2,\n\t\troundingMode: \"round\",\n\t\tunits: [\n\t\t\t{ value: FILE_SIZE_BASE ** 5, suffix: \"ПиБ\" },\n\t\t\t{ value: FILE_SIZE_BASE ** 4, suffix: \"ТиБ\" },\n\t\t\t{ value: FILE_SIZE_BASE ** 3, suffix: \"ГиБ\" },\n\t\t\t{ value: FILE_SIZE_BASE ** 2, suffix: \"МиБ\" },\n\t\t\t{ value: FILE_SIZE_BASE, suffix: \"КиБ\" },\n\t\t\t{ value: 1, suffix: \"Б\" }\n\t\t]\n\t}\n};\n\n/**\n * Показывает неотрицательный размер в подходящих двоичных единицах,\n * до двух знаков после запятой. Маленький файл не превращается в «0 МиБ».\n * Отсутствие размера является состоянием потребителя, а не нулём байтов.\n */\nexport function formatFileSize(sizeBytes: number): string {\n\tif (!Number.isSafeInteger(sizeBytes) || sizeBytes < 0)\n\t\tthrow new RangeError(\"Размер файла должен быть неотрицательным безопасным целым числом.\");\n\tif (sizeBytes === 0) return \"0 Б\";\n\t// Выбор единицы определяется байтами: десятичный compact не должен\n\t// переносить 1000 Б в КиБ до достижения двоичного порога 1024.\n\tconst unit = FILE_SIZE_PRESET.compact?.units?.find(({ value }) => sizeBytes >= value);\n\treturn formatNumber(sizeBytes, {\n\t\t...FILE_SIZE_PRESET,\n\t\tcompact: { ...FILE_SIZE_PRESET.compact, units: unit ? [unit] : [] }\n\t});\n}\n","export function formatPhone(number: string): string {\n\tconst match = clearPhone(number).match(/^(\\+7)(\\d{3})(\\d{3})(\\d{2})(\\d{2})$/);\n\tif (!match) return number;\n\treturn `+7 (${match[2]}) ${match[3]}-${match[4]}-${match[5]}`;\n}\n\nexport function clearPhone(number: string): string {\n\treturn `+7${number\n\t\t.replace(/\\D/g, \"\")\n\t\t.slice(0, 11)\n\t\t.replace(/^(7|8)/, \"\")}`;\n}\n","/**\n * Создаёт календарную дату без timezone-семантики.\n *\n * Внутри используется локальный конструктор `Date`, чтобы видимые\n * компоненты даты и времени сохранялись без дополнительных смещений.\n */\nexport function createCalendarDate(year: number, monthIndex: number, day: number, hour = 0, minute = 0, second = 0, millisecond = 0): Date {\n\treturn new Date(year, monthIndex, day, hour, minute, second, millisecond);\n}\n\n/**\n * Нормализует дату к началу суток без timezone-сдвига.\n */\nexport function getStartOfDay(date: Date): Date {\n\treturn createCalendarDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);\n}\n\n/**\n * Нормализует дату к концу суток без timezone-сдвига.\n */\nexport function getEndOfDay(date: Date): Date {\n\treturn createCalendarDate(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 0);\n}\n\n/**\n * Возвращает первый день месяца для переданной даты.\n */\nexport function getStartOfMonth(date: Date): Date {\n\treturn createCalendarDate(date.getFullYear(), date.getMonth(), 1, 0, 0, 0, 0);\n}\n\n/**\n * Возвращает первый день года для переданной даты.\n */\nexport function getStartOfYear(date: Date): Date {\n\treturn createCalendarDate(date.getFullYear(), 0, 1, 0, 0, 0, 0);\n}\n\n/**\n * Возвращает начало календарной недели без timezone-сдвига.\n *\n * По умолчанию неделя начинается в понедельник, как в русскоязычной календарной\n * сетке. При необходимости вызывающий код может передать другой день недели\n * в формате `Date.getDay()`: 0 — воскресенье, 1 — понедельник.\n */\nexport function getStartOfWeek(date: Date, weekStartsOn = 1): Date {\n\tconst normalizedWeekStart = Number.isInteger(weekStartsOn) ? ((weekStartsOn % 7) + 7) % 7 : 1;\n\tconst start = getStartOfDay(date);\n\tconst offset = (start.getDay() - normalizedWeekStart + 7) % 7;\n\n\treturn addCalendarDays(start, -offset);\n}\n\n/**\n * Сдвигает дату на указанное количество суток в календарной семантике.\n */\nexport function addCalendarDays(date: Date, amount: number): Date {\n\treturn createCalendarDate(date.getFullYear(), date.getMonth(), date.getDate() + amount, 0, 0, 0, 0);\n}\n\n/**\n * Сдвигает дату на указанное количество месяцев в календарной семантике.\n */\nexport function addCalendarMonths(date: Date, amount: number): Date {\n\treturn createCalendarDate(date.getFullYear(), date.getMonth() + amount, date.getDate(), 0, 0, 0, 0);\n}\n\n/**\n * Сдвигает дату на указанное количество лет в календарной семантике.\n */\nexport function addCalendarYears(date: Date, amount: number): Date {\n\treturn createCalendarDate(date.getFullYear() + amount, date.getMonth(), date.getDate(), 0, 0, 0, 0);\n}\n\n/**\n * Проверяет, совпадают ли две даты по календарному дню.\n */\nexport function isSameCalendarDay(left: Date | null | undefined, right: Date | null | undefined): boolean {\n\tif (!(left instanceof Date) || !(right instanceof Date)) return false;\n\n\treturn left.getFullYear() === right.getFullYear() && left.getMonth() === right.getMonth() && left.getDate() === right.getDate();\n}\n","import {\n\taddCalendarDays,\n\tcreateCalendarDate,\n\tgetEndOfDay,\n\tgetStartOfDay,\n\tgetStartOfMonth,\n\tgetStartOfWeek,\n\tgetStartOfYear\n} from \"./calendarDate\";\n\nexport type CalendarPeriodSelectionMode = \"day\" | \"week\" | \"month\" | \"year\";\nexport type CalendarWeekEndDay = \"friday\" | \"saturday\" | \"sunday\";\n\nexport interface CalendarPeriodOptions {\n\t/**\n\t * Определяет размер выбираемого календарного периода.\n\t */\n\tselectionMode?: CalendarPeriodSelectionMode;\n\t/**\n\t * Последний включённый день недели для режима `week`.\n\t */\n\tweekEndDay?: CalendarWeekEndDay;\n}\n\nexport interface CalendarPeriod {\n\treadonly start: Date;\n\treadonly end: Date;\n}\n\nexport interface CalendarPeriodDateBounds {\n\treadonly minDate?: Date;\n\treadonly maxDate?: Date;\n}\n\nconst WEEK_END_DAY_OFFSETS: Record<CalendarWeekEndDay, number> = {\n\tfriday: 4,\n\tsaturday: 5,\n\tsunday: 6\n};\n\nfunction isValidCalendarDate(value: Date | null | undefined): value is Date {\n\treturn value instanceof Date && Number.isFinite(value.getTime());\n}\n\nfunction resolveCalendarWeekEndDay(weekEndDay?: CalendarWeekEndDay): CalendarWeekEndDay {\n\treturn weekEndDay ?? \"sunday\";\n}\n\nfunction resolveCalendarPeriodSelectionMode(selectionMode?: CalendarPeriodSelectionMode): CalendarPeriodSelectionMode {\n\treturn selectionMode ?? \"day\";\n}\n\n/**\n * Возвращает включительные границы календарного периода без timezone-сдвига.\n */\nexport function getCalendarPeriod(value: Date | null | undefined, options: CalendarPeriodOptions = {}): CalendarPeriod | null {\n\tif (!isValidCalendarDate(value)) return null;\n\n\tconst selectionMode = resolveCalendarPeriodSelectionMode(options.selectionMode);\n\n\tswitch (selectionMode) {\n\t\tcase \"day\": {\n\t\t\treturn {\n\t\t\t\tstart: getStartOfDay(value),\n\t\t\t\tend: getEndOfDay(value)\n\t\t\t};\n\t\t}\n\n\t\tcase \"week\": {\n\t\t\tconst start = getStartOfWeek(value);\n\t\t\tconst weekEndOffset = WEEK_END_DAY_OFFSETS[resolveCalendarWeekEndDay(options.weekEndDay)];\n\n\t\t\treturn {\n\t\t\t\tstart,\n\t\t\t\tend: getEndOfDay(addCalendarDays(start, weekEndOffset))\n\t\t\t};\n\t\t}\n\n\t\tcase \"month\": {\n\t\t\treturn {\n\t\t\t\tstart: getStartOfMonth(value),\n\t\t\t\tend: getEndOfDay(createCalendarDate(value.getFullYear(), value.getMonth() + 1, 0))\n\t\t\t};\n\t\t}\n\n\t\tcase \"year\": {\n\t\t\treturn {\n\t\t\t\tstart: getStartOfYear(value),\n\t\t\t\tend: getEndOfDay(createCalendarDate(value.getFullYear(), 11, 31))\n\t\t\t};\n\t\t}\n\n\t\tdefault: {\n\t\t\tconst checker: never = selectionMode;\n\t\t\tvoid checker;\n\n\t\t\treturn null;\n\t\t}\n\t}\n}\n\n/**\n * Проверяет, входит ли дата в уже вычисленные включительные границы периода.\n */\nexport function isDateInsideCalendarPeriod(value: Date | null | undefined, period: CalendarPeriod | null | undefined): boolean {\n\tif (!isValidCalendarDate(value) || !period) return false;\n\n\tconst dateTime = getStartOfDay(value).getTime();\n\treturn dateTime >= period.start.getTime() && dateTime <= period.end.getTime();\n}\n\n/**\n * Проверяет, что весь период укладывается в заданные календарные границы выбора.\n */\nexport function isCalendarPeriodWithinDateBounds(\n\tperiod: CalendarPeriod | null | undefined,\n\tbounds: CalendarPeriodDateBounds = {}\n): boolean {\n\tif (!period) return false;\n\n\tif (bounds.minDate && period.start.getTime() < getStartOfDay(bounds.minDate).getTime()) return false;\n\tif (bounds.maxDate && period.end.getTime() > getEndOfDay(bounds.maxDate).getTime()) return false;\n\n\treturn true;\n}\n","import { getEndOfDay, getStartOfDay } from \"./calendarDate\";\n\nconst MS_IN_DAY = 24 * 60 * 60 * 1000;\n\nexport type NullableDateRange = readonly [Date | null, Date | null];\nexport type DateRange = readonly [Date, Date];\n\nexport type DateRangeInput = Date | null | NullableDateRange;\n\nexport type DateRangeTimeMode = \"dayBounds\" | \"preserve\";\n\nexport interface NormalizeDateRangeOptions {\n\t/**\n\t * Режим времени для двух граничных дат.\n\t *\n\t * `dayBounds` расширяет диапазон до полных календарных дней,\n\t * `preserve` оставляет время исходных дат после упорядочивания.\n\t */\n\ttimeMode?: DateRangeTimeMode;\n}\n\nexport function isValidDate(value: Date | null): value is Date {\n\treturn value instanceof Date && Number.isFinite(value.getTime());\n}\n\nexport function isDateRangeTuple(value: DateRangeInput): value is NullableDateRange {\n\treturn Array.isArray(value);\n}\n\nexport function cloneDate(date: Date): Date {\n\treturn new Date(date.getTime());\n}\n\nexport function orderDates(left: Date, right: Date): DateRange {\n\tif (left.getTime() <= right.getTime()) return [left, right];\n\n\treturn [right, left];\n}\n\nexport function resolveDateRangePair(value: DateRangeInput): DateRange | null {\n\tif (isDateRangeTuple(value)) {\n\t\tconst [left, right] = value;\n\t\tconst hasLeft = isValidDate(left);\n\t\tconst hasRight = isValidDate(right);\n\n\t\tif (hasLeft && hasRight) return orderDates(left, right);\n\t\tif (hasLeft) return [left, left];\n\t\tif (hasRight) return [right, right];\n\n\t\treturn null;\n\t}\n\n\treturn isValidDate(value) ? [value, value] : null;\n}\n\nexport function normalizeDateRange(value: DateRangeInput, options: NormalizeDateRangeOptions = {}): DateRange | null {\n\tconst range = resolveDateRangePair(value);\n\tif (!range) return null;\n\n\tconst [start, end] = range;\n\tif (options.timeMode === \"preserve\") {\n\t\treturn [cloneDate(start), cloneDate(end)];\n\t}\n\n\treturn [getStartOfDay(start), getEndOfDay(end)];\n}\n\nexport function requireDateRange(value: DateRangeInput, options?: NormalizeDateRangeOptions): DateRange {\n\tconst range = normalizeDateRange(value, options);\n\tif (!range) {\n\t\tthrow new Error(\"Диапазон дат не задан\");\n\t}\n\n\treturn range;\n}\n\nfunction getCalendarDayTime(date: Date): number {\n\treturn Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\n/**\n * Считает количество календарных дней в диапазоне включительно.\n */\nexport function countCalendarDaysInDateRange(value: DateRangeInput): number | null {\n\tconst range = normalizeDateRange(value);\n\tif (!range) return null;\n\n\tconst [startDate, endDate] = range;\n\treturn Math.floor((getCalendarDayTime(endDate) - getCalendarDayTime(startDate)) / MS_IN_DAY) + 1;\n}\n","import { addCalendarDays, createCalendarDate, getStartOfDay } from \"./calendarDate\";\n\nconst EDUCATION_DATE_KEY_RE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst EDUCATION_TIME_KEY_RE = /^(\\d{2}):(\\d{2})(?::(\\d{2}))?$/;\nconst EDUCATION_YEAR_START_MONTH_INDEX = 8;\nconst EDUCATION_YEAR_START_DAY = 1;\nconst EDUCATION_YEAR_END_MONTH_INDEX = 7;\nconst EDUCATION_YEAR_END_DAY = 31;\nconst EDUCATION_WEEK_DAY_COUNT = 6;\n\n/**\n * Машинный ключ календарного дня в образовательной системе.\n *\n * Формат всегда `YYYY-MM-DD`: он безопасен для URL, SQL-параметров и query keys,\n * но не несёт timezone-семантики.\n */\nexport type EducationDateKey = string;\n\n/**\n * Диапазон учебного года. Учебный год начинается 1 сентября и заканчивается 31 августа.\n */\nexport type EducationYearRange = {\n\treadonly year: number;\n\treadonly start: Date;\n\treadonly end: Date;\n};\n\n/**\n * Диапазон учебной недели. В публичном расписании неделя отображается с понедельника по субботу.\n */\nexport type EducationWeekRange = {\n\treadonly start: Date;\n\treadonly end: Date;\n};\n\nfunction isValidTimePart(value: number, max: number): boolean {\n\treturn Number.isInteger(value) && value >= 0 && value <= max;\n}\n\n/**\n * Разбирает `YYYY-MM-DD` без `Date.parse`, чтобы не получить скрытый timezone-сдвиг.\n */\nexport function parseEducationDateKey(value: string | undefined): Date | null {\n\tconst match = value?.match(EDUCATION_DATE_KEY_RE);\n\tif (!match) {\n\t\treturn null;\n\t}\n\n\tconst year = Number(match[1]);\n\tconst month = Number(match[2]);\n\tconst day = Number(match[3]);\n\tconst date = createCalendarDate(year, month - 1, day);\n\n\treturn date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ? date : null;\n}\n\n/**\n * Форматирует `Date` в стабильный ключ `YYYY-MM-DD` по локальным календарным компонентам.\n */\nexport function formatEducationDateKey(value: Date): EducationDateKey {\n\tconst year = value.getFullYear();\n\tconst month = String(value.getMonth() + 1).padStart(2, \"0\");\n\tconst day = String(value.getDate()).padStart(2, \"0\");\n\n\treturn `${year}-${month}-${day}`;\n}\n\n/**\n * Форматирует время в `HH:mm` по локальным компонентам, без UTC-преобразования.\n */\nexport function formatEducationTimeKey(value: Date): string {\n\tconst hours = String(value.getHours()).padStart(2, \"0\");\n\tconst minutes = String(value.getMinutes()).padStart(2, \"0\");\n\n\treturn `${hours}:${minutes}`;\n}\n\n/**\n * Создаёт календарную дату-время из отдельных ключей даты и времени, удобных для SQL-выдачи.\n */\nexport function createEducationDateTime(dateKey: EducationDateKey, timeKey: string): Date | null {\n\tconst date = parseEducationDateKey(dateKey);\n\tconst match = timeKey.match(EDUCATION_TIME_KEY_RE);\n\tif (!date || !match) {\n\t\treturn null;\n\t}\n\n\tconst hours = Number(match[1]);\n\tconst minutes = Number(match[2]);\n\tconst seconds = Number(match[3] ?? 0);\n\tif (!isValidTimePart(hours, 23) || !isValidTimePart(minutes, 59) || !isValidTimePart(seconds, 59)) {\n\t\treturn null;\n\t}\n\n\treturn createCalendarDate(date.getFullYear(), date.getMonth(), date.getDate(), hours, minutes, seconds);\n}\n\n/**\n * Возвращает учебный год для календарной даты.\n */\nexport function getEducationYear(value: Date = new Date()): number {\n\treturn value.getMonth() < EDUCATION_YEAR_START_MONTH_INDEX ? value.getFullYear() - 1 : value.getFullYear();\n}\n\n/**\n * Возвращает границы учебного года для переданной даты.\n */\nexport function getEducationYearRange(value: Date = new Date()): EducationYearRange {\n\tconst year = getEducationYear(value);\n\n\treturn {\n\t\tyear,\n\t\tstart: createCalendarDate(year, EDUCATION_YEAR_START_MONTH_INDEX, EDUCATION_YEAR_START_DAY),\n\t\tend: createCalendarDate(year + 1, EDUCATION_YEAR_END_MONTH_INDEX, EDUCATION_YEAR_END_DAY)\n\t};\n}\n\n/**\n * Проверяет, входит ли дата в учебный год, вычисленный от `now`.\n */\nexport function isEducationDateInCurrentYear(value: Date, now: Date = new Date()): boolean {\n\tconst date = getStartOfDay(value);\n\tconst { start, end } = getEducationYearRange(now);\n\n\treturn date >= start && date <= end;\n}\n\n/**\n * Возвращает понедельник учебной недели. Воскресенье относится к следующей учебной неделе.\n */\nexport function getEducationWeekStartDate(value: Date): Date {\n\tconst date = getStartOfDay(value);\n\tconst dayOfWeek = (date.getDay() + 6) % 7;\n\tconst daysToMonday = dayOfWeek === 6 ? 1 : -dayOfWeek;\n\n\treturn addCalendarDays(date, daysToMonday);\n}\n\n/**\n * Возвращает диапазон учебной недели: понедельник-суббота.\n */\nexport function getEducationWeekRange(value: Date): EducationWeekRange {\n\tconst start = getEducationWeekStartDate(value);\n\n\treturn {\n\t\tstart,\n\t\tend: addCalendarDays(start, EDUCATION_WEEK_DAY_COUNT - 1)\n\t};\n}\n\n/**\n * Возвращает ключи дней учебной недели для SSR, prefetch и клиентского отображения.\n */\nexport function getEducationWeekDays(week: EducationDateKey | Date): readonly EducationDateKey[] {\n\tconst start =\n\t\ttypeof week === \"string\" ? (parseEducationDateKey(week) ?? getEducationWeekStartDate(new Date())) : getEducationWeekStartDate(week);\n\n\treturn Array.from({ length: EDUCATION_WEEK_DAY_COUNT }, (_, index) => formatEducationDateKey(addCalendarDays(start, index)));\n}\n\n/**\n * Возвращает ключ текущей учебной недели.\n */\nexport function getCurrentEducationWeekKey(now: Date = new Date()): EducationDateKey {\n\treturn formatEducationDateKey(getEducationWeekStartDate(now));\n}\n\n/**\n * Нормализует произвольный ключ недели к понедельнику текущего учебного года.\n */\nexport function normalizeEducationWeekKey(value: string | undefined, now: Date = new Date()): EducationDateKey | undefined {\n\tconst parsed = parseEducationDateKey(value);\n\tif (!parsed || !isEducationDateInCurrentYear(parsed, now)) {\n\t\treturn undefined;\n\t}\n\n\tconst weekStart = getEducationWeekStartDate(parsed);\n\n\treturn isEducationDateInCurrentYear(weekStart, now) ? formatEducationDateKey(weekStart) : undefined;\n}\n\n/**\n * Возвращает валидный ключ недели или текущую учебную неделю как fallback.\n */\nexport function resolveEducationWeekKey(value: string | undefined, now: Date = new Date()): EducationDateKey {\n\treturn normalizeEducationWeekKey(value, now) ?? getCurrentEducationWeekKey(now);\n}\n\n/**\n * Проверяет, совпадает ли неделя с текущей учебной неделей.\n */\nexport function isCurrentEducationWeek(week: EducationDateKey, now: Date = new Date()): boolean {\n\treturn resolveEducationWeekKey(week, now) === getCurrentEducationWeekKey(now);\n}\n","import { parseDateValueTZ } from \"./parseDate\";\n\n/**\n * Настройки отображения абсолютного момента в явно заданном часовом поясе.\n * Обязательный `timeZone` исключает зависимость результата от часового пояса\n * браузера или SSR-сервера.\n */\nexport type FormatInstantDateTimeOptions = {\n\treadonly timeZone: string;\n\treadonly locale?: string;\n\treadonly dateStyle?: Intl.DateTimeFormatOptions[\"dateStyle\"];\n\treadonly timeStyle?: Intl.DateTimeFormatOptions[\"timeStyle\"];\n\treadonly fallback?: string;\n};\n\n/**\n * Форматирует ISO/OData/Unix timestamp как абсолютный момент в указанном\n * часовом поясе IANA. Используется для времени событий и аудита; календарные\n * значения без часового пояса следует форматировать через обычный `formatDate`.\n */\nexport function formatInstantDateTime(value: unknown, options: FormatInstantDateTimeOptions): string {\n\tconst parsed = parseDateValueTZ(value);\n\tconst fallback = options.fallback ?? \"\";\n\tif (!parsed || parsed.kind !== \"date-time\") return fallback;\n\n\ttry {\n\t\treturn new Intl.DateTimeFormat(options.locale ?? \"ru-RU\", {\n\t\t\tdateStyle: options.dateStyle ?? \"medium\",\n\t\t\ttimeStyle: options.timeStyle ?? \"medium\",\n\t\t\ttimeZone: options.timeZone\n\t\t}).format(parsed.date);\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n","import { addCalendarDays, addCalendarMonths, getEndOfDay, getStartOfDay, getStartOfMonth } from \"./calendarDate\";\n\nimport type { NullableDateRange } from \"./dateRange\";\n\n/**\n * Контекст вычисления относительного диапазона дат.\n */\nexport interface DateRangeReferenceContext {\n\treferenceDate: Date;\n}\n\n/**\n * Нормализует опорную дату к началу суток без timezone-сдвига.\n */\nexport function normalizeDateRangeReferenceDate(referenceDate: Date): Date {\n\treturn getStartOfDay(referenceDate);\n}\n\n/**\n * Возвращает безопасную конечную дату для диапазона \"с начала месяца по вчера\".\n *\n * На первом дне месяца диапазон не может закончиться \"вчера\" без инверсии,\n * поэтому конец диапазона прижимается к старту месяца.\n */\nexport function resolveMonthStartToYesterdayRange({ referenceDate }: DateRangeReferenceContext): NullableDateRange {\n\tconst normalizedReferenceDate = normalizeDateRangeReferenceDate(referenceDate);\n\tconst monthStart = getStartOfMonth(normalizedReferenceDate);\n\tconst yesterday = addCalendarDays(normalizedReferenceDate, -1);\n\tconst safeEndDate = yesterday < monthStart ? monthStart : yesterday;\n\n\treturn [monthStart, getEndOfDay(safeEndDate)];\n}\n\n/**\n * Возвращает диапазон \"с начала месяца по сегодня\".\n */\nexport function resolveMonthStartToTodayRange({ referenceDate }: DateRangeReferenceContext): NullableDateRange {\n\tconst normalizedReferenceDate = normalizeDateRangeReferenceDate(referenceDate);\n\treturn [getStartOfMonth(normalizedReferenceDate), getEndOfDay(normalizedReferenceDate)];\n}\n\n/**\n * Возвращает диапазон \"за сегодня\".\n */\nexport function resolveTodayRange({ referenceDate }: DateRangeReferenceContext): NullableDateRange {\n\tconst normalizedReferenceDate = normalizeDateRangeReferenceDate(referenceDate);\n\treturn [normalizedReferenceDate, getEndOfDay(normalizedReferenceDate)];\n}\n\n/**\n * Возвращает диапазон \"за вчера\".\n */\nexport function resolveYesterdayRange({ referenceDate }: DateRangeReferenceContext): NullableDateRange {\n\tconst yesterday = addCalendarDays(normalizeDateRangeReferenceDate(referenceDate), -1);\n\treturn [yesterday, getEndOfDay(yesterday)];\n}\n\n/**\n * Возвращает диапазон \"месяц назад\" от текущего дня.\n */\nexport function resolveMonthAgoRange({ referenceDate }: DateRangeReferenceContext): NullableDateRange {\n\tconst normalizedReferenceDate = normalizeDateRangeReferenceDate(referenceDate);\n\tconst monthAgo = addCalendarMonths(normalizedReferenceDate, -1);\n\treturn [getStartOfDay(monthAgo), getEndOfDay(normalizedReferenceDate)];\n}\n","import { toFiniteNumber } from \"./number\";\nimport { stripInnerSpaces } from \"./strings\";\n\n/**\n * Нормализует ведущие нули у числового значения.\n *\n * Правило fixed:\n * - fixed === 0 / undefined / NaN / не finite -> ничего не делает, возвращает исходное значение\n * - fixed < 0 -> удаляет ведущие нули (только в целой части)\n * - fixed > 0 -> дополняет целую часть ведущими нулями до длины fixed\n *\n * Если value не является числом и не парсится в число — возвращается как есть.\n * Возвращаемый тип: если вход number -> number, если вход string -> string, иначе unknown.\n */\nexport function normalizeLeadingZeros(value: unknown, fixed?: number): unknown {\n\tconst n = typeof value === \"number\" ? value : Number(value);\n\tif (!Number.isFinite(n)) return value;\n\n\t// Работаем со строкой числа, но pad применяем только к целой части\n\tconst raw = String(n); // \"-12.3\" | \"12\" | \"0.5\"\n\n\tif (typeof fixed === \"number\" && fixed > 0) {\n\t\tlet i = 0;\n\t\tlet sign = \"\";\n\n\t\tconst c0 = raw.charCodeAt(0);\n\t\tif (c0 === 45 /*-*/ || c0 === 43 /*+*/) {\n\t\t\tsign = raw[0];\n\t\t\ti = 1;\n\t\t}\n\n\t\t// отделяем дробь\n\t\tconst dot = raw.indexOf(\".\", i);\n\t\tconst intEnd = dot === -1 ? raw.length : dot;\n\n\t\tlet intPart = raw.slice(i, intEnd);\n\t\tconst fracPart = dot === -1 ? \"\" : raw.slice(dot); // включая '.'\n\n\t\t// \".5\" -> \"0.5\" не бывает у String(number), но на всякий случай:\n\t\tif (!intPart) intPart = \"0\";\n\n\t\tconst len = intPart.length;\n\t\tif (len < fixed) {\n\t\t\tintPart = \"0\".repeat(fixed - len) + intPart;\n\t\t}\n\n\t\treturn sign + intPart + fracPart;\n\t}\n\n\treturn raw;\n}\n\n/**\n * Нормализует ведущие нули у числового значения.\n *\n * Использовать только если значения могут иметь нестандартное форматирование, например разделители групп и тд.\n *\n * Правило fixed:\n * - fixed === 0 / undefined / NaN / не finite -> ничего не делает, возвращает исходное значение\n * - fixed < 0 -> удаляет ведущие нули (только в целой части)\n * - fixed > 0 -> дополняет целую часть ведущими нулями до длины fixed\n *\n * Если value не является числом и не парсится в число — возвращается как есть.\n * Возвращаемый тип: если вход number -> number, если вход string -> string, иначе unknown.\n */\nexport function normalizeLeadingZerosStrict<T>(value: T, fixed?: number): T {\n\t// fixed не задан / 0 / не число / бесконечность => no-op\n\tif (!fixed || !Number.isFinite(fixed)) return value;\n\n\t// Быстрый путь для number\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isFinite(value)) return value;\n\n\t\t// Для number делаем преобразование через строку.\n\t\t// Важно: у number ведущих нулей в обычном представлении нет,\n\t\t// поэтому fixed < 0 по факту ничего не меняет.\n\t\tconst out = normalizeStringNumber(String(value), fixed);\n\t\t// Пытаемся вернуть number обратно, чтобы не ломать типы/ожидания.\n\t\tconst n = Number(out);\n\t\treturn Number.isFinite(n) ? (n as unknown as T) : value;\n\t}\n\n\t// Основной путь для строк\n\tif (typeof value === \"string\") {\n\t\t// Быстрая проверка «похоже ли на число»: если вообще нет цифр — не парсим.\n\t\t// (Чуть ускоряет кейсы с произвольными строками).\n\t\tlet hasDigit = false;\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\tconst c = value.charCodeAt(i);\n\t\t\tif (c >= 48 && c <= 57) {\n\t\t\t\thasDigit = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!hasDigit) return value;\n\n\t\t// Проверка, что строка реально парсится в число.\n\t\t// Поддержка \"SAP-like\": пробелы и NBSP как группировка, ',' как десятичный.\n\t\tconst parsed = toFiniteNumber(value);\n\t\tif (parsed === undefined) return value;\n\n\t\tconst out = normalizeStringNumber(value, fixed);\n\t\treturn out as unknown as T;\n\t}\n\n\t// Для остальных типов — не трогаем\n\treturn value;\n}\n\n/**\n * Нормализация для строкового представления числа.\n * Работает только с целой частью (до '.' или ',').\n */\nfunction normalizeStringNumber(input: string, fixed: number): string {\n\tconst s = input.trim();\n\tif (!s) return input;\n\n\t// Знак\n\tlet i = 0;\n\tlet sign = \"\";\n\tconst first = s.charCodeAt(0);\n\tif (first === 45 /* - */ || first === 43 /* + */) {\n\t\tsign = s[0];\n\t\ti = 1;\n\t}\n\n\t// Найдём позицию десятичного разделителя ('.' или ',') — берём первый найденный.\n\t// Нули нормализуем только в целой части.\n\tlet dot = -1;\n\tfor (let k = i; k < s.length; k++) {\n\t\tconst ch = s.charCodeAt(k);\n\t\tif (ch === 46 /* . */ || ch === 44 /* , */) {\n\t\t\tdot = k;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst intEnd = dot === -1 ? s.length : dot;\n\tconst frac = dot === -1 ? \"\" : s.slice(dot); // включая разделитель и дробную часть\n\n\t// Выделим целую часть (без знака)\n\tlet intPart = s.slice(i, intEnd);\n\n\t// Уберём пробелы/неразрывные пробелы внутри (SAP-like grouping), чтобы не мешали.\n\t// Делается без regex, чтобы быть дешевле при массовых вызовах.\n\tintPart = stripInnerSpaces(intPart);\n\n\t// Если целая часть пустая (\".5\") — считаем как \"0\"\n\tif (intPart.length === 0) intPart = \"0\";\n\n\tconst target = fixed | 0; // быстрый int\n\tif (target <= 0) {\n\t\t// Trim leading zeros\n\t\tlet p = 0;\n\t\twhile (p < intPart.length && intPart.charCodeAt(p) === 48 /* '0' */) p++;\n\n\t\t// Если всё было нулями — оставляем один ноль\n\t\tintPart = p === intPart.length ? \"0\" : intPart.slice(p);\n\t} else {\n\t\tif (target > 0) {\n\t\t\tconst len = intPart.length;\n\t\t\tif (len < target) {\n\t\t\t\t// Быстрое дополняние без padStart (чуть меньше накладных расходов)\n\t\t\t\tintPart = \"0\".repeat(target - len) + intPart;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn sign + intPart + frac;\n}\n","import { addUnique } from \"../../array\";\n\nimport { validateFormattersPipelineConfig } from \"./validate\";\n\nimport type { FormattersPipelineConfig } from \"./types\";\n\n/**\n * Минимальный контракт поля, достаточный для сбора runtime-зависимостей.\n *\n * Контракт не привязан к view-config, analytical-table или detail-table:\n * любая зона может передать свой объект, если он содержит эти optional-поля.\n */\nexport type RuntimeDependencyField = {\n\tformattersPipeline?: FormattersPipelineConfig;\n\tcellRenderer?: {\n\t\tbindings?: Readonly<Record<string, string | undefined>>;\n\t};\n};\n\n/**\n * План дополнительных полей, которые нужны runtime-логике отображения.\n */\nexport type RuntimeFieldDependenciesPlan = {\n\t/** Поля, которые нужно добавить в query для formatter pipeline или renderer bindings. */\n\trequiredColumnIds: string[];\n\t/** Зависимости, указанные в настройках, но отсутствующие в справочнике полей. */\n\tmissingColumnIds: string[];\n};\n\n/**\n * Собирает только зависимости, объявленные внутри formatter pipeline.\n *\n * Pipeline не проверяет наличие этих полей в конкретной таблице или графике:\n * это ответственность потребителя, который знает свой полный набор полей.\n */\nexport function collectFormattersPipelineDependencyIds(config: FormattersPipelineConfig | undefined): string[] {\n\tconst validation = validateFormattersPipelineConfig(config);\n\tif (!validation.ok || !validation.plan) return [];\n\n\tconst dependencyIds: string[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const step of validation.plan.steps) {\n\t\tif (step.type === \"resolveValueState\" && step.config.valueSource?.kind === \"formula\") {\n\t\t\tfor (const dependencyId of step.config.valueSource.dependencyIds) {\n\t\t\t\taddUnique(dependencyIds, seen, dependencyId);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (step.type !== \"rowBasedOverride\") continue;\n\n\t\tif (step.config.mode === \"field\") {\n\t\t\taddUnique(dependencyIds, seen, step.config.fieldKey);\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (const dependencyId of step.config.dependencyIds ?? []) {\n\t\t\taddUnique(dependencyIds, seen, dependencyId);\n\t\t}\n\t}\n\n\treturn dependencyIds;\n}\n\n/**\n * Собирает runtime-зависимости отображения для набора полей.\n *\n * Учитывает:\n * - binding-поля renderer-а;\n * - field/dependency поля formatter-а `rowBasedOverride`.\n * - зависимости формулы источника состояния `resolveValueState`.\n */\nexport function collectRuntimeFieldDependencyIds<TField extends RuntimeDependencyField>(\n\tfieldIds: readonly string[],\n\tfieldsById: Readonly<Record<string, TField | undefined>>\n): RuntimeFieldDependenciesPlan {\n\tconst requiredColumnIds: string[] = [];\n\tconst missingColumnIds: string[] = [];\n\tconst seenRequired = new Set<string>();\n\tconst seenMissing = new Set<string>();\n\n\tconst addDependency = (fieldId: string | undefined) => {\n\t\tconst normalizedId = fieldId?.trim();\n\t\tif (!normalizedId) return;\n\n\t\tif (!fieldsById[normalizedId]) {\n\t\t\taddUnique(missingColumnIds, seenMissing, normalizedId);\n\t\t\treturn;\n\t\t}\n\n\t\taddUnique(requiredColumnIds, seenRequired, normalizedId);\n\t};\n\n\tfor (const fieldId of fieldIds) {\n\t\tconst field = fieldsById[fieldId];\n\t\tif (!field) continue;\n\n\t\tfor (const bindingFieldId of Object.values(field.cellRenderer?.bindings ?? {})) {\n\t\t\taddDependency(bindingFieldId);\n\t\t}\n\n\t\tfor (const dependencyId of collectFormattersPipelineDependencyIds(field.formattersPipeline)) {\n\t\t\taddDependency(dependencyId);\n\t\t}\n\t}\n\n\treturn { requiredColumnIds, missingColumnIds };\n}\n","import { State } from \"../../types\";\n\nimport { ValueStateResolver } from \"./types\";\n\n/** Реестр: id → скомпилированная функция-резолвер */\nconst resolverRegistry = new Map<string, ValueStateResolver>();\n\n/** Обратный индекс: каноническая строка конфигурации → id (дедупликация) */\nconst configToIdIndex = new Map<string, string>();\n\n/**\n * Вычислить короткий хеш из строки (djb2 → base36).\n * Результат — строка вида `\"id_1a2b3c4\"`.\n */\nexport function computeShortHash(input: string): string {\n\tlet hash = 5381;\n\tfor (let i = 0; i < input.length; i++) {\n\t\thash = ((hash << 5) + hash + input.charCodeAt(i)) >>> 0;\n\t}\n\treturn \"id_\" + hash.toString(36);\n}\n\n/**\n * Зарегистрировать резолвер в общем реестре.\n * Если конфигурация с таким каноническим ключом уже есть — вернуть существующий id.\n * Возвращает `{ id, isNew }`.\n */\nexport function registerResolver(canonicalKey: string, compileFunction: () => ValueStateResolver): { id: string; isNew: boolean } {\n\tconst existingId = configToIdIndex.get(canonicalKey);\n\tif (existingId) return { id: existingId, isNew: false };\n\n\tconst id = computeShortHash(canonicalKey);\n\tconst resolver = compileFunction();\n\n\tresolverRegistry.set(id, resolver);\n\tconfigToIdIndex.set(canonicalKey, id);\n\n\treturn { id, isNew: true };\n}\n\n/**\n * Найти существующий резолвер по каноническому ключу.\n * Возвращает `undefined`, если не найден.\n */\nexport function findResolverByCanonicalKey(canonicalKey: string): ValueStateResolver | undefined {\n\tconst id = configToIdIndex.get(canonicalKey);\n\tif (!id) return undefined;\n\treturn resolverRegistry.get(id);\n}\n\n/**\n * Применить зарегистрированный резолвер к значению.\n * Если резолвер с указанным `id` не найден — возвращает `\"none\"`.\n *\n * @example\n * ```ts\n * resolveValueState(\"v_1a2b3c4\", 75); // → \"success\"\n * ```\n */\nexport function resolveValueState(id: string, value: unknown): State {\n\tconst resolver = resolverRegistry.get(id);\n\tif (!resolver) return \"none\";\n\treturn resolver(value);\n}\n\n/**\n * Получить функцию-резолвер по `id`.\n * Возвращает `undefined`, если резолвер не зарегистрирован.\n */\nexport function getValueStateResolver(id: string): ValueStateResolver | undefined {\n\treturn resolverRegistry.get(id);\n}\n\n/**\n * Получить список всех зарегистрированных id.\n */\nexport function getValueStateResolverIds(): string[] {\n\treturn Array.from(resolverRegistry.keys());\n}\n\n/**\n * Полная очистка реестра. Предназначена для тестов.\n */\nexport function resetValueStateResolvers(): void {\n\tresolverRegistry.clear();\n\tconfigToIdIndex.clear();\n}\n","import { compareStrings } from \"../../string-comparison\";\nimport { State } from \"../../types\";\n\nimport { FixedValueStateResolverConfig, ValueStateResolver } from \"./types\";\nimport { findResolverByCanonicalKey, registerResolver } from \"./valueStateRegistry\";\n\n/** Создать каноническую строку конфигурации (с префиксом типа для изоляции от пороговых резолверов) */\nfunction createCanonicalKey(sortedEntries: [string, State][], fallbackState: State): string {\n\tconst entriesPart = sortedEntries.map(([key, state]) => `${key}:${state}`).join(\",\");\n\treturn `fixed|${entriesPart}|${fallbackState}`;\n}\n\n/** Скомпилировать функцию-резолвер из маппинга */\nfunction compileFixedResolver(entries: Record<string, State>, fallbackState: State): ValueStateResolver {\n\tconst lookup = new Map<string, State>(Object.entries(entries));\n\n\treturn (value: unknown): State => {\n\t\tif (value === null || value === undefined) return fallbackState;\n\n\t\tconst key = String(value);\n\t\treturn lookup.get(key) ?? fallbackState;\n\t};\n}\n\n/** Нормализовать конфигурацию и вернуть подготовленные данные */\nfunction prepareConfig(config: FixedValueStateResolverConfig) {\n\tconst fallbackState: State = config.fallbackState ?? \"none\";\n\n\t// Сортировка ключей для детерминированного канонического ключа\n\tconst sortedEntries = Object.entries(config.entries).sort(([a], [b]) => compareStrings(a, b)) as [string, State][];\n\n\tconst canonicalKey = createCanonicalKey(sortedEntries, fallbackState);\n\n\treturn { entries: config.entries, fallbackState, canonicalKey };\n}\n\n/**\n * Зарегистрировать фиксированный резолвер из конфигурации.\n * Возвращает короткий уникальный `id`.\n *\n * Фиксированный резолвер определяет State по точному совпадению значения ячейки\n * со значениями из маппинга `entries`.\n *\n * @example\n * ```ts\n * const id = registerFixedResolver({\n * entries: {\n * \"01\": \"success\",\n * \"02\": \"warning\",\n * \"03\": \"error\",\n * },\n * fallbackState: \"none\",\n * });\n *\n * // В ячейке: resolveValueState(id, cellValue)\n * ```\n */\nexport function registerFixedResolver(config: FixedValueStateResolverConfig): string {\n\tconst { entries, fallbackState, canonicalKey } = prepareConfig(config);\n\n\tconst { id } = registerResolver(canonicalKey, () => compileFixedResolver(entries, fallbackState));\n\n\treturn id;\n}\n\n/**\n * Создать фиксированный резолвер напрямую.\n * Резолвер регистрируется в общем реестре.\n * Идентичные конфигурации возвращают одну и ту же функцию.\n *\n * @example\n * ```ts\n * const resolve = createFixedResolver({\n * entries: { \"01\": \"success\", \"02\": \"warning\", \"03\": \"error\" },\n * });\n *\n * resolve(\"01\"); // → \"success\"\n * resolve(\"02\"); // → \"warning\"\n * resolve(\"99\"); // → \"none\" (fallback)\n * ```\n */\nexport function createFixedResolver(config: FixedValueStateResolverConfig): ValueStateResolver {\n\tconst { entries, fallbackState, canonicalKey } = prepareConfig(config);\n\n\tconst existing = findResolverByCanonicalKey(canonicalKey);\n\tif (existing) return existing;\n\n\tregisterResolver(canonicalKey, () => compileFixedResolver(entries, fallbackState));\n\n\treturn findResolverByCanonicalKey(canonicalKey)!;\n}\n","import { State } from \"../../types\";\n\nimport { ThresholdBoundary, ThresholdDefinition, ThresholdValueStateResolverConfig, ValueStateResolver } from \"./types\";\nimport { findResolverByCanonicalKey, registerResolver } from \"./valueStateRegistry\";\n\n/**\n * Нормализованный порог (внутренний тип)\n */\ninterface NormalizedThreshold {\n\tvalue: number;\n\tboundary: ThresholdBoundary;\n}\n\n/** Нормализовать порог: число → объект с boundary по умолчанию */\nfunction normalizeThreshold(threshold: number | ThresholdDefinition): NormalizedThreshold {\n\tif (typeof threshold === \"number\") {\n\t\treturn { value: threshold, boundary: \"upper\" };\n\t}\n\treturn { value: threshold.value, boundary: threshold.boundary ?? \"upper\" };\n}\n\n/** Создать каноническую строку конфигурации (с префиксом типа для изоляции от других резолверов) */\nfunction createCanonicalKey(sortedThresholds: NormalizedThreshold[], states: State[], invalidState: State): string {\n\tconst thresholdsPart = sortedThresholds.map((t) => `${t.value}:${t.boundary}`).join(\",\");\n\treturn `threshold|${thresholdsPart}|${states.join(\",\")}|${invalidState}`;\n}\n\n/** Валидация конфигурации */\nfunction validateConfig(thresholds: NormalizedThreshold[], states: State[]): void {\n\tif (states.length !== thresholds.length + 1) {\n\t\tthrow new Error(`Количество состояний (${states.length}) должно быть равно количеству порогов + 1 (${thresholds.length + 1})`);\n\t}\n\n\tconst seen = new Set<number>();\n\tfor (const threshold of thresholds) {\n\t\tif (seen.has(threshold.value)) {\n\t\t\tconsole.warn(`Дублирующееся пороговое значение: ${threshold.value}`);\n\t\t}\n\t\tseen.add(threshold.value);\n\t}\n}\n\n/** Скомпилировать функцию-резолвер из нормализованных данных */\nfunction compileThresholdResolver(sortedThresholds: NormalizedThreshold[], configStates: State[], invalidState: State): ValueStateResolver {\n\tconst thresholdValues = sortedThresholds.map((t) => t.value);\n\tconst thresholdIsLower = sortedThresholds.map((t) => t.boundary === \"lower\");\n\tconst states = [...configStates];\n\n\treturn (value: unknown): State => {\n\t\tif (value === \"\" || value === null || value === undefined) return invalidState;\n\n\t\tconst numericValue = +(value as number);\n\n\t\tif (!Number.isFinite(numericValue)) return invalidState;\n\n\t\tfor (let i = 0; i < thresholdValues.length; i++) {\n\t\t\tconst belongsToLowerSegment = thresholdIsLower[i] ? numericValue <= thresholdValues[i] : numericValue < thresholdValues[i];\n\n\t\t\tif (belongsToLowerSegment) return states[i];\n\t\t}\n\n\t\treturn states[states.length - 1];\n\t};\n}\n\n/** Нормализовать конфигурацию и вернуть подготовленные данные */\nfunction prepareConfig(config: ThresholdValueStateResolverConfig) {\n\tconst invalidState: State = config.invalidState ?? \"none\";\n\tconst sortedThresholds = config.thresholds.map(normalizeThreshold).sort((a, b) => a.value - b.value);\n\n\tvalidateConfig(sortedThresholds, config.states);\n\n\tconst canonicalKey = createCanonicalKey(sortedThresholds, config.states, invalidState);\n\n\treturn { sortedThresholds, invalidState, canonicalKey, states: config.states };\n}\n\n/**\n * Зарегистрировать пороговый резолвер из конфигурации.\n * Возвращает короткий уникальный `id` для использования в столбцах таблицы.\n *\n * Идентичные конфигурации (даже с разным порядком порогов)\n * возвращают один и тот же `id` — функция дедуплицирует по содержимому.\n *\n * @example\n * ```ts\n * const id = registerThresholdResolver({\n * thresholds: [60, 95],\n * states: [\"warning\", \"success\", \"error\"],\n * });\n * // id → \"v_1a2b3c4\"\n *\n * // В ячейке: resolveValueState(id, cellValue)\n * ```\n */\nexport function registerThresholdResolver(config: ThresholdValueStateResolverConfig): string {\n\tconst { sortedThresholds, invalidState, canonicalKey, states } = prepareConfig(config);\n\n\tconst { id } = registerResolver(canonicalKey, () => compileThresholdResolver(sortedThresholds, states, invalidState));\n\n\treturn id;\n}\n\n/**\n * Создать функцию-резолвер напрямую.\n * Резолвер регистрируется в общем реестре.\n * Идентичные конфигурации возвращают одну и ту же функцию.\n *\n * @example\n * ```ts\n * const resolve = createThresholdResolver({\n * thresholds: [60, 95],\n * states: [\"warning\", \"success\", \"error\"],\n * });\n *\n * resolve(45); // → \"warning\"\n * resolve(75); // → \"success\"\n * resolve(100); // → \"error\"\n * resolve(\"abc\"); // → \"none\"\n * ```\n */\nexport function createThresholdResolver(config: ThresholdValueStateResolverConfig): ValueStateResolver {\n\tconst { sortedThresholds, invalidState, canonicalKey, states } = prepareConfig(config);\n\n\tconst existing = findResolverByCanonicalKey(canonicalKey);\n\tif (existing) return existing;\n\n\tregisterResolver(canonicalKey, () => compileThresholdResolver(sortedThresholds, states, invalidState));\n\n\treturn findResolverByCanonicalKey(canonicalKey)!;\n}\n","import { compileTableFormula } from \"../../formulas\";\nimport { getBaseTypeFromODataType } from \"../../odata-service\";\nimport { isSafe } from \"../../validators\";\nimport { formatDate, getDatePreset, parseDate } from \"../date\";\nimport { normalizeLeadingZeros } from \"../normalizeLeadingZeros\";\nimport { formatNumber, getNumberPreset } from \"../number\";\nimport { createRowBasedFormatterContext, getRowBasedFormatterById } from \"../rowBased\";\nimport { toSafeString } from \"../strings\";\nimport { registerFixedResolver } from \"../valueState/fixedValueStateResolver\";\nimport { registerThresholdResolver } from \"../valueState/thresholdValueStateResolver\";\nimport { resolveValueState } from \"../valueState/valueStateRegistry\";\n\nimport { validateFormattersPipelineConfig } from \"./validate\";\n\nimport type {\n\tFormattersPipelineConfig,\n\tFormattersPipelineExecutionContext,\n\tFormattersPipelineExecutionResult,\n\tFormattersPipelineExecutor,\n\tFormattersPipelineTypedValueContext,\n\tFormattersPipelineTypedValueFormatConfig,\n\tFormattersPipelineValueIcon\n} from \"./types\";\n\ntype CompileFormattersPipelineExecutorResult =\n\t| {\n\t\t\tok: true;\n\t\t\texecutor: FormattersPipelineExecutor;\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\treason: \"pipeline_invalid\" | \"row_based_formula_not_found\" | \"value_state_formula_not_found\";\n\t };\n\nconst DEFAULT_TYPED_VALUE_FORMAT_PRESETS = Object.freeze({\n\tnumber: \"decimal\",\n\tdate: \"date\"\n});\n\n/**\n * Выполняет типизированное форматирование по метаданным колонки.\n */\nexport function formatTypedCellValue(\n\tvalue: unknown,\n\tcolumn: FormattersPipelineTypedValueContext,\n\tconfig?: FormattersPipelineTypedValueFormatConfig\n): string {\n\tif (!isSafe(value)) return \"\";\n\n\tconst baseType = getBaseTypeFromODataType(column.type);\n\n\tif (baseType === \"number\") {\n\t\tconst normalizedPresetName = config?.numberPresetName?.trim();\n\t\tconst presetName =\n\t\t\tnormalizedPresetName && getNumberPreset(normalizedPresetName)\n\t\t\t\t? normalizedPresetName\n\t\t\t\t: DEFAULT_TYPED_VALUE_FORMAT_PRESETS.number;\n\t\treturn formatNumber(value, presetName);\n\t}\n\n\tif (baseType === \"date\") {\n\t\tconst normalizedPresetName = config?.datePresetName?.trim();\n\t\tconst presetName =\n\t\t\tnormalizedPresetName && getDatePreset(normalizedPresetName) ? normalizedPresetName : DEFAULT_TYPED_VALUE_FORMAT_PRESETS.date;\n\t\treturn formatDate(parseDate(value), presetName);\n\t}\n\n\treturn toSafeString(value);\n}\n\nfunction resolveIconState(value: string): FormattersPipelineValueIcon | undefined {\n\tif (value === \"success\" || value === \"warning\" || value === \"error\" || value === \"information\") {\n\t\treturn value;\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Определяет, должен ли `rowBasedOverride` применяться для текущего типа строки.\n *\n * Семантика:\n * - `group` — historical analytical group-row;\n * - `totals` — применяется только formula-режим;\n * - `plain`/`tree` — generic row-based override.\n */\nfunction shouldApplyRowBasedOverride(ctx: FormattersPipelineExecutionContext, mode: \"field\" | \"formula\"): boolean {\n\tswitch (ctx.rowKind) {\n\t\tcase \"group\":\n\t\tcase \"plain\":\n\t\tcase \"tree\":\n\t\t\treturn true;\n\t\tcase \"totals\":\n\t\t\treturn mode === \"formula\";\n\t}\n}\n\n/**\n * Компилирует pipeline форматтеров колонки в runtime executor.\n */\nexport function compileFormattersPipelineExecutor(args: {\n\tconfig: FormattersPipelineConfig | undefined;\n\tcolumn: FormattersPipelineTypedValueContext;\n}): CompileFormattersPipelineExecutorResult {\n\tconst validation = validateFormattersPipelineConfig(args.config);\n\tif (!validation.ok || !validation.plan) {\n\t\tif (validation.errors.some((error) => error.code === \"step_value_state_formula_not_found\")) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason: \"value_state_formula_not_found\"\n\t\t\t};\n\t\t}\n\n\t\tif (validation.errors.some((error) => error.code === \"step_row_based_override_formula_not_found\")) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason: \"row_based_formula_not_found\"\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"pipeline_invalid\"\n\t\t};\n\t}\n\n\tconst { steps } = validation.plan;\n\tconst hasRowBasedOverride = steps.some((step) => step.type === \"rowBasedOverride\");\n\tconst hasTypedValueFormat = steps.some((step) => step.type === \"typedValueFormat\");\n\n\tconst valueStateStep = steps.find((step) => step.type === \"resolveValueState\");\n\tconst valueStateSource = valueStateStep?.config.valueSource;\n\t// Формулу компилируем один раз вместе с колонкой, а не для каждой ячейки.\n\tconst valueStateFormula =\n\t\tvalueStateSource?.kind === \"formula\"\n\t\t\t? compileTableFormula({ formulaId: valueStateSource.formulaId, keys: valueStateSource.dependencyIds })\n\t\t\t: undefined;\n\tif (valueStateFormula && !valueStateFormula.ok) {\n\t\treturn { ok: false, reason: \"value_state_formula_not_found\" };\n\t}\n\tconst valueStateResolverId =\n\t\tvalueStateStep?.config.resolver.kind === \"fixed\"\n\t\t\t? registerFixedResolver({\n\t\t\t\t\tentries: valueStateStep.config.resolver.entries,\n\t\t\t\t\tfallbackState: valueStateStep.config.resolver.fallbackState\n\t\t\t\t})\n\t\t\t: valueStateStep?.config.resolver.kind === \"threshold\"\n\t\t\t\t? registerThresholdResolver({\n\t\t\t\t\t\tthresholds: valueStateStep.config.resolver.thresholds,\n\t\t\t\t\t\tstates: valueStateStep.config.resolver.states,\n\t\t\t\t\t\tinvalidState: valueStateStep.config.resolver.invalidState\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\n\tconst rowBasedOverrideStep = steps.find((step) => step.type === \"rowBasedOverride\");\n\tconst rowBasedFormula =\n\t\trowBasedOverrideStep?.config.mode === \"formula\" ? getRowBasedFormatterById(rowBasedOverrideStep.config.formulaId) : undefined;\n\tif (rowBasedOverrideStep?.config.mode === \"formula\" && !rowBasedFormula) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"row_based_formula_not_found\"\n\t\t};\n\t}\n\n\treturn {\n\t\tok: true,\n\t\texecutor: {\n\t\t\thasRowBasedOverride,\n\t\t\thasTypedValueFormat,\n\t\t\texecute: (ctx: FormattersPipelineExecutionContext): FormattersPipelineExecutionResult => {\n\t\t\t\tlet nextValue = ctx.value;\n\t\t\t\tlet nextState: FormattersPipelineExecutionResult[\"state\"] = \"none\";\n\t\t\t\tlet showIcon = false;\n\t\t\t\tlet showValue = true;\n\t\t\t\tlet iconPosition: FormattersPipelineExecutionResult[\"iconPosition\"] = \"left\";\n\t\t\t\tlet icon: FormattersPipelineExecutionResult[\"icon\"] = undefined;\n\n\t\t\t\tfor (const step of steps) {\n\t\t\t\t\tswitch (step.type) {\n\t\t\t\t\t\tcase \"normalizeLeadingZeros\":\n\t\t\t\t\t\t\tnextValue = normalizeLeadingZeros(nextValue, step.config?.fixed);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"rowBasedOverride\":\n\t\t\t\t\t\t\tif (!shouldApplyRowBasedOverride(ctx, step.config.mode)) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (step.config.mode === \"field\") {\n\t\t\t\t\t\t\t\tconst fieldValue = ctx.rowData[step.config.fieldKey];\n\t\t\t\t\t\t\t\tif (fieldValue == null && step.config.fallbackToRaw !== false) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tnextValue = fieldValue;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (!rowBasedFormula) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconst formulaValue = rowBasedFormula.fn(\n\t\t\t\t\t\t\t\t\tcreateRowBasedFormatterContext({\n\t\t\t\t\t\t\t\t\t\trowData: ctx.rowData,\n\t\t\t\t\t\t\t\t\t\trawValue: nextValue,\n\t\t\t\t\t\t\t\t\t\tcolumnId: ctx.columnId,\n\t\t\t\t\t\t\t\t\t\tkeys: step.config.dependencyIds\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (formulaValue == null && step.config.fallbackToRaw !== false) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tnextValue = formulaValue;\n\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\tif (step.config.fallbackToRaw === false) {\n\t\t\t\t\t\t\t\t\tnextValue = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"resolveValueState\":\n\t\t\t\t\t\t\tif (!valueStateResolverId) {\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (valueStateSource?.kind === \"formula\") {\n\t\t\t\t\t\t\t\tconst rowData = ctx.valueStateFormulaRowData ?? ctx.rowData;\n\t\t\t\t\t\t\t\t// ctx.num() превращает отсутствующие значения в 0. Для состояния\n\t\t\t\t\t\t\t\t// отсутствие исходных данных нельзя выдавать за успешный расчёт.\n\t\t\t\t\t\t\t\tconst hasDependencies = valueStateSource.dependencyIds.every(\n\t\t\t\t\t\t\t\t\t(dependencyId) =>\n\t\t\t\t\t\t\t\t\t\tObject.hasOwn(rowData, dependencyId) &&\n\t\t\t\t\t\t\t\t\t\trowData[dependencyId] != null &&\n\t\t\t\t\t\t\t\t\t\t(typeof rowData[dependencyId] !== \"string\" || rowData[dependencyId].trim() !== \"\")\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tconst result = hasDependencies && valueStateFormula?.ok ? valueStateFormula.execute(rowData) : undefined;\n\t\t\t\t\t\t\t\tnextState = result?.ok ? resolveValueState(valueStateResolverId, result.value) : \"none\";\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tnextState = resolveValueState(valueStateResolverId, nextValue);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tshowIcon = step.config.icon?.enabled ?? false;\n\t\t\t\t\t\t\tshowValue = step.config.icon?.showValue ?? true;\n\t\t\t\t\t\t\ticonPosition = step.config.icon?.position ?? \"left\";\n\t\t\t\t\t\t\ticon = showIcon ? resolveIconState(nextState) : undefined;\n\t\t\t\t\t\t\tif (valueStateSource?.kind === \"formula\" && nextState === \"none\") {\n\t\t\t\t\t\t\t\tshowIcon = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"typedValueFormat\":\n\t\t\t\t\t\t\tnextValue = formatTypedCellValue(nextValue, args.column, step.config);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tvalue: nextValue,\n\t\t\t\t\tstate: nextState,\n\t\t\t\t\ticon,\n\t\t\t\t\tshowIcon,\n\t\t\t\t\tshowValue,\n\t\t\t\t\ticonPosition,\n\t\t\t\t\thasTypedValueFormat\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t};\n}\n","import { type State } from \"../../types\";\nimport { isRecord } from \"../../validators/isRecord\";\n\nimport { cloneFormattersPipelineConfig } from \"./clone\";\nimport {\n\ttype FormattersPipelineConfig,\n\ttype FormattersPipelineEdge,\n\ttype FormattersPipelineGraph,\n\ttype FormattersPipelineNode,\n\ttype FormattersPipelinePlan,\n\ttype FormattersPipelineResolveValueStateConfig,\n\ttype FormattersPipelineRowBasedOverrideConfig,\n\ttype FormattersPipelineStep,\n\ttype FormattersPipelineTypedValueFormatConfig\n} from \"./types\";\nimport { validateFormattersPipelineConfig } from \"./validate\";\n\nconst VALUE_STATES: readonly State[] = [\"\", \"none\", \"information\", \"success\", \"warning\", \"error\"];\n\nfunction isState(value: unknown): value is State {\n\treturn typeof value === \"string\" && VALUE_STATES.some((state) => state === value);\n}\n\nfunction isOptionalBoolean(value: unknown): boolean {\n\treturn value === undefined || typeof value === \"boolean\";\n}\n\nfunction isOptionalString(value: unknown): boolean {\n\treturn value === undefined || typeof value === \"string\";\n}\n\nfunction isTypedValueFormatConfig(value: unknown): value is FormattersPipelineTypedValueFormatConfig {\n\tif (value === undefined) return true;\n\tif (!isRecord(value)) return false;\n\n\treturn isOptionalString(value.numberPresetName) && isOptionalString(value.datePresetName);\n}\n\nfunction isRowBasedOverrideConfig(value: unknown): value is FormattersPipelineRowBasedOverrideConfig {\n\tif (!isRecord(value)) return false;\n\n\tif (value.mode === \"field\") {\n\t\treturn typeof value.fieldKey === \"string\" && isOptionalBoolean(value.fallbackToRaw);\n\t}\n\n\tif (value.mode === \"formula\") {\n\t\treturn (\n\t\t\ttypeof value.formulaId === \"string\" &&\n\t\t\t(value.dependencyIds === undefined ||\n\t\t\t\t(Array.isArray(value.dependencyIds) && value.dependencyIds.every((dependencyId) => typeof dependencyId === \"string\"))) &&\n\t\t\tisOptionalBoolean(value.fallbackToRaw)\n\t\t);\n\t}\n\n\treturn false;\n}\n\nfunction isThresholdDefinition(value: unknown): boolean {\n\treturn (\n\t\ttypeof value === \"number\" ||\n\t\t(isRecord(value) &&\n\t\t\ttypeof value.value === \"number\" &&\n\t\t\t(value.boundary === undefined || value.boundary === \"lower\" || value.boundary === \"upper\"))\n\t);\n}\n\nfunction isResolveValueStateConfig(value: unknown): value is FormattersPipelineResolveValueStateConfig {\n\tif (!isRecord(value) || !isRecord(value.resolver)) return false;\n\n\tconst { resolver, icon } = value;\n\tconst source = value.valueSource;\n\tconst hasValidSource =\n\t\tsource === undefined ||\n\t\t(isRecord(source) &&\n\t\t\t(source.kind === \"value\" ||\n\t\t\t\t(source.kind === \"formula\" &&\n\t\t\t\t\ttypeof source.formulaId === \"string\" &&\n\t\t\t\t\tArray.isArray(source.dependencyIds) &&\n\t\t\t\t\tsource.dependencyIds.every((dependencyId) => typeof dependencyId === \"string\"))));\n\tconst hasValidIcon =\n\t\ticon === undefined ||\n\t\t(isRecord(icon) &&\n\t\t\tisOptionalBoolean(icon.enabled) &&\n\t\t\tisOptionalBoolean(icon.showValue) &&\n\t\t\t(icon.position === undefined || icon.position === \"left\" || icon.position === \"right\"));\n\n\tif (!hasValidIcon || !hasValidSource) return false;\n\n\tif (resolver.kind === \"fixed\") {\n\t\treturn (\n\t\t\tisRecord(resolver.entries) &&\n\t\t\tObject.values(resolver.entries).every(isState) &&\n\t\t\t(resolver.fallbackState === undefined || isState(resolver.fallbackState))\n\t\t);\n\t}\n\n\tif (resolver.kind === \"threshold\") {\n\t\treturn (\n\t\t\tArray.isArray(resolver.thresholds) &&\n\t\t\tresolver.thresholds.every(isThresholdDefinition) &&\n\t\t\tArray.isArray(resolver.states) &&\n\t\t\tresolver.states.every(isState) &&\n\t\t\t(resolver.invalidState === undefined || isState(resolver.invalidState))\n\t\t);\n\t}\n\n\treturn false;\n}\n\nfunction isPipelineStep(value: unknown): value is FormattersPipelineStep {\n\tif (!isRecord(value) || typeof value.id !== \"string\") return false;\n\n\tif (value.type === \"normalizeLeadingZeros\") {\n\t\treturn (\n\t\t\tvalue.config === undefined ||\n\t\t\t(isRecord(value.config) && (value.config.fixed === undefined || typeof value.config.fixed === \"number\"))\n\t\t);\n\t}\n\n\tif (value.type === \"rowBasedOverride\") {\n\t\treturn isRowBasedOverrideConfig(value.config);\n\t}\n\n\tif (value.type === \"resolveValueState\") {\n\t\treturn isResolveValueStateConfig(value.config);\n\t}\n\n\tif (value.type === \"typedValueFormat\") {\n\t\treturn isTypedValueFormatConfig(value.config);\n\t}\n\n\treturn false;\n}\n\nfunction isPipelinePlan(value: unknown): value is FormattersPipelinePlan {\n\treturn isRecord(value) && Array.isArray(value.steps) && value.steps.every(isPipelineStep);\n}\n\nfunction isPipelinePosition(value: unknown): value is { x: number; y: number } {\n\treturn isRecord(value) && typeof value.x === \"number\" && typeof value.y === \"number\";\n}\n\nfunction isPipelineNode(value: unknown): value is FormattersPipelineNode {\n\tif (!isRecord(value) || typeof value.id !== \"string\" || !isPipelinePosition(value.position)) return false;\n\n\tif (value.type === \"source\" || value.type === \"sink\") {\n\t\treturn true;\n\t}\n\n\treturn isPipelineStep(value);\n}\n\nfunction isPipelineEdge(value: unknown): value is FormattersPipelineEdge {\n\treturn isRecord(value) && typeof value.id === \"string\" && typeof value.source === \"string\" && typeof value.target === \"string\";\n}\n\nfunction isPipelineGraph(value: unknown): value is FormattersPipelineGraph {\n\treturn (\n\t\tisRecord(value) &&\n\t\tArray.isArray(value.nodes) &&\n\t\tvalue.nodes.every(isPipelineNode) &&\n\t\tArray.isArray(value.edges) &&\n\t\tvalue.edges.every(isPipelineEdge)\n\t);\n}\n\nfunction isPipelineConfig(value: unknown): value is FormattersPipelineConfig {\n\treturn (\n\t\tisRecord(value) &&\n\t\tvalue.version === 1 &&\n\t\t(value.plan === undefined || isPipelinePlan(value.plan)) &&\n\t\t(value.graph === undefined || isPipelineGraph(value.graph)) &&\n\t\t(isPipelinePlan(value.plan) || isPipelineGraph(value.graph))\n\t);\n}\n\nexport function normalizeFormattersPipelineConfig(value: unknown): FormattersPipelineConfig | undefined {\n\tif (!isPipelineConfig(value)) return undefined;\n\n\tconst validation = validateFormattersPipelineConfig(value);\n\tif (!validation.ok) return undefined;\n\n\treturn cloneFormattersPipelineConfig(value);\n}\n","import type { FormattersPipelineFormatterId } from \"./types\";\n\n/**\n * Описание вида форматтера для каталога.\n */\nexport type FormattersPipelineDefinition = {\n\tid: FormattersPipelineFormatterId;\n\tname: string;\n\tdescription: string;\n};\n\n/**\n * Справочник доступных видов форматтеров.\n */\nexport const formattersPipelineDefinitions: readonly FormattersPipelineDefinition[] = Object.freeze([\n\t{\n\t\tid: \"normalizeLeadingZeros\",\n\t\tname: \"Нормализация ведущих нулей\",\n\t\tdescription: \"Нормализует числовое значение по правилам fixed.\"\n\t},\n\t{\n\t\tid: \"rowBasedOverride\",\n\t\tname: \"Контекстная формула\",\n\t\tdescription: \"Подменяет значение из другого поля текущей строки или отдельной формулы.\"\n\t},\n\t{\n\t\tid: \"resolveValueState\",\n\t\tname: \"Определение состояния значения\",\n\t\tdescription: \"Вычисляет value state и опционально настраивает отображение статусной иконки.\"\n\t},\n\t{\n\t\tid: \"typedValueFormat\",\n\t\tname: \"Типизированное форматирование\",\n\t\tdescription: \"Формирует итоговый внешний вид значения по типу и роли столбца.\"\n\t}\n]);\n","import { formattersPipelineDefinitions } from \"./definitions\";\n\nimport type { FormattersPipelineDefinition } from \"./definitions\";\nimport type { FormattersPipelineFormatterId } from \"./types\";\n\nconst formatterDefinitionsById = new Map<FormattersPipelineFormatterId, FormattersPipelineDefinition>(\n\tformattersPipelineDefinitions.map((definition) => [definition.id, definition])\n);\n\n/**\n * Возвращает каталог видов форматтеров pipeline.\n */\nexport function getFormattersPipelineDefinitions(): readonly FormattersPipelineDefinition[] {\n\treturn formattersPipelineDefinitions;\n}\n\n/**\n * Возвращает описание вида форматтера по id.\n */\nexport function getFormattersPipelineDefinitionById(id: FormattersPipelineFormatterId): FormattersPipelineDefinition | undefined {\n\treturn formatterDefinitionsById.get(id);\n}\n","import { uuidv4 } from \"../../crypto\";\n\nimport { cloneResolveValueStateConfig } from \"./clone\";\nimport { validateFormattersPipelineGraph } from \"./validate\";\n\nimport type {\n\tFormattersPipelineConfig,\n\tFormattersPipelineEdge,\n\tFormattersPipelineGraph,\n\tFormattersPipelineNode,\n\tFormattersPipelinePlan\n} from \"./types\";\n\ntype IdFactory = () => string;\n\nfunction isSystemNode(node: FormattersPipelineNode): boolean {\n\treturn node.type === \"source\" || node.type === \"sink\";\n}\n\nfunction cloneNodeWithId(node: FormattersPipelineNode, id: string): FormattersPipelineNode {\n\tswitch (node.type) {\n\t\tcase \"source\":\n\t\tcase \"sink\":\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tid,\n\t\t\t\tposition: { ...node.position }\n\t\t\t};\n\t\tcase \"typedValueFormat\":\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tid,\n\t\t\t\tposition: { ...node.position },\n\t\t\t\tconfig: node.config ? { ...node.config } : undefined\n\t\t\t};\n\t\tcase \"normalizeLeadingZeros\":\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tid,\n\t\t\t\tposition: { ...node.position },\n\t\t\t\tconfig: node.config ? { ...node.config } : undefined\n\t\t\t};\n\t\tcase \"rowBasedOverride\":\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tid,\n\t\t\t\tposition: { ...node.position },\n\t\t\t\tconfig:\n\t\t\t\t\tnode.config.mode === \"formula\"\n\t\t\t\t\t\t? { ...node.config, dependencyIds: node.config.dependencyIds ? [...node.config.dependencyIds] : undefined }\n\t\t\t\t\t\t: { ...node.config }\n\t\t\t};\n\t\tcase \"resolveValueState\":\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tid,\n\t\t\t\tposition: { ...node.position },\n\t\t\t\tconfig: cloneResolveValueStateConfig(node.config)\n\t\t\t};\n\t}\n}\n\nfunction rekeyGraph(graph: FormattersPipelineGraph, idFactory: IdFactory): FormattersPipelineGraph {\n\tconst nodeIdByPreviousId = new Map<string, string>();\n\n\tfor (const node of graph.nodes) {\n\t\tnodeIdByPreviousId.set(node.id, isSystemNode(node) ? node.id : idFactory());\n\t}\n\n\tconst edges: FormattersPipelineEdge[] = graph.edges.map((edge) => ({\n\t\tid: idFactory(),\n\t\tsource: nodeIdByPreviousId.get(edge.source) ?? edge.source,\n\t\ttarget: nodeIdByPreviousId.get(edge.target) ?? edge.target\n\t}));\n\n\treturn {\n\t\tnodes: graph.nodes.map((node) => cloneNodeWithId(node, nodeIdByPreviousId.get(node.id) ?? node.id)),\n\t\tedges,\n\t\tviewport: graph.viewport ? { ...graph.viewport } : undefined\n\t};\n}\n\nfunction rekeyPlan(plan: FormattersPipelinePlan, idFactory: IdFactory): FormattersPipelinePlan {\n\treturn {\n\t\tsteps: plan.steps.map((step) => {\n\t\t\tswitch (step.type) {\n\t\t\t\tcase \"typedValueFormat\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...step,\n\t\t\t\t\t\tid: idFactory(),\n\t\t\t\t\t\tconfig: step.config ? { ...step.config } : undefined\n\t\t\t\t\t};\n\t\t\t\tcase \"normalizeLeadingZeros\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...step,\n\t\t\t\t\t\tid: idFactory(),\n\t\t\t\t\t\tconfig: step.config ? { ...step.config } : undefined\n\t\t\t\t\t};\n\t\t\t\tcase \"rowBasedOverride\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...step,\n\t\t\t\t\t\tid: idFactory(),\n\t\t\t\t\t\tconfig:\n\t\t\t\t\t\t\tstep.config.mode === \"formula\"\n\t\t\t\t\t\t\t\t? { ...step.config, dependencyIds: step.config.dependencyIds ? [...step.config.dependencyIds] : undefined }\n\t\t\t\t\t\t\t\t: { ...step.config }\n\t\t\t\t\t};\n\t\t\t\tcase \"resolveValueState\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...step,\n\t\t\t\t\t\tid: idFactory(),\n\t\t\t\t\t\tconfig: cloneResolveValueStateConfig(step.config)\n\t\t\t\t\t};\n\t\t\t}\n\t\t})\n\t};\n}\n\n/**\n * Переиздаёт технические id formatter-нод и step-ов без изменения настроек pipeline.\n *\n * Source/sink сохраняют стабильные id, потому что редактор использует их как\n * служебные якоря графа; все пользовательские formatter id не имеют бизнес-смысла.\n */\nexport function rekeyFormattersPipelineConfig(\n\tconfig: FormattersPipelineConfig | undefined,\n\tidFactory: IdFactory = uuidv4\n): FormattersPipelineConfig | undefined {\n\tif (!config) return undefined;\n\n\tconst graph = config.graph ? rekeyGraph(config.graph, idFactory) : undefined;\n\tconst graphValidation = graph ? validateFormattersPipelineGraph(graph) : undefined;\n\n\treturn {\n\t\tversion: config.version,\n\t\tgraph,\n\t\tplan:\n\t\t\tgraphValidation?.ok && graphValidation.plan ? graphValidation.plan : config.plan ? rekeyPlan(config.plan, idFactory) : undefined\n\t};\n}\n","import { compileTableFormula, type TableFormulaCompiledExecutor, type TableFormulaRowData } from \"../../formulas\";\nimport { isZeroValue } from \"../number\";\nimport { toSafeString } from \"../strings\";\n\nimport { compileFormattersPipelineExecutor, formatTypedCellValue } from \"./execute\";\n\nimport type {\n\tFormatPipelineDisplayValueArgs,\n\tFormattersPipelineDisplayValue,\n\tFormattersPipelineExecutionResult,\n\tFormattersPipelineRuntimeField,\n\tFormattersPipelineRuntimeFields\n} from \"./types\";\n\ntype ResolvedPipelineSourceValue = {\n\tvalue: unknown;\n\tshouldRenderEmpty: boolean;\n};\n\nconst missingFormulaExecutor: TableFormulaCompiledExecutor = () => ({ ok: false, reason: \"formula_not_found\" });\n\nfunction createDefaultDisplayValue(value: unknown, overflowTooltip: boolean): FormattersPipelineDisplayValue {\n\treturn {\n\t\tvalue,\n\t\tstate: \"none\",\n\t\tshowIcon: false,\n\t\tshowValue: true,\n\t\ticonPosition: \"left\",\n\t\toverflowTooltip\n\t};\n}\n\nfunction formatFallbackValue(value: unknown, field: Pick<FormattersPipelineRuntimeField, \"role\" | \"type\"> | undefined): string {\n\tif (!field) {\n\t\treturn toSafeString(value);\n\t}\n\n\treturn formatTypedCellValue(value, field);\n}\n\nfunction compileFormulaExecutor(field: FormattersPipelineRuntimeField): TableFormulaCompiledExecutor | undefined {\n\tif (!field.formulaId) {\n\t\treturn undefined;\n\t}\n\n\tconst compiled = compileTableFormula({\n\t\tformulaId: field.formulaId,\n\t\tkeys: field.formulaDependencies\n\t});\n\n\treturn compiled.ok ? compiled.execute : missingFormulaExecutor;\n}\n\nfunction resolvePipelineSourceValue(\n\trawValue: unknown,\n\trowData: TableFormulaRowData,\n\tfield: Readonly<FormattersPipelineRuntimeField>\n): ResolvedPipelineSourceValue {\n\tif (!field.formulaId) {\n\t\treturn {\n\t\t\tvalue: rawValue,\n\t\t\tshouldRenderEmpty: false\n\t\t};\n\t}\n\n\tif (!field.formulaExecutor) {\n\t\treturn {\n\t\t\tvalue: field.purelyDerived ? undefined : rawValue,\n\t\t\tshouldRenderEmpty: field.purelyDerived === true\n\t\t};\n\t}\n\n\tconst execution = field.formulaExecutor(rowData);\n\tif (execution.ok) {\n\t\treturn {\n\t\t\tvalue: execution.value,\n\t\t\tshouldRenderEmpty: false\n\t\t};\n\t}\n\n\treturn {\n\t\tvalue: field.purelyDerived ? undefined : rawValue,\n\t\tshouldRenderEmpty: field.purelyDerived === true\n\t};\n}\n\nfunction executePipelineFormatting<TField extends FormattersPipelineRuntimeField>(\n\targs: FormatPipelineDisplayValueArgs<TField>,\n\tsourceValue: unknown,\n\tfield: Readonly<TField>\n): FormattersPipelineDisplayValue {\n\tconst executor = field.formattersPipelineExecutor;\n\tif (!executor) {\n\t\treturn createDefaultDisplayValue(formatFallbackValue(sourceValue, field), field.overflowTooltip === true);\n\t}\n\n\tconst execution: FormattersPipelineExecutionResult = executor.execute({\n\t\tvalue: sourceValue,\n\t\trowData: args.rowData,\n\t\tvalueStateFormulaRowData: args.valueStateFormulaRowData,\n\t\trowKind: args.rowKind,\n\t\tisGroupRow: args.rowKind === \"group\",\n\t\tisTotalsRow: args.rowKind === \"totals\",\n\t\trowLevel: args.rowLevel ?? 0,\n\t\tgroupingIds: args.groupingIds ?? [],\n\t\tcolumnId: field.id\n\t});\n\tconst value = execution.hasTypedValueFormat ? execution.value : formatFallbackValue(execution.value, field);\n\n\treturn {\n\t\tvalue,\n\t\tstate: execution.state,\n\t\ticon: execution.icon,\n\t\tshowIcon: execution.showIcon,\n\t\tshowValue: execution.showValue,\n\t\ticonPosition: execution.iconPosition,\n\t\toverflowTooltip: field.overflowTooltip === true\n\t};\n}\n\nfunction applyDisplayPostProcessing(\n\tdisplayValue: FormattersPipelineDisplayValue,\n\tsourceValue: ResolvedPipelineSourceValue,\n\tfield: Readonly<FormattersPipelineRuntimeField>\n): FormattersPipelineDisplayValue {\n\tif (sourceValue.shouldRenderEmpty) {\n\t\treturn {\n\t\t\t...displayValue,\n\t\t\tvalue: \"\"\n\t\t};\n\t}\n\n\tif (field.emptyWhenZero && isZeroValue(sourceValue.value)) {\n\t\treturn {\n\t\t\t...displayValue,\n\t\t\tvalue: \"\"\n\t\t};\n\t}\n\n\treturn displayValue;\n}\n\n/**\n * Компилирует formula/pipeline runtime прямо в исходный объект поля.\n *\n * Функция предназначена только для construction-stage: до помещения полей в\n * readonly context, memoized snapshot или query-independent cache.\n */\nexport function compileFormattersPipelineRuntime<TField extends FormattersPipelineRuntimeField>(field: TField): Readonly<TField> {\n\tconst formulaExecutor = compileFormulaExecutor(field);\n\tif (formulaExecutor) {\n\t\tfield.formulaExecutor = formulaExecutor;\n\t} else {\n\t\tdelete field.formulaExecutor;\n\t}\n\n\tif (!field.formattersPipeline) {\n\t\tdelete field.formattersPipelineExecutor;\n\t\treturn field;\n\t}\n\n\tconst compiled = compileFormattersPipelineExecutor({\n\t\tconfig: field.formattersPipeline,\n\t\tcolumn: field\n\t});\n\n\tif (compiled.ok) {\n\t\tfield.formattersPipelineExecutor = compiled.executor;\n\t} else {\n\t\tdelete field.formattersPipelineExecutor;\n\t}\n\n\treturn field;\n}\n\n/**\n * Компилирует runtime-карту без пересборки самих полей.\n */\nexport function compileFormattersPipelineRuntimeFields<TField extends FormattersPipelineRuntimeField>(\n\tfieldsById: Readonly<Record<string, TField | undefined>>\n): FormattersPipelineRuntimeFields<TField> {\n\tconst runtimeFields: Record<string, Readonly<TField>> = {};\n\n\tfor (const [fieldId, field] of Object.entries(fieldsById)) {\n\t\tif (!field) continue;\n\t\truntimeFields[fieldId] = compileFormattersPipelineRuntime(field);\n\t}\n\n\treturn runtimeFields;\n}\n\n/**\n * Вычисляет display-result для одного значения без знания о таблице или графике.\n */\nexport function formatPipelineDisplayValue<TField extends FormattersPipelineRuntimeField>(\n\targs: FormatPipelineDisplayValueArgs<TField>\n): FormattersPipelineDisplayValue {\n\tif (!args.field) {\n\t\treturn createDefaultDisplayValue(toSafeString(args.rawValue), false);\n\t}\n\n\tconst sourceValue = resolvePipelineSourceValue(args.rawValue, args.rowData, args.field);\n\tconst displayValue = executePipelineFormatting(args, sourceValue.value, args.field);\n\n\treturn applyDisplayPostProcessing(displayValue, sourceValue, args.field);\n}\n","import type { State } from \"../../types\";\n\nexport const DEFAULT_VALUE_STATES: readonly State[] = [\"none\", \"information\", \"success\", \"warning\", \"error\"];\n\nexport const VALUE_STATE_COLOR_TOKENS: Record<State, string> = {\n\t\"\": \"transparent\",\n\tnone: \"var(--content-1)\",\n\tinformation: \"var(--info-text)\",\n\tsuccess: \"var(--success-text)\",\n\twarning: \"var(--warning-text)\",\n\terror: \"var(--error-text)\"\n};\n","import { State } from \"../../types\";\n\nexport function resolveValueStateClassName(state: State, fallbackClassName?: string): string | undefined {\n\tswitch (state) {\n\t\tcase \"success\":\n\t\t\treturn \"statusSuccess\";\n\t\tcase \"warning\":\n\t\t\treturn \"statusWarning\";\n\t\tcase \"error\":\n\t\t\treturn \"statusError\";\n\t\tcase \"information\":\n\t\t\treturn \"statusInfo\";\n\t\tdefault:\n\t\t\treturn fallbackClassName;\n\t}\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"query-client-Gk7lxFz7.js","names":[],"sources":["../../src/query-client/broadcast.ts","../../src/query-client/invalidationBroadcast.ts","../../src/query-client/queryPersistenceAdapter.ts","../../src/query-client/persistence.ts","../../src/query-client/queryMeta.ts","../../src/query-client/queryClient.ts","../../src/query-client/sessionScopedResetLifecycle.ts","../../src/query-client/sessionScoped.ts"],"sourcesContent":["import { broadcastQueryClient } from \"@tanstack/query-broadcast-client-experimental\";\n\nimport { uuidv4 } from \"../crypto\";\nimport { isRecord } from \"../validators\";\n\nimport type { QueryClient, QueryKey } from \"@tanstack/react-query\";\n\ntype CacheSyncEvent =\n\t| { type: \"invalidate\"; keys: QueryKey[]; refetchType?: \"none\" | \"all\" | \"active\" | \"inactive\" }\n\t| { type: \"setQueryData\"; key: QueryKey; data: unknown };\n\ntype InstallBroadcastOptions = {\n\tqueriesChannel: string;\n\teventsChannel: string;\n};\n\ntype InstalledReactQueryBroadcast = {\n\tbroadcast: (event: CacheSyncEvent) => void;\n\tcleanup: () => void;\n};\n\ntype WireMessage = {\n\ttabId: string;\n\tevent: CacheSyncEvent;\n};\n\nlet broadcastFn: ((event: CacheSyncEvent) => void) | null = null;\n\nexport function setBroadcastFn(fn: (event: CacheSyncEvent) => void) {\n\tbroadcastFn = fn;\n}\n\nexport function broadcastCacheEvent(event: CacheSyncEvent) {\n\tbroadcastFn?.(event);\n}\n\nexport function installReactQueryBroadcast(queryClient: QueryClient, opts: InstallBroadcastOptions): InstalledReactQueryBroadcast {\n\tif (typeof window === \"undefined\") {\n\t\treturn { broadcast: () => {}, cleanup: () => {} };\n\t}\n\n\tlet isApplyingRemoteEvent = false;\n\n\t// 1) Sync query cache (data/state)\n\tconst cleanupQueryBroadcast = broadcastQueryClient({\n\t\tqueryClient,\n\t\tbroadcastChannel: opts.queriesChannel\n\t});\n\n\t// 2) Sync \"events\" (invalidate/setQueryData)\n\tconst tabId = uuidv4();\n\tconst bc = new BroadcastChannel(opts.eventsChannel);\n\tconst unsubscribeInvalidationBroadcast = queryClient.getQueryCache().subscribe((queryEvent) => {\n\t\tif (isApplyingRemoteEvent) return;\n\t\tif (queryEvent.type !== \"updated\" || queryEvent.action.type !== \"invalidate\") return;\n\n\t\tbroadcast({\n\t\t\ttype: \"invalidate\",\n\t\t\tkeys: [queryEvent.query.queryKey],\n\t\t\trefetchType: \"none\"\n\t\t});\n\t});\n\n\tbc.onmessage = (ev: MessageEvent) => {\n\t\t// MessageEvent.data: unknown -> делаем безопасное сужение без any\n\t\tconst data: unknown = ev.data;\n\t\tif (!isWireMessage(data)) return;\n\t\tif (data.tabId === tabId) return;\n\n\t\tconst e = data.event;\n\t\tisApplyingRemoteEvent = true;\n\n\t\ttry {\n\t\t\tif (e.type === \"invalidate\") {\n\t\t\t\tconst refetchType = e.refetchType ?? \"none\";\n\t\t\t\tfor (const key of e.keys) {\n\t\t\t\t\tqueryClient.invalidateQueries({ queryKey: key, refetchType });\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (e.type === \"setQueryData\") {\n\t\t\t\tqueryClient.setQueryData(e.key, e.data);\n\t\t\t\treturn;\n\t\t\t}\n\t\t} finally {\n\t\t\tisApplyingRemoteEvent = false;\n\t\t}\n\t};\n\n\tfunction broadcast(event: CacheSyncEvent) {\n\t\tconst msg: WireMessage = { tabId, event };\n\t\tbc.postMessage(msg);\n\t}\n\n\treturn {\n\t\tbroadcast,\n\t\tcleanup: () => {\n\t\t\tunsubscribeInvalidationBroadcast();\n\t\t\tcleanupQueryBroadcast();\n\t\t\tbc.close();\n\t\t}\n\t};\n}\n\nfunction isWireMessage(v: unknown): v is WireMessage {\n\tif (!isRecord(v)) return false;\n\tif (typeof v.tabId !== \"string\") return false;\n\tif (!(\"event\" in v)) return false;\n\treturn isCacheSyncEvent(v.event);\n}\n\nfunction isCacheSyncEvent(v: unknown): v is CacheSyncEvent {\n\tif (!isRecord(v)) return false;\n\tif (v.type === \"invalidate\") {\n\t\t// keys: QueryKey[] — runtime мы не сможем валидировать “идеально”,\n\t\t// но проверим что это массив, а refetchType — допустимое значение\n\t\tif (!Array.isArray(v.keys)) return false;\n\t\tif (v.refetchType === undefined) return true;\n\t\treturn v.refetchType === \"none\" || v.refetchType === \"active\" || v.refetchType === \"inactive\" || v.refetchType === \"all\";\n\t}\n\tif (v.type === \"setQueryData\") {\n\t\t// key должен быть массивом или строкой — QueryKey в рантайме обычно массив\n\t\t// проверим хотя бы наличие поля\n\t\treturn \"key\" in v && \"data\" in v;\n\t}\n\treturn false;\n}\n","import { uuidv4 } from \"../crypto\";\nimport { isRecord } from \"../validators\";\n\nimport type { QueryClient, QueryKey } from \"@tanstack/react-query\";\n\nexport type QueryInvalidationBroadcastOptions = {\n\t/** Уникальное в пределах origin имя канала приложения. */\n\treadonly channelName: string;\n\t/** Ограничивает как исходящие, так и входящие Query keys. */\n\treadonly matchesQuery: (queryKey: QueryKey) => boolean;\n\t/** Определяет, какие queries получающей вкладки будут перезапрошены сразу. */\n\treadonly refetchType?: \"none\" | \"active\" | \"inactive\" | \"all\";\n};\n\nexport type InstalledQueryInvalidationBroadcast = {\n\t/** Явно передаёт точечную инвалидацию без изменения локального QueryClient. */\n\treadonly broadcast: (queryKey: QueryKey) => void;\n\treadonly cleanup: () => void;\n};\n\ntype QueryInvalidationWireMessage = {\n\treadonly type: \"query.invalidate\";\n\treadonly senderId: string;\n\treadonly queryKey: QueryKey;\n};\n\nfunction isQueryInvalidationWireMessage(value: unknown): value is QueryInvalidationWireMessage {\n\treturn isRecord(value) && value.type === \"query.invalidate\" && typeof value.senderId === \"string\" && Array.isArray(value.queryKey);\n}\n\n/**\n * Передаёт между вкладками только факт инвалидации выбранного Query key.\n * Server data остаётся у QueryClient каждой вкладки и заново читается её queryFn.\n */\nexport function installQueryInvalidationBroadcast(\n\tqueryClient: QueryClient,\n\toptions: QueryInvalidationBroadcastOptions\n): InstalledQueryInvalidationBroadcast {\n\tif (typeof window === \"undefined\" || typeof BroadcastChannel === \"undefined\") {\n\t\treturn { broadcast: () => undefined, cleanup: () => undefined };\n\t}\n\n\tconst senderId = uuidv4();\n\tconst channel = new BroadcastChannel(options.channelName);\n\tlet applyingRemoteInvalidation = false;\n\tlet closed = false;\n\n\tconst broadcast = (queryKey: QueryKey) => {\n\t\tif (closed || !options.matchesQuery(queryKey)) return;\n\t\tchannel.postMessage({ type: \"query.invalidate\", senderId, queryKey } satisfies QueryInvalidationWireMessage);\n\t};\n\n\tconst unsubscribe = queryClient.getQueryCache().subscribe((event) => {\n\t\tif (applyingRemoteInvalidation || event.type !== \"updated\" || event.action.type !== \"invalidate\") return;\n\t\tbroadcast(event.query.queryKey);\n\t});\n\n\tchannel.onmessage = (event: MessageEvent<unknown>) => {\n\t\tconst message = event.data;\n\t\tif (closed || !isQueryInvalidationWireMessage(message) || message.senderId === senderId || !options.matchesQuery(message.queryKey))\n\t\t\treturn;\n\n\t\tapplyingRemoteInvalidation = true;\n\t\ttry {\n\t\t\t// Первый GET без data иначе присоединяется к refetch и может вернуть\n\t\t\t// состояние до чужой записи. cancelQueries отменяет retryer синхронно:\n\t\t\t// не оставляем await-окна между отменой и точечной invalidation.\n\t\t\tvoid queryClient.cancelQueries({ queryKey: message.queryKey, exact: true });\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: message.queryKey,\n\t\t\t\texact: true,\n\t\t\t\trefetchType: options.refetchType ?? \"active\"\n\t\t\t});\n\t\t} finally {\n\t\t\tapplyingRemoteInvalidation = false;\n\t\t}\n\t};\n\n\treturn {\n\t\tbroadcast,\n\t\tcleanup() {\n\t\t\tif (closed) return;\n\t\t\tclosed = true;\n\t\t\tunsubscribe();\n\t\t\tchannel.close();\n\t\t}\n\t};\n}\n","export type QueryPersistenceProjectAdapter = {\n\tpersistenceBuster?: string;\n\tresolveSystemIdentifier?: () => string | null | undefined;\n};\n\nlet queryPersistenceProjectAdapter: QueryPersistenceProjectAdapter = {};\n\nexport function configureQueryPersistenceProjectAdapter(adapter: QueryPersistenceProjectAdapter) {\n\tqueryPersistenceProjectAdapter = adapter;\n}\n\nexport function getQueryPersistenceProjectAdapter() {\n\treturn queryPersistenceProjectAdapter;\n}\n","import { experimental_createQueryPersister, type AsyncStorage, type PersistedQuery } from \"@tanstack/query-persist-client-core\";\nimport { type Query, type QueryFunction, type QueryFunctionContext, type QueryKey } from \"@tanstack/react-query\";\n\nimport { getQueryPersistenceProjectAdapter } from \"./queryPersistenceAdapter\";\n\nconst STORE_NAME = \"queries\";\nconst SESSION_PERSISTENCE_MARKER = \"session\";\n\nexport const REACT_QUERY_PERSISTENCE_BUSTER = __REACT_QUERY_PERSISTENCE_BUSTER__;\nexport const REACT_QUERY_PERSISTENCE_MAX_AGE = 90 * 24 * 60 * 60 * 1000;\n\ntype IndexedDbQueryStorageOptions = {\n\tdbName?: string;\n\tstoreName?: string;\n\tindexedDB?: IDBFactory;\n};\n\nexport type SessionQueryPersistenceScope = {\n\t/** Публичная identity серверной сессии; cookie secret или access token здесь недопустимы. */\n\treadonly id: string;\n\t/** Абсолютная граница, после которой snapshots этой сессии нельзя читать или записывать. */\n\treadonly expiresAt: string;\n};\n\nlet activeSessionPersistenceScope: SessionQueryPersistenceScope | null = null;\n\n/**\n * Описывает итог синхронного преобразования записи внутри одной IndexedDB-транзакции.\n * Отдельный result позволяет вернуть вызывающему коду вычисленный снимок только после commit.\n */\nexport type IndexedDbAtomicUpdateDecision<TStorageValue, TResult> =\n\t| { readonly action: \"keep\"; readonly result: TResult }\n\t| { readonly action: \"set\"; readonly value: TStorageValue; readonly result: TResult }\n\t| { readonly action: \"remove\"; readonly result: TResult };\n\nexport type IndexedDbQueryStorage<TStorageValue> = AsyncStorage<TStorageValue> & {\n\t/** Перечисляет записи object store для garbage collection и bounded persistence. */\n\treadonly entries: () => Promise<Array<[key: string, value: TStorageValue]>>;\n\t/**\n\t * Читает и изменяет одну запись в общей readwrite-транзакции, поэтому параллельные\n\t * вкладки не могут потерять уже зафиксированное изменение между get и set.\n\t * Callback должен быть синхронным: IndexedDB закрывает неактивную транзакцию до\n\t * завершения произвольной асинхронной работы.\n\t */\n\treadonly updateItem: <TResult>(\n\t\tkey: string,\n\t\tupdate: (value: TStorageValue | undefined) => IndexedDbAtomicUpdateDecision<TStorageValue, TResult>\n\t) => Promise<TResult>;\n};\n\nfunction resolveIndexedDbFactory(factory?: IDBFactory) {\n\tif (factory) return factory;\n\tif (typeof globalThis.indexedDB === \"undefined\") return undefined;\n\treturn globalThis.indexedDB;\n}\n\nfunction requestToPromise<T>(request: IDBRequest<T>) {\n\treturn new Promise<T>((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error);\n\t});\n}\n\nfunction getSystemIdentifier() {\n\tif (__DEV__) {\n\t\treturn `dev:${__APP_ID__}`;\n\t}\n\n\tconst projectIdentifier = getQueryPersistenceProjectAdapter().resolveSystemIdentifier?.();\n\tif (projectIdentifier) return projectIdentifier;\n\n\tconst hostname = typeof window === \"undefined\" ? \"server\" : window.location.hostname;\n\treturn `${__APP_ID__}:${hostname}`;\n}\n\nfunction getPersistencePrefix() {\n\tconst system = getSystemIdentifier();\n\treturn `${system}`;\n}\n\nfunction encodePersistenceSegment(value: string) {\n\treturn encodeURIComponent(value);\n}\n\nfunction getSessionPersistenceRootPrefix() {\n\treturn `${getPersistencePrefix()}-${SESSION_PERSISTENCE_MARKER}-`;\n}\n\nfunction getSessionPersistenceScopePrefix(scopeId: string) {\n\treturn `${getSessionPersistenceRootPrefix()}${encodePersistenceSegment(scopeId)}-`;\n}\n\nfunction getSessionPersistenceCachePrefix(scopeId: string, cacheName: string) {\n\treturn `${getSessionPersistenceScopePrefix(scopeId)}${encodePersistenceSegment(cacheName)}`;\n}\n\nfunction isSessionPersistenceScopeValid(scope: SessionQueryPersistenceScope | null): scope is SessionQueryPersistenceScope {\n\treturn (\n\t\tBoolean(scope?.id.trim()) && Number.isFinite(Date.parse(scope?.expiresAt ?? \"\")) && Date.parse(scope?.expiresAt ?? \"\") > Date.now()\n\t);\n}\n\n/** Возвращает текущую browser-session identity, которой разрешено читать приватные persisted query. */\nexport function getSessionQueryPersistenceScope(): SessionQueryPersistenceScope | null {\n\treturn isSessionPersistenceScopeValid(activeSessionPersistenceScope) ? activeSessionPersistenceScope : null;\n}\n\nasync function removeSessionPersistenceEntries(predicate: (key: string) => boolean) {\n\tconst storage = createIndexedDbQueryStorage<PersistedQuery>();\n\tif (!storage?.entries) return;\n\n\tconst entries = await storage.entries();\n\tawait Promise.all(entries.filter(([key]) => predicate(key)).map(([key]) => storage.removeItem(key)));\n}\n\n/** Удаляет persisted query одной сессии или всех сессий текущего приложения. */\nexport async function clearSessionQueryPersistence(scopeId?: string): Promise<void> {\n\tconst prefix = scopeId === undefined ? getSessionPersistenceRootPrefix() : getSessionPersistenceScopePrefix(scopeId);\n\tawait removeSessionPersistenceEntries((key) => key.startsWith(prefix));\n}\n\n/**\n * Устанавливает единственную активную server-session identity и сразу исключает\n * чтение старого scope. Browser storage очищается асинхронно уже после смены\n * in-memory границы, поэтому параллельный query не может записаться старому пользователю.\n */\nexport async function setSessionQueryPersistenceScope(scope: SessionQueryPersistenceScope | null): Promise<void> {\n\tconst normalized = scope && isSessionPersistenceScopeValid(scope) ? { id: scope.id.trim(), expiresAt: scope.expiresAt } : null;\n\tactiveSessionPersistenceScope = normalized;\n\n\tif (!normalized) {\n\t\tawait clearSessionQueryPersistence();\n\t\treturn;\n\t}\n\n\tconst activePrefix = getSessionPersistenceScopePrefix(normalized.id);\n\tawait removeSessionPersistenceEntries((key) => key.startsWith(getSessionPersistenceRootPrefix()) && !key.startsWith(activePrefix));\n}\n\nexport function shouldPersistQuery(query: Pick<Query, \"meta\">) {\n\treturn query.meta?.persist === true && query.meta.sessionScoped !== true;\n}\n\nfunction readSessionQueryPersistencePolicy(query: Pick<Query, \"meta\">) {\n\tconst policy = query.meta?.sessionPersistence;\n\tif (query.meta?.sessionScoped !== true || !policy || typeof policy !== \"object\") return null;\n\tif (!(\"cacheName\" in policy) || !(\"maxEntries\" in policy)) return null;\n\tif (typeof policy.cacheName !== \"string\" || !/^[a-z0-9][a-z0-9_-]{0,63}$/u.test(policy.cacheName)) return null;\n\tif (!Number.isSafeInteger(policy.maxEntries) || policy.maxEntries < 1 || policy.maxEntries > 5_000) return null;\n\tconst refetchOnRestore = \"refetchOnRestore\" in policy ? policy.refetchOnRestore : undefined;\n\tif (refetchOnRestore !== undefined && typeof refetchOnRestore !== \"boolean\") return null;\n\tconst monotonicRevisionField = \"monotonicRevisionField\" in policy ? policy.monotonicRevisionField : undefined;\n\tif (\n\t\tmonotonicRevisionField !== undefined &&\n\t\t(typeof monotonicRevisionField !== \"string\" || !/^[A-Za-z][A-Za-z0-9_]{0,63}$/u.test(monotonicRevisionField))\n\t) {\n\t\treturn null;\n\t}\n\treturn { cacheName: policy.cacheName, maxEntries: policy.maxEntries, refetchOnRestore, monotonicRevisionField } as const;\n}\n\nfunction readMonotonicDecimalRevision(query: PersistedQuery, field: string): string | null {\n\tconst data = query.state.data;\n\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) return null;\n\tconst revision = Reflect.get(data, field);\n\tif (typeof revision !== \"string\" || !/^\\d+$/u.test(revision)) return null;\n\treturn revision.replace(/^0+(?=\\d)/u, \"\");\n}\n\nfunction compareDecimalRevisions(left: string, right: string): number {\n\tif (left.length !== right.length) return left.length - right.length;\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\n\nexport function createIndexedDbQueryStorage<TStorageValue = unknown>(\n\toptions: IndexedDbQueryStorageOptions = {}\n): IndexedDbQueryStorage<TStorageValue> | undefined {\n\tconst factory = resolveIndexedDbFactory(options.indexedDB);\n\tif (!factory) return undefined;\n\n\tconst idb = factory;\n\tconst dbName = options.dbName ?? getSystemIdentifier();\n\tconst storeName = options.storeName ?? STORE_NAME;\n\tlet dbPromise: Promise<IDBDatabase> | null = null;\n\n\tfunction openDb() {\n\t\tif (dbPromise) return dbPromise;\n\n\t\tdbPromise = new Promise<IDBDatabase>((resolve, reject) => {\n\t\t\tconst request = idb.open(dbName, 1);\n\n\t\t\trequest.onupgradeneeded = () => {\n\t\t\t\tconst db = request.result;\n\t\t\t\tif (!db.objectStoreNames.contains(storeName)) {\n\t\t\t\t\tdb.createObjectStore(storeName);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\trequest.onsuccess = () => resolve(request.result);\n\t\t\trequest.onerror = () => reject(request.error);\n\t\t\trequest.onblocked = () => reject(request.error ?? new Error(\"Открытие IndexedDB заблокировано\"));\n\t\t});\n\n\t\treturn dbPromise;\n\t}\n\n\tasync function runTransaction<T>(mode: IDBTransactionMode, action: (store: IDBObjectStore) => IDBRequest<T>) {\n\t\tconst db = await openDb();\n\t\tconst tx = db.transaction(storeName, mode);\n\t\tconst store = tx.objectStore(storeName);\n\t\tconst request = action(store);\n\t\treturn requestToPromise(request);\n\t}\n\n\tasync function runWrite(mode: IDBTransactionMode, action: (store: IDBObjectStore) => void) {\n\t\tconst db = await openDb();\n\n\t\treturn new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = db.transaction(storeName, mode);\n\t\t\tconst store = tx.objectStore(storeName);\n\n\t\t\ttx.oncomplete = () => resolve();\n\t\t\ttx.onerror = () => reject(tx.error);\n\t\t\ttx.onabort = () => reject(tx.error);\n\n\t\t\ttry {\n\t\t\t\taction(store);\n\t\t\t} catch (error) {\n\t\t\t\ttx.abort();\n\t\t\t\treject(error);\n\t\t\t}\n\t\t});\n\t}\n\n\tasync function updateItem<TResult>(\n\t\tkey: string,\n\t\tupdate: (value: TStorageValue | undefined) => IndexedDbAtomicUpdateDecision<TStorageValue, TResult>\n\t) {\n\t\tconst db = await openDb();\n\t\treturn new Promise<TResult>((resolve, reject) => {\n\t\t\tconst tx = db.transaction(storeName, \"readwrite\");\n\t\t\tconst store = tx.objectStore(storeName);\n\t\t\tconst request = store.get(key);\n\t\t\tlet result: TResult;\n\t\t\tlet hasResult = false;\n\n\t\t\ttx.oncomplete = () => {\n\t\t\t\tif (hasResult) resolve(result);\n\t\t\t\telse reject(new Error(\"IndexedDB-транзакция завершилась без результата обновления\"));\n\t\t\t};\n\t\t\ttx.onerror = () => reject(tx.error);\n\t\t\ttx.onabort = () => reject(tx.error);\n\t\t\trequest.onerror = () => {\n\t\t\t\ttx.abort();\n\t\t\t\treject(request.error);\n\t\t\t};\n\t\t\trequest.onsuccess = () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst decision = update(request.result as TStorageValue | undefined);\n\t\t\t\t\tresult = decision.result;\n\t\t\t\t\thasResult = true;\n\t\t\t\t\tif (decision.action === \"set\") store.put(decision.value, key);\n\t\t\t\t\telse if (decision.action === \"remove\") store.delete(key);\n\t\t\t\t} catch (error) {\n\t\t\t\t\ttx.abort();\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t};\n\t\t});\n\t}\n\n\treturn {\n\t\tgetItem: (key) => runTransaction<TStorageValue | undefined>(\"readonly\", (store) => store.get(key)),\n\t\tsetItem: (key, value) => runWrite(\"readwrite\", (store) => store.put(value, key)),\n\t\tremoveItem: (key) => runWrite(\"readwrite\", (store) => store.delete(key)),\n\t\tupdateItem,\n\t\tentries: async () => {\n\t\t\tconst db = await openDb();\n\n\t\t\treturn new Promise<Array<[key: string, value: TStorageValue]>>((resolve, reject) => {\n\t\t\t\tconst tx = db.transaction(storeName, \"readonly\");\n\t\t\t\tconst store = tx.objectStore(storeName);\n\t\t\t\tconst request = store.openCursor();\n\t\t\t\tconst entries: Array<[key: string, value: TStorageValue]> = [];\n\n\t\t\t\ttx.oncomplete = () => resolve(entries);\n\t\t\t\ttx.onerror = () => reject(tx.error);\n\t\t\t\ttx.onabort = () => reject(tx.error);\n\n\t\t\t\trequest.onsuccess = () => {\n\t\t\t\t\tconst cursor = request.result;\n\t\t\t\t\tif (!cursor) return;\n\n\t\t\t\t\tentries.push([String(cursor.key), cursor.value as TStorageValue]);\n\t\t\t\t\tcursor.continue();\n\t\t\t\t};\n\t\t\t\trequest.onerror = () => reject(request.error);\n\t\t\t});\n\t\t}\n\t};\n}\n\nexport function createReactQueryPersister() {\n\tconst storage = createIndexedDbQueryStorage<PersistedQuery>();\n\tif (!storage) return undefined;\n\tconst indexedDbStorage = storage;\n\n\tconst publicPersister = experimental_createQueryPersister<PersistedQuery>({\n\t\tstorage: indexedDbStorage,\n\t\tserialize: (query) => query,\n\t\tdeserialize: (query) => query,\n\t\tprefix: getPersistencePrefix(),\n\t\tbuster: getQueryPersistenceProjectAdapter().persistenceBuster ?? REACT_QUERY_PERSISTENCE_BUSTER,\n\t\tmaxAge: REACT_QUERY_PERSISTENCE_MAX_AGE,\n\t\trefetchOnRestore: true,\n\t\tfilters: { predicate: shouldPersistQuery }\n\t});\n\tconst sessionPersisters = new Map<string, ReturnType<typeof experimental_createQueryPersister<PersistedQuery>>>();\n\n\tfunction getSessionPersister(\n\t\tscope: SessionQueryPersistenceScope,\n\t\tcacheName: string,\n\t\tmaxEntries: number,\n\t\trefetchOnRestore = true,\n\t\tmonotonicRevisionField?: string\n\t) {\n\t\tconst prefix = getSessionPersistenceCachePrefix(scope.id, cacheName);\n\t\tconst identity = `${prefix}:${maxEntries}:${refetchOnRestore}:${monotonicRevisionField ?? \"\"}:${scope.expiresAt}`;\n\t\tconst existing = sessionPersisters.get(identity);\n\t\tif (existing) return existing;\n\n\t\tlet pruneQueue = Promise.resolve();\n\t\tconst boundedStorage: AsyncStorage<PersistedQuery> = {\n\t\t\tgetItem: (key) => {\n\t\t\t\tconst activeScope = getSessionQueryPersistenceScope();\n\t\t\t\treturn activeScope?.id === scope.id ? indexedDbStorage.getItem(key) : undefined;\n\t\t\t},\n\t\t\tremoveItem: (key) => indexedDbStorage.removeItem(key),\n\t\t\tentries: () => indexedDbStorage.entries(),\n\t\t\tsetItem: async (key, value) => {\n\t\t\t\tconst activeScope = getSessionQueryPersistenceScope();\n\t\t\t\tif (activeScope?.id !== scope.id) return;\n\n\t\t\t\tif (monotonicRevisionField === undefined) {\n\t\t\t\t\tawait indexedDbStorage.setItem(key, value);\n\t\t\t\t} else {\n\t\t\t\t\tawait indexedDbStorage.updateItem(key, (stored) => {\n\t\t\t\t\t\tif (!stored || stored.buster !== value.buster) return { action: \"set\", value, result: undefined };\n\t\t\t\t\t\tconst storedRevision = readMonotonicDecimalRevision(stored, monotonicRevisionField);\n\t\t\t\t\t\tconst nextRevision = readMonotonicDecimalRevision(value, monotonicRevisionField);\n\t\t\t\t\t\tif (storedRevision !== null && nextRevision !== null && compareDecimalRevisions(storedRevision, nextRevision) > 0) {\n\t\t\t\t\t\t\treturn { action: \"keep\", result: undefined };\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn { action: \"set\", value, result: undefined };\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tpruneQueue = pruneQueue.then(async () => {\n\t\t\t\t\tconst entries = (await indexedDbStorage.entries()).filter(([entryKey]) => entryKey.startsWith(`${prefix}-`));\n\t\t\t\t\tentries.sort((left, right) => (right[1].state.dataUpdatedAt ?? 0) - (left[1].state.dataUpdatedAt ?? 0));\n\t\t\t\t\tawait Promise.all(entries.slice(maxEntries).map(([entryKey]) => indexedDbStorage.removeItem(entryKey)));\n\t\t\t\t});\n\t\t\t\tawait pruneQueue;\n\t\t\t}\n\t\t};\n\t\tconst persister = experimental_createQueryPersister<PersistedQuery>({\n\t\t\tstorage: boundedStorage,\n\t\t\tserialize: (query) => query,\n\t\t\tdeserialize: (query) => query,\n\t\t\tprefix,\n\t\t\tbuster: getQueryPersistenceProjectAdapter().persistenceBuster ?? REACT_QUERY_PERSISTENCE_BUSTER,\n\t\t\tmaxAge: REACT_QUERY_PERSISTENCE_MAX_AGE,\n\t\t\trefetchOnRestore,\n\t\t\tfilters: {\n\t\t\t\tpredicate: (query) => readSessionQueryPersistencePolicy(query)?.cacheName === cacheName\n\t\t\t}\n\t\t});\n\t\tsessionPersisters.set(identity, persister);\n\t\treturn persister;\n\t}\n\n\treturn {\n\t\t...publicPersister,\n\t\tasync persisterFn<T, TQueryKey extends QueryKey>(\n\t\t\tqueryFn: QueryFunction<T, TQueryKey>,\n\t\t\tcontext: QueryFunctionContext<TQueryKey>,\n\t\t\tquery: Query\n\t\t): Promise<T> {\n\t\t\tconst policy = readSessionQueryPersistencePolicy(query);\n\t\t\tif (!policy) return publicPersister.persisterFn(queryFn, context, query);\n\n\t\t\tconst scope = getSessionQueryPersistenceScope();\n\t\t\tif (!scope) return Promise.resolve(queryFn(context));\n\n\t\t\treturn getSessionPersister(\n\t\t\t\tscope,\n\t\t\t\tpolicy.cacheName,\n\t\t\t\tpolicy.maxEntries,\n\t\t\t\tpolicy.refetchOnRestore,\n\t\t\t\tpolicy.monotonicRevisionField\n\t\t\t).persisterFn(queryFn, context, query);\n\t\t}\n\t};\n}\n","import { type QueryMeta } from \"@tanstack/react-query\";\n\nexport type SessionQueryPersistencePolicy = {\n\t/** Устойчивое предметно независимое имя bounded-кэша внутри текущей сессии. */\n\treadonly cacheName: string;\n\t/** Максимальное число последних успешно полученных query в этом кэше. */\n\treadonly maxEntries: number;\n\t/**\n\t * По умолчанию восстановленный snapshot сразу перепроверяется полным queryFn.\n\t * `false` допустим только при наличии отдельной лёгкой проверки актуальности.\n\t */\n\treadonly refetchOnRestore?: boolean;\n\t/**\n\t * Поле верхнего уровня query data с неотрицательной целочисленной ревизией.\n\t * При наличии поля IndexedDB атомарно отклоняет запоздавшую запись меньшей\n\t * ревизии, поэтому параллельная вкладка не может откатить сохранённый snapshot.\n\t */\n\treadonly monotonicRevisionField?: string;\n};\n\n/**\n * Общие признаки query, которые управляют инфраструктурой клиентского кеша.\n *\n * `persist` разрешает долговременное хранение публичных справочных данных,\n * а `sessionScoped` помечает данные, доступность или содержимое которых зависит\n * от текущей серверной сессии. Эти признаки нельзя совмещать. Отдельный\n * `sessionPersistence` разрешает bounded storage только внутри активного scope.\n */\nexport type AppQueryMeta = {\n\treadonly persist?: boolean;\n\treadonly sessionScoped?: boolean;\n\treadonly sessionPersistence?: SessionQueryPersistencePolicy;\n} & Record<string, unknown>;\n\ndeclare module \"@tanstack/react-query\" {\n\tinterface Register {\n\t\tqueryMeta: AppQueryMeta;\n\t}\n}\n\n/** Разрешает сохранять публичную query в долговременном хранилище. */\nexport const persistedQueryMeta = { persist: true } as const satisfies QueryMeta;\n\n/** Помечает query для сброса при изменении серверной сессии. */\nexport const sessionScopedQueryMeta = { sessionScoped: true } as const satisfies QueryMeta;\n\n/**\n * Разрешает bounded persistence только внутри явно установленной server-session identity.\n * Scope и его срок жизни задаются отдельно через `setSessionQueryPersistenceScope`,\n * поэтому query contract не хранит идентификатор пользователя или сессии.\n */\nexport function createSessionQueryPersistenceMeta(policy: SessionQueryPersistencePolicy) {\n\tconst cacheName = policy.cacheName.trim();\n\tif (!/^[a-z0-9][a-z0-9_-]{0,63}$/u.test(cacheName)) {\n\t\tthrow new Error(\"Имя session query cache должно содержать только строчные латинские буквы, цифры, _ и -\");\n\t}\n\tif (!Number.isSafeInteger(policy.maxEntries) || policy.maxEntries < 1 || policy.maxEntries > 5_000) {\n\t\tthrow new Error(\"Лимит session query cache должен быть целым числом от 1 до 5000\");\n\t}\n\tif (policy.refetchOnRestore !== undefined && typeof policy.refetchOnRestore !== \"boolean\") {\n\t\tthrow new Error(\"Политика refetch после восстановления должна быть boolean\");\n\t}\n\tconst monotonicRevisionField = policy.monotonicRevisionField?.trim();\n\tif (monotonicRevisionField !== undefined && !/^[A-Za-z][A-Za-z0-9_]{0,63}$/u.test(monotonicRevisionField)) {\n\t\tthrow new Error(\"Имя поля монотонной ревизии должно быть JavaScript-идентификатором длиной до 64 символов\");\n\t}\n\n\treturn {\n\t\tsessionScoped: true,\n\t\tsessionPersistence: {\n\t\t\tcacheName,\n\t\t\tmaxEntries: policy.maxEntries,\n\t\t\t...(policy.refetchOnRestore === undefined ? {} : { refetchOnRestore: policy.refetchOnRestore }),\n\t\t\t...(monotonicRevisionField === undefined ? {} : { monotonicRevisionField })\n\t\t}\n\t} as const satisfies QueryMeta;\n}\n","import {\n\tDefaultError,\n\tMutation,\n\tMutationCache,\n\tMutationFunctionContext,\n\tQuery,\n\tQueryCache,\n\tQueryClient,\n\tQueryKey\n} from \"@tanstack/react-query\";\n\nimport { createReactQueryPersister } from \"./persistence\";\n\nexport type QueryBroadcastOptions = {\n\tonQueryError?: (error: DefaultError, query: Query<unknown, unknown, unknown>) => void;\n\tonMutationError?: (\n\t\terror: DefaultError,\n\t\tvariables: unknown,\n\t\tonMutateResult: unknown,\n\t\tmutation: Mutation<unknown, unknown, unknown>,\n\t\tcontext: MutationFunctionContext\n\t) => Promise<unknown> | unknown;\n};\n\n/**\n * Создаёт настроенный QueryClient. Singleton lifecycle обеспечивает host-приложение.\n */\nexport function createQueryClient({ onQueryError, onMutationError }: QueryBroadcastOptions) {\n\tconst queryPersister = createReactQueryPersister();\n\n\tconst queryClient = new QueryClient({\n\t\tqueryCache: new QueryCache({ onError: onQueryError }),\n\t\tmutationCache: new MutationCache({ onError: onMutationError }),\n\t\tdefaultOptions: {\n\t\t\t// TODO: Vite env\n\t\t\tqueries: {\n\t\t\t\tstaleTime: 10 * 60 * 1000,\n\t\t\t\tgcTime: 30 * 60 * 1000,\n\t\t\t\tthrowOnError: false,\n\t\t\t\tretry: false,\n\t\t\t\t...(queryPersister ? { persister: queryPersister.persisterFn } : {})\n\t\t\t},\n\t\t\tmutations: {\n\t\t\t\tthrowOnError: false,\n\t\t\t\tretry: false\n\t\t\t}\n\t\t}\n\t});\n\n\treturn queryClient;\n}\n\n/**\n * Контекст, возвращаемый из `onMutateOptimistic`.\n * Содержит снапшот кеша до оптимистичного обновления.\n * Передаётся в `onError`, `onSuccess`, `onSettled` через TanStack Query.\n */\nexport type OptimisticContext<TQueryCacheData> = {\n\tprevious: TQueryCacheData | undefined;\n};\n\n/**\n * _Оптимистичное обновление — это когда мы моментально меняем локальный кэш (и UI) до ответа сервера,\n * чтобы интерфейс был отзывчивым. Если сервер вернул ошибку — откатываем изменения.\n * Если сервер подтвердил — либо принимаем ответ сервера,\n * либо оставляем оптимистичное состояние (в зависимости от ситуации)._\n *\n * Обработчик `onMutate` для оптимистичного обновления.\n *\n * Выполняет три шага:\n * 1. Отменяет активные запросы по ключу — предотвращает гонку с сервером.\n * 2. Сохраняет снапшот текущего состояния кеша.\n * 3. Применяет оптимистичный патч через `patcher`.\n *\n * Возвращает {@link OptimisticContext} — передаётся в `onError` для отката.\n *\n * @example\n * // Обновление точечных данных в списке объектов\n * useMutation({\n * mutationFn: (data: SomeType) => { ... },\n * onMutate: async (data) =>\n * onMutateOptimistic(queryClient, someQueryKey, (old) => {\n * if (!old) return old;\n * return old.map((item) =>\n * item.id === data.id ? { ...item, visible: data.visible } : item\n * );\n * }),\n * onError: (_err, _vars, context) =>\n * onErrorOptimistic(queryClient, someQueryKey, context),\n * onSettled: () =>\n * onSettledOptimistic(queryClient, someQueryKey),\n * })\n */\nexport async function onMutateOptimistic<TQueryCacheData>(\n\tqc: QueryClient,\n\tqueryKey: QueryKey,\n\tpatcher: (oldCache: TQueryCacheData | undefined) => TQueryCacheData | undefined\n): Promise<OptimisticContext<TQueryCacheData>> {\n\t// 1) Отменяем запросы по ключу, чтобы не было гонки с сервером\n\tawait qc.cancelQueries({ queryKey });\n\n\t// 2) Сохраняем снапшот кеша для возможного отката\n\tconst previous = qc.getQueryData<TQueryCacheData>(queryKey);\n\n\t// 3) Применяем оптимистичный патч\n\tqc.setQueryData<TQueryCacheData>(queryKey, patcher);\n\n\treturn { previous };\n}\n\n/**\n * Обработчик `onError` для оптимистичного обновления.\n *\n * Откатывает кеш к снапшоту, сохранённому в {@link OptimisticContext}.\n * Если `context` не определён (например, `onMutate` сам выбросил ошибку) — безопасно ничего не делает.\n *\n * @example\n * onError: (_err, _vars, context) =>\n * onErrorOptimistic(queryClient, someQueryKey, context),\n */\nexport function onErrorOptimistic<TQueryCacheData>(\n\tqc: QueryClient,\n\tqueryKey: QueryKey,\n\tcontext: OptimisticContext<TQueryCacheData> | undefined\n): void {\n\tif (context?.previous !== undefined) {\n\t\tqc.setQueryData<TQueryCacheData>(queryKey, context.previous);\n\t}\n}\n\n/**\n * Обработчик `onSuccess` для оптимистичного обновления — применяет данные сервера в кеш.\n *\n * Используется, когда сервер вернул авторитетные данные, которые нужно слить\n * с текущим состоянием кеша (или заменить его целиком).\n *\n * Если сервер вернул `void` — эту функцию вызывать не нужно:\n * оптимистичное состояние в кеше остаётся без изменений.\n *\n * @param qc - экземпляр `QueryClient`\n * @param queryKey - ключ кеша\n * @param serverData - данные, вернувшиеся от сервера\n * @param merger - функция слияния: получает текущий кеш и ответ сервера, возвращает новое состояние\n *\n * @example\n * // Полная замена кеша ответом сервера\n * onSuccess: (data) =>\n * onSuccessOptimistic(queryClient, someQueryKey, data, (_cache, serverData) => serverData),\n *\n * @example\n * // Точечное обновление одного элемента в списке\n * onSuccess: (updatedItem) =>\n * onSuccessOptimistic(queryClient, someQueryKey, updatedItem,\n * (cache, item) => cache?.map((i) => (i.id === item.id ? item : i))\n * ),\n */\nexport function onSuccessOptimistic<TQueryCacheData, TServerData>(\n\tqc: QueryClient,\n\tqueryKey: QueryKey,\n\tserverData: TServerData,\n\tmerger: (cache: TQueryCacheData | undefined, serverData: TServerData) => TQueryCacheData | undefined\n): void {\n\tqc.setQueryData<TQueryCacheData>(queryKey, (old) => merger(old, serverData));\n}\n\n/**\n * Параметры для {@link onSettledOptimistic}.\n */\nexport type OnSettledOptimisticOptions = {\n\t/**\n\t * Тип рефетча при инвалидации.\n\t * - `\"none\"` (по умолчанию) — помечает кеш устаревшим, но не запускает рефетч немедленно.\n\t * - `\"active\"` — немедленно перезапрашивает активные подписчики.\n\t * - `\"inactive\"` — перезапрашивает неактивные (фоновые) запросы.\n\t * - `\"all\"` — перезапрашивает все подходящие запросы.\n\t */\n\trefetchType?: \"none\" | \"active\" | \"inactive\" | \"all\";\n};\n\n/**\n * Обработчик `onSettled` для оптимистичного обновления.\n *\n * Инвалидирует один или несколько query-ключей после завершения мутации\n * (выполняется и при успехе, и при ошибке).\n *\n * По умолчанию использует `refetchType: \"none\"` — помечает кеш устаревшим,\n * но не запускает немедленный рефетч. Это безопасный вариант при оптимистике:\n * данные в кеше уже актуальны (подтверждены или откатаны), а рефетч произойдёт\n * при следующем монтировании или явном запросе.\n *\n * Broadcast в другие вкладки — ответственность вызывающего кода через `broadcastCacheEvent`.\n *\n * @example\n * onSettled: () =>\n * onSettledOptimistic(queryClient, someQueryKey),\n *\n * @example\n * // С принудительным рефетчем активных подписчиков\n * onSettled: () =>\n * onSettledOptimistic(queryClient, [queryKeyA, queryKeyB], { refetchType: \"active\" }),\n */\nexport async function onSettledOptimistic(\n\tqc: QueryClient,\n\tqueryKeys: QueryKey | QueryKey[],\n\toptions?: OnSettledOptimisticOptions\n): Promise<void> {\n\tconst refetchType = options?.refetchType ?? \"none\";\n\t// Нормализуем одиночный ключ к массиву для единообразной обработки\n\tconst keys: QueryKey[] = isNestedQueryKey(queryKeys) ? queryKeys : [queryKeys];\n\n\tawait Promise.all(keys.map((queryKey) => qc.invalidateQueries({ queryKey, refetchType })));\n}\n\n/**\n * Определяет, является ли значение массивом query-ключей (QueryKey[]),\n * а не одиночным QueryKey.\n *\n * QueryKey в TanStack Query — это `string | readonly unknown[]`.\n * Чтобы отличить `QueryKey[]` от `QueryKey` типа `readonly unknown[]`,\n * проверяем, содержит ли массив хотя бы один вложенный массив или строку на верхнем уровне.\n */\nfunction isNestedQueryKey(value: QueryKey | QueryKey[]): value is QueryKey[] {\n\tif (!Array.isArray(value)) return false;\n\t// Если первый элемент сам является массивом — это QueryKey[]\n\treturn Array.isArray(value[0]);\n}\n","import { type QueryClient } from \"@tanstack/react-query\";\n\nimport {\n\ttype InstalledSessionScopedQueryResetLifecycle,\n\ttype SessionScopedQueryResetLifecycle,\n\ttype SessionScopedQueryResetOutcome\n} from \"./sessionScopedResetLifecycleTypes\";\n\ntype SessionScopedQueryResetRegistrationState = {\n\tregistrations: Set<SessionScopedQueryResetLifecycle>;\n\tactiveResets: number;\n};\n\ntype SessionScopedQueryResetBoundary = Readonly<{\n\tretirement: Promise<readonly unknown[]> | null;\n\tcomplete: (outcome: SessionScopedQueryResetOutcome) => Promise<readonly unknown[]>;\n}>;\n\n// WeakMap содержит только subscriptions и число вызовов, а не второй auth/Query snapshot.\nconst registrationsByClient = new WeakMap<QueryClient, SessionScopedQueryResetRegistrationState>();\n\nfunction getRegistrationState(queryClient: QueryClient): SessionScopedQueryResetRegistrationState {\n\tconst existing = registrationsByClient.get(queryClient);\n\tif (existing) return existing;\n\tconst state = { registrations: new Set<SessionScopedQueryResetLifecycle>(), activeResets: 0 };\n\tregistrationsByClient.set(queryClient, state);\n\treturn state;\n}\n\nfunction releaseEmptyRegistrationState(queryClient: QueryClient, state: SessionScopedQueryResetRegistrationState): void {\n\tif (state.activeResets === 0 && state.registrations.size === 0 && registrationsByClient.get(queryClient) === state) {\n\t\tregistrationsByClient.delete(queryClient);\n\t}\n}\n\n/**\n * Устанавливать до запуска session-bound runtime на весь срок жизни QueryClient.\n * Поздняя регистрация не может восстановить пропущенный before: вместо частичной\n * защиты она синхронно отклоняется до startup нового consumer.\n */\nexport function installSessionScopedQueryResetLifecycle(\n\tqueryClient: QueryClient,\n\tlifecycle: SessionScopedQueryResetLifecycle\n): InstalledSessionScopedQueryResetLifecycle {\n\tconst state = getRegistrationState(queryClient);\n\tif (state.activeResets > 0) throw new Error(\"Lifecycle session-scoped Query нельзя установить во время сброса\");\n\tconst registration = { beforeReset: lifecycle.beforeReset, afterReset: lifecycle.afterReset };\n\tstate.registrations.add(registration);\n\tlet closed = false;\n\treturn {\n\t\tcleanup() {\n\t\t\tif (closed) return;\n\t\t\tclosed = true;\n\t\t\tstate.registrations.delete(registration);\n\t\t\treleaseEmptyRegistrationState(queryClient, state);\n\t\t}\n\t};\n}\n\n/** Приватная пара одного reset: перекрывающиеся вызовы не объединяются и не меняют прежний Query flow. */\nexport function beginSessionScopedQueryReset(queryClient: QueryClient): SessionScopedQueryResetBoundary {\n\tconst state = getRegistrationState(queryClient);\n\t// Marker публикуется даже без callbacks и раньше любого reentrant before/Query notification.\n\tstate.activeResets += 1;\n\tconst captured = [...state.registrations];\n\tconst retirements = captured.map(({ beforeReset }) => {\n\t\ttry {\n\t\t\treturn Promise.resolve(beforeReset());\n\t\t} catch (error) {\n\t\t\treturn Promise.reject(error);\n\t\t}\n\t});\n\treturn {\n\t\t// Без listeners cancelQueries начинается в прежнем синхронном проходе вызова reset.\n\t\tretirement: captured.length === 0 ? null : collectResetCallbackFailures(retirements),\n\t\tasync complete(outcome) {\n\t\t\ttry {\n\t\t\t\tconst completions = captured.map(({ afterReset }) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn Promise.resolve(afterReset(outcome));\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treturn Promise.reject(error);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\treturn await collectResetCallbackFailures(completions);\n\t\t\t} finally {\n\t\t\t\tstate.activeResets -= 1;\n\t\t\t\treleaseEmptyRegistrationState(queryClient, state);\n\t\t\t}\n\t\t}\n\t};\n}\n\n/** Все callbacks завершаются независимо; порядок ошибок совпадает с порядком регистрации. */\nasync function collectResetCallbackFailures(operations: readonly Promise<void>[]): Promise<readonly unknown[]> {\n\tconst results = await Promise.allSettled(operations);\n\treturn results.flatMap((result) => (result.status === \"rejected\" ? [result.reason] : []));\n}\n","import { type Query, type QueryClient } from \"@tanstack/react-query\";\n\nimport { beginSessionScopedQueryReset } from \"./sessionScopedResetLifecycle\";\nimport {\n\ttype InstalledSessionScopedQueryReset,\n\ttype InstallSessionScopedQueryResetOptions,\n\ttype ResetSessionScopedQueriesOptions\n} from \"./sessionScopedTypes\";\n\nconst SESSION_SCOPED_QUERY_CACHE_RESET_EVENT = \"session-scoped-query-cache-reset\";\n\ntype SessionScopedQueryCacheResetEvent = {\n\treadonly type: typeof SESSION_SCOPED_QUERY_CACHE_RESET_EVENT;\n};\n\nlet broadcastSessionScopedQueryReset: (() => void) | undefined;\n\n/** Проверяет, относится ли query к данным текущей серверной сессии. */\nexport function isSessionScopedQuery(query: Pick<Query, \"meta\">): boolean {\n\treturn query.meta?.sessionScoped === true;\n}\n\n/**\n * Сбрасывает все session-scoped query после входа, выхода, обновления сессии\n * или потери авторизации.\n *\n * Сначала отменяются незавершённые запросы старой сессии, затем TanStack Query\n * очищает их состояние и повторно запрашивает только активные query. Обычные\n * публичные данные при этом остаются в кеше. По умолчанию отдельная команда\n * сброса отправляется другим вкладкам без передачи содержимого кеша или токенов.\n */\nexport async function resetSessionScopedQueries(queryClient: QueryClient, options: ResetSessionScopedQueriesOptions = {}): Promise<void> {\n\tconst boundary = beginSessionScopedQueryReset(queryClient);\n\tconst filters = { predicate: isSessionScopedQuery } as const;\n\tconst failures: unknown[] = [];\n\ttry {\n\t\tif (boundary.retirement) failures.push(...(await boundary.retirement));\n\t\ttry {\n\t\t\tawait queryClient.cancelQueries(filters);\n\t\t} catch (error) {\n\t\t\tfailures.push(error);\n\t\t}\n\n\t\t// Отказ retirement не оставляет доступным прежний snapshot и не допускает\n\t\t// новые чтения. Ошибка одного observer также не прерывает очистку остальных.\n\t\tconst matchedQueries = queryClient.getQueryCache().findAll(filters);\n\t\tfor (const query of matchedQueries) {\n\t\t\ttry {\n\t\t\t\tif (query.getObserversCount() === 0) {\n\t\t\t\t\tqueryClient.getQueryCache().remove(query);\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tquery.reset();\n\t\t\t\t\t} finally {\n\t\t\t\t\t\t// Hydration сохраняет SSR initialData: обычный reset мог бы вернуть старую сессию.\n\t\t\t\t\t\tquery.setState({ data: undefined, dataUpdatedAt: 0 });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tfailures.push(error);\n\t\t\t}\n\t\t}\n\n\t\tif (failures.length === 0) {\n\t\t\tawait queryClient.refetchQueries(\n\t\t\t\t{\n\t\t\t\t\tpredicate: (query) => isSessionScopedQuery(query) && query.getObserversCount() > 0,\n\t\t\t\t\ttype: \"active\"\n\t\t\t\t},\n\t\t\t\t{ cancelRefetch: true }\n\t\t\t);\n\n\t\t\tif (options.broadcast !== false) broadcastSessionScopedQueryReset?.();\n\t\t}\n\t} catch (error) {\n\t\tfailures.push(error);\n\t} finally {\n\t\tfailures.push(...(await boundary.complete({ status: failures.length === 0 ? \"completed\" : \"failed\" })));\n\t}\n\tif (failures.length === 1) throw failures[0];\n\tif (failures.length > 1) throw new AggregateError(failures, \"Не удалось завершить сброс session-scoped Query\");\n}\n\n/**\n * Подключает безопасную межвкладочную синхронизацию смены сессии.\n *\n * Канал передаёт только команду сброса. В отличие от полной синхронизации\n * Query cache, он не публикует данные query и потому подходит для auth-зависимых\n * ответов. На сервере и в браузерах без BroadcastChannel возвращается no-op.\n */\nexport function installSessionScopedQueryReset(\n\tqueryClient: QueryClient,\n\toptions: InstallSessionScopedQueryResetOptions\n): InstalledSessionScopedQueryReset {\n\tif (typeof window === \"undefined\" || typeof BroadcastChannel === \"undefined\") {\n\t\treturn { cleanup: () => undefined };\n\t}\n\n\tconst channel = new BroadcastChannel(options.channelName);\n\tconst broadcast = () => {\n\t\tconst event: SessionScopedQueryCacheResetEvent = {\n\t\t\ttype: SESSION_SCOPED_QUERY_CACHE_RESET_EVENT\n\t\t};\n\t\tchannel.postMessage(event);\n\t};\n\n\tbroadcastSessionScopedQueryReset = broadcast;\n\tchannel.onmessage = (event: MessageEvent<unknown>) => {\n\t\tif (!isSessionScopedQueryCacheResetEvent(event.data)) return;\n\t\tvoid resetSessionScopedQueries(queryClient, { broadcast: false }).catch(async (error: unknown) => {\n\t\t\tif (!options.onError) {\n\t\t\t\tconsole.error(\"Не удалось обработать межвкладочный сброс session-scoped Query\", error);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait options.onError(error);\n\t\t\t} catch (reportingError) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t\"Не удалось сообщить об ошибке межвкладочного сброса session-scoped Query\",\n\t\t\t\t\tnew AggregateError([error, reportingError], \"Ошибка сброса и её обработчика\")\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t};\n\n\tlet closed = false;\n\treturn {\n\t\tcleanup: () => {\n\t\t\tif (closed) return;\n\t\t\tclosed = true;\n\t\t\tchannel.onmessage = null;\n\t\t\tchannel.close();\n\t\t\tif (broadcastSessionScopedQueryReset === broadcast) {\n\t\t\t\tbroadcastSessionScopedQueryReset = undefined;\n\t\t\t}\n\t\t}\n\t};\n}\n\n/** Защищает обработчик канала от посторонних или устаревших сообщений. */\nfunction isSessionScopedQueryCacheResetEvent(value: unknown): value is SessionScopedQueryCacheResetEvent {\n\treturn typeof value === \"object\" && value !== null && \"type\" in value && value.type === SESSION_SCOPED_QUERY_CACHE_RESET_EVENT;\n}\n"],"mappings":"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"}