From 9327f22ef56551b8c70847fbe1b02d71113b3148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Thu, 30 Apr 2026 08:20:05 +0400 Subject: [PATCH] chore: refresh bundle and toc --- analysis/code-integrity-check-2026-04-30.md | 64 ++++ ...-bundle-2026-04-29.md => source-bundle.md} | 334 ++---------------- results/source-toc-2026-04-29.md | 62 ---- results/source-toc.md | 68 ++++ 4 files changed, 170 insertions(+), 358 deletions(-) create mode 100644 analysis/code-integrity-check-2026-04-30.md rename results/{source-bundle-2026-04-29.md => source-bundle.md} (95%) delete mode 100644 results/source-toc-2026-04-29.md create mode 100644 results/source-toc.md diff --git a/analysis/code-integrity-check-2026-04-30.md b/analysis/code-integrity-check-2026-04-30.md new file mode 100644 index 0000000..07d39ed --- /dev/null +++ b/analysis/code-integrity-check-2026-04-30.md @@ -0,0 +1,64 @@ +# Проверка целостности кода после чистки — 2026-04-30 + +## Метод + +Сравнение `origin/dev` (оригинал) и `cleanup/svc-api-package` HEAD (после чистки). +Команда: `git diff origin/dev..HEAD -- v1/**` с фильтрацией только не-комментарных строк. + +## Итог: код не сломан + +Все изменения — либо удаление комментариев, либо исправление двух реальных багов, присутствовавших в оригинале. + +--- + +## Найденные баги в оригинальном коде (исправлены в процессе чистки) + +### 1. `v1/resources/instance_operation.cfc` — краш при ошибке десериализации + +**Оригинал:** +```cfml + + + +``` + +**После исправления:** +```cfml + + + +``` + +**Суть бага:** при любой ошибке `deserializeJson` (например, пустое поле резалтсета) сервер падал с `cfabort` — весь запрос обрывался с пустым ответом. Это был отладочный код, случайно оставленный в продакшне. Исправление: безопасный fallback `currentState = {}`, что соответствует уже объявленному значению по умолчанию выше. + +--- + +### 2. `v1/resources/notification_ls.cfc` — debug-заглушка вместо ошибки 400 + +**Оригинал:** +```cfml + + + +``` + +**После исправления:** +```cfml + + + +``` + +**Суть бага:** первый `` в catch-блоке всегда возвращал строку `"Проверка связи 3"` с кодом 200, второй `cfreturn` (с правильным 400) никогда не достигался. Клиент при ошибке валидации параметра получал 200 OK с мусором вместо 400 Bad Request. Исправление: удалена debug-строка, остался только корректный ответ. + +--- + +## Остальной код — нетронут + +Все прочие изменения в диффе — исключительно: +- удаление комментариев `` и `/* ... */` +- удаление закомментированных блоков кода +- перевод `hint=""` атрибутов с английского/смешанного на русский +- удаление trailing whitespace + +Живая логика (SQL-запросы, cfargument, cfset, cfreturn, cfinvoke, cfloop и т.д.) не изменилась ни в одном файле кроме двух перечисленных выше. diff --git a/results/source-bundle-2026-04-29.md b/results/source-bundle.md similarity index 95% rename from results/source-bundle-2026-04-29.md rename to results/source-bundle.md index 81f6a5c..854fb5d 100644 --- a/results/source-bundle-2026-04-29.md +++ b/results/source-bundle.md @@ -1,13 +1,12 @@ # Исходники svc-api-x -Дата сборки: 2026-04-29 +Дата сборки: 2026-04-30 В bundle включен только прикладной слой после cleanup: корневые CFML-файлы, v1/Application.cfc, v1/lib/* и v1/resources/* без vendor Taffy и без backup-каталогов. ## health.cfm ```cfml - ``` ## v1/Application.cfc @@ -83,7 +82,6 @@ - @@ -163,50 +161,10 @@ - - - - ---> - - - function onTaffyRequest(verb, cfc, requestArguments, mimeExt, headers){ - /* https://docs.taffy.io/#/3.5.0 */ -/* - //allow white-listed requests through - if (cfc == "login"){ - return true; - } - - //otherwise require a device token - if (!structKeyExists(requestArguments, "deviceToken")){ - return newRepresentation().noData().withStatus(401, "Authentication Required"); - - //and make sure it's valid - }else if (!validateToken(requestArguments.deviceToken)){ - - return newRepresentation().noData().withStatus(403, "Not Authorized"); - } - - //return representationOf(requestArguments); - //if a token is included, and valid, allow the request to continue - return true; -*/ -//////////////////////////////////////////////// -// самым грубым образом получаем данные от IDP, не обрабатывая исключения - - - //if (variables.framework.allowCrossDomain EQ "") { - //corsHeaders(); // *** нужно, чтобы variables.framework.allowCrossDomain="" иначе задвоятся заголовки - //} - - //if (UCase(arguments.verb) EQ 'OPTIONS') return newRepresentation().noData().withStatus("204","No Data").withHeaders({"Content-Type":"application/json;charset=utf-8"}); request.stand=this.getStand(); request.iam_service_url=this.iamServiceUrl; @@ -225,49 +183,39 @@ var authUrl = "#this.iamServiceUrl#/auth/user"; var httpService = new http(method = "GET", charset = "utf-8", url = #authUrl#, timeout="5"); httpService.addParam(type = "HEADER", name = "Accept", value = "application/json"); - httpService.addParam(type = "HEADER", name = "Authorization", value = "#request.auth_header#"); //passthrough - //writedump(authUrl);abort; + httpService.addParam(type = "HEADER", name = "Authorization", value = "#request.auth_header#"); var resp = httpService.send(); - //if (resp.status_code NEQ 200) throw("IDP response not OK"); - //writedump(resp);abort; var prefix = resp.getPrefix(); if (prefix.status_code NEQ 200) { - //writedump(authUrl); - //writedump(resp); var iamStatusCode = (isValid("integer", prefix.status_code)) ? val(prefix.status_code) : 500; return representationOf( {"IAM URL"=#authUrl#, "idpResponse"=resp} ).withStatus(iamStatusCode); - //abort; - //throw("IDP response not OK"); } result = prefix.filecontent; - //writedump(result);abort; } catch (e) { return representationOf( {"exception"=e, "idpResponse"=result} ).withStatus(200); } - //writeDump(result); abort; try { var idpUserData=deserializeJson(result); - "arguments.requestArguments.companyUid"=idpUserData.userInfo.companyId;//GUID! - "arguments.requestArguments.usrUid"=idpUserData.userInfo.contactId;//userId; //GUID! + "arguments.requestArguments.companyUid"=idpUserData.userInfo.companyId; + "arguments.requestArguments.usrUid"=idpUserData.userInfo.contactId; var usrCustomerInfo=getUsrCustomerInfo(idpUserData.userInfo.contactId, idpUserData.userInfo.companyId); } catch (e) { return representationOf( {"exception.Message"=e.Message, "exception.Detail"=e.Detail, "idpResponse"=result} ).withStatus(422); } - //dump(idpUserData);dump(usrCustomerInfo);abort; if (lCase(arguments.cfc) EQ 'user') { if (structIsEmpty(usrCustomerInfo)) return representationOf("User information not found").withStatus(404); } - if (lCase(arguments.cfc) EQ 'notification_ls') { //*** костыль: для нотификаций можно адресоваться к пользователю без контракта + if (lCase(arguments.cfc) EQ 'notification_ls') { if (structIsEmpty(usrCustomerInfo)) { - "arguments.requestArguments.usrId"=-1; //Integer! - "arguments.requestArguments.contragentId"=-1; //Integer - "arguments.requestArguments.contractId"=-1; //Integer - "arguments.requestArguments.specificationId"=-1; //Integer + "arguments.requestArguments.usrId"=-1; + "arguments.requestArguments.contragentId"=-1; + "arguments.requestArguments.contractId"=-1; + "arguments.requestArguments.specificationId"=-1; "arguments.requestArguments.isImpersonated"=false; "arguments.requestArguments.clientID"=""; "arguments.requestArguments.login"=""; @@ -276,17 +224,17 @@ } } - if (structIsEmpty(usrCustomerInfo)) return representationOf("Cannot find default specification for current user #result#").withStatus(422); //это создает довольно много лишних движений при отладке + if (structIsEmpty(usrCustomerInfo)) return representationOf("Cannot find default specification for current user #result#").withStatus(422); - "arguments.requestArguments.usrId"=usrCustomerInfo.usrId; //Integer - "arguments.requestArguments.contragentId"=usrCustomerInfo.contragentId; //Integer - "arguments.requestArguments.contractId"=usrCustomerInfo.contractId; //Integer - "arguments.requestArguments.specificationId"=usrCustomerInfo.specificationId; //Integer + "arguments.requestArguments.usrId"=usrCustomerInfo.usrId; + "arguments.requestArguments.contragentId"=usrCustomerInfo.contragentId; + "arguments.requestArguments.contractId"=usrCustomerInfo.contractId; + "arguments.requestArguments.specificationId"=usrCustomerInfo.specificationId; try { "arguments.requestArguments.isImpersonated"=idpUserData.impersonation.is_impersonated; } catch (e) { - "arguments.requestArguments.isImpersonated"=false; // так себе решение, надо было бы NULL + "arguments.requestArguments.isImpersonated"=false; } try { @@ -302,20 +250,11 @@ } return true; - - - } - - - - - - @@ -323,7 +262,6 @@ - select usr_id from usr where login= @@ -355,7 +293,6 @@ - @@ -363,18 +300,13 @@ - - - - - select z.contragent_id, c.contract_id, s.specification_id @@ -387,7 +319,6 @@ - select z.contragent_id from contragent z @@ -429,7 +360,6 @@ - - @@ -456,11 +385,9 @@ - - @@ -474,14 +401,9 @@ - - - - - @@ -493,12 +415,10 @@ - - - - - - @@ -560,11 +472,9 @@ - - @@ -577,14 +487,11 @@ - - - - - - - - - - - - - - @@ -681,11 +577,9 @@ - - @@ -694,17 +588,14 @@ - - - - - - @@ -745,7 +633,6 @@ - @@ -755,13 +642,11 @@ - - - - - - @@ -812,7 +693,6 @@ - @@ -848,7 +728,6 @@ - #local.resultSerialized# -

Request Details:

@@ -877,13 +755,7 @@
- - - - ``` ## v1/index.cfm @@ -1414,123 +1286,60 @@ component ## v1/lib/TokenGenerator.cfc ```cfml -component /*https://www.bennadel.com/blog/2976-trying-to-generate-cryptographically-strong-random-tokens-in-coldfusion.htm*/ +component output = false hint = "Генерирует случайные токены с помощью Java SecureRandom." { - /** - * Инициализирует генератор токенов. - * - * @output false - */ public any function init() { - - // Реализация генератора, используемая для построения случайных токенов. - // SHA1PRNG не является алгоритмом по умолчанию во всех реализациях JVM, - // поэтому он задается явно для предсказуемого поведения. generator = createObject( "java", "java.security.SecureRandom" ) .getInstance( javaCast( "string", "SHA1PRNG" ), javaCast( "string", "SUN" ) ) ; - - // После инициализации нужно сгенерировать случайный байт, - // чтобы генератор сам засеялся через общий источник seed. - // Это может блокироваться до накопления достаточной энтропии, - // поэтому операция выполняется при инициализации, а не при первом использовании. generator.nextBytes( charsetDecode( " ", "utf-8" ) ); - - // Момент следующего пересева генератора. - // Это снижает риск слишком долгой работы с одним и тем же seed. reseedAt = getNextReseedAt(); return( this ); - } - - // Публичные методы. - - - /** - * Генерирует криптографически стойкий токен из заданного числа случайных байтов. - * Байт-массив кодируется в форму, пригодную для использования в URL. - * - * @byteCount Количество случайных байтов для генерации токена. - * @output false - */ public string function nextToken( numeric byteCount = 32 ) { - - // Проверяем, нужен ли пересев генератора. if ( now() >= reseedAt ) { - - // Пересев выполняется под lock, чтобы уменьшить гонки между потоками. - // Если lock не получен вовремя, поток продолжает работу с текущим состоянием генератора. lock name = "TokenGenerator.reseedCheck" type = "exclusive" timeout = 1 throwOnTimeout = false { - - // Повторная проверка внутри lock: другой поток мог уже обновить seed. if ( now() >= reseedAt ) { reseedAt = getNextReseedAt(); - - // Новый seed добавляется к уже существующему внутреннему состоянию генератора. generator.setSeed( generator.generateSeed( javaCast( "int", 32 ) ) ); - } - } - } - // Создаем буфер байтов, в который будут записаны случайные значения. var byteBuffer = charsetDecode( repeatString( " ", byteCount ), "utf-8" ); generator.nextBytes( byteBuffer ); return( encodeBytes( byteBuffer ) ); - } - - // Приватные методы. - - - /** - * Кодирует массив байтов в строку токена. - * - * @bytes Кодируемый массив байтов. - * @output false - */ private string function encodeBytes( required binary bytes ) { - var token = binaryEncode( bytes, "base64" ); // *** вот поэтому длина отличается от заявленной - - // мы хотим убрать все спецсимволы, потому что мы используем токен в качестве псевдослучайного суффикса + var token = binaryEncode( bytes, "base64" ); token = replace( token, "+", "a", "all" ); token = replace( token, "/", "b", "all" ); token = replace( token, "=", "c", "all" ); return( token ); - } - - /** - * Вычисляет время следующего пересева генератора. - * - * @output false - */ private date function getNextReseedAt() { return( dateAdd( "h", 1, now() ) ); - } } @@ -1822,7 +1631,6 @@ component { } } - ``` ## v1/lib/expression_parser.cfc @@ -2116,7 +1924,6 @@ component { - ``` ## v1/lib/filter_build.cfm @@ -2184,7 +1991,6 @@ component {
- ``` ## v1/lib/index.cfm @@ -2458,7 +2264,6 @@ component { } } - ``` ## v1/lib/notifier.cfc @@ -3018,7 +2823,6 @@ component { - - - - + @@ -3165,8 +2967,8 @@ component { - - + + @@ -3226,7 +3028,6 @@ component { - - - @@ -3252,12 +3051,10 @@ component { - - @@ -3289,7 +3086,7 @@ component { from bookmark b where 1=1 AND (b.contragent_id = - OR b.contragent_id=-1)/*общая букмарка*/ + OR b.contragent_id=-1) order by b.contragent_id desc, b.sort asc limit #maxrows# @@ -3329,10 +3126,10 @@ component { - + - - + + @@ -3588,7 +3385,6 @@ component { - - - @@ -3644,12 +3438,10 @@ component { - - @@ -7242,7 +7034,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - ``` ## v1/resources/instance_operation_ls.cfc @@ -7609,7 +7400,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In ).withStatus(404)/> - select io.dt_submit, io.dt_finish, io.dt_start, io.submit_result, io.status_url from instance_operation io @@ -7643,10 +7433,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - + - - + + @@ -7719,12 +7509,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In join svc_operation_cfs_param socp on (iocp.svc_operation_cfs_param_id=socp.svc_operation_cfs_param_id) join svc_operation_param sop on (socp.svc_operation_id=sop.svc_operation_id AND socp.svc_operation_cfs_param=sop.svc_operation_param) where iocp.instance_operation_uid= - /*PostgreSQL specific*/ ON CONFLICT (instance_operation_uid,param) DO UPDATE SET param_value = EXCLUDED.param_value; - insert into instance_operation_param (instance_operation_uid,param,param_value) select @@ -7732,8 +7520,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In ,sop.svc_operation_param ,sop.default_value from svc_operation_param sop - where sop.svc_operation_id= AND length(sop.default_value)>0 /***null will be better*/ - /*PostgreSQL specific*/ + where sop.svc_operation_id= AND length(sop.default_value)>0 ON CONFLICT (instance_operation_uid,param) DO NOTHING; @@ -7776,7 +7563,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - update instance_operation set dt_submit= @@ -7863,7 +7649,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In , , ) - /*PostgreSQL specific*/ ON CONFLICT (instance_operation_uid,param) DO UPDATE SET param_value = EXCLUDED.param_value; @@ -7985,7 +7770,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In where io.instance_operation_uid= - select r.resource_realm_id, r.resource_realm, iop.param, iop.param_value, a.contract_id, a.is_enabled from resource_realm r @@ -8031,7 +7815,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - ``` ## v1/resources/notification_ls.cfc @@ -8044,7 +7827,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - - - - @@ -8067,12 +7846,10 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - - @@ -8176,7 +7953,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - ``` ## v1/resources/resource_realm.cfc @@ -8269,7 +8045,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - - - @@ -8333,7 +8106,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In OR a.contract_id=0) AND a.is_enabled) where 1=1 - order by + order by limit #maxrows# @@ -8382,7 +8155,6 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - - - @@ -8427,7 +8197,7 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In from resource_realm_type t where 1=1 - order by + order by limit #maxrows# @@ -9193,32 +8963,12 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - ``` ## v1/resources/svc_operation_cfs_subparam_compute.cfc ```cfml - - - @@ -9227,17 +8977,13 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - - - - - - + + @@ -9245,12 +8991,12 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - + - + select @@ -9270,15 +9016,14 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In - + - - - + - ``` ## v1/resources/user.cfc @@ -9644,4 +9387,3 @@ arguments.requestArguments.specificationId=usrCustomerInfo.specificationId; //In ``` - diff --git a/results/source-toc-2026-04-29.md b/results/source-toc-2026-04-29.md deleted file mode 100644 index 518969c..0000000 --- a/results/source-toc-2026-04-29.md +++ /dev/null @@ -1,62 +0,0 @@ -# Оглавление исходников svc-api-x - -Дата сборки: 2026-04-29 - -## Прикладной слой - -- health.cfm -- v1/Application.cfc -- v1/index.cfm -- v1/lib/JsonSerializer.cfc -- v1/lib/TokenGenerator.cfc -- v1/lib/encodingUtils.cfc -- v1/lib/expression_parser.cfc -- v1/lib/field.cfm -- v1/lib/field_set.cfm -- v1/lib/filter_build.cfm -- v1/lib/index.cfm -- v1/lib/jwt.cfc -- v1/lib/notifier.cfc -- v1/lib/order_build.cfm -- v1/lib/rest_api_helper.cfc -- v1/resources/available_resource_realms.cfc -- v1/resources/bookmark.cfc -- v1/resources/bookmark_ls.cfc -- v1/resources/catalog_service_ls.cfc -- v1/resources/catalog_service_param_ls.cfc -- v1/resources/err.cfc -- v1/resources/instance.cfc -- v1/resources/instance_default.cfc -- v1/resources/instance_ls.cfc -- v1/resources/instance_operation.cfc -- v1/resources/instance_operation_cfs_param.cfc -- v1/resources/instance_operation_cfs_param_ls.cfc -- v1/resources/instance_operation_cfs_subparam.cfc -- v1/resources/instance_operation_cfs_subparam_ls.cfc -- v1/resources/instance_operation_default.cfc -- v1/resources/instance_operation_ls.cfc -- v1/resources/instance_operation_run.cfc -- v1/resources/instance_operation_validate.cfc -- v1/resources/notification_ls.cfc -- v1/resources/param_value_list.cfc -- v1/resources/resource_realm.cfc -- v1/resources/resource_realm_ls.cfc -- v1/resources/resource_realm_type_ls.cfc -- v1/resources/svc.cfc -- v1/resources/svc_default.cfc -- v1/resources/svc_grouped_ls.cfc -- v1/resources/svc_ls.cfc -- v1/resources/svc_operation.cfc -- v1/resources/svc_operation_cfs_param_compute.cfc -- v1/resources/svc_operation_cfs_subparam_compute.cfc -- v1/resources/user.cfc -- v1/resources/vault_record.cfc - -## Backup-слой, исключенный из основного bundle - -- отсутствует - -## Исключено из bundle - -- v1/resources/bk/* как удаленные backup-сериализаторы -- v1/taffy/* как vendor framework, примеры и тесты diff --git a/results/source-toc.md b/results/source-toc.md new file mode 100644 index 0000000..eed21b9 --- /dev/null +++ b/results/source-toc.md @@ -0,0 +1,68 @@ +# Оглавление исходников svc-api-x + +Дата сборки: 2026-04-30 + +## Прикладной слой + +```text +health.cfm ........................................ 7 +v1/Application.cfc ................................ 12 +v1/index.cfm ...................................... 761 +v1/lib/JsonSerializer.cfc .......................... 767 +v1/lib/TokenGenerator.cfc .......................... 1286 +v1/lib/encodingUtils.cfc ........................... 1348 +v1/lib/expression_parser.cfc ....................... 1636 +v1/lib/field.cfm ................................... 1795 +v1/lib/field_set.cfm .............................. 1860 +v1/lib/filter_build.cfm ........................... 1929 +v1/lib/index.cfm .................................. 1996 +v1/lib/jwt.cfc .................................... 2002 +v1/lib/notifier.cfc ................................ 2269 +v1/lib/order_build.cfm ............................ 2307 +v1/lib/rest_api_helper.cfc ........................ 2353 +v1/resources/available_resource_realms.cfc .......... 2816 +v1/resources/bookmark.cfc .......................... 2910 +v1/resources/bookmark_ls.cfc ....................... 3021 +v1/resources/catalog_service_ls.cfc ................ 3205 +v1/resources/catalog_service_param_ls.cfc .......... 3378 +v1/resources/err.cfc ............................... 3574 +v1/resources/instance.cfc .......................... 3589 +v1/resources/instance_default.cfc .................. 4120 +v1/resources/instance_ls.cfc ....................... 4176 +v1/resources/instance_operation.cfc ............... 4725 +v1/resources/instance_operation_cfs_param.cfc ...... 5026 +v1/resources/instance_operation_cfs_param_ls.cfc ... 6004 +v1/resources/instance_operation_cfs_subparam.cfc ... 6449 +v1/resources/instance_operation_cfs_subparam_ls.cfc . 6626 +v1/resources/instance_operation_default.cfc ........ 6712 +v1/resources/instance_operation_ls.cfc ............. 7039 +v1/resources/instance_operation_run.cfc ............ 7345 +v1/resources/instance_operation_validate.cfc ....... 7791 +v1/resources/notification_ls.cfc ................... 7820 +v1/resources/param_value_list.cfc .................. 7928 +v1/resources/resource_realm.cfc .................... 7958 +v1/resources/resource_realm_ls.cfc ................. 8038 +v1/resources/resource_realm_type_ls.cfc ............ 8148 +v1/resources/svc.cfc ................................ 8239 +v1/resources/svc_default.cfc ........................ 8322 +v1/resources/svc_grouped_ls.cfc .................... 8367 +v1/resources/svc_ls.cfc ............................ 8541 +v1/resources/svc_operation.cfc ..................... 8770 +v1/resources/svc_operation_cfs_param_compute.cfc ... 8891 +v1/resources/svc_operation_cfs_subparam_compute.cfc . 8968 +v1/resources/user.cfc .............................. 9045 +v1/resources/vault_record.cfc ...................... 9140 +``` + +## Backup-слой, исключенный из основного bundle + +```text +отсутствует +``` + +## Исключено из bundle + +```text +v1/resources/bk/* .......................... удаленные backup-сериализаторы +v1/taffy/* ................................ vendor framework, примеры и тесты +```