F6 — PDF-контент в поиске (сейчас не индексируется)
Проблема
OnlyOffice ES индексирует содержимое только office-форматов (docx/xlsx/pptx).
PDF попадает в индекс по имени (см. F3 #37, docs/elasticsearch.md). Поэтому oo search --content "S1019" не находит номера внутри PDF-счетов.
Research (обязательно, до кода)
Разобрать в исходниках CommunityServer (web/.../Products/Files/Core/Search/FilesWrapper.cs, module/ASC.ElasticSearch/Core/WrapperWithDoc.cs, FileUtility.CanIndex) — почему
PDF исключён и какие есть пути:
Включить PDF в CanIndex + reindex (патч сервера — рискованно, ломается
при обновлении OO).
Отдельный ingest-пайплайн/attachment-процессор для PDF в ES (server-side).
Свой индекс в ES, наполняемый нашим конвейером (internal/docpipe/OCR),
не трогая индекс OnlyOffice — отдельный индекс/алиас, esSearcher умеет и его.
Не индексировать, а искать по PDF локально (docpipe + pdftotext) — fallback.
Оценить риски/сложность/детерминированность. Зафиксировать решение в docs/elasticsearch.md.
Реализация (по выбранному пути, детерминированно)
Если путь 3 (наш индекс): отдельный ES-индекс (напр. oo_docs_text),
наполнение из docpipe, Searcher умеет его; oo search --content --backend own.
Если путь 4: oo search --content --files с локальным извлечением текста.
Никаких правок сервера OO без явного решения PO/владельца.
Критерии
Решение зафиксировано (с рисками) в docs/elasticsearch.md.
Если реализуемо безопасно — oo search --content "<текст из PDF>" находит
PDF; иначе — блокер с точной причиной.
go build/vet/test зелёные; интеграционные — skip без сервиса.
Epic: #34
## F6 — PDF-контент в поиске (сейчас не индексируется)
### Проблема
OnlyOffice ES индексирует содержимое только office-форматов (docx/xlsx/pptx).
PDF попадает в индекс по **имени** (см. F3 #37, `docs/elasticsearch.md`). Поэтому
`oo search --content "S1019"` не находит номера внутри PDF-счетов.
### Research (обязательно, до кода)
Разобрать в исходниках CommunityServer (`web/.../Products/Files/Core/Search/FilesWrapper.cs`,
`module/ASC.ElasticSearch/Core/WrapperWithDoc.cs`, `FileUtility.CanIndex`) — почему
PDF исключён и какие есть пути:
1. Включить PDF в `CanIndex` + reindex (патч сервера — **рискованно**, ломается
при обновлении OO).
2. Отдельный ingest-пайплайн/attachment-процессор для PDF в ES (server-side).
3. **Свой индекс** в ES, наполняемый нашим конвейером (`internal/docpipe`/OCR),
не трогая индекс OnlyOffice — отдельный индекс/алиас, `esSearcher` умеет и его.
4. Не индексировать, а искать по PDF локально (`docpipe` + `pdftotext`) — fallback.
Оценить риски/сложность/детерминированность. Зафиксировать решение в
`docs/elasticsearch.md`.
### Реализация (по выбранному пути, детерминированно)
- Если путь 3 (наш индекс): отдельный ES-индекс (напр. `oo_docs_text`),
наполнение из docpipe, `Searcher` умеет его; `oo search --content --backend own`.
- Если путь 4: `oo search --content --files` с локальным извлечением текста.
- Никаких правок сервера OO без явного решения PO/владельца.
### Критерии
- [ ] Решение зафиксировано (с рисками) в `docs/elasticsearch.md`.
- [ ] Если реализуемо безопасно — `oo search --content "<текст из PDF>"` находит
PDF; иначе — блокер с точной причиной.
- [ ] `go build/vet/test` зелёные; интеграционные — skip без сервиса.
Значит PDF исключён настройкой, а не кодом. Варианты: (a) правка files.index.formats + reindex 39k — это сервер, настройка теряется при
обновлении, Tika не OCR (сканы пусты) → отклонён; (b) server-side ingest — то
же; (c) свой индекс — выбран; (d) локальный поиск — без индекса/highlight,
отклонён.
Итог: вариант c — отдельный индекс oo_docs_text, наполняемый нашим internal/docpipe (pdftotext + OCR). Сервер OO и его индекс files_file не
меняются. Решение зафиксировано в docs/elasticsearch.md (таблица вариантов,
риски, маппинг, грабли).
Что сделано
file_es_text.go — ESTextIndex (Name()="es-text"): Ensure (явный
маппинг), Put (bulk, refresh), Delete (по id), Search (multi_match
title^2 + content, фильтры folder/ext, highlight). Только stdlib.
Уточнение PO (важно): оцифрованные PDF после классификации содержат embedded attachment (напр. оригинал/скан, добавленный конвейером). Сейчас oo index извлекает текст только основного тела PDF.
Нужно: при извлечении текста для индекса oo_docs_textдополнительно обходить встроенные вложения PDF и индексировать их текст (и/или их содержимое, если это PDF/документ). Инструменты: pdfdetach -list/-saveall (poppler), при необходимости mutool, затем internal/docpipe (pdftotext/OCR) по вложениям. Индексировать по тому же file_id, склеивая текст тело+вложения (с маркером источника).
Критерий: oo search --content "<текст из вложения>" находит файл; и по шести номерам — где текст лежит только во вложении.
**Уточнение PO (важно):** оцифрованные PDF после классификации содержат **embedded attachment** (напр. оригинал/скан, добавленный конвейером). Сейчас `oo index` извлекает текст только основного тела PDF.
Нужно: при извлечении текста для индекса `oo_docs_text` **дополнительно** обходить встроенные вложения PDF и индексировать их текст (и/или их содержимое, если это PDF/документ). Инструменты: `pdfdetach -list`/`-saveall` (poppler), при необходимости `mutool`, затем `internal/docpipe` (pdftotext/OCR) по вложениям. Индексировать по тому же `file_id`, склеивая текст тело+вложения (с маркером источника).
Критерий: `oo search --content "<текст из вложения>"` находит файл; и по шести номерам — где текст лежит только во вложении.
PR #44 обновлён (та же ветка): #44
Ветка feat/pdf-content#42 · коммиты 3cc288d, ce4778b, 3fe43ee (base main).
Ребейз на main сделан (F2 #45, F4 #43). Не мержу и не закрываю.
Ребейз
Конфликты: .env.example (секции ES/SQL + наш own-индекс — свёл), cmd/oo/search.go (F4 вынес поиск в фасад c.Files().Search(); --backend own
остался, для oo теперь фасад).
mainне собирался после мержа F4+F2: ProviderPG объявлен дважды
(file_facade.go и file_pg.go). Убрал дубликат в file_facade.go
(константа осталась у SQL-стора); тестовый fakeStore переименован в textFakeStore (конфликт с фасадом). Отдельным коммитом ce4778b.
go build/vet/test -race — зелёные.
Что добавлено
internal/docpipe/pdfattach.go:
ListAttachments (pdfdetach -list), SaveAttachment (-save, безопасные
имена без путей), JoinWithAttachments (маркер [attachment: <имя>]), xmlToText (снятие тегов/entity для XML/HTML), чтение .json/.csv.
ToMarkdownWithAttachments: тело через штатный ToMarkdown (OCR для
сканов), затем вложения; нечитаемое — пропускается, нет pdfdetach/вложений —
тело без падения.
file_text_index.go: дефолтный TextExtractor теперь зовёт ToMarkdownWithAttachments.
Integration (-tags=integration, skip без ONLYOFFICE_ES_URL):
TestIntegrationESTextIndex — PASS live.
TestIntegrationESTextIndexPDFAttachment — fixture → реальный конвейер
(pdfdetach + pdftotext) → ES → поиск токена gooattachmenttoken, который
есть только во вложении — PASS live.
go build/vet/test -race ./... — зелёные; go vet -tags=integration ./ —
чисто; gitleaks no leaks; po scan — clean. Фикстура testdata/pdf-with-attachment.pdf (3 КБ, сгенерирована gs+pdfattach, без PII).
Живая проверка вложений (OO)
Реальный e-invoice: файл 21749 (2025-12-05-…E-INVOICE…pdf, папка 672) с
вложением factur-x.xml.
oo index files 21749 → в индексе маркер [attachment: factur-x.xml].
IBAN DE26330403100827038100 (есть только в XML, не в теле) → oo search "DE26330403100827038100" --content --backend ownнаходит 21749; --backend oo (индекс OnlyOffice) — пусто.
Шесть номеров (папки 634/639, 22 PDF; вложений в этих PDF нет)
номер
результат
источник
S1019
3576 2026-07-28-112626-…
тело
S1020
3577 2026-07-28-114826-…
тело
S1021
3578 2026-07-28-S1021-…
тело
S1030
3581 2026-07-28-S1030-…
тело
S1055
3591 S1055.pdf + 3584 2026-08-20-S1055-…
тело
S1063
не найден
—
Проверил вложения всех 22 PDF и всего локального корпуса
(office-assistant/var, ~4750 PDF: 314 xml, 61 md, 61 yaml, 16 json) — ни один
не содержит S1019/S1020/S1021/S1030/S1055/S1063. S1063 нет и как файла.
Т.е. по этим шести «только во вложении» нет ни одного; вложения дают эффект на
других документах (e-invoices с factur-x.xml и оцифрованные PDF с <doc>.md).
Блокеры
Нет. Замечания:
.md-вложения (текст скана) и .xml (EN 16931) индексируются; .json
(CuraSoft) — как текст (возможен шум токенов).
Индекс наполняется вручную (oo index), после изменений повторить
(идемпотентно).
main был красный (дубль ProviderPG); фикс в этом PR — при желании вынесу
отдельно.
## F6 — отчёт (продолжение): встроенные вложения PDF
PR #44 обновлён (та же ветка): https://git.produktor.io/eSlider/go-onlyoffice/pulls/44
Ветка `feat/pdf-content#42` · коммиты `3cc288d`, `ce4778b`, `3fe43ee` (base `main`).
Ребейз на `main` сделан (F2 #45, F4 #43). Не мержу и не закрываю.
### Ребейз
- Конфликты: `.env.example` (секции ES/SQL + наш own-индекс — свёл),
`cmd/oo/search.go` (F4 вынес поиск в фасад `c.Files().Search()`; `--backend own`
остался, для `oo` теперь фасад).
- `main` **не собирался** после мержа F4+F2: `ProviderPG` объявлен дважды
(`file_facade.go` и `file_pg.go`). Убрал дубликат в `file_facade.go`
(константа осталась у SQL-стора); тестовый `fakeStore` переименован в
`textFakeStore` (конфликт с фасадом). Отдельным коммитом `ce4778b`.
- `go build/vet/test -race` — зелёные.
### Что добавлено
- `internal/docpipe/pdfattach.go`:
- `ListAttachments` (`pdfdetach -list`), `SaveAttachment` (`-save`, безопасные
имена без путей), `JoinWithAttachments` (маркер `[attachment: <имя>]`),
`xmlToText` (снятие тегов/entity для XML/HTML), чтение `.json`/`.csv`.
- `ToMarkdownWithAttachments`: тело через штатный `ToMarkdown` (OCR для
сканов), затем вложения; нечитаемое — пропускается, нет pdfdetach/вложений —
тело без падения.
- `file_text_index.go`: дефолтный `TextExtractor` теперь зовёт
`ToMarkdownWithAttachments`.
- `Tools` + `LookPath`: добавлен `pdfdetach`.
- Docs: `docs/elasticsearch.md` (раздел «Встроенные вложения PDF»), README,
package-doc docpipe.
Вложение в индекс идёт под тем же `file_id`, текст склеен «тело + секции
вложений»; upsert идемпотентен.
### Тесты (имена + PASS)
Unit (без сети):
- docpipe: `TestParseAttachmentList`, `TestParseAttachmentListEmptyAndMalformed`,
`TestSafeAttachmentName`, `TestJoinWithAttachments`,
`TestJoinWithAttachmentsNoAttachments`, `TestListAttachmentsWithoutTool`,
`TestXMLToText`, `TestAttachmentMarkdownFallback`,
`TestToMarkdownWithAttachmentsFixture` (реальный pdfdetach+pdftotext на
`testdata/pdf-with-attachment.pdf`), `TestToMarkdownWithAttachmentsPlainPDF`.
- root: прежние `TestESText*`, `TestTextIndexer*` — PASS.
- cmd/oo: `TestIndex*`, `TestSplitList` — PASS.
Integration (`-tags=integration`, skip без `ONLYOFFICE_ES_URL`):
- `TestIntegrationESTextIndex` — PASS live.
- `TestIntegrationESTextIndexPDFAttachment` — fixture → реальный конвейер
(pdfdetach + pdftotext) → ES → поиск токена `gooattachmenttoken`, который
есть **только во вложении** — PASS live.
- `TestIntegrationESSearch` (регрессия F3) — PASS live.
`go build/vet/test -race ./...` — зелёные; `go vet -tags=integration ./` —
чисто; gitleaks `no leaks`; `po scan` — clean. Фикстура
`testdata/pdf-with-attachment.pdf` (3 КБ, сгенерирована gs+pdfattach, без PII).
### Живая проверка вложений (OO)
Реальный e-invoice: файл **21749** (`2025-12-05-…E-INVOICE…pdf`, папка 672) с
вложением `factur-x.xml`.
- `oo index files 21749` → в индексе маркер `[attachment: factur-x.xml]`.
- IBAN `DE26330403100827038100` (есть только в XML, не в теле) →
`oo search "DE26330403100827038100" --content --backend own` **находит 21749**;
`--backend oo` (индекс OnlyOffice) — пусто.
### Шесть номеров (папки 634/639, 22 PDF; вложений в этих PDF нет)
| номер | результат | источник |
|-------|-----------|----------|
| S1019 | 3576 `2026-07-28-112626-…` | тело |
| S1020 | 3577 `2026-07-28-114826-…` | тело |
| S1021 | 3578 `2026-07-28-S1021-…` | тело |
| S1030 | 3581 `2026-07-28-S1030-…` | тело |
| S1055 | 3591 `S1055.pdf` + 3584 `2026-08-20-S1055-…` | тело |
| S1063 | не найден | — |
Проверил вложения всех 22 PDF и всего локального корпуса
(`office-assistant/var`, ~4750 PDF: 314 xml, 61 md, 61 yaml, 16 json) — ни один
не содержит S1019/S1020/S1021/S1030/S1055/S1063. S1063 нет и как файла.
Т.е. по этим шести «только во вложении» нет ни одного; вложения дают эффект на
других документах (e-invoices с `factur-x.xml` и оцифрованные PDF с `<doc>.md`).
### Блокеры
Нет. Замечания:
- `.md`-вложения (текст скана) и `.xml` (EN 16931) индексируются; `.json`
(CuraSoft) — как текст (возможен шум токенов).
- Индекс наполняется вручную (`oo index`), после изменений повторить
(идемпотентно).
- `main` был красный (дубль `ProviderPG`); фикс в этом PR — при желании вынесу
отдельно.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Epic: #34
F6 — PDF-контент в поиске (сейчас не индексируется)
Проблема
OnlyOffice ES индексирует содержимое только office-форматов (docx/xlsx/pptx).
PDF попадает в индекс по имени (см. F3 #37,
docs/elasticsearch.md). Поэтомуoo search --content "S1019"не находит номера внутри PDF-счетов.Research (обязательно, до кода)
Разобрать в исходниках CommunityServer (
web/.../Products/Files/Core/Search/FilesWrapper.cs,module/ASC.ElasticSearch/Core/WrapperWithDoc.cs,FileUtility.CanIndex) — почемуPDF исключён и какие есть пути:
CanIndex+ reindex (патч сервера — рискованно, ломаетсяпри обновлении OO).
internal/docpipe/OCR),не трогая индекс OnlyOffice — отдельный индекс/алиас,
esSearcherумеет и его.docpipe+pdftotext) — fallback.Оценить риски/сложность/детерминированность. Зафиксировать решение в
docs/elasticsearch.md.Реализация (по выбранному пути, детерминированно)
oo_docs_text),наполнение из docpipe,
Searcherумеет его;oo search --content --backend own.oo search --content --filesс локальным извлечением текста.Критерии
docs/elasticsearch.md.oo search --content "<текст из PDF>"находитPDF; иначе — блокер с точной причиной.
go build/vet/testзелёные; интеграционные — skip без сервиса.F6 — отчёт: PDF-контент в поиске
PR: #44
Ветка
feat/pdf-content#42· коммит7c3a0b8(basemain).Не мержу и не закрываю.
Решение и почему
Research по исходникам CommunityServer:
web/core/ASC.Web.Core/Files/FileUtility.cs—CanIndexчитает сервернуюнастройку
files.index.formats(default".pptx|.xlsx|.docx").Products/Files/Core/Search/FilesWrapper.cs—GetDocumentStream*возвращаетnullпри!CanIndex/encrypted/большой файл.ASC.ElasticSearch/Core/WrapperWithDoc.cs+ маппинг — формат-агностичны.Значит PDF исключён настройкой, а не кодом. Варианты: (a) правка
files.index.formats+ reindex 39k — это сервер, настройка теряется приобновлении, Tika не OCR (сканы пусты) → отклонён; (b) server-side ingest — то
же; (c) свой индекс — выбран; (d) локальный поиск — без индекса/highlight,
отклонён.
Итог: вариант c — отдельный индекс
oo_docs_text, наполняемый нашимinternal/docpipe(pdftotext + OCR). Сервер OO и его индексfiles_fileнеменяются. Решение зафиксировано в
docs/elasticsearch.md(таблица вариантов,риски, маппинг, грабли).
Что сделано
file_es_text.go—ESTextIndex(Name()="es-text"):Ensure(явныймаппинг),
Put(bulk, refresh),Delete(по id),Search(multi_matchtitle^2 + content, фильтры
folder/ext, highlight). Только stdlib.file_text_index.go—TextIndexer:FileStore.List/Stat/Download→docpipe-экстракция →
TextIndex; пул воркеров,PlanFolder/PlanFiles.file_es.go— добавленыterms-клауза иcontentв highlight.oo index folder FOLDER_ID | files FILE_ID...(
--recursive --exts --limit --workers --lang --dry-run);oo search --backend oo|own.docs/elasticsearch.md,README.md,.env.example(
ONLYOFFICE_ES_TEXT_INDEX, defaultoo_docs_text), docstringmain.go.Тесты
Unit (без сети):
TestESTextSearchRequestShape,TestESTextBulkBody,TestParseESTextResponse,TestNewESTextIndexDefaults,TestESTextConfigFromEnvIndexDefault,TestTextIndexerIndexEntries,TestTextIndexerRecordsExtractionFailure,TestTextIndexerPlanFolder— PASS.cmd/oo:TestIndexCommandRegistered,TestIndexSearchBackendFlag,TestSplitList— PASS.Integration (
//go:build integration, skip безONLYOFFICE_ES_URL):TestIntegrationESTextIndex— create/put/search/filters/delete/drop — PASS live.TestIntegrationESSearch(F3, регрессия) — PASS live.go build ./...,go vet ./...,go test -race ./...— зелёные.gitleaks — no leaks;
po scan— clean.go vet -tags=integration ./— чисто.Live-проверка (свой индекс, папки 634/639, 22 PDF)
oo index folder 634+oo index folder 639(indexed 14+8, failed 0), затемoo search "<N>" --content --backend own:2026-07-28-112626-Edelweiss-rechnung.pdf2026-07-28-114826-Edelweiss-rechnung.pdf2026-07-28-S1021-Edelweiss-rechnung.pdf2026-07-28-S1030-Edelweiss-rechnung.pdfS1055.pdf+ id 35842026-08-20-S1055-…(дубль, ожидаемо)Контроль: те же запросы с
--backend oo(default) — пусто, т.е. PDF-контентв индексе OnlyOffice действительно не ищется.
Блокеры
Нет. Ограничение: индекс наполняется вручную (
oo index), после изменений PDFнадо повторить (идемпотентный upsert по id).
Уточнение PO (важно): оцифрованные PDF после классификации содержат embedded attachment (напр. оригинал/скан, добавленный конвейером). Сейчас
oo indexизвлекает текст только основного тела PDF.Нужно: при извлечении текста для индекса
oo_docs_textдополнительно обходить встроенные вложения PDF и индексировать их текст (и/или их содержимое, если это PDF/документ). Инструменты:pdfdetach -list/-saveall(poppler), при необходимостиmutool, затемinternal/docpipe(pdftotext/OCR) по вложениям. Индексировать по тому жеfile_id, склеивая текст тело+вложения (с маркером источника).Критерий:
oo search --content "<текст из вложения>"находит файл; и по шести номерам — где текст лежит только во вложении.F6 — отчёт (продолжение): встроенные вложения PDF
PR #44 обновлён (та же ветка): #44
Ветка
feat/pdf-content#42· коммиты3cc288d,ce4778b,3fe43ee(basemain).Ребейз на
mainсделан (F2 #45, F4 #43). Не мержу и не закрываю.Ребейз
.env.example(секции ES/SQL + наш own-индекс — свёл),cmd/oo/search.go(F4 вынес поиск в фасадc.Files().Search();--backend ownостался, для
ooтеперь фасад).mainне собирался после мержа F4+F2:ProviderPGобъявлен дважды(
file_facade.goиfile_pg.go). Убрал дубликат вfile_facade.go(константа осталась у SQL-стора); тестовый
fakeStoreпереименован вtextFakeStore(конфликт с фасадом). Отдельным коммитомce4778b.go build/vet/test -race— зелёные.Что добавлено
internal/docpipe/pdfattach.go:ListAttachments(pdfdetach -list),SaveAttachment(-save, безопасныеимена без путей),
JoinWithAttachments(маркер[attachment: <имя>]),xmlToText(снятие тегов/entity для XML/HTML), чтение.json/.csv.ToMarkdownWithAttachments: тело через штатныйToMarkdown(OCR длясканов), затем вложения; нечитаемое — пропускается, нет pdfdetach/вложений —
тело без падения.
file_text_index.go: дефолтныйTextExtractorтеперь зовётToMarkdownWithAttachments.Tools+LookPath: добавленpdfdetach.docs/elasticsearch.md(раздел «Встроенные вложения PDF»), README,package-doc docpipe.
Вложение в индекс идёт под тем же
file_id, текст склеен «тело + секциивложений»; upsert идемпотентен.
Тесты (имена + PASS)
Unit (без сети):
TestParseAttachmentList,TestParseAttachmentListEmptyAndMalformed,TestSafeAttachmentName,TestJoinWithAttachments,TestJoinWithAttachmentsNoAttachments,TestListAttachmentsWithoutTool,TestXMLToText,TestAttachmentMarkdownFallback,TestToMarkdownWithAttachmentsFixture(реальный pdfdetach+pdftotext наtestdata/pdf-with-attachment.pdf),TestToMarkdownWithAttachmentsPlainPDF.TestESText*,TestTextIndexer*— PASS.TestIndex*,TestSplitList— PASS.Integration (
-tags=integration, skip безONLYOFFICE_ES_URL):TestIntegrationESTextIndex— PASS live.TestIntegrationESTextIndexPDFAttachment— fixture → реальный конвейер(pdfdetach + pdftotext) → ES → поиск токена
gooattachmenttoken, которыйесть только во вложении — PASS live.
TestIntegrationESSearch(регрессия F3) — PASS live.go build/vet/test -race ./...— зелёные;go vet -tags=integration ./—чисто; gitleaks
no leaks;po scan— clean. Фикстураtestdata/pdf-with-attachment.pdf(3 КБ, сгенерирована gs+pdfattach, без PII).Живая проверка вложений (OO)
Реальный e-invoice: файл 21749 (
2025-12-05-…E-INVOICE…pdf, папка 672) свложением
factur-x.xml.oo index files 21749→ в индексе маркер[attachment: factur-x.xml].DE26330403100827038100(есть только в XML, не в теле) →oo search "DE26330403100827038100" --content --backend ownнаходит 21749;--backend oo(индекс OnlyOffice) — пусто.Шесть номеров (папки 634/639, 22 PDF; вложений в этих PDF нет)
2026-07-28-112626-…2026-07-28-114826-…2026-07-28-S1021-…2026-07-28-S1030-…S1055.pdf+ 35842026-08-20-S1055-…Проверил вложения всех 22 PDF и всего локального корпуса
(
office-assistant/var, ~4750 PDF: 314 xml, 61 md, 61 yaml, 16 json) — ни одинне содержит S1019/S1020/S1021/S1030/S1055/S1063. S1063 нет и как файла.
Т.е. по этим шести «только во вложении» нет ни одного; вложения дают эффект на
других документах (e-invoices с
factur-x.xmlи оцифрованные PDF с<doc>.md).Блокеры
Нет. Замечания:
.md-вложения (текст скана) и.xml(EN 16931) индексируются;.json(CuraSoft) — как текст (возможен шум токенов).
oo index), после изменений повторить(идемпотентно).
mainбыл красный (дубльProviderPG); фикс в этом PR — при желании вынесуотдельно.