eimzo-ts 0.0.0-stage → 0.1.1
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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +230 -2
- package/dist/index.cjs +707 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +357 -0
- package/dist/index.d.ts +357 -0
- package/dist/index.global.js +685 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +660 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Формат — [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/), версии — [SemVer](https://semver.org/lang/ru/).
|
|
4
|
+
До 1.0.0 минорная версия может менять API.
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.1.1]
|
|
9
|
+
|
|
10
|
+
### Исправлено
|
|
11
|
+
|
|
12
|
+
- «Отмена» в окне пароля E-IMZO (`status -5000`) теперь даёт `CANCELLED`, а не `EIMZO_ERROR`.
|
|
13
|
+
|
|
14
|
+
## [0.1.0]
|
|
15
|
+
|
|
16
|
+
Первая версия.
|
|
17
|
+
|
|
18
|
+
### Добавлено
|
|
19
|
+
|
|
20
|
+
- `createEimzo()` / `Eimzo`: `init`, `getVersion`, `listKeys`, `listDevices`, `loadKey`, `unloadKey`,
|
|
21
|
+
`sign`, `signFile`, `login`, `signDocument`, `call`.
|
|
22
|
+
- Ключи PFX, ID-карта, BAIK-токен, CKC; токены `ftjc` для E-IMZO старше 4.12.
|
|
23
|
+
- `createFetchBackend()` — challenge, вход и метка времени через ваш backend в формате e-imzo-server.
|
|
24
|
+
- `EimzoError` с кодами ошибок, сообщения на русском, узбекском (латиница и кириллица),
|
|
25
|
+
каракалпакском и английском; свои тексты через `messages`.
|
|
26
|
+
- Повторная загрузка ключа, если E-IMZO забыл `keyId` (`autoReloadKey`).
|
|
27
|
+
- Очередь: операции с окном пароля выполняются по одной.
|
|
28
|
+
- Сборки ESM, CJS и IIFE (`EimzoClient`) с типами.
|
|
29
|
+
- Демо с режимом без E-IMZO и просмотром содержимого PKCS#7.
|
|
30
|
+
|
|
31
|
+
[Unreleased]: https://github.com/abduazimkhuja/eimzo-ts/compare/v0.1.1...HEAD
|
|
32
|
+
[0.1.1]: https://github.com/abduazimkhuja/eimzo-ts/compare/v0.1.0...v0.1.1
|
|
33
|
+
[0.1.0]: https://github.com/abduazimkhuja/eimzo-ts/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abduazimkhoja
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,231 @@
|
|
|
1
|
-
#
|
|
1
|
+
# eimzo-ts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/eimzo-ts)
|
|
4
|
+
[](https://github.com/abduazimkhuja/eimzo-ts/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
TypeScript-клиент для [E-IMZO](https://e-imzo.uz): список ключей ЭЦП, подпись PKCS#7, вход по ЭЦП.
|
|
8
|
+
Без зависимостей и без привязки к фреймворку: Angular, React, Vue, Svelte или обычная страница.
|
|
9
|
+
|
|
10
|
+
Заменяет `e-imzo.js` и `e-imzo-client.js` из официального примера. Вызовы E-IMZO те же, но
|
|
11
|
+
на Promise, с типами, без глобальных переменных и без правок `String.prototype`/`Date.prototype`.
|
|
12
|
+
|
|
13
|
+
> **Неофициальная библиотека.** Не связана с разработчиками E-IMZO.
|
|
14
|
+
|
|
15
|
+
**[Демо](https://abduazimkhuja.github.io/eimzo-ts/)** работает и без E-IMZO, на вымышленных ключах.
|
|
16
|
+
|
|
17
|
+
Только для ПК: подпись выполняет приложение E-IMZO на компьютере пользователя. Мобильный
|
|
18
|
+
вход (ID-CARD E-IMZO MOBILE) пока не поддерживается.
|
|
19
|
+
|
|
20
|
+
## Установка
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install eimzo-ts
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Без сборщика:
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<script src="https://unpkg.com/eimzo-ts@0.1.1/dist/index.global.js"></script>
|
|
30
|
+
<script>
|
|
31
|
+
const eimzo = EimzoClient.createEimzo({ apiKeys: EimzoClient.DEV_API_KEYS });
|
|
32
|
+
</script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Быстрый старт
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { createEimzo, createFetchBackend, DEV_API_KEYS } from 'eimzo-ts';
|
|
39
|
+
|
|
40
|
+
const eimzo = createEimzo({
|
|
41
|
+
apiKeys: { ...DEV_API_KEYS, 'my-site.uz': 'API-KEY от НИЦ НТ' },
|
|
42
|
+
backend: createFetchBackend({
|
|
43
|
+
challengeUrl: '/api/eimzo/challenge',
|
|
44
|
+
loginUrl: '/api/eimzo/login',
|
|
45
|
+
timestampUrl: '/api/eimzo/timestamp',
|
|
46
|
+
init: { credentials: 'include' }, // заголовки, CSRF и т.п.
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const keys = await eimzo.listKeys(); // ключи пользователя
|
|
51
|
+
const user = await eimzo.login(keys[0]); // вход: пароль спросит окно E-IMZO
|
|
52
|
+
const pkcs7 = await eimzo.signDocument('документ'); // подпись + метка времени
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Создавайте **один** экземпляр на всё приложение: он хранит `keyId` загруженного ключа.
|
|
56
|
+
|
|
57
|
+
## API
|
|
58
|
+
|
|
59
|
+
| Метод | Что делает |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `init()` | Проверяет версию E-IMZO и устанавливает API-ключи. Вызывается сам перед любой операцией. |
|
|
62
|
+
| `getVersion()` | Версия установленного E-IMZO. |
|
|
63
|
+
| `listKeys()` | Ключи (`EimzoKey`): `CN`, `TIN`, `PINFL`, `O`, `T`, `validFrom`, `validTo`, `expired`. |
|
|
64
|
+
| `listDevices()` | Подключены ли ID-карта, BAIK-токен, CKC: `{ idcard, baikey, ckc }`. |
|
|
65
|
+
| `loadKey(key \| 'idcard' \| 'baikey' \| 'ckc')` | Загружает ключ, запоминает `keyId`. Предыдущий ключ выгружается. |
|
|
66
|
+
| `sign(data, { detached, base64, keyId })` | PKCS#7 в base64. `data` — строка, `Uint8Array` или `ArrayBuffer`. |
|
|
67
|
+
| `signFile(file, { detached, maxSize })` | Подпись `File`/`Blob`. По умолчанию detached, не больше 10 МБ. |
|
|
68
|
+
| `login(key)` | Вход: challenge → `loadKey` → подпись → `backend.login`. |
|
|
69
|
+
| `signDocument(data, options)` | `sign` → `backend.timestamp`. |
|
|
70
|
+
| `unloadKey()` | Выгружает ключ из E-IMZO. Вызывайте при выходе пользователя. |
|
|
71
|
+
| `call(plugin, name, args)` | Любая функция E-IMZO, см. `http://127.0.0.1:64646/apidoc.html`. |
|
|
72
|
+
| `setLocale(locale)`, `t(error)` | Язык сообщений об ошибках. |
|
|
73
|
+
|
|
74
|
+
### Опции `createEimzo`
|
|
75
|
+
|
|
76
|
+
| Опция | По умолчанию | |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `apiKeys` | — | `{ домен: ключ }` или `[домен, ключ, ...]`. Для localhost — `DEV_API_KEYS`. |
|
|
79
|
+
| `backend` | — | `createFetchBackend(...)` или свой объект, см. ниже. |
|
|
80
|
+
| `minVersion` | `'4.86'` | Ниже — ошибка `OUTDATED_VERSION`. |
|
|
81
|
+
| `locale` | `'ru'` | `'ru'`, `'uz-Latn'`, `'uz-Cyrl'`, `'kaa'`, `'en'` или свой код. |
|
|
82
|
+
| `messages` | — | Свои тексты ошибок. |
|
|
83
|
+
| `autoReloadKey` | `true` | Если E-IMZO забыл `keyId`, загрузить ключ заново и повторить подпись. |
|
|
84
|
+
| `timeoutMs` | без ограничения | Таймаут вызова E-IMZO. |
|
|
85
|
+
| `url`, `WebSocketImpl`, `transport` | | Для тестов и нестандартного окружения. |
|
|
86
|
+
|
|
87
|
+
## Ошибки
|
|
88
|
+
|
|
89
|
+
Все ошибки — `EimzoError`. Для логики используйте `code`, для показа — `message`
|
|
90
|
+
(уже на языке `locale`) или `eimzo.t(error)`.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { isEimzoError } from 'eimzo-ts';
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
await eimzo.login(key);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
if (isEimzoError(e, 'CANCELLED')) return; // пользователь закрыл окно
|
|
99
|
+
if (isEimzoError(e, 'NOT_INSTALLED')) showDownloadLink();
|
|
100
|
+
else showError(eimzo.t(e));
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| `code` | Когда |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `NOT_INSTALLED` | E-IMZO не установлен или не запущен. |
|
|
107
|
+
| `OUTDATED_VERSION` | Версия ниже `minVersion`; в `params` — `installed` и `min`. |
|
|
108
|
+
| `API_KEY_REJECTED` | Нет API-KEY для домена страницы. |
|
|
109
|
+
| `WRONG_PASSWORD` | Неверный пароль ключа. |
|
|
110
|
+
| `CANCELLED` | Пользователь закрыл окно пароля. |
|
|
111
|
+
| `KEY_NOT_FOUND` | E-IMZO не знает `keyId` (прошло 24 часа, перезапуск E-IMZO). |
|
|
112
|
+
| `KEY_NOT_LOADED` | `sign()` до `loadKey()` — ошибка в коде. |
|
|
113
|
+
| `FILE_TOO_LARGE` | Файл больше `maxSize`. |
|
|
114
|
+
| `BACKEND_NOT_CONFIGURED` | Нет нужного метода в `backend` — ошибка в коде. |
|
|
115
|
+
| `BACKEND_ERROR` | Backend ответил ошибкой; `httpStatus`, `params.detail`. |
|
|
116
|
+
| `EIMZO_ERROR` | Прочие ошибки E-IMZO; исходный текст в `reason`, код в `status`. |
|
|
117
|
+
| `TIMEOUT`, `WEBSOCKET_UNSUPPORTED` | Редкие случаи. |
|
|
118
|
+
|
|
119
|
+
### Языки
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const eimzo = createEimzo({
|
|
123
|
+
locale: 'uz-Latn',
|
|
124
|
+
messages: {
|
|
125
|
+
'uz-Latn': { WRONG_PASSWORD: () => 'Parol xato, qayta urinib ko‘ring.' }, // заменить один текст
|
|
126
|
+
tg: { NOT_INSTALLED: () => '...' }, // добавить язык
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
eimzo.setLocale('en');
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Недостающие тексты берутся из русского. Без экземпляра: `formatEimzoError(error, 'en')`.
|
|
133
|
+
|
|
134
|
+
## Пароль
|
|
135
|
+
|
|
136
|
+
- Пароль вводится в окне E-IMZO; сайт его не видит.
|
|
137
|
+
- Без флажка «Запомнить на 6 час.» E-IMZO помнит пароль 1 минуту, с флажком — 6 часов.
|
|
138
|
+
Запоминается для `keyId`, поэтому держите один экземпляр `Eimzo`.
|
|
139
|
+
- После перезагрузки страницы `keyId` теряется — снова нужен `loadKey()`.
|
|
140
|
+
- `unloadKey()` при выходе из аккаунта стирает запомненный пароль на этом компьютере.
|
|
141
|
+
- Несколько вызовов `sign()` подряд (двойной клик) выполняются по очереди, а не открывают два окна.
|
|
142
|
+
|
|
143
|
+
## Backend
|
|
144
|
+
|
|
145
|
+
Браузер не обращается к e-imzo-server напрямую. `createFetchBackend` ждёт ответы в формате
|
|
146
|
+
e-imzo-server, поэтому backend может проксировать запросы:
|
|
147
|
+
|
|
148
|
+
| Запрос клиента | Backend | Ответ |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `GET challengeUrl` | `GET /frontend/challenge`, без кеша | `{ "status": 1, "challenge": "..." }` |
|
|
151
|
+
| `POST loginUrl`, тело — PKCS#7 | `POST /backend/auth`, поиск пользователя по ПИНФЛ/ИНН, сессия | любой JSON — вернётся из `login()` |
|
|
152
|
+
| `POST timestampUrl`, тело — PKCS#7 | `POST /frontend/timestamp/pkcs7` | `{ "status": 1, "pkcs7b64": "..." }` |
|
|
153
|
+
|
|
154
|
+
Подробно, с примером на Java (Spring Boot): [docs/backend.md](docs/backend.md).
|
|
155
|
+
|
|
156
|
+
Другой формат — свой объект вместо `createFetchBackend`:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
createEimzo({
|
|
160
|
+
backend: {
|
|
161
|
+
getChallenge: () => api.get('/challenge').then((r) => r.data.value),
|
|
162
|
+
login: (pkcs7) => api.post('/login', { pkcs7 }).then((r) => r.data),
|
|
163
|
+
timestamp: (pkcs7) => api.post('/timestamp', { pkcs7 }).then((r) => r.data.pkcs7),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Фреймворки
|
|
169
|
+
|
|
170
|
+
**Angular**
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
@Injectable({ providedIn: 'root' })
|
|
174
|
+
export class EimzoService {
|
|
175
|
+
readonly client = createEimzo({ apiKeys: environment.eimzoApiKeys, backend: createFetchBackend({ /* ... */ }) });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// компонент
|
|
179
|
+
keys = signal<EimzoKey[]>([]);
|
|
180
|
+
async ngOnInit() { this.keys.set(await this.eimzo.client.listKeys()); }
|
|
181
|
+
async login(key: EimzoKey) { await this.eimzo.client.login(key); this.router.navigate(['/cabinet']); }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Для `HttpClient` с интерсепторами передайте свой `backend` через `firstValueFrom(this.http.get(...))`.
|
|
185
|
+
|
|
186
|
+
**React**
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
export const eimzo = createEimzo({ /* ... */ }); // модуль-синглтон
|
|
190
|
+
|
|
191
|
+
function KeyPicker() {
|
|
192
|
+
const [keys, setKeys] = useState<EimzoKey[]>([]);
|
|
193
|
+
const [error, setError] = useState<string>();
|
|
194
|
+
useEffect(() => { eimzo.listKeys().then(setKeys, (e) => setError(eimzo.t(e))); }, []);
|
|
195
|
+
// ...
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Vue**
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
export const eimzo = createEimzo({ /* ... */ });
|
|
203
|
+
|
|
204
|
+
const keys = ref<EimzoKey[]>([]);
|
|
205
|
+
onMounted(async () => { keys.value = await eimzo.listKeys(); });
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## API-ключи
|
|
209
|
+
|
|
210
|
+
- `localhost` и `127.0.0.1` — `DEV_API_KEYS` (публичные ключи из официального примера).
|
|
211
|
+
- Другой домен или IP при разработке — «Режим разработчика» в меню E-IMZO в трее.
|
|
212
|
+
- Production — API-KEY выдаёт НИЦ НТ, отдельно на каждый домен и поддомен.
|
|
213
|
+
|
|
214
|
+
## Переход с e-imzo.js
|
|
215
|
+
|
|
216
|
+
Соответствие функций `EIMZOClient` и этой библиотеки: [docs/migration.md](docs/migration.md).
|
|
217
|
+
|
|
218
|
+
## Разработка
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
npm install
|
|
222
|
+
npm test # vitest
|
|
223
|
+
npm run typecheck
|
|
224
|
+
npm run build # dist/: ESM, CJS, IIFE + .d.ts
|
|
225
|
+
npm run demo # http://localhost:3000/demo/
|
|
226
|
+
npm run check:eimzo # проверка на установленном E-IMZO; --interactive N — с подписью ключом N
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Лицензия
|
|
230
|
+
|
|
231
|
+
[MIT](LICENSE)
|