@ryuzaki13/react-foundation-lib 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/error-report/safeValue.ts","../../src/error-report/breadcrumbs.ts","../../src/error-report/errorInfo.ts","../../src/error-report/diagnostics.ts","../../src/error-report/environment.ts","../../src/error-report/drafts.ts","../../src/error-report/transport.ts","../../src/error-report/capture.ts","../../src/error-report/runtime.ts"],"sourcesContent":["import type { ErrorReportSafeValue } from \"./types\";\n\nconst MAX_DEPTH = 4;\nconst MAX_ARRAY_ITEMS = 20;\nconst MAX_OBJECT_KEYS = 40;\n\n/**\n * Превращает произвольное значение в JSON-совместимую диагностическую форму.\n * Значения сохраняются читаемыми, потому что queryKey/meta/context нужны\n * для восстановления пользовательского сценария; тяжелые данные query cache\n * отсекаются отдельно и сюда не передаются.\n */\nexport function createDiagnosticValue(value: unknown, depth = 0): ErrorReportSafeValue {\n\tif (depth >= MAX_DEPTH) return { type: \"truncated\" };\n\tif (value === null || typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n\t\treturn value;\n\t}\n\tif (value === undefined) return { type: \"undefined\" };\n\tif (value instanceof Date) return value.toISOString();\n\tif (Array.isArray(value)) {\n\t\treturn value.slice(0, MAX_ARRAY_ITEMS).map((item) => createDiagnosticValue(item, depth + 1));\n\t}\n\tif (typeof value === \"object\") {\n\t\tconst entries = Object.entries(value as Record<string, unknown>).slice(0, MAX_OBJECT_KEYS);\n\n\t\treturn Object.fromEntries(entries.map(([key, item]) => [key, createDiagnosticValue(item, depth + 1)]));\n\t}\n\tif (typeof value === \"function\") return { type: \"function\" };\n\tif (typeof value === \"symbol\") return { type: \"symbol\" };\n\tif (typeof value === \"bigint\") return value.toString();\n\n\treturn { type: \"unknown\" };\n}\n\n/**\n * Описывает форму данных кеша без самих значений: типы, размеры массивов и ключи объектов.\n */\nexport function createDataShape(value: unknown, depth = 0): ErrorReportSafeValue {\n\tif (depth >= MAX_DEPTH) return { type: \"truncated\" };\n\tif (value === null) return { type: \"null\" };\n\tif (value === undefined) return { type: \"undefined\" };\n\tif (Array.isArray(value)) {\n\t\treturn {\n\t\t\ttype: \"array\",\n\t\t\tlength: value.length,\n\t\t\tfirstItemsShape: value.slice(0, 3).map((item) => createDataShape(item, depth + 1))\n\t\t};\n\t}\n\tif (typeof value === \"object\") {\n\t\tconst entries = Object.entries(value as Record<string, unknown>).slice(0, MAX_OBJECT_KEYS);\n\n\t\treturn {\n\t\t\ttype: \"object\",\n\t\t\tkeys: entries.map(([key]) => key),\n\t\t\tshape: Object.fromEntries(entries.map(([key, item]) => [key, createDataShape(item, depth + 1)]))\n\t\t};\n\t}\n\n\treturn { type: typeof value };\n}\n\nexport function sanitizeDetail(detail: Record<string, unknown> | undefined): Record<string, ErrorReportSafeValue> | undefined {\n\tif (!detail) return undefined;\n\n\tconst entries = Object.entries(detail)\n\t\t.slice(0, MAX_OBJECT_KEYS)\n\t\t.map(([key, value]) => [key, createDiagnosticValue(value)] as const);\n\n\treturn entries.length ? Object.fromEntries(entries) : undefined;\n}\n","import { truncateText } from \"../formatters\";\n\nimport { sanitizeDetail } from \"./safeValue\";\n\nimport type { ErrorReportBreadcrumb } from \"./types\";\n\nconst MAX_BREADCRUMBS = 80;\nconst MAX_ELEMENT_CHAIN = 20;\nconst MAX_TEXT_LENGTH = 80;\nconst MAX_ATTRIBUTE_LENGTH = 120;\nconst CLICK_TARGET_SELECTOR = [\n\t\"button\",\n\t\"a\",\n\t\"input\",\n\t\"select\",\n\t\"textarea\",\n\t\"[role]\",\n\t\"[aria-label]\",\n\t\"[title]\",\n\t\"[data-ui]\",\n\t\"[data-action]\"\n].join(\",\");\n\nlet breadcrumbs: ErrorReportBreadcrumb[] = [];\n\nfunction nowUtc() {\n\treturn new Date().toISOString();\n}\n\nexport function addErrorReportBreadcrumb(breadcrumb: Omit<ErrorReportBreadcrumb, \"utc\"> & { utc?: string }) {\n\tbreadcrumbs = [...breadcrumbs, { ...breadcrumb, utc: breadcrumb.utc ?? nowUtc() }].slice(-MAX_BREADCRUMBS);\n}\n\nexport function getErrorReportBreadcrumbs() {\n\treturn breadcrumbs;\n}\n\nexport function clearErrorReportBreadcrumbs() {\n\tbreadcrumbs = [];\n}\n\nfunction truncateAttributeText(value: string | null | undefined) {\n\treturn truncateText(value, MAX_ATTRIBUTE_LENGTH);\n}\n\nfunction readStableAttributes(element: Element) {\n\treturn {\n\t\tid: truncateAttributeText(element.id),\n\t\trole: truncateAttributeText(element.getAttribute(\"role\")),\n\t\ttype: truncateAttributeText(element.getAttribute(\"type\")),\n\t\ttitle: truncateAttributeText(element.getAttribute(\"title\")),\n\t\tariaLabel: truncateAttributeText(element.getAttribute(\"aria-label\")),\n\t\tdataUi: truncateAttributeText(element.getAttribute(\"data-ui\")),\n\t\tdataAction: truncateAttributeText(element.getAttribute(\"data-action\"))\n\t};\n}\n\nfunction readClickText(element: Element) {\n\tif (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {\n\t\treturn undefined;\n\t}\n\n\tif (!element.matches(\"button,a,[role],[aria-label],[title]\")) return undefined;\n\n\treturn truncateText(element.textContent, MAX_TEXT_LENGTH);\n}\n\nfunction escapeSelectorPart(value: string) {\n\tif (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\") {\n\t\treturn CSS.escape(value);\n\t}\n\n\treturn value.replace(/[\"\\\\#.[\\]>~+:]/g, \"\\\\$&\");\n}\n\nfunction buildElementSelector(element: Element) {\n\tconst attrs = readStableAttributes(element);\n\tconst parts = [element.tagName.toLowerCase()];\n\n\tif (attrs.id) parts.push(`#${escapeSelectorPart(attrs.id)}`);\n\tif (attrs.role) parts.push(`[role=\"${attrs.role}\"]`);\n\tif (attrs.type) parts.push(`[type=\"${attrs.type}\"]`);\n\tif (attrs.dataUi) parts.push(`[data-ui=\"${attrs.dataUi}\"]`);\n\tif (attrs.dataAction) parts.push(`[data-action=\"${attrs.dataAction}\"]`);\n\n\treturn parts.join(\"\");\n}\n\nfunction describeElement(element: Element) {\n\tconst attrs = readStableAttributes(element);\n\n\treturn {\n\t\ttag: element.tagName.toLowerCase(),\n\t\tselector: buildElementSelector(element),\n\t\ttext: readClickText(element),\n\t\t...attrs\n\t};\n}\n\nfunction compactElementDescription(description: ReturnType<typeof describeElement>) {\n\treturn Object.fromEntries(\n\t\tObject.entries(description).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0))\n\t);\n}\n\nfunction getClickElementChain(target: Element) {\n\tconst chain: Element[] = [];\n\tlet current: Element | null = target;\n\n\twhile (current && current !== document.documentElement && chain.length < MAX_ELEMENT_CHAIN) {\n\t\tchain.push(current);\n\t\tif (current === document.body) break;\n\t\tcurrent = current.parentElement;\n\t}\n\n\treturn chain;\n}\n\nfunction findMeaningfulClickTarget(target: Element) {\n\treturn target.closest(CLICK_TARGET_SELECTOR) ?? target;\n}\n\nfunction describeClickTarget(target: EventTarget | null) {\n\tif (!(target instanceof Element)) return undefined;\n\n\tconst meaningfulTarget = findMeaningfulClickTarget(target);\n\tconst chain = getClickElementChain(meaningfulTarget);\n\n\treturn chain.map((element) => buildElementSelector(element)).join(\" > \");\n}\n\nfunction describeClickDetail(event: MouseEvent) {\n\tconst target = event.target instanceof Element ? event.target : undefined;\n\tconst meaningfulTarget = target ? findMeaningfulClickTarget(target) : undefined;\n\tconst chain = meaningfulTarget\n\t\t? getClickElementChain(meaningfulTarget).map((element) => compactElementDescription(describeElement(element)))\n\t\t: [];\n\n\treturn sanitizeDetail({\n\t\tbutton: event.button,\n\t\tctrlKey: event.ctrlKey,\n\t\tshiftKey: event.shiftKey,\n\t\taltKey: event.altKey,\n\t\tclientX: event.clientX,\n\t\tclientY: event.clientY,\n\t\ttarget: meaningfulTarget ? compactElementDescription(describeElement(meaningfulTarget)) : undefined,\n\t\tchain\n\t});\n}\n\n/**\n * Подключает breadcrumbs по действиям пользователя без тел запросов.\n */\nexport function installErrorReportBrowserBreadcrumbs() {\n\tif (typeof window === \"undefined\") return () => undefined;\n\n\tconst onClick = (event: MouseEvent) => {\n\t\taddErrorReportBreadcrumb({\n\t\t\ttype: \"click\",\n\t\t\ttarget: describeClickTarget(event.target),\n\t\t\tdetail: describeClickDetail(event)\n\t\t});\n\t};\n\n\tconst onVisibilityChange = () => {\n\t\taddErrorReportBreadcrumb({\n\t\t\ttype: \"visibility\",\n\t\t\tdetail: sanitizeDetail({ visibilityState: document.visibilityState })\n\t\t});\n\t};\n\n\twindow.addEventListener(\"click\", onClick, true);\n\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\treturn () => {\n\t\twindow.removeEventListener(\"click\", onClick, true);\n\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t};\n}\n","import { normalizeText } from \"../formatters\";\n\nimport type { ErrorReportErrorInfo } from \"./types\";\n\nconst DEFAULT_ERROR_MESSAGE = \"Неизвестная ошибка\";\n\n/**\n * Гарантирует непустое диагностическое сообщение, сохраняя исходный текст ошибки.\n * Fallback нужен для Error без message и для брошенных пустых строк.\n */\nfunction resolveErrorMessage(message: string) {\n\treturn normalizeText(message) ? message : DEFAULT_ERROR_MESSAGE;\n}\n\nfunction readStatus(value: Record<string, unknown>) {\n\tconst status = value.status ?? value.statusCode ?? value.httpStatus;\n\treturn typeof status === \"number\" && Number.isFinite(status) ? status : undefined;\n}\n\nfunction readServerFnTransportErrorInfo(error: unknown): ErrorReportErrorInfo | null {\n\tif (!error || typeof error !== \"object\") {\n\t\treturn null;\n\t}\n\n\tconst transport = error as Record<string, unknown>;\n\tconst appError = transport.appError;\n\tif (!appError || typeof appError !== \"object\") {\n\t\treturn null;\n\t}\n\n\tconst payload = appError as Record<string, unknown>;\n\tconst message = normalizeText(payload.message);\n\tif (!message) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tname: \"AppError\",\n\t\tmessage,\n\t\tcode: normalizeText(payload.code),\n\t\thttpStatus: readStatus(payload)\n\t};\n}\n\n/**\n * Нормализует любое брошенное значение в форму, удобную для поиска и сохранения.\n * Stacktrace намеренно сохраняется полностью: это основная диагностическая ценность отчета.\n */\nexport function createErrorInfo(error: unknown): ErrorReportErrorInfo {\n\tconst serverFnErrorInfo = readServerFnTransportErrorInfo(error);\n\tif (serverFnErrorInfo) {\n\t\treturn serverFnErrorInfo;\n\t}\n\n\tif (error instanceof Error) {\n\t\treturn {\n\t\t\tname: error.name || \"Error\",\n\t\t\tmessage: resolveErrorMessage(error.message),\n\t\t\tstackTrace: error.stack\n\t\t};\n\t}\n\n\tif (error && typeof error === \"object\") {\n\t\tconst record = error as Record<string, unknown>;\n\t\treturn {\n\t\t\tname: typeof record.name === \"string\" ? record.name : \"Error\",\n\t\t\tmessage: resolveErrorMessage(typeof record.message === \"string\" ? record.message : String(error)),\n\t\t\tstackTrace: typeof record.stack === \"string\" ? record.stack : undefined,\n\t\t\thttpStatus: readStatus(record)\n\t\t};\n\t}\n\n\treturn {\n\t\tname: \"Error\",\n\t\tmessage: resolveErrorMessage(typeof error === \"string\" ? error : String(error ?? DEFAULT_ERROR_MESSAGE))\n\t};\n}\n","import { type PersistedQuery } from \"@tanstack/query-persist-client-core\";\nimport { type MutationKey, type MutationState, type QueryClient, type QueryKey, type QueryState } from \"@tanstack/react-query\";\n\nimport { createIndexedDbQueryStorage } from \"../query-client\";\n\nimport { createErrorInfo } from \"./errorInfo\";\nimport { createDataShape, createDiagnosticValue, sanitizeDetail } from \"./safeValue\";\nimport {\n\ttype ErrorReportMutationDiagnostics,\n\ttype ErrorReportPersistedQueryDiagnostics,\n\ttype ErrorReportQueryDiagnostics,\n\ttype ErrorReportSafeValue\n} from \"./types\";\n\ntype QueryDiagnosticSource = {\n\tqueryHash: string;\n\tqueryKey: QueryKey;\n\tstate: QueryState<unknown, unknown>;\n\tmeta?: Record<string, unknown>;\n\tgetObserversCount: () => number;\n};\n\ntype MutationDiagnosticSource = {\n\toptions: {\n\t\tmutationKey?: MutationKey;\n\t};\n\tstate: MutationState<unknown, unknown, unknown, unknown>;\n\tmeta?: Record<string, unknown>;\n};\n\nconst MAX_QUERY_DIAGNOSTICS = 40;\nconst MAX_MUTATION_DIAGNOSTICS = 40;\nconst MAX_PERSISTED_QUERY_DIAGNOSTICS = 40;\n\nfunction sanitizeMeta(meta: Record<string, unknown> | undefined): Record<string, ErrorReportSafeValue> | undefined {\n\treturn sanitizeDetail(meta);\n}\n\nexport function collectQueryDiagnostics(query: QueryDiagnosticSource): ErrorReportQueryDiagnostics {\n\treturn {\n\t\tqueryHash: query.queryHash,\n\t\tqueryKey: createDiagnosticValue(query.queryKey),\n\t\tstatus: query.state.status,\n\t\tfetchStatus: query.state.fetchStatus,\n\t\tdataUpdatedAt: query.state.dataUpdatedAt,\n\t\terrorUpdatedAt: query.state.errorUpdatedAt,\n\t\tfailureCount: query.state.fetchFailureCount,\n\t\tisInvalidated: query.state.isInvalidated,\n\t\tobserversCount: query.getObserversCount(),\n\t\tmeta: sanitizeMeta(query.meta),\n\t\tdataShape: createDataShape(query.state.data),\n\t\terror: query.state.error ? createErrorInfo(query.state.error) : undefined\n\t};\n}\n\nexport function collectMutationDiagnostics(mutation: MutationDiagnosticSource): ErrorReportMutationDiagnostics {\n\treturn {\n\t\tmutationKey: mutation.options.mutationKey ? createDiagnosticValue(mutation.options.mutationKey) : undefined,\n\t\tstatus: mutation.state.status,\n\t\tfailureCount: mutation.state.failureCount,\n\t\tsubmittedAt: mutation.state.submittedAt,\n\t\tmeta: sanitizeMeta(mutation.meta),\n\t\terror: mutation.state.error ? createErrorInfo(mutation.state.error) : undefined\n\t};\n}\n\nexport function collectQueryClientDiagnostics(queryClient: QueryClient) {\n\treturn {\n\t\tqueries: queryClient.getQueryCache().getAll().slice(-MAX_QUERY_DIAGNOSTICS).map(collectQueryDiagnostics),\n\t\tmutations: queryClient.getMutationCache().getAll().slice(-MAX_MUTATION_DIAGNOSTICS).map(collectMutationDiagnostics)\n\t};\n}\n\nfunction collectPersistedStateDiagnostics(storageKey: string, persistedQuery: PersistedQuery): ErrorReportPersistedQueryDiagnostics {\n\tconst state = persistedQuery.state;\n\n\treturn {\n\t\tstorageKey,\n\t\tbuster: persistedQuery.buster,\n\t\tqueryHash: persistedQuery.queryHash,\n\t\tqueryKey: createDiagnosticValue(persistedQuery.queryKey),\n\t\tstatus: state.status,\n\t\tfetchStatus: state.fetchStatus,\n\t\tdataUpdatedAt: state.dataUpdatedAt,\n\t\terrorUpdatedAt: state.errorUpdatedAt,\n\t\tfailureCount: state.fetchFailureCount,\n\t\terror: state.error ? createErrorInfo(state.error) : undefined\n\t};\n}\n\n/**\n * Читает IndexedDB persistence и возвращает только технические поля.\n * `state.data` и другие значения кеша не попадают в результат.\n */\nexport async function collectPersistedQueryDiagnostics(): Promise<ErrorReportPersistedQueryDiagnostics[]> {\n\tconst storage = createIndexedDbQueryStorage<PersistedQuery>();\n\tif (!storage?.entries) return [];\n\n\ttry {\n\t\tconst entries = await storage.entries();\n\t\treturn entries\n\t\t\t.slice(-MAX_PERSISTED_QUERY_DIAGNOSTICS)\n\t\t\t.map(([storageKey, persistedQuery]) => collectPersistedStateDiagnostics(storageKey, persistedQuery));\n\t} catch {\n\t\treturn [];\n\t}\n}\n","type ErrorReportEnvironmentOptions = {\n\tisDev?: boolean;\n};\n\n/**\n * Отчеты собираются только в браузере и только вне dev-режима.\n * На серверном рендеринге draft не создается, чтобы не смешивать SSR-ошибки с клиентской диагностикой.\n */\nexport function isErrorReportingEnabled(options: ErrorReportEnvironmentOptions = {}) {\n\tconst isDev = options.isDev ?? __DEV__;\n\n\treturn typeof window !== \"undefined\" && !isDev;\n}\n\nexport function getErrorReportEnvironment() {\n\tconst mode: \"development\" | \"production\" = __DEV__ ? \"development\" : \"production\";\n\n\treturn {\n\t\tmode,\n\t\tbuildId: typeof __APP_BUILD_ID__ === \"string\" ? __APP_BUILD_ID__ : undefined\n\t};\n}\n\nexport function getErrorReportClientEnvironment() {\n\tif (typeof window === \"undefined\") return undefined;\n\n\treturn {\n\t\tuserAgent: window.navigator.userAgent,\n\t\tlanguage: window.navigator.language,\n\t\ttimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone\n\t};\n}\n","import { uuidv4 } from \"../crypto\";\nimport { formatDateAsODataDatetime } from \"../formatters\";\nimport { getSessionStorageId } from \"../session-storage\";\n\nimport { getErrorReportBreadcrumbs } from \"./breadcrumbs\";\nimport { getErrorReportClientEnvironment, getErrorReportEnvironment, isErrorReportingEnabled } from \"./environment\";\n\nimport type { ErrorReportCategory, ErrorReportDraft, ErrorReportErrorInfo, ErrorReportPayload, ErrorReportSafeValue } from \"./types\";\n\nconst STORAGE_KEY = `${__APP_ID__}.errorReport.drafts.v1`;\nconst SESSION_STORAGE_KEY = `${__APP_ID__}.errorReport.sessionId.v1`;\nconst MAX_DRAFTS = 10;\n\nlet drafts = loadDrafts();\n\nfunction nowUtc() {\n\treturn formatDateAsODataDatetime(new Date());\n}\n\nfunction loadDrafts(): ErrorReportDraft[] {\n\tif (typeof sessionStorage === \"undefined\") return [];\n\n\ttry {\n\t\tconst raw = sessionStorage.getItem(STORAGE_KEY);\n\t\tconst parsed = raw ? JSON.parse(raw) : [];\n\t\treturn Array.isArray(parsed) ? (parsed as ErrorReportDraft[]) : [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction saveDrafts(nextDrafts: ErrorReportDraft[]) {\n\tdrafts = nextDrafts.slice(-MAX_DRAFTS);\n\tif (typeof sessionStorage === \"undefined\") return;\n\n\ttry {\n\t\tsessionStorage.setItem(STORAGE_KEY, JSON.stringify(drafts));\n\t} catch {\n\t\t// Переполнение sessionStorage не должно ломать пользовательский сценарий.\n\t}\n}\n\nfunction getLocationSnapshot() {\n\tif (typeof window === \"undefined\") return undefined;\n\n\treturn {\n\t\tpathname: window.location.pathname,\n\t\torigin: window.location.origin\n\t};\n}\n\nfunction getViewportSnapshot() {\n\tif (typeof window === \"undefined\") return undefined;\n\n\treturn {\n\t\twidth: window.innerWidth,\n\t\theight: window.innerHeight,\n\t\tdevicePixelRatio: window.devicePixelRatio\n\t};\n}\n\nexport function getErrorReportDraft(reportId: string) {\n\treturn drafts.find((draft) => draft.reportId === reportId);\n}\n\nexport function updateErrorReportDraft(reportId: string, patch: Partial<ErrorReportDraft>) {\n\tconst current = getErrorReportDraft(reportId);\n\tif (!current) return undefined;\n\n\tconst next = { ...current, ...patch };\n\tsaveDrafts(drafts.map((draft) => (draft.reportId === reportId ? next : draft)));\n\treturn next;\n}\n\nexport function getErrorReportDrafts() {\n\treturn drafts;\n}\n\nexport function captureErrorReportDraft(args: {\n\tcategory: ErrorReportCategory;\n\tsource: string;\n\terror: ErrorReportErrorInfo;\n\tquery?: ErrorReportPayload[\"query\"];\n\tmutation?: ErrorReportPayload[\"mutation\"];\n\tqueryClient?: ErrorReportPayload[\"queryClient\"];\n\tpersistedQueries?: ErrorReportPayload[\"persistedQueries\"];\n\treact?: ErrorReportPayload[\"react\"];\n\tcontext?: Record<string, ErrorReportSafeValue>;\n}) {\n\tif (!isErrorReportingEnabled()) return undefined;\n\n\tconst reportId = uuidv4();\n\tconst sessionId = getSessionStorageId(SESSION_STORAGE_KEY);\n\tconst createdUtc = nowUtc();\n\tconst payload: ErrorReportPayload = {\n\t\treportId,\n\t\tsessionId,\n\t\tcreatedUtc,\n\t\tcategory: args.category,\n\t\tsource: args.source,\n\t\terror: args.error,\n\t\tenvironment: getErrorReportEnvironment(),\n\t\tclient: getErrorReportClientEnvironment(),\n\t\tlocation: getLocationSnapshot(),\n\t\tviewport: getViewportSnapshot(),\n\t\treact: args.react,\n\t\tquery: args.query,\n\t\tmutation: args.mutation,\n\t\tqueryClient: args.queryClient,\n\t\tpersistedQueries: args.persistedQueries,\n\t\tbreadcrumbs: getErrorReportBreadcrumbs(),\n\t\tcontext: args.context\n\t};\n\tconst draft: ErrorReportDraft = {\n\t\treportId,\n\t\tsessionId,\n\t\tcreatedUtc,\n\t\tcategory: args.category,\n\t\tstatus: \"pending\",\n\t\tpayload\n\t};\n\n\tsaveDrafts([...drafts, draft]);\n\treturn draft;\n}\n","export type ErrorReportTransportHtmlSummary = {\n\tlength: number;\n\ttitle?: string;\n\tformCount?: number;\n\tinputNames?: string[];\n\ttextPreview?: string;\n};\n\nexport type ErrorReportTransportErrorContext = {\n\tsource: \"fetch.parseResponse\" | \"fetch.buildHttpError\";\n\trequestUrl?: string;\n\tresponseUrl?: string;\n\tmethod?: string;\n\tbaseUrlType?: string;\n\tsapClient?: string | null;\n\tstatus?: number;\n\tstatusText?: string;\n\tcontentType?: string;\n\tredirected?: boolean;\n\tresponseType?: string;\n\thtml?: ErrorReportTransportHtmlSummary;\n};\n\nexport type ErrorReportTransportErrorReporter = (error: Error, context: ErrorReportTransportErrorContext) => void | Promise<void>;\n\nconst DEDUPLICATION_INTERVAL_MS = 60_000;\nconst MAX_RECENT_REPORT_KEYS = 100;\nconst TRANSPORT_REPORT_DELAY_MS = 250;\n\nlet transportErrorReporter: ErrorReportTransportErrorReporter | undefined;\nconst scheduledTransportErrors = new WeakSet<Error>();\nconst suppressedTransportErrors = new WeakSet<Error>();\nconst recentReportKeys = new Map<string, number>();\n\nfunction createReportKey(context: ErrorReportTransportErrorContext) {\n\treturn [context.source, context.method, context.status, context.requestUrl, context.responseUrl].filter(Boolean).join(\"|\");\n}\n\nfunction shouldSkipByRecentKey(context: ErrorReportTransportErrorContext) {\n\tconst now = Date.now();\n\tconst key = createReportKey(context);\n\n\tfor (const [recentKey, timestamp] of recentReportKeys) {\n\t\tif (now - timestamp > DEDUPLICATION_INTERVAL_MS || recentReportKeys.size > MAX_RECENT_REPORT_KEYS) {\n\t\t\trecentReportKeys.delete(recentKey);\n\t\t}\n\t}\n\n\tconst lastReportedAt = recentReportKeys.get(key);\n\tif (lastReportedAt && now - lastReportedAt < DEDUPLICATION_INTERVAL_MS) return true;\n\n\trecentReportKeys.set(key, now);\n\treturn false;\n}\n\n/**\n * Регистрирует приложение как получателя транспортных ошибок.\n * Сам OData transport не знает про QueryClient и UI, поэтому только публикует событие,\n * а app-слой решает, как именно создать и отправить отчет.\n */\nexport function setErrorReportTransportErrorReporter(reporter: ErrorReportTransportErrorReporter | undefined) {\n\ttransportErrorReporter = reporter;\n\trecentReportKeys.clear();\n}\n\nexport function isTransportErrorReportScheduled(error: unknown) {\n\treturn error instanceof Error && scheduledTransportErrors.has(error);\n}\n\nexport function suppressTransportErrorReport(error: unknown) {\n\tif (error instanceof Error) {\n\t\tsuppressedTransportErrors.add(error);\n\t}\n}\n\nexport function reportTransportError(error: Error, context: ErrorReportTransportErrorContext) {\n\tscheduledTransportErrors.add(error);\n\n\t// Даем React Query шанс перехватить эту же ошибку и создать query/mutation-отчет\n\t// с более полезным снимком кеша. Если такого перехвата нет, transport-отчет уйдет сам.\n\tsetTimeout(() => {\n\t\tif (suppressedTransportErrors.has(error)) return;\n\t\tif (!transportErrorReporter) return;\n\t\tif (shouldSkipByRecentKey(context)) return;\n\n\t\ttry {\n\t\t\tconst result = transportErrorReporter(error, context);\n\t\t\tvoid Promise.resolve(result).catch(() => undefined);\n\t\t} catch {\n\t\t\t// Ошибка диагностического hook не должна менять поведение исходного fetch.\n\t\t}\n\t}, TRANSPORT_REPORT_DELAY_MS);\n}\n","import { type Mutation, type Query, type QueryClient } from \"@tanstack/react-query\";\n\nimport { addErrorReportBreadcrumb } from \"./breadcrumbs\";\nimport {\n\tcollectMutationDiagnostics,\n\tcollectPersistedQueryDiagnostics,\n\tcollectQueryClientDiagnostics,\n\tcollectQueryDiagnostics\n} from \"./diagnostics\";\nimport { captureErrorReportDraft } from \"./drafts\";\nimport { createErrorInfo } from \"./errorInfo\";\nimport { sanitizeDetail } from \"./safeValue\";\nimport { suppressTransportErrorReport } from \"./transport\";\n\nimport type { ErrorReportMutationContext, ErrorReportQueryContext, ErrorReportRuntimeContext } from \"./types\";\n\nexport async function captureQueryErrorReport(\n\terror: unknown,\n\tquery: Query<unknown, unknown, unknown>,\n\tqueryClient: QueryClient,\n\tcontext?: ErrorReportQueryContext\n) {\n\tsuppressTransportErrorReport(error);\n\tconst source = context?.source ?? \"query\";\n\n\taddErrorReportBreadcrumb({\n\t\ttype: \"query-error\",\n\t\tdetail: sanitizeDetail({\n\t\t\tqueryHash: query.queryHash,\n\t\t\tqueryKey: context?.queryKey ?? query.queryKey,\n\t\t\t...context?.detail\n\t\t})\n\t});\n\n\tconst [persistedQueries, queryClientDiagnostics] = await Promise.all([\n\t\tcollectPersistedQueryDiagnostics(),\n\t\tPromise.resolve(collectQueryClientDiagnostics(queryClient))\n\t]);\n\n\treturn captureErrorReportDraft({\n\t\tcategory: \"query\",\n\t\tsource,\n\t\terror: context?.errorInfo ?? createErrorInfo(error),\n\t\tquery: collectQueryDiagnostics(query),\n\t\tqueryClient: queryClientDiagnostics,\n\t\tpersistedQueries,\n\t\tcontext: sanitizeDetail(context?.detail)\n\t});\n}\n\nexport async function captureMutationErrorReport(\n\terror: unknown,\n\tmutation: Mutation<unknown, unknown, unknown, unknown>,\n\tqueryClient: QueryClient,\n\tcontext: ErrorReportMutationContext = {}\n) {\n\tsuppressTransportErrorReport(error);\n\tconst source = context.source ?? \"mutation\";\n\n\taddErrorReportBreadcrumb({\n\t\ttype: \"mutation-error\",\n\t\tdetail: sanitizeDetail({ mutationKey: mutation.options.mutationKey, ...context.detail })\n\t});\n\n\tconst [persistedQueries, queryClientDiagnostics] = await Promise.all([\n\t\tcollectPersistedQueryDiagnostics(),\n\t\tPromise.resolve(collectQueryClientDiagnostics(queryClient))\n\t]);\n\n\treturn captureErrorReportDraft({\n\t\tcategory: \"mutation\",\n\t\tsource,\n\t\terror: context.errorInfo ?? createErrorInfo(error),\n\t\tmutation: collectMutationDiagnostics(mutation),\n\t\tqueryClient: queryClientDiagnostics,\n\t\tpersistedQueries\n\t});\n}\n\nexport async function captureRuntimeErrorReport(error: unknown, context: ErrorReportRuntimeContext = {}, queryClient?: QueryClient) {\n\tconst source = context.source ?? context.category ?? \"runtime\";\n\n\taddErrorReportBreadcrumb({\n\t\ttype: \"runtime-error\",\n\t\tdetail: sanitizeDetail({ category: context.category ?? \"runtime\", source, ...context.detail })\n\t});\n\n\tconst [persistedQueries, queryClientDiagnostics] = queryClient\n\t\t? await Promise.all([collectPersistedQueryDiagnostics(), Promise.resolve(collectQueryClientDiagnostics(queryClient))])\n\t\t: [undefined, undefined];\n\n\treturn captureErrorReportDraft({\n\t\tcategory: context.category ?? \"runtime\",\n\t\tsource,\n\t\terror: context.errorInfo ?? createErrorInfo(error),\n\t\treact: context.componentStack ? { componentStack: context.componentStack } : undefined,\n\t\tqueryClient: queryClientDiagnostics,\n\t\tpersistedQueries,\n\t\tcontext: sanitizeDetail(context.detail)\n\t});\n}\n","import type { ErrorReportRuntimeContext } from \"./types\";\n\nexport type ErrorReportRuntimeErrorReporter = (error: unknown, context: ErrorReportRuntimeContext) => void | Promise<void>;\n\nlet runtimeErrorReporter: ErrorReportRuntimeErrorReporter | undefined;\n\n/**\n * Регистрирует app-слой как получателя ручных runtime-ошибок.\n * Shared-код может вызвать reportRuntimeError без знания о QueryClient, UI и режиме доставки.\n */\nexport function setErrorReportRuntimeErrorReporter(reporter: ErrorReportRuntimeErrorReporter | undefined) {\n\truntimeErrorReporter = reporter;\n}\n\n/**\n * Ручная отправка runtime-ошибки из try/catch или другого кода вне React-компонента.\n * Функция не бросает исключений: диагностический отчет не должен ломать основной сценарий.\n *\n * @example\n *\n * try {\n * // ...\n * } catch (error) {\n * reportRuntimeError(error, {\n * detail: {\n * source: \"s3-download\",\n * action: \"download-from-s3\"\n * }\n * });\n * }\n */\nexport async function reportRuntimeError(error: unknown, context: ErrorReportRuntimeContext = {}) {\n\tif (!runtimeErrorReporter) return;\n\n\ttry {\n\t\tawait runtimeErrorReporter(error, context);\n\t} catch {\n\t\t// Ошибка диагностического hook не должна менять поведение пользовательского кода.\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/error-report/safeValue.ts","../../src/error-report/breadcrumbs.ts","../../src/error-report/errorInfo.ts","../../src/error-report/diagnostics.ts","../../src/error-report/environment.ts","../../src/error-report/drafts.ts","../../src/error-report/runtimeDeduplication.ts","../../src/error-report/transport.ts","../../src/error-report/capture.ts","../../src/error-report/runtime.ts"],"sourcesContent":["import type { ErrorReportSafeValue } from \"./types\";\n\nconst MAX_DEPTH = 4;\nconst MAX_ARRAY_ITEMS = 20;\nconst MAX_OBJECT_KEYS = 40;\n\n/**\n * Превращает произвольное значение в JSON-совместимую диагностическую форму.\n * Значения сохраняются читаемыми, потому что queryKey/meta/context нужны\n * для восстановления пользовательского сценария; тяжелые данные query cache\n * отсекаются отдельно и сюда не передаются.\n */\nexport function createDiagnosticValue(value: unknown, depth = 0): ErrorReportSafeValue {\n\tif (depth >= MAX_DEPTH) return { type: \"truncated\" };\n\tif (value === null || typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n\t\treturn value;\n\t}\n\tif (value === undefined) return { type: \"undefined\" };\n\tif (value instanceof Date) return value.toISOString();\n\tif (Array.isArray(value)) {\n\t\treturn value.slice(0, MAX_ARRAY_ITEMS).map((item) => createDiagnosticValue(item, depth + 1));\n\t}\n\tif (typeof value === \"object\") {\n\t\tconst entries = Object.entries(value as Record<string, unknown>).slice(0, MAX_OBJECT_KEYS);\n\n\t\treturn Object.fromEntries(entries.map(([key, item]) => [key, createDiagnosticValue(item, depth + 1)]));\n\t}\n\tif (typeof value === \"function\") return { type: \"function\" };\n\tif (typeof value === \"symbol\") return { type: \"symbol\" };\n\tif (typeof value === \"bigint\") return value.toString();\n\n\treturn { type: \"unknown\" };\n}\n\n/**\n * Описывает форму данных кеша без самих значений: типы, размеры массивов и ключи объектов.\n */\nexport function createDataShape(value: unknown, depth = 0): ErrorReportSafeValue {\n\tif (depth >= MAX_DEPTH) return { type: \"truncated\" };\n\tif (value === null) return { type: \"null\" };\n\tif (value === undefined) return { type: \"undefined\" };\n\tif (Array.isArray(value)) {\n\t\treturn {\n\t\t\ttype: \"array\",\n\t\t\tlength: value.length,\n\t\t\tfirstItemsShape: value.slice(0, 3).map((item) => createDataShape(item, depth + 1))\n\t\t};\n\t}\n\tif (typeof value === \"object\") {\n\t\tconst entries = Object.entries(value as Record<string, unknown>).slice(0, MAX_OBJECT_KEYS);\n\n\t\treturn {\n\t\t\ttype: \"object\",\n\t\t\tkeys: entries.map(([key]) => key),\n\t\t\tshape: Object.fromEntries(entries.map(([key, item]) => [key, createDataShape(item, depth + 1)]))\n\t\t};\n\t}\n\n\treturn { type: typeof value };\n}\n\nexport function sanitizeDetail(detail: Record<string, unknown> | undefined): Record<string, ErrorReportSafeValue> | undefined {\n\tif (!detail) return undefined;\n\n\tconst entries = Object.entries(detail)\n\t\t.slice(0, MAX_OBJECT_KEYS)\n\t\t.map(([key, value]) => [key, createDiagnosticValue(value)] as const);\n\n\treturn entries.length ? Object.fromEntries(entries) : undefined;\n}\n","import { truncateText } from \"../formatters\";\n\nimport { sanitizeDetail } from \"./safeValue\";\n\nimport type { ErrorReportBreadcrumb } from \"./types\";\n\nconst MAX_BREADCRUMBS = 80;\nconst MAX_ELEMENT_CHAIN = 20;\nconst MAX_TEXT_LENGTH = 80;\nconst MAX_ATTRIBUTE_LENGTH = 120;\nconst CLICK_TARGET_SELECTOR = [\n\t\"button\",\n\t\"a\",\n\t\"input\",\n\t\"select\",\n\t\"textarea\",\n\t\"[role]\",\n\t\"[aria-label]\",\n\t\"[title]\",\n\t\"[data-ui]\",\n\t\"[data-action]\"\n].join(\",\");\n\nlet breadcrumbs: ErrorReportBreadcrumb[] = [];\n\nfunction nowUtc() {\n\treturn new Date().toISOString();\n}\n\nexport function addErrorReportBreadcrumb(breadcrumb: Omit<ErrorReportBreadcrumb, \"utc\"> & { utc?: string }) {\n\tbreadcrumbs = [...breadcrumbs, { ...breadcrumb, utc: breadcrumb.utc ?? nowUtc() }].slice(-MAX_BREADCRUMBS);\n}\n\nexport function getErrorReportBreadcrumbs() {\n\treturn breadcrumbs;\n}\n\nexport function clearErrorReportBreadcrumbs() {\n\tbreadcrumbs = [];\n}\n\nfunction truncateAttributeText(value: string | null | undefined) {\n\treturn truncateText(value, MAX_ATTRIBUTE_LENGTH);\n}\n\nfunction readStableAttributes(element: Element) {\n\treturn {\n\t\tid: truncateAttributeText(element.id),\n\t\trole: truncateAttributeText(element.getAttribute(\"role\")),\n\t\ttype: truncateAttributeText(element.getAttribute(\"type\")),\n\t\ttitle: truncateAttributeText(element.getAttribute(\"title\")),\n\t\tariaLabel: truncateAttributeText(element.getAttribute(\"aria-label\")),\n\t\tdataUi: truncateAttributeText(element.getAttribute(\"data-ui\")),\n\t\tdataAction: truncateAttributeText(element.getAttribute(\"data-action\"))\n\t};\n}\n\nfunction readClickText(element: Element) {\n\tif (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {\n\t\treturn undefined;\n\t}\n\n\tif (!element.matches(\"button,a,[role],[aria-label],[title]\")) return undefined;\n\n\treturn truncateText(element.textContent, MAX_TEXT_LENGTH);\n}\n\nfunction escapeSelectorPart(value: string) {\n\tif (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\") {\n\t\treturn CSS.escape(value);\n\t}\n\n\treturn value.replace(/[\"\\\\#.[\\]>~+:]/g, \"\\\\$&\");\n}\n\nfunction buildElementSelector(element: Element) {\n\tconst attrs = readStableAttributes(element);\n\tconst parts = [element.tagName.toLowerCase()];\n\n\tif (attrs.id) parts.push(`#${escapeSelectorPart(attrs.id)}`);\n\tif (attrs.role) parts.push(`[role=\"${attrs.role}\"]`);\n\tif (attrs.type) parts.push(`[type=\"${attrs.type}\"]`);\n\tif (attrs.dataUi) parts.push(`[data-ui=\"${attrs.dataUi}\"]`);\n\tif (attrs.dataAction) parts.push(`[data-action=\"${attrs.dataAction}\"]`);\n\n\treturn parts.join(\"\");\n}\n\nfunction describeElement(element: Element) {\n\tconst attrs = readStableAttributes(element);\n\n\treturn {\n\t\ttag: element.tagName.toLowerCase(),\n\t\tselector: buildElementSelector(element),\n\t\ttext: readClickText(element),\n\t\t...attrs\n\t};\n}\n\nfunction compactElementDescription(description: ReturnType<typeof describeElement>) {\n\treturn Object.fromEntries(\n\t\tObject.entries(description).filter(([, value]) => value !== undefined && (!Array.isArray(value) || value.length > 0))\n\t);\n}\n\nfunction getClickElementChain(target: Element) {\n\tconst chain: Element[] = [];\n\tlet current: Element | null = target;\n\n\twhile (current && current !== document.documentElement && chain.length < MAX_ELEMENT_CHAIN) {\n\t\tchain.push(current);\n\t\tif (current === document.body) break;\n\t\tcurrent = current.parentElement;\n\t}\n\n\treturn chain;\n}\n\nfunction findMeaningfulClickTarget(target: Element) {\n\treturn target.closest(CLICK_TARGET_SELECTOR) ?? target;\n}\n\nfunction describeClickTarget(target: EventTarget | null) {\n\tif (!(target instanceof Element)) return undefined;\n\n\tconst meaningfulTarget = findMeaningfulClickTarget(target);\n\tconst chain = getClickElementChain(meaningfulTarget);\n\n\treturn chain.map((element) => buildElementSelector(element)).join(\" > \");\n}\n\nfunction describeClickDetail(event: MouseEvent) {\n\tconst target = event.target instanceof Element ? event.target : undefined;\n\tconst meaningfulTarget = target ? findMeaningfulClickTarget(target) : undefined;\n\tconst chain = meaningfulTarget\n\t\t? getClickElementChain(meaningfulTarget).map((element) => compactElementDescription(describeElement(element)))\n\t\t: [];\n\n\treturn sanitizeDetail({\n\t\tbutton: event.button,\n\t\tctrlKey: event.ctrlKey,\n\t\tshiftKey: event.shiftKey,\n\t\taltKey: event.altKey,\n\t\tclientX: event.clientX,\n\t\tclientY: event.clientY,\n\t\ttarget: meaningfulTarget ? compactElementDescription(describeElement(meaningfulTarget)) : undefined,\n\t\tchain\n\t});\n}\n\n/**\n * Подключает breadcrumbs по действиям пользователя без тел запросов.\n */\nexport function installErrorReportBrowserBreadcrumbs() {\n\tif (typeof window === \"undefined\") return () => undefined;\n\n\tconst onClick = (event: MouseEvent) => {\n\t\taddErrorReportBreadcrumb({\n\t\t\ttype: \"click\",\n\t\t\ttarget: describeClickTarget(event.target),\n\t\t\tdetail: describeClickDetail(event)\n\t\t});\n\t};\n\n\tconst onVisibilityChange = () => {\n\t\taddErrorReportBreadcrumb({\n\t\t\ttype: \"visibility\",\n\t\t\tdetail: sanitizeDetail({ visibilityState: document.visibilityState })\n\t\t});\n\t};\n\n\twindow.addEventListener(\"click\", onClick, true);\n\tdocument.addEventListener(\"visibilitychange\", onVisibilityChange);\n\n\treturn () => {\n\t\twindow.removeEventListener(\"click\", onClick, true);\n\t\tdocument.removeEventListener(\"visibilitychange\", onVisibilityChange);\n\t};\n}\n","import { normalizeText } from \"../formatters\";\n\nimport type { ErrorReportErrorInfo } from \"./types\";\n\nconst DEFAULT_ERROR_MESSAGE = \"Неизвестная ошибка\";\n\n/**\n * Гарантирует непустое диагностическое сообщение, сохраняя исходный текст ошибки.\n * Fallback нужен для Error без message и для брошенных пустых строк.\n */\nfunction resolveErrorMessage(message: string) {\n\treturn normalizeText(message) ? message : DEFAULT_ERROR_MESSAGE;\n}\n\nfunction readStatus(value: Record<string, unknown>) {\n\tconst status = value.status ?? value.statusCode ?? value.httpStatus;\n\treturn typeof status === \"number\" && Number.isFinite(status) ? status : undefined;\n}\n\nfunction readServerFnTransportErrorInfo(error: unknown): ErrorReportErrorInfo | null {\n\tif (!error || typeof error !== \"object\") {\n\t\treturn null;\n\t}\n\n\tconst transport = error as Record<string, unknown>;\n\tconst appError = transport.appError;\n\tif (!appError || typeof appError !== \"object\") {\n\t\treturn null;\n\t}\n\n\tconst payload = appError as Record<string, unknown>;\n\tconst message = normalizeText(payload.message);\n\tif (!message) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tname: \"AppError\",\n\t\tmessage,\n\t\tcode: normalizeText(payload.code),\n\t\thttpStatus: readStatus(payload)\n\t};\n}\n\n/**\n * Нормализует любое брошенное значение в форму, удобную для поиска и сохранения.\n * Stacktrace намеренно сохраняется полностью: это основная диагностическая ценность отчета.\n */\nexport function createErrorInfo(error: unknown): ErrorReportErrorInfo {\n\tconst serverFnErrorInfo = readServerFnTransportErrorInfo(error);\n\tif (serverFnErrorInfo) {\n\t\treturn serverFnErrorInfo;\n\t}\n\n\tif (error instanceof Error) {\n\t\treturn {\n\t\t\tname: error.name || \"Error\",\n\t\t\tmessage: resolveErrorMessage(error.message),\n\t\t\tstackTrace: error.stack\n\t\t};\n\t}\n\n\tif (error && typeof error === \"object\") {\n\t\tconst record = error as Record<string, unknown>;\n\t\treturn {\n\t\t\tname: typeof record.name === \"string\" ? record.name : \"Error\",\n\t\t\tmessage: resolveErrorMessage(typeof record.message === \"string\" ? record.message : String(error)),\n\t\t\tstackTrace: typeof record.stack === \"string\" ? record.stack : undefined,\n\t\t\thttpStatus: readStatus(record)\n\t\t};\n\t}\n\n\treturn {\n\t\tname: \"Error\",\n\t\tmessage: resolveErrorMessage(typeof error === \"string\" ? error : String(error ?? DEFAULT_ERROR_MESSAGE))\n\t};\n}\n","import { type PersistedQuery } from \"@tanstack/query-persist-client-core\";\nimport { type MutationKey, type MutationState, type QueryClient, type QueryKey, type QueryState } from \"@tanstack/react-query\";\n\nimport { createIndexedDbQueryStorage } from \"../query-client\";\n\nimport { createErrorInfo } from \"./errorInfo\";\nimport { createDataShape, createDiagnosticValue, sanitizeDetail } from \"./safeValue\";\nimport {\n\ttype ErrorReportMutationDiagnostics,\n\ttype ErrorReportPersistedQueryDiagnostics,\n\ttype ErrorReportQueryDiagnostics,\n\ttype ErrorReportSafeValue\n} from \"./types\";\n\ntype QueryDiagnosticSource = {\n\tqueryHash: string;\n\tqueryKey: QueryKey;\n\tstate: QueryState<unknown, unknown>;\n\tmeta?: Record<string, unknown>;\n\tgetObserversCount: () => number;\n};\n\ntype MutationDiagnosticSource = {\n\toptions: {\n\t\tmutationKey?: MutationKey;\n\t};\n\tstate: MutationState<unknown, unknown, unknown, unknown>;\n\tmeta?: Record<string, unknown>;\n};\n\nconst MAX_QUERY_DIAGNOSTICS = 40;\nconst MAX_MUTATION_DIAGNOSTICS = 40;\nconst MAX_PERSISTED_QUERY_DIAGNOSTICS = 40;\n\nfunction sanitizeMeta(meta: Record<string, unknown> | undefined): Record<string, ErrorReportSafeValue> | undefined {\n\treturn sanitizeDetail(meta);\n}\n\nexport function collectQueryDiagnostics(query: QueryDiagnosticSource): ErrorReportQueryDiagnostics {\n\treturn {\n\t\tqueryHash: query.queryHash,\n\t\tqueryKey: createDiagnosticValue(query.queryKey),\n\t\tstatus: query.state.status,\n\t\tfetchStatus: query.state.fetchStatus,\n\t\tdataUpdatedAt: query.state.dataUpdatedAt,\n\t\terrorUpdatedAt: query.state.errorUpdatedAt,\n\t\tfailureCount: query.state.fetchFailureCount,\n\t\tisInvalidated: query.state.isInvalidated,\n\t\tobserversCount: query.getObserversCount(),\n\t\tmeta: sanitizeMeta(query.meta),\n\t\tdataShape: createDataShape(query.state.data),\n\t\terror: query.state.error ? createErrorInfo(query.state.error) : undefined\n\t};\n}\n\nexport function collectMutationDiagnostics(mutation: MutationDiagnosticSource): ErrorReportMutationDiagnostics {\n\treturn {\n\t\tmutationKey: mutation.options.mutationKey ? createDiagnosticValue(mutation.options.mutationKey) : undefined,\n\t\tstatus: mutation.state.status,\n\t\tfailureCount: mutation.state.failureCount,\n\t\tsubmittedAt: mutation.state.submittedAt,\n\t\tmeta: sanitizeMeta(mutation.meta),\n\t\terror: mutation.state.error ? createErrorInfo(mutation.state.error) : undefined\n\t};\n}\n\nexport function collectQueryClientDiagnostics(queryClient: QueryClient) {\n\treturn {\n\t\tqueries: queryClient.getQueryCache().getAll().slice(-MAX_QUERY_DIAGNOSTICS).map(collectQueryDiagnostics),\n\t\tmutations: queryClient.getMutationCache().getAll().slice(-MAX_MUTATION_DIAGNOSTICS).map(collectMutationDiagnostics)\n\t};\n}\n\nfunction collectPersistedStateDiagnostics(storageKey: string, persistedQuery: PersistedQuery): ErrorReportPersistedQueryDiagnostics {\n\tconst state = persistedQuery.state;\n\n\treturn {\n\t\tstorageKey,\n\t\tbuster: persistedQuery.buster,\n\t\tqueryHash: persistedQuery.queryHash,\n\t\tqueryKey: createDiagnosticValue(persistedQuery.queryKey),\n\t\tstatus: state.status,\n\t\tfetchStatus: state.fetchStatus,\n\t\tdataUpdatedAt: state.dataUpdatedAt,\n\t\terrorUpdatedAt: state.errorUpdatedAt,\n\t\tfailureCount: state.fetchFailureCount,\n\t\terror: state.error ? createErrorInfo(state.error) : undefined\n\t};\n}\n\n/**\n * Читает IndexedDB persistence и возвращает только технические поля.\n * `state.data` и другие значения кеша не попадают в результат.\n */\nexport async function collectPersistedQueryDiagnostics(): Promise<ErrorReportPersistedQueryDiagnostics[]> {\n\tconst storage = createIndexedDbQueryStorage<PersistedQuery>();\n\tif (!storage?.entries) return [];\n\n\ttry {\n\t\tconst entries = await storage.entries();\n\t\treturn entries\n\t\t\t.slice(-MAX_PERSISTED_QUERY_DIAGNOSTICS)\n\t\t\t.map(([storageKey, persistedQuery]) => collectPersistedStateDiagnostics(storageKey, persistedQuery));\n\t} catch {\n\t\treturn [];\n\t}\n}\n","type ErrorReportEnvironmentOptions = {\n\tisDev?: boolean;\n};\n\n/**\n * Отчеты собираются только в браузере и только вне dev-режима.\n * На серверном рендеринге draft не создается, чтобы не смешивать SSR-ошибки с клиентской диагностикой.\n */\nexport function isErrorReportingEnabled(options: ErrorReportEnvironmentOptions = {}) {\n\tconst isDev = options.isDev ?? __DEV__;\n\n\treturn typeof window !== \"undefined\" && !isDev;\n}\n\nexport function getErrorReportEnvironment() {\n\tconst mode: \"development\" | \"production\" = __DEV__ ? \"development\" : \"production\";\n\n\treturn {\n\t\tmode,\n\t\tbuildId: typeof __APP_BUILD_ID__ === \"string\" ? __APP_BUILD_ID__ : undefined\n\t};\n}\n\nexport function getErrorReportClientEnvironment() {\n\tif (typeof window === \"undefined\") return undefined;\n\n\treturn {\n\t\tuserAgent: window.navigator.userAgent,\n\t\tlanguage: window.navigator.language,\n\t\ttimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone\n\t};\n}\n","import { uuidv4 } from \"../crypto\";\nimport { formatDateAsODataDatetime } from \"../formatters\";\nimport { getSessionStorageId } from \"../session-storage\";\n\nimport { getErrorReportBreadcrumbs } from \"./breadcrumbs\";\nimport { getErrorReportClientEnvironment, getErrorReportEnvironment, isErrorReportingEnabled } from \"./environment\";\n\nimport type { ErrorReportCategory, ErrorReportDraft, ErrorReportErrorInfo, ErrorReportPayload, ErrorReportSafeValue } from \"./types\";\n\nconst STORAGE_KEY = `${__APP_ID__}.errorReport.drafts.v1`;\nconst SESSION_STORAGE_KEY = `${__APP_ID__}.errorReport.sessionId.v1`;\nconst MAX_DRAFTS = 10;\n\nlet drafts = loadDrafts();\n\nfunction nowUtc() {\n\treturn formatDateAsODataDatetime(new Date());\n}\n\nfunction loadDrafts(): ErrorReportDraft[] {\n\tif (typeof sessionStorage === \"undefined\") return [];\n\n\ttry {\n\t\tconst raw = sessionStorage.getItem(STORAGE_KEY);\n\t\tconst parsed = raw ? JSON.parse(raw) : [];\n\t\treturn Array.isArray(parsed) ? (parsed as ErrorReportDraft[]) : [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction saveDrafts(nextDrafts: ErrorReportDraft[]) {\n\tdrafts = nextDrafts.slice(-MAX_DRAFTS);\n\tif (typeof sessionStorage === \"undefined\") return;\n\n\ttry {\n\t\tsessionStorage.setItem(STORAGE_KEY, JSON.stringify(drafts));\n\t} catch {\n\t\t// Переполнение sessionStorage не должно ломать пользовательский сценарий.\n\t}\n}\n\nfunction getLocationSnapshot() {\n\tif (typeof window === \"undefined\") return undefined;\n\n\treturn {\n\t\tpathname: window.location.pathname,\n\t\torigin: window.location.origin\n\t};\n}\n\nfunction getViewportSnapshot() {\n\tif (typeof window === \"undefined\") return undefined;\n\n\treturn {\n\t\twidth: window.innerWidth,\n\t\theight: window.innerHeight,\n\t\tdevicePixelRatio: window.devicePixelRatio\n\t};\n}\n\nexport function getErrorReportDraft(reportId: string) {\n\treturn drafts.find((draft) => draft.reportId === reportId);\n}\n\nexport function updateErrorReportDraft(reportId: string, patch: Partial<ErrorReportDraft>) {\n\tconst current = getErrorReportDraft(reportId);\n\tif (!current) return undefined;\n\n\tconst next = { ...current, ...patch };\n\tsaveDrafts(drafts.map((draft) => (draft.reportId === reportId ? next : draft)));\n\treturn next;\n}\n\nexport function getErrorReportDrafts() {\n\treturn drafts;\n}\n\nexport function captureErrorReportDraft(args: {\n\tcategory: ErrorReportCategory;\n\tsource: string;\n\terror: ErrorReportErrorInfo;\n\tquery?: ErrorReportPayload[\"query\"];\n\tmutation?: ErrorReportPayload[\"mutation\"];\n\tqueryClient?: ErrorReportPayload[\"queryClient\"];\n\tpersistedQueries?: ErrorReportPayload[\"persistedQueries\"];\n\treact?: ErrorReportPayload[\"react\"];\n\tcontext?: Record<string, ErrorReportSafeValue>;\n}) {\n\tif (!isErrorReportingEnabled()) return undefined;\n\n\tconst reportId = uuidv4();\n\tconst sessionId = getSessionStorageId(SESSION_STORAGE_KEY);\n\tconst createdUtc = nowUtc();\n\tconst payload: ErrorReportPayload = {\n\t\treportId,\n\t\tsessionId,\n\t\tcreatedUtc,\n\t\tcategory: args.category,\n\t\tsource: args.source,\n\t\terror: args.error,\n\t\tenvironment: getErrorReportEnvironment(),\n\t\tclient: getErrorReportClientEnvironment(),\n\t\tlocation: getLocationSnapshot(),\n\t\tviewport: getViewportSnapshot(),\n\t\treact: args.react,\n\t\tquery: args.query,\n\t\tmutation: args.mutation,\n\t\tqueryClient: args.queryClient,\n\t\tpersistedQueries: args.persistedQueries,\n\t\tbreadcrumbs: getErrorReportBreadcrumbs(),\n\t\tcontext: args.context\n\t};\n\tconst draft: ErrorReportDraft = {\n\t\treportId,\n\t\tsessionId,\n\t\tcreatedUtc,\n\t\tcategory: args.category,\n\t\tstatus: \"pending\",\n\t\tpayload\n\t};\n\n\tsaveDrafts([...drafts, draft]);\n\treturn draft;\n}\n","import { hashString128 } from \"../crypto\";\nimport { stableStringify } from \"../utils\";\n\nimport type { ErrorReportDraft, ErrorReportErrorInfo, ErrorReportSafeValue } from \"./types\";\n\nexport const RUNTIME_ERROR_REPORT_DEDUPLICATION_WINDOW_MS = 60_000;\n\nconst MAX_RECENT_RUNTIME_ERROR_REPORTS = 100;\nconst recentRuntimeErrorReports = new Map<\n\tstring,\n\t{\n\t\tcapturedAt: number;\n\t\tcapture: Promise<ErrorReportDraft | undefined>;\n\t\texpirationTimer: ReturnType<typeof setTimeout>;\n\t}\n>();\n\ntype RuntimeErrorReportDeduplicationInput = {\n\tcategory: \"runtime\" | \"react\";\n\tsource: string;\n\terror: ErrorReportErrorInfo;\n\tpathname?: string;\n\tscope?: {\n\t\tcomponentStack?: string;\n\t\tdetail?: Record<string, ErrorReportSafeValue>;\n\t};\n};\n\nfunction normalizeStackTrace(stackTrace: string | undefined) {\n\tif (!stackTrace) return undefined;\n\n\treturn stackTrace\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n}\n\nfunction createRuntimeErrorReportFingerprint(input: RuntimeErrorReportDeduplicationInput) {\n\treturn hashString128(\n\t\tstableStringify({\n\t\t\tcategory: input.category,\n\t\t\tsource: input.source,\n\t\t\terror: {\n\t\t\t\t...input.error,\n\t\t\t\tstackTrace: normalizeStackTrace(input.error.stackTrace)\n\t\t\t},\n\t\t\tpathname: input.pathname,\n\t\t\tscope: input.scope\n\t\t})\n\t);\n}\n\n/**\n * Удаляет только ожидаемую запись и освобождает связанный таймер.\n * Проверка Promise защищает новую запись того же fingerprint от завершения\n * или отклонения старого capture после lifecycle-сброса.\n */\nfunction removeRuntimeErrorReport(fingerprint: string, expectedCapture?: Promise<ErrorReportDraft | undefined>) {\n\tconst recentReport = recentRuntimeErrorReports.get(fingerprint);\n\tif (!recentReport || (expectedCapture && recentReport.capture !== expectedCapture)) {\n\t\treturn;\n\t}\n\n\tclearTimeout(recentReport.expirationTimer);\n\trecentRuntimeErrorReports.delete(fingerprint);\n}\n\nfunction removeExpiredRuntimeErrorReports(now: number) {\n\tfor (const [fingerprint, recentReport] of recentRuntimeErrorReports) {\n\t\tif (now - recentReport.capturedAt >= RUNTIME_ERROR_REPORT_DEDUPLICATION_WINDOW_MS) {\n\t\t\tremoveRuntimeErrorReport(fingerprint, recentReport.capture);\n\t\t}\n\t}\n}\n\nfunction removeOldestRuntimeErrorReport() {\n\tconst oldestFingerprint = recentRuntimeErrorReports.keys().next().value;\n\tif (oldestFingerprint) {\n\t\tremoveRuntimeErrorReport(oldestFingerprint);\n\t}\n}\n\n/**\n * Объединяет одинаковые runtime capture-вызовы вокруг одной Promise.\n * Все потребители получают один draft, поэтому capture-only boundary не может\n * заблокировать последующую app-level доставку того же отчёта.\n */\nexport function coalesceRuntimeErrorReportCapture(\n\tinput: RuntimeErrorReportDeduplicationInput,\n\tcapture: () => Promise<ErrorReportDraft | undefined>\n) {\n\tconst now = Date.now();\n\tconst fingerprint = createRuntimeErrorReportFingerprint(input);\n\tconst recentReport = recentRuntimeErrorReports.get(fingerprint);\n\n\tif (recentReport && now - recentReport.capturedAt < RUNTIME_ERROR_REPORT_DEDUPLICATION_WINDOW_MS) {\n\t\treturn recentReport.capture;\n\t}\n\n\tremoveExpiredRuntimeErrorReports(now);\n\twhile (recentRuntimeErrorReports.size >= MAX_RECENT_RUNTIME_ERROR_REPORTS) {\n\t\tremoveOldestRuntimeErrorReport();\n\t}\n\n\tconst capturePromise = capture();\n\tconst expirationTimer = setTimeout(() => {\n\t\tremoveRuntimeErrorReport(fingerprint, capturePromise);\n\t}, RUNTIME_ERROR_REPORT_DEDUPLICATION_WINDOW_MS);\n\n\trecentRuntimeErrorReports.set(fingerprint, {\n\t\tcapturedAt: now,\n\t\tcapture: capturePromise,\n\t\texpirationTimer\n\t});\n\n\tvoid capturePromise.catch(() => {\n\t\tremoveRuntimeErrorReport(fingerprint, capturePromise);\n\t});\n\n\treturn capturePromise;\n}\n\n/**\n * Очищает модульное состояние при смене app-level runtime reporter.\n * Функция не входит в публичный package subpath и нужна lifecycle-коду и тестам.\n */\nexport function clearRuntimeErrorReportDeduplication() {\n\tfor (const recentReport of recentRuntimeErrorReports.values()) {\n\t\tclearTimeout(recentReport.expirationTimer);\n\t}\n\trecentRuntimeErrorReports.clear();\n}\n","export type ErrorReportTransportHtmlSummary = {\n\tlength: number;\n\ttitle?: string;\n\tformCount?: number;\n\tinputNames?: string[];\n\ttextPreview?: string;\n};\n\nexport type ErrorReportTransportErrorContext = {\n\tsource: \"fetch.parseResponse\" | \"fetch.buildHttpError\";\n\trequestUrl?: string;\n\tresponseUrl?: string;\n\tmethod?: string;\n\tbaseUrlType?: string;\n\tsapClient?: string | null;\n\tstatus?: number;\n\tstatusText?: string;\n\tcontentType?: string;\n\tredirected?: boolean;\n\tresponseType?: string;\n\thtml?: ErrorReportTransportHtmlSummary;\n};\n\nexport type ErrorReportTransportErrorReporter = (error: Error, context: ErrorReportTransportErrorContext) => void | Promise<void>;\n\nconst DEDUPLICATION_INTERVAL_MS = 60_000;\nconst MAX_RECENT_REPORT_KEYS = 100;\nconst TRANSPORT_REPORT_DELAY_MS = 250;\n\nlet transportErrorReporter: ErrorReportTransportErrorReporter | undefined;\nconst scheduledTransportErrors = new WeakSet<Error>();\nconst suppressedTransportErrors = new WeakSet<Error>();\nconst recentReportKeys = new Map<string, number>();\n\nfunction createReportKey(context: ErrorReportTransportErrorContext) {\n\treturn [context.source, context.method, context.status, context.requestUrl, context.responseUrl].filter(Boolean).join(\"|\");\n}\n\nfunction shouldSkipByRecentKey(context: ErrorReportTransportErrorContext) {\n\tconst now = Date.now();\n\tconst key = createReportKey(context);\n\n\tfor (const [recentKey, timestamp] of recentReportKeys) {\n\t\tif (now - timestamp > DEDUPLICATION_INTERVAL_MS || recentReportKeys.size > MAX_RECENT_REPORT_KEYS) {\n\t\t\trecentReportKeys.delete(recentKey);\n\t\t}\n\t}\n\n\tconst lastReportedAt = recentReportKeys.get(key);\n\tif (lastReportedAt && now - lastReportedAt < DEDUPLICATION_INTERVAL_MS) return true;\n\n\trecentReportKeys.set(key, now);\n\treturn false;\n}\n\n/**\n * Регистрирует приложение как получателя транспортных ошибок.\n * Сам OData transport не знает про QueryClient и UI, поэтому только публикует событие,\n * а app-слой решает, как именно создать и отправить отчет.\n */\nexport function setErrorReportTransportErrorReporter(reporter: ErrorReportTransportErrorReporter | undefined) {\n\ttransportErrorReporter = reporter;\n\trecentReportKeys.clear();\n}\n\nexport function isTransportErrorReportScheduled(error: unknown) {\n\treturn error instanceof Error && scheduledTransportErrors.has(error);\n}\n\nexport function suppressTransportErrorReport(error: unknown) {\n\tif (error instanceof Error) {\n\t\tsuppressedTransportErrors.add(error);\n\t}\n}\n\nexport function reportTransportError(error: Error, context: ErrorReportTransportErrorContext) {\n\tscheduledTransportErrors.add(error);\n\n\t// Даем React Query шанс перехватить эту же ошибку и создать query/mutation-отчет\n\t// с более полезным снимком кеша. Если такого перехвата нет, transport-отчет уйдет сам.\n\tsetTimeout(() => {\n\t\tif (suppressedTransportErrors.has(error)) return;\n\t\tif (!transportErrorReporter) return;\n\t\tif (shouldSkipByRecentKey(context)) return;\n\n\t\ttry {\n\t\t\tconst result = transportErrorReporter(error, context);\n\t\t\tvoid Promise.resolve(result).catch(() => undefined);\n\t\t} catch {\n\t\t\t// Ошибка диагностического hook не должна менять поведение исходного fetch.\n\t\t}\n\t}, TRANSPORT_REPORT_DELAY_MS);\n}\n","import { type Mutation, type Query, type QueryClient } from \"@tanstack/react-query\";\n\nimport { addErrorReportBreadcrumb } from \"./breadcrumbs\";\nimport {\n\tcollectMutationDiagnostics,\n\tcollectPersistedQueryDiagnostics,\n\tcollectQueryClientDiagnostics,\n\tcollectQueryDiagnostics\n} from \"./diagnostics\";\nimport { captureErrorReportDraft } from \"./drafts\";\nimport { createErrorInfo } from \"./errorInfo\";\nimport { coalesceRuntimeErrorReportCapture } from \"./runtimeDeduplication\";\nimport { sanitizeDetail } from \"./safeValue\";\nimport { suppressTransportErrorReport } from \"./transport\";\n\nimport type { ErrorReportMutationContext, ErrorReportQueryContext, ErrorReportRuntimeContext } from \"./types\";\n\nexport async function captureQueryErrorReport(\n\terror: unknown,\n\tquery: Query<unknown, unknown, unknown>,\n\tqueryClient: QueryClient,\n\tcontext?: ErrorReportQueryContext\n) {\n\tsuppressTransportErrorReport(error);\n\tconst source = context?.source ?? \"query\";\n\n\taddErrorReportBreadcrumb({\n\t\ttype: \"query-error\",\n\t\tdetail: sanitizeDetail({\n\t\t\tqueryHash: query.queryHash,\n\t\t\tqueryKey: context?.queryKey ?? query.queryKey,\n\t\t\t...context?.detail\n\t\t})\n\t});\n\n\tconst [persistedQueries, queryClientDiagnostics] = await Promise.all([\n\t\tcollectPersistedQueryDiagnostics(),\n\t\tPromise.resolve(collectQueryClientDiagnostics(queryClient))\n\t]);\n\n\treturn captureErrorReportDraft({\n\t\tcategory: \"query\",\n\t\tsource,\n\t\terror: context?.errorInfo ?? createErrorInfo(error),\n\t\tquery: collectQueryDiagnostics(query),\n\t\tqueryClient: queryClientDiagnostics,\n\t\tpersistedQueries,\n\t\tcontext: sanitizeDetail(context?.detail)\n\t});\n}\n\nexport async function captureMutationErrorReport(\n\terror: unknown,\n\tmutation: Mutation<unknown, unknown, unknown, unknown>,\n\tqueryClient: QueryClient,\n\tcontext: ErrorReportMutationContext = {}\n) {\n\tsuppressTransportErrorReport(error);\n\tconst source = context.source ?? \"mutation\";\n\n\taddErrorReportBreadcrumb({\n\t\ttype: \"mutation-error\",\n\t\tdetail: sanitizeDetail({ mutationKey: mutation.options.mutationKey, ...context.detail })\n\t});\n\n\tconst [persistedQueries, queryClientDiagnostics] = await Promise.all([\n\t\tcollectPersistedQueryDiagnostics(),\n\t\tPromise.resolve(collectQueryClientDiagnostics(queryClient))\n\t]);\n\n\treturn captureErrorReportDraft({\n\t\tcategory: \"mutation\",\n\t\tsource,\n\t\terror: context.errorInfo ?? createErrorInfo(error),\n\t\tmutation: collectMutationDiagnostics(mutation),\n\t\tqueryClient: queryClientDiagnostics,\n\t\tpersistedQueries\n\t});\n}\n\nexport function captureRuntimeErrorReport(error: unknown, context: ErrorReportRuntimeContext = {}, queryClient?: QueryClient) {\n\tconst category = context.category ?? \"runtime\";\n\tconst source = context.source ?? context.category ?? \"runtime\";\n\tconst errorInfo = context.errorInfo ?? createErrorInfo(error);\n\tconst detail = sanitizeDetail(context.detail);\n\n\tconst captureDraft = async () => {\n\t\taddErrorReportBreadcrumb({\n\t\t\ttype: \"runtime-error\",\n\t\t\tdetail: sanitizeDetail({ category, source, ...context.detail })\n\t\t});\n\n\t\tconst [persistedQueries, queryClientDiagnostics] = queryClient\n\t\t\t? await Promise.all([collectPersistedQueryDiagnostics(), Promise.resolve(collectQueryClientDiagnostics(queryClient))])\n\t\t\t: [undefined, undefined];\n\n\t\treturn captureErrorReportDraft({\n\t\t\tcategory,\n\t\t\tsource,\n\t\t\terror: errorInfo,\n\t\t\treact: context.componentStack ? { componentStack: context.componentStack } : undefined,\n\t\t\tqueryClient: queryClientDiagnostics,\n\t\t\tpersistedQueries,\n\t\t\tcontext: detail\n\t\t});\n\t};\n\n\t/**\n\t * Error report draft создаётся только в браузере. Прямой вызов capture\n\t * сохраняет прежнее SSR-поведение без клиентского TTL-таймера, который\n\t * иначе удерживал бы Node-процесс после no-op результата.\n\t */\n\tif (typeof window === \"undefined\") {\n\t\treturn captureDraft();\n\t}\n\n\treturn coalesceRuntimeErrorReportCapture(\n\t\t{\n\t\t\tcategory,\n\t\t\tsource,\n\t\t\terror: errorInfo,\n\t\t\tpathname: window.location.pathname,\n\t\t\tscope: {\n\t\t\t\tcomponentStack: context.componentStack,\n\t\t\t\tdetail\n\t\t\t}\n\t\t},\n\t\tcaptureDraft\n\t);\n}\n","import { clearRuntimeErrorReportDeduplication } from \"./runtimeDeduplication\";\n\nimport type { ErrorReportRuntimeContext } from \"./types\";\n\nexport type ErrorReportRuntimeErrorReporter = (error: unknown, context: ErrorReportRuntimeContext) => void | Promise<void>;\n\nlet runtimeErrorReporter: ErrorReportRuntimeErrorReporter | undefined;\n\n/**\n * Регистрирует app-слой как получателя ручных runtime-ошибок.\n * Shared-код может вызвать reportRuntimeError без знания о QueryClient, UI и режиме доставки.\n */\nexport function setErrorReportRuntimeErrorReporter(reporter: ErrorReportRuntimeErrorReporter | undefined) {\n\truntimeErrorReporter = reporter;\n\tclearRuntimeErrorReportDeduplication();\n}\n\n/**\n * Ручная отправка runtime-ошибки из try/catch или другого кода вне React-компонента.\n * Функция не бросает исключений: диагностический отчет не должен ломать основной сценарий.\n *\n * @example\n *\n * try {\n * // ...\n * } catch (error) {\n * reportRuntimeError(error, {\n * detail: {\n * source: \"s3-download\",\n * action: \"download-from-s3\"\n * }\n * });\n * }\n */\nexport async function reportRuntimeError(error: unknown, context: ErrorReportRuntimeContext = {}) {\n\tif (!runtimeErrorReporter) return;\n\n\ttry {\n\t\tawait runtimeErrorReporter(error, context);\n\t} catch {\n\t\t// Ошибка диагностического hook не должна менять поведение пользовательского кода.\n\t}\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/error-report/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAEzD,MAAM,MAAM,+BAA+B,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAI3H;;;GAGG;AACH,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,+BAA+B,GAAG,SAAS,QAEvG;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,yBAA8B,iBAQ/F"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/error-report/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAEzD,MAAM,MAAM,+BAA+B,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAI3H;;;GAGG;AACH,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,+BAA+B,GAAG,SAAS,QAGvG;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,yBAA8B,iBAQ/F"}
@@ -0,0 +1,25 @@
1
+ import type { ErrorReportDraft, ErrorReportErrorInfo, ErrorReportSafeValue } from "./types";
2
+ export declare const RUNTIME_ERROR_REPORT_DEDUPLICATION_WINDOW_MS = 60000;
3
+ type RuntimeErrorReportDeduplicationInput = {
4
+ category: "runtime" | "react";
5
+ source: string;
6
+ error: ErrorReportErrorInfo;
7
+ pathname?: string;
8
+ scope?: {
9
+ componentStack?: string;
10
+ detail?: Record<string, ErrorReportSafeValue>;
11
+ };
12
+ };
13
+ /**
14
+ * Объединяет одинаковые runtime capture-вызовы вокруг одной Promise.
15
+ * Все потребители получают один draft, поэтому capture-only boundary не может
16
+ * заблокировать последующую app-level доставку того же отчёта.
17
+ */
18
+ export declare function coalesceRuntimeErrorReportCapture(input: RuntimeErrorReportDeduplicationInput, capture: () => Promise<ErrorReportDraft | undefined>): Promise<ErrorReportDraft | undefined>;
19
+ /**
20
+ * Очищает модульное состояние при смене app-level runtime reporter.
21
+ * Функция не входит в публичный package subpath и нужна lifecycle-коду и тестам.
22
+ */
23
+ export declare function clearRuntimeErrorReportDeduplication(): void;
24
+ export {};
25
+ //# sourceMappingURL=runtimeDeduplication.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeDeduplication.d.ts","sourceRoot":"","sources":["../../src/error-report/runtimeDeduplication.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE5F,eAAO,MAAM,4CAA4C,QAAS,CAAC;AAYnE,KAAK,oCAAoC,GAAG;IAC3C,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,oBAAoB,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE;QACP,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;KAC9C,CAAC;CACF,CAAC;AAwDF;;;;GAIG;AACH,wBAAgB,iCAAiC,CAChD,KAAK,EAAE,oCAAoC,EAC3C,OAAO,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,yCA+BpD;AAED;;;GAGG;AACH,wBAAgB,oCAAoC,SAKnD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuzaki13/react-foundation-lib",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Reusable React and TypeScript foundation utilities, hooks, and primitives.",
5
5
  "license": "MIT",
6
6
  "author": "Ryuzaki13",
@@ -27,6 +27,8 @@
27
27
  "sideEffects": false,
28
28
  "files": [
29
29
  "dist",
30
+ "src/**/*.mdx",
31
+ "src/**/README.md",
30
32
  "README.md",
31
33
  "LICENSE"
32
34
  ],
@@ -0,0 +1,110 @@
1
+ # `shared/lib/binary`
2
+
3
+ Модуль содержит небольшие утилиты для работы с бинарным содержимым, которое приходит в приложение строкой. Основной сценарий — получить из SAP/OData/REST-ответа base64-документ, определить его MIME-тип и передать дальше как `Blob` в просмотрщик, ссылку скачивания или API браузера.
4
+
5
+ Модуль находится в `shared`, поэтому не содержит бизнес-знаний о типах документов конкретного приложения. Например, `detectMimeType` не использует коды вроде `documentType`: MIME определяется только по имени файла, data URL или сигнатуре самих данных.
6
+
7
+ ## Публичный API
8
+
9
+ ```ts
10
+ import { binaryToBlob, detectMimeType, useBinaryFile } from "@/shared/lib";
11
+ ```
12
+
13
+ Также модуль реэкспортируется через общий `@/shared/lib`.
14
+
15
+ ### `binaryToBlob`
16
+
17
+ ```ts
18
+ binaryToBlob(binaryData: string, mimeType?: string, isBase64?: boolean): Blob
19
+ ```
20
+
21
+ Преобразует строковое бинарное содержимое в `Blob`.
22
+
23
+ Параметры:
24
+
25
+ - `binaryData` — исходные данные. По умолчанию ожидается base64-строка.
26
+ - `mimeType` — MIME-тип создаваемого Blob. Если передана пустая строка, используется `application/octet-stream`.
27
+ - `isBase64` — признак формата входных данных. По умолчанию `true`. Если передать `false`, строка будет обработана как raw binary string.
28
+
29
+ Поддерживаются:
30
+
31
+ - обычный base64;
32
+ - base64 с пробелами и переносами строк;
33
+ - URL-safe base64 с символами `-` и `_`;
34
+ - data URL вида `data:application/pdf;base64,...`.
35
+
36
+ Функция возвращает именно `Blob`. Object URL нужно создавать отдельно через `URL.createObjectURL(blob)` и освобождать через `URL.revokeObjectURL(url)` на стороне вызывающего кода.
37
+
38
+ Пример:
39
+
40
+ ```ts
41
+ const mime = detectMimeType(fileName, fileContent);
42
+ const blob = binaryToBlob(fileContent, mime);
43
+ const url = URL.createObjectURL(blob);
44
+ ```
45
+
46
+ ### `detectMimeType`
47
+
48
+ ```ts
49
+ detectMimeType(fileName?: string, binaryData?: string): string
50
+ ```
51
+
52
+ Определяет MIME-тип без привязки к бизнес-типу документа.
53
+
54
+ Порядок определения:
55
+
56
+ 1. По расширению `fileName`.
57
+ 2. По MIME-типу из data URL.
58
+ 3. По сигнатуре первых байтов base64-данных.
59
+ 4. Fallback `application/pdf`.
60
+
61
+ Поддерживаемые расширения включают распространённые документы и изображения: `pdf`, `jpg`, `jpeg`, `png`, `gif`, `webp`, `svg`, `bmp`, `tif`, `tiff`, `xml`, `txt`, `csv`, `json`, `doc`, `docx`, `xls`, `xlsx`, `ppt`, `pptx`, `zip`.
62
+
63
+ По сигнатуре определяются:
64
+
65
+ - PDF;
66
+ - JPEG;
67
+ - PNG;
68
+ - GIF;
69
+ - WEBP;
70
+ - ZIP;
71
+ - XML-подобные текстовые данные.
72
+
73
+ Fallback `application/pdf` сохранён намеренно: большинство документов в текущем контуре отображаются как PDF, а неизвестный тип чаще должен открыться в документном просмотрщике, чем стать произвольным `application/octet-stream`.
74
+
75
+ ### `useBinaryFile`
76
+
77
+ ```ts
78
+ useBinaryFile(bin: string | undefined): {
79
+ data: { blob: Blob; mime: string } | null;
80
+ error: string | null;
81
+ }
82
+ ```
83
+
84
+ React-хук для компонентов, которым нужно получить готовую пару `Blob + MIME` из base64-строки.
85
+
86
+ Поведение:
87
+
88
+ - при наличии `bin` определяет MIME через `detectMimeType`;
89
+ - создаёт `Blob` через `binaryToBlob`;
90
+ - пишет ошибку в `error`, если данные некорректны;
91
+ - очищает `data` и `error`, когда `bin` становится пустым.
92
+
93
+ Хук не создаёт object URL и не управляет его жизненным циклом. Это должно оставаться в компоненте, потому что именно компонент знает, когда URL больше не нужен.
94
+
95
+ ## Ограничения
96
+
97
+ - Модуль рассчитан на браузерное окружение и использует `Blob` и `atob`.
98
+ - MIME по сигнатуре определяется только для ограниченного набора популярных форматов.
99
+ - Функции не валидируют содержимое файла полностью. Проверяется только формат base64 и несколько первых байтов для MIME-detection.
100
+ - Модуль не должен импортировать сущности, фичи, виджеты или приложения. Если нужен бизнес-маппинг кодов документов, его нужно держать в соответствующем доменном слое и передавать результат в этот модуль уже как MIME-строку.
101
+
102
+ ## Проверки
103
+
104
+ Поведение покрыто тестами в `binaryToBlob.test.ts`.
105
+
106
+ Релевантный узкий запуск:
107
+
108
+ ```bash
109
+ npm run test -- run src/shared/lib/binary/binaryToBlob.test.ts
110
+ ```