الأخطاء
كل إخفاق يرمي استثناءً. صنفان اثنان، ومعرّف طلب على كل خطأ من API.
التقاط خطأ
import { OpenEmailApiError, OpenEmailNetworkError, openemail } from '@openemail/sdk' try { await openemail.emails.send(message)} catch (error) { if (error instanceof OpenEmailApiError) { if (error.isValidation) console.error(error.code, error.param, error.message) if (error.isPermission) console.error(await openemail.addresses.list()) if (error.isRateLimited) console.error('try again in', error.retryAfterSeconds, 'seconds') console.error(error.status, error.requestId) } if (error instanceof OpenEmailNetworkError && error.isTimeout) console.error('no answer in time') throw error}الخطأ permission_error عند الإرسال يعود عادةً إلى نطاق الإرسال الخاص بالمفتاح، أي نطاق أو عنوان لم يُمنح له، لا إلى مساحة العمل، ولهذا يطبع المثال ما تقوله addresses.list() عن العناوين التي يجوز لهذا المفتاح الإرسال باسمها.
الأصناف
| الصنف | متى |
|---|---|
| `OpenEmailApiError` | استجاب API، ولم يكن الرد نجاحًا. يحمل status وtype وcode وparam وdocUrl وrequestId وretryAfterSeconds. |
| `OpenEmailNetworkError` | لم تصل أي استجابة: DNS أو TLS أو اتصال انقطع أو انتهاء المهلة أو AbortSignal الخاص بك. يحمل cause، وتكون isTimeout بقيمة true عندما يكون انتهاء المهلة هو السبب. |
| `Error` | يُرمى قبل إرسال أي شيء: مفتاح مفقود أو مشوَّه، أو baseUrl غير صالح للاستخدام، أو متصفح، أو معرّف فارغ. |
| الخاصية القارئة | تكون true عندما |
|---|---|
| `isAuth` | تكون type بقيمة authentication_error، أي 401: لا مفتاح، أو نوع اعتماد خاطئ، أو مفتاح لم نُصدره. |
| `isPermission` | permission_error، أي 403: مفتاح حقيقي ينقصه النطاق أو عنوان From الذي يحتاجه. |
| `isScopeMissing` | تكون code بقيمة insufficient_scope، وهو الخطأ 403 الذي يسمّي نطاقًا مفقودًا. |
| `isInvalidRequest` | invalid_request_error، أي 400: طلب تعذّر فهمه. أما الرسالة التي تتجاوز سقف الحجم فتعود بالخطأ 422 message_too_large، ولذا فإن isValidation هي الخاصية القارئة التي تلتقطها. |
| `isValidation` | validation_error، أي 422: رفضه المخطط، وparam تسمّي الحقل. |
| `isNotFound` | not_found_error، أي 404: لا وجود لهذا المورد. |
| `isConflict` | conflict_error، أي 409: تجاوز المورد النقطة التي كان يمكن عندها تنفيذ هذا عليه. |
| `isRateLimited` | rate_limit_error، أي 429. وتحمل retryAfterSeconds مدة الانتظار عندما يحدّدها الخادم. |
| `isServerError` | تكون status بقيمة 500 أو أعلى. اذكر requestId إن تواصلت مع الدعم. |
| `isRetryable` | تكون status بقيمة 408 أو 429 أو 500 أو 502 أو 503 أو 504. |
تقرأ الخصائص القارئة الحقل type، وهو النصف المجمَّد من الغلاف. أما code فيبقى string، لأن API يضمن أنه مفتوح وتراكمي، فعامِل أي قيمة لا تعرفها معاملة type الخاص بها. والاتحاد المغلق كان سيجعل ترقية SDK ثمنًا لقراءة وضع إخفاق جديد.
المتن الذي لا يطابق غلاف الخطأ الخاص بـAPI يصبح مع ذلك OpenEmailApiError، مع استنتاج type من رمز الحالة وضبط code على unrecognised_response. والاستجابة الناجحة التي ليس متنها JSON ترمي الخطأ نفسه.
الإجهاض هو كذلك OpenEmailNetworkError، سواء وقع أثناء الطلب أو أثناء الانتظار قبل إعادة المحاولة، ويُحفظ الإجهاض على cause. تحقّق من signal.aborted حين تحتاج إلى تمييز إلغائك أنت عن إخفاق في الشبكة.
requestId
يحمل كل OpenEmailApiError معرّف الطلب الذي أرسله الخادم، من متن الخطأ أو من الترويسة x-request-id، وهو الشيء الوحيد الذي يربط إخفاقك بسطر في سجل الخادم. أما الاستجابة الناجحة فتُحَل إلى المتن المحلَّل وحده، فلا يوجد فيها معرّف طلب لقراءته.