@su-record/vibe 2.7.12 → 2.7.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +37 -37
- package/CLAUDE.md +126 -126
- package/LICENSE +21 -21
- package/README.md +449 -580
- package/agents/architect-low.md +41 -41
- package/agents/architect-medium.md +59 -59
- package/agents/architect.md +80 -80
- package/agents/build-error-resolver.md +115 -115
- package/agents/compounder.md +261 -261
- package/agents/diagrammer.md +178 -178
- package/agents/docs/api-documenter.md +99 -99
- package/agents/docs/changelog-writer.md +93 -93
- package/agents/e2e-tester.md +294 -266
- package/agents/explorer-low.md +42 -42
- package/agents/explorer-medium.md +59 -59
- package/agents/explorer.md +48 -48
- package/agents/implementer-low.md +43 -43
- package/agents/implementer-medium.md +52 -52
- package/agents/implementer.md +54 -54
- package/agents/junior-mentor.md +141 -141
- package/agents/planning/requirements-analyst.md +84 -84
- package/agents/planning/ux-advisor.md +83 -83
- package/agents/qa/acceptance-tester.md +86 -86
- package/agents/qa/edge-case-finder.md +93 -93
- package/agents/refactor-cleaner.md +143 -143
- package/agents/research/best-practices-agent.md +199 -199
- package/agents/research/codebase-patterns-agent.md +157 -157
- package/agents/research/framework-docs-agent.md +188 -188
- package/agents/research/security-advisory-agent.md +213 -213
- package/agents/review/architecture-reviewer.md +107 -107
- package/agents/review/complexity-reviewer.md +116 -116
- package/agents/review/data-integrity-reviewer.md +88 -88
- package/agents/review/git-history-reviewer.md +103 -103
- package/agents/review/performance-reviewer.md +86 -86
- package/agents/review/python-reviewer.md +150 -150
- package/agents/review/rails-reviewer.md +139 -139
- package/agents/review/react-reviewer.md +144 -144
- package/agents/review/security-reviewer.md +80 -80
- package/agents/review/simplicity-reviewer.md +140 -140
- package/agents/review/test-coverage-reviewer.md +116 -116
- package/agents/review/typescript-reviewer.md +127 -127
- package/agents/searcher.md +54 -54
- package/agents/simplifier.md +120 -120
- package/agents/tester.md +49 -49
- package/agents/ui/ui-a11y-auditor.md +93 -93
- package/agents/ui/ui-antipattern-detector.md +94 -94
- package/agents/ui/ui-dataviz-advisor.md +69 -69
- package/agents/ui/ui-design-system-gen.md +57 -57
- package/agents/ui/ui-industry-analyzer.md +49 -49
- package/agents/ui/ui-layout-architect.md +65 -65
- package/agents/ui/ui-stack-implementer.md +68 -68
- package/agents/ui/ux-compliance-reviewer.md +81 -81
- package/agents/ui-previewer.md +258 -260
- package/commands/vibe.analyze.md +8 -0
- package/commands/vibe.review.md +10 -3
- package/commands/vibe.run.md +2078 -2022
- package/commands/vibe.spec.md +10 -10
- package/commands/vibe.spec.review.md +565 -558
- package/commands/vibe.utils.md +413 -413
- package/commands/vibe.verify.md +45 -0
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +1 -7
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/collaborator.js +52 -52
- package/dist/cli/commands/evolution.js +12 -12
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +55 -70
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +6 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/remove.js +14 -14
- package/dist/cli/commands/sentinel.js +27 -27
- package/dist/cli/commands/setup.js +1 -1
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/skills.js +5 -5
- package/dist/cli/commands/slack.js +10 -10
- package/dist/cli/commands/telegram.js +12 -12
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +3 -4
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/detect.js +32 -32
- package/dist/cli/index.js +51 -55
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/llm/claude-commands.js +16 -16
- package/dist/cli/llm/config.js +20 -20
- package/dist/cli/llm/config.js.map +1 -1
- package/dist/cli/llm/gemini-commands.d.ts +4 -6
- package/dist/cli/llm/gemini-commands.d.ts.map +1 -1
- package/dist/cli/llm/gemini-commands.js +52 -322
- package/dist/cli/llm/gemini-commands.js.map +1 -1
- package/dist/cli/llm/gpt-commands.js +21 -21
- package/dist/cli/llm/gpt-commands.js.map +1 -1
- package/dist/cli/llm/help.js +21 -21
- package/dist/cli/postinstall/constants.js +1 -1
- package/dist/cli/postinstall/constants.js.map +1 -1
- package/dist/cli/postinstall/cursor-agents.js +32 -32
- package/dist/cli/postinstall/cursor-rules.js +83 -83
- package/dist/cli/postinstall/cursor-skills.js +743 -743
- package/dist/cli/postinstall/inline-skills.js +1 -1
- package/dist/cli/postinstall/inline-skills.js.map +1 -1
- package/dist/cli/setup/Provisioner.js +42 -42
- package/dist/cli/types.d.ts +2 -18
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/utils.d.ts +0 -9
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js +0 -28
- package/dist/cli/utils.js.map +1 -1
- package/dist/infra/lib/DeepInit.js +24 -24
- package/dist/infra/lib/IterationTracker.js +11 -11
- package/dist/infra/lib/PythonParser.js +108 -108
- package/dist/infra/lib/ReviewRace.js +96 -96
- package/dist/infra/lib/SkillFrontmatter.js +28 -28
- package/dist/infra/lib/SkillQualityGate.js +9 -9
- package/dist/infra/lib/SkillRepository.js +159 -159
- package/dist/infra/lib/UltraQA.js +99 -99
- package/dist/infra/lib/autonomy/AuditStore.js +41 -41
- package/dist/infra/lib/autonomy/ConfirmationStore.js +30 -30
- package/dist/infra/lib/autonomy/EventOutbox.js +38 -38
- package/dist/infra/lib/autonomy/PolicyEngine.js +18 -18
- package/dist/infra/lib/autonomy/SecuritySentinel.js +1 -1
- package/dist/infra/lib/autonomy/SuggestionStore.js +33 -33
- package/dist/infra/lib/config/GlobalConfigManager.d.ts +0 -2
- package/dist/infra/lib/config/GlobalConfigManager.d.ts.map +1 -1
- package/dist/infra/lib/config/GlobalConfigManager.js +0 -27
- package/dist/infra/lib/config/GlobalConfigManager.js.map +1 -1
- package/dist/infra/lib/embedding/VectorStore.js +22 -22
- package/dist/infra/lib/evolution/AgentAnalyzer.js +10 -10
- package/dist/infra/lib/evolution/DescriptionOptimizer.js +21 -21
- package/dist/infra/lib/evolution/GenerationRegistry.js +36 -36
- package/dist/infra/lib/evolution/InsightStore.js +90 -90
- package/dist/infra/lib/evolution/RollbackManager.js +5 -5
- package/dist/infra/lib/evolution/SkillBenchmark.js +23 -23
- package/dist/infra/lib/evolution/SkillEvalRunner.js +50 -50
- package/dist/infra/lib/evolution/SkillGapDetector.js +10 -10
- package/dist/infra/lib/evolution/UsageTracker.js +28 -28
- package/dist/infra/lib/gemini/auth.d.ts +4 -16
- package/dist/infra/lib/gemini/auth.d.ts.map +1 -1
- package/dist/infra/lib/gemini/auth.js +10 -405
- package/dist/infra/lib/gemini/auth.js.map +1 -1
- package/dist/infra/lib/gemini/capabilities.d.ts +4 -8
- package/dist/infra/lib/gemini/capabilities.d.ts.map +1 -1
- package/dist/infra/lib/gemini/capabilities.js +8 -166
- package/dist/infra/lib/gemini/capabilities.js.map +1 -1
- package/dist/infra/lib/gemini/chat.d.ts +4 -13
- package/dist/infra/lib/gemini/chat.d.ts.map +1 -1
- package/dist/infra/lib/gemini/chat.js +10 -323
- package/dist/infra/lib/gemini/chat.js.map +1 -1
- package/dist/infra/lib/gemini/completion.d.ts +5 -15
- package/dist/infra/lib/gemini/completion.d.ts.map +1 -1
- package/dist/infra/lib/gemini/completion.js +6 -97
- package/dist/infra/lib/gemini/completion.js.map +1 -1
- package/dist/infra/lib/gemini/constants.d.ts +2 -31
- package/dist/infra/lib/gemini/constants.d.ts.map +1 -1
- package/dist/infra/lib/gemini/constants.js +2 -77
- package/dist/infra/lib/gemini/constants.js.map +1 -1
- package/dist/infra/lib/gemini/index.d.ts +5 -8
- package/dist/infra/lib/gemini/index.d.ts.map +1 -1
- package/dist/infra/lib/gemini/index.js +4 -7
- package/dist/infra/lib/gemini/index.js.map +1 -1
- package/dist/infra/lib/gemini/models.d.ts +3 -4
- package/dist/infra/lib/gemini/models.d.ts.map +1 -1
- package/dist/infra/lib/gemini/models.js +8 -84
- package/dist/infra/lib/gemini/models.js.map +1 -1
- package/dist/infra/lib/gemini/orchestration.js +5 -5
- package/dist/infra/lib/gemini/types.d.ts +16 -44
- package/dist/infra/lib/gemini/types.d.ts.map +1 -1
- package/dist/infra/lib/gemini/types.js +1 -1
- package/dist/infra/lib/gpt/auth.d.ts +2 -5
- package/dist/infra/lib/gpt/auth.d.ts.map +1 -1
- package/dist/infra/lib/gpt/auth.js +8 -38
- package/dist/infra/lib/gpt/auth.js.map +1 -1
- package/dist/infra/lib/gpt/chat.d.ts +3 -3
- package/dist/infra/lib/gpt/chat.d.ts.map +1 -1
- package/dist/infra/lib/gpt/chat.js +37 -53
- package/dist/infra/lib/gpt/chat.js.map +1 -1
- package/dist/infra/lib/gpt/constants.d.ts +2 -5
- package/dist/infra/lib/gpt/constants.d.ts.map +1 -1
- package/dist/infra/lib/gpt/constants.js +4 -9
- package/dist/infra/lib/gpt/constants.js.map +1 -1
- package/dist/infra/lib/gpt/embedding.d.ts +1 -1
- package/dist/infra/lib/gpt/embedding.js +3 -3
- package/dist/infra/lib/gpt/embedding.js.map +1 -1
- package/dist/infra/lib/gpt/oauth.d.ts +6 -39
- package/dist/infra/lib/gpt/oauth.d.ts.map +1 -1
- package/dist/infra/lib/gpt/oauth.js +8 -340
- package/dist/infra/lib/gpt/oauth.js.map +1 -1
- package/dist/infra/lib/gpt/orchestration.js +5 -5
- package/dist/infra/lib/gpt/orchestration.js.map +1 -1
- package/dist/infra/lib/gpt/specializations.d.ts +2 -2
- package/dist/infra/lib/gpt/specializations.js +3 -3
- package/dist/infra/lib/gpt/specializations.js.map +1 -1
- package/dist/infra/lib/gpt/types.d.ts +1 -1
- package/dist/infra/lib/gpt/types.d.ts.map +1 -1
- package/dist/infra/lib/llm/auth/AuthProfileManager.d.ts +2 -2
- package/dist/infra/lib/llm/auth/AuthProfileManager.d.ts.map +1 -1
- package/dist/infra/lib/llm/auth/AuthProfileManager.js.map +1 -1
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.js +1 -1
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.js.map +1 -1
- package/dist/infra/lib/llm/auth/TokenRefresher.d.ts +1 -1
- package/dist/infra/lib/llm/auth/TokenRefresher.js +1 -1
- package/dist/infra/lib/llm/auth/index.d.ts +2 -12
- package/dist/infra/lib/llm/auth/index.d.ts.map +1 -1
- package/dist/infra/lib/llm/auth/index.js +5 -63
- package/dist/infra/lib/llm/auth/index.js.map +1 -1
- package/dist/infra/lib/llm/types.d.ts +1 -1
- package/dist/infra/lib/llm/types.d.ts.map +1 -1
- package/dist/infra/lib/memory/KnowledgeGraph.js +4 -4
- package/dist/infra/lib/memory/MemorySearch.js +57 -57
- package/dist/infra/lib/memory/MemoryStorage.js +181 -181
- package/dist/infra/lib/memory/ObservationStore.js +28 -28
- package/dist/infra/lib/memory/ReflectionStore.js +30 -30
- package/dist/infra/lib/memory/SessionRAGRetriever.js +7 -7
- package/dist/infra/lib/memory/SessionRAGStore.js +225 -225
- package/dist/infra/lib/memory/SessionSummarizer.js +9 -9
- package/dist/infra/orchestrator/AgentManager.js +12 -12
- package/dist/infra/orchestrator/AgentRegistry.js +65 -65
- package/dist/infra/orchestrator/MultiLlmResearch.js +8 -8
- package/dist/infra/orchestrator/SwarmOrchestrator.test.js +16 -16
- package/dist/infra/orchestrator/parallelResearch.js +24 -24
- package/dist/tools/convention/analyzeComplexity.test.js +115 -115
- package/dist/tools/convention/validateCodeQuality.test.js +104 -104
- package/dist/tools/memory/createMemoryTimeline.js +10 -10
- package/dist/tools/memory/getMemoryGraph.js +12 -12
- package/dist/tools/memory/getSessionContext.js +9 -9
- package/dist/tools/memory/linkMemories.js +14 -14
- package/dist/tools/memory/listMemories.js +4 -4
- package/dist/tools/memory/recallMemory.js +4 -4
- package/dist/tools/memory/saveMemory.js +4 -4
- package/dist/tools/memory/searchMemoriesAdvanced.js +23 -23
- package/dist/tools/semantic/analyzeDependencyGraph.js +12 -12
- package/dist/tools/semantic/astGrep.test.js +6 -6
- package/dist/tools/spec/prdParser.test.js +171 -171
- package/dist/tools/spec/specGenerator.js +169 -169
- package/dist/tools/spec/traceabilityMatrix.js +64 -64
- package/dist/tools/spec/traceabilityMatrix.test.js +28 -28
- package/hooks/gemini-hooks.json +73 -73
- package/hooks/hooks.json +137 -137
- package/hooks/scripts/code-check.js +70 -70
- package/hooks/scripts/context-save.js +212 -212
- package/hooks/scripts/hud-status.js +291 -291
- package/hooks/scripts/keyword-detector.js +214 -214
- package/hooks/scripts/llm-orchestrate.js +475 -646
- package/hooks/scripts/post-edit.js +32 -32
- package/hooks/scripts/pre-tool-guard.js +125 -125
- package/hooks/scripts/prompt-dispatcher.js +185 -185
- package/hooks/scripts/sentinel-guard.js +104 -104
- package/hooks/scripts/session-start.js +106 -106
- package/hooks/scripts/stop-notify.js +209 -209
- package/hooks/scripts/utils.js +100 -100
- package/languages/csharp-unity.md +515 -515
- package/languages/gdscript-godot.md +470 -470
- package/languages/ruby-rails.md +489 -489
- package/languages/typescript-angular.md +433 -433
- package/languages/typescript-astro.md +416 -416
- package/languages/typescript-electron.md +406 -406
- package/languages/typescript-nestjs.md +524 -524
- package/languages/typescript-svelte.md +407 -407
- package/languages/typescript-tauri.md +365 -365
- package/package.json +121 -121
- package/skills/agents-md/SKILL.md +120 -120
- package/skills/arch-guard/SKILL.md +180 -180
- package/skills/brand-assets/SKILL.md +146 -146
- package/skills/capability-loop/SKILL.md +167 -167
- package/skills/characterization-test/SKILL.md +206 -206
- package/skills/commerce-patterns/SKILL.md +59 -59
- package/skills/commit-push-pr/SKILL.md +75 -75
- package/skills/context7-usage/SKILL.md +105 -105
- package/skills/core-capabilities/SKILL.md +48 -48
- package/skills/e2e-commerce/SKILL.md +57 -57
- package/skills/exec-plan/SKILL.md +147 -147
- package/skills/frontend-design/SKILL.md +73 -73
- package/skills/git-worktree/SKILL.md +72 -72
- package/skills/handoff/SKILL.md +109 -109
- package/skills/parallel-research/SKILL.md +87 -87
- package/skills/priority-todos/SKILL.md +63 -63
- package/skills/seo-checklist/SKILL.md +57 -57
- package/skills/techdebt/SKILL.md +122 -122
- package/skills/tool-fallback/SKILL.md +103 -103
- package/skills/typescript-advanced-types/SKILL.md +66 -65
- package/skills/ui-ux-pro-max/SKILL.md +206 -206
- package/skills/vercel-react-best-practices/SKILL.md +59 -59
- package/skills/video-production/SKILL.md +51 -51
- package/vibe/config.json +29 -29
- package/vibe/constitution.md +227 -227
- package/vibe/rules/principles/communication-guide.md +98 -98
- package/vibe/rules/principles/development-philosophy.md +52 -52
- package/vibe/rules/principles/quick-start.md +102 -102
- package/vibe/rules/quality/bdd-contract-testing.md +393 -393
- package/vibe/rules/quality/checklist.md +276 -276
- package/vibe/rules/quality/performance.md +236 -236
- package/vibe/rules/quality/testing-strategy.md +440 -440
- package/vibe/rules/standards/anti-patterns.md +541 -541
- package/vibe/rules/standards/code-structure.md +291 -291
- package/vibe/rules/standards/complexity-metrics.md +313 -313
- package/vibe/rules/standards/git-workflow.md +237 -237
- package/vibe/rules/standards/naming-conventions.md +198 -198
- package/vibe/rules/standards/security.md +305 -305
- package/vibe/rules/writing/document-style.md +74 -74
- package/vibe/setup.sh +31 -31
- package/vibe/templates/constitution-template.md +252 -252
- package/vibe/templates/contract-backend-template.md +526 -526
- package/vibe/templates/contract-frontend-template.md +599 -599
- package/vibe/templates/feature-template.md +96 -96
- package/vibe/templates/spec-template.md +221 -221
- package/vibe/ui-ux-data/charts.csv +26 -26
- package/vibe/ui-ux-data/colors.csv +97 -97
- package/vibe/ui-ux-data/icons.csv +101 -101
- package/vibe/ui-ux-data/landing.csv +31 -31
- package/vibe/ui-ux-data/products.csv +96 -96
- package/vibe/ui-ux-data/react-performance.csv +45 -45
- package/vibe/ui-ux-data/stacks/astro.csv +54 -54
- package/vibe/ui-ux-data/stacks/flutter.csv +53 -53
- package/vibe/ui-ux-data/stacks/html-tailwind.csv +56 -56
- package/vibe/ui-ux-data/stacks/jetpack-compose.csv +53 -53
- package/vibe/ui-ux-data/stacks/nextjs.csv +53 -53
- package/vibe/ui-ux-data/stacks/nuxt-ui.csv +51 -51
- package/vibe/ui-ux-data/stacks/nuxtjs.csv +59 -59
- package/vibe/ui-ux-data/stacks/react-native.csv +52 -52
- package/vibe/ui-ux-data/stacks/react.csv +54 -54
- package/vibe/ui-ux-data/stacks/shadcn.csv +61 -61
- package/vibe/ui-ux-data/stacks/svelte.csv +54 -54
- package/vibe/ui-ux-data/stacks/swiftui.csv +51 -51
- package/vibe/ui-ux-data/stacks/vue.csv +50 -50
- package/vibe/ui-ux-data/styles.csv +68 -68
- package/vibe/ui-ux-data/typography.csv +57 -57
- package/vibe/ui-ux-data/ui-reasoning.csv +101 -101
- package/vibe/ui-ux-data/ux-guidelines.csv +99 -99
- package/vibe/ui-ux-data/version.json +31 -31
- package/vibe/ui-ux-data/web-interface.csv +31 -31
- package/commands/vibe.voice.md +0 -79
|
@@ -1,416 +1,416 @@
|
|
|
1
|
-
# 🚀 TypeScript + Astro Quality Rules
|
|
2
|
-
|
|
3
|
-
## Core Principles (inherited from core)
|
|
4
|
-
|
|
5
|
-
```markdown
|
|
6
|
-
✅ Single Responsibility (SRP)
|
|
7
|
-
✅ Don't Repeat Yourself (DRY)
|
|
8
|
-
✅ Reusability
|
|
9
|
-
✅ Low Complexity
|
|
10
|
-
✅ Functions ≤ 30 lines
|
|
11
|
-
✅ Nesting ≤ 3 levels
|
|
12
|
-
✅ Cyclomatic complexity ≤ 10
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Astro Architecture
|
|
16
|
-
|
|
17
|
-
```
|
|
18
|
-
┌─────────────────────────────────────────────┐
|
|
19
|
-
│ .astro Components (Server-first) │
|
|
20
|
-
│ - Zero JS by default │
|
|
21
|
-
│ - HTML + CSS + optional JS │
|
|
22
|
-
├─────────────────────────────────────────────┤
|
|
23
|
-
│ Islands (Interactive Components) │
|
|
24
|
-
│ - React, Vue, Svelte, Solid │
|
|
25
|
-
│ - Hydration on demand │
|
|
26
|
-
├─────────────────────────────────────────────┤
|
|
27
|
-
│ Content Collections (Type-safe content) │
|
|
28
|
-
│ - Markdown, MDX │
|
|
29
|
-
│ - Schema validation │
|
|
30
|
-
└─────────────────────────────────────────────┘
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Astro Component Patterns
|
|
34
|
-
|
|
35
|
-
### 1. Basic Component Structure
|
|
36
|
-
|
|
37
|
-
```astro
|
|
38
|
-
---
|
|
39
|
-
// Component Script (Server-side TypeScript)
|
|
40
|
-
interface Props {
|
|
41
|
-
title: string;
|
|
42
|
-
description?: string;
|
|
43
|
-
tags?: string[];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const { title, description, tags = [] } = Astro.props;
|
|
47
|
-
|
|
48
|
-
// Server-side data fetching
|
|
49
|
-
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
<!-- Component Template -->
|
|
53
|
-
<article class="card">
|
|
54
|
-
<h2>{title}</h2>
|
|
55
|
-
{description && <p>{description}</p>}
|
|
56
|
-
|
|
57
|
-
{tags.length > 0 && (
|
|
58
|
-
<ul class="tags">
|
|
59
|
-
{tags.map(tag => <li>{tag}</li>)}
|
|
60
|
-
</ul>
|
|
61
|
-
)}
|
|
62
|
-
</article>
|
|
63
|
-
|
|
64
|
-
<style>
|
|
65
|
-
/* Scoped styles by default */
|
|
66
|
-
.card {
|
|
67
|
-
padding: 1rem;
|
|
68
|
-
border: 1px solid #ccc;
|
|
69
|
-
border-radius: 8px;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.tags {
|
|
73
|
-
display: flex;
|
|
74
|
-
gap: 0.5rem;
|
|
75
|
-
list-style: none;
|
|
76
|
-
padding: 0;
|
|
77
|
-
}
|
|
78
|
-
</style>
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### 2. Slots and Named Slots
|
|
82
|
-
|
|
83
|
-
```astro
|
|
84
|
-
---
|
|
85
|
-
// Layout.astro
|
|
86
|
-
interface Props {
|
|
87
|
-
title: string;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const { title } = Astro.props;
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
<!DOCTYPE html>
|
|
94
|
-
<html>
|
|
95
|
-
<head>
|
|
96
|
-
<title>{title}</title>
|
|
97
|
-
<slot name="head" />
|
|
98
|
-
</head>
|
|
99
|
-
<body>
|
|
100
|
-
<header>
|
|
101
|
-
<slot name="header">
|
|
102
|
-
<h1>{title}</h1>
|
|
103
|
-
</slot>
|
|
104
|
-
</header>
|
|
105
|
-
|
|
106
|
-
<main>
|
|
107
|
-
<slot /> <!-- Default slot -->
|
|
108
|
-
</main>
|
|
109
|
-
|
|
110
|
-
<footer>
|
|
111
|
-
<slot name="footer">
|
|
112
|
-
<p>© 2024</p>
|
|
113
|
-
</slot>
|
|
114
|
-
</footer>
|
|
115
|
-
</body>
|
|
116
|
-
</html>
|
|
117
|
-
|
|
118
|
-
<!-- Usage -->
|
|
119
|
-
---
|
|
120
|
-
import Layout from '../layouts/Layout.astro';
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
<Layout title="Home">
|
|
124
|
-
<Fragment slot="header">
|
|
125
|
-
<nav>...</nav>
|
|
126
|
-
</Fragment>
|
|
127
|
-
|
|
128
|
-
<p>Main content goes here</p>
|
|
129
|
-
|
|
130
|
-
<p slot="footer">Custom footer</p>
|
|
131
|
-
</Layout>
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### 3. Content Collections
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
// src/content/config.ts
|
|
138
|
-
import { defineCollection, z } from 'astro:content';
|
|
139
|
-
|
|
140
|
-
const blogCollection = defineCollection({
|
|
141
|
-
type: 'content',
|
|
142
|
-
schema: z.object({
|
|
143
|
-
title: z.string(),
|
|
144
|
-
description: z.string(),
|
|
145
|
-
pubDate: z.coerce.date(),
|
|
146
|
-
author: z.string().default('Anonymous'),
|
|
147
|
-
tags: z.array(z.string()).default([]),
|
|
148
|
-
image: z.string().optional(),
|
|
149
|
-
draft: z.boolean().default(false),
|
|
150
|
-
}),
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
const authorsCollection = defineCollection({
|
|
154
|
-
type: 'data',
|
|
155
|
-
schema: z.object({
|
|
156
|
-
name: z.string(),
|
|
157
|
-
email: z.string().email(),
|
|
158
|
-
avatar: z.string().url(),
|
|
159
|
-
}),
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
export const collections = {
|
|
163
|
-
blog: blogCollection,
|
|
164
|
-
authors: authorsCollection,
|
|
165
|
-
};
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
```astro
|
|
169
|
-
---
|
|
170
|
-
// src/pages/blog/[...slug].astro
|
|
171
|
-
import { getCollection, getEntry } from 'astro:content';
|
|
172
|
-
import Layout from '../../layouts/Layout.astro';
|
|
173
|
-
|
|
174
|
-
export async function getStaticPaths() {
|
|
175
|
-
const posts = await getCollection('blog', ({ data }) => !data.draft);
|
|
176
|
-
|
|
177
|
-
return posts.map(post => ({
|
|
178
|
-
params: { slug: post.slug },
|
|
179
|
-
props: { post },
|
|
180
|
-
}));
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const { post } = Astro.props;
|
|
184
|
-
const { Content, headings } = await post.render();
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
<Layout title={post.data.title}>
|
|
188
|
-
<article>
|
|
189
|
-
<h1>{post.data.title}</h1>
|
|
190
|
-
<time datetime={post.data.pubDate.toISOString()}>
|
|
191
|
-
{post.data.pubDate.toLocaleDateString()}
|
|
192
|
-
</time>
|
|
193
|
-
<Content />
|
|
194
|
-
</article>
|
|
195
|
-
</Layout>
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
### 4. Islands (Client Interactivity)
|
|
199
|
-
|
|
200
|
-
```astro
|
|
201
|
-
---
|
|
202
|
-
// Using React component
|
|
203
|
-
import Counter from '../components/Counter.tsx';
|
|
204
|
-
import SearchBar from '../components/SearchBar.tsx';
|
|
205
|
-
import Newsletter from '../components/Newsletter.tsx';
|
|
206
|
-
import Comments from '../components/Comments.tsx';
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
<!-- No JS - static HTML -->
|
|
210
|
-
<h1>My Page</h1>
|
|
211
|
-
|
|
212
|
-
<!-- Hydrate on page load -->
|
|
213
|
-
<Counter client:load initialCount={0} />
|
|
214
|
-
|
|
215
|
-
<!-- Hydrate when visible in viewport -->
|
|
216
|
-
<Comments client:visible postId="123" />
|
|
217
|
-
|
|
218
|
-
<!-- Hydrate on idle (requestIdleCallback) -->
|
|
219
|
-
<Newsletter client:idle />
|
|
220
|
-
|
|
221
|
-
<!-- Hydrate on media query match -->
|
|
222
|
-
<SearchBar client:media="(min-width: 768px)" />
|
|
223
|
-
|
|
224
|
-
<!-- Never hydrate - just render HTML -->
|
|
225
|
-
<StaticComponent />
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
### 5. API Routes
|
|
229
|
-
|
|
230
|
-
```typescript
|
|
231
|
-
// src/pages/api/posts.ts
|
|
232
|
-
import type { APIRoute } from 'astro';
|
|
233
|
-
|
|
234
|
-
export const GET: APIRoute = async ({ request, url }) => {
|
|
235
|
-
const page = Number(url.searchParams.get('page') ?? '1');
|
|
236
|
-
const limit = Number(url.searchParams.get('limit') ?? '10');
|
|
237
|
-
|
|
238
|
-
const posts = await db.post.findMany({
|
|
239
|
-
skip: (page - 1) * limit,
|
|
240
|
-
take: limit,
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
return new Response(JSON.stringify(posts), {
|
|
244
|
-
status: 200,
|
|
245
|
-
headers: { 'Content-Type': 'application/json' },
|
|
246
|
-
});
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
export const POST: APIRoute = async ({ request }) => {
|
|
250
|
-
try {
|
|
251
|
-
const body = await request.json();
|
|
252
|
-
|
|
253
|
-
if (!body.title) {
|
|
254
|
-
return new Response(JSON.stringify({ error: 'Title is required' }), {
|
|
255
|
-
status: 400,
|
|
256
|
-
headers: { 'Content-Type': 'application/json' },
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const post = await db.post.create({ data: body });
|
|
261
|
-
|
|
262
|
-
return new Response(JSON.stringify(post), {
|
|
263
|
-
status: 201,
|
|
264
|
-
headers: { 'Content-Type': 'application/json' },
|
|
265
|
-
});
|
|
266
|
-
} catch (error) {
|
|
267
|
-
return new Response(JSON.stringify({ error: 'Server error' }), {
|
|
268
|
-
status: 500,
|
|
269
|
-
headers: { 'Content-Type': 'application/json' },
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### 6. Middleware
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
// src/middleware.ts
|
|
279
|
-
import { defineMiddleware, sequence } from 'astro:middleware';
|
|
280
|
-
|
|
281
|
-
const auth = defineMiddleware(async ({ locals, request, redirect }, next) => {
|
|
282
|
-
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
|
|
283
|
-
|
|
284
|
-
if (token) {
|
|
285
|
-
const user = await validateToken(token);
|
|
286
|
-
if (user) {
|
|
287
|
-
locals.user = user;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Protected routes
|
|
292
|
-
if (request.url.includes('/admin') && !locals.user?.isAdmin) {
|
|
293
|
-
return redirect('/login');
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return next();
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
const logger = defineMiddleware(async ({ request }, next) => {
|
|
300
|
-
console.log(`${request.method} ${request.url}`);
|
|
301
|
-
const response = await next();
|
|
302
|
-
console.log(`Response: ${response.status}`);
|
|
303
|
-
return response;
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
export const onRequest = sequence(logger, auth);
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### 7. View Transitions
|
|
310
|
-
|
|
311
|
-
```astro
|
|
312
|
-
---
|
|
313
|
-
// Layout.astro
|
|
314
|
-
import { ViewTransitions } from 'astro:transitions';
|
|
315
|
-
---
|
|
316
|
-
|
|
317
|
-
<html>
|
|
318
|
-
<head>
|
|
319
|
-
<ViewTransitions />
|
|
320
|
-
</head>
|
|
321
|
-
<body>
|
|
322
|
-
<header transition:persist>
|
|
323
|
-
<nav>...</nav>
|
|
324
|
-
</header>
|
|
325
|
-
|
|
326
|
-
<main transition:animate="slide">
|
|
327
|
-
<slot />
|
|
328
|
-
</main>
|
|
329
|
-
</body>
|
|
330
|
-
</html>
|
|
331
|
-
|
|
332
|
-
<!-- Custom animation -->
|
|
333
|
-
<div transition:animate={{
|
|
334
|
-
old: {
|
|
335
|
-
name: 'fadeOut',
|
|
336
|
-
duration: '0.2s',
|
|
337
|
-
easing: 'ease-out',
|
|
338
|
-
},
|
|
339
|
-
new: {
|
|
340
|
-
name: 'fadeIn',
|
|
341
|
-
duration: '0.3s',
|
|
342
|
-
easing: 'ease-in',
|
|
343
|
-
},
|
|
344
|
-
}}>
|
|
345
|
-
Content
|
|
346
|
-
</div>
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### 8. Environment Variables
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
// astro.config.mjs
|
|
353
|
-
export default defineConfig({
|
|
354
|
-
vite: {
|
|
355
|
-
define: {
|
|
356
|
-
'import.meta.env.PUBLIC_API_URL': JSON.stringify(process.env.PUBLIC_API_URL),
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// Usage in .astro files
|
|
362
|
-
---
|
|
363
|
-
const apiUrl = import.meta.env.PUBLIC_API_URL; // Client-safe
|
|
364
|
-
const dbUrl = import.meta.env.DATABASE_URL; // Server-only
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
// Type declarations (env.d.ts)
|
|
368
|
-
/// <reference types="astro/client" />
|
|
369
|
-
interface ImportMetaEnv {
|
|
370
|
-
readonly DATABASE_URL: string;
|
|
371
|
-
readonly PUBLIC_API_URL: string;
|
|
372
|
-
}
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
## Project Structure
|
|
376
|
-
|
|
377
|
-
```
|
|
378
|
-
src/
|
|
379
|
-
├── components/
|
|
380
|
-
│ ├── Card.astro
|
|
381
|
-
│ ├── Header.astro
|
|
382
|
-
│ └── islands/ # Interactive components
|
|
383
|
-
│ ├── Counter.tsx
|
|
384
|
-
│ └── Search.svelte
|
|
385
|
-
├── content/
|
|
386
|
-
│ ├── config.ts # Collection schemas
|
|
387
|
-
│ └── blog/
|
|
388
|
-
│ ├── post-1.md
|
|
389
|
-
│ └── post-2.mdx
|
|
390
|
-
├── layouts/
|
|
391
|
-
│ ├── Layout.astro
|
|
392
|
-
│ └── BlogPost.astro
|
|
393
|
-
├── pages/
|
|
394
|
-
│ ├── index.astro
|
|
395
|
-
│ ├── blog/
|
|
396
|
-
│ │ ├── index.astro
|
|
397
|
-
│ │ └── [...slug].astro
|
|
398
|
-
│ └── api/
|
|
399
|
-
│ └── posts.ts
|
|
400
|
-
├── styles/
|
|
401
|
-
│ └── global.css
|
|
402
|
-
└── middleware.ts
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
## Checklist
|
|
406
|
-
|
|
407
|
-
- [ ] Use `.astro` for static content
|
|
408
|
-
- [ ] Use Islands only where needed (client:*)
|
|
409
|
-
- [ ] Define Content Collections with schemas
|
|
410
|
-
- [ ] Use TypeScript for type safety
|
|
411
|
-
- [ ] Scope styles by default
|
|
412
|
-
- [ ] Use slots for composition
|
|
413
|
-
- [ ] Implement proper error handling
|
|
414
|
-
- [ ] Use middleware for auth/logging
|
|
415
|
-
- [ ] Enable View Transitions for SPA-like navigation
|
|
416
|
-
- [ ] Optimize images with astro:assets
|
|
1
|
+
# 🚀 TypeScript + Astro Quality Rules
|
|
2
|
+
|
|
3
|
+
## Core Principles (inherited from core)
|
|
4
|
+
|
|
5
|
+
```markdown
|
|
6
|
+
✅ Single Responsibility (SRP)
|
|
7
|
+
✅ Don't Repeat Yourself (DRY)
|
|
8
|
+
✅ Reusability
|
|
9
|
+
✅ Low Complexity
|
|
10
|
+
✅ Functions ≤ 30 lines
|
|
11
|
+
✅ Nesting ≤ 3 levels
|
|
12
|
+
✅ Cyclomatic complexity ≤ 10
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Astro Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────────────────────────────────┐
|
|
19
|
+
│ .astro Components (Server-first) │
|
|
20
|
+
│ - Zero JS by default │
|
|
21
|
+
│ - HTML + CSS + optional JS │
|
|
22
|
+
├─────────────────────────────────────────────┤
|
|
23
|
+
│ Islands (Interactive Components) │
|
|
24
|
+
│ - React, Vue, Svelte, Solid │
|
|
25
|
+
│ - Hydration on demand │
|
|
26
|
+
├─────────────────────────────────────────────┤
|
|
27
|
+
│ Content Collections (Type-safe content) │
|
|
28
|
+
│ - Markdown, MDX │
|
|
29
|
+
│ - Schema validation │
|
|
30
|
+
└─────────────────────────────────────────────┘
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Astro Component Patterns
|
|
34
|
+
|
|
35
|
+
### 1. Basic Component Structure
|
|
36
|
+
|
|
37
|
+
```astro
|
|
38
|
+
---
|
|
39
|
+
// Component Script (Server-side TypeScript)
|
|
40
|
+
interface Props {
|
|
41
|
+
title: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
tags?: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { title, description, tags = [] } = Astro.props;
|
|
47
|
+
|
|
48
|
+
// Server-side data fetching
|
|
49
|
+
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
<!-- Component Template -->
|
|
53
|
+
<article class="card">
|
|
54
|
+
<h2>{title}</h2>
|
|
55
|
+
{description && <p>{description}</p>}
|
|
56
|
+
|
|
57
|
+
{tags.length > 0 && (
|
|
58
|
+
<ul class="tags">
|
|
59
|
+
{tags.map(tag => <li>{tag}</li>)}
|
|
60
|
+
</ul>
|
|
61
|
+
)}
|
|
62
|
+
</article>
|
|
63
|
+
|
|
64
|
+
<style>
|
|
65
|
+
/* Scoped styles by default */
|
|
66
|
+
.card {
|
|
67
|
+
padding: 1rem;
|
|
68
|
+
border: 1px solid #ccc;
|
|
69
|
+
border-radius: 8px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.tags {
|
|
73
|
+
display: flex;
|
|
74
|
+
gap: 0.5rem;
|
|
75
|
+
list-style: none;
|
|
76
|
+
padding: 0;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2. Slots and Named Slots
|
|
82
|
+
|
|
83
|
+
```astro
|
|
84
|
+
---
|
|
85
|
+
// Layout.astro
|
|
86
|
+
interface Props {
|
|
87
|
+
title: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const { title } = Astro.props;
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
<!DOCTYPE html>
|
|
94
|
+
<html>
|
|
95
|
+
<head>
|
|
96
|
+
<title>{title}</title>
|
|
97
|
+
<slot name="head" />
|
|
98
|
+
</head>
|
|
99
|
+
<body>
|
|
100
|
+
<header>
|
|
101
|
+
<slot name="header">
|
|
102
|
+
<h1>{title}</h1>
|
|
103
|
+
</slot>
|
|
104
|
+
</header>
|
|
105
|
+
|
|
106
|
+
<main>
|
|
107
|
+
<slot /> <!-- Default slot -->
|
|
108
|
+
</main>
|
|
109
|
+
|
|
110
|
+
<footer>
|
|
111
|
+
<slot name="footer">
|
|
112
|
+
<p>© 2024</p>
|
|
113
|
+
</slot>
|
|
114
|
+
</footer>
|
|
115
|
+
</body>
|
|
116
|
+
</html>
|
|
117
|
+
|
|
118
|
+
<!-- Usage -->
|
|
119
|
+
---
|
|
120
|
+
import Layout from '../layouts/Layout.astro';
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
<Layout title="Home">
|
|
124
|
+
<Fragment slot="header">
|
|
125
|
+
<nav>...</nav>
|
|
126
|
+
</Fragment>
|
|
127
|
+
|
|
128
|
+
<p>Main content goes here</p>
|
|
129
|
+
|
|
130
|
+
<p slot="footer">Custom footer</p>
|
|
131
|
+
</Layout>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 3. Content Collections
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// src/content/config.ts
|
|
138
|
+
import { defineCollection, z } from 'astro:content';
|
|
139
|
+
|
|
140
|
+
const blogCollection = defineCollection({
|
|
141
|
+
type: 'content',
|
|
142
|
+
schema: z.object({
|
|
143
|
+
title: z.string(),
|
|
144
|
+
description: z.string(),
|
|
145
|
+
pubDate: z.coerce.date(),
|
|
146
|
+
author: z.string().default('Anonymous'),
|
|
147
|
+
tags: z.array(z.string()).default([]),
|
|
148
|
+
image: z.string().optional(),
|
|
149
|
+
draft: z.boolean().default(false),
|
|
150
|
+
}),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const authorsCollection = defineCollection({
|
|
154
|
+
type: 'data',
|
|
155
|
+
schema: z.object({
|
|
156
|
+
name: z.string(),
|
|
157
|
+
email: z.string().email(),
|
|
158
|
+
avatar: z.string().url(),
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
export const collections = {
|
|
163
|
+
blog: blogCollection,
|
|
164
|
+
authors: authorsCollection,
|
|
165
|
+
};
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```astro
|
|
169
|
+
---
|
|
170
|
+
// src/pages/blog/[...slug].astro
|
|
171
|
+
import { getCollection, getEntry } from 'astro:content';
|
|
172
|
+
import Layout from '../../layouts/Layout.astro';
|
|
173
|
+
|
|
174
|
+
export async function getStaticPaths() {
|
|
175
|
+
const posts = await getCollection('blog', ({ data }) => !data.draft);
|
|
176
|
+
|
|
177
|
+
return posts.map(post => ({
|
|
178
|
+
params: { slug: post.slug },
|
|
179
|
+
props: { post },
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const { post } = Astro.props;
|
|
184
|
+
const { Content, headings } = await post.render();
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
<Layout title={post.data.title}>
|
|
188
|
+
<article>
|
|
189
|
+
<h1>{post.data.title}</h1>
|
|
190
|
+
<time datetime={post.data.pubDate.toISOString()}>
|
|
191
|
+
{post.data.pubDate.toLocaleDateString()}
|
|
192
|
+
</time>
|
|
193
|
+
<Content />
|
|
194
|
+
</article>
|
|
195
|
+
</Layout>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 4. Islands (Client Interactivity)
|
|
199
|
+
|
|
200
|
+
```astro
|
|
201
|
+
---
|
|
202
|
+
// Using React component
|
|
203
|
+
import Counter from '../components/Counter.tsx';
|
|
204
|
+
import SearchBar from '../components/SearchBar.tsx';
|
|
205
|
+
import Newsletter from '../components/Newsletter.tsx';
|
|
206
|
+
import Comments from '../components/Comments.tsx';
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
<!-- No JS - static HTML -->
|
|
210
|
+
<h1>My Page</h1>
|
|
211
|
+
|
|
212
|
+
<!-- Hydrate on page load -->
|
|
213
|
+
<Counter client:load initialCount={0} />
|
|
214
|
+
|
|
215
|
+
<!-- Hydrate when visible in viewport -->
|
|
216
|
+
<Comments client:visible postId="123" />
|
|
217
|
+
|
|
218
|
+
<!-- Hydrate on idle (requestIdleCallback) -->
|
|
219
|
+
<Newsletter client:idle />
|
|
220
|
+
|
|
221
|
+
<!-- Hydrate on media query match -->
|
|
222
|
+
<SearchBar client:media="(min-width: 768px)" />
|
|
223
|
+
|
|
224
|
+
<!-- Never hydrate - just render HTML -->
|
|
225
|
+
<StaticComponent />
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 5. API Routes
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// src/pages/api/posts.ts
|
|
232
|
+
import type { APIRoute } from 'astro';
|
|
233
|
+
|
|
234
|
+
export const GET: APIRoute = async ({ request, url }) => {
|
|
235
|
+
const page = Number(url.searchParams.get('page') ?? '1');
|
|
236
|
+
const limit = Number(url.searchParams.get('limit') ?? '10');
|
|
237
|
+
|
|
238
|
+
const posts = await db.post.findMany({
|
|
239
|
+
skip: (page - 1) * limit,
|
|
240
|
+
take: limit,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return new Response(JSON.stringify(posts), {
|
|
244
|
+
status: 200,
|
|
245
|
+
headers: { 'Content-Type': 'application/json' },
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export const POST: APIRoute = async ({ request }) => {
|
|
250
|
+
try {
|
|
251
|
+
const body = await request.json();
|
|
252
|
+
|
|
253
|
+
if (!body.title) {
|
|
254
|
+
return new Response(JSON.stringify({ error: 'Title is required' }), {
|
|
255
|
+
status: 400,
|
|
256
|
+
headers: { 'Content-Type': 'application/json' },
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const post = await db.post.create({ data: body });
|
|
261
|
+
|
|
262
|
+
return new Response(JSON.stringify(post), {
|
|
263
|
+
status: 201,
|
|
264
|
+
headers: { 'Content-Type': 'application/json' },
|
|
265
|
+
});
|
|
266
|
+
} catch (error) {
|
|
267
|
+
return new Response(JSON.stringify({ error: 'Server error' }), {
|
|
268
|
+
status: 500,
|
|
269
|
+
headers: { 'Content-Type': 'application/json' },
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### 6. Middleware
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
// src/middleware.ts
|
|
279
|
+
import { defineMiddleware, sequence } from 'astro:middleware';
|
|
280
|
+
|
|
281
|
+
const auth = defineMiddleware(async ({ locals, request, redirect }, next) => {
|
|
282
|
+
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
|
|
283
|
+
|
|
284
|
+
if (token) {
|
|
285
|
+
const user = await validateToken(token);
|
|
286
|
+
if (user) {
|
|
287
|
+
locals.user = user;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Protected routes
|
|
292
|
+
if (request.url.includes('/admin') && !locals.user?.isAdmin) {
|
|
293
|
+
return redirect('/login');
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return next();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const logger = defineMiddleware(async ({ request }, next) => {
|
|
300
|
+
console.log(`${request.method} ${request.url}`);
|
|
301
|
+
const response = await next();
|
|
302
|
+
console.log(`Response: ${response.status}`);
|
|
303
|
+
return response;
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
export const onRequest = sequence(logger, auth);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### 7. View Transitions
|
|
310
|
+
|
|
311
|
+
```astro
|
|
312
|
+
---
|
|
313
|
+
// Layout.astro
|
|
314
|
+
import { ViewTransitions } from 'astro:transitions';
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
<html>
|
|
318
|
+
<head>
|
|
319
|
+
<ViewTransitions />
|
|
320
|
+
</head>
|
|
321
|
+
<body>
|
|
322
|
+
<header transition:persist>
|
|
323
|
+
<nav>...</nav>
|
|
324
|
+
</header>
|
|
325
|
+
|
|
326
|
+
<main transition:animate="slide">
|
|
327
|
+
<slot />
|
|
328
|
+
</main>
|
|
329
|
+
</body>
|
|
330
|
+
</html>
|
|
331
|
+
|
|
332
|
+
<!-- Custom animation -->
|
|
333
|
+
<div transition:animate={{
|
|
334
|
+
old: {
|
|
335
|
+
name: 'fadeOut',
|
|
336
|
+
duration: '0.2s',
|
|
337
|
+
easing: 'ease-out',
|
|
338
|
+
},
|
|
339
|
+
new: {
|
|
340
|
+
name: 'fadeIn',
|
|
341
|
+
duration: '0.3s',
|
|
342
|
+
easing: 'ease-in',
|
|
343
|
+
},
|
|
344
|
+
}}>
|
|
345
|
+
Content
|
|
346
|
+
</div>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### 8. Environment Variables
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// astro.config.mjs
|
|
353
|
+
export default defineConfig({
|
|
354
|
+
vite: {
|
|
355
|
+
define: {
|
|
356
|
+
'import.meta.env.PUBLIC_API_URL': JSON.stringify(process.env.PUBLIC_API_URL),
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Usage in .astro files
|
|
362
|
+
---
|
|
363
|
+
const apiUrl = import.meta.env.PUBLIC_API_URL; // Client-safe
|
|
364
|
+
const dbUrl = import.meta.env.DATABASE_URL; // Server-only
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
// Type declarations (env.d.ts)
|
|
368
|
+
/// <reference types="astro/client" />
|
|
369
|
+
interface ImportMetaEnv {
|
|
370
|
+
readonly DATABASE_URL: string;
|
|
371
|
+
readonly PUBLIC_API_URL: string;
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Project Structure
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
src/
|
|
379
|
+
├── components/
|
|
380
|
+
│ ├── Card.astro
|
|
381
|
+
│ ├── Header.astro
|
|
382
|
+
│ └── islands/ # Interactive components
|
|
383
|
+
│ ├── Counter.tsx
|
|
384
|
+
│ └── Search.svelte
|
|
385
|
+
├── content/
|
|
386
|
+
│ ├── config.ts # Collection schemas
|
|
387
|
+
│ └── blog/
|
|
388
|
+
│ ├── post-1.md
|
|
389
|
+
│ └── post-2.mdx
|
|
390
|
+
├── layouts/
|
|
391
|
+
│ ├── Layout.astro
|
|
392
|
+
│ └── BlogPost.astro
|
|
393
|
+
├── pages/
|
|
394
|
+
│ ├── index.astro
|
|
395
|
+
│ ├── blog/
|
|
396
|
+
│ │ ├── index.astro
|
|
397
|
+
│ │ └── [...slug].astro
|
|
398
|
+
│ └── api/
|
|
399
|
+
│ └── posts.ts
|
|
400
|
+
├── styles/
|
|
401
|
+
│ └── global.css
|
|
402
|
+
└── middleware.ts
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Checklist
|
|
406
|
+
|
|
407
|
+
- [ ] Use `.astro` for static content
|
|
408
|
+
- [ ] Use Islands only where needed (client:*)
|
|
409
|
+
- [ ] Define Content Collections with schemas
|
|
410
|
+
- [ ] Use TypeScript for type safety
|
|
411
|
+
- [ ] Scope styles by default
|
|
412
|
+
- [ ] Use slots for composition
|
|
413
|
+
- [ ] Implement proper error handling
|
|
414
|
+
- [ ] Use middleware for auth/logging
|
|
415
|
+
- [ ] Enable View Transitions for SPA-like navigation
|
|
416
|
+
- [ ] Optimize images with astro:assets
|