import{n as e}from"./rolldown-runtime-C0FnF6B9.js";import{n as t}from"./iframe-DaN-XFub.js";import{i as n,r}from"./react-qN2cStNd.js";import{l as i,m as a}from"./blocks-BiaKjftm.js";function o(e){let t={code:`code`,h1:`h1`,h2:`h2`,h3:`h3`,li:`li`,p:`p`,pre:`pre`,ul:`ul`,...n(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(i,{title:`Foundation Lib/Файлы и Excel`}),`
`,(0,c.jsx)(t.h1,{id:`файлы-бинарные-данные-и-excel`,children:`Файлы, бинарные данные и Excel`}),`
`,(0,c.jsxs)(t.p,{children:[`Эта страница описывает browser-only API для чтения пользовательских файлов, преобразования base64 и формирования `,(0,c.jsx)(t.code,{children:`.xlsx`}),`. Подготовка бизнес-полей, проверка прав доступа и отправка файла на сервер остаются ответственностью host-приложения.`]}),`
`,(0,c.jsxs)(t.h2,{id:`file-чтение-файлов-и-http-заголовки`,children:[(0,c.jsx)(t.code,{children:`file`}),`: чтение файлов и HTTP-заголовки`]}),`
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import {
	createContentDispositionHeader,
	readFile,
	readImageFile,
	resolveMimeTypeByExtension,
	type ReadFileError,
	type ReadImageError
} from "@ryuzaki13/react-foundation-lib/file";
`})}),`
`,(0,c.jsx)(t.h3,{id:`когда-использовать`,children:`Когда использовать`}),`
`,(0,c.jsxs)(t.ul,{children:[`
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`readFile`}),` — получить `,(0,c.jsx)(t.code,{children:`dataUrl`}),` для preview или `,(0,c.jsx)(t.code,{children:`ArrayBuffer`}),` для дальнейшей обработки;`]}),`
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`readImageFile`}),` — то же для изображений с проверкой image MIME и размером изображения;`]}),`
`,(0,c.jsx)(t.li,{children:`helpers Content-Disposition — сформировать безопасный заголовок в browser-compatible/edge runtime коде;`}),`
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`resolveMimeTypeByExtension`}),` — определить MIME по расширению, если содержимого для sniffing нет.`]}),`
`]}),`
`,(0,c.jsx)(t.h3,{id:`readfile`,children:(0,c.jsx)(t.code,{children:`readFile`})}),`
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`readFile(file, options?): Promise<ReadFileResult>
`})}),`
`,(0,c.jsxs)(t.p,{children:[`| Опция                             | Назначение                                        |
| --------------------------------- | ------------------------------------------------- |
| `,(0,c.jsx)(t.code,{children:`mode?: "data-url"`}),`               | Режим по умолчанию. Результат содержит `,(0,c.jsx)(t.code,{children:`dataUrl`}),`. |
| `,(0,c.jsx)(t.code,{children:`mode: "array-buffer"`}),`            | Результат содержит `,(0,c.jsx)(t.code,{children:`buffer`}),`.                      |
| `,(0,c.jsx)(t.code,{children:`allowedMime?: readonly string[]`}),` | Точный список разрешённых MIME.                   |
| `,(0,c.jsx)(t.code,{children:`maxBytes?: number`}),`               | Максимальный размер файла в байтах.               |
| `,(0,c.jsx)(t.code,{children:`signal?: AbortSignal`}),`            | Отменяет чтение через `,(0,c.jsx)(t.code,{children:`FileReader.abort()`}),`.       |`]}),`
`,(0,c.jsxs)(t.p,{children:[`Возвращаемый discriminated union всегда содержит `,(0,c.jsx)(t.code,{children:`file`}),` и `,(0,c.jsx)(t.code,{children:`meta`}),` (`,(0,c.jsx)(t.code,{children:`name`}),`, `,(0,c.jsx)(t.code,{children:`mime`}),`, `,(0,c.jsx)(t.code,{children:`size`}),`, `,(0,c.jsx)(t.code,{children:`lastModified`}),`). После проверки `,(0,c.jsx)(t.code,{children:`result.mode`}),` TypeScript раскрывает либо `,(0,c.jsx)(t.code,{children:`dataUrl`}),`, либо `,(0,c.jsx)(t.code,{children:`buffer`}),`.`]}),`
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const result = await readFile(file, {
	mode: "data-url",
	allowedMime: ["application/pdf"],
	maxBytes: 10 * 1024 * 1024,
	signal
});

if (result.mode === "data-url") {
	previewUrl = result.dataUrl;
}
`})}),`
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`ReadFileError`}),` имеет предсказуемый `,(0,c.jsx)(t.code,{children:`code`}),`: `,(0,c.jsx)(t.code,{children:`NO_FILE`}),`, `,(0,c.jsx)(t.code,{children:`MIME_NOT_ALLOWED`}),`, `,(0,c.jsx)(t.code,{children:`FILE_TOO_LARGE`}),`, `,(0,c.jsx)(t.code,{children:`READ_ABORTED`}),` или `,(0,c.jsx)(t.code,{children:`READ_FAILED`}),`. Обрабатывайте его как ожидаемую ошибку выбора файла, а остальные ошибки не скрывайте.`]}),`
`,(0,c.jsx)(t.h3,{id:`readimagefile`,children:(0,c.jsx)(t.code,{children:`readImageFile`})}),`
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`readImageFile(image, options?): Promise<ReadImageResult>
`})}),`
`,(0,c.jsxs)(t.p,{children:[`Поддерживаемые `,(0,c.jsx)(t.code,{children:`ImageMime`}),`: `,(0,c.jsx)(t.code,{children:`image/png`}),`, `,(0,c.jsx)(t.code,{children:`image/jpeg`}),`, `,(0,c.jsx)(t.code,{children:`image/webp`}),`, `,(0,c.jsx)(t.code,{children:`image/gif`}),`, `,(0,c.jsx)(t.code,{children:`image/svg+xml`}),`, `,(0,c.jsx)(t.code,{children:`image/avif`}),`. В режиме `,(0,c.jsx)(t.code,{children:`data-url`}),` результат дополнительно включает `,(0,c.jsx)(t.code,{children:`dimensions`}),` (`,(0,c.jsx)(t.code,{children:`width`}),`, `,(0,c.jsx)(t.code,{children:`height`}),`). В режиме `,(0,c.jsx)(t.code,{children:`array-buffer`}),` декодирование изображения не выполняется.`]}),`
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`ReadImageError`}),` расширяет коды `,(0,c.jsx)(t.code,{children:`ReadFileError`}),` значениями `,(0,c.jsx)(t.code,{children:`NOT_AN_IMAGE`}),` и `,(0,c.jsx)(t.code,{children:`IMAGE_DECODE_FAILED`}),`.`]}),`
`,(0,c.jsxs)(t.h3,{id:`дополнительный-api-file`,children:[`Дополнительный API `,(0,c.jsx)(t.code,{children:`file`})]}),`
`,(0,c.jsxs)(t.p,{children:[`| API                                             | Контракт                                                                                                              |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `,(0,c.jsx)(t.code,{children:`buildMeta(file)`}),`                               | Возвращает serializable метаданные `,(0,c.jsx)(t.code,{children:`FileMeta`}),`.                                                                        |
| `,(0,c.jsx)(t.code,{children:`attachAbort(signal, reader)`}),`                   | Привязывает отмену к `,(0,c.jsx)(t.code,{children:`FileReader`}),`; возвращённую функцию cleanup вызывают только custom consumers низкоуровневого API. |
| `,(0,c.jsx)(t.code,{children:`assertNotAborted(signal?)`}),`                     | Бросает `,(0,c.jsx)(t.code,{children:`ReadFileError("READ_ABORTED")`}),`, если сигнал уже отменён.                                                     |
| `,(0,c.jsx)(t.code,{children:`assertValidAllowedMime`}),`, `,(0,c.jsx)(t.code,{children:`assertValidAccept`}),`   | Проверяют конфигурацию accept/MIME до чтения.                                                                         |
| `,(0,c.jsx)(t.code,{children:`sanitizeFileName(value, fallback?)`}),`            | Удаляет unsafe символы из имени.                                                                                      |
| `,(0,c.jsx)(t.code,{children:`encodeFilenameRFC5987(value)`}),`                  | Кодирует Unicode filename для HTTP header.                                                                            |
| `,(0,c.jsx)(t.code,{children:`createAsciiFilenameFallback(value, fallback?)`}),` | Создаёт ASCII fallback для старых consumers заголовка.                                                                |
| `,(0,c.jsx)(t.code,{children:`createContentDispositionHeader(options)`}),`       | Создаёт `,(0,c.jsx)(t.code,{children:`inline`}),`/`,(0,c.jsx)(t.code,{children:`attachment`}),` Content-Disposition с `,(0,c.jsx)(t.code,{children:`filename`}),` и `,(0,c.jsx)(t.code,{children:`filename*`}),`.                                         |`]}),`
`,(0,c.jsxs)(t.h2,{id:`binary-base64--blob`,children:[(0,c.jsx)(t.code,{children:`binary`}),`: base64 → `,(0,c.jsx)(t.code,{children:`Blob`})]}),`
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import { binaryToBlob, detectMimeType, useBinaryFile } from "@ryuzaki13/react-foundation-lib/binary";
`})}),`
`,(0,c.jsxs)(t.p,{children:[`| API                                              | Вход и результат                                                                                                                  |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| `,(0,c.jsx)(t.code,{children:`binaryToBlob(binaryData, mimeType?, isBase64?)`}),` | Создаёт `,(0,c.jsx)(t.code,{children:`Blob`}),` из base64/data URL (по умолчанию) или raw binary string (`,(0,c.jsx)(t.code,{children:`false`}),`). Пустые или некорректные данные бросают `,(0,c.jsx)(t.code,{children:`Error`}),`. |
| `,(0,c.jsx)(t.code,{children:`detectMimeType(fileName?, binaryData?)`}),`         | Ищет MIME по расширению, data URL, затем сигнатуре base64. Неопределённый документ трактует как `,(0,c.jsx)(t.code,{children:`application/pdf`}),`.                |
| `,(0,c.jsx)(t.code,{children:`useBinaryFile(bin)`}),`                             | React hook, возвращает `,(0,c.jsx)(t.code,{children:`{ data: { blob, mime } \\| null, error: string \\| null }`}),`.                                                 |`]}),`
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`const mime = detectMimeType("report.pdf", base64);
const blob = binaryToBlob(base64, mime);
const objectUrl = URL.createObjectURL(blob);

// Владелец UI обязан освободить URL при смене файла/размонтировании.
URL.revokeObjectURL(objectUrl);
`})}),`
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`useBinaryFile`}),` не создаёт object URL и не освобождает его: это lifecycle конкретного preview-компонента. Hook использует `,(0,c.jsx)(t.code,{children:`Blob`}),` и `,(0,c.jsx)(t.code,{children:`atob`}),`, поэтому не вызывайте его в SSR или worker без совместимого polyfill.`]}),`
`,(0,c.jsxs)(t.h2,{id:`excel-скачивание-xlsx`,children:[(0,c.jsx)(t.code,{children:`excel`}),`: скачивание `,(0,c.jsx)(t.code,{children:`.xlsx`})]}),`
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`import {
	buildExcelAutoFilterRef,
	buildExcelSheetData,
	createTableExcelColumn,
	downloadExcelFile,
	downloadExcelWorkbook,
	downloadResolvedExcelTable,
	downloadResolvedExcelWorkbook,
	resolveExcelCellStyleFromState,
	resolveExcelSheetNames
} from "@ryuzaki13/react-foundation-lib/excel";
`})}),`
`,(0,c.jsx)(t.h3,{id:`базовый-экспорт`,children:`Базовый экспорт`}),`
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`downloadExcelFile({ fileName, columns, rows, autoFilter?, getCellStyle? })`}),` создаёт один лист; `,(0,c.jsx)(t.code,{children:`downloadExcelWorkbook({ fileName, sheets })`}),` — книгу из нескольких листов. Это asynchronous browser API, который инициирует скачивание через `,(0,c.jsx)(t.code,{children:`write-excel-file`}),`.`]}),`
`,(0,c.jsx)(t.pre,{children:(0,c.jsx)(t.code,{className:`language-ts`,children:`await downloadExcelFile({
	fileName: "users.xlsx",
	columns: [
		{ id: "name", header: "Имя", type: String, width: 28 },
		{ id: "createdAt", header: "Дата", type: Date, format: "dd.mm.yyyy" }
	],
	rows: [{ name: "Анна", createdAt: new Date("2026-07-30") }],
	autoFilter: true
});
`})}),`
`,(0,c.jsxs)(t.p,{children:[`| Public type/API                             | Назначение                                                                                                           |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `,(0,c.jsx)(t.code,{children:`ExcelColumn`}),`, `,(0,c.jsx)(t.code,{children:`ExcelRow`}),`, `,(0,c.jsx)(t.code,{children:`ExcelCellStyle`}),` | Контракты столбца, строки и стиля ячейки. `,(0,c.jsx)(t.code,{children:`type`}),` передаёт в файл истинные `,(0,c.jsx)(t.code,{children:`Number`}),`/`,(0,c.jsx)(t.code,{children:`Date`}),`/`,(0,c.jsx)(t.code,{children:`Boolean`}),`, а не UI-строку. |
| `,(0,c.jsx)(t.code,{children:`buildExcelSheetData(args)`}),`                 | Строит данные листа без запуска download; подходит для тестируемой подготовки.                                       |
| `,(0,c.jsx)(t.code,{children:`buildExcelAutoFilterRef(columns, rows)`}),`    | Даёт Excel-диапазон или `,(0,c.jsx)(t.code,{children:`undefined`}),` для пустой сетки.                                                                |
| `,(0,c.jsx)(t.code,{children:`resolveExcelSheetNames(names)`}),`             | Нормализует ограничения Excel (31 символ, forbidden chars) и делает имена уникальными.                               |
| `,(0,c.jsx)(t.code,{children:`resolveExcelCellStyleFromState(state)`}),`     | Преобразует `,(0,c.jsx)(t.code,{children:`State`}),` в базовый цвет текста Excel.                                                                     |`]}),`
`,(0,c.jsx)(t.h3,{id:`экспорт-подготовленной-таблицы`,children:`Экспорт подготовленной таблицы`}),`
`,(0,c.jsxs)(t.p,{children:[(0,c.jsx)(t.code,{children:`TableExcelColumnDescriptor`}),` связывает generic Excel column с OData type/role и необязательным `,(0,c.jsx)(t.code,{children:`formattersPipeline`}),`. Используйте `,(0,c.jsx)(t.code,{children:`createTableExcelColumn(descriptor)`}),` для получения типизированного столбца, затем передавайте значения строк без предварительного UI-форматирования.`]}),`
`,(0,c.jsxs)(t.p,{children:[`| API                                                            | Назначение                                                                        |
| -------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `,(0,c.jsx)(t.code,{children:`resolveTableExcelColumnType(descriptor)`}),`                      | Выбирает `,(0,c.jsx)(t.code,{children:`String`}),`/`,(0,c.jsx)(t.code,{children:`Number`}),`/`,(0,c.jsx)(t.code,{children:`Date`}),`/`,(0,c.jsx)(t.code,{children:`Boolean`}),` по OData metadata и роли.             |
| `,(0,c.jsx)(t.code,{children:`createTableExcelColumn(descriptor)`}),`                           | Создаёт `,(0,c.jsx)(t.code,{children:`ExcelColumn`}),` с Excel-маской числа/даты из published formatter contracts. |
| `,(0,c.jsx)(t.code,{children:`downloadResolvedExcelTable({ fileName, table, autoFilter? })`}),` | Скачивает одну уже подготовленную таблицу.                                        |
| `,(0,c.jsx)(t.code,{children:`downloadResolvedExcelWorkbook({ fileName, sheets })`}),`          | Скачивает несколько `,(0,c.jsx)(t.code,{children:`ResolvedExcelTable`}),` как листы книги.                         |`]}),`
`,(0,c.jsxs)(t.p,{children:[`Не передавайте в `,(0,c.jsx)(t.code,{children:`rows`}),` отформатированные через `,(0,c.jsx)(t.code,{children:`Intl`}),` строки для числовых и date-столбцов: Excel потеряет сортировку, формулы и формат. Строки создавайте только для действительно текстовых значений.`]}),`
`,(0,c.jsx)(t.h2,{id:`ограничения`,children:`Ограничения`}),`
`,(0,c.jsxs)(t.ul,{children:[`
`,(0,c.jsx)(t.li,{children:`Все три entrypoint ориентированы на DOM/browser; не вызывайте их в server-only коде.`}),`
`,(0,c.jsx)(t.li,{children:`MIME по имени/сигнатуре не является проверкой безопасности содержимого. Сервер обязан валидировать загружаемые файлы повторно.`}),`
`,(0,c.jsxs)(t.li,{children:[(0,c.jsx)(t.code,{children:`download*`}),` имеет пользовательский side effect. Вызывайте его из event handler, а не из render/effect при монтировании.`]}),`
`]})]})}function s(e={}){let{wrapper:t}={...n(),...e.components};return t?(0,c.jsx)(t,{...e,children:(0,c.jsx)(o,{...e})}):o(e)}var c;function l(){return(l=e((()=>{c=t(),r(),a()})))()}l();export{s as default};