QueryWeave
حالة عنوان URL بأنواع محدَّدة، في المتصفح والخادم والاختبارات.
محرّك لإدارة حالة عنوان URL لا يرتبط بأي إطار عمل. تصف معاملات الاستعلام مرة واحدة في نموذج محدَّد الأنواع، وهو يتولّى قراءتها والتحقق منها وكتابتها بالطريقة نفسها في المتصفح وعلى الخادم وفي الاختبارات. وإن وردت قيمة غير صالحة عاد إلى القيمة الافتراضية وأبلغ عنها، دون أن تتعطّل الصفحة.

المشكلة
حالة عنوان URL حاضرة في أكثر من موضع: في المتصفح، وعلى الخادم، وفي الاختبارات، وفي الروابط التي يتبادلها المستخدمون. وفي أغلب المشاريع تُوصف هذه الحالة أكثر من مرة: دالة تقرؤها في المتصفح، وأخرى على الخادم، وثالثة تكتبها، وقيم افتراضية مكرّرة في كل واحدة. ومع الوقت تختلف هذه النسخ.
نموذج واحد ومحوّلات
في QueryWeave تُعرَّف حالة الاستعلام مرة واحدة في نموذج، ويتولّى محوّل لكل بيئة مزامنتها. والمسار واحد في كل البيئات: يُقرأ نص الاستعلام، ويُتحقَّق منه فيصير حالة محدَّدة الأنواع، ثم تُحدَّث الحالة، ثم تُكتب في العنوان بصيغة قياسية.
import { createQueryRuntime, defineQueryModel, param } from '@queryweave/core'
const productFilters = defineQueryModel({
search: param.text().optional(),
page: param.integer({ min: 1 }).default(1),
archived: param.boolean().default(false),
sort: param.choice(['name', 'created_at', 'price']).default('created_at'),
tags: param.list(param.text()).default([]),
})
const runtime = createQueryRuntime({ model: productFilters, adapter })
runtime.read().values.page // 1
await runtime.update({ search: 'vue', page: 1 })
await runtime.update({ page: 2 }, { navigation: 'replace' })
await runtime.transaction((draft) => {
draft.search = 'vue'
draft.page = 1
})
والرابط الذي فيه خطأ لا يعطّل الصفحة. فالاستعلام ?page=abc يُقرأ على أنه الصفحة 1، ومع النتيجة ملاحظة تبيّن السبب، فيستطيع التطبيق أن يكمل عمله وأن يُعلم المستخدم.
الاستعمال في Vue
const filters = useQueryModel(productFilters)
filters.values.page // readonly reactive state
await filters.update({ page: 2 }) // explicit operation
const search = filters.field('search', { navigation: 'replace' }) // v-model target
لا دالة خاصة بكل مفتاح، ولا دالة تعيين تُرجَع مع القيمة، ولا تعديل مباشر للحالة. و Vue و Nuxt أول ما دعمتُه من الواجهات، لكن تصميم النواة لا يعتمد على أيٍّ منهما.
تسع حزم
| الحزمة | الدور | الحجم بعد الضغط |
|---|---|---|
@queryweave/core | النماذج، وقراءة العنوان وكتابته، وعقود بيئة التشغيل | 7.39 كيلوبايت |
@queryweave/browser | محوّل History API في المتصفح | 564 بايت |
@queryweave/server | أدوات للخادم مبنية على معايير الويب | 469 بايت |
@queryweave/node | التعامل مع طلبات Node.js | 691 بايت |
@queryweave/standard-schema | التوافق مع Standard Schema | 341 بايت |
@queryweave/testing | محوّل يعمل في الذاكرة وأدوات للاختبار | 491 بايت |
@queryweave/vue | ربط نماذج الاستعلام بـ Vue | 850 بايت |
@queryweave/vue-router | محوّل Vue Router | 699 بايت |
@queryweave/nuxt | وحدة Nuxt، وبيئة تشغيل لكل طلب | 911 بايت |
وهذه الحدود مفروضة: يفشل الأمر pnpm bundle:size:check إذا تجاوزت حزمةٌ حدّها.
الاختبارات
قبل كل إصدار تُشغَّل كل مشاريع Vitest، ومنها الاختبارات داخل المتصفح واختبارات الأنواع، ثم اختبارات في متصفحات فعلية هي Chromium و Firefox و WebKit، ثم Playwright على تطبيق التجربة. وتُفحص حدود الحزم في المصادر وملفات التعريف ومخطط التبعيات، ويُفحص ما يُبنى وملفات الأنواع المرافقة له، ثم تُثبَّت الحزم بعد تحزيمها في مشاريع نظيفة للتأكد من أنها تعمل عند من يستعملها.
والمشروع تجريبي ولم يبلغ الإصدار 1.0 بعد: قد تتغيّر واجهته العامة بين إصدار وآخر، وكل تغيير غير متوافق يُدوَّن في سجل القرارات المعمارية وفي changeset.
نظرة على المشروع
مختبر مصغّر لعناوين URL
نسخة مبسّطة كتبتُها لهذه الصفحة، وليست المكتبة نفسها. غيّر أحد المرشّحات، أو حمّل رابطًا فيه أخطاء.
الحالة بعد التحقق
{
search: "vue",
page: 2,
sort: "newest",
archived: false,
}العنوان القياسي
/products?search=vue&page=2
القيم المصحَّحة
لا أخطاء
لقطات




