@gerege-systems/eid-mongolia-sdk 0.9.1 → 0.11.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,48 @@ 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). `pdf.document` санах ойд `pdfMaxBytes` (анхдагч `DEFAULT_PDF_MAX_BYTES` = 64 MiB)
258
+ хүртэл уншина; хэтэрвэл `ResponseTooLargeError`. JSON хариу 4 MiB-ээс хэтэрвэл мөн `ResponseTooLargeError` (таслахгүй).
259
+ `getRepresentations`-ийн `raw` — серверийн түүхий JSON.
260
+
241
261
  Олон талт гэрээ: А байгууллагын гаралтыг өөрчлөлтгүй Б-ийн `prepare`-д (`docID`, өөр `onBehalfOf`) илгээнэ.
242
262
  Session-ий `onBehalfOf` блок (`role`/`rightType`) нь хариу угсрах үеийн бүртгэл; `pdf.signer.onBehalfOf`/`claimedRole`
243
263
  нь баримтад бичигдсэн утга.
244
264
 
265
+ ## 3b. Иргэний PKI самбар (`PKI_READ` эрх)
266
+
267
+ `eid.person.certificates(etsi)` · `devices` · `activity(etsi, { flow, limit, offset })` · `summary` · `children` — иргэн
268
+ сүүлийн 24 цагт танай RP-ээр нэвтэрсэн байх ёстой (эс бөгөөс `ForbiddenError`). Хариу бүрт `raw`.
269
+
270
+ ## 3c. USB токен
271
+
272
+ ```ts
273
+ import { verifyTokenCertificates, tokenAuthDigest, tokenLimitOf } from "@gerege-systems/eid-mongolia-sdk";
274
+
275
+ // гэрчилгээ олголт (TOKEN_ISSUE эрх) — иргэн утсаараа PIN2-оор батална
276
+ const st = await eid.token.issueCertificate({ signerEtsi, csrAuth, csrSign, tokenSerial: "EP2003-0042A1" }); // onBehalfOf алга = хувийн
277
+ // … eid.session.poll(st.sessionId) → result.tokenCertificate?.status === "ISSUED"
278
+ const res = await eid.token.certificateResult(st.sessionId);
279
+ verifyTokenCertificates(res, csrAuth, csrSign); // гэрчилгээ бүр CSR-ийн түлхүүртэй
280
+ // 409 TOKEN_LIMIT_REACHED → tokenLimitOf(err): { kind, limit, organization? }
281
+
282
+ // токеноор нэвтрэх (AUTH эрх)
283
+ const ch = await eid.token.authChallenge();
284
+ const digest = tokenAuthDigest(ch.rpUUID, ch.challenge); // токен CKM_ECDSA → түүхий r||s
285
+ const who = await eid.token.authVerify(ch.challengeId, authCertDer, rs);
286
+
287
+ // токеноор PDF (PAdES, SIGN эрх) — session/утасгүй; байгууллагын токенд onBehalfOf = тухайн байгууллага
288
+ const p = await eid.pdf.tokenPrepare({ pdf, fileName: "Гэрээ.pdf", signerEtsi, certificate: signCertDer });
289
+ const rs = /* токен (PKCS#11): CKM_ECDSA(p.hashToSign) → түүхий r||s, 64 байт — p.expiresAt хүртэл */ new Uint8Array(64);
290
+ const blk = await eid.pdf.tokenFinalize(p.signingId, rs); // READY; 503 DOCUMENT_PENDING → ижил rs-ийг дахин
291
+ const signed = await eid.pdf.tokenDocument(p.signingId); // SHA-256 = blk.outSha256 тулга
292
+ ```
293
+
294
+ `/token/auth/*`, `/pdf/token/*` нь CA-д байгаа ч `/.well-known/eid`-д хараахан жагсаагдаагүй (гэрээний тест WARN).
295
+
245
296
  ## 4. QR ба ижил төхөөрөмж — device link v3
246
297
 
247
298
  Иргэн өөр төхөөрөмжийн дэлгэц дээрх **QR**-ийг утсаараа уншина, эсвэл RP-ийн сайт/аппыг eID апптай **ижил
@@ -330,7 +381,12 @@ OK ирж болзошгүй. Иймд `ResponseValidator`:
330
381
 
331
382
  1. `state === COMPLETE && endResult === OK`
332
383
  2. Иргэний гэрчилгээг **trust anchor (Mongolian National Root CA)** хүртэл chain-аар баталгаажуулна
333
- (Root → National Issuing CA → Gerege Issuing CA → eID Mongolia Issuing CA → иргэн)
384
+ (Root → National Issuing CA → Gerege Issuing CA → eID Mongolia Issuing CA → иргэн) — RFC 5280 замын шалгалт
385
+ (Go SDK-ийн `x509.Certificate.Verify`-тэй ижил дүрэм): issuer нэр + AKI/SKI + гарын үсэг, CA basicConstraints +
386
+ **pathLenConstraint** (04 нь `pathlen:0`), CA-ийн keyUsage keyCertSign, CA бүрийн хугацаа, **EKU** (auth →
387
+ clientAuth, sign → emailProtection — CA-ийн профайл; CA-д EKU байвал түүнд багтах), үл таних **critical
388
+ extension** → татгалзана. **Name constraints**-тэй CA гинжинд байвал fail-closed (CA-ийн бодит гинжинд алга).
389
+ `intermediatesPem`-д 02, 03, 04-ийг бүгдийг өгнө (хариунд гинж ирдэггүй). certificatePolicies-ийг шалгахгүй.
334
390
  3. Гэрчилгээ хүчинтэй хугацаанд + шаардсан `certificateLevel` (default `QUALIFIED`)
335
391
  4. Гарын үсгийг иргэний public key-ээр шалгана:
336
392
  - **auth** → ACSP_V2 payload (`smart-id|ACSP_V2|serverRandom|rpChallenge|userChallenge|B64(rpName)|B64(brokeredRpName)|B64(SHA-256(interactions))|interactionTypeUsed|initialCallbackUrl|flowType`, hash = хүсэлтийн `hashAlgorithm`) дээр — `flowType`, `userChallenge`, `serverRandom` ба алгоритмын downgrade-ийг мөн шалгана;
@@ -345,18 +401,26 @@ OK ирж болзошгүй. Иймд `ResponseValidator`:
345
401
  зөвхөн SHA-256 digest зурдаг (өөр hash хүссэн session-д сервер гарын үсэг гаргахгүй), тиймээс SHA-384/512
346
402
  хүссэн session-д `ecdsa-with-SHA256` хариу ирвэл хуурамч гэж татгалзана.
347
403
 
348
- Аль нэг алхам бүтэлгүйтвэл `ValidationError` шиднэ — хариунд **итгэхгүй**.
404
+ 5. Цуцлалт — `trust.revocation` (OCSP/CRL hook) → `checkRevocation`, эсвэл `checkRevocationStatus` (`"good"` /
405
+ `"unchecked"` (soft-fail) / `"skipped"` (hook алга)).
406
+ 6. Нэмэлт (сонголттой): `allowedIssuerSkis: [CITIZEN_ISSUING_CA_SKI]` — зөвхөн иргэний Issuing CA (04)-ийн гэрчилгээ;
407
+ `checkKeyUsage: true` — auth → clientAuth, sign → nonRepudiation (CA-ийн профайл); `schemeName` — CA-ийн
408
+ `acsp_scheme_name` (анхдагч `smart-id`).
409
+
410
+ Аль нэг алхам бүтэлгүйтвэл `ValidationError` шиднэ — хариунд **итгэхгүй**. Алдааны текстэд иргэний нэр, PNOMN, РД
411
+ ордоггүй (`NetworkError.op`-д замыг `/etsi/…` болгоно) — шууд лог руу бичиж болно.
349
412
 
350
413
  ## Алдааны төрлүүд
351
414
 
352
415
  | Алдаа | Утга |
353
416
  |---|---|
354
- | `AuthenticationError` | 401 — API secret буруу/байхгүй |
417
+ | `AuthenticationError` | 401 — API secret буруу/байхгүй; CA-ийн JSON бол `.serverMessage` (+ `.code`), proxy-ийнх бол алга (`.body`, `.headers`) |
355
418
  | `ForbiddenError` | 403 — IP allowlist / mTLS / RP-ийн эрх; onBehalfOf татгалзвал `.code` (`OnBehalfErrorCode`) |
356
419
  | `SessionFailedError` | endResult ≠ OK (TIMEOUT, USER_REFUSED…) — `.endResult`-оор салга |
357
420
  | `ValidationError` | cert chain/signature/level шалгалт бүтэлгүйтсэн |
358
- | `ApiError` | бусад HTTP алдаа (`.status`, `.body`, серверийн `.code` — ж: 404 `ORG_NOT_FOUND`) |
359
- | `NetworkError` | timeout/сүлжээ |
421
+ | `ApiError` | бусад HTTP алдаа (`.status`, `.body`, серверийн `.code`, `.retryAfterMs`, `.headers`, `.retryable` — ж: 404 `ORG_NOT_FOUND`) |
422
+ | `ResponseTooLargeError` | хариу SDK-ийн хязгаараас их (JSON 4 MiB, PDF `pdfMaxBytes`) — `.op`, `.limit` |
423
+ | `NetworkError` | timeout/сүлжээ (`.op` — таних тэмдэггүй зам, `.timeout`) |
360
424
  | `EidOidcError` | «eID-ээр нэвтрэх» (OIDC) — `.code`: `access_denied`, `login_required`, `state_mismatch`, `invalid_grant`, `invalid_id_token`, `server_error` … |
361
425
  | `DeviceLinkError` | device-link холбоос угсрах боломжгүй (`deviceLinkBase` https биш, token/secret/lang хэлбэр, төрөлд хэрэгтэй талбар дутуу) |
362
426
 
@@ -377,3 +441,16 @@ npm install
377
441
  npm run lint # tsc --noEmit
378
442
  npm test # build + node --test
379
443
  ```
444
+
445
+ ### Гэрээний шалгалт
446
+
447
+ SDK-ийн дууддаг RP-API зам бүр (`src/contract.ts`) live [`/.well-known/eid`](https://rp.eidmongolia.mn/.well-known/eid)-ийн
448
+ `endpoints`-д байх ёстой; OIDC-ийн хүлээдэг утгууд (issuer, `ES256`, PKCE `S256`, `client_secret_basic`, `EidIdTokenClaims`-ийн
449
+ талбарууд) [discovery](https://login.eidmongolia.mn/.well-known/openid-configuration)-тэй таарах ёстой. Discovery-д
450
+ байгаа ч SDK хамраагүй замууд WARN (fail биш). Сүлжээ шаарддаг тул `npm test`-д ордоггүй:
451
+
452
+ ```bash
453
+ npm run test:contract
454
+ ```
455
+
456
+ 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";
@@ -185,6 +225,8 @@ interface Representation {
185
225
  interface RepresentationsResponse {
186
226
  personEtsi: string;
187
227
  representations: Representation[];
228
+ /** Серверийн түүхий JSON (SDK задлаагүй шинэ талбарт). */
229
+ raw?: unknown;
188
230
  }
189
231
  /** Session-ий байгууллагын блок — байгууллага + тухайн иргэний эрх (бүртгэлээс real-time). */
190
232
  interface OrgResult {
@@ -259,8 +301,32 @@ declare function openOrShowQR(session: DeviceLinkInput, opts: OpenOrShowQROption
259
301
  declare class EidError extends Error {
260
302
  constructor(message: string);
261
303
  }
262
- /** HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. */
304
+ /** HTTP хариуны нэмэлт мэдээлэл (алдааны төрлүүдэд). */
305
+ interface HttpErrorInit {
306
+ /** Хариуны header-ууд. */
307
+ headers?: Headers;
308
+ /** Retry-After (секунд эсвэл HTTP-огноо задалсан), мс; байхгүй/буруу бол undefined. */
309
+ retryAfterMs?: number;
310
+ }
311
+ /**
312
+ * HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. CA-ийн өөрийн 401 нь JSON `{"error"[,"code"]}` —
313
+ * `serverMessage` (ба байвал `code`) бөглөгдөнө; JSON биш (proxy / nginx) бол хоёул undefined — үүгээр CA-ийн 401-ийг
314
+ * proxy-ийнхоос ялгана.
315
+ */
263
316
  declare class AuthenticationError extends EidError {
317
+ /** Серверийн `{"code"}`; байхгүй / JSON биш (proxy) бол undefined. */
318
+ readonly code?: string;
319
+ /** Серверийн `{"error"}` текст (логт); JSON биш (proxy) бол undefined. */
320
+ readonly serverMessage?: string;
321
+ /** Хариуны бие (таслагдаагүй текст). */
322
+ readonly body?: string;
323
+ readonly headers?: Headers;
324
+ constructor(message: string, init?: {
325
+ code?: string;
326
+ serverMessage?: string;
327
+ body?: string;
328
+ headers?: Headers;
329
+ });
264
330
  }
265
331
  /**
266
332
  * HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй, RP-д эрх алга, эсвэл onBehalfOf-ийн төлөөлөл татгалзсан.
@@ -269,9 +335,12 @@ declare class AuthenticationError extends EidError {
269
335
  declare class ForbiddenError extends EidError {
270
336
  /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
271
337
  readonly code?: string | undefined;
338
+ readonly headers?: Headers;
339
+ /** Retry-After (мс), байхгүй бол undefined. */
340
+ readonly retryAfterMs?: number;
272
341
  constructor(message: string,
273
342
  /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
274
- code?: string | undefined);
343
+ code?: string | undefined, init?: HttpErrorInit);
275
344
  }
276
345
  /** RP-API-аас ирсэн бусад HTTP алдаа (4xx/5xx). status ба хариуны биеийг агуулна. */
277
346
  declare class ApiError extends EidError {
@@ -279,10 +348,31 @@ declare class ApiError extends EidError {
279
348
  readonly body: string;
280
349
  /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
281
350
  readonly code?: string | undefined;
351
+ readonly headers?: Headers;
352
+ /**
353
+ * Retry-After (мс, секунд эсвэл HTTP-огноо задалсан); байхгүй/буруу бол undefined. CA түр алдаанд (429
354
+ * PDF_PREPARE_BUSY / RATE_LIMITED, 503 DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE г.м.) тавина.
355
+ */
356
+ readonly retryAfterMs?: number;
282
357
  constructor(status: number, body: string,
283
358
  /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
284
- code?: string | undefined);
359
+ code?: string | undefined, init?: HttpErrorInit);
360
+ /**
361
+ * CA-ийн түр алдаа ({@link RETRYABLE_ERROR_CODES}-ийн нэг, эсвэл 429/503 + Retry-After): `retryAfterMs`-ийн дараа
362
+ * ижил хүсэлтийг давтаж болно.
363
+ */
364
+ get retryable(): boolean;
285
365
  }
366
+ /**
367
+ * CA-ийн түр (давтаж болох) алдааны кодууд — `ApiError.code`. PDF_PREPARE_BUSY (429 prepare, Retry-After 5),
368
+ * DOCUMENT_PENDING (503 document, 5), TSA_UNAVAILABLE (503, 30), LTV_UNAVAILABLE (503, 60), RATE_LIMITED (429 токен),
369
+ * STATE_UNAVAILABLE (503 токен), ISSUANCE_PENDING (503 токены үр дүн, 5).
370
+ */
371
+ type RetryableErrorCode = "PDF_PREPARE_BUSY" | "DOCUMENT_PENDING" | "TSA_UNAVAILABLE" | "LTV_UNAVAILABLE" | "RATE_LIMITED" | "STATE_UNAVAILABLE" | "ISSUANCE_PENDING";
372
+ /** {@link RetryableErrorCode}-ийн бүх утга. */
373
+ declare const RETRYABLE_ERROR_CODES: readonly RetryableErrorCode[];
374
+ /** RFC 9110 §10.2.3: delay-seconds эсвэл HTTP-date → мс. Буруу / өнгөрсөн / 24 цагаас их → undefined. */
375
+ declare function parseRetryAfter(value: string | null | undefined, now?: number): number | undefined;
286
376
  /**
287
377
  * Байгууллагын нэрийн өмнөөс (`onBehalfOf`) урсгалын татгалзлын кодууд — `ForbiddenError.code` /
288
378
  * `ApiError.code`-д ирнэ. 403: REPRESENTATION_DENIED (идэвхтэй төлөөлөгч биш), REPRESENTATION_PENDING (PIN2-оор
@@ -293,8 +383,28 @@ declare class ApiError extends EidError {
293
383
  type OnBehalfErrorCode = "REPRESENTATION_DENIED" | "REPRESENTATION_PENDING" | "REPRESENTATION_EXPIRED" | "ORG_NOT_ACTIVE" | "SIGNER_UNIDENTIFIED" | "ORG_NOT_FOUND" | "REPRESENTATION_REVOKED";
294
384
  /** {@link OnBehalfErrorCode}-ийн бүх утга (runtime шалгалтад). */
295
385
  declare const ON_BEHALF_ERROR_CODES: readonly OnBehalfErrorCode[];
296
- /** Сүлжээ/timeout — upstream хариу өгсөнгүй. */
386
+ /**
387
+ * Сүлжээ/timeout — upstream хариу өгсөнгүй. Мессеж ба `op`-д хувь хүний таних тэмдэг ОРОХГҮЙ (замын `/etsi/{…}`,
388
+ * `/document/{…}` → `…`) — шууд лог руу бичиж болно.
389
+ */
297
390
  declare class NetworkError extends EidError {
391
+ /** "GET /session/…" (таних тэмдэг хасагдсан зам). */
392
+ readonly op?: string;
393
+ /** Хүсэлт хугацаа хэтэрсэн. */
394
+ readonly timeout?: boolean;
395
+ constructor(message: string, init?: {
396
+ op?: string;
397
+ timeout?: boolean;
398
+ });
399
+ }
400
+ /**
401
+ * Хариуны бие SDK-ийн хязгаараас их (JSON 4 MiB, PDF `pdfMaxBytes`) — бие таслагдаж задлагдахгүй. `op` — method +
402
+ * таних тэмдэггүй зам.
403
+ */
404
+ declare class ResponseTooLargeError extends EidError {
405
+ readonly op: string;
406
+ readonly limit: number;
407
+ constructor(op: string, limit: number);
298
408
  }
299
409
  /**
300
410
  * Session амжилтгүй дууссан (endResult ≠ OK): TIMEOUT, USER_REFUSED*, DOCUMENT_UNUSABLE гм.
@@ -318,4 +428,4 @@ declare class ValidationError extends EidError {
318
428
  declare class DeviceLinkError extends EidError {
319
429
  }
320
430
 
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 };
431
+ export { type AcspContext as A, deviceLink as B, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, isMobileUserAgent as G, type HashAlgorithm as H, type Interaction as I, openOrShowQR as J, parseRetryAfter as K, 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, ResponseTooLargeError as t, type RetryableErrorCode as u, SessionFailedError as v, type SessionState as w, type SignatureAlgorithmParameters as x, type SignatureProtocol as y, type TokenCertStatus 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";
@@ -185,6 +225,8 @@ interface Representation {
185
225
  interface RepresentationsResponse {
186
226
  personEtsi: string;
187
227
  representations: Representation[];
228
+ /** Серверийн түүхий JSON (SDK задлаагүй шинэ талбарт). */
229
+ raw?: unknown;
188
230
  }
189
231
  /** Session-ий байгууллагын блок — байгууллага + тухайн иргэний эрх (бүртгэлээс real-time). */
190
232
  interface OrgResult {
@@ -259,8 +301,32 @@ declare function openOrShowQR(session: DeviceLinkInput, opts: OpenOrShowQROption
259
301
  declare class EidError extends Error {
260
302
  constructor(message: string);
261
303
  }
262
- /** HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. */
304
+ /** HTTP хариуны нэмэлт мэдээлэл (алдааны төрлүүдэд). */
305
+ interface HttpErrorInit {
306
+ /** Хариуны header-ууд. */
307
+ headers?: Headers;
308
+ /** Retry-After (секунд эсвэл HTTP-огноо задалсан), мс; байхгүй/буруу бол undefined. */
309
+ retryAfterMs?: number;
310
+ }
311
+ /**
312
+ * HTTP 401 — API secret буруу/байхгүй. Тохиргооны (RP secret) алдаа. CA-ийн өөрийн 401 нь JSON `{"error"[,"code"]}` —
313
+ * `serverMessage` (ба байвал `code`) бөглөгдөнө; JSON биш (proxy / nginx) бол хоёул undefined — үүгээр CA-ийн 401-ийг
314
+ * proxy-ийнхоос ялгана.
315
+ */
263
316
  declare class AuthenticationError extends EidError {
317
+ /** Серверийн `{"code"}`; байхгүй / JSON биш (proxy) бол undefined. */
318
+ readonly code?: string;
319
+ /** Серверийн `{"error"}` текст (логт); JSON биш (proxy) бол undefined. */
320
+ readonly serverMessage?: string;
321
+ /** Хариуны бие (таслагдаагүй текст). */
322
+ readonly body?: string;
323
+ readonly headers?: Headers;
324
+ constructor(message: string, init?: {
325
+ code?: string;
326
+ serverMessage?: string;
327
+ body?: string;
328
+ headers?: Headers;
329
+ });
264
330
  }
265
331
  /**
266
332
  * HTTP 403 — IP allowlist/mTLS зөвшөөрөөгүй, RP-д эрх алга, эсвэл onBehalfOf-ийн төлөөлөл татгалзсан.
@@ -269,9 +335,12 @@ declare class AuthenticationError extends EidError {
269
335
  declare class ForbiddenError extends EidError {
270
336
  /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
271
337
  readonly code?: string | undefined;
338
+ readonly headers?: Headers;
339
+ /** Retry-After (мс), байхгүй бол undefined. */
340
+ readonly retryAfterMs?: number;
272
341
  constructor(message: string,
273
342
  /** Серверийн тогтвортой алдааны код (`{"code"}`), байхгүй бол undefined. */
274
- code?: string | undefined);
343
+ code?: string | undefined, init?: HttpErrorInit);
275
344
  }
276
345
  /** RP-API-аас ирсэн бусад HTTP алдаа (4xx/5xx). status ба хариуны биеийг агуулна. */
277
346
  declare class ApiError extends EidError {
@@ -279,10 +348,31 @@ declare class ApiError extends EidError {
279
348
  readonly body: string;
280
349
  /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
281
350
  readonly code?: string | undefined;
351
+ readonly headers?: Headers;
352
+ /**
353
+ * Retry-After (мс, секунд эсвэл HTTP-огноо задалсан); байхгүй/буруу бол undefined. CA түр алдаанд (429
354
+ * PDF_PREPARE_BUSY / RATE_LIMITED, 503 DOCUMENT_PENDING / TSA_UNAVAILABLE / LTV_UNAVAILABLE г.м.) тавина.
355
+ */
356
+ readonly retryAfterMs?: number;
282
357
  constructor(status: number, body: string,
283
358
  /** Серверийн тогтвортой алдааны код (`{"error","code"}`-ийн `code`), байхгүй бол undefined. */
284
- code?: string | undefined);
359
+ code?: string | undefined, init?: HttpErrorInit);
360
+ /**
361
+ * CA-ийн түр алдаа ({@link RETRYABLE_ERROR_CODES}-ийн нэг, эсвэл 429/503 + Retry-After): `retryAfterMs`-ийн дараа
362
+ * ижил хүсэлтийг давтаж болно.
363
+ */
364
+ get retryable(): boolean;
285
365
  }
366
+ /**
367
+ * CA-ийн түр (давтаж болох) алдааны кодууд — `ApiError.code`. PDF_PREPARE_BUSY (429 prepare, Retry-After 5),
368
+ * DOCUMENT_PENDING (503 document, 5), TSA_UNAVAILABLE (503, 30), LTV_UNAVAILABLE (503, 60), RATE_LIMITED (429 токен),
369
+ * STATE_UNAVAILABLE (503 токен), ISSUANCE_PENDING (503 токены үр дүн, 5).
370
+ */
371
+ type RetryableErrorCode = "PDF_PREPARE_BUSY" | "DOCUMENT_PENDING" | "TSA_UNAVAILABLE" | "LTV_UNAVAILABLE" | "RATE_LIMITED" | "STATE_UNAVAILABLE" | "ISSUANCE_PENDING";
372
+ /** {@link RetryableErrorCode}-ийн бүх утга. */
373
+ declare const RETRYABLE_ERROR_CODES: readonly RetryableErrorCode[];
374
+ /** RFC 9110 §10.2.3: delay-seconds эсвэл HTTP-date → мс. Буруу / өнгөрсөн / 24 цагаас их → undefined. */
375
+ declare function parseRetryAfter(value: string | null | undefined, now?: number): number | undefined;
286
376
  /**
287
377
  * Байгууллагын нэрийн өмнөөс (`onBehalfOf`) урсгалын татгалзлын кодууд — `ForbiddenError.code` /
288
378
  * `ApiError.code`-д ирнэ. 403: REPRESENTATION_DENIED (идэвхтэй төлөөлөгч биш), REPRESENTATION_PENDING (PIN2-оор
@@ -293,8 +383,28 @@ declare class ApiError extends EidError {
293
383
  type OnBehalfErrorCode = "REPRESENTATION_DENIED" | "REPRESENTATION_PENDING" | "REPRESENTATION_EXPIRED" | "ORG_NOT_ACTIVE" | "SIGNER_UNIDENTIFIED" | "ORG_NOT_FOUND" | "REPRESENTATION_REVOKED";
294
384
  /** {@link OnBehalfErrorCode}-ийн бүх утга (runtime шалгалтад). */
295
385
  declare const ON_BEHALF_ERROR_CODES: readonly OnBehalfErrorCode[];
296
- /** Сүлжээ/timeout — upstream хариу өгсөнгүй. */
386
+ /**
387
+ * Сүлжээ/timeout — upstream хариу өгсөнгүй. Мессеж ба `op`-д хувь хүний таних тэмдэг ОРОХГҮЙ (замын `/etsi/{…}`,
388
+ * `/document/{…}` → `…`) — шууд лог руу бичиж болно.
389
+ */
297
390
  declare class NetworkError extends EidError {
391
+ /** "GET /session/…" (таних тэмдэг хасагдсан зам). */
392
+ readonly op?: string;
393
+ /** Хүсэлт хугацаа хэтэрсэн. */
394
+ readonly timeout?: boolean;
395
+ constructor(message: string, init?: {
396
+ op?: string;
397
+ timeout?: boolean;
398
+ });
399
+ }
400
+ /**
401
+ * Хариуны бие SDK-ийн хязгаараас их (JSON 4 MiB, PDF `pdfMaxBytes`) — бие таслагдаж задлагдахгүй. `op` — method +
402
+ * таних тэмдэггүй зам.
403
+ */
404
+ declare class ResponseTooLargeError extends EidError {
405
+ readonly op: string;
406
+ readonly limit: number;
407
+ constructor(op: string, limit: number);
298
408
  }
299
409
  /**
300
410
  * Session амжилтгүй дууссан (endResult ≠ OK): TIMEOUT, USER_REFUSED*, DOCUMENT_UNUSABLE гм.
@@ -318,4 +428,4 @@ declare class ValidationError extends EidError {
318
428
  declare class DeviceLinkError extends EidError {
319
429
  }
320
430
 
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 };
431
+ export { type AcspContext as A, deviceLink as B, type CertificateLevel as C, type DeviceLinkSession as D, EidError as E, type FlowType as F, isMobileUserAgent as G, type HashAlgorithm as H, type Interaction as I, openOrShowQR as J, parseRetryAfter as K, 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, ResponseTooLargeError as t, type RetryableErrorCode as u, SessionFailedError as v, type SessionState as w, type SignatureAlgorithmParameters as x, type SignatureProtocol as y, type TokenCertStatus 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, B as deviceLink, G as isMobileUserAgent, J as openOrShowQR } from './browser-BVR6gtrx.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, B as deviceLink, G as isMobileUserAgent, J as openOrShowQR } from './browser-BVR6gtrx.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-QPJDZLHR.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,28 @@ 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
+ var ResponseTooLargeError = class extends EidError {
103
+ constructor(op, limit) {
104
+ super(`${op}: \u0445\u0430\u0440\u0438\u0443 ${limit} \u0431\u0430\u0439\u0442\u0430\u0430\u0441 \u0438\u0445`);
105
+ this.op = op;
106
+ this.limit = limit;
107
+ }
108
+ op;
109
+ limit;
110
+ };
111
+ function maskPath(path) {
112
+ return path.split(/[?#]/)[0].replace(/\/(etsi|document)\/[^/?#\s]+/g, "/$1/\u2026");
113
+ }
39
114
  var SessionFailedError = class extends EidError {
40
115
  constructor(endResult) {
41
116
  super(`Session \u0430\u043C\u0436\u0438\u043B\u0442\u0433\u04AF\u0439: ${endResult}`);
@@ -95,8 +170,12 @@ export {
95
170
  AuthenticationError,
96
171
  ForbiddenError,
97
172
  ApiError,
173
+ RETRYABLE_ERROR_CODES,
174
+ parseRetryAfter,
98
175
  ON_BEHALF_ERROR_CODES,
99
176
  NetworkError,
177
+ ResponseTooLargeError,
178
+ maskPath,
100
179
  SessionFailedError,
101
180
  ValidationError,
102
181
  DeviceLinkError,