كل المشاريع

QueryWeave

نسخة تجريبيةمنذ 2026MIT

حالة عنوان URL بأنواع محدَّدة، في المتصفح والخادم والاختبارات.

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

الصفحة الرئيسية لموقع QueryWeave: نص استعلام يتحوّل إلى حالة محدَّدة الأنواع

المشكلة

حالة عنوان URL حاضرة في أكثر من موضع: في المتصفح، وعلى الخادم، وفي الاختبارات، وفي الروابط التي يتبادلها المستخدمون. وفي أغلب المشاريع تُوصف هذه الحالة أكثر من مرة: دالة تقرؤها في المتصفح، وأخرى على الخادم، وثالثة تكتبها، وقيم افتراضية مكرّرة في كل واحدة. ومع الوقت تختلف هذه النسخ.

نموذج واحد ومحوّلات

في QueryWeave تُعرَّف حالة الاستعلام مرة واحدة في نموذج، ويتولّى محوّل لكل بيئة مزامنتها. والمسار واحد في كل البيئات: يُقرأ نص الاستعلام، ويُتحقَّق منه فيصير حالة محدَّدة الأنواع، ثم تُحدَّث الحالة، ثم تُكتب في العنوان بصيغة قياسية.

ts
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

ts
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.js691 بايت
@queryweave/standard-schemaالتوافق مع Standard Schema341 بايت
@queryweave/testingمحوّل يعمل في الذاكرة وأدوات للاختبار491 بايت
@queryweave/vueربط نماذج الاستعلام بـ Vue850 بايت
@queryweave/vue-routerمحوّل Vue Router699 بايت
@queryweave/nuxtوحدة Nuxt، وبيئة تشغيل لكل طلب911 بايت

وهذه الحدود مفروضة: يفشل الأمر pnpm bundle:size:check إذا تجاوزت حزمةٌ حدّها.

الاختبارات

قبل كل إصدار تُشغَّل كل مشاريع Vitest، ومنها الاختبارات داخل المتصفح واختبارات الأنواع، ثم اختبارات في متصفحات فعلية هي Chromium و Firefox و WebKit، ثم Playwright على تطبيق التجربة. وتُفحص حدود الحزم في المصادر وملفات التعريف ومخطط التبعيات، ويُفحص ما يُبنى وملفات الأنواع المرافقة له، ثم تُثبَّت الحزم بعد تحزيمها في مشاريع نظيفة للتأكد من أنها تعمل عند من يستعملها.

والمشروع تجريبي ولم يبلغ الإصدار 1.0 بعد: قد تتغيّر واجهته العامة بين إصدار وآخر، وكل تغيير غير متوافق يُدوَّن في سجل القرارات المعمارية وفي changeset.

نظرة على المشروع

مختبر مصغّر لعناوين URL

نسخة مبسّطة كتبتُها لهذه الصفحة، وليست المكتبة نفسها. غيّر أحد المرشّحات، أو حمّل رابطًا فيه أخطاء.

الصفحة
2
الترتيب

الحالة بعد التحقق

{
  search: "vue",
  page: 2,
  sort: "newest",
  archived: false,
}

العنوان القياسي

/products?search=vue&page=2

القيم المصحَّحة

لا أخطاء