مسارات استدعاء مقروءة من JavaScript المصغّر: خرائط المصدر وتجميع الأخطاء
السطر index-8f3a1c.js:1:48213 لا يخبرك بشيء. ارفع خرائط المصدر من البناء ليشير كل خطأ إلى ملفك وسطرك ودالتك، وتعرّف على طريقة تجميع الأخطاء في مشكلات.
6 دقائق قراءة

السطر index-8f3a1c.js:1:48213 لا يخبرك بشيء. ارفع خرائط المصدر من البناء ليشير كل خطأ إلى ملفك وسطرك ودالتك، وتعرّف على طريقة تجميع الأخطاء في مشكلات.
6 دقائق قراءة


معظم المستخدمين لا يبلّغون عن الأعطال، بل يغادرون. أعدّ تتبّع الأعطال في تطبيق Flutter خلال دقائق: ما الذي يُلتقط، وكيف تضيف السياق، وكيف تتخلص من الضجيج.
5 دقائق قراءة

لدى مستخدمك هاتف وحاسوب وتبويب في المتصفح. توقّف عن تخزين رموز الأجهزة: اربط الأجهزة بمعرّف المستخدم في نظامك وأرسل مرة واحدة إلى iOS وأندرويد والويب وmacOS.
شيفرة JavaScript في بيئة الإنتاج مصغّرة (minified): تتقلص أسماء المتغيرات إلى حرف واحد، وتُدمج الملفات في أجزاء (chunks) بأسماء مشتقة من محتواها، وكثيراً ما يصبح الملف كله سطراً واحداً. هذا ممتاز لسرعة التحميل وسيئ جداً لتتبّع الأخطاء. يصلك الخطأ من الإنتاج هكذا:
TypeError: Cannot read properties of undefined (reading 'id')
at e.r (index-8f3a1c.js:1:48213)
at Object.o (index-8f3a1c.js:1:51007)
لا ملف تعرفه، ولا اسم دالة، ولا سطر يمكنك فتحه. خرائط المصدر (source maps) تحلّ هذه المشكلة. أداة البناء تعرف أصلاً كيف يقابل كل حرف في الناتج شيفرتك الأصلية؛ ارفع هذه الخريطة، فيصلك الخطأ نفسه على أنه الدالة submitOrder في الملف src/checkout/submit.ts السطر 42، مع الشيفرة المحيطة به.
يشرح هذا الدليل الإعداد مع Vite وCreate React App وNext.js وAngular وFlutter Web، ثم يوضح كيف يجمع ليركس الأخطاء الناتجة في مشكلات.
فعّل الخرائط، ويُفضّل أن تكون مخفية (hidden): تُكتب ملفات .map لكن الحزمة لا تشير إليها، فلا يحمّلها المتصفح أبداً.
| الإعداد | الخيار | مجلد البناء |
|---|---|---|
| Vite (React وVue وSvelte…) | build: { sourcemap: 'hidden' } في vite.config.ts |
dist |
| Create React App | مفعّلة افتراضياً (ما لم تضبط GENERATE_SOURCEMAP=false) |
build |
| Next.js | productionBrowserSourceMaps: true في next.config |
.next |
| Angular | "sourceMap": { "scripts": true, "hidden": true } في angular.json |
قيمة outputPath للمشروع |
| Flutter Web | flutter build web --source-maps |
build/web |
أمر واحد يُشغَّل مباشرة بعد البناء:
npx @lerix-dev/lerix-cli upload
تجد الأداة مجلد البناء بنفسها (outputPath في Angular، أو .next أو build/web أو dist أو build أو out)، وترفع كل ملف .js.map تجده (وتتجاوز خرائط CSS وnode_modules)، ثم تحذف الخرائط من ناتج البناء حتى لا تُنشر شيفرتك الأصلية أبداً.
وتقرأ إعداداتها من متغيرات البيئة:
| المتغير | ما هو |
|---|---|
LERIX_API_KEY |
مفتاح الـ API العام لمشروعك |
LERIX_PROJECT_ID |
الاسم المختصر (slug) للمشروع |
LERIX_SECRET_KEY |
مفتاح خاص بصلاحية رفع خرائط المصدر، من إعدادات المشروع ← مفاتيح API |
LERIX_RELEASE |
الإصدار الذي يُنشر به هذا البناء؛ وافتراضياً قيمة version في package.json (أو pubspec.yaml) |
لماذا مفتاح خاص؟ المفتاح العام موجود داخل كل حزمة ويب، ولو اكتُفي به لاستطاع أي شخص استبدال خرائطك وتزوير مسارات الاستدعاء. احتفظ بالمفتاح الخاص في بيئة CI لا داخل التطبيق.
--optional لتشغيل الرفع من سكربت postbuild: على جهاز المطوّر الذي لا يحوي LERIX_SECRET_KEY يُتجاوز الرفع بدل أن يفشل البناء. ومع pnpm أو Yarn 2+، اللذين لا يشغّلان postbuild، اربطه بأمر البناء: "build": "vite build && lerix upload --optional".لا تُحذف الخرائط إلا إذا نجح رفعها كلها، فيمكنك إعادة تشغيل الأمر كما هو عند الفشل. استخدم --keep-maps للإبقاء عليها، و--dry-run لترى ما سيُرسل دون إرساله.
يطابق ليركس الخرائط أولاً حسب اسم ملف السكربت، ولأن أسماء الأجزاء مشتقة من محتواها فهذا يكفي في أغلب الحالات. تمرير الإصدار نفسه إلى الحزمة البرمجية يجعل المطابقة دقيقة، ويوسم كل خطأ بالإصدار الذي جاء منه:
<LerixProvider
options={{
apiKey: 'your-api-key',
projectId: 'your-project-slug',
release: import.meta.env.VITE_LERIX_RELEASE,
}}
>
<App />
</LerixProvider>
في Next.js يقرأ LerixNextProvider المتغير NEXT_PUBLIC_LERIX_RELEASE بنفسه، وفي Angular هو الخيار release في provideLerix(). استخدم قيمة لا تكتبها يدوياً: قيمة version في package.json أو معرّف الـ commit.
1.4.0 بدل معرّف الـ commit، لأن قواعد التحديث تقارن الإصدارات كأرقام.jobs:
deploy:
runs-on: ubuntu-latest
env:
LERIX_RELEASE: ${{ github.sha }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
# The same release, available to the app at build time
- run: npm run build
env:
VITE_LERIX_RELEASE: ${{ github.sha }}
- run: npx @lerix-dev/lerix-cli upload
env:
LERIX_API_KEY: ${{ secrets.LERIX_API_KEY }}
LERIX_PROJECT_ID: ${{ secrets.LERIX_PROJECT_ID }}
LERIX_SECRET_KEY: ${{ secrets.LERIX_SECRET_KEY }}
# Deploy dist here. It no longer contains .map files.
يتم ربط الخرائط على الخادم لحظة وصول الخطأ. تعرض الأخطاء الجديدة الملف الأصلي والسطر والدالة، مع خمسة أسطر من الشيفرة قبل السطر المتعطل وبعده. وزر Show minified يعرض مسار الاستدعاء كما أرسله المتصفح تماماً.
ومسار الاستدعاء المقروء هو أيضاً ما يراه مساعد الذكاء الاصطناعي ومشكلات GitHub وخادم MCP. أمران يجب معرفتهما:
عشرة آلاف تقرير عن خلل واحد يجب أن تكون سطراً واحداً في صندوقك، لا عشرة آلاف سطر. يعطي ليركس كل خطأ بصمة من جزأين:
PGRST202 أو #441 فتبقى كما هي.node_modules والمتصفح والحزمة البرمجية.
ولأن رقم السطر واسم الجزء مستبعدان، يبقى الخطأ المشكلةَ نفسها عبر عمليات النشر بدل أن يبدأ من الصفر مع كل بناء. ولأن مسار الاستدعاء يُربط بالخرائط قبل أخذ البصمة، يعتمد التجميع على أسماء دوالك الحقيقية.
تتتبّع كل مشكلة عدد مرات حدوثها والمستخدمين المتأثرين وأول ظهور وآخر ظهور. والحالة تسري على كل التكرارات:
بعض أخطاء المتصفح لا تحمل أي معلومة. يُسقطها الخادم لكل المشاريع:
ResizeObserver loop limit exceeded وResizeObserver loop completed with undelivered notificationsScript error.، وهي الرسالة البديلة التي يعرضها المتصفح لخطأ في سكربت من نطاق آخر، بلا رسالة ولا مساروللضجيج الخاص بتطبيقك، صفِّ على الجهاز قبل أن يُرسل أي شيء:
<LerixProvider
options={{
apiKey: 'your-api-key',
projectId: 'your-project-slug',
ignoreErrors: ['ResizeObserver loop', /^ChunkLoadError/],
beforeSend: (event) =>
event.message.includes('extension://') ? null : event,
}}
>
النصوص تطابق أي جزء من الرسالة دون تمييز حالة الأحرف، والتعابير النمطية تُختبر كما كُتبت. وإرجاع null من beforeSend يُسقط الحدث. وكلاهما يعمل في المتصفح قبل أن يربط الخادم مسار الاستدعاء بالخرائط، فطابِق على نص الرسالة لا على أسماء الملفات.
npx @lerix-dev/lerix-cli upload مباشرة بعد البناء، مع LERIX_SECRET_KEY في بيئة CI.يحتفظ ليركس بأحدث 5,000 خريطة لكل مشروع، بحجم يصل إلى 50 ميغابايت للخريطة.
5 دقائق قراءة

إصدار قديم يتعطل مع الـ API الجديدة، أو الخادم يحتاج ساعة صيانة. اضبط قواعد التحديث ونوافذ الصيانة من لوحة التحكم وستلتزم بها كل التطبيقات خلال دقائق.
6 دقائق قراءة