@gerege-systems/eid-mongolia-sdk 0.9.1 → 0.10.0

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
@@ -154,6 +154,17 @@ console.log(who.documentNumber, who.subject); // баталгаажсан ирг
154
154
  const { name, serialNumber } = who.parsedSubject; // "Болд Бат", "PNOMN-12345678" (givenName/surname/commonName/country…)
155
155
  ```
156
156
 
157
+ Session-ий `person` блок (CA бүртгэлээс — гэрчилгээнд байдаггүй кирилл нэр, төрсөн огноо, хүйс) гарын үсэгт **ордоггүй** —
158
+ гэрчилгээний `SERIALNUMBER`-тэй тулгаж байж итгэнэ:
159
+
160
+ ```ts
161
+ import { personMatchesCertificate } from "@gerege-systems/eid-mongolia-sdk";
162
+ if (personMatchesCertificate(result.person, who.parsedSubject)) {
163
+ const { firstName, lastName /* ЭЦГИЙН нэр */, familyName /* УРГИЙН овог */, civilId, regNo, birthDate, gender, geID } = result.person!;
164
+ }
165
+ // result.raw — серверийн түүхий JSON (SDK задлаагүй талбарт)
166
+ ```
167
+
157
168
  Subject-ийн талбарууд (`parsedSubject`, `validateSign`-д ч мөн) нь `parseSubject(cert)`-ээс — OID 2.5.4.42/4/3/5/6/10/97,
158
169
  UTF8String/PrintableString/BMPString. `parseSubject` нь зөвхөн задлалт: баталгаажаагүй гэрчилгээний нэрэнд бүү итгэ.
159
170
 
@@ -182,6 +193,8 @@ const sig = eid.validator.validateSign(result, digest); // digest-ийн эср
182
193
  console.log(sig.signatureValueB64, sig.subject);
183
194
  ```
184
195
 
196
+ `SignOptions.fileName` — баримтын нэр CA-ийн /verify хуудас, иргэний түүхэнд харагдана (заавал биш).
197
+
185
198
  Device-link / push-оор эхлүүлсэн sign session-д эхлүүлсэн урсгалаа `expectedFlowType`-оор тулга (`validateAuth`-тай
186
199
  ижил: зөрвөл `ValidationError`). Гуравдах параметр нь `hashAlgorithm` мөр (хуучин хэлбэр) эсвэл объект:
187
200
 
@@ -238,10 +251,38 @@ try {
238
251
  (харуулсан урсгалаа). Гарын үсэгтэй PDF-ийг
239
252
  `pdf.document()`-ийн оронд `pdf.documentVerified(sessionId, r.pdf)`-аар тат — `READY` + `outSha256` тулгалтыг хийнэ.
240
253
 
254
+ Түр алдаа — `ApiError.retryable` үнэн, `retryAfterMs`-ийн (CA-ийн `Retry-After`, секунд эсвэл огноо) дараа давтана:
255
+ prepare 429 `PDF_PREPARE_BUSY`, 503 `LTV_UNAVAILABLE` / `TSA_UNAVAILABLE`; document 503 `DOCUMENT_PENDING`,
256
+ `TSA_UNAVAILABLE`, `LTV_UNAVAILABLE` (`RETRYABLE_ERROR_CODES`). `headers` — хариуны header. PDF дуудлагын хугацаа
257
+ `pdfTimeoutMs` (анхдагч 80000).
258
+
241
259
  Олон талт гэрээ: А байгууллагын гаралтыг өөрчлөлтгүй Б-ийн `prepare`-д (`docID`, өөр `onBehalfOf`) илгээнэ.
242
260
  Session-ий `onBehalfOf` блок (`role`/`rightType`) нь хариу угсрах үеийн бүртгэл; `pdf.signer.onBehalfOf`/`claimedRole`
243
261
  нь баримтад бичигдсэн утга.
244
262
 
263
+ ## 3b. Иргэний PKI самбар (`PKI_READ` эрх)
264
+
265
+ `eid.person.certificates(etsi)` · `devices` · `activity(etsi, { flow, limit, offset })` · `summary` · `children` — иргэн
266
+ сүүлийн 24 цагт танай RP-ээр нэвтэрсэн байх ёстой (эс бөгөөс `ForbiddenError`). Хариу бүрт `raw`.
267
+
268
+ ## 3c. USB токен
269
+
270
+ ```ts
271
+ import { verifyTokenCertificates, tokenAuthDigest, tokenLimitOf } from "@gerege-systems/eid-mongolia-sdk";
272
+
273
+ // гэрчилгээ олголт (TOKEN_ISSUE эрх) — иргэн утсаараа PIN2-оор батална
274
+ const st = await eid.token.issueCertificate({ signerEtsi, csrAuth, csrSign, tokenSerial: "EP2003-0042A1" }); // onBehalfOf алга = хувийн
275
+ // … eid.session.poll(st.sessionId) → result.tokenCertificate?.status === "ISSUED"
276
+ const res = await eid.token.certificateResult(st.sessionId);
277
+ verifyTokenCertificates(res, csrAuth, csrSign); // гэрчилгээ бүр CSR-ийн түлхүүртэй
278
+ // 409 TOKEN_LIMIT_REACHED → tokenLimitOf(err): { kind, limit, organization? }
279
+
280
+ // токеноор нэвтрэх (AUTH эрх)
281
+ const ch = await eid.token.authChallenge();
282
+ const digest = tokenAuthDigest(ch.rpUUID, ch.challenge); // токен CKM_ECDSA → түүхий r||s
283
+ const who = await eid.token.authVerify(ch.challengeId, authCertDer, rs);
284
+ ```
285
+
245
286
  ## 4. QR ба ижил төхөөрөмж — device link v3
246
287
 
247
288
  Иргэн өөр төхөөрөмжийн дэлгэц дээрх **QR**-ийг утсаараа уншина, эсвэл RP-ийн сайт/аппыг eID апптай **ижил
@@ -345,18 +386,25 @@ OK ирж болзошгүй. Иймд `ResponseValidator`:
345
386
  зөвхөн SHA-256 digest зурдаг (өөр hash хүссэн session-д сервер гарын үсэг гаргахгүй), тиймээс SHA-384/512
346
387
  хүссэн session-д `ecdsa-with-SHA256` хариу ирвэл хуурамч гэж татгалзана.
347
388
 
348
- Аль нэг алхам бүтэлгүйтвэл `ValidationError` шиднэ — хариунд **итгэхгүй**.
389
+ 5. Цуцлалт — `trust.revocation` (OCSP/CRL hook) → `checkRevocation`, эсвэл `checkRevocationStatus` (`"good"` /
390
+ `"unchecked"` (soft-fail) / `"skipped"` (hook алга)).
391
+ 6. Нэмэлт (сонголттой): `allowedIssuerSkis: [CITIZEN_ISSUING_CA_SKI]` — зөвхөн иргэний Issuing CA (04)-ийн гэрчилгээ;
392
+ `checkKeyUsage: true` — auth → clientAuth, sign → nonRepudiation (CA-ийн профайл); `schemeName` — CA-ийн
393
+ `acsp_scheme_name` (анхдагч `smart-id`).
394
+
395
+ Аль нэг алхам бүтэлгүйтвэл `ValidationError` шиднэ — хариунд **итгэхгүй**. Алдааны текстэд иргэний нэр, PNOMN, РД
396
+ ордоггүй (`NetworkError.op`-д замыг `/etsi/…` болгоно) — шууд лог руу бичиж болно.
349
397
 
350
398
  ## Алдааны төрлүүд
351
399
 
352
400
  | Алдаа | Утга |
353
401
  |---|---|
354
- | `AuthenticationError` | 401 — API secret буруу/байхгүй |
402
+ | `AuthenticationError` | 401 — API secret буруу/байхгүй; CA-ийн JSON бол `.serverMessage` (+ `.code`), proxy-ийнх бол алга (`.body`, `.headers`) |
355
403
  | `ForbiddenError` | 403 — IP allowlist / mTLS / RP-ийн эрх; onBehalfOf татгалзвал `.code` (`OnBehalfErrorCode`) |
356
404
  | `SessionFailedError` | endResult ≠ OK (TIMEOUT, USER_REFUSED…) — `.endResult`-оор салга |
357
405
  | `ValidationError` | cert chain/signature/level шалгалт бүтэлгүйтсэн |
358
- | `ApiError` | бусад HTTP алдаа (`.status`, `.body`, серверийн `.code` — ж: 404 `ORG_NOT_FOUND`) |
359
- | `NetworkError` | timeout/сүлжээ |
406
+ | `ApiError` | бусад HTTP алдаа (`.status`, `.body`, серверийн `.code`, `.retryAfterMs`, `.headers`, `.retryable` — ж: 404 `ORG_NOT_FOUND`) |
407
+ | `NetworkError` | timeout/сүлжээ (`.op` — таних тэмдэггүй зам, `.timeout`) |
360
408
  | `EidOidcError` | «eID-ээр нэвтрэх» (OIDC) — `.code`: `access_denied`, `login_required`, `state_mismatch`, `invalid_grant`, `invalid_id_token`, `server_error` … |
361
409
  | `DeviceLinkError` | device-link холбоос угсрах боломжгүй (`deviceLinkBase` https биш, token/secret/lang хэлбэр, төрөлд хэрэгтэй талбар дутуу) |
362
410
 
@@ -377,3 +425,16 @@ npm install
377
425
  npm run lint # tsc --noEmit
378
426
  npm test # build + node --test
379
427
  ```
428
+
429
+ ### Гэрээний шалгалт
430
+
431
+ SDK-ийн дууддаг RP-API зам бүр (`src/contract.ts`) live [`/.well-known/eid`](https://rp.eidmongolia.mn/.well-known/eid)-ийн
432
+ `endpoints`-д байх ёстой; OIDC-ийн хүлээдэг утгууд (issuer, `ES256`, PKCE `S256`, `client_secret_basic`, `EidIdTokenClaims`-ийн
433
+ талбарууд) [discovery](https://login.eidmongolia.mn/.well-known/openid-configuration)-тэй таарах ёстой. Discovery-д
434
+ байгаа ч SDK хамраагүй замууд WARN (fail биш). Сүлжээ шаарддаг тул `npm test`-д ордоггүй:
435
+
436
+ ```bash
437
+ npm run test:contract
438
+ ```
439
+
440
+ CI: `.github/workflows/contract.yml` — PR, `main` push, өдөр бүр 00:00 UTC, гараар.
@@ -135,6 +135,46 @@ interface SessionResult {
135
135
  * угсарсан объект). {@link PdfSessionBlock}.
136
136
  */
137
137
  pdf?: PdfSessionBlock | null;
138
+ /**
139
+ * COMPLETE+OK auth session-ий `person` блок (CA бүртгэлээс: кирилл нэр, төрсөн огноо, хүйс — гэрчилгээнд байдаггүй).
140
+ * Зөвхөн session эзэмшигч RP-д; CA иргэнийг олоогүй бол null. ГАРЫН ҮСЭГТ ОРДОГГҮЙ — validateAuth-ийн дараа
141
+ * {@link personMatchesCertificate}-ээр гэрчилгээний SERIALNUMBER (`PNOMN-<civilId>`)-тэй тулгаж л хэрэглэнэ.
142
+ */
143
+ person?: PersonBlock | null;
144
+ /** USB токены гэрчилгээ олголтын session-ий `tokenCertificate` блок (`token.issueCertificate`); бусад урсгалд null. */
145
+ tokenCertificate?: TokenCertSessionBlock | null;
146
+ /** Серверийн хариуны түүхий JSON (`parseSessionResult`-ийн оролт) — SDK задлаагүй талбарт. */
147
+ raw?: unknown;
148
+ }
149
+ /**
150
+ * Session-ий `person` блок (CA `dto.PersonBlock`). Нэршил core.gerege.mn-тэй нэг мөр — гурван нэр ӨӨР ойлголт:
151
+ * `firstName` = нэр, `lastName` = ЭЦГИЙН нэр (гэрчилгээнд ордог), `familyName` = УРГИЙН овог (гэрчилгээнд ОРОХГҮЙ).
152
+ * Мэдэгдэхгүй талбар undefined — RP заавал байх талбар гэж үзэхгүй.
153
+ */
154
+ interface PersonBlock {
155
+ firstName?: string;
156
+ lastName?: string;
157
+ familyName?: string;
158
+ firstNameEn?: string;
159
+ lastNameEn?: string;
160
+ civilId?: string;
161
+ regNo?: string;
162
+ /** YYYY-MM-DD (цаг/бүсгүй). */
163
+ birthDate?: string;
164
+ /** M | F | X. */
165
+ gender?: string;
166
+ /** core.gerege.mn-ий дотоод дугаар; байхгүй = тулгагдаагүй. */
167
+ geID?: number;
168
+ }
169
+ /** Токены гэрчилгээ олголтын төлөв. */
170
+ type TokenCertStatus = "PENDING" | "ISSUED" | "FAILED" | "EXPIRED";
171
+ /** GET /session/{id}-ийн `tokenCertificate` (TOKEN_CERT §Төлөв). */
172
+ interface TokenCertSessionBlock {
173
+ status: TokenCertStatus | string;
174
+ /** FAILED үед: endResult (USER_REFUSED, TIMEOUT …) эсвэл CSR_DIGEST_MISMATCH, TOKEN_LIMIT_REACHED г.м. */
175
+ errorCode?: string;
176
+ authSerialHex?: string;
177
+ signSerialHex?: string;
138
178
  }
139
179
  /** PDF баримтын төлөв: PENDING → READY (эсвэл FAILED / EXPIRED). */
140
180
  type PdfDocumentStatus = "PENDING" | "READY" | "FAILED" | "EXPIRED";
@@ -259,8 +299,32 @@ declare function openOrShowQR(session: DeviceLinkInput, opts: OpenOrShowQROption
259
299
  declare class EidError extends Error {
260
300
  constructor(message: string);
261
301
  }
262
- /** HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. */
302
+ /** HTTP хариуны нэмэлт мэдээлэл (алдааны төрлүүдэд). */
303
+ interface HttpErrorInit {
304
+ /** Хариуны header-ууд. */
305
+ headers?: Headers;
306
+ /** Retry-After (секунд эсвэл HTTP-огноо задалсан), мс; байхгүй/буруу бол undefined. */
307
+ retryAfterMs?: number;
308
+ }
309
+ /**
310
+ * HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. CA-ийн өөрийн 401 нь JSON `{"error"[,"code"]}` —
311
+ * `serverMessage` (ба байвал `code`) бөглөгдөнө; JSON биш (proxy / nginx) бол хоёул undefined — үүгээр CA-ийн 401-ийг
312
+ * proxy-ийнхоос ялгана.
313
+ */
263
314
  declare class AuthenticationError extends EidError {
315
+ /** Серверийн `{"code"}`; байхгүй / JSON биш (proxy) бол undefined. */
316
+ readonly code?: string;
317
+ /** Серверийн `{"error"}` текст (логт); JSON биш (proxy) бол undefined. */
318
+ readonly serverMessage?: string;
319
+ /** Хариуны бие (таслагдаагүй текст). */
320
+ readonly body?: string;
321
+ readonly headers?: Headers;
322
+ constructor(message: string, init?: {
323
+ code?: string;
324
+ serverMessage?: string;
325
+ body?: string;
326
+ headers?: Headers;
327
+ });
264
328
  }
265
329
  /**
266
330
  * HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй, RP-д эрх алга, эсвэл onBehalfOf-ийн төлөөлөл татгалзсан.
@@ -269,9 +333,12 @@ declare class AuthenticationError extends EidError {
269
333
  declare class ForbiddenError extends EidError {
270
334
  /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
271
335
  readonly code?: string | undefined;
336
+ readonly headers?: Headers;
337
+ /** Retry-After (мс), байхгүй бол undefined. */
338
+ readonly retryAfterMs?: number;
272
339
  constructor(message: string,
273
340
  /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
274
- code?: string | undefined);
341
+ code?: string | undefined, init?: HttpErrorInit);
275
342
  }
276
343
  /** RP-API-аас ирсэн бусад HTTP алдаа (4xx/5xx). status ба хариуны биеийг агуулна. */
277
344
  declare class ApiError extends EidError {
@@ -279,10 +346,31 @@ declare class ApiError extends EidError {
279
346
  readonly body: string;
280
347
  /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
281
348
  readonly code?: string | undefined;
349
+ readonly headers?: Headers;
350
+ /**
351
+ * Retry-After (мс, секунд эсвэл HTTP-огноо задалсан); байхгүй/буруу бол undefined. CA түр алдаанд (429
352
+ * PDF_PREPARE_BUSY / RATE_LIMITED, 503 DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE г.м.) тавина.
353
+ */
354
+ readonly retryAfterMs?: number;
282
355
  constructor(status: number, body: string,
283
356
  /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
284
- code?: string | undefined);
357
+ code?: string | undefined, init?: HttpErrorInit);
358
+ /**
359
+ * CA-ийн түр алдаа ({@link RETRYABLE_ERROR_CODES}-ийн нэг, эсвэл 429/503 + Retry-After): `retryAfterMs`-ийн дараа
360
+ * ижил хүсэлтийг давтаж болно.
361
+ */
362
+ get retryable(): boolean;
285
363
  }
364
+ /**
365
+ * CA-ийн түр (давтаж болох) алдааны кодууд — `ApiError.code`. PDF_PREPARE_BUSY (429 prepare, Retry-After 5),
366
+ * DOCUMENT_PENDING (503 document, 5), TSA_UNAVAILABLE (503, 30), LTV_UNAVAILABLE (503, 60), RATE_LIMITED (429 токен),
367
+ * STATE_UNAVAILABLE (503 токен), ISSUANCE_PENDING (503 токены үр дүн, 5).
368
+ */
369
+ type RetryableErrorCode = "PDF_PREPARE_BUSY" | "DOCUMENT_PENDING" | "TSA_UNAVAILABLE" | "LTV_UNAVAILABLE" | "RATE_LIMITED" | "STATE_UNAVAILABLE" | "ISSUANCE_PENDING";
370
+ /** {@link RetryableErrorCode}-ийн бүх утга. */
371
+ declare const RETRYABLE_ERROR_CODES: readonly RetryableErrorCode[];
372
+ /** RFC 9110 §10.2.3: delay-seconds эсвэл HTTP-date → мс. Буруу / өнгөрсөн / 24 цагаас их → undefined. */
373
+ declare function parseRetryAfter(value: string | null | undefined, now?: number): number | undefined;
286
374
  /**
287
375
  * Байгууллагын нэрийн өмнөөс (`onBehalfOf`) урсгалын татгалзлын кодууд — `ForbiddenError.code` /
288
376
  * `ApiError.code`-д ирнэ. 403: REPRESENTATION_DENIED (идэвхтэй төлөөлөгч биш), REPRESENTATION_PENDING (PIN2-оор
@@ -293,8 +381,19 @@ declare class ApiError extends EidError {
293
381
  type OnBehalfErrorCode = "REPRESENTATION_DENIED" | "REPRESENTATION_PENDING" | "REPRESENTATION_EXPIRED" | "ORG_NOT_ACTIVE" | "SIGNER_UNIDENTIFIED" | "ORG_NOT_FOUND" | "REPRESENTATION_REVOKED";
294
382
  /** {@link OnBehalfErrorCode}-ийн бүх утга (runtime шалгалтад). */
295
383
  declare const ON_BEHALF_ERROR_CODES: readonly OnBehalfErrorCode[];
296
- /** Сүлжээ/timeout — upstream хариу өгсөнгүй. */
384
+ /**
385
+ * Сүлжээ/timeout — upstream хариу өгсөнгүй. Мессеж ба `op`-д хувь хүний таних тэмдэг ОРОХГҮЙ (замын `/etsi/{…}`,
386
+ * `/document/{…}` → `…`) — шууд лог руу бичиж болно.
387
+ */
297
388
  declare class NetworkError extends EidError {
389
+ /** "GET /session/…" (таних тэмдэг хасагдсан зам). */
390
+ readonly op?: string;
391
+ /** Хүсэлт хугацаа хэтэрсэн. */
392
+ readonly timeout?: boolean;
393
+ constructor(message: string, init?: {
394
+ op?: string;
395
+ timeout?: boolean;
396
+ });
298
397
  }
299
398
  /**
300
399
  * Session амжилтгүй дууссан (endResult ≠ OK): TIMEOUT, USER_REFUSED*, DOCUMENT_UNUSABLE гм.
@@ -318,4 +417,4 @@ declare class ValidationError extends EidError {
318
417
  declare class DeviceLinkError extends EidError {
319
418
  }
320
419
 
321
- export { type AcspContext as A, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, type HashAlgorithm as H, type Interaction as I, type NotificationSession as N, ON_BEHALF_ERROR_CODES as O, type PdfSessionBlock as P, type RpCredentials as R, type SignatureAlgorithm as S, ValidationError as V, type SessionResult as a, type RepresentationsResponse as b, ApiError as c, AuthenticationError as d, DeviceLinkError as e, type DeviceLinkInput as f, type EndResult as g, ForbiddenError as h, type NavigatorLike as i, NetworkError as j, type OnBehalfErrorCode as k, type OpenOrShowQROptions as l, type OrgResult as m, type PdfDocumentStatus as n, type PdfSigner as o, type Representation as p, SessionFailedError as q, type SessionState as r, type SignatureAlgorithmParameters as s, type SignatureProtocol as t, deviceLink as u, isMobileUserAgent as v, openOrShowQR as w };
420
+ export { type AcspContext as A, isMobileUserAgent as B, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, openOrShowQR as G, type HashAlgorithm as H, type Interaction as I, parseRetryAfter as J, type NotificationSession as N, ON_BEHALF_ERROR_CODES as O, type PersonBlock as P, type RpCredentials as R, type SignatureAlgorithm as S, type TokenCertSessionBlock as T, ValidationError as V, type SessionResult as a, type PdfSessionBlock as b, type RepresentationsResponse as c, ApiError as d, AuthenticationError as e, DeviceLinkError as f, type DeviceLinkInput as g, type EndResult as h, ForbiddenError as i, type HttpErrorInit as j, type NavigatorLike as k, NetworkError as l, type OnBehalfErrorCode as m, type OpenOrShowQROptions as n, type OrgResult as o, type PdfDocumentStatus as p, type PdfSigner as q, RETRYABLE_ERROR_CODES as r, type Representation as s, type RetryableErrorCode as t, SessionFailedError as u, type SessionState as v, type SignatureAlgorithmParameters as w, type SignatureProtocol as x, type TokenCertStatus as y, deviceLink as z };
@@ -135,6 +135,46 @@ interface SessionResult {
135
135
  * угсарсан объект). {@link PdfSessionBlock}.
136
136
  */
137
137
  pdf?: PdfSessionBlock | null;
138
+ /**
139
+ * COMPLETE+OK auth session-ий `person` блок (CA бүртгэлээс: кирилл нэр, төрсөн огноо, хүйс — гэрчилгээнд байдаггүй).
140
+ * Зөвхөн session эзэмшигч RP-д; CA иргэнийг олоогүй бол null. ГАРЫН ҮСЭГТ ОРДОГГҮЙ — validateAuth-ийн дараа
141
+ * {@link personMatchesCertificate}-ээр гэрчилгээний SERIALNUMBER (`PNOMN-<civilId>`)-тэй тулгаж л хэрэглэнэ.
142
+ */
143
+ person?: PersonBlock | null;
144
+ /** USB токены гэрчилгээ олголтын session-ий `tokenCertificate` блок (`token.issueCertificate`); бусад урсгалд null. */
145
+ tokenCertificate?: TokenCertSessionBlock | null;
146
+ /** Серверийн хариуны түүхий JSON (`parseSessionResult`-ийн оролт) — SDK задлаагүй талбарт. */
147
+ raw?: unknown;
148
+ }
149
+ /**
150
+ * Session-ий `person` блок (CA `dto.PersonBlock`). Нэршил core.gerege.mn-тэй нэг мөр — гурван нэр ӨӨР ойлголт:
151
+ * `firstName` = нэр, `lastName` = ЭЦГИЙН нэр (гэрчилгээнд ордог), `familyName` = УРГИЙН овог (гэрчилгээнд ОРОХГҮЙ).
152
+ * Мэдэгдэхгүй талбар undefined — RP заавал байх талбар гэж үзэхгүй.
153
+ */
154
+ interface PersonBlock {
155
+ firstName?: string;
156
+ lastName?: string;
157
+ familyName?: string;
158
+ firstNameEn?: string;
159
+ lastNameEn?: string;
160
+ civilId?: string;
161
+ regNo?: string;
162
+ /** YYYY-MM-DD (цаг/бүсгүй). */
163
+ birthDate?: string;
164
+ /** M | F | X. */
165
+ gender?: string;
166
+ /** core.gerege.mn-ий дотоод дугаар; байхгүй = тулгагдаагүй. */
167
+ geID?: number;
168
+ }
169
+ /** Токены гэрчилгээ олголтын төлөв. */
170
+ type TokenCertStatus = "PENDING" | "ISSUED" | "FAILED" | "EXPIRED";
171
+ /** GET /session/{id}-ийн `tokenCertificate` (TOKEN_CERT §Төлөв). */
172
+ interface TokenCertSessionBlock {
173
+ status: TokenCertStatus | string;
174
+ /** FAILED үед: endResult (USER_REFUSED, TIMEOUT …) эсвэл CSR_DIGEST_MISMATCH, TOKEN_LIMIT_REACHED г.м. */
175
+ errorCode?: string;
176
+ authSerialHex?: string;
177
+ signSerialHex?: string;
138
178
  }
139
179
  /** PDF баримтын төлөв: PENDING → READY (эсвэл FAILED / EXPIRED). */
140
180
  type PdfDocumentStatus = "PENDING" | "READY" | "FAILED" | "EXPIRED";
@@ -259,8 +299,32 @@ declare function openOrShowQR(session: DeviceLinkInput, opts: OpenOrShowQROption
259
299
  declare class EidError extends Error {
260
300
  constructor(message: string);
261
301
  }
262
- /** HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. */
302
+ /** HTTP хариуны нэмэлт мэдээлэл (алдааны төрлүүдэд). */
303
+ interface HttpErrorInit {
304
+ /** Хариуны header-ууд. */
305
+ headers?: Headers;
306
+ /** Retry-After (секунд эсвэл HTTP-огноо задалсан), мс; байхгүй/буруу бол undefined. */
307
+ retryAfterMs?: number;
308
+ }
309
+ /**
310
+ * HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. CA-ийн өөрийн 401 нь JSON `{"error"[,"code"]}` —
311
+ * `serverMessage` (ба байвал `code`) бөглөгдөнө; JSON биш (proxy / nginx) бол хоёул undefined — үүгээр CA-ийн 401-ийг
312
+ * proxy-ийнхоос ялгана.
313
+ */
263
314
  declare class AuthenticationError extends EidError {
315
+ /** Серверийн `{"code"}`; байхгүй / JSON биш (proxy) бол undefined. */
316
+ readonly code?: string;
317
+ /** Серверийн `{"error"}` текст (логт); JSON биш (proxy) бол undefined. */
318
+ readonly serverMessage?: string;
319
+ /** Хариуны бие (таслагдаагүй текст). */
320
+ readonly body?: string;
321
+ readonly headers?: Headers;
322
+ constructor(message: string, init?: {
323
+ code?: string;
324
+ serverMessage?: string;
325
+ body?: string;
326
+ headers?: Headers;
327
+ });
264
328
  }
265
329
  /**
266
330
  * HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй, RP-д эрх алга, эсвэл onBehalfOf-ийн төлөөлөл татгалзсан.
@@ -269,9 +333,12 @@ declare class AuthenticationError extends EidError {
269
333
  declare class ForbiddenError extends EidError {
270
334
  /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
271
335
  readonly code?: string | undefined;
336
+ readonly headers?: Headers;
337
+ /** Retry-After (мс), байхгүй бол undefined. */
338
+ readonly retryAfterMs?: number;
272
339
  constructor(message: string,
273
340
  /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
274
- code?: string | undefined);
341
+ code?: string | undefined, init?: HttpErrorInit);
275
342
  }
276
343
  /** RP-API-аас ирсэн бусад HTTP алдаа (4xx/5xx). status ба хариуны биеийг агуулна. */
277
344
  declare class ApiError extends EidError {
@@ -279,10 +346,31 @@ declare class ApiError extends EidError {
279
346
  readonly body: string;
280
347
  /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
281
348
  readonly code?: string | undefined;
349
+ readonly headers?: Headers;
350
+ /**
351
+ * Retry-After (мс, секунд эсвэл HTTP-огноо задалсан); байхгүй/буруу бол undefined. CA түр алдаанд (429
352
+ * PDF_PREPARE_BUSY / RATE_LIMITED, 503 DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE г.м.) тавина.
353
+ */
354
+ readonly retryAfterMs?: number;
282
355
  constructor(status: number, body: string,
283
356
  /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
284
- code?: string | undefined);
357
+ code?: string | undefined, init?: HttpErrorInit);
358
+ /**
359
+ * CA-ийн түр алдаа ({@link RETRYABLE_ERROR_CODES}-ийн нэг, эсвэл 429/503 + Retry-After): `retryAfterMs`-ийн дараа
360
+ * ижил хүсэлтийг давтаж болно.
361
+ */
362
+ get retryable(): boolean;
285
363
  }
364
+ /**
365
+ * CA-ийн түр (давтаж болох) алдааны кодууд — `ApiError.code`. PDF_PREPARE_BUSY (429 prepare, Retry-After 5),
366
+ * DOCUMENT_PENDING (503 document, 5), TSA_UNAVAILABLE (503, 30), LTV_UNAVAILABLE (503, 60), RATE_LIMITED (429 токен),
367
+ * STATE_UNAVAILABLE (503 токен), ISSUANCE_PENDING (503 токены үр дүн, 5).
368
+ */
369
+ type RetryableErrorCode = "PDF_PREPARE_BUSY" | "DOCUMENT_PENDING" | "TSA_UNAVAILABLE" | "LTV_UNAVAILABLE" | "RATE_LIMITED" | "STATE_UNAVAILABLE" | "ISSUANCE_PENDING";
370
+ /** {@link RetryableErrorCode}-ийн бүх утга. */
371
+ declare const RETRYABLE_ERROR_CODES: readonly RetryableErrorCode[];
372
+ /** RFC 9110 §10.2.3: delay-seconds эсвэл HTTP-date → мс. Буруу / өнгөрсөн / 24 цагаас их → undefined. */
373
+ declare function parseRetryAfter(value: string | null | undefined, now?: number): number | undefined;
286
374
  /**
287
375
  * Байгууллагын нэрийн өмнөөс (`onBehalfOf`) урсгалын татгалзлын кодууд — `ForbiddenError.code` /
288
376
  * `ApiError.code`-д ирнэ. 403: REPRESENTATION_DENIED (идэвхтэй төлөөлөгч биш), REPRESENTATION_PENDING (PIN2-оор
@@ -293,8 +381,19 @@ declare class ApiError extends EidError {
293
381
  type OnBehalfErrorCode = "REPRESENTATION_DENIED" | "REPRESENTATION_PENDING" | "REPRESENTATION_EXPIRED" | "ORG_NOT_ACTIVE" | "SIGNER_UNIDENTIFIED" | "ORG_NOT_FOUND" | "REPRESENTATION_REVOKED";
294
382
  /** {@link OnBehalfErrorCode}-ийн бүх утга (runtime шалгалтад). */
295
383
  declare const ON_BEHALF_ERROR_CODES: readonly OnBehalfErrorCode[];
296
- /** Сүлжээ/timeout — upstream хариу өгсөнгүй. */
384
+ /**
385
+ * Сүлжээ/timeout — upstream хариу өгсөнгүй. Мессеж ба `op`-д хувь хүний таних тэмдэг ОРОХГҮЙ (замын `/etsi/{…}`,
386
+ * `/document/{…}` → `…`) — шууд лог руу бичиж болно.
387
+ */
297
388
  declare class NetworkError extends EidError {
389
+ /** "GET /session/…" (таних тэмдэг хасагдсан зам). */
390
+ readonly op?: string;
391
+ /** Хүсэлт хугацаа хэтэрсэн. */
392
+ readonly timeout?: boolean;
393
+ constructor(message: string, init?: {
394
+ op?: string;
395
+ timeout?: boolean;
396
+ });
298
397
  }
299
398
  /**
300
399
  * Session амжилтгүй дууссан (endResult ≠ OK): TIMEOUT, USER_REFUSED*, DOCUMENT_UNUSABLE гм.
@@ -318,4 +417,4 @@ declare class ValidationError extends EidError {
318
417
  declare class DeviceLinkError extends EidError {
319
418
  }
320
419
 
321
- export { type AcspContext as A, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, type HashAlgorithm as H, type Interaction as I, type NotificationSession as N, ON_BEHALF_ERROR_CODES as O, type PdfSessionBlock as P, type RpCredentials as R, type SignatureAlgorithm as S, ValidationError as V, type SessionResult as a, type RepresentationsResponse as b, ApiError as c, AuthenticationError as d, DeviceLinkError as e, type DeviceLinkInput as f, type EndResult as g, ForbiddenError as h, type NavigatorLike as i, NetworkError as j, type OnBehalfErrorCode as k, type OpenOrShowQROptions as l, type OrgResult as m, type PdfDocumentStatus as n, type PdfSigner as o, type Representation as p, SessionFailedError as q, type SessionState as r, type SignatureAlgorithmParameters as s, type SignatureProtocol as t, deviceLink as u, isMobileUserAgent as v, openOrShowQR as w };
420
+ export { type AcspContext as A, isMobileUserAgent as B, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, openOrShowQR as G, type HashAlgorithm as H, type Interaction as I, parseRetryAfter as J, type NotificationSession as N, ON_BEHALF_ERROR_CODES as O, type PersonBlock as P, type RpCredentials as R, type SignatureAlgorithm as S, type TokenCertSessionBlock as T, ValidationError as V, type SessionResult as a, type PdfSessionBlock as b, type RepresentationsResponse as c, ApiError as d, AuthenticationError as e, DeviceLinkError as f, type DeviceLinkInput as g, type EndResult as h, ForbiddenError as i, type HttpErrorInit as j, type NavigatorLike as k, NetworkError as l, type OnBehalfErrorCode as m, type OpenOrShowQROptions as n, type OrgResult as o, type PdfDocumentStatus as p, type PdfSigner as q, RETRYABLE_ERROR_CODES as r, type Representation as s, type RetryableErrorCode as t, SessionFailedError as u, type SessionState as v, type SignatureAlgorithmParameters as w, type SignatureProtocol as x, type TokenCertStatus as y, deviceLink as z };
@@ -1 +1 @@
1
- export { e as DeviceLinkError, f as DeviceLinkInput, D as DeviceLinkSession, E as EidError, i as NavigatorLike, l as OpenOrShowQROptions, u as deviceLink, v as isMobileUserAgent, w as openOrShowQR } from './browser-B_XtyuFG.cjs';
1
+ export { f as DeviceLinkError, g as DeviceLinkInput, D as DeviceLinkSession, E as EidError, k as NavigatorLike, n as OpenOrShowQROptions, z as deviceLink, B as isMobileUserAgent, G as openOrShowQR } from './browser-BWCDj8xO.cjs';
package/dist/browser.d.ts CHANGED
@@ -1 +1 @@
1
- export { e as DeviceLinkError, f as DeviceLinkInput, D as DeviceLinkSession, E as EidError, i as NavigatorLike, l as OpenOrShowQROptions, u as deviceLink, v as isMobileUserAgent, w as openOrShowQR } from './browser-B_XtyuFG.js';
1
+ export { f as DeviceLinkError, g as DeviceLinkInput, D as DeviceLinkSession, E as EidError, k as NavigatorLike, n as OpenOrShowQROptions, z as deviceLink, B as isMobileUserAgent, G as openOrShowQR } from './browser-BWCDj8xO.js';
package/dist/browser.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  deviceLink,
5
5
  isMobileUserAgent,
6
6
  openOrShowQR
7
- } from "./chunk-NXDKQZIW.js";
7
+ } from "./chunk-YDVPZMAL.js";
8
8
  export {
9
9
  DeviceLinkError,
10
10
  EidError,
@@ -6,25 +6,79 @@ var EidError = class extends Error {
6
6
  }
7
7
  };
8
8
  var AuthenticationError = class extends EidError {
9
+ /** Серверийн `{"code"}`; байхгүй / JSON биш (proxy) бол undefined. */
10
+ code;
11
+ /** Серверийн `{"error"}` текст (логт); JSON биш (proxy) бол undefined. */
12
+ serverMessage;
13
+ /** Хариуны бие (таслагдаагүй текст). */
14
+ body;
15
+ headers;
16
+ constructor(message, init) {
17
+ super(message);
18
+ this.code = init?.code;
19
+ this.serverMessage = init?.serverMessage;
20
+ this.body = init?.body;
21
+ this.headers = init?.headers;
22
+ }
9
23
  };
10
24
  var ForbiddenError = class extends EidError {
11
- constructor(message, code) {
25
+ constructor(message, code, init) {
12
26
  super(message);
13
27
  this.code = code;
28
+ this.headers = init?.headers;
29
+ this.retryAfterMs = init?.retryAfterMs;
14
30
  }
15
31
  code;
32
+ headers;
33
+ /** Retry-After (мс), байхгүй бол undefined. */
34
+ retryAfterMs;
16
35
  };
17
36
  var ApiError = class extends EidError {
18
- constructor(status, body, code) {
37
+ constructor(status, body, code, init) {
19
38
  super(`RP-API ${status}: ${body}`);
20
39
  this.status = status;
21
40
  this.body = body;
22
41
  this.code = code;
42
+ this.headers = init?.headers;
43
+ this.retryAfterMs = init?.retryAfterMs;
23
44
  }
24
45
  status;
25
46
  body;
26
47
  code;
48
+ headers;
49
+ /**
50
+ * Retry-After (мс, секунд эсвэл HTTP-огноо задалсан); байхгүй/буруу бол undefined. CA түр алдаанд (429
51
+ * PDF_PREPARE_BUSY / RATE_LIMITED, 503 DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE г.м.) тавина.
52
+ */
53
+ retryAfterMs;
54
+ /**
55
+ * CA-ийн түр алдаа ({@link RETRYABLE_ERROR_CODES}-ийн нэг, эсвэл 429/503 + Retry-After): `retryAfterMs`-ийн дараа
56
+ * ижил хүсэлтийг давтаж болно.
57
+ */
58
+ get retryable() {
59
+ if (this.code && RETRYABLE_ERROR_CODES.includes(this.code)) return true;
60
+ return (this.status === 429 || this.status === 503) && (this.retryAfterMs ?? 0) > 0;
61
+ }
27
62
  };
63
+ var RETRYABLE_ERROR_CODES = [
64
+ "PDF_PREPARE_BUSY",
65
+ "DOCUMENT_PENDING",
66
+ "TSA_UNAVAILABLE",
67
+ "LTV_UNAVAILABLE",
68
+ "RATE_LIMITED",
69
+ "STATE_UNAVAILABLE",
70
+ "ISSUANCE_PENDING"
71
+ ];
72
+ function parseRetryAfter(value, now = Date.now()) {
73
+ const v = (value ?? "").trim();
74
+ if (!v) return void 0;
75
+ if (/^-?\d+$/.test(v)) {
76
+ const n = Number(v);
77
+ return n > 0 && n <= 86400 ? n * 1e3 : void 0;
78
+ }
79
+ const t = Date.parse(v);
80
+ return Number.isNaN(t) || t <= now ? void 0 : t - now;
81
+ }
28
82
  var ON_BEHALF_ERROR_CODES = [
29
83
  "REPRESENTATION_DENIED",
30
84
  "REPRESENTATION_PENDING",
@@ -35,7 +89,19 @@ var ON_BEHALF_ERROR_CODES = [
35
89
  "REPRESENTATION_REVOKED"
36
90
  ];
37
91
  var NetworkError = class extends EidError {
92
+ /** "GET /session/…" (таних тэмдэг хасагдсан зам). */
93
+ op;
94
+ /** Хүсэлт хугацаа хэтэрсэн. */
95
+ timeout;
96
+ constructor(message, init) {
97
+ super(message);
98
+ this.op = init?.op;
99
+ this.timeout = init?.timeout;
100
+ }
38
101
  };
102
+ function maskPath(path) {
103
+ return path.split(/[?#]/)[0].replace(/\/(etsi|document)\/[^/?#\s]+/g, "/$1/\u2026");
104
+ }
39
105
  var SessionFailedError = class extends EidError {
40
106
  constructor(endResult) {
41
107
  super(`Session \u0430\u043C\u0436\u0438\u043B\u0442\u0433\u04AF\u0439: ${endResult}`);
@@ -95,8 +161,11 @@ export {
95
161
  AuthenticationError,
96
162
  ForbiddenError,
97
163
  ApiError,
164
+ RETRYABLE_ERROR_CODES,
165
+ parseRetryAfter,
98
166
  ON_BEHALF_ERROR_CODES,
99
167
  NetworkError,
168
+ maskPath,
100
169
  SessionFailedError,
101
170
  ValidationError,
102
171
  DeviceLinkError,