eimzo-ts 0.1.1 → 0.1.3

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.
@@ -0,0 +1,43 @@
1
+ # Using eimzo-ts with AI coding tools
2
+
3
+ Resources that help AI assistants (Claude Code, Cursor, Codex, Gemini CLI, ChatGPT and others) use this
4
+ library correctly. None of them is discovered automatically unless your tool is configured to read it.
5
+
6
+ | Resource | What it is | Where |
7
+ |---|---|---|
8
+ | `llms.txt` | Index of the documentation in the [llms.txt](https://llmstxt.org/) format | https://abduazimkhuja.github.io/eimzo-ts/llms.txt |
9
+ | `llms-full.txt` | README, backend guide and skill references in one text file | https://abduazimkhuja.github.io/eimzo-ts/llms-full.txt |
10
+ | Agent Skill | Task instructions in the [Agent Skills](https://agentskills.io/specification) format | [`skills/eimzo-ts/`](skills/eimzo-ts/SKILL.md), also inside the npm package |
11
+ | `AGENTS.md` | Rules for agents working on this repository or integrating the library | [`AGENTS.md`](AGENTS.md) |
12
+ | Type declarations | Full TypeScript API with JSDoc (comments in Russian) | `node_modules/eimzo-ts/dist/index.d.ts` |
13
+
14
+ ## Installing the Agent Skill
15
+
16
+ The skill is the folder `skills/eimzo-ts/` (a `SKILL.md` plus `references/`). Copy it into the skills
17
+ directory your tool reads. After `npm install eimzo-ts` it is available at
18
+ `node_modules/eimzo-ts/skills/eimzo-ts/`.
19
+
20
+ Claude Code (project-level):
21
+
22
+ ```bash
23
+ mkdir -p .claude/skills
24
+ cp -r node_modules/eimzo-ts/skills/eimzo-ts .claude/skills/
25
+ ```
26
+
27
+ Claude Code (user-level, all projects): copy to `~/.claude/skills/eimzo-ts/` instead.
28
+
29
+ Other tools that support Agent Skills: copy the same folder into the location described in that tool's
30
+ documentation. The folder name must stay `eimzo-ts` (it must match `name` in `SKILL.md`).
31
+
32
+ Without skill support, point the assistant to the file directly, e.g.
33
+ "Read node_modules/eimzo-ts/skills/eimzo-ts/SKILL.md and follow it."
34
+
35
+ ## Prompts that work well
36
+
37
+ - "Add login with E-IMZO to this app using eimzo-ts. Backend endpoints: …"
38
+ - "Replace e-imzo.js / e-imzo-client.js with eimzo-ts, keep the same UI."
39
+ - "Add a 'Sign' button that signs the order JSON with a timestamp and posts it to /api/orders/:id/sign."
40
+ - "eimzo-ts throws API_KEY_REJECTED on staging — what is wrong?"
41
+
42
+ Ask the assistant to also implement or review the backend: the library creates signatures, but only the
43
+ server (via e-imzo-server) can verify them.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.1.3]
9
+
10
+ ### Документация
11
+
12
+ - README на английском (русский — `README.ru.md`), сайт документации с примерами, FAQ и разбором ошибок.
13
+ - `llms.txt`, `llms-full.txt`, `AGENTS.md`, Agent Skill `skills/eimzo-ts/` (входит в npm-пакет), `AI_INTEGRATION.md`.
14
+
15
+ ## [0.1.2]
16
+
17
+ ### Исправлено
18
+
19
+ - Неверный пароль в E-IMZO 6.3 (`status -9999`, «Sequence tag error» / «Password is not ASCII»)
20
+ теперь даёт `WRONG_PASSWORD`, а не `EIMZO_ERROR`.
21
+
8
22
  ## [0.1.1]
9
23
 
10
24
  ### Исправлено
@@ -28,6 +42,8 @@
28
42
  - Сборки ESM, CJS и IIFE (`EimzoClient`) с типами.
29
43
  - Демо с режимом без E-IMZO и просмотром содержимого PKCS#7.
30
44
 
31
- [Unreleased]: https://github.com/abduazimkhuja/eimzo-ts/compare/v0.1.1...HEAD
45
+ [Unreleased]: https://github.com/abduazimkhuja/eimzo-ts/compare/v0.1.3...HEAD
46
+ [0.1.3]: https://github.com/abduazimkhuja/eimzo-ts/compare/v0.1.2...v0.1.3
47
+ [0.1.2]: https://github.com/abduazimkhuja/eimzo-ts/compare/v0.1.1...v0.1.2
32
48
  [0.1.1]: https://github.com/abduazimkhuja/eimzo-ts/compare/v0.1.0...v0.1.1
33
49
  [0.1.0]: https://github.com/abduazimkhuja/eimzo-ts/releases/tag/v0.1.0
package/README.md CHANGED
@@ -1,93 +1,100 @@
1
- # eimzo-ts
1
+ # eimzo-ts — TypeScript client for E-IMZO
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/eimzo-ts)](https://www.npmjs.com/package/eimzo-ts)
4
4
  [![CI](https://github.com/abduazimkhuja/eimzo-ts/actions/workflows/ci.yml/badge.svg)](https://github.com/abduazimkhuja/eimzo-ts/actions/workflows/ci.yml)
5
5
  [![license](https://img.shields.io/npm/l/eimzo-ts)](LICENSE)
6
6
 
7
- TypeScript-клиент для [E-IMZO](https://e-imzo.uz): список ключей ЭЦП, подпись PKCS#7, вход по ЭЦП.
8
- Без зависимостей и без привязки к фреймворку: Angular, React, Vue, Svelte или обычная страница.
7
+ **English** · [Русский](README.ru.md)
9
8
 
10
- Заменяет `e-imzo.js` и `e-imzo-client.js` из официального примера. Вызовы E-IMZO те же, но
11
- на Promise, с типами, без глобальных переменных и без правок `String.prototype`/`Date.prototype`.
9
+ `eimzo-ts` lets a web page talk to [E-IMZO](https://e-imzo.uz), the desktop application used in
10
+ Uzbekistan for electronic digital signatures (ЭЦП / ERI). With it you can list the user's signing keys,
11
+ create PKCS#7 (CMS) signatures, and log users in with their digital signature.
12
12
 
13
- > **Неофициальная библиотека.** Не связана с разработчиками E-IMZO.
13
+ It replaces `e-imzo.js` and `e-imzo-client.js` from the official E-IMZO example with a typed,
14
+ Promise-based API: no global variables, no changes to `String.prototype` or `Date.prototype`,
15
+ zero runtime dependencies.
14
16
 
15
- **[Демо](https://abduazimkhuja.github.io/eimzo-ts/)** работает и без E-IMZO, на вымышленных ключах.
17
+ > **Unofficial.** This project is not affiliated with the developers of E-IMZO.
16
18
 
17
- Только для ПК: подпись выполняет приложение E-IMZO на компьютере пользователя. Мобильный
18
- вход (ID-CARD E-IMZO MOBILE) пока не поддерживается.
19
+ [Documentation](https://abduazimkhuja.github.io/eimzo-ts/) ·
20
+ [Live demo](https://abduazimkhuja.github.io/eimzo-ts/demo/) (works without E-IMZO, using mock keys) ·
21
+ [npm](https://www.npmjs.com/package/eimzo-ts) ·
22
+ [Changelog](CHANGELOG.md)
19
23
 
20
- ## Установка
24
+ ## Use cases
25
+
26
+ - **Login with a digital signature.** Get a one-time challenge from your backend, sign it with the user's key,
27
+ and send the signature to your backend for verification.
28
+ - **Sign a document or form** (attached or detached PKCS#7), optionally with a trusted timestamp.
29
+ - **Sign a file** picked with `<input type="file">`.
30
+ - **Show the user's keys:** owner name, TIN (INN), PINFL, organization, validity, and an `expired` flag.
31
+ - **Migrate from `e-imzo.js` / `e-imzo-client.js`** callbacks to `async`/`await` ([migration guide](docs/migration.md), in Russian).
32
+
33
+ ## How it works
34
+
35
+ ```
36
+ your page (eimzo-ts) ──WebSocket──► E-IMZO on the user's PC (asks for the key password)
37
+ │
38
+ └── fetch ──► your backend ──► e-imzo-server (verifies the signature)
39
+ ```
40
+
41
+ The library runs in the **browser**. It connects to E-IMZO at `ws://127.0.0.1:64646` (pages served over
42
+ http) or `wss://127.0.0.1:64443` (https). If the `EIMZOEXT` browser extension object is present on the
43
+ page, it is used instead. The password is typed into E-IMZO's own window; your page never sees it.
44
+
45
+ Signatures are **verified on the server** by e-imzo-server, not by this library.
46
+
47
+ ## Installation
21
48
 
22
49
  ```bash
23
50
  npm install eimzo-ts
24
51
  ```
25
52
 
26
- Без сборщика:
53
+ Without a bundler, use the IIFE build. It exposes a global `EimzoClient`:
27
54
 
28
55
  ```html
29
- <script src="https://unpkg.com/eimzo-ts@0.1.1/dist/index.global.js"></script>
56
+ <script src="https://unpkg.com/eimzo-ts@0.1/dist/index.global.js"></script>
30
57
  <script>
31
58
  const eimzo = EimzoClient.createEimzo({ apiKeys: EimzoClient.DEV_API_KEYS });
32
59
  </script>
33
60
  ```
34
61
 
35
- ## Быстрый старт
62
+ The package ships ESM, CommonJS, and IIFE builds with TypeScript declarations. It is side-effect free.
63
+
64
+ ## Quick start
36
65
 
37
66
  ```ts
38
67
  import { createEimzo, createFetchBackend, DEV_API_KEYS } from 'eimzo-ts';
39
68
 
40
69
  const eimzo = createEimzo({
41
- apiKeys: { ...DEV_API_KEYS, 'my-site.uz': 'API-KEY от НИЦ НТ' },
70
+ apiKeys: { ...DEV_API_KEYS, 'my-site.uz': 'API key for my-site.uz' },
42
71
  backend: createFetchBackend({
43
72
  challengeUrl: '/api/eimzo/challenge',
44
73
  loginUrl: '/api/eimzo/login',
45
74
  timestampUrl: '/api/eimzo/timestamp',
46
- init: { credentials: 'include' }, // заголовки, CSRF и т.п.
75
+ init: { credentials: 'include' }, // headers, CSRF token, etc.
47
76
  }),
48
77
  });
49
78
 
50
- const keys = await eimzo.listKeys(); // ключи пользователя
51
- const user = await eimzo.login(keys[0]); // вход: пароль спросит окно E-IMZO
52
- const pkcs7 = await eimzo.signDocument('документ'); // подпись + метка времени
79
+ const keys = await eimzo.listKeys(); // the user's keys
80
+ const user = await eimzo.login(keys[0]); // E-IMZO asks for the password
81
+ const pkcs7 = await eimzo.signDocument('document'); // signature + timestamp
53
82
  ```
54
83
 
55
- Создавайте **один** экземпляр на всё приложение: он хранит `keyId` загруженного ключа.
84
+ Create **one** instance per application. It keeps the `keyId` of the loaded key, and E-IMZO remembers
85
+ the password per `keyId`.
56
86
 
57
- ## API
87
+ ### Sign without a backend
58
88
 
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
- ## Ошибки
89
+ ```ts
90
+ const keys = await eimzo.listKeys();
91
+ await eimzo.loadKey(keys.find((k) => !k.expired)!);
92
+ const pkcs7 = await eimzo.sign('text to sign'); // attached PKCS#7, base64
93
+ const detached = await eimzo.sign(bytes, { detached: true }); // Uint8Array or ArrayBuffer
94
+ const filePkcs7 = await eimzo.signFile(input.files[0]); // detached by default, max 10 MB
95
+ ```
88
96
 
89
- Все ошибки — `EimzoError`. Для логики используйте `code`, для показа — `message`
90
- (уже на языке `locale`) или `eimzo.t(error)`.
97
+ ### Handle errors
91
98
 
92
99
  ```ts
93
100
  import { isEimzoError } from 'eimzo-ts';
@@ -95,127 +102,173 @@ import { isEimzoError } from 'eimzo-ts';
95
102
  try {
96
103
  await eimzo.login(key);
97
104
  } catch (e) {
98
- if (isEimzoError(e, 'CANCELLED')) return; // пользователь закрыл окно
99
- if (isEimzoError(e, 'NOT_INSTALLED')) showDownloadLink();
100
- else showError(eimzo.t(e));
105
+ if (isEimzoError(e, 'CANCELLED')) return; // the user closed the password window
106
+ if (isEimzoError(e, 'NOT_INSTALLED')) showInstallHint();
107
+ else showError(eimzo.t(e)); // localized message
101
108
  }
102
109
  ```
103
110
 
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
- ### Языки
111
+ ## Features
120
112
 
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
- ```
113
+ - Typed API for keys, signing, login, and timestamps; Promise-based.
114
+ - PFX keys, ID card, BAIK token, and CKC devices (`listDevices`, `loadKey('idcard' | 'baikey' | 'ckc')`).
115
+ - Typed `EimzoError` with a stable `code`. E-IMZO's raw `reason` and `status` are kept.
116
+ - Error messages in Russian, Uzbek (Latin and Cyrillic), Karakalpak, and English; you can override any text.
117
+ - Concurrent `sign()` calls are queued, so a double click does not open two password windows.
118
+ - If E-IMZO has forgotten the `keyId` (24 hours passed, or E-IMZO restarted), the key is loaded again and
119
+ the signature is retried (`autoReloadKey`).
120
+ - `unloadKey()` on logout removes the key and its remembered password from E-IMZO.
121
+ - `createFetchBackend()` matches e-imzo-server's response format, so your backend can proxy it as-is.
122
+ - Zero runtime dependencies; ESM, CJS, and IIFE builds; `.d.ts` included.
131
123
 
132
- Недостающие тексты берутся из русского. Без экземпляра: `formatEimzoError(error, 'en')`.
124
+ Behaviour for cancel, wrong password, signing, and unloading was checked against E-IMZO 6.3.5.
133
125
 
134
- ## Пароль
126
+ ## API
135
127
 
136
- - Пароль вводится в окне E-IMZO; сайт его не видит.
137
- - Без флажка «Запомнить на 6 час.» E-IMZO помнит пароль 1 минуту, с флажком — 6 часов.
138
- Запоминается для `keyId`, поэтому держите один экземпляр `Eimzo`.
139
- - После перезагрузки страницы `keyId` теряется — снова нужен `loadKey()`.
140
- - `unloadKey()` при выходе из аккаунта стирает запомненный пароль на этом компьютере.
141
- - Несколько вызовов `sign()` подряд (двойной клик) выполняются по очереди, а не открывают два окна.
128
+ | Method | Description |
129
+ |---|---|
130
+ | `createEimzo(options)` | Creates a client. See options below. |
131
+ | `init()` | Checks the E-IMZO version and installs API keys. Called automatically. |
132
+ | `getVersion()` | Installed E-IMZO version, `{ major, minor }`. |
133
+ | `listKeys()` | User keys as `EimzoKey[]`: `CN`, `TIN`, `PINFL`, `O`, `T`, `validFrom`, `validTo`, `expired`. Keys without TIN and PINFL are skipped. |
134
+ | `listDevices()` | `{ idcard, baikey, ckc }` — which hardware devices are connected. |
135
+ | `loadKey(key, { verifyPassword })` | Loads a key (or `'idcard' \| 'baikey' \| 'ckc'`) and stores its `keyId`. Unloads the previous key. |
136
+ | `sign(data, { detached, base64, keyId })` | Returns a base64 PKCS#7. `data`: `string`, `Uint8Array`, or `ArrayBuffer`. |
137
+ | `signFile(blob, { detached, maxSize, keyId })` | Signs a `File` or `Blob`. Detached by default, 10 MB limit. |
138
+ | `login(key)` | Challenge → `loadKey` → sign → `backend.login`. Returns what `backend.login` returns. |
139
+ | `signDocument(data, options)` | `sign` → `backend.timestamp`. |
140
+ | `unloadKey()` | Unloads the key from E-IMZO. Call it on logout. |
141
+ | `call(plugin, name, args)` | Calls any E-IMZO function (see `http://127.0.0.1:64646/apidoc.html`). |
142
+ | `keyId`, `locale`, `setLocale(locale)`, `t(error)` | Current key and language of error messages. |
142
143
 
143
- ## Backend
144
+ Other exports: `createFetchBackend`, `EimzoError`, `isEimzoError`, `formatEimzoError`, `parseX500`,
145
+ `parseCertificateInfo`, `createWebSocketTransport`, `createCapiwsTransport`, `createDefaultTransport`,
146
+ `defaultEimzoUrl`, `DEV_API_KEYS`, `DEFAULT_MAX_FILE_SIZE`, the locale objects (`ru`, `uzLatn`, `uzCyrl`,
147
+ `kaa`, `en`), and all types.
144
148
 
145
- Браузер не обращается к e-imzo-server напрямую. `createFetchBackend` ждёт ответы в формате
146
- e-imzo-server, поэтому backend может проксировать запросы:
149
+ ### Options
147
150
 
148
- | Запрос клиента | Backend | Ответ |
151
+ | Option | Default | Description |
152
+ |---|---|---|
153
+ | `apiKeys` | — | `{ domain: key }` or `[domain, key, ...]`. Use `DEV_API_KEYS` for `localhost` and `127.0.0.1`. If omitted, `apikey` is not called. |
154
+ | `backend` | — | `createFetchBackend(...)` or your own `{ getChallenge, login, timestamp }`. |
155
+ | `minVersion` | `'4.86'` | Older E-IMZO → `OUTDATED_VERSION`. |
156
+ | `locale` | `'ru'` | `'ru'`, `'uz-Latn'`, `'uz-Cyrl'`, `'kaa'`, `'en'`, or your own code with `messages`. |
157
+ | `messages` | — | Override texts: `{ en: { WRONG_PASSWORD: () => '...' } }`. Missing texts fall back to Russian. |
158
+ | `autoReloadKey` | `true` | Reload the key and retry when E-IMZO no longer knows the `keyId`. |
159
+ | `timeoutMs` | none | Timeout of one E-IMZO call. Off by default because signing waits for the user. |
160
+ | `url`, `WebSocketImpl`, `transport` | — | For tests and non-standard environments. |
161
+
162
+ ### Error codes
163
+
164
+ | `code` | Meaning |
165
+ |---|---|
166
+ | `NOT_INSTALLED` | E-IMZO is not installed or not running. |
167
+ | `OUTDATED_VERSION` | E-IMZO is older than `minVersion` (`params.installed`, `params.min`). |
168
+ | `API_KEY_REJECTED` | E-IMZO rejected the API key for this domain. |
169
+ | `WRONG_PASSWORD` | Wrong key password. |
170
+ | `CANCELLED` | The user closed the password window. Usually not worth showing. |
171
+ | `KEY_NOT_FOUND` | E-IMZO does not know this `keyId`. |
172
+ | `KEY_NOT_LOADED` | `sign()` was called before `loadKey()` — a bug in the calling code. |
173
+ | `FILE_TOO_LARGE` | File is larger than `maxSize` (`params.size`, `params.max`). |
174
+ | `BACKEND_NOT_CONFIGURED` | A required `backend` method is missing — a bug in the calling code. |
175
+ | `BACKEND_ERROR` | Your backend returned an error (`httpStatus`, `params.detail`). |
176
+ | `EIMZO_ERROR` | Any other E-IMZO error; see `reason` and `status`. |
177
+ | `TIMEOUT`, `WEBSOCKET_UNSUPPORTED` | Rare. |
178
+
179
+ ## Backend contract
180
+
181
+ The browser never calls e-imzo-server directly. `createFetchBackend` expects e-imzo-server's response
182
+ format, so your backend can proxy these calls:
183
+
184
+ | Client request | Your backend calls | Response |
149
185
  |---|---|---|
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": "..." }` |
186
+ | `GET challengeUrl` | `GET /frontend/challenge` (do not cache) | `{ "status": 1, "challenge": "..." }` |
187
+ | `POST loginUrl`, body: PKCS#7 (`text/plain`) | `POST /backend/auth`, then finds the user by PINFL/TIN and opens a session | any JSON, returned from `login()` |
188
+ | `POST timestampUrl`, body: PKCS#7 (`text/plain`) | `POST /frontend/timestamp/pkcs7` | `{ "status": 1, "pkcs7b64": "..." }` |
153
189
 
154
- Подробно, с примером на Java (Spring Boot): [docs/backend.md](docs/backend.md).
190
+ A full guide with a Spring Boot example, status codes, and security checklist (in Russian):
191
+ [docs/backend.md](docs/backend.md).
155
192
 
156
- Другой формат — свой объект вместо `createFetchBackend`:
193
+ ## Compatibility
157
194
 
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
- ```
195
+ - **Browsers:** any modern browser with WebSocket. The build targets ES2020.
196
+ - **TypeScript:** declarations included for ESM and CJS.
197
+ - **Frameworks:** framework-agnostic: plain functions and a class, no framework dependency. It can be used
198
+ from Angular, React, Vue, Svelte, or a plain page. With SSR frameworks (Next.js, Nuxt, Angular SSR), call
199
+ it only on the client: E-IMZO runs on the user's computer, not on your server.
200
+ - **Node.js:** not the intended environment. It can talk to a local E-IMZO from a script if you pass a
201
+ `WebSocketImpl` that sends an `Origin` header (see `scripts/check-real-eimzo.mjs`).
202
+ - **E-IMZO:** 4.86 or newer by default (`minVersion`). Tested with E-IMZO 6.3.5.
167
203
 
168
- ## Фреймворки
204
+ ## When not to use it
169
205
 
170
- **Angular**
206
+ - **To verify signatures.** Verification needs e-imzo-server on your backend; this is a client library.
207
+ - **For mobile signing** (ID-CARD E-IMZO MOBILE, QR code / deep link). Not supported yet.
208
+ - **If you need a ready-made UI** (key picker modal). `eimzo-ts` has no UI components.
209
+ - **If you need another provider** (for example RS-IMZO).
171
210
 
172
- ```ts
173
- @Injectable({ providedIn: 'root' })
174
- export class EimzoService {
175
- readonly client = createEimzo({ apiKeys: environment.eimzoApiKeys, backend: createFetchBackend({ /* ... */ }) });
176
- }
211
+ ## Alternatives
177
212
 
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
- ```
213
+ - The official `e-imzo.js` + `e-imzo-client.js` example: callback-based, uses globals.
214
+ - Other npm packages for E-IMZO exist. Some are framework-specific (Vue or React wrappers, UI modals),
215
+ some are framework-agnostic clients, and some are server-side clients for e-imzo-server. Search npm for
216
+ [`e-imzo`](https://www.npmjs.com/search?q=e-imzo) and compare their APIs and maintenance with your needs.
217
+ This project has not audited or benchmarked them.
183
218
 
184
- Для `HttpClient` с интерсепторами передайте свой `backend` через `firstValueFrom(this.http.get(...))`.
219
+ ## FAQ
185
220
 
186
- **React**
221
+ **Does it work over HTTPS?** Yes. On https pages it connects to `wss://127.0.0.1:64443`.
187
222
 
188
- ```tsx
189
- export const eimzo = createEimzo({ /* ... */ }); // модуль-синглтон
223
+ **Do I need an API key?** For `localhost` and `127.0.0.1`, use `DEV_API_KEYS` (public keys from the official
224
+ example). For another domain during development, enable "Developer mode" in the E-IMZO tray menu. For
225
+ production, request an API key for each domain and subdomain.
190
226
 
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
- ```
227
+ **Is the password sent to my site?** No. E-IMZO asks for it in its own window. Without "Remember for 6 hours"
228
+ it is remembered for 1 minute per `keyId`.
198
229
 
199
- **Vue**
230
+ **How do I test my code without E-IMZO?** Pass your own `transport` — a function
231
+ `(request) => Promise<response>`:
200
232
 
201
233
  ```ts
202
- export const eimzo = createEimzo({ /* ... */ });
203
-
204
- const keys = ref<EimzoKey[]>([]);
205
- onMounted(async () => { keys.value = await eimzo.listKeys(); });
234
+ const eimzo = createEimzo({
235
+ transport: async (req) => {
236
+ if (req.name === 'version') return { success: true, major: '6', minor: '3' };
237
+ if (req.plugin === 'pfx' && req.name === 'list_all_certificates') return { success: true, certificates: [] };
238
+ return { success: false, reason: `not mocked: ${req.plugin}.${req.name}` };
239
+ },
240
+ });
206
241
  ```
207
242
 
208
- ## API-ключи
243
+ The demo's mock ([`demo/mock-eimzo.js`](demo/mock-eimzo.js)) is a fuller example.
244
+
245
+ **Can I read what is inside a PKCS#7?** The demo has a viewer ([`demo/pkcs7-viewer.js`](demo/pkcs7-viewer.js))
246
+ that shows the document, signer, and signing time. It does not verify the signature.
247
+
248
+ **Is `signingTime` trustworthy?** No. It comes from the user's computer clock. Use a timestamp
249
+ (`signDocument`) and verify it on the server.
209
250
 
210
- - `localhost` и `127.0.0.1` — `DEV_API_KEYS` (публичные ключи из официального примера).
211
- - Другой домен или IP при разработке — «Режим разработчика» в меню E-IMZO в трее.
212
- - Production — API-KEY выдаёт НИЦ НТ, отдельно на каждый домен и поддомен.
251
+ ## Troubleshooting
252
+
253
+ | Symptom | Cause and fix |
254
+ |---|---|
255
+ | `NOT_INSTALLED` | E-IMZO is not running. Start it (tray icon) or install it from [e-imzo.uz](https://e-imzo.uz/main/downloads/). |
256
+ | `API_KEY_REJECTED`, or E-IMZO functions fail on a new domain | No API key for this domain. Use Developer mode for testing; add the production key to `apiKeys`. |
257
+ | `OUTDATED_VERSION` | Update E-IMZO, or lower `minVersion` if you know your users' versions. |
258
+ | `WRONG_PASSWORD` with `reason` "Password is not ASCII" | The password was typed in another keyboard layout. |
259
+ | `listKeys()` returns fewer keys than E-IMZO shows | Keys without TIN and PINFL are skipped, as in the official client. |
260
+ | Password is asked on every signature | A new `Eimzo` instance or `loadKey()` per signature creates a new `keyId`. Keep one instance. |
261
+ | `KEY_NOT_FOUND` with `autoReloadKey: false` | E-IMZO restarted or 24 hours passed. Call `loadKey()` again. |
262
+ | `BACKEND_ERROR` | Check `httpStatus` and `params.detail`; for `/backend/auth` status codes see [docs/backend.md](docs/backend.md). |
213
263
 
214
- ## Переход с e-imzo.js
264
+ ## For AI coding assistants
215
265
 
216
- Соответствие функций `EIMZOClient` и этой библиотеки: [docs/migration.md](docs/migration.md).
266
+ - [`llms.txt`](https://abduazimkhuja.github.io/eimzo-ts/llms.txt) and
267
+ [`llms-full.txt`](https://abduazimkhuja.github.io/eimzo-ts/llms-full.txt) — documentation index and full text.
268
+ - [Agent Skill](skills/eimzo-ts/SKILL.md) for tools that support the Agent Skills format.
269
+ - See [AI_INTEGRATION.md](AI_INTEGRATION.md).
217
270
 
218
- ## Разработка
271
+ ## Development
219
272
 
220
273
  ```bash
221
274
  npm install
@@ -223,9 +276,9 @@ npm test # vitest
223
276
  npm run typecheck
224
277
  npm run build # dist/: ESM, CJS, IIFE + .d.ts
225
278
  npm run demo # http://localhost:3000/demo/
226
- npm run check:eimzo # проверка на установленном E-IMZO; --interactive N — с подписью ключом N
279
+ npm run check:eimzo # checks against a locally installed E-IMZO; --interactive N signs with key N
227
280
  ```
228
281
 
229
- ## Лицензия
282
+ ## License
230
283
 
231
284
  [MIT](LICENSE)