@zigrivers/scaffold 3.6.0 → 3.8.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.
Files changed (115) hide show
  1. package/README.md +127 -12
  2. package/content/knowledge/backend/backend-api-design.md +103 -0
  3. package/content/knowledge/backend/backend-architecture.md +100 -0
  4. package/content/knowledge/backend/backend-async-patterns.md +101 -0
  5. package/content/knowledge/backend/backend-auth-patterns.md +100 -0
  6. package/content/knowledge/backend/backend-conventions.md +105 -0
  7. package/content/knowledge/backend/backend-data-modeling.md +102 -0
  8. package/content/knowledge/backend/backend-deployment.md +100 -0
  9. package/content/knowledge/backend/backend-dev-environment.md +102 -0
  10. package/content/knowledge/backend/backend-observability.md +102 -0
  11. package/content/knowledge/backend/backend-project-structure.md +100 -0
  12. package/content/knowledge/backend/backend-requirements.md +103 -0
  13. package/content/knowledge/backend/backend-security.md +104 -0
  14. package/content/knowledge/backend/backend-testing.md +101 -0
  15. package/content/knowledge/backend/backend-worker-patterns.md +100 -0
  16. package/content/knowledge/cli/cli-architecture.md +101 -0
  17. package/content/knowledge/cli/cli-conventions.md +117 -0
  18. package/content/knowledge/cli/cli-dev-environment.md +121 -0
  19. package/content/knowledge/cli/cli-distribution-patterns.md +106 -0
  20. package/content/knowledge/cli/cli-interactivity-patterns.md +116 -0
  21. package/content/knowledge/cli/cli-output-patterns.md +107 -0
  22. package/content/knowledge/cli/cli-project-structure.md +124 -0
  23. package/content/knowledge/cli/cli-requirements.md +101 -0
  24. package/content/knowledge/cli/cli-shell-integration.md +130 -0
  25. package/content/knowledge/cli/cli-testing.md +134 -0
  26. package/content/knowledge/library/library-api-design.md +306 -0
  27. package/content/knowledge/library/library-architecture.md +247 -0
  28. package/content/knowledge/library/library-bundling.md +244 -0
  29. package/content/knowledge/library/library-conventions.md +229 -0
  30. package/content/knowledge/library/library-dev-environment.md +220 -0
  31. package/content/knowledge/library/library-documentation.md +300 -0
  32. package/content/knowledge/library/library-project-structure.md +237 -0
  33. package/content/knowledge/library/library-requirements.md +173 -0
  34. package/content/knowledge/library/library-security.md +257 -0
  35. package/content/knowledge/library/library-testing.md +319 -0
  36. package/content/knowledge/library/library-type-definitions.md +284 -0
  37. package/content/knowledge/library/library-versioning.md +300 -0
  38. package/content/knowledge/mobile-app/mobile-app-architecture.md +283 -0
  39. package/content/knowledge/mobile-app/mobile-app-conventions.md +180 -0
  40. package/content/knowledge/mobile-app/mobile-app-deployment.md +298 -0
  41. package/content/knowledge/mobile-app/mobile-app-dev-environment.md +257 -0
  42. package/content/knowledge/mobile-app/mobile-app-distribution.md +264 -0
  43. package/content/knowledge/mobile-app/mobile-app-observability.md +317 -0
  44. package/content/knowledge/mobile-app/mobile-app-offline-patterns.md +311 -0
  45. package/content/knowledge/mobile-app/mobile-app-project-structure.md +245 -0
  46. package/content/knowledge/mobile-app/mobile-app-push-notifications.md +321 -0
  47. package/content/knowledge/mobile-app/mobile-app-requirements.md +147 -0
  48. package/content/knowledge/mobile-app/mobile-app-security.md +338 -0
  49. package/content/knowledge/mobile-app/mobile-app-testing.md +400 -0
  50. package/content/knowledge/web-app/web-app-api-patterns.md +224 -0
  51. package/content/knowledge/web-app/web-app-architecture.md +116 -0
  52. package/content/knowledge/web-app/web-app-auth-patterns.md +256 -0
  53. package/content/knowledge/web-app/web-app-conventions.md +121 -0
  54. package/content/knowledge/web-app/web-app-data-patterns.md +218 -0
  55. package/content/knowledge/web-app/web-app-deployment-workflow.md +143 -0
  56. package/content/knowledge/web-app/web-app-deployment.md +134 -0
  57. package/content/knowledge/web-app/web-app-design-system.md +158 -0
  58. package/content/knowledge/web-app/web-app-dev-environment.md +173 -0
  59. package/content/knowledge/web-app/web-app-observability.md +221 -0
  60. package/content/knowledge/web-app/web-app-project-structure.md +160 -0
  61. package/content/knowledge/web-app/web-app-rendering-strategies.md +133 -0
  62. package/content/knowledge/web-app/web-app-requirements.md +112 -0
  63. package/content/knowledge/web-app/web-app-security.md +193 -0
  64. package/content/knowledge/web-app/web-app-session-patterns.md +214 -0
  65. package/content/knowledge/web-app/web-app-testing.md +249 -0
  66. package/content/knowledge/web-app/web-app-ux-patterns.md +162 -0
  67. package/content/methodology/backend-overlay.yml +73 -0
  68. package/content/methodology/cli-overlay.yml +69 -0
  69. package/content/methodology/library-overlay.yml +67 -0
  70. package/content/methodology/mobile-app-overlay.yml +71 -0
  71. package/content/methodology/web-app-overlay.yml +79 -0
  72. package/dist/cli/commands/init.d.ts +21 -0
  73. package/dist/cli/commands/init.d.ts.map +1 -1
  74. package/dist/cli/commands/init.js +261 -13
  75. package/dist/cli/commands/init.js.map +1 -1
  76. package/dist/cli/commands/init.test.js +206 -0
  77. package/dist/cli/commands/init.test.js.map +1 -1
  78. package/dist/config/schema.d.ts +1392 -64
  79. package/dist/config/schema.d.ts.map +1 -1
  80. package/dist/config/schema.js +82 -5
  81. package/dist/config/schema.js.map +1 -1
  82. package/dist/config/schema.test.js +302 -1
  83. package/dist/config/schema.test.js.map +1 -1
  84. package/dist/core/assembly/overlay-loader.d.ts.map +1 -1
  85. package/dist/core/assembly/overlay-loader.js +2 -1
  86. package/dist/core/assembly/overlay-loader.js.map +1 -1
  87. package/dist/core/assembly/overlay-loader.test.js +56 -0
  88. package/dist/core/assembly/overlay-loader.test.js.map +1 -1
  89. package/dist/e2e/game-pipeline.test.js +1 -0
  90. package/dist/e2e/game-pipeline.test.js.map +1 -1
  91. package/dist/e2e/project-type-overlays.test.d.ts +16 -0
  92. package/dist/e2e/project-type-overlays.test.d.ts.map +1 -0
  93. package/dist/e2e/project-type-overlays.test.js +834 -0
  94. package/dist/e2e/project-type-overlays.test.js.map +1 -0
  95. package/dist/types/config.d.ts +19 -2
  96. package/dist/types/config.d.ts.map +1 -1
  97. package/dist/types/index.d.ts +0 -1
  98. package/dist/types/index.d.ts.map +1 -1
  99. package/dist/types/index.js +0 -1
  100. package/dist/types/index.js.map +1 -1
  101. package/dist/wizard/questions.d.ts +27 -1
  102. package/dist/wizard/questions.d.ts.map +1 -1
  103. package/dist/wizard/questions.js +142 -3
  104. package/dist/wizard/questions.js.map +1 -1
  105. package/dist/wizard/questions.test.js +206 -8
  106. package/dist/wizard/questions.test.js.map +1 -1
  107. package/dist/wizard/wizard.d.ts +21 -0
  108. package/dist/wizard/wizard.d.ts.map +1 -1
  109. package/dist/wizard/wizard.js +27 -1
  110. package/dist/wizard/wizard.js.map +1 -1
  111. package/package.json +1 -1
  112. package/dist/types/wizard.d.ts +0 -14
  113. package/dist/types/wizard.d.ts.map +0 -1
  114. package/dist/types/wizard.js +0 -2
  115. package/dist/types/wizard.js.map +0 -1
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: web-app-architecture
3
+ description: Rendering strategy tradeoffs, CDN edge patterns, hydration strategies, BFF pattern, and micro-frontend considerations for web apps
4
+ topics: [web-app, architecture, ssr, ssg, spa, hydration, bff, micro-frontends, cdn]
5
+ ---
6
+
7
+ Web application architecture is the set of decisions that are expensive to reverse: rendering strategy, client-server boundary, data fetching patterns, and infrastructure topology. These decisions must be made with explicit tradeoff acknowledgment and documented as Architecture Decision Records. The most common architectural mistake is choosing a sophisticated pattern because it is interesting, not because the problem demands it.
8
+
9
+ ## Summary
10
+
11
+ Web app architecture decisions — rendering strategy (CSR, SSG, SSR, ISR, hybrid), CDN edge patterns, hydration strategy, and the BFF pattern — are expensive to reverse. Match each strategy to the use case rather than trends. Most real-world apps benefit from a hybrid approach mixing strategies per route. Document every significant architectural choice as an Architecture Decision Record.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Rendering Strategy Tradeoffs
16
+
17
+ Every rendering strategy has a cost and a benefit profile. Match the strategy to the use case:
18
+
19
+ **CSR (Client-Side Rendering)**
20
+ - All rendering happens in the browser; server delivers a shell HTML + JS bundle
21
+ - Benefits: Simplest server infrastructure (static file hosting), excellent for authenticated apps with no SEO requirement
22
+ - Costs: Poor initial LCP (blank page until JS loads), crawlers may not index content, no server-side data access
23
+ - Use when: Dashboard, admin panel, authenticated tools, single-page apps behind login
24
+
25
+ **SSG (Static Site Generation)**
26
+ - Pages built at deploy time; served as static files from CDN
27
+ - Benefits: Near-zero TTFB, cheapest to serve, CDN-native, best Lighthouse scores
28
+ - Costs: Data is stale until next deploy, build time grows with page count (thousands of pages = slow builds)
29
+ - Use when: Marketing sites, documentation, blogs, any content that changes infrequently
30
+
31
+ **SSR (Server-Side Rendering)**
32
+ - Every request rendered on the server; fresh data every time
33
+ - Benefits: SEO-friendly, always fresh data, works without JavaScript enabled, good LCP on slow connections
34
+ - Costs: Server infrastructure required, cold start latency, harder caching, session management complexity
35
+ - Use when: E-commerce product pages, news sites, any dynamic content that must be SEO-indexed
36
+
37
+ **ISR (Incremental Static Regeneration)**
38
+ - Static generation with time-based revalidation; stale-while-revalidate per route
39
+ - Benefits: CDN-served like SSG, data freshness configurable per route (10 seconds to 24 hours)
40
+ - Costs: Staleness window must be acceptable to the business, first-visitor after expiry pays SSR cost
41
+ - Use when: Product listings, blog index pages, any content where "minutes stale" is acceptable
42
+
43
+ **Hybrid**
44
+ - Mix strategies per route: SSG for marketing pages, SSR for product pages, CSR for dashboard
45
+ - Next.js and Remix enable hybrid out of the box — this is the recommended approach for most real-world apps
46
+
47
+ ### CDN Edge Patterns
48
+
49
+ Deploy static assets and, when possible, entire page renders to CDN edge nodes geographically close to users:
50
+
51
+ - **Edge caching**: Cache SSR responses at the CDN by setting appropriate `Cache-Control` headers. A page with `Cache-Control: s-maxage=60` is served from CDN for 60 seconds before revalidation.
52
+ - **Edge middleware**: Run logic at the CDN edge (Cloudflare Workers, Vercel Edge Functions) for auth redirects, A/B testing, and geolocation routing without hitting the origin server.
53
+ - **CDN-first asset serving**: All static assets (JS bundles, CSS, images) should always be CDN-served with long-lived cache headers (`Cache-Control: immutable, max-age=31536000`) and content-hashed filenames. Frameworks handle this automatically at build time.
54
+
55
+ ### Hydration Strategies
56
+
57
+ Hydration is the process of attaching JavaScript interactivity to server-rendered HTML. It is the primary performance cost of SSR frameworks:
58
+
59
+ - **Full hydration**: The entire component tree hydrates at load. Simplest, but pays full JS parse/execute cost even for static content. Next.js and Remix default behavior.
60
+ - **Progressive hydration**: Hydrate high-priority interactive components first (navigation, above-fold CTAs), defer lower-priority components. Reduces TTI (Time to Interactive) without reducing interactivity.
61
+ - **Selective hydration / Islands architecture**: Only interactive components hydrate; purely static content never runs JS. Implemented by Astro natively; Qwik takes this further with resumability.
62
+ - **React Server Components (RSC)**: Components marked `"server"` never ship to the client. Their rendered output is streamed as a serialized component tree, not HTML. Zero hydration cost for server components.
63
+
64
+ Match hydration strategy to interactivity requirements. A content site with sparse interactive elements benefits significantly from islands architecture. A complex dashboard with no static content benefits from full hydration.
65
+
66
+ ### BFF (Backend for Frontend) Pattern
67
+
68
+ The BFF pattern places a purpose-built server layer between the frontend and backend microservices:
69
+
70
+ - **Problem solved**: Frontend needs data aggregated from 5 microservices to render one page. Directly calling all 5 from the client creates waterfall requests, exposes internal service URLs, and splits auth concerns.
71
+ - **BFF solution**: The frontend calls one BFF endpoint. The BFF aggregates service responses, transforms data to match the UI's needs, handles auth tokens, and returns a single optimized response.
72
+ - **Implementation**: Next.js API routes or Remix loader functions are natural BFF layers. For separate backend services: a Node.js/Express/Fastify service that the frontend treats as its own API.
73
+
74
+ Do not build a BFF that becomes an unowned monolith. The BFF is owned by the frontend team and changes with the frontend's needs.
75
+
76
+ ### Architecture Decision Record Template
77
+
78
+ Document every significant architectural choice:
79
+
80
+ ```markdown
81
+ # ADR-001: Rendering Strategy Selection
82
+
83
+ ## Status
84
+ Accepted — 2024-01-15
85
+
86
+ ## Context
87
+ Marketing site (40 pages, infrequent content updates) plus authenticated dashboard.
88
+
89
+ ## Decision
90
+ - Marketing pages: SSG via Next.js; rebuild on content change via webhook
91
+ - Dashboard: CSR with React Query; no SEO requirement
92
+
93
+ ## Consequences
94
+ - Marketing pages: near-zero TTFB, excellent SEO, requires rebuild pipeline
95
+ - Dashboard: full JS bundle on load; auth gate prevents SEO concerns
96
+ - Hybrid in one codebase (Next.js handles both routing strategies)
97
+ ```
98
+
99
+ ### Micro-Frontend Considerations
100
+
101
+ Micro-frontends split one frontend monolith into independently deployable pieces, each owned by a different team. Adopt only when the organizational cost exceeds the technical cost:
102
+
103
+ - **When justified**: 50+ frontend engineers across 10+ teams, independent release cadences are blocked by coordination overhead, different teams own completely separate product domains
104
+ - **When NOT justified**: Under 20 engineers, single team owns the frontend, coordination overhead is manageable
105
+ - **Implementation options**: Module Federation (webpack), iframe composition (isolation but poor UX), server-side composition (ESI, Nginx include), link-based navigation (least coupling, least shared state)
106
+
107
+ Most teams that adopt micro-frontends at 10 engineers regret it. Prefer well-structured monorepos with internal package boundaries first.
108
+
109
+ ### Streaming SSR
110
+
111
+ React 18+ enables streaming HTML responses: send the page shell immediately, stream in component subtrees as their data resolves. This dramatically improves TTFB perception and LCP:
112
+
113
+ - Critical path renders immediately; slow data sources do not block the initial HTML flush
114
+ - Implement with React's `<Suspense>` boundaries and `renderToPipeableStream`
115
+ - Next.js App Router uses streaming by default; wrap slow data-fetching components in `<Suspense>`
116
+ - Monitor streaming behavior in production — if a slow database query is not wrapped in Suspense, it blocks the entire response stream
@@ -0,0 +1,256 @@
1
+ ---
2
+ name: web-app-auth-patterns
3
+ description: OAuth 2.0 + PKCE flows, cookie security, passkey/WebAuthn, social login, CSRF protection, and auth state management
4
+ topics: [web-app, auth, oauth, pkce, webauthn, passkeys, csrf, security]
5
+ ---
6
+
7
+ Authentication in web applications is a deep domain where implementation mistakes have severe security consequences. The auth surface spans the browser, the server, and third-party identity providers — and each boundary has its own threat model. OAuth 2.0 with PKCE is now the standard for delegated authorization; WebAuthn/passkeys are rapidly becoming the standard for credential-free authentication; and session cookie security attributes are the baseline that every web app must get right. Skipping any of these correctly has historically led to breaches.
8
+
9
+ ## Summary
10
+
11
+ ### OAuth 2.0 + PKCE Flow
12
+
13
+ OAuth 2.0 Authorization Code flow with PKCE (Proof Key for Code Exchange) is the correct flow for user-facing web apps. It prevents authorization code interception attacks that plagued the original Authorization Code flow.
14
+
15
+ **PKCE flow:**
16
+ 1. Client generates a cryptographically random `code_verifier` (43–128 chars)
17
+ 2. Client derives `code_challenge = BASE64URL(SHA256(code_verifier))`
18
+ 3. Client redirects user to authorization server with `code_challenge` and `code_challenge_method=S256`
19
+ 4. User authenticates with the identity provider
20
+ 5. Authorization server redirects back with `code` in the query string
21
+ 6. Client exchanges `code` + `code_verifier` for tokens — the server verifies the challenge hash
22
+ 7. Without the original `code_verifier`, a stolen `code` is useless
23
+
24
+ Never store the `code_verifier` in `localStorage` — use `sessionStorage` or in-memory. The code exchange must happen from your backend (BFF pattern) to avoid exposing `client_secret` in browser code.
25
+
26
+ ### Cookie Security Attributes
27
+
28
+ Every session cookie and auth cookie must have all four attributes correctly configured:
29
+
30
+ - **HttpOnly** — prevents JavaScript access, blocking XSS-based token theft
31
+ - **Secure** — restricts transmission to HTTPS, preventing network interception
32
+ - **SameSite=Strict** — cookie not sent on any cross-site request, preventing CSRF
33
+ - **SameSite=Lax** — cookie sent on top-level navigations only; use when cross-site POST is never needed but external links should work
34
+ - **`__Host-` prefix** — enforces Secure + path=/ + no Domain; prevents subdomain hijacking
35
+
36
+ Use `SameSite=Strict` for session cookies. If your app needs to accept inbound cross-site navigation with the user's session (e.g., linking from a third-party site into an authenticated page), use `Lax`.
37
+
38
+ ### Passkey / WebAuthn Implementation
39
+
40
+ WebAuthn is the W3C standard for hardware-backed authentication. Passkeys are synced WebAuthn credentials — the user registers once and the credential is available on all their devices via iCloud Keychain or Google Password Manager.
41
+
42
+ **Why passkeys matter:**
43
+ - No password to phish, leak, or forget
44
+ - Phishing-resistant by design — the credential is bound to the origin URL
45
+ - Biometric authentication without biometric data leaving the device
46
+ - Major platform support as of 2023 (iOS 16+, macOS Ventura+, Android 9+, Windows 11)
47
+
48
+ **Registration flow:** `navigator.credentials.create()` with a challenge from your server → user authenticates with biometric/PIN → store the public key and credential ID on your server.
49
+
50
+ **Authentication flow:** `navigator.credentials.get()` with a challenge → WebAuthn assertion → verify signature on server using stored public key.
51
+
52
+ ### Social Login Integration
53
+
54
+ Social login (Google, GitHub, Apple, etc.) delegates authentication to a trusted identity provider. The implementation pattern:
55
+
56
+ 1. Redirect to provider OAuth endpoint (with PKCE)
57
+ 2. Provider redirects back with `code`
58
+ 3. Backend exchanges `code` for `id_token` + `access_token`
59
+ 4. Verify `id_token` signature against provider's public keys (JWKS endpoint)
60
+ 5. Extract user identity (`sub`, `email`, `name`) from verified token
61
+ 6. Look up or create user record; issue your app's session
62
+
63
+ **Account linking:** The same email across different providers should map to the same user account. Store `provider:sub` pairs linked to a single user record to handle this.
64
+
65
+ ## Deep Guidance
66
+
67
+ ### PKCE Implementation
68
+
69
+ ```typescript
70
+ // PKCE utilities — run in browser before redirect
71
+ async function generatePKCE() {
72
+ // Generate cryptographically random code_verifier
73
+ const codeVerifier = base64URLEncode(crypto.getRandomValues(new Uint8Array(32)));
74
+
75
+ // Derive code_challenge via SHA-256
76
+ const codeChallenge = base64URLEncode(
77
+ new Uint8Array(
78
+ await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier))
79
+ )
80
+ );
81
+
82
+ return { codeVerifier, codeChallenge };
83
+ }
84
+
85
+ function base64URLEncode(buffer: Uint8Array): string {
86
+ return btoa(String.fromCharCode(...buffer))
87
+ .replace(/\+/g, '-')
88
+ .replace(/\//g, '_')
89
+ .replace(/=/g, '');
90
+ }
91
+
92
+ // Initiate login
93
+ async function initiateOAuthLogin(provider: string) {
94
+ const { codeVerifier, codeChallenge } = await generatePKCE();
95
+ const state = base64URLEncode(crypto.getRandomValues(new Uint8Array(16)));
96
+
97
+ // Store verifier and state — sessionStorage, never localStorage
98
+ sessionStorage.setItem('pkce_verifier', codeVerifier);
99
+ sessionStorage.setItem('oauth_state', state);
100
+
101
+ const params = new URLSearchParams({
102
+ client_id: process.env.NEXT_PUBLIC_OAUTH_CLIENT_ID!,
103
+ redirect_uri: `${window.location.origin}/auth/callback`,
104
+ response_type: 'code',
105
+ scope: 'openid email profile',
106
+ code_challenge: codeChallenge,
107
+ code_challenge_method: 'S256',
108
+ state,
109
+ });
110
+
111
+ window.location.href = `${OAUTH_AUTHORIZATION_ENDPOINT}?${params}`;
112
+ }
113
+
114
+ // Handle callback
115
+ async function handleOAuthCallback(searchParams: URLSearchParams) {
116
+ const code = searchParams.get('code');
117
+ const returnedState = searchParams.get('state');
118
+
119
+ // Verify state to prevent CSRF
120
+ const savedState = sessionStorage.getItem('oauth_state');
121
+ if (!savedState || returnedState !== savedState) {
122
+ throw new Error('State mismatch — possible CSRF attack');
123
+ }
124
+
125
+ const codeVerifier = sessionStorage.getItem('pkce_verifier');
126
+ sessionStorage.removeItem('pkce_verifier');
127
+ sessionStorage.removeItem('oauth_state');
128
+
129
+ // Exchange code + verifier for tokens (via your backend)
130
+ return fetch('/api/auth/callback', {
131
+ method: 'POST',
132
+ body: JSON.stringify({ code, codeVerifier }),
133
+ headers: { 'Content-Type': 'application/json' },
134
+ });
135
+ }
136
+ ```
137
+
138
+ ### WebAuthn Passkey Registration
139
+
140
+ ```typescript
141
+ // Register a new passkey
142
+ async function registerPasskey(userId: string, username: string) {
143
+ // 1. Get challenge from server
144
+ const { challenge, rpId } = await api.getRegistrationChallenge();
145
+
146
+ // 2. Create credential
147
+ const credential = await navigator.credentials.create({
148
+ publicKey: {
149
+ challenge: base64URLDecode(challenge),
150
+ rp: { name: 'My App', id: rpId },
151
+ user: {
152
+ id: new TextEncoder().encode(userId),
153
+ name: username,
154
+ displayName: username,
155
+ },
156
+ pubKeyCredParams: [
157
+ { alg: -7, type: 'public-key' }, // ES256 (most supported)
158
+ { alg: -257, type: 'public-key' }, // RS256 (Windows Hello fallback)
159
+ ],
160
+ authenticatorSelection: {
161
+ residentKey: 'required', // Required for passkeys
162
+ userVerification: 'required', // Biometric/PIN required
163
+ },
164
+ attestation: 'none', // No attestation for consumer apps
165
+ },
166
+ }) as PublicKeyCredential;
167
+
168
+ // 3. Send response to server for verification and storage
169
+ const response = credential.response as AuthenticatorAttestationResponse;
170
+ return api.completeRegistration({
171
+ credentialId: base64URLEncode(credential.rawId),
172
+ clientDataJSON: base64URLEncode(response.clientDataJSON),
173
+ attestationObject: base64URLEncode(response.attestationObject),
174
+ });
175
+ }
176
+ ```
177
+
178
+ Use the `@simplewebauthn/server` library on the backend for verification. It handles the complex binary parsing, signature verification, and CBOR decoding correctly.
179
+
180
+ ### CSRF Protection
181
+
182
+ With `SameSite=Strict` cookies, CSRF is largely mitigated for standard same-origin flows. However, for APIs that accept `application/json` content type (which browsers can't trigger via forms or `<img>` tags), the risk is already limited.
183
+
184
+ For applications that cannot use `SameSite=Strict` (e.g., cross-site embedded auth flows):
185
+
186
+ ```typescript
187
+ // Double-submit cookie pattern
188
+ // Server sets a CSRF token in a readable cookie (not HttpOnly)
189
+ // Client reads the cookie and sends it as a request header
190
+ // Server validates that header matches cookie value
191
+
192
+ // Server: set CSRF cookie on login
193
+ res.cookie('csrf-token', generateCSRFToken(), {
194
+ httpOnly: false, // Must be readable by JavaScript
195
+ secure: true,
196
+ sameSite: 'lax',
197
+ });
198
+
199
+ // Client: inject header on every mutating request
200
+ apiClient.defaults.headers.common['X-CSRF-Token'] = getCookie('csrf-token');
201
+
202
+ // Server: validate on every mutation
203
+ app.use((req, res, next) => {
204
+ if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
205
+ if (req.headers['x-csrf-token'] !== req.cookies['csrf-token']) {
206
+ return res.status(403).json({ error: 'CSRF token mismatch' });
207
+ }
208
+ }
209
+ next();
210
+ });
211
+ ```
212
+
213
+ ### Auth State Management in React
214
+
215
+ ```typescript
216
+ // Auth context — single source of truth for authentication state
217
+ interface AuthState {
218
+ user: User | null;
219
+ isLoading: boolean;
220
+ isAuthenticated: boolean;
221
+ }
222
+
223
+ // Use React Query to manage auth state (benefits from cache, refetch on focus)
224
+ export function useAuth() {
225
+ const { data: user, isLoading } = useQuery({
226
+ queryKey: ['auth', 'me'],
227
+ queryFn: () => api.getCurrentUser(),
228
+ retry: false, // Don't retry 401s
229
+ staleTime: Infinity, // Only refetch manually or on window focus
230
+ });
231
+
232
+ return {
233
+ user: user ?? null,
234
+ isLoading,
235
+ isAuthenticated: !!user,
236
+ };
237
+ }
238
+
239
+ // Route protection
240
+ function ProtectedRoute({ children }: { children: React.ReactNode }) {
241
+ const { isAuthenticated, isLoading } = useAuth();
242
+ const router = useRouter();
243
+
244
+ useEffect(() => {
245
+ if (!isLoading && !isAuthenticated) {
246
+ router.replace(`/login?returnTo=${encodeURIComponent(router.asPath)}`);
247
+ }
248
+ }, [isAuthenticated, isLoading]);
249
+
250
+ if (isLoading) return <PageSpinner />;
251
+ if (!isAuthenticated) return null;
252
+ return <>{children}</>;
253
+ }
254
+ ```
255
+
256
+ Store the return URL before redirecting to login so users land on the page they were trying to reach after authentication.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: web-app-conventions
3
+ description: Component naming, file colocation, state management patterns, custom hook conventions, and CSS methodology selection for web apps
4
+ topics: [web-app, conventions, components, state-management, css, hooks]
5
+ ---
6
+
7
+ Conventions are the difference between a codebase where any engineer can find and modify any file in minutes versus one where only the original author can navigate it confidently. Establish these once, enforce them via linting and code review, and never let individual preferences override them during the project lifecycle.
8
+
9
+ ## Summary
10
+
11
+ Web app conventions establish consistent patterns for component naming (PascalCase, domain-specific), file colocation (feature-based over type-based), state management (local, server, global, URL tiers), custom hook conventions, and CSS methodology selection. Choose one approach per area and enforce it via linting.
12
+
13
+ ## Deep Guidance
14
+
15
+ ### Component Naming
16
+
17
+ - **PascalCase for components**: `UserProfile`, `NavigationMenu`, `CheckoutSummary`. This is universal across React, Vue (single-file components), and Svelte.
18
+ - **Descriptive, domain-specific names**: `ProductCard` not `Card`, `AuthModal` not `Modal`, `OrderStatusBadge` not `Badge`. Generic names cause naming conflicts as the codebase grows.
19
+ - **Suffix by type where ambiguous**: `UserList` (renders list), `UserListItem` (renders one item), `UserListContainer` (fetches data and passes to list). Be consistent — never mix `Container`/`Provider`/`Wrapper` conventions across the codebase.
20
+ - **Page components**: `LoginPage`, `DashboardPage`, `CheckoutPage` — suffix with `Page` to distinguish from reusable components.
21
+
22
+ ### File Colocation
23
+
24
+ Colocate files that change together. The industry has converged on feature-based colocation over type-based grouping:
25
+
26
+ **Preferred (feature-colocated):**
27
+ ```
28
+ features/
29
+ user-profile/
30
+ UserProfile.tsx
31
+ UserProfile.test.tsx
32
+ UserProfile.stories.tsx # Storybook stories
33
+ useUserProfile.ts # Feature-specific hook
34
+ user-profile.types.ts # Feature-specific types
35
+ index.ts # Barrel export
36
+ ```
37
+
38
+ **Avoid (type-segregated):**
39
+ ```
40
+ components/UserProfile.tsx
41
+ hooks/useUserProfile.ts
42
+ types/userProfile.ts
43
+ tests/UserProfile.test.tsx
44
+ ```
45
+
46
+ The type-segregated structure requires navigating four directories to understand one feature. It also means deleting a feature requires hunting across the entire directory tree.
47
+
48
+ **Exception**: Truly shared utilities (`lib/`, `utils/`, `hooks/`) that have no single feature owner belong in a flat shared directory, not inside any feature.
49
+
50
+ ### State Management Patterns
51
+
52
+ Choose the state tier that matches the problem. Do not reach for Redux when `useState` is sufficient:
53
+
54
+ - **Local state (`useState`, `useReducer`)**: UI-only state that no sibling or parent needs (open/closed, form draft, hover state). Keep it local.
55
+ - **Server state (React Query, SWR, RTK Query)**: Data fetched from an API. Use a server-state library — it handles caching, deduplication, background refresh, and error states for free. Do not put API data in a global Redux store unless you have a compelling reason.
56
+ - **Global client state (Zustand, Jotai, Redux Toolkit)**: Shared UI state that multiple distant components need (authenticated user, theme, cart items, notification queue). Use sparingly. Every piece of global state is a coupling point.
57
+ - **URL state**: Sort order, filters, pagination, tab selection — anything that should survive a page reload or be shareable via link. Use `useSearchParams` or a URL state library. This is underused; most "global state" is actually URL state in disguise.
58
+
59
+ ### Custom Hook Conventions
60
+
61
+ - Name all custom hooks with the `use` prefix: `useAuth`, `usePagination`, `useDebounce`.
62
+ - Single responsibility: one hook does one thing. `useUserProfile` fetches and returns user data. It does not also handle form state.
63
+ - Return stable references: memoize returned objects and callbacks to prevent unnecessary re-renders in consumers.
64
+ - Keep hooks pure from the component's perspective: no side effects that the hook consumer cannot control. Accept options objects for configuration rather than hardcoding behavior.
65
+ - Co-locate the hook with the feature that owns it. Move to `lib/hooks/` only when reused across three or more features.
66
+
67
+ ### CSS Methodology Selection
68
+
69
+ Choose one methodology and enforce it. Mixing methodologies creates chaos:
70
+
71
+ - **Tailwind CSS**: Best for teams that want to move fast and avoid naming bikeshedding. Excellent with design tokens. Downsides: verbose JSX, requires purging to avoid large CSS bundles, learning curve for custom designs.
72
+ - **CSS Modules**: Best for teams that prefer semantic class names and encapsulation without a utility framework. No runtime, good TypeScript support via `typed-css-modules`.
73
+ - **CSS-in-JS (styled-components, Emotion)**: Best for highly dynamic styles or design systems with programmatic theming. Downsides: runtime cost, hydration complexity with SSR.
74
+ - **Vanilla CSS with custom properties**: Best for simple apps or teams who want zero abstraction. Use a consistent BEM-like naming convention.
75
+
76
+ ### Enforcing Conventions with Tooling
77
+
78
+ Conventions without enforcement degrade immediately. Configure linting to automate the common cases:
79
+
80
+ ```json
81
+ // ESLint rules for React component conventions
82
+ {
83
+ "rules": {
84
+ "react/jsx-pascal-case": "error", // Enforce PascalCase for JSX components
85
+ "import/no-default-export": "warn", // Prefer named exports (easier to find with search)
86
+ "@typescript-eslint/naming-convention": [
87
+ "error",
88
+ { "selector": "function", "format": ["camelCase", "PascalCase"] },
89
+ { "selector": "variable", "format": ["camelCase", "UPPER_CASE", "PascalCase"] }
90
+ ]
91
+ }
92
+ }
93
+ ```
94
+
95
+ Add a Storybook or component documentation requirement: every shared component must have a story before it can be merged. This enforces composability — if you cannot write a story for it in isolation, the component is too coupled.
96
+
97
+ ### State Management Decision Flowchart
98
+
99
+ When choosing state location, answer in order:
100
+
101
+ 1. Is this state only used by one component? → `useState`
102
+ 2. Is this state fetched from a server? → React Query / SWR
103
+ 3. Can this state live in the URL? → `useSearchParams` / URL state
104
+ 4. Is this state shared across multiple distant routes? → Zustand / Jotai / Redux Toolkit
105
+ 5. Is this state needed server-side during SSR? → Context with SSR-safe initialization
106
+
107
+ Resist the urge to centralize all state globally. Distributed state is easier to delete when features are removed and easier to reason about during debugging.
108
+
109
+ ### Hook Testing Conventions
110
+
111
+ Test hooks in isolation using `renderHook` from React Testing Library. This keeps hook logic testable without mounting a component:
112
+
113
+ ```typescript
114
+ // Good: test hook behavior directly
115
+ const { result } = renderHook(() => useDebounce("search", 300));
116
+ expect(result.current).toBe("");
117
+ act(() => { jest.advanceTimersByTime(300); });
118
+ expect(result.current).toBe("search");
119
+ ```
120
+
121
+ Never test a hook only via its parent component — that couples the hook test to the component's rendering and makes failures harder to diagnose.