@coopenomics/factory 2026.2.22-2 → 2026.3.4-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.
package/README.md CHANGED
@@ -1,77 +1,127 @@
1
- # Кооперативный генератор документов
2
-
3
- [![npm version][npm-version-src]][npm-version-href]
4
- [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
- [![bundle][bundle-src]][bundle-href]
6
- [![JSDocs][jsdocs-src]][jsdocs-href]
7
- [![License][license-src]][license-href]
8
-
9
- ## Обзор
10
- Данная библиотека предназначена для кооперативов, которые используют блокчейн EOSIO для хранения шаблонов документов и реализуют систему цифровой подписи на основе EOSIO. Шаблоны документов и соответствующие переводы хранятся в блокчейне, в то время как приватные данные для заполнения шаблонов находятся в закрытом хранилище кооператива. Библиотека позволяет генерировать документы, автоматически заполняя их данными из хранилища и подписывая при помощи приватного ключа.
11
-
12
- ## Функциональность
13
- Библиотека предоставляет следующие основные возможности:
14
-
15
- - Генерация метаданных для документов на основе предопределенных шаблонов.
16
- - Конструирование данных для документов, используя JSON Schema, загружаемую из блокчейна.
17
- - Получение шаблонов документов и переводов из блокчейна.
18
- - Рендеринг документов для создания цифровой версии в формате doc и gdoc.
19
- - Подпись сгенерированных документов с использованием приватных ключей EOSIO.
20
-
21
- ## Процесс генерации документов
22
- Получение шаблона и схемы из блокчейна: Шаблоны и схемы загружаются из блокчейна. Шаблоны включают в себя как структуру самого документа, так и переводы элементов документа на разные языки.
23
-
24
- Конструирование данных: Фабрика шаблонов использует JSON Schema для определения структуры данных, необходимых для каждого шаблона. Приватные данные загружаются из хранилища кооператива и используются для заполнения шаблонов.
25
-
26
- Рендеринг шаблона: Используя загруженные данные и шаблон, библиотека рендерит окончательный документ. Этот этап включает подстановку реальных данных пользователя и кооператива в шаблон.
27
-
28
- Подпись документа: Сгенерированный документ подписывается с использованием приватного ключа EOSIO.
29
-
30
- Верификация документа: При необходимости, сгенерированные и подписанные документы могут быть верифицированы для подтверждения их подлинности.
31
-
32
- ## Подготовка к использованию
33
- Для начала работы с библиотекой необходимо ознакомиться с требованиями окружения:
34
- - Ваше приложение должно взаимодействовать с блокчейном EOSIO для получения шаблонов и схем.
35
- - У кооператива должно быть настроено приватное хранилище для доступа к личным данным.
36
- - Необходим модуль для работы с JSON Schema, например, json-schema для Node.js.
37
- - Вам потребуется EOSJS для подписи документов.
38
-
39
- ## Пример использования
40
- Пример реализации фабрики для генерации документа типа joincoop из файла documents/joincoop.ts:
41
-
42
- ``` javascript
43
- const joinCoopFactory = new JoinCoopTemplateFactory()
44
- joinCoopFactory.getComplexTemplate()
45
- .then((template) => {
46
- const options = {
47
- username: 'user1',
48
- lang: 'en',
49
- action: 'joincoop'
50
- }
51
-
52
- const generator = new GeneratorJSImpl()
53
- return generator.generate(options)
54
- })
55
- .then((document) => {
56
- // Работа с сгенерированным и подписанным документом
57
- })
58
- .catch((error) => {
59
- console.log(error)
60
- // Обработка ошибок
61
- })
1
+ # 📄 @coopenomics/factory
2
+
3
+ Фабрика документов кооператива — библиотека для генерации юридически значимых документов (PDF). Объединяет шаблоны из блокчейна, приватные данные из хранилища и рендерит HTML через Handlebars/Nunjucks с последующей генерацией PDF через WeasyPrint.
4
+
5
+ ## Основные возможности
6
+
7
+ - Более 60 шаблонов юридических документов (заявления, протоколы, решения, договоры, акты)
8
+ - Рендеринг HTML из шаблонов Handlebars и Nunjucks
9
+ - Генерация PDF через WeasyPrint
10
+ - Валидация данных по JSON Schema
11
+ - Электронная подпись документов через EOSIO-ключи
12
+ - Хранение документов и метаданных в MongoDB
13
+ - Сборка как ES-модуль и CommonJS (unbuild)
14
+
15
+ ## Установка
16
+
17
+ Компонент является частью монорепозитория. Установка зависимостей из корня проекта:
18
+
19
+ ```bash
20
+ pnpm install
62
21
  ```
63
22
 
64
- ## Расширение и модификация
65
- Библиотека построена с учетом возможности расширения и модификации. Для добавления новых типов документов реализуйте свои фабрики, соответствующие интерфейсу IDocumentTemplateFactory, и регистрируйте их в главном классе библиотеки.
23
+ Или только для этого компонента:
66
24
 
67
- ## Поддержка и вклад
68
- Если у вас есть вопросы, предложения или вы хотите внести вклад в проект, пожалуйста, свяжитесь с технической поддержкой или откройте issue в системе управления проектами.
25
+ ```bash
26
+ pnpm install --filter @coopenomics/factory
27
+ ```
69
28
 
70
- ## Таблица документов
71
- Здесь соберём таблицу имен и шаблонов типизированных документов.
29
+ ### Системные зависимости
72
30
 
73
- ## Лицензия
31
+ Для генерации PDF необходим [WeasyPrint](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#installation).
32
+
33
+ ## Скрипты
34
+
35
+ | Скрипт | Команда | Описание |
36
+ |--------|---------|----------|
37
+ | `build` | `pnpm run build` | Сборка библиотеки (unbuild) |
38
+ | `dev` | `pnpm run dev` | Режим разработки с автопересборкой (nodemon) |
39
+ | `test` | `pnpm run test` | Запуск тестов (Vitest) |
40
+ | `lint` | `pnpm run lint` | Проверка кода (ESLint) |
41
+ | `typecheck` | `pnpm run typecheck` | Проверка типов TypeScript |
42
+ | `setup-indexes` | `pnpm run setup-indexes` | Настройка индексов MongoDB |
43
+
44
+ Из корня монорепозитория:
45
+
46
+ ```bash
47
+ pnpm run build:lib # Сборка factory + cooptypes
48
+ pnpm run dev:lib # Режим разработки factory + cooptypes
49
+ pnpm run test:component # Компонентные тесты factory
50
+ ```
51
+
52
+ ## Конфигурация
53
+
54
+ Для тестирования и локальной работы необходимо подключение к MongoDB. Переменные окружения:
55
+
56
+ - `MONGO_URI` — строка подключения к MongoDB
57
+ - `SOURCE` — источник данных (`local` для тестов)
74
58
 
75
- [MIT](./LICENSE) License © 2024-PRESENT [Alex Ant](https://github.com/dacom-dark-sun)
59
+ Подробное описание переменных в файле `.env-example`.
60
+
61
+ ## Тестирование
62
+
63
+ ```bash
64
+ SOURCE=local NODE_ENV=test pnpm --filter @coopenomics/factory run test
65
+ ```
66
+
67
+ Тестовый набор покрывает генерацию документов различных типов. Тесты требуют запущенный MongoDB и используют таймаут 240 секунд.
68
+
69
+ Файлы тестов:
70
+
71
+ | Файл | Описание |
72
+ |------|----------|
73
+ | `test/index.test.ts` | Основные тесты генерации документов |
74
+ | `test/wallet.test.ts` | Тесты документов кошелька |
75
+ | `test/meet.test.ts` | Тесты документов собраний |
76
+ | `test/blagorost.test.ts` | Тесты документов программы «Благорост» |
77
+ | `test/market.test.ts` | Тесты документов маркетплейса |
78
+ | `test/search.test.ts` | Тесты поиска документов |
79
+ | `test/udata.test.ts` | Тесты пользовательских данных |
80
+ | `test/documents-1000-plus.test.ts` | Тесты документов с registry_id > 1000 |
81
+
82
+ ## Архитектура
83
+
84
+ ```
85
+ src/
86
+ ├── index.ts # Точка входа библиотеки
87
+ ├── config.ts # Конфигурация подключений
88
+ ├── Actions/ # Генераторы документов (по registry_id)
89
+ │ ├── 1.WalletAgreement.ts
90
+ │ ├── 100.ParticipantApplication.ts
91
+ │ ├── 300.AnnualGeneralMeetingAgenda.ts
92
+ │ ├── 600.FreeDecision.ts
93
+ │ ├── 1001.GenerationContract.ts
94
+ │ └── ... # Более 60 шаблонов
95
+ ├── Factory/ # Ядро фабрики документов
96
+ ├── Models/ # Модели данных MongoDB
97
+ │ ├── Cooperative.ts
98
+ │ ├── Individual.ts
99
+ │ ├── Organization.ts
100
+ │ ├── Document.ts
101
+ │ └── ...
102
+ └── templates/ # HTML-шаблоны (Nunjucks/Handlebars)
103
+ test/ # Тесты (Vitest)
104
+ ```
105
+
106
+ ### Процесс генерации документа
107
+
108
+ 1. Загрузка шаблона и JSON Schema из блокчейна
109
+ 2. Получение приватных данных из хранилища (MongoDB)
110
+ 3. Валидация данных по JSON Schema (Ajv)
111
+ 4. Конструирование контекста для шаблона
112
+ 5. Рендеринг HTML из Nunjucks/Handlebars-шаблона
113
+ 6. Подпись хеша документа EOSIO-ключом
114
+ 7. Генерация PDF через WeasyPrint
115
+
116
+ ## Ключевые зависимости
117
+
118
+ - **nunjucks / handlebars** — шаблонизаторы
119
+ - **ajv** — валидация JSON Schema
120
+ - **mongodb** — хранилище данных
121
+ - **eosjs-ecc** — криптографические операции EOSIO
122
+ - **pdf-lib** — работа с PDF
123
+ - **unbuild** — сборка библиотеки
124
+
125
+ ## Лицензия
76
126
 
77
- Версия документации: 1.0.0
127
+ [BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.ru)
package/dist/index.cjs CHANGED
@@ -4,8 +4,8 @@ const Ajv = require('ajv');
4
4
  const addFormats = require('ajv-formats');
5
5
  const localize_ru = require('ajv-i18n/localize/ru');
6
6
  const cooptypes = require('cooptypes');
7
- const dotenv = require('dotenv');
8
7
  const mongodb = require('mongodb');
8
+ const dotenv = require('dotenv');
9
9
  const moment = require('moment-timezone');
10
10
  const fs = require('node:fs');
11
11
  const path = require('node:path');
@@ -3347,13 +3347,140 @@ const meetTableMock = {
3347
3347
  match: (_url, _params) => false
3348
3348
  };
3349
3349
 
3350
+ let mongoClient$2 = null;
3351
+ let deltasCollection$1 = null;
3352
+ async function getMongoConnection$2() {
3353
+ if (!mongoClient$2) {
3354
+ const uri = process.env.MONGO_URI || `mongodb://${process.env.MONGO_HOST || "127.0.0.1"}:27017/cooperative-x`;
3355
+ mongoClient$2 = new mongodb.MongoClient(uri);
3356
+ await mongoClient$2.connect();
3357
+ deltasCollection$1 = mongoClient$2.db().collection("deltas");
3358
+ }
3359
+ return deltasCollection$1;
3360
+ }
3361
+ const draftTableMock = {
3362
+ code: "draft",
3363
+ table: "drafts",
3364
+ data: null,
3365
+ match(url, params) {
3366
+ if (!url.includes("/get-tables") || !params)
3367
+ return false;
3368
+ const filter = params.get("filter");
3369
+ if (!filter)
3370
+ return false;
3371
+ try {
3372
+ const obj = JSON.parse(filter);
3373
+ return obj.code === "draft" && obj.table === "drafts";
3374
+ } catch {
3375
+ return false;
3376
+ }
3377
+ },
3378
+ async resolve(url, params) {
3379
+ const filter = params ? JSON.parse(params.get("filter") || "{}") : {};
3380
+ const deltas = await getMongoConnection$2();
3381
+ const query = { code: "draft", table: "drafts" };
3382
+ if (filter["value.registry_id"])
3383
+ query["value.registry_id"] = filter["value.registry_id"];
3384
+ const results = await deltas.find(query).sort({ block_num: -1 }).toArray();
3385
+ return { results };
3386
+ }
3387
+ };
3388
+ const draftTranslationsMock = {
3389
+ code: "draft",
3390
+ table: "translations",
3391
+ data: null,
3392
+ match(url, params) {
3393
+ if (!url.includes("/get-tables") || !params)
3394
+ return false;
3395
+ const filter = params.get("filter");
3396
+ if (!filter)
3397
+ return false;
3398
+ try {
3399
+ const obj = JSON.parse(filter);
3400
+ return obj.code === "draft" && obj.table === "translations";
3401
+ } catch {
3402
+ return false;
3403
+ }
3404
+ },
3405
+ async resolve(url, params) {
3406
+ const filter = params ? JSON.parse(params.get("filter") || "{}") : {};
3407
+ const deltas = await getMongoConnection$2();
3408
+ let draftId = filter["value.draft_id"];
3409
+ if (draftId) {
3410
+ const draft = await deltas.findOne({
3411
+ code: "draft",
3412
+ table: "drafts",
3413
+ "value.registry_id": draftId
3414
+ });
3415
+ if (draft?.value?.id) {
3416
+ draftId = String(draft.value.id);
3417
+ }
3418
+ }
3419
+ const query = { code: "draft", table: "translations" };
3420
+ if (draftId)
3421
+ query["value.draft_id"] = draftId;
3422
+ const results = await deltas.find(query).sort({ block_num: -1 }).toArray();
3423
+ return { results };
3424
+ }
3425
+ };
3426
+
3427
+ let mongoClient$1 = null;
3428
+ let deltasCollection = null;
3429
+ async function getMongoConnection$1() {
3430
+ if (!mongoClient$1) {
3431
+ const uri = process.env.MONGO_URI || `mongodb://${process.env.MONGO_HOST || "127.0.0.1"}:27017/cooperative-x`;
3432
+ mongoClient$1 = new mongodb.MongoClient(uri);
3433
+ await mongoClient$1.connect();
3434
+ deltasCollection = mongoClient$1.db().collection("deltas");
3435
+ }
3436
+ return deltasCollection;
3437
+ }
3438
+ const cooperativeTableMock = {
3439
+ match(url, params) {
3440
+ if (!url.includes("/get-tables") || !params)
3441
+ return false;
3442
+ const filter = params.get("filter");
3443
+ if (!filter)
3444
+ return false;
3445
+ try {
3446
+ const obj = JSON.parse(filter);
3447
+ return obj.code === "registrator" && obj.table === "coops" || obj.code === "soviet" && obj.table === "boards" || obj.code === "soviet" && obj.table === "decisions" || obj.code === "soviet" && obj.table === "participants";
3448
+ } catch {
3449
+ return false;
3450
+ }
3451
+ },
3452
+ async resolve(url, params) {
3453
+ const filter = params ? JSON.parse(params.get("filter") || "{}") : {};
3454
+ const deltas = await getMongoConnection$1();
3455
+ const query = {};
3456
+ if (filter.code)
3457
+ query.code = filter.code;
3458
+ if (filter.scope)
3459
+ query.scope = filter.scope;
3460
+ if (filter.table)
3461
+ query.table = filter.table;
3462
+ for (const key of Object.keys(filter)) {
3463
+ if (key.startsWith("value."))
3464
+ query[key] = filter[key];
3465
+ }
3466
+ const results = await deltas.find(query).sort({ block_num: -1 }).toArray();
3467
+ return { results };
3468
+ }
3469
+ };
3470
+
3350
3471
  meetTableMock.match = function(url, params) {
3351
3472
  return matchMock(this, url, params);
3352
3473
  };
3353
3474
  meetQuestionsMock.match = function(url, params) {
3354
3475
  return matchMock(this, url, params);
3355
3476
  };
3356
- const tablesMocks = [meetTableMock, meetQuestionsMock];
3477
+ const tablesMocks = [
3478
+ draftTableMock,
3479
+ draftTranslationsMock,
3480
+ cooperativeTableMock,
3481
+ meetTableMock,
3482
+ meetQuestionsMock
3483
+ ];
3357
3484
 
3358
3485
  const voteForAction = {
3359
3486
  _id: "6852b0389bd56240ad71b225",
@@ -3511,6 +3638,50 @@ const voteAgainstDecision9001Mock = {
3511
3638
  match: (_url, _params) => false
3512
3639
  };
3513
3640
 
3641
+ let mongoClient = null;
3642
+ let actionsCollection = null;
3643
+ async function getMongoConnection() {
3644
+ if (!mongoClient) {
3645
+ const uri = process.env.MONGO_URI || `mongodb://${process.env.MONGO_HOST || "127.0.0.1"}:27017/cooperative-x`;
3646
+ mongoClient = new mongodb.MongoClient(uri);
3647
+ await mongoClient.connect();
3648
+ actionsCollection = mongoClient.db().collection("actions");
3649
+ }
3650
+ return actionsCollection;
3651
+ }
3652
+ const dbActionsMock = {
3653
+ match(url, params) {
3654
+ if (!url.includes("/get-actions") || !params)
3655
+ return false;
3656
+ const filter = params.get("filter");
3657
+ if (!filter)
3658
+ return false;
3659
+ try {
3660
+ JSON.parse(filter);
3661
+ return true;
3662
+ } catch {
3663
+ return false;
3664
+ }
3665
+ },
3666
+ async resolve(url, params) {
3667
+ const filter = params ? JSON.parse(params.get("filter") || "{}") : {};
3668
+ const actions = await getMongoConnection();
3669
+ const query = {};
3670
+ if (filter.account)
3671
+ query.account = filter.account;
3672
+ if (filter.name)
3673
+ query.name = filter.name;
3674
+ if (filter.receiver)
3675
+ query.receiver = filter.receiver;
3676
+ for (const key of Object.keys(filter)) {
3677
+ if (key.startsWith("data."))
3678
+ query[key] = filter[key];
3679
+ }
3680
+ const results = await actions.find(query).sort({ block_num: -1 }).toArray();
3681
+ return { results };
3682
+ }
3683
+ };
3684
+
3514
3685
  voteforActionMock.match = function(url, params) {
3515
3686
  return matchMock(this, url, params);
3516
3687
  };
@@ -3527,7 +3698,8 @@ const actionsMocks = [
3527
3698
  voteforActionMock,
3528
3699
  returnByMoneyDecisionActionMock,
3529
3700
  voteForDecision9001Mock,
3530
- voteAgainstDecision9001Mock
3701
+ voteAgainstDecision9001Mock,
3702
+ dbActionsMock
3531
3703
  ];
3532
3704
 
3533
3705
  const testMocks = [
@@ -3538,8 +3710,11 @@ const testMocks = [
3538
3710
  async function getFetch(url, params) {
3539
3711
  if (process.env.NODE_ENV === "test") {
3540
3712
  const mock = testMocks.find((m) => m.match(url, params));
3541
- if (mock)
3713
+ if (mock) {
3714
+ if (mock.resolve)
3715
+ return await mock.resolve(url, params);
3542
3716
  return mock.data;
3717
+ }
3543
3718
  }
3544
3719
  try {
3545
3720
  const response = await fetch(`${url}?${params?.toString()}`, {
@@ -3981,8 +4156,8 @@ class PDFService {
3981
4156
 
3982
4157
  const name = "@coopenomics/factory";
3983
4158
  const type = "module";
3984
- const version = "2026.2.22-2";
3985
- const description = "";
4159
+ const version = "2026.3.4-2";
4160
+ const description = "Фабрика юридических документов кооператива";
3986
4161
  const author = "Alex Ant <chairman.voskhod@gmail.com>";
3987
4162
  const license = "MIT";
3988
4163
  const homepage = "https://github.com/copenomics/@coopenomics/factory#readme";
@@ -4018,7 +4193,7 @@ const files = [
4018
4193
  const scripts = {
4019
4194
  prebuild: "pnpm run typecheck",
4020
4195
  build: "unbuild",
4021
- dev: "concurrently -n 'GENERATOR' -c 'bgGreen.black' \"nodemon --watch src --ext ts --exec 'unbuild'\"",
4196
+ dev: "nodemon --watch src --ext ts --exec 'unbuild'",
4022
4197
  lint: "eslint .",
4023
4198
  prepublishOnly: "nr build",
4024
4199
  release: "bumpp && npm publish",
@@ -4030,15 +4205,15 @@ const dependencies = {
4030
4205
  ajv: "^8.13.0",
4031
4206
  "ajv-formats": "^3.0.1",
4032
4207
  "ajv-i18n": "^4.2.0",
4033
- axios: "^1.7.2",
4034
- cooptypes: "2026.2.22-2",
4208
+ axios: "^1.13.5",
4209
+ cooptypes: "2026.3.4-2",
4035
4210
  dotenv: "^16.4.5",
4036
4211
  "eosjs-ecc": "^4.0.7",
4037
4212
  handlebars: "^4.7.8",
4038
4213
  "inline-css": "^4.0.2",
4039
4214
  "json-schema": "^0.4.0",
4040
4215
  "moment-timezone": "^0.5.45",
4041
- mongodb: "^6.6.1",
4216
+ mongodb: "^7.1.0",
4042
4217
  nunjucks: "^3.2.4",
4043
4218
  "pdf-lib": "^1.17.1",
4044
4219
  uuid: "^11.0.0"
@@ -4054,6 +4229,7 @@ const devDependencies = {
4054
4229
  concurrently: "^8.2.2",
4055
4230
  eslint: "^8.57.0",
4056
4231
  esno: "^4.7.0",
4232
+ nodemon: "^3.1.4",
4057
4233
  pnpm: "^8.15.8",
4058
4234
  rimraf: "^5.0.7",
4059
4235
  "simple-git-hooks": "^2.11.1",
@@ -4062,7 +4238,7 @@ const devDependencies = {
4062
4238
  vite: "^5.2.11",
4063
4239
  vitest: "^1.6.0"
4064
4240
  };
4065
- const gitHead = "350af86b650d74a2e1f63e1e9f725a63cb89bbcf";
4241
+ const gitHead = "dc450899081b4d9c022e5a091b49f8ca7f8a1fbd";
4066
4242
  const packageJson = {
4067
4243
  name: name,
4068
4244
  type: type,