diff --git a/site/src/components/RedirectPage.astro b/site/src/components/RedirectPage.astro new file mode 100644 index 0000000..c0351a3 --- /dev/null +++ b/site/src/components/RedirectPage.astro @@ -0,0 +1,60 @@ +--- +/** + * Full-page meta-refresh redirect stub. + * + * The standalone spec-sync site has been retired — its marketing and docs now + * live on the CorvidLabs hub. Every route renders one of these so visitors and + * search engines land on the canonical hub URL. + */ +interface Props { + /** Absolute hub URL to redirect to. */ + to: string +} + +const { to } = Astro.props +--- + + + + + + + This site has moved to CorvidLabs + + + + + + + +
+

The standalone spec-sync site has been retired.

+ This site has moved to CorvidLabs → +

If you are not redirected automatically, follow the link above.

+
+ + diff --git a/site/src/lib/hub.ts b/site/src/lib/hub.ts new file mode 100644 index 0000000..0af28a6 --- /dev/null +++ b/site/src/lib/hub.ts @@ -0,0 +1,8 @@ +/** + * Canonical CorvidLabs hub URLs that supersede the retired standalone site. + * + * - Marketing (incl. the languages gallery and blog) -> HUB_MARKETING + * - Docs and examples -> HUB_DOCS + */ +export const HUB_MARKETING = 'https://corvidlabs.github.io/corvidlabs-site/spec-sync/' +export const HUB_DOCS = 'https://corvidlabs.github.io/corvidlabs-site/spec-sync/docs/' diff --git a/site/src/pages/404.astro b/site/src/pages/404.astro index bb6a794..1cafde2 100644 --- a/site/src/pages/404.astro +++ b/site/src/pages/404.astro @@ -1,19 +1,5 @@ --- -import BaseLayout from '../layouts/BaseLayout.astro' -import Button from '../components/Button.astro' -import languages from '../data/languages.json' with { type: 'json' } -import { base } from '../lib/path' +import RedirectPage from '../components/RedirectPage.astro' +import { HUB_MARKETING } from '../lib/hub' --- - -
-

404

-

This page took flight without us.

-

- The page you're looking for doesn't exist (any more). Try the docs or the language registry. -

-
- - -
-
-
+ diff --git a/site/src/pages/blog/[...slug].astro b/site/src/pages/blog/[...slug].astro index acc2cf2..1a5223f 100644 --- a/site/src/pages/blog/[...slug].astro +++ b/site/src/pages/blog/[...slug].astro @@ -1,22 +1,13 @@ --- import { getCollection } from 'astro:content' -import ArticleLayout from '../../layouts/ArticleLayout.astro' +import RedirectPage from '../../components/RedirectPage.astro' +import { HUB_MARKETING } from '../../lib/hub' +// Keep enumerating every blog post slug so each retired URL still emits a +// redirect stub pointing at the hub marketing site. export async function getStaticPaths() { const entries = await getCollection('blog', p => !p.data.draft) - return entries.map(entry => ({ params: { slug: entry.slug }, props: { entry } })) + return entries.map(entry => ({ params: { slug: entry.slug } })) } - -const { entry } = Astro.props -const { Content } = await entry.render() -const date = entry.data.date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) -const meta = `${date} · ${entry.data.author} · ${entry.data.readTime} min read` --- - - - + diff --git a/site/src/pages/blog/index.astro b/site/src/pages/blog/index.astro index 2eab007..0e2b160 100644 --- a/site/src/pages/blog/index.astro +++ b/site/src/pages/blog/index.astro @@ -1,97 +1,5 @@ --- -import { getCollection } from 'astro:content' -import BaseLayout from '../../layouts/BaseLayout.astro' -import PostCard from '../../components/PostCard.astro' -import CategoryTag from '../../components/CategoryTag.astro' -import languages from '../../data/languages.json' with { type: 'json' } -import { base } from '../../lib/path' -const posts = (await getCollection('blog', p => !p.data.draft)) - .sort((a, b) => +b.data.date - +a.data.date) -const featured = posts.find(p => p.data.featured) -const rest = posts.filter(p => p !== featured) -const fmt = (d: Date) => d.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }) +import RedirectPage from '../../components/RedirectPage.astro' +import { HUB_MARKETING } from '../../lib/hub' --- - -
- -
-
-
-

The spec-sync blog

-

Updates, languages, and field notes.

-

Release notes, language additions, integration deep-dives, and the occasional design rant.

-
-
- RSS -
-
-
- -{featured && ( - -)} - -
-
-
-

All posts

- {posts.length} posts -
-
    - {rest.map(p => )} -
-
-
- -
- -
- -
-
- - + diff --git a/site/src/pages/docs/[...slug].astro b/site/src/pages/docs/[...slug].astro index 1a5c4c5..f23e4c7 100644 --- a/site/src/pages/docs/[...slug].astro +++ b/site/src/pages/docs/[...slug].astro @@ -1,17 +1,15 @@ --- import { getCollection } from 'astro:content' -import DocsLayout from '../../layouts/DocsLayout.astro' +import RedirectPage from '../../components/RedirectPage.astro' +import { HUB_DOCS } from '../../lib/hub' +// Keep enumerating every docs slug (incl. integrations/*) so each retired URL +// still emits a redirect stub pointing at the hub docs. export async function getStaticPaths() { const entries = await getCollection('docs') return entries .filter(e => e.slug !== 'index') - .map(entry => ({ params: { slug: entry.slug }, props: { entry } })) + .map(entry => ({ params: { slug: entry.slug } })) } - -const { entry } = Astro.props -const { Content } = await entry.render() --- - - - + diff --git a/site/src/pages/docs/index.astro b/site/src/pages/docs/index.astro index e7e6644..cf005d0 100644 --- a/site/src/pages/docs/index.astro +++ b/site/src/pages/docs/index.astro @@ -1,10 +1,5 @@ --- -import { getEntry } from 'astro:content' -import DocsLayout from '../../layouts/DocsLayout.astro' -const entry = await getEntry('docs', 'index') -if (!entry) throw new Error('docs/index entry not found') -const { Content } = await entry.render() +import RedirectPage from '../../components/RedirectPage.astro' +import { HUB_DOCS } from '../../lib/hub' --- - - - + diff --git a/site/src/pages/examples/[...slug].astro b/site/src/pages/examples/[...slug].astro index 291bf3b..fcacd3e 100644 --- a/site/src/pages/examples/[...slug].astro +++ b/site/src/pages/examples/[...slug].astro @@ -1,21 +1,13 @@ --- import { getCollection } from 'astro:content' -import ArticleLayout from '../../layouts/ArticleLayout.astro' +import RedirectPage from '../../components/RedirectPage.astro' +import { HUB_DOCS } from '../../lib/hub' +// Keep enumerating every example slug so each retired URL still emits a +// redirect stub pointing at the hub docs. export async function getStaticPaths() { const entries = await getCollection('examples', e => !e.data.draft) - return entries.map(entry => ({ params: { slug: entry.slug }, props: { entry } })) + return entries.map(entry => ({ params: { slug: entry.slug } })) } - -const { entry } = Astro.props -const { Content } = await entry.render() -const meta = `${entry.data.steps} steps · ${entry.data.minutes} min · ${entry.data.pillars.join(' · ')}` --- - - - + diff --git a/site/src/pages/examples/index.astro b/site/src/pages/examples/index.astro index aeed5bd..cf005d0 100644 --- a/site/src/pages/examples/index.astro +++ b/site/src/pages/examples/index.astro @@ -1,84 +1,5 @@ --- -import { getCollection } from 'astro:content' -import BaseLayout from '../../layouts/BaseLayout.astro' -import { base } from '../../lib/path' -const all = (await getCollection('examples', e => !e.data.draft)).sort( - (a, b) => (a.data.order ?? 999) - (b.data.order ?? 999), -) -const featured = all.find(e => e.data.featured) -const rest = all.filter(e => !e.data.featured) +import RedirectPage from '../../components/RedirectPage.astro' +import { HUB_DOCS } from '../../lib/hub' --- - -
- -
-
-

Walkthroughs

-

spec-sync on a real project.

-

End-to-end walkthroughs. Every command, every file, every output. Run them yourself or read them like recipes.

-
-
- -{featured && ( - -)} - -
- -
- -
-
- - + diff --git a/site/src/pages/index.astro b/site/src/pages/index.astro index f88471d..1cafde2 100644 --- a/site/src/pages/index.astro +++ b/site/src/pages/index.astro @@ -1,271 +1,5 @@ --- -import BaseLayout from '../layouts/BaseLayout.astro' -import Badge from '../components/Badge.astro' -import Button from '../components/Button.astro' -import SpecCodeDiff from '../components/SpecCodeDiff.astro' -import BigPillar from '../components/BigPillar.astro' -import Callout from '../components/Callout.astro' -import languages from '../data/languages.json' with { type: 'json' } -import { base } from '../lib/path' -const languageCount = languages.length +import RedirectPage from '../components/RedirectPage.astro' +import { HUB_MARKETING } from '../lib/hub' --- - -
- -
-
-
-
- {languageCount} languages · GitHub Marketplace -

Keep your docs
ready to be honest.

-

Bidirectional spec-to-code validation. Cross-project refs. AI generation. CI-enforced contract checking — in any language.

-
- - -
-

Install in a single command: cargo install specsync

-
-
- -
-
-
-
- -
-
-
    -
  • -
    {languageCount}
    -
    languages
    -
    places drift can hide — we catch them all
    -
  • -
  • 3
    powers
    detect · fix · prevent
  • -
  • 1
    spec format
    open, portable, language-agnostic
  • -
  • 1
    binary
    install, done
  • -
-
-
- - -
-
-
-

Three powers

-

Detect, fix, and prevent
spec drift.

-

A narrative arc from discovery to enforcement. Each power is a focused subcommand you can wire into any CI.

-
- - -

What is drift? Drift is the gap between what your specs say and what your code actually does — undocumented exports, stale entries, broken cross-references. spec-sync finds it, fixes it, and blocks it from merging.

-
- -
    - - Find the gap between what your specs say and what your code does. - - - - Patch undocumented exports and stale spec entries with AI-assisted generation. - - - - Make drift impossible to merge. Block on CI, surface in your editor. - -
-
-
- -{/* Future cleanup: replace hardcoded list with getCollection or import from languages.json. */} -
- -
- -
- -
- -
-
-

Stop documenting in fiction.
Sync up.

-

Install spec-sync and run your first check in under a minute.

-
- - cargo install specsync -
-
- - -
-
-
- -
-
- - + diff --git a/site/src/pages/languages/[slug].astro b/site/src/pages/languages/[slug].astro index 2855203..eaf8f41 100644 --- a/site/src/pages/languages/[slug].astro +++ b/site/src/pages/languages/[slug].astro @@ -1,11 +1,11 @@ --- -import { marked } from 'marked' -import BaseLayout from '../../layouts/BaseLayout.astro' -import LanguageCard from '../../components/LanguageCard.astro' import { readdir, readFile } from 'node:fs/promises' import { join } from 'node:path' -import { base } from '../../lib/path' +import RedirectPage from '../../components/RedirectPage.astro' +import { HUB_MARKETING } from '../../lib/hub' +// Keep enumerating every language slug so each retired URL still emits a +// redirect stub pointing at the hub's languages gallery (part of marketing). export async function getStaticPaths() { const dir = join(process.cwd(), 'src/data/languages') const files = await readdir(dir) @@ -14,307 +14,9 @@ export async function getStaticPaths() { if (!file.endsWith('.json')) continue const raw = await readFile(join(dir, file), 'utf-8') const data = JSON.parse(raw) - paths.push({ params: { slug: data.slug }, props: { lang: data } }) + paths.push({ params: { slug: data.slug } }) } return paths } - -const { lang } = Astro.props - -// Render example_spec_md as HTML -const specHtml = await marked.parse(lang.example_spec_md ?? '', { async: true }) - -// Load related language data -const relatedLangs: Array<{ slug: string; name: string; family: string; detection_style: string; test_patterns: string[]; description: string }> = [] -for (const relSlug of (lang.related_slugs ?? [])) { - try { - const raw = await readFile(join(process.cwd(), `src/data/languages/${relSlug}.json`), 'utf-8') - relatedLangs.push(JSON.parse(raw)) - } catch { - // skip missing related slugs - } -} - -const familyLabel: Record = { - native: 'Native', - managed: 'Managed', - dynamic: 'Dynamic', - markup: 'Markup', -} -const styleLabel: Record = { - ast: 'AST', - regex: 'Regex', - hybrid: 'Hybrid', -} --- - - -
- -
- - - - - -
-
-

{lang.name}

-
- {familyLabel[lang.family]} - {styleLabel[lang.detection_style]} - {lang.since_version && ( - since {lang.since_version} - )} -
-
-

{lang.description}

- {lang.extensions && lang.extensions.length > 0 && ( -
- {lang.extensions.map((ext: string) => ( - {ext} - ))} -
- )} -
- - - {lang.detection_rules && lang.detection_rules.length > 0 && ( -
-

Detection rules

-
- {lang.detection_rules.map((rule: { title: string; description: string; example_code: string }) => ( -
-

{rule.title}

-

{rule.description}

- {rule.example_code && ( -
{rule.example_code}
- )} -
- ))} -
-
- )} - - - {lang.test_pattern_examples && lang.test_pattern_examples.length > 0 && ( -
-

Test-file patterns

- - - - - - - - - {lang.test_pattern_examples.map((p: { pattern: string; explanation: string }) => ( - - - - - ))} - -
PatternExplanation
{p.pattern}{p.explanation}
-
- )} - - - {(lang.example_spec_md || lang.example_source) && ( -
-

Example: spec & source

-
- {lang.example_spec_md && ( -
-

Spec (rendered)

-
-
- )} - {lang.example_source && ( -
-

Source

-
{lang.example_source}
-
- )} -
-
- )} - - - {lang.caveats && lang.caveats.length > 0 && ( -
-

Caveats

-
    - {lang.caveats.map((c: string) => ( -
  • {c}
  • - ))} -
-
- )} - - - {false && ( -
-

Deep dive

-
- )} - - - {relatedLangs.length > 0 && ( -
- - -
- )} - - - - -
-
-
- - + diff --git a/site/src/pages/languages/index.astro b/site/src/pages/languages/index.astro index 749c135..0e2b160 100644 --- a/site/src/pages/languages/index.astro +++ b/site/src/pages/languages/index.astro @@ -1,242 +1,5 @@ --- -import BaseLayout from '../../layouts/BaseLayout.astro' -import LanguageCard from '../../components/LanguageCard.astro' -import languages from '../../data/languages.json' with { type: 'json' } +import RedirectPage from '../../components/RedirectPage.astro' +import { HUB_MARKETING } from '../../lib/hub' --- - - -
- -
-
-

Languages

-

Spec-sync speaks your stack.

-

{languages.length} languages, auto-detected, one spec format. Drop spec-sync check into any CI and it figures out the rest.

- -
    -
  • {languages.length} languages
  • - -
  • auto-detected
  • - -
  • one spec format
  • -
-
-
- -
-
- - -

{languages.length} languages

- -
    - {languages.map(lang => ( -
  • - -
  • - ))} -
- - -
-
- -
-
-
-

Don't see your language?

-

Open a discussion to request support or contribute detection rules.

- Request a language -
-
-
- -
-
- - - - +