eimzo-sign-core 0.1.4 → 0.1.5

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,9 +1,13 @@
1
1
  # eimzo-sign-core
2
2
 
3
- E-IMZO bilan imzolash mantig'i — **oynasiz**, framework'siz.
4
- UI'ni o'zingiz qurasiz. Vue / React / oddiy JS — farqi yo'q.
3
+ O'zbekiston elektron raqamli imzosi (**E-IMZO**) bilan hujjat imzolash mantig'i —
4
+ **oynasiz, framework'siz**. Sertifikat ro'yxati va tugmalarni o'zingiz chizasiz,
5
+ bu paket E-IMZO dasturi bilan aloqani, timeout'larni, xatolar va seriya
6
+ tekshiruvini o'z zimmasiga oladi. React / Svelte / vanilla JS — farqi yo'q.
5
7
 
6
- Tayyor Vue oyna kerak bo'lsa: [`eimzo-sign-vue`](https://www.npmjs.com/package/eimzo-sign-vue).
8
+ > Vue uchun **tayyor imzolash oynasi** kerak bo'lsa
9
+ > [**eimzo-sign-vue**](https://www.npmjs.com/package/eimzo-sign-vue) (u shu
10
+ > paketni ichida ishlatadi).
7
11
 
8
12
  ---
9
13
 
@@ -113,6 +117,28 @@ interface Cert {
113
117
 
114
118
  ---
115
119
 
120
+ ## `SignFlow` — modal holat mashinasi
121
+
122
+ Framework-agnostik oqim: yuklash → sertifikat tanlash → imzolash → qisman
123
+ xato / qayta urinish → yakun. O'z UI'ingiz (Vue 2.7, React, …) shu holatga
124
+ qarab chiziladi.
125
+
126
+ ```ts
127
+ import { SignFlow } from 'eimzo-sign-core'
128
+
129
+ const flow = new SignFlow({ client, docs: [{ id: 'p1', title: "To'lov", content: xml }] })
130
+ flow.subscribe((s) => render(s)) // s.status, s.certs, s.docs, s.progress …
131
+ flow.selectCert(serial)
132
+ flow.confirm() // "Imzolash"
133
+ flow.cancel() // "Bekor qilish" / Esc
134
+
135
+ const { results } = await flow.promise // yoki reject: EimzoError (.code === 'E_CANCELLED')
136
+ ```
137
+
138
+ `eimzo-sign-vue` ning modali aynan shu ustiga qurilgan.
139
+
140
+ ---
141
+
116
142
  ## Sozlamalar
117
143
 
118
144
  ```ts
package/dist/index.cjs CHANGED
@@ -1,12 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
-
10
3
  // src/errors.ts
11
4
  var EimzoError = class _EimzoError extends Error {
12
5
  constructor(code, message, options) {
@@ -81,9 +74,6 @@ function withTimeout(timeoutMs, timeoutError, executor, signal) {
81
74
  var _Base64 = global.Base64;
82
75
  var version = "2.1.4";
83
76
  var buffer;
84
- if (typeof module !== "undefined" && module.exports) {
85
- buffer = __require("buffer").Buffer;
86
- }
87
77
  var b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
88
78
  var b64tab = (function(bin) {
89
79
  var t = {};
@@ -229,9 +219,6 @@ function withTimeout(timeoutMs, timeoutError, executor, signal) {
229
219
  var _Base64 = global.Base64;
230
220
  var version = "2.1.4";
231
221
  var buffer;
232
- if (typeof module !== "undefined" && module.exports) {
233
- buffer = __require("buffer").Buffer;
234
- }
235
222
  var b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
236
223
  var b64tab = (function(bin) {
237
224
  var t = {};
@@ -1345,8 +1332,218 @@ function dedupeCerts(certs) {
1345
1332
  return out;
1346
1333
  }
1347
1334
 
1335
+ // src/sign-flow.ts
1336
+ var ID_CARD_SERIAL = "IDCARD";
1337
+ var SignFlow = class {
1338
+ constructor(deps) {
1339
+ this.listeners = /* @__PURE__ */ new Set();
1340
+ this.ac = new AbortController();
1341
+ this.finished = false;
1342
+ this.client = deps.client;
1343
+ this.autoInstall = deps.autoInstall ?? true;
1344
+ this.state = {
1345
+ status: "loading",
1346
+ certs: [],
1347
+ idCardAvailable: false,
1348
+ selectedSerial: null,
1349
+ docs: deps.docs.map((d) => ({
1350
+ id: d.id,
1351
+ title: d.title,
1352
+ content: d.content,
1353
+ preview: d.preview ?? [],
1354
+ status: "pending"
1355
+ })),
1356
+ progress: { done: 0, total: deps.docs.length }
1357
+ };
1358
+ this.promise = new Promise((resolve, reject) => {
1359
+ this.resolveOuter = resolve;
1360
+ this.rejectOuter = reject;
1361
+ });
1362
+ void this.load();
1363
+ }
1364
+ // ─── Kuzatish ──────────────────────────────────────────────────────────
1365
+ getState() {
1366
+ return this.state;
1367
+ }
1368
+ subscribe(listener) {
1369
+ this.listeners.add(listener);
1370
+ return () => this.listeners.delete(listener);
1371
+ }
1372
+ setState(patch) {
1373
+ this.state = { ...this.state, ...patch };
1374
+ for (const listener of this.listeners) listener(this.state);
1375
+ }
1376
+ // ─── Hosil qilingan qiymatlar ──────────────────────────────────────────
1377
+ get selectedCert() {
1378
+ const { selectedSerial, certs } = this.state;
1379
+ if (!selectedSerial) return null;
1380
+ if (selectedSerial === ID_CARD_SERIAL) return EimzoClient.idCardCert();
1381
+ return certs.find((c) => c.serialNumber === selectedSerial) ?? null;
1382
+ }
1383
+ get canConfirm() {
1384
+ return (this.state.status === "ready" || this.state.status === "partial") && this.selectedCert !== null && this.pendingDocs.length > 0;
1385
+ }
1386
+ /** Navbatdagi imzolанадиган hujjатlар (partial rejimда — faqat xatоликдагилар). */
1387
+ get pendingDocs() {
1388
+ return this.state.docs.filter((d) => d.status !== "done");
1389
+ }
1390
+ // ─── Harakatlar ────────────────────────────────────────────────────────
1391
+ async load() {
1392
+ this.setState({ status: "loading", fatal: void 0, lastError: void 0 });
1393
+ try {
1394
+ if (this.autoInstall) await this.client.install();
1395
+ const [rawCerts, idCard] = await Promise.all([
1396
+ this.client.listCerts(),
1397
+ this.client.isIdCardPlugged()
1398
+ ]);
1399
+ const certs = sortCerts(dedupeCerts(rawCerts));
1400
+ if (certs.length === 0 && !idCard) {
1401
+ this.fail(
1402
+ new EimzoError("E_NO_CERTS", "Imzolash uchun sertifikat topilmadi.")
1403
+ );
1404
+ return;
1405
+ }
1406
+ const firstUsable = certs.find((c) => certIsUsable(c));
1407
+ this.setState({
1408
+ status: "ready",
1409
+ certs,
1410
+ idCardAvailable: idCard,
1411
+ selectedSerial: firstUsable?.serialNumber ?? (idCard ? ID_CARD_SERIAL : certs[0]?.serialNumber ?? null)
1412
+ });
1413
+ } catch (error) {
1414
+ this.fail(toEimzoError(error, "E_NOT_INSTALLED", "E-IMZO bilan ulanib bo'lmadi."));
1415
+ }
1416
+ }
1417
+ /** Fatal xatodan keyin qayta yuklash. */
1418
+ retryLoad() {
1419
+ if (this.state.status !== "error" || this.finished) return;
1420
+ void this.load();
1421
+ }
1422
+ selectCert(serial) {
1423
+ if (this.state.status !== "ready" && this.state.status !== "partial") return;
1424
+ this.setState({ selectedSerial: serial, lastError: void 0 });
1425
+ }
1426
+ /** "Imzolash" tugmasi. */
1427
+ async confirm() {
1428
+ if (!this.canConfirm || this.finished) return;
1429
+ const cert = this.selectedCert;
1430
+ if (!cert) return;
1431
+ const targets = this.pendingDocs;
1432
+ this.patchDocs(targets.map((d) => d.id), { status: "pending", error: void 0 });
1433
+ this.setState({
1434
+ status: "signing",
1435
+ lastError: void 0,
1436
+ progress: {
1437
+ done: this.state.docs.length - targets.length,
1438
+ total: this.state.docs.length
1439
+ }
1440
+ });
1441
+ try {
1442
+ const items = await this.client.signBatch(
1443
+ cert,
1444
+ targets.map((d) => ({ id: d.id, content: d.content })),
1445
+ {
1446
+ signal: this.ac.signal,
1447
+ onProgress: (p) => {
1448
+ const doneTotal = this.state.docs.length - targets.length + p.done;
1449
+ this.setState({ progress: { done: doneTotal, total: this.state.docs.length } });
1450
+ if (p.currentId) {
1451
+ this.patchDocs([p.currentId], { status: "signing" });
1452
+ }
1453
+ }
1454
+ }
1455
+ );
1456
+ for (const item of items) {
1457
+ if (item.ok) {
1458
+ this.patchDocs([item.id], {
1459
+ status: "done",
1460
+ result: {
1461
+ id: item.id,
1462
+ pkcs7_64: item.pkcs7_64,
1463
+ signature_hex: item.signature_hex,
1464
+ signer_serial_number: item.signer_serial_number
1465
+ }
1466
+ });
1467
+ } else {
1468
+ this.patchDocs([item.id], { status: "failed", error: item.error });
1469
+ }
1470
+ }
1471
+ const failed = this.state.docs.filter((d) => d.status === "failed");
1472
+ if (failed.length === 0) {
1473
+ this.succeed();
1474
+ } else {
1475
+ this.setState({
1476
+ status: "partial",
1477
+ lastError: new EimzoError(
1478
+ "E_SIGN_FAILED",
1479
+ `${failed.length} ta hujjat imzolanmadi.`
1480
+ )
1481
+ });
1482
+ }
1483
+ } catch (error) {
1484
+ if (EimzoError.is(error, "E_CANCELLED")) {
1485
+ this.cancelledByAbort();
1486
+ return;
1487
+ }
1488
+ this.patchDocs(targets.map((d) => d.id), { status: "pending" });
1489
+ this.setState({
1490
+ status: this.state.docs.some((d) => d.status === "done") ? "partial" : "ready",
1491
+ lastError: toEimzoError(error, "E_SIGN_FAILED", "Imzolab bo'lmadi.")
1492
+ });
1493
+ }
1494
+ }
1495
+ /** "Bekor qilish" / Esc / overlay / idle-timeout / "Yopish" (fatal ekranда). */
1496
+ cancel() {
1497
+ if (this.finished) return;
1498
+ if (this.state.status === "signing") {
1499
+ this.ac.abort();
1500
+ return;
1501
+ }
1502
+ if (this.state.status === "error" && this.state.fatal) {
1503
+ const fatal = this.state.fatal;
1504
+ this.finish(() => this.rejectOuter(fatal));
1505
+ return;
1506
+ }
1507
+ this.finish(() => this.rejectOuter(this.cancelError()));
1508
+ }
1509
+ // ─── Yakuniy holatlar ──────────────────────────────────────────────────
1510
+ succeed() {
1511
+ const results = this.state.docs.map((d) => d.result).filter((r) => Boolean(r));
1512
+ this.finish(() => this.resolveOuter({ results }));
1513
+ }
1514
+ cancelledByAbort() {
1515
+ this.patchDocs(
1516
+ this.state.docs.filter((d) => d.status === "signing").map((d) => d.id),
1517
+ { status: "pending" }
1518
+ );
1519
+ this.finish(() => this.rejectOuter(this.cancelError()));
1520
+ }
1521
+ fail(error) {
1522
+ this.setState({ status: "error", fatal: error });
1523
+ }
1524
+ cancelError() {
1525
+ const partial = this.state.docs.map((d) => d.result).filter((r) => Boolean(r));
1526
+ const err = new EimzoError("E_CANCELLED", "Imzolash bekor qilindi.");
1527
+ err.partial = partial;
1528
+ return err;
1529
+ }
1530
+ finish(action) {
1531
+ if (this.finished) return;
1532
+ this.finished = true;
1533
+ action();
1534
+ }
1535
+ patchDocs(ids, patch) {
1536
+ const set = new Set(ids);
1537
+ this.setState({
1538
+ docs: this.state.docs.map((d) => set.has(d.id) ? { ...d, ...patch } : d)
1539
+ });
1540
+ }
1541
+ };
1542
+
1348
1543
  exports.EimzoClient = EimzoClient;
1349
1544
  exports.EimzoError = EimzoError;
1545
+ exports.ID_CARD_SERIAL = ID_CARD_SERIAL;
1546
+ exports.SignFlow = SignFlow;
1350
1547
  exports.certExpiresSoon = certExpiresSoon;
1351
1548
  exports.certIsExpired = certIsExpired;
1352
1549
  exports.certIsIndividual = certIsIndividual;