From 97345d83123525bfe5e8f2fb529ca8be493e935f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:53:28 +0000 Subject: [PATCH 1/4] Initial plan From 4cc06e3314ae4b89d5dcc8a8fa5aad1d9c0dd03e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 15:00:00 +0000 Subject: [PATCH 2/4] Add fumadocs blog functionality with sample posts Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/site/app/blog/[[...slug]]/page.tsx | 128 ++++++++++++++++++ apps/site/app/blog/page.tsx | 86 ++++++++++++ apps/site/app/layout.config.tsx | 5 + .../blog/protocol-driven-architecture.mdx | 82 +++++++++++ apps/site/content/blog/welcome.mdx | 40 ++++++ apps/site/lib/source.ts | 7 +- apps/site/source.config.ts | 7 +- 7 files changed, 353 insertions(+), 2 deletions(-) create mode 100644 apps/site/app/blog/[[...slug]]/page.tsx create mode 100644 apps/site/app/blog/page.tsx create mode 100644 apps/site/content/blog/protocol-driven-architecture.mdx create mode 100644 apps/site/content/blog/welcome.mdx diff --git a/apps/site/app/blog/[[...slug]]/page.tsx b/apps/site/app/blog/[[...slug]]/page.tsx new file mode 100644 index 00000000..b8a250c7 --- /dev/null +++ b/apps/site/app/blog/[[...slug]]/page.tsx @@ -0,0 +1,128 @@ +import { blogSource } from '@/lib/source'; +import type { Metadata } from 'next'; +import { notFound } from 'next/navigation'; +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import { HomeLayout } from 'fumadocs-ui/home-layout'; +import { baseOptions } from '@/app/layout.config'; +import Link from 'next/link'; + +export default async function BlogPostPage({ + params, +}: { + params: { slug?: string[] }; +}) { + const page = blogSource.getPage(params.slug); + if (!page) notFound(); + + const MDX = page.data.body; + + return ( + +
+
+ + + + + Back to Blog + + +
+

+ {page.data.title} +

+ + {page.data.description && ( +

+ {page.data.description} +

+ )} + +
+ {page.data.date && ( + + )} + {page.data.authors && ( + by {page.data.authors.join(', ')} + )} +
+ + {page.data.tags && page.data.tags.length > 0 && ( +
+ {page.data.tags.map((tag: string) => ( + + {tag} + + ))} +
+ )} +
+ +
+ +
+ + +
+
+
+ ); +} + +export async function generateStaticParams() { + return blogSource.generateParams(); +} + +export function generateMetadata({ params }: { params: { slug?: string[] } }): Metadata { + const page = blogSource.getPage(params.slug); + if (!page) notFound(); + + return { + title: page.data.title, + description: page.data.description, + }; +} diff --git a/apps/site/app/blog/page.tsx b/apps/site/app/blog/page.tsx new file mode 100644 index 00000000..631a3fab --- /dev/null +++ b/apps/site/app/blog/page.tsx @@ -0,0 +1,86 @@ +import { blogSource } from '@/lib/source'; +import Link from 'next/link'; +import { HomeLayout } from 'fumadocs-ui/home-layout'; +import { baseOptions } from '@/app/layout.config'; + +export default function BlogPage() { + const posts = blogSource.getPages(); + + return ( + +
+
+
+

+ ObjectQL Blog +

+

+ Updates, tutorials, and insights about the standard protocol for AI software generation +

+
+ +
+ {posts + .sort((a, b) => { + const dateA = new Date(a.data.date || 0); + const dateB = new Date(b.data.date || 0); + return dateB.getTime() - dateA.getTime(); + }) + .map((post) => ( +
+ +
+ {post.data.date && ( + + )} + {post.data.authors && ( + by {post.data.authors.join(', ')} + )} +
+ +

+ {post.data.title} +

+ + {post.data.description && ( +

+ {post.data.description} +

+ )} + + {post.data.tags && post.data.tags.length > 0 && ( +
+ {post.data.tags.map((tag: string) => ( + + {tag} + + ))} +
+ )} + +
+ ))} +
+ + {posts.length === 0 && ( +
+

No blog posts yet. Check back soon!

+
+ )} +
+
+
+ ); +} diff --git a/apps/site/app/layout.config.tsx b/apps/site/app/layout.config.tsx index 7b97d640..affc833f 100644 --- a/apps/site/app/layout.config.tsx +++ b/apps/site/app/layout.config.tsx @@ -11,6 +11,11 @@ export const baseOptions: Omit = { url: '/docs', active: 'nested-url', }, + { + text: 'Blog', + url: '/blog', + active: 'nested-url', + }, ], githubUrl: 'https://github.com/objectstack-ai/objectql', // Enable sidebar search diff --git a/apps/site/content/blog/protocol-driven-architecture.mdx b/apps/site/content/blog/protocol-driven-architecture.mdx new file mode 100644 index 00000000..265096b9 --- /dev/null +++ b/apps/site/content/blog/protocol-driven-architecture.mdx @@ -0,0 +1,82 @@ +--- +title: Understanding ObjectQL's Protocol-Driven Architecture +description: A deep dive into how ObjectQL separates intent from implementation through its protocol-driven design. +date: 2026-01-19 +authors: + - ObjectQL Team +tags: + - architecture + - technical + - protocol +--- + +# Understanding ObjectQL's Protocol-Driven Architecture + +One of ObjectQL's core principles is being **Protocol-Driven, Not Code-Driven**. But what does this mean in practice? + +## The Problem with Traditional ORMs + +Traditional ORMs mix two concerns: + +1. **What** you want (the business logic) +2. **How** to achieve it (the implementation details) + +This creates several issues: + +- Tight coupling to specific databases +- Difficulty in testing and mocking +- Hard to optimize queries +- Challenging for AI to generate correctly + +## The ObjectQL Approach + +ObjectQL decouples these concerns through a three-layer architecture: + +### 1. The Intent Layer (YAML Schema) + +```yaml +name: project +fields: + name: + type: text + required: true + status: + type: select + options: [planning, active, completed] +``` + +This layer is **database-agnostic**. It describes what you want, not how to achieve it. + +### 2. The Compilation Layer + +The ObjectQL engine acts as a **compiler**, not a runtime wrapper: + +- Validates schema integrity +- Injects permission checks +- Optimizes query patterns +- Generates type definitions + +### 3. The Driver Layer + +Drivers translate the compiled AST into database-specific operations: + +- `@objectql/driver-sql` → PostgreSQL, MySQL +- `@objectql/driver-mongo` → MongoDB +- `@objectql/driver-sdk` → Remote HTTP APIs + +## Benefits for AI Code Generation + +This architecture makes ObjectQL ideal for AI-driven development: + +✅ **Hallucination-Free**: YAML schemas are validated before compilation +✅ **Type-Safe**: All operations are strongly typed +✅ **Testable**: Mock drivers for testing without a real database +✅ **Portable**: Change databases without rewriting business logic + +## Learn More + +Want to dive deeper? Check out: + +- [Architecture Specification](/docs/reference/spec/architecture) +- [Core Concepts](/docs/modeling/concepts) +- [Driver Development Guide](/docs/reference/api/drivers) diff --git a/apps/site/content/blog/welcome.mdx b/apps/site/content/blog/welcome.mdx new file mode 100644 index 00000000..2e809cef --- /dev/null +++ b/apps/site/content/blog/welcome.mdx @@ -0,0 +1,40 @@ +--- +title: Welcome to ObjectQL Blog +description: Introducing the ObjectQL blog - your source for updates, tutorials, and insights about the standard protocol for AI software generation. +date: 2026-01-20 +authors: + - ObjectQL Team +tags: + - announcement + - getting-started +--- + +# Welcome to the ObjectQL Blog + +We're excited to launch the ObjectQL blog! This is your go-to resource for: + +## What You'll Find Here + +- **Product Updates**: Stay informed about new features, improvements, and releases +- **Technical Deep Dives**: Learn about the architecture and design decisions behind ObjectQL +- **Best Practices**: Discover how to get the most out of ObjectQL in your projects +- **Community Stories**: Hear from developers building with ObjectQL + +## Why ObjectQL? + +ObjectQL represents a fundamental shift in how we approach software development. Instead of writing boilerplate code, you define **intent** through declarative schemas. The framework handles: + +- ✅ Type-safe database schemas +- ✅ Validated CRUD operations +- ✅ Permission enforcement +- ✅ API endpoint generation + +## Getting Started + +If you're new to ObjectQL, check out our [Getting Started Guide](/docs/getting-started) to begin your journey. + +## Stay Connected + +Follow our blog for regular updates, and don't hesitate to [contribute on GitHub](https://github.com/objectstack-ai/objectql) or join our community discussions. + +Happy coding! 🚀 diff --git a/apps/site/lib/source.ts b/apps/site/lib/source.ts index d786824d..911b8bba 100644 --- a/apps/site/lib/source.ts +++ b/apps/site/lib/source.ts @@ -1,4 +1,4 @@ -import { docs, meta } from '@/.source'; +import { docs, meta, blog } from '@/.source'; import { createMDXSource } from 'fumadocs-mdx'; import { loader } from 'fumadocs-core/source'; @@ -6,3 +6,8 @@ export const source = loader({ baseUrl: '/docs', source: createMDXSource(docs, meta), }); + +export const blogSource = loader({ + baseUrl: '/blog', + source: createMDXSource(blog, []), +}); diff --git a/apps/site/source.config.ts b/apps/site/source.config.ts index 0ed039e7..2606dc2c 100644 --- a/apps/site/source.config.ts +++ b/apps/site/source.config.ts @@ -1,4 +1,4 @@ -import { defineDocs, defineConfig } from 'fumadocs-mdx/config'; +import { defineDocs, defineConfig, defineCollections } from 'fumadocs-mdx/config'; import { rehypeCode } from 'fumadocs-core/mdx-plugins'; import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; @@ -10,6 +10,11 @@ export const { docs, meta } = defineDocs({ dir: 'content/docs', }); +export const blog = defineCollections({ + dir: 'content/blog', + type: 'doc', +}); + export default defineConfig({ mdxOptions: { remarkPlugins: [ From a6c4c52cb25b8c755cd80574453abce4c3dc76c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 15:04:39 +0000 Subject: [PATCH 3/4] Fix blog routing conflict by merging listing and post pages Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/site/app/blog/[[...slug]]/page.tsx | 292 ++++++++++++++++-------- apps/site/app/blog/page.tsx | 86 ------- 2 files changed, 200 insertions(+), 178 deletions(-) delete mode 100644 apps/site/app/blog/page.tsx diff --git a/apps/site/app/blog/[[...slug]]/page.tsx b/apps/site/app/blog/[[...slug]]/page.tsx index b8a250c7..cef706fa 100644 --- a/apps/site/app/blog/[[...slug]]/page.tsx +++ b/apps/site/app/blog/[[...slug]]/page.tsx @@ -6,108 +6,206 @@ import { HomeLayout } from 'fumadocs-ui/home-layout'; import { baseOptions } from '@/app/layout.config'; import Link from 'next/link'; -export default async function BlogPostPage({ +// Blog listing component +function BlogListing() { + const posts = blogSource.getPages(); + + return ( +
+
+

+ ObjectQL Blog +

+

+ Updates, tutorials, and insights about the standard protocol for AI software generation +

+
+ +
+ {posts + .sort((a, b) => { + const dateA = new Date(a.data.date || 0); + const dateB = new Date(b.data.date || 0); + return dateB.getTime() - dateA.getTime(); + }) + .map((post) => ( +
+ +
+ {post.data.date && ( + + )} + {post.data.authors && ( + by {post.data.authors.join(', ')} + )} +
+ +

+ {post.data.title} +

+ + {post.data.description && ( +

+ {post.data.description} +

+ )} + + {post.data.tags && post.data.tags.length > 0 && ( +
+ {post.data.tags.map((tag: string) => ( + + {tag} + + ))} +
+ )} + +
+ ))} +
+ + {posts.length === 0 && ( +
+

No blog posts yet. Check back soon!

+
+ )} +
+ ); +} + +// Blog post component +function BlogPost({ page }: { page: any }) { + const MDX = page.data.body; + + return ( +
+ + + + + Back to Blog + + +
+

+ {page.data.title} +

+ + {page.data.description && ( +

+ {page.data.description} +

+ )} + +
+ {page.data.date && ( + + )} + {page.data.authors && ( + by {page.data.authors.join(', ')} + )} +
+ + {page.data.tags && page.data.tags.length > 0 && ( +
+ {page.data.tags.map((tag: string) => ( + + {tag} + + ))} +
+ )} +
+ +
+ +
+ +
+ + + + + Back to Blog + +
+
+ ); +} + +export default async function BlogPage({ params, }: { params: { slug?: string[] }; }) { + // If no slug, show blog listing + if (!params.slug || params.slug.length === 0) { + return ( + +
+ +
+
+ ); + } + + // Otherwise, show individual blog post const page = blogSource.getPage(params.slug); if (!page) notFound(); - const MDX = page.data.body; - return (
-
- - - - - Back to Blog - - -
-

- {page.data.title} -

- - {page.data.description && ( -

- {page.data.description} -

- )} - -
- {page.data.date && ( - - )} - {page.data.authors && ( - by {page.data.authors.join(', ')} - )} -
- - {page.data.tags && page.data.tags.length > 0 && ( -
- {page.data.tags.map((tag: string) => ( - - {tag} - - ))} -
- )} -
- -
- -
- -
- - - - - Back to Blog - -
-
+
); @@ -118,6 +216,15 @@ export async function generateStaticParams() { } export function generateMetadata({ params }: { params: { slug?: string[] } }): Metadata { + // Default metadata for blog listing + if (!params.slug || params.slug.length === 0) { + return { + title: 'Blog', + description: 'Updates, tutorials, and insights about the standard protocol for AI software generation', + }; + } + + // Metadata for individual blog post const page = blogSource.getPage(params.slug); if (!page) notFound(); @@ -126,3 +233,4 @@ export function generateMetadata({ params }: { params: { slug?: string[] } }): M description: page.data.description, }; } + diff --git a/apps/site/app/blog/page.tsx b/apps/site/app/blog/page.tsx deleted file mode 100644 index 631a3fab..00000000 --- a/apps/site/app/blog/page.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { blogSource } from '@/lib/source'; -import Link from 'next/link'; -import { HomeLayout } from 'fumadocs-ui/home-layout'; -import { baseOptions } from '@/app/layout.config'; - -export default function BlogPage() { - const posts = blogSource.getPages(); - - return ( - -
-
-
-

- ObjectQL Blog -

-

- Updates, tutorials, and insights about the standard protocol for AI software generation -

-
- -
- {posts - .sort((a, b) => { - const dateA = new Date(a.data.date || 0); - const dateB = new Date(b.data.date || 0); - return dateB.getTime() - dateA.getTime(); - }) - .map((post) => ( -
- -
- {post.data.date && ( - - )} - {post.data.authors && ( - by {post.data.authors.join(', ')} - )} -
- -

- {post.data.title} -

- - {post.data.description && ( -

- {post.data.description} -

- )} - - {post.data.tags && post.data.tags.length > 0 && ( -
- {post.data.tags.map((tag: string) => ( - - {tag} - - ))} -
- )} - -
- ))} -
- - {posts.length === 0 && ( -
-

No blog posts yet. Check back soon!

-
- )} -
-
-
- ); -} From 67e0495c298aa1557da510849359ee4605386ff1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 20 Jan 2026 15:07:08 +0000 Subject: [PATCH 4/4] Address code review feedback: improve type safety and remove redundant annotations Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- apps/site/app/blog/[[...slug]]/page.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/site/app/blog/[[...slug]]/page.tsx b/apps/site/app/blog/[[...slug]]/page.tsx index cef706fa..a05c8a7a 100644 --- a/apps/site/app/blog/[[...slug]]/page.tsx +++ b/apps/site/app/blog/[[...slug]]/page.tsx @@ -61,7 +61,7 @@ function BlogListing() { {post.data.tags && post.data.tags.length > 0 && (
- {post.data.tags.map((tag: string) => ( + {post.data.tags.map((tag) => ( }) { + if (!page) return null; + const MDX = page.data.body; return ( @@ -140,7 +142,7 @@ function BlogPost({ page }: { page: any }) { {page.data.tags && page.data.tags.length > 0 && (
- {page.data.tags.map((tag: string) => ( + {page.data.tags.map((tag) => (