@edupia-tutor/spec-driven-docs 0.14.0
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/bin/build.js +230 -0
- package/bin/index.js +598 -0
- package/commands/debug.md +830 -0
- package/commands/debug.tmpl +257 -0
- package/commands/define-product.md +652 -0
- package/commands/define-product.tmpl +158 -0
- package/commands/dev-gen-test.md +1010 -0
- package/commands/dev-gen-test.tmpl +490 -0
- package/commands/dev-run-test.md +744 -0
- package/commands/dev-run-test.tmpl +224 -0
- package/commands/dev-smoke-test.md +711 -0
- package/commands/dev-smoke-test.tmpl +217 -0
- package/commands/fix-bug.md +744 -0
- package/commands/fix-bug.tmpl +171 -0
- package/commands/generate-bdd.md +1054 -0
- package/commands/generate-bdd.tmpl +534 -0
- package/commands/generate-code.md +869 -0
- package/commands/generate-code.tmpl +349 -0
- package/commands/generate-design-spec.md +958 -0
- package/commands/generate-design-spec.tmpl +464 -0
- package/commands/generate-prd.md +748 -0
- package/commands/generate-prd.tmpl +254 -0
- package/commands/generate-spec-manifest.md +658 -0
- package/commands/generate-spec-manifest.tmpl +164 -0
- package/commands/generate-tech-docs.md +849 -0
- package/commands/generate-tech-docs.tmpl +355 -0
- package/commands/learn.md +636 -0
- package/commands/learn.tmpl +63 -0
- package/commands/map-testids.md +575 -0
- package/commands/map-testids.tmpl +81 -0
- package/commands/propose-scenario.md +623 -0
- package/commands/propose-scenario.tmpl +129 -0
- package/commands/qc-analyze.md +580 -0
- package/commands/qc-analyze.tmpl +86 -0
- package/commands/qc-design-test.md +562 -0
- package/commands/qc-design-test.tmpl +68 -0
- package/commands/qc-plan.md +543 -0
- package/commands/qc-plan.tmpl +49 -0
- package/commands/qc-report.md +554 -0
- package/commands/qc-report.tmpl +60 -0
- package/commands/qc-review.md +547 -0
- package/commands/qc-review.tmpl +53 -0
- package/commands/qc-run-test.md +604 -0
- package/commands/qc-run-test.tmpl +84 -0
- package/commands/refine-prd.md +772 -0
- package/commands/refine-prd.tmpl +140 -0
- package/commands/report-bug.md +639 -0
- package/commands/report-bug.tmpl +145 -0
- package/commands/review-code.md +677 -0
- package/commands/review-code.tmpl +104 -0
- package/commands/review-context.md +1047 -0
- package/commands/review-context.tmpl +415 -0
- package/commands/review-tech-docs.md +811 -0
- package/commands/review-tech-docs.tmpl +317 -0
- package/commands/setup-ai-first.md +545 -0
- package/commands/setup-ai-first.tmpl +358 -0
- package/commands/sync.md +451 -0
- package/commands/sync.tmpl +351 -0
- package/commands/update-framework.md +251 -0
- package/commands/update-framework.tmpl +151 -0
- package/commands/validate-traces.md +842 -0
- package/commands/validate-traces.tmpl +348 -0
- package/core/FRAMEWORK_VERSION +1 -0
- package/core/commands/debug.md +830 -0
- package/core/commands/define-product.md +652 -0
- package/core/commands/dev-gen-test.md +1010 -0
- package/core/commands/dev-run-test.md +744 -0
- package/core/commands/dev-smoke-test.md +711 -0
- package/core/commands/fix-bug.md +744 -0
- package/core/commands/generate-bdd.md +1054 -0
- package/core/commands/generate-code.md +869 -0
- package/core/commands/generate-design-spec.md +958 -0
- package/core/commands/generate-prd.md +748 -0
- package/core/commands/generate-spec-manifest.md +658 -0
- package/core/commands/generate-tech-docs.md +849 -0
- package/core/commands/learn.md +636 -0
- package/core/commands/map-testids.md +575 -0
- package/core/commands/propose-scenario.md +623 -0
- package/core/commands/qc-analyze.md +580 -0
- package/core/commands/qc-design-test.md +562 -0
- package/core/commands/qc-plan.md +543 -0
- package/core/commands/qc-report.md +554 -0
- package/core/commands/qc-review.md +547 -0
- package/core/commands/qc-run-test.md +604 -0
- package/core/commands/refine-prd.md +772 -0
- package/core/commands/report-bug.md +639 -0
- package/core/commands/review-code.md +677 -0
- package/core/commands/review-context.md +1047 -0
- package/core/commands/review-tech-docs.md +811 -0
- package/core/commands/setup-ai-first.md +545 -0
- package/core/commands/sync.md +451 -0
- package/core/commands/update-framework.md +251 -0
- package/core/commands/validate-traces.md +842 -0
- package/core/hooks/data-guard.js +141 -0
- package/core/hooks/settings.json +18 -0
- package/core/modules/android-compose/module.yaml +13 -0
- package/core/modules/android-compose/stack-profile.yaml +57 -0
- package/core/modules/angular/architecture-snippets/component-patterns.md +187 -0
- package/core/modules/angular/module.yaml +6 -0
- package/core/modules/angular/stack-profile.yaml +38 -0
- package/core/modules/context-engineering/architecture-snippets/context-design.md +119 -0
- package/core/modules/context-engineering/module.yaml +9 -0
- package/core/modules/context-engineering/stack-profile.yaml +61 -0
- package/core/modules/dotnet/architecture-snippets/clean-arch.md +160 -0
- package/core/modules/dotnet/module.yaml +6 -0
- package/core/modules/dotnet/stack-profile.yaml +50 -0
- package/core/modules/flutter/module.yaml +14 -0
- package/core/modules/flutter/stack-profile.yaml +59 -0
- package/core/modules/golang/architecture-snippets/domain-layout.md +283 -0
- package/core/modules/golang/module.yaml +6 -0
- package/core/modules/golang/stack-profile.yaml +40 -0
- package/core/modules/ios-swiftui/module.yaml +13 -0
- package/core/modules/ios-swiftui/stack-profile.yaml +55 -0
- package/core/modules/java-spring/architecture-snippets/layered-arch.md +201 -0
- package/core/modules/java-spring/module.yaml +15 -0
- package/core/modules/java-spring/stack-profile.yaml +28 -0
- package/core/modules/nextjs/architecture-snippets/app-router-patterns.md +269 -0
- package/core/modules/nextjs/module.yaml +14 -0
- package/core/modules/nextjs/stack-profile.yaml +74 -0
- package/core/modules/nuxt/module.yaml +14 -0
- package/core/modules/nuxt/stack-profile.yaml +58 -0
- package/core/modules/php-laravel/architecture-snippets/service-repository.md +302 -0
- package/core/modules/php-laravel/module.yaml +15 -0
- package/core/modules/php-laravel/stack-profile.yaml +56 -0
- package/core/modules/qc-playwright/stack-profile.yaml +66 -0
- package/core/modules/react/architecture-snippets/hooks-query-patterns.md +254 -0
- package/core/modules/react/module.yaml +14 -0
- package/core/modules/react/stack-profile.yaml +63 -0
- package/core/modules/react-native/module.yaml +14 -0
- package/core/modules/react-native/stack-profile.yaml +56 -0
- package/core/modules/vue/module.yaml +14 -0
- package/core/modules/vue/stack-profile.yaml +65 -0
- package/core/rules/data-protection.md +80 -0
- package/core/rules/workflow.md +44 -0
- package/core/skills/code/SKILL.md +770 -0
- package/core/skills/debug/SKILL.md +869 -0
- package/core/skills/design-spec/SKILL.md +589 -0
- package/core/skills/discovery/SKILL.md +554 -0
- package/core/skills/prd/SKILL.md +562 -0
- package/core/skills/qc/qa-analyst/DOC_GAPS.template.md +63 -0
- package/core/skills/qc/qa-analyst/acceptance-criteria.md +60 -0
- package/core/skills/qc/qa-analyst/business-rules.md +59 -0
- package/core/skills/qc/qa-analyst/data-flow.md +64 -0
- package/core/skills/qc/qa-analyst/spec-breakdown.md +61 -0
- package/core/skills/qc/qa-designer/e2e/journey.md +41 -0
- package/core/skills/qc/qa-designer/exploratory/charter.md +68 -0
- package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +43 -0
- package/core/skills/qc/qa-designer/functional/api.md +45 -0
- package/core/skills/qc/qa-designer/functional/gui-feature.md +46 -0
- package/core/skills/qc/qa-designer/functional/gui-screen.md +52 -0
- package/core/skills/qc/qa-designer/integration/api.md +42 -0
- package/core/skills/qc/qa-designer/integration/db.md +39 -0
- package/core/skills/qc/qa-designer/integration/gui.md +40 -0
- package/core/skills/qc/qa-designer/integration/kafka.md +40 -0
- package/core/skills/qc/qa-designer/non-functional.md +40 -0
- package/core/skills/qc/qa-planner/test-plan.md +120 -0
- package/core/skills/qc/qa-reviewer/script/e2e.md +87 -0
- package/core/skills/qc/qa-reviewer/script/exploratory.md +45 -0
- package/core/skills/qc/qa-reviewer/script/functional.md +101 -0
- package/core/skills/qc/qa-reviewer/script/integration.md +91 -0
- package/core/skills/qc/qa-reviewer/script/non-functional.md +126 -0
- package/core/skills/qc/qa-reviewer/test-case/e2e.md +73 -0
- package/core/skills/qc/qa-reviewer/test-case/exploratory.md +43 -0
- package/core/skills/qc/qa-reviewer/test-case/functional.md +76 -0
- package/core/skills/qc/qa-reviewer/test-case/integration.md +69 -0
- package/core/skills/qc/qa-reviewer/test-case/non-functional.md +73 -0
- package/core/skills/qc/qa-runner/e2e.md +49 -0
- package/core/skills/qc/qa-runner/exploratory/session.md +36 -0
- package/core/skills/qc/qa-runner/functional/api.md +35 -0
- package/core/skills/qc/qa-runner/functional/gui-feature.md +51 -0
- package/core/skills/qc/qa-runner/functional/gui-screen.md +55 -0
- package/core/skills/qc/qa-runner/integration.md +47 -0
- package/core/skills/qc/qa-runner/non-functional.md +49 -0
- package/core/skills/qc/qa-runner/report/report.md +37 -0
- package/core/skills/setup-ai-first/SKILL.md +216 -0
- package/core/skills/spec/SKILL.md +461 -0
- package/core/skills/test/SKILL.md +1297 -0
- package/core/steps/capture-lesson.md +79 -0
- package/core/steps/context-loader.md +307 -0
- package/core/steps/gate.md +87 -0
- package/core/steps/report-footer.md +100 -0
- package/core/steps/review-fanout.md +138 -0
- package/core/steps/spawn-agent.md +124 -0
- package/core/steps/trace-mirror.md +26 -0
- package/core/templates/architecture.template.md +113 -0
- package/core/templates/design-spec.template.md +217 -0
- package/core/templates/feature.template +259 -0
- package/core/templates/platform-guide.template.md +145 -0
- package/core/templates/prd.template.md +327 -0
- package/core/templates/product-definition.template.md +168 -0
- package/core/templates/project-context.yaml +161 -0
- package/docs/01-getting-started/README.md +19 -0
- package/docs/01-getting-started/core-concepts.md +102 -0
- package/docs/01-getting-started/installation.md +156 -0
- package/docs/01-getting-started/quickstart.md +85 -0
- package/docs/02-guides/README.md +26 -0
- package/docs/02-guides/developer/README.md +46 -0
- package/docs/02-guides/developer/bdd-and-trace.md +125 -0
- package/docs/02-guides/developer/commands.md +76 -0
- package/docs/02-guides/developer/pr-checklist.md +15 -0
- package/docs/02-guides/developer/scenarios.md +460 -0
- package/docs/02-guides/developer/workflow.md +121 -0
- package/docs/02-guides/product-owner/README.md +79 -0
- package/docs/02-guides/product-owner/commands.md +30 -0
- package/docs/02-guides/product-owner/handoff-checklist.md +42 -0
- package/docs/02-guides/product-owner/prd-writing-rules.md +45 -0
- package/docs/02-guides/product-owner/scenarios.md +436 -0
- package/docs/02-guides/tester/README.md +75 -0
- package/docs/02-guides/tester/bug-reporting.md +117 -0
- package/docs/02-guides/tester/qc-automation.md +165 -0
- package/docs/02-guides/tester/reading-specs.md +79 -0
- package/docs/02-guides/tester/scenarios.md +186 -0
- package/docs/02-guides/tester/spec-manifest.md +130 -0
- package/docs/02-guides/tester/test-checklist.md +31 -0
- package/docs/02-guides/tester/workflow.md +77 -0
- package/docs/03-concepts/README.md +19 -0
- package/docs/03-concepts/architecture.md +248 -0
- package/docs/03-concepts/pipeline.md +274 -0
- package/docs/03-concepts/traceability.md +149 -0
- package/docs/04-operations/README.md +33 -0
- package/docs/04-operations/bug-flow.md +362 -0
- package/docs/04-operations/publishing.md +137 -0
- package/docs/04-operations/sync-and-update.md +522 -0
- package/docs/05-reference/README.md +32 -0
- package/docs/05-reference/command-cheatsheet.md +147 -0
- package/docs/05-reference/commands.md +232 -0
- package/docs/05-reference/modules.md +110 -0
- package/docs/05-reference/trace-schema.md +153 -0
- package/docs/README.md +49 -0
- package/hooks/data-guard.js +141 -0
- package/hooks/settings.json +18 -0
- package/modules/android-compose/module.yaml +13 -0
- package/modules/android-compose/stack-profile.yaml +57 -0
- package/modules/angular/architecture-snippets/component-patterns.md +187 -0
- package/modules/angular/module.yaml +6 -0
- package/modules/angular/stack-profile.yaml +38 -0
- package/modules/context-engineering/architecture-snippets/context-design.md +119 -0
- package/modules/context-engineering/module.yaml +9 -0
- package/modules/context-engineering/stack-profile.yaml +61 -0
- package/modules/dotnet/architecture-snippets/clean-arch.md +160 -0
- package/modules/dotnet/module.yaml +6 -0
- package/modules/dotnet/stack-profile.yaml +50 -0
- package/modules/flutter/module.yaml +14 -0
- package/modules/flutter/stack-profile.yaml +59 -0
- package/modules/golang/architecture-snippets/domain-layout.md +283 -0
- package/modules/golang/module.yaml +6 -0
- package/modules/golang/stack-profile.yaml +40 -0
- package/modules/ios-swiftui/module.yaml +13 -0
- package/modules/ios-swiftui/stack-profile.yaml +55 -0
- package/modules/java-spring/architecture-snippets/layered-arch.md +201 -0
- package/modules/java-spring/module.yaml +15 -0
- package/modules/java-spring/stack-profile.yaml +28 -0
- package/modules/nextjs/architecture-snippets/app-router-patterns.md +269 -0
- package/modules/nextjs/module.yaml +14 -0
- package/modules/nextjs/stack-profile.yaml +74 -0
- package/modules/nuxt/module.yaml +14 -0
- package/modules/nuxt/stack-profile.yaml +58 -0
- package/modules/php-laravel/architecture-snippets/service-repository.md +302 -0
- package/modules/php-laravel/module.yaml +15 -0
- package/modules/php-laravel/stack-profile.yaml +56 -0
- package/modules/qc-playwright/stack-profile.yaml +66 -0
- package/modules/react/architecture-snippets/hooks-query-patterns.md +254 -0
- package/modules/react/module.yaml +14 -0
- package/modules/react/stack-profile.yaml +63 -0
- package/modules/react-native/module.yaml +14 -0
- package/modules/react-native/stack-profile.yaml +56 -0
- package/modules/vue/module.yaml +14 -0
- package/modules/vue/stack-profile.yaml +65 -0
- package/package.json +49 -0
- package/rules/data-protection.md +80 -0
- package/rules/workflow.md +44 -0
- package/scripts/init.sh +49 -0
- package/scripts/migrate-specs.js +256 -0
- package/scripts/upgrade.sh +94 -0
- package/skills/code/SKILL.md +770 -0
- package/skills/code/SKILL.tmpl +176 -0
- package/skills/debug/SKILL.md +869 -0
- package/skills/debug/SKILL.tmpl +262 -0
- package/skills/design-spec/SKILL.md +589 -0
- package/skills/design-spec/SKILL.tmpl +95 -0
- package/skills/discovery/SKILL.md +554 -0
- package/skills/discovery/SKILL.tmpl +147 -0
- package/skills/prd/SKILL.md +562 -0
- package/skills/prd/SKILL.tmpl +188 -0
- package/skills/qc/qa-analyst/DOC_GAPS.template.md +63 -0
- package/skills/qc/qa-analyst/acceptance-criteria.md +60 -0
- package/skills/qc/qa-analyst/business-rules.md +59 -0
- package/skills/qc/qa-analyst/data-flow.md +64 -0
- package/skills/qc/qa-analyst/spec-breakdown.md +61 -0
- package/skills/qc/qa-designer/e2e/journey.md +41 -0
- package/skills/qc/qa-designer/exploratory/charter.md +68 -0
- package/skills/qc/qa-designer/exploratory/explore-to-functional.md +43 -0
- package/skills/qc/qa-designer/functional/api.md +45 -0
- package/skills/qc/qa-designer/functional/gui-feature.md +46 -0
- package/skills/qc/qa-designer/functional/gui-screen.md +52 -0
- package/skills/qc/qa-designer/integration/api.md +42 -0
- package/skills/qc/qa-designer/integration/db.md +39 -0
- package/skills/qc/qa-designer/integration/gui.md +40 -0
- package/skills/qc/qa-designer/integration/kafka.md +40 -0
- package/skills/qc/qa-designer/non-functional.md +40 -0
- package/skills/qc/qa-planner/test-plan.md +120 -0
- package/skills/qc/qa-reviewer/script/e2e.md +87 -0
- package/skills/qc/qa-reviewer/script/exploratory.md +45 -0
- package/skills/qc/qa-reviewer/script/functional.md +101 -0
- package/skills/qc/qa-reviewer/script/integration.md +91 -0
- package/skills/qc/qa-reviewer/script/non-functional.md +126 -0
- package/skills/qc/qa-reviewer/test-case/e2e.md +73 -0
- package/skills/qc/qa-reviewer/test-case/exploratory.md +43 -0
- package/skills/qc/qa-reviewer/test-case/functional.md +76 -0
- package/skills/qc/qa-reviewer/test-case/integration.md +69 -0
- package/skills/qc/qa-reviewer/test-case/non-functional.md +73 -0
- package/skills/qc/qa-runner/e2e.md +49 -0
- package/skills/qc/qa-runner/exploratory/session.md +36 -0
- package/skills/qc/qa-runner/functional/api.md +35 -0
- package/skills/qc/qa-runner/functional/gui-feature.md +51 -0
- package/skills/qc/qa-runner/functional/gui-screen.md +55 -0
- package/skills/qc/qa-runner/integration.md +47 -0
- package/skills/qc/qa-runner/non-functional.md +49 -0
- package/skills/qc/qa-runner/report/report.md +37 -0
- package/skills/setup-ai-first/SKILL.md +216 -0
- package/skills/setup-ai-first/SKILL.tmpl +116 -0
- package/skills/spec/SKILL.md +461 -0
- package/skills/spec/SKILL.tmpl +174 -0
- package/skills/test/SKILL.md +1297 -0
- package/skills/test/SKILL.tmpl +296 -0
- package/steps/capture-lesson.md +79 -0
- package/steps/context-loader.md +307 -0
- package/steps/gate.md +87 -0
- package/steps/report-footer.md +100 -0
- package/steps/review-fanout.md +138 -0
- package/steps/spawn-agent.md +124 -0
- package/steps/trace-mirror.md +26 -0
- package/templates/architecture.template.md +113 -0
- package/templates/design-spec.template.md +217 -0
- package/templates/feature.template +259 -0
- package/templates/platform-guide.template.md +145 -0
- package/templates/prd.template.md +327 -0
- package/templates/product-definition.template.md +168 -0
- package/templates/project-context.yaml +161 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Next.js App Router — Server Components, Server Actions & DAL Patterns
|
|
2
|
+
|
|
3
|
+
## Data Access Layer (server-only)
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// lib/dal/orders.dal.ts
|
|
7
|
+
// @trace.source=specs/order/order-management/bdd/ORD-UC1.feature
|
|
8
|
+
import 'server-only'; // prevents accidental import on client
|
|
9
|
+
import { db } from '@/lib/db';
|
|
10
|
+
import type { Order } from '@/types/order';
|
|
11
|
+
|
|
12
|
+
export async function getOrderById(id: number): Promise<Order | null> {
|
|
13
|
+
return db.order.findUnique({
|
|
14
|
+
where: { id },
|
|
15
|
+
include: { items: { include: { product: true } }, customer: true },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function listOrdersByCustomer(customerId: number): Promise<Order[]> {
|
|
20
|
+
return db.order.findMany({
|
|
21
|
+
where: { customerId },
|
|
22
|
+
include: { items: true },
|
|
23
|
+
orderBy: { createdAt: 'desc' },
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Server Component (Data Fetching + Layout)
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
// app/(dashboard)/orders/page.tsx
|
|
32
|
+
// @trace.implements=ORD-UC1-SC1
|
|
33
|
+
// @trace.source=specs/order/order-management/bdd/ORD-UC1.feature
|
|
34
|
+
import { listOrdersByCustomer } from '@/lib/dal/orders.dal';
|
|
35
|
+
import { getCurrentUser } from '@/lib/auth';
|
|
36
|
+
import { OrderCard } from './_components/OrderCard';
|
|
37
|
+
import { EmptyState } from '@/components/ui/EmptyState';
|
|
38
|
+
|
|
39
|
+
export default async function OrdersPage() {
|
|
40
|
+
const user = await getCurrentUser();
|
|
41
|
+
const orders = await listOrdersByCustomer(user.id);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<section>
|
|
45
|
+
<h1>My Orders</h1>
|
|
46
|
+
{orders.length === 0 ? (
|
|
47
|
+
<EmptyState message="No orders yet." />
|
|
48
|
+
) : (
|
|
49
|
+
<ul>
|
|
50
|
+
{orders.map(order => (
|
|
51
|
+
<OrderCard key={order.id} order={order} />
|
|
52
|
+
))}
|
|
53
|
+
</ul>
|
|
54
|
+
)}
|
|
55
|
+
</section>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Zod Schema (shared server + client)
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// lib/validations/order.schema.ts
|
|
64
|
+
import { z } from 'zod';
|
|
65
|
+
|
|
66
|
+
export const createOrderSchema = z.object({
|
|
67
|
+
customerId: z.number().positive(),
|
|
68
|
+
items: z.array(z.object({
|
|
69
|
+
productId: z.number().positive(),
|
|
70
|
+
quantity: z.number().int().min(1, 'Quantity must be at least 1'),
|
|
71
|
+
})).min(1, 'At least one item is required'),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export type CreateOrderInput = z.infer<typeof createOrderSchema>;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Server Action (Mutation)
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// lib/actions/order.actions.ts
|
|
81
|
+
'use server';
|
|
82
|
+
// @trace.implements=ORD-UC2-SC1
|
|
83
|
+
// @trace.source=specs/order/order-management/bdd/ORD-UC2.feature
|
|
84
|
+
|
|
85
|
+
import { revalidatePath } from 'next/cache';
|
|
86
|
+
import { redirect } from 'next/navigation';
|
|
87
|
+
import { createOrderSchema } from '@/lib/validations/order.schema';
|
|
88
|
+
import { db } from '@/lib/db';
|
|
89
|
+
import { getCurrentUser } from '@/lib/auth';
|
|
90
|
+
|
|
91
|
+
// Returns { error } on failure, redirects on success
|
|
92
|
+
export async function createOrder(formData: FormData) {
|
|
93
|
+
const user = await getCurrentUser();
|
|
94
|
+
|
|
95
|
+
const parsed = createOrderSchema.safeParse({
|
|
96
|
+
customerId: user.id,
|
|
97
|
+
items: JSON.parse(formData.get('items') as string),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!parsed.success) {
|
|
101
|
+
return { error: parsed.error.flatten().fieldErrors };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
await db.order.create({
|
|
106
|
+
data: {
|
|
107
|
+
customerId: parsed.data.customerId,
|
|
108
|
+
status: 'pending',
|
|
109
|
+
items: {
|
|
110
|
+
create: parsed.data.items.map(item => ({
|
|
111
|
+
productId: item.productId,
|
|
112
|
+
quantity: item.quantity,
|
|
113
|
+
})),
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
} catch {
|
|
118
|
+
return { error: { _form: ['Failed to create order. Please try again.'] } };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
revalidatePath('/orders');
|
|
122
|
+
redirect('/orders');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function cancelOrder(orderId: number) {
|
|
126
|
+
const user = await getCurrentUser();
|
|
127
|
+
|
|
128
|
+
const order = await db.order.findFirst({
|
|
129
|
+
where: { id: orderId, customerId: user.id },
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (!order) return { error: 'Order not found.' };
|
|
133
|
+
if (order.status !== 'pending') return { error: 'Only pending orders can be cancelled.' };
|
|
134
|
+
|
|
135
|
+
await db.order.update({ where: { id: orderId }, data: { status: 'cancelled' } });
|
|
136
|
+
|
|
137
|
+
revalidatePath('/orders');
|
|
138
|
+
return { success: true };
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Client Component (Interactive UI)
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
// app/(dashboard)/orders/_components/CancelOrderButton.tsx
|
|
146
|
+
'use client';
|
|
147
|
+
// @trace.implements=ORD-UC3-SC1
|
|
148
|
+
// @trace.source=specs/order/order-management/bdd/ORD-UC3.feature
|
|
149
|
+
import { useTransition } from 'react';
|
|
150
|
+
import { cancelOrder } from '@/lib/actions/order.actions';
|
|
151
|
+
|
|
152
|
+
interface Props {
|
|
153
|
+
orderId: number;
|
|
154
|
+
disabled?: boolean;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function CancelOrderButton({ orderId, disabled }: Props) {
|
|
158
|
+
const [isPending, startTransition] = useTransition();
|
|
159
|
+
|
|
160
|
+
const handleCancel = () => {
|
|
161
|
+
startTransition(async () => {
|
|
162
|
+
const result = await cancelOrder(orderId);
|
|
163
|
+
if (result?.error) {
|
|
164
|
+
alert(result.error); // replace with toast in real app
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<button
|
|
171
|
+
onClick={handleCancel}
|
|
172
|
+
disabled={disabled || isPending}
|
|
173
|
+
className="btn-danger"
|
|
174
|
+
>
|
|
175
|
+
{isPending ? 'Cancelling...' : 'Cancel Order'}
|
|
176
|
+
</button>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## API Route (for webhooks / external consumers)
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// app/api/orders/webhook/route.ts
|
|
185
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
186
|
+
import { verifyWebhookSignature } from '@/lib/webhook';
|
|
187
|
+
import { db } from '@/lib/db';
|
|
188
|
+
|
|
189
|
+
export async function POST(request: NextRequest) {
|
|
190
|
+
const body = await request.json();
|
|
191
|
+
|
|
192
|
+
if (!verifyWebhookSignature(request, body)) {
|
|
193
|
+
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (body.event === 'payment.completed') {
|
|
197
|
+
await db.order.update({
|
|
198
|
+
where: { id: body.orderId },
|
|
199
|
+
data: { status: 'paid', paidAt: new Date() },
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return NextResponse.json({ received: true });
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Loading + Error Boundaries
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
// app/(dashboard)/orders/loading.tsx
|
|
211
|
+
export default function OrdersLoading() {
|
|
212
|
+
return (
|
|
213
|
+
<div className="order-skeleton">
|
|
214
|
+
{Array.from({ length: 3 }).map((_, i) => (
|
|
215
|
+
<div key={i} className="skeleton-card animate-pulse" />
|
|
216
|
+
))}
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
// app/(dashboard)/orders/error.tsx
|
|
224
|
+
'use client';
|
|
225
|
+
export default function OrdersError({ error, reset }: {
|
|
226
|
+
error: Error & { digest?: string };
|
|
227
|
+
reset: () => void;
|
|
228
|
+
}) {
|
|
229
|
+
return (
|
|
230
|
+
<div className="error-state">
|
|
231
|
+
<p>Failed to load orders: {error.message}</p>
|
|
232
|
+
<button onClick={reset}>Try again</button>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Playwright E2E Test
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// e2e/orders/create-order.spec.ts
|
|
242
|
+
// @trace.verifies=ORD-UC2
|
|
243
|
+
// @trace.test_type=e2e
|
|
244
|
+
import { test, expect } from '@playwright/test';
|
|
245
|
+
import { loginAs } from '../fixtures/auth';
|
|
246
|
+
|
|
247
|
+
test.describe('Create Order', () => {
|
|
248
|
+
test.use({ storageState: 'e2e/.auth/customer.json' }); // reuse auth state
|
|
249
|
+
|
|
250
|
+
test('customer creates an order successfully', async ({ page }) => {
|
|
251
|
+
await page.goto('/orders/new');
|
|
252
|
+
|
|
253
|
+
await page.getByLabel('Product').selectOption({ label: 'Wireless Headphones' });
|
|
254
|
+
await page.getByLabel('Quantity').fill('2');
|
|
255
|
+
await page.getByRole('button', { name: 'Add to Cart' }).click();
|
|
256
|
+
await page.getByRole('button', { name: 'Place Order' }).click();
|
|
257
|
+
|
|
258
|
+
await expect(page).toHaveURL(/\/orders\/\d+/);
|
|
259
|
+
await expect(page.getByText('Order placed successfully')).toBeVisible();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('shows validation error when cart is empty', async ({ page }) => {
|
|
263
|
+
await page.goto('/orders/new');
|
|
264
|
+
await page.getByRole('button', { name: 'Place Order' }).click();
|
|
265
|
+
|
|
266
|
+
await expect(page.getByText('At least one item is required')).toBeVisible();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: "Next.js"
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
description: "Next.js 14+ App Router with Server Components, Server Actions, and React Query"
|
|
4
|
+
language: "TypeScript"
|
|
5
|
+
framework: "Next.js"
|
|
6
|
+
stack_type: "fullstack"
|
|
7
|
+
default_layer_order:
|
|
8
|
+
- Types / interfaces
|
|
9
|
+
- Server Actions (mutations)
|
|
10
|
+
- Data access layer (DAL) — server-side DB/API calls
|
|
11
|
+
- Server Components (data fetching + layout)
|
|
12
|
+
- Client Components (interactive UI)
|
|
13
|
+
- API Routes (for external consumers / webhooks)
|
|
14
|
+
test_framework: "Vitest + React Testing Library + Playwright (E2E)"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
build:
|
|
2
|
+
compile: "npm run build"
|
|
3
|
+
test: "npm test"
|
|
4
|
+
run: "npm run dev"
|
|
5
|
+
lint: "npm run lint"
|
|
6
|
+
e2e: "npx playwright test"
|
|
7
|
+
|
|
8
|
+
architecture:
|
|
9
|
+
style: "App Router — Server Components + Server Actions + Client Components"
|
|
10
|
+
key_rules:
|
|
11
|
+
- "Default to Server Components — only add 'use client' when interactivity is needed"
|
|
12
|
+
- "Data fetching happens in Server Components or Server Actions — never in useEffect"
|
|
13
|
+
- "Server Actions handle all mutations (form submits, data writes)"
|
|
14
|
+
- "Data Access Layer (DAL) abstracts all DB/external API calls — used only on server"
|
|
15
|
+
- "Client Components are 'leaf nodes' — keep them small and focused on interactivity"
|
|
16
|
+
- "API Routes (/api) used only for external consumers or webhooks, not for own UI"
|
|
17
|
+
- "Use Next.js cache() and unstable_cache for data memoization"
|
|
18
|
+
folder_structure: |
|
|
19
|
+
app/
|
|
20
|
+
├── (auth)/ ← route group: auth pages
|
|
21
|
+
├── (dashboard)/ ← route group: authenticated pages
|
|
22
|
+
│ └── orders/
|
|
23
|
+
│ ├── page.tsx ← Server Component (list page)
|
|
24
|
+
│ ├── [id]/
|
|
25
|
+
│ │ └── page.tsx ← Server Component (detail page)
|
|
26
|
+
│ └── _components/ ← feature-scoped Client Components
|
|
27
|
+
├── api/ ← API Routes (webhooks, external consumers)
|
|
28
|
+
└── layout.tsx
|
|
29
|
+
lib/
|
|
30
|
+
├── dal/ ← Data Access Layer (server-only DB/API calls)
|
|
31
|
+
│ └── orders.dal.ts
|
|
32
|
+
├── actions/ ← Server Actions
|
|
33
|
+
│ └── order.actions.ts
|
|
34
|
+
└── validations/ ← Zod schemas (shared server+client)
|
|
35
|
+
└── order.schema.ts
|
|
36
|
+
components/
|
|
37
|
+
└── ui/ ← shared primitive Client Components
|
|
38
|
+
|
|
39
|
+
coding_standards:
|
|
40
|
+
naming:
|
|
41
|
+
server_components: "PascalCase, no suffix — co-locate in app/ route folder"
|
|
42
|
+
client_components: "PascalCase + 'use client' at top — in _components/ folders"
|
|
43
|
+
server_actions: "camelCase verb + noun (e.g., createOrder, cancelOrder) in lib/actions/"
|
|
44
|
+
dal_functions: "camelCase (e.g., getOrderById, listOrdersByCustomer) in lib/dal/"
|
|
45
|
+
files:
|
|
46
|
+
page: "app/{route}/page.tsx"
|
|
47
|
+
layout: "app/{route}/layout.tsx"
|
|
48
|
+
server_action: "lib/actions/{domain}.actions.ts"
|
|
49
|
+
dal: "lib/dal/{domain}.dal.ts"
|
|
50
|
+
schema: "lib/validations/{domain}.schema.ts"
|
|
51
|
+
patterns:
|
|
52
|
+
mutations: "Server Actions with useFormState / useTransition"
|
|
53
|
+
data_fetching: "Server Component async/await + fetch with revalidate options"
|
|
54
|
+
client_state: "Zustand or Context API (avoid for server-derived data)"
|
|
55
|
+
forms: "React Hook Form (client) OR native <form> with Server Actions"
|
|
56
|
+
error_handling: "error.tsx (route-level) + try/catch in Server Actions returning {error}"
|
|
57
|
+
loading: "loading.tsx (Suspense boundary) per route segment"
|
|
58
|
+
auth: "next-auth v5 (Auth.js) or middleware-based session check"
|
|
59
|
+
|
|
60
|
+
testing:
|
|
61
|
+
unit: "Vitest + React Testing Library for Client Components"
|
|
62
|
+
integration: "Vitest + MSW for Server Actions"
|
|
63
|
+
e2e: "Playwright — full browser tests"
|
|
64
|
+
patterns:
|
|
65
|
+
- "Test Server Components by rendering with async render utilities"
|
|
66
|
+
- "Mock fetch/DB calls for Server Action tests"
|
|
67
|
+
- "Playwright tests cover full user flows (auth → action → result)"
|
|
68
|
+
- "Use @playwright/test fixtures for auth state reuse"
|
|
69
|
+
|
|
70
|
+
trace_tags:
|
|
71
|
+
implements: "// @trace.implements={UC-ID}-SC{N}"
|
|
72
|
+
source: "// @trace.source=specs/{domain}/{prd-slug}/bdd/{UC-ID}.feature"
|
|
73
|
+
verifies: "// @trace.verifies={UC-ID}"
|
|
74
|
+
test_type: "// @trace.test_type=unit|e2e"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: "Nuxt"
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
description: "Nuxt 3 with SSR/SSG, Composition API, Pinia, and useFetch"
|
|
4
|
+
language: "TypeScript"
|
|
5
|
+
framework: "Nuxt 3"
|
|
6
|
+
stack_type: "frontend"
|
|
7
|
+
default_layer_order:
|
|
8
|
+
- Types / interfaces
|
|
9
|
+
- Server API routes (server/api/)
|
|
10
|
+
- Composables (useFetch / useAsyncData)
|
|
11
|
+
- State store (Pinia)
|
|
12
|
+
- UI components (presentational)
|
|
13
|
+
- Page components (pages/)
|
|
14
|
+
test_framework: "Vitest + Vue Test Utils + @nuxt/test-utils"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
build:
|
|
2
|
+
compile: "npm run build"
|
|
3
|
+
test: "npm run test"
|
|
4
|
+
run: "npm run dev"
|
|
5
|
+
lint: "npm run lint"
|
|
6
|
+
|
|
7
|
+
architecture:
|
|
8
|
+
style: "Pages + Composables + Server routes (SSR/SSG)"
|
|
9
|
+
key_rules:
|
|
10
|
+
- "Pages in pages/ are route-level containers — no business logic"
|
|
11
|
+
- "Business logic in composables/ using useAsyncData or useFetch"
|
|
12
|
+
- "Server-side logic in server/api/ routes (nitro)"
|
|
13
|
+
- "Client-only state in Pinia stores; server state via useAsyncData"
|
|
14
|
+
- "Shared UI primitives in components/ui/, auto-imported by Nuxt"
|
|
15
|
+
- "Use useRoute/useRouter instead of $route/$router in Composition API"
|
|
16
|
+
- "'use client' equivalent: wrap client-only code in onMounted or <ClientOnly>"
|
|
17
|
+
folder_structure: |
|
|
18
|
+
├── components/
|
|
19
|
+
│ └── ui/ ← auto-imported UI primitives (BaseButton, BaseInput...)
|
|
20
|
+
├── composables/ ← auto-imported composables (useOrderList...)
|
|
21
|
+
├── pages/ ← file-based routing
|
|
22
|
+
├── server/
|
|
23
|
+
│ └── api/ ← nitro server routes (GET /api/orders.ts)
|
|
24
|
+
├── stores/ ← Pinia stores (auto-imported)
|
|
25
|
+
├── plugins/ ← Nuxt plugins
|
|
26
|
+
└── utils/ ← auto-imported utilities
|
|
27
|
+
|
|
28
|
+
coding_standards:
|
|
29
|
+
naming:
|
|
30
|
+
components: "PascalCase (auto-imported, e.g., OrderList, BaseButton)"
|
|
31
|
+
composables: "camelCase with 'use' prefix (auto-imported, e.g., useOrderList)"
|
|
32
|
+
stores: "camelCase + Store suffix via defineStore()"
|
|
33
|
+
server_routes: "method.path.ts (e.g., get.orders.ts or [id].get.ts)"
|
|
34
|
+
files:
|
|
35
|
+
component: "{Feature}.vue"
|
|
36
|
+
composable: "use{Feature}.ts"
|
|
37
|
+
store: "{feature}.store.ts"
|
|
38
|
+
server_route: "{resource}.get.ts / {resource}.post.ts"
|
|
39
|
+
patterns:
|
|
40
|
+
data_fetching: "useAsyncData() for SSR data, useFetch() for client fetches"
|
|
41
|
+
global_state: "Pinia stores"
|
|
42
|
+
forms: "VeeValidate + Zod"
|
|
43
|
+
api_client: "$fetch (ofetch) — built-in to Nuxt, SSR-compatible"
|
|
44
|
+
ssr_hydration: "useState() for shared SSR/client state to avoid hydration mismatch"
|
|
45
|
+
|
|
46
|
+
testing:
|
|
47
|
+
unit: "Vitest + @nuxt/test-utils"
|
|
48
|
+
e2e: "Playwright via @nuxt/test-utils/e2e"
|
|
49
|
+
patterns:
|
|
50
|
+
- "Use mountSuspended() for components that need Nuxt context"
|
|
51
|
+
- "Use registerEndpoint() to mock server routes in tests"
|
|
52
|
+
- "Test pages with renderSuspended() from @nuxt/test-utils"
|
|
53
|
+
|
|
54
|
+
trace_tags:
|
|
55
|
+
implements: "// @trace.implements={UC-ID}-SC{N}"
|
|
56
|
+
source: "// @trace.source=specs/{domain}/{prd-slug}/bdd/{UC-ID}.feature"
|
|
57
|
+
verifies: "// @trace.verifies={UC-ID}"
|
|
58
|
+
test_type: "// @trace.test_type=unit|integration"
|