@mr.dj2u/knowledge 0.1.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 (83) hide show
  1. package/dist/content/checklists/ship-test-loop.md +16 -0
  2. package/dist/content/checklists/unified-agent-bundle-validation.md +8 -0
  3. package/dist/content/examples/ship-test-loop.md +13 -0
  4. package/dist/content/examples/unified-agent-bundle-bootstrap.md +8 -0
  5. package/dist/content/guides/animation-performance.md +30 -0
  6. package/dist/content/guides/post-create-onboarding.md +113 -0
  7. package/dist/content/patterns/api/api-routes.md +314 -0
  8. package/dist/content/patterns/api/error-handling.md +311 -0
  9. package/dist/content/patterns/database/drizzle-schema.md +280 -0
  10. package/dist/content/patterns/database/migrations.md +365 -0
  11. package/dist/content/patterns/database/query-organization.md +537 -0
  12. package/dist/content/patterns/database/relations.md +450 -0
  13. package/dist/content/patterns/deployment/build-configuration.md +452 -0
  14. package/dist/content/patterns/deployment/ci-cd-patterns.md +448 -0
  15. package/dist/content/patterns/deployment/environment-config.md +380 -0
  16. package/dist/content/patterns/deployment/hosting-setup.md +425 -0
  17. package/dist/content/patterns/project/configuration-patterns.md +459 -0
  18. package/dist/content/patterns/project/documentation-org.md +506 -0
  19. package/dist/content/patterns/project/folder-structure.md +398 -0
  20. package/dist/content/patterns/project/library-exports.md +465 -0
  21. package/dist/content/patterns/project/monorepo-structure.md +500 -0
  22. package/dist/content/patterns/routing/dynamic-routes.md +221 -0
  23. package/dist/content/patterns/routing/file-based-routing.md +186 -0
  24. package/dist/content/patterns/routing/route-groups.md +429 -0
  25. package/dist/content/patterns/state/persistence-middleware.md +521 -0
  26. package/dist/content/patterns/state/selector-hooks.md +538 -0
  27. package/dist/content/patterns/state/store-organization.md +539 -0
  28. package/dist/content/patterns/state/zustand-patterns.md +348 -0
  29. package/dist/content/patterns/styling/component-styling.md +468 -0
  30. package/dist/content/patterns/styling/responsive-patterns.md +398 -0
  31. package/dist/content/patterns/styling/theme-configuration.md +426 -0
  32. package/dist/content/patterns/styling/uniwind-setup.md +412 -0
  33. package/dist/content/prompts/continue-development.md +27 -0
  34. package/dist/content/prompts/create-expo-super-stack.md +29 -0
  35. package/dist/content/prompts/fix-seo.md +29 -0
  36. package/dist/content/prompts/onboard-new-expo-app.md +11 -0
  37. package/dist/content/prompts/prepare-deploy.md +29 -0
  38. package/dist/content/prompts/project-research-plan.md +29 -0
  39. package/dist/content/prompts/review-expo-project.md +29 -0
  40. package/dist/content/prompts/run-doctor.md +30 -0
  41. package/dist/content/prompts/ship-test-loop.md +24 -0
  42. package/dist/content/reference/create-expo-stack-uniwind.md +29 -0
  43. package/dist/content/reference/doctor-dogfood.md +42 -0
  44. package/dist/content/reference/mcp-sdk-transport.md +30 -0
  45. package/dist/content/reference/package-ci-patterns.md +24 -0
  46. package/dist/content/reference/reference-repo-evacuation.md +31 -0
  47. package/dist/content/resource-index.json +67 -0
  48. package/dist/content/rules/app-folder-architecture.md +13 -0
  49. package/dist/content/rules/env-hygiene.md +9 -0
  50. package/dist/content/rules/seo-metadata.md +7 -0
  51. package/dist/content/rules/ssr-safety.md +9 -0
  52. package/dist/content/skills/api-routes.md +33 -0
  53. package/dist/content/skills/continue-development.md +31 -0
  54. package/dist/content/skills/debugging.md +31 -0
  55. package/dist/content/skills/deployment.md +32 -0
  56. package/dist/content/skills/dev-server-management.md +31 -0
  57. package/dist/content/skills/env-vars.md +32 -0
  58. package/dist/content/skills/expo-router-architecture.md +33 -0
  59. package/dist/content/skills/expo-ssr-safety.md +32 -0
  60. package/dist/content/skills/plugin-creation.md +41 -0
  61. package/dist/content/skills/production-server-patterns.md +31 -0
  62. package/dist/content/skills/project-onboarding.md +31 -0
  63. package/dist/content/skills/research-plan-intake.md +31 -0
  64. package/dist/content/skills/seo-metadata.md +31 -0
  65. package/dist/content/skills/super-stack-startup.md +31 -0
  66. package/dist/content/skills/uniwind-theming.md +32 -0
  67. package/dist/index.d.ts +46 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +369 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/patterns/index.d.ts +78 -0
  72. package/dist/patterns/index.d.ts.map +1 -0
  73. package/dist/patterns/index.js +264 -0
  74. package/dist/patterns/index.js.map +1 -0
  75. package/dist/prompts/index.d.ts +35 -0
  76. package/dist/prompts/index.d.ts.map +1 -0
  77. package/dist/prompts/index.js +270 -0
  78. package/dist/prompts/index.js.map +1 -0
  79. package/dist/skills/index.d.ts +3 -0
  80. package/dist/skills/index.d.ts.map +1 -0
  81. package/dist/skills/index.js +2 -0
  82. package/dist/skills/index.js.map +1 -0
  83. package/package.json +50 -0
@@ -0,0 +1,16 @@
1
+ # Ship-Test Loop Checklist
2
+
3
+ Use this checklist when running the PR loop into `test`.
4
+
5
+ 1. Doctor CI pass: `mds doctor --ci`.
6
+ 2. Stage intentional files only.
7
+ 3. Create meaningful commit message.
8
+ 4. Push branch.
9
+ 5. Open/update PR to `test`.
10
+ 6. Wait ~2 minutes before polling checks/comments.
11
+ 7. Collect failed checks + review comments.
12
+ 8. Fix issues locally.
13
+ 9. Rerun Doctor CI.
14
+ 10. Push and poll again.
15
+ 11. Repeat up to 5 cycles.
16
+ 12. Merge only when checks are green and blockers are cleared.
@@ -0,0 +1,8 @@
1
+ # Unified Agent Bundle Validation Checklist
2
+
3
+ Phase 9 validation artifact intent:
4
+
5
+ 1. Run Doctor (`--ci`) through the agent.
6
+ 2. Fetch one guide via MCP resource (`animation-performance`).
7
+ 3. Execute one CLI workflow (`mds continue` or `/ship-test-loop`).
8
+ 4. Confirm tools/resources are visible in the target client.
@@ -0,0 +1,13 @@
1
+ # Ship-Test Loop Example
2
+
3
+ Example iteration log for `/ship-test-loop`:
4
+
5
+ - Iteration 1:
6
+ - Failed: `lint` and one requested review change.
7
+ - Fixes: remove dead import, update API guard clause.
8
+ - Result: `lint` green, one test still failing.
9
+ - Iteration 2:
10
+ - Failed: `test` in `packages/cli`.
11
+ - Fixes: update stale skill ID expectation.
12
+ - Result: all checks green.
13
+ - Merge: PR merged into `test`.
@@ -0,0 +1,8 @@
1
+ # Unified Agent Bundle Bootstrap Example
2
+
3
+ Phase 9 bootstrap artifact intent:
4
+
5
+ 1. Install MCP server config.
6
+ 2. Install generated skills/prompts package.
7
+ 3. Verify the agent can call Doctor + knowledge tools.
8
+ 4. Verify the agent can run one CLI workflow.
@@ -0,0 +1,30 @@
1
+ # Animation Performance Guide
2
+
3
+ Source: Expo blog, "The real cost of React Native animations: benchmarking
4
+ every approach" by App & Flow:
5
+ https://expo.dev/blog/the-real-cost-of-react-native-animations-benchmarking-every-approach
6
+
7
+ ## Practical Guidance
8
+
9
+ - Measure animation cost in release builds before changing libraries. Debug
10
+ builds can exaggerate overhead, especially for Reanimated.
11
+ - Library choice matters most for long-running animations, large animated lists,
12
+ low-end Android devices, and screens with many simultaneously animated views.
13
+ - Library choice matters less for short one-shot transitions where animation
14
+ work ends quickly and the component count is low.
15
+ - Prefer native/platform-driven animation paths for large repeated animations
16
+ when the interaction does not require gesture coupling or complex shared
17
+ values.
18
+ - Keep Reanimated for gesture-driven interactions, layout-sensitive motion, and
19
+ complex UI-thread coordination.
20
+ - Animated view count is a budget. A single animated card is different from 100
21
+ animated rows.
22
+
23
+ ## Doctor And Onboarding Rules
24
+
25
+ - Warn when route or list files animate many repeated items without an explicit
26
+ performance note.
27
+ - Warn when benchmark claims are based only on debug builds.
28
+ - Ask during onboarding whether the app has long-running, list-heavy, or
29
+ gesture-heavy animation requirements.
30
+
@@ -0,0 +1,113 @@
1
+ # Post-Create Expo Onboarding
2
+
3
+ MDS onboarding runs after `rn-new`, `create-expo-app`, `create-expo-stack`, or
4
+ another generator has created an Expo project. `create-expo-super-stack` wraps
5
+ that same idea by running `create-expo-stack` first and then applying the MDS
6
+ onboarding pass.
7
+
8
+ ## Flow
9
+
10
+ 1. Detect package manager, Expo SDK, Expo Router, app directory, aliases, and
11
+ styling stack.
12
+ 2. Explain project memory first, then ask what the app is for, who it serves,
13
+ primary flows, data needs, and release/deployment intent.
14
+ 3. Use friendly Clack prompts with visible defaults, helpful explanations, and
15
+ no blank required answers.
16
+ 4. Derive defaults from selected answers instead of asking for comma-separated
17
+ internal keywords.
18
+ 5. Offer rich boilerplate by default: project memory docs, exposition pages,
19
+ Uniwind repair for existing apps, Software Mansion core examples, Supabase or
20
+ local data guidance, MCP/Codex/Claude instructions, and CI/release safeguards.
21
+ 6. Scaffold only selected pieces.
22
+ 7. Add generated cleanup scripts such as `clear-expo-start` and `free-port` (with `kill-port` compatibility alias).
23
+ 8. Install added dependencies, run `expo install --fix`, install known missing
24
+ Expo peers such as `expo-font` for `@expo/vector-icons`, then run Doctor.
25
+
26
+ ## Agentic Onboarding (MCP)
27
+
28
+ The terminal `mds onboard` command writes the project memory files. The
29
+ agentic version is a conversation with Claude Code, Codex, or Cursor that
30
+ fills those files in collaboratively.
31
+
32
+ Install **once, globally** (user scope, default) so every workspace gets
33
+ the prompts:
34
+
35
+ ```bash
36
+ mds mcp install --client claude # merges into ~/.claude.json
37
+ mds mcp install --client codex # merges into ~/.codex/config.toml
38
+ mds mcp install --client cursor # merges into ~/.cursor/mcp.json
39
+ mds mcp install --dry-run # preview merge before writing
40
+ ```
41
+
42
+ Restart the host (or run `claude mcp reload`) and the MDS prompts are
43
+ available from any workspace.
44
+
45
+ Two prompts ship with the server:
46
+
47
+ - `create_expo_super_stack` — invoke from a **parent folder** (e.g.
48
+ `F:\ReactNativeApps`) when the app folder does not exist yet. The
49
+ agent confirms platforms, styling, data start, etc., then runs
50
+ `create-expo-super-stack`, verifies the generated app folder, and
51
+ offers to resolve TodoForContext markers.
52
+ - `onboard_new_expo_app` — invoke from **inside an existing Expo app
53
+ folder** (a freshly generated one or a year-old project). Runs the
54
+ intake → normalize → plan → scaffold flow.
55
+
56
+ After generation, the user-dev should open the generated app folder
57
+ directly in a new agent session and run `mds continue`. That fresh
58
+ app-root session reduces token usage and saves money because future
59
+ searches, reads, and plans are scoped to the app instead of the parent
60
+ folder and old generator conversation.
61
+
62
+ Both prompts enforce or surface a **TodoForContext blocker** before
63
+ implementation work: they scan every `project/` file for the literal
64
+ marker `# TodoForContext(optional):` and ask the user to fill it in or
65
+ delete the marker line.
66
+
67
+ `mds doctor` mirrors this rule with a `todo-for-context markers`
68
+ error so the same blocker stops CI and editor surfaces until the user
69
+ fills the section or deletes the marker line.
70
+
71
+ ### What's new in the agentic prompts
72
+
73
+ - **PHASE 0 file intake.** The agent offers to digest an existing
74
+ `project/info.md` and `project/style.md` if the user has them already.
75
+ Questions whose answers are unambiguous in the file are skipped; the
76
+ agent asks for clarification only when the file is silent or unclear.
77
+ A reference template URL is mentioned in the prompt; the agent can
78
+ also inline the template on request.
79
+ - **Android TV alongside Apple TV.** The platform multi-select now
80
+ includes Android TV. Android TV builds from the same Android target
81
+ with leanback config in `app.json`; Apple TV is a separate tvOS build
82
+ target via `react-native-tvos`. Selecting either records the intent
83
+ in project memory.
84
+ - **Embedded flag map.** Both prompts now include the exact
85
+ create-expo-stack and `--mds-*` flag map so the agent does not have
86
+ to grep `node_modules` or scan source to translate answers into a
87
+ command. This was the biggest source of slowness in early test runs.
88
+ - **Credits while waiting.** When generation kicks off, the agent
89
+ prints a recognition note for the upstream teams and individuals
90
+ whose work fills the MDS knowledge base.
91
+ - **Success message surfaced.** After a successful run, the agent
92
+ quotes the generator's `MDS onboarding complete` tail block back
93
+ verbatim so the Mr. DJ thank-you text appears in chat the same way
94
+ it does in a terminal CLI run.
95
+ - **MDS Continue handoff.** The agent runs `mds continue` from the
96
+ generated app folder, then tells the user-dev to open that app folder
97
+ in a fresh agent session to lower token usage and cost.
98
+
99
+ ## Defaults
100
+
101
+ - Prefer Uniwind and Tailwind v4 for new projects.
102
+ - Keep route files thin and move business logic into features, services, hooks,
103
+ and utilities.
104
+ - Always add `project/info.md`, `project/todo.md`, `project/style.md`, and
105
+ `project/guidelines.md` when project memory is selected.
106
+ - Normalize existing `project/info.md` and `project/style.md` into canonical
107
+ sections while preserving unknown content under Imported Notes.
108
+ - Add `project/intake-agent.md` when context is thin or imported notes need a
109
+ follow-up agent conversation.
110
+ - Keep `project/style.md` visual-only; put technical and agent rules in
111
+ `project/guidelines.md`.
112
+ - Treat monorepo-aware scaffolding as a future step after the single-app MVP is
113
+ stable.
@@ -0,0 +1,314 @@
1
+ # API Routes with Catch-All Pattern
2
+
3
+ ## Description
4
+
5
+ Expo Router API routes use the `+api.ts` pattern to create server-side endpoints directly alongside client routes. The `[...segments]+api.ts` syntax creates catch-all routes that capture all path segments, enabling flexible API endpoint structures with full control over HTTP methods, CORS, and request handling.
6
+
7
+ ## When to Use
8
+
9
+ **Use API routes** for:
10
+ - ✅ Backend endpoints served from same domain as frontend
11
+ - ✅ Proxy servers that forward requests upstream
12
+ - ✅ Server-side validation and data transformation
13
+ - ✅ Protected endpoints with authentication
14
+ - ✅ Catch-all routes for dynamic path handling
15
+
16
+ ## Code Example
17
+
18
+ ### Basic API Route (Single Endpoint)
19
+
20
+ ```typescript
21
+ // File: app/api/quantum-backend/[...segments]+api.ts
22
+ import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
23
+
24
+ type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
25
+
26
+ const ALLOWED_ORIGINS = [
27
+ 'http://localhost:3000',
28
+ 'http://localhost:8081',
29
+ 'http://127.0.0.1:3000',
30
+ 'http://127.0.0.1:8081',
31
+ ];
32
+
33
+ function normalizeOrigin(request: ExpoRequest): string | null {
34
+ const origin = request.headers.get('origin');
35
+ if (!origin) return null;
36
+
37
+ return ALLOWED_ORIGINS.includes(origin) ? origin : null;
38
+ }
39
+
40
+ export async function GET(request: ExpoRequest) {
41
+ const allowedOrigin = normalizeOrigin(request);
42
+
43
+ if (request.method === 'OPTIONS') {
44
+ return new ExpoResponse(null, {
45
+ status: 204,
46
+ headers: allowedOrigin
47
+ ? { 'Access-Control-Allow-Origin': allowedOrigin }
48
+ : {},
49
+ });
50
+ }
51
+
52
+ // Handle GET request
53
+ return new ExpoResponse(JSON.stringify({ message: 'GET OK' }), {
54
+ status: 200,
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ ...(allowedOrigin && {
58
+ 'Access-Control-Allow-Origin': allowedOrigin,
59
+ }),
60
+ },
61
+ });
62
+ }
63
+
64
+ export async function POST(request: ExpoRequest) {
65
+ const allowedOrigin = normalizeOrigin(request);
66
+ const body = await request.json();
67
+
68
+ // Process request
69
+ const result = {
70
+ success: true,
71
+ data: body,
72
+ };
73
+
74
+ return new ExpoResponse(JSON.stringify(result), {
75
+ status: 200,
76
+ headers: {
77
+ 'Content-Type': 'application/json',
78
+ ...(allowedOrigin && {
79
+ 'Access-Control-Allow-Origin': allowedOrigin,
80
+ }),
81
+ },
82
+ });
83
+ }
84
+ ```
85
+
86
+ **From:** DJsPortfolio/src/app/api/quantum-backend/[...segments]+api.ts
87
+
88
+ ### Catch-All Route with Path Normalization
89
+
90
+ ```typescript
91
+ // File: app/public-facing/api/quantum/v1/[...segments]+api.ts
92
+ import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
93
+
94
+ const DISALLOWED_PREFIXES = ['/v1/keys', '/v1/ibm/profiles'];
95
+ const DEFAULT_UPSTREAM_BASE_URL = 'http://127.0.0.1:8000/v1';
96
+
97
+ type ProxyMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
98
+
99
+ function normalizeUpstreamBaseUrl(): string {
100
+ const raw = process.env.EXPO_PUBLIC_QUANTUM_API_BASE_URL?.trim();
101
+ const base = raw || DEFAULT_UPSTREAM_BASE_URL;
102
+
103
+ // Remove trailing slash
104
+ const trimmed = base.replace(/\/+$/, '');
105
+
106
+ // Ensure /v1 suffix
107
+ return trimmed.endsWith('/v1') ? trimmed : `${trimmed}/v1`;
108
+ }
109
+
110
+ function normalizeOperationPath(pathname: string): string {
111
+ // Remove leading/trailing slashes, lowercase
112
+ return `/${pathname.toLowerCase().trim().replace(/^\/+|\/+$/g, '')}`;
113
+ }
114
+
115
+ export async function POST(request: ExpoRequest) {
116
+ try {
117
+ // Parse segments from URL
118
+ const url = new URL(request.url);
119
+ const pathname = url.pathname;
120
+
121
+ // Extract segments after route prefix
122
+ const segments = pathname
123
+ .replace('/public-facing/api/quantum/v1', '')
124
+ .split('/')
125
+ .filter(Boolean);
126
+
127
+ const operationPath = '/' + segments.join('/');
128
+
129
+ // Validate disallowed paths
130
+ for (const prefix of DISALLOWED_PREFIXES) {
131
+ if (operationPath.startsWith(prefix)) {
132
+ return new ExpoResponse(
133
+ JSON.stringify({ error: 'Path not allowed' }),
134
+ { status: 403, headers: { 'Content-Type': 'application/json' } }
135
+ );
136
+ }
137
+ }
138
+
139
+ // Forward to upstream
140
+ const upstreamUrl = normalizeUpstreamBaseUrl() + operationPath;
141
+ const upstreamRequest = new Request(upstreamUrl, {
142
+ method: 'POST',
143
+ headers: request.headers,
144
+ body: await request.text(),
145
+ });
146
+
147
+ const response = await fetch(upstreamRequest);
148
+ return new ExpoResponse(await response.text(), {
149
+ status: response.status,
150
+ headers: Object.fromEntries(response.headers),
151
+ });
152
+ } catch (error) {
153
+ return new ExpoResponse(
154
+ JSON.stringify({ error: 'Internal Server Error' }),
155
+ { status: 500, headers: { 'Content-Type': 'application/json' } }
156
+ );
157
+ }
158
+ }
159
+ ```
160
+
161
+ **From:** DJsPortfolio/src/app/public-facing/api/quantum/v1/[...segments]+api.ts (lines 1-50)
162
+
163
+ ## Configuration
164
+
165
+ ### Enable API Routes in app.json
166
+
167
+ ```json
168
+ {
169
+ "expo": {
170
+ "plugins": [
171
+ [
172
+ "expo-router",
173
+ {
174
+ "apiRoutes": true,
175
+ "origin": false
176
+ }
177
+ ]
178
+ ]
179
+ }
180
+ }
181
+ ```
182
+
183
+ ### Environment Variables for API Base URLs
184
+
185
+ ```bash
186
+ # .env or .env.production
187
+ EXPO_PUBLIC_QUANTUM_API_BASE_URL=http://127.0.0.1:8000/v1
188
+ QUANTUM_UPSTREAM_API_SECRET=your-secret-key
189
+ ```
190
+
191
+ ### TypeScript Setup
192
+
193
+ ```typescript
194
+ import { ExpoRequest, ExpoResponse } from 'expo-server-rendering';
195
+
196
+ export type { ExpoRequest, ExpoResponse };
197
+
198
+ // Type for allowed HTTP methods
199
+ type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
200
+ ```
201
+
202
+ ## Best Practices
203
+
204
+ ### ✅ DO
205
+
206
+ 1. **Always validate CORS origin** before responding
207
+ ```typescript
208
+ const ALLOWED_ORIGINS = ['http://localhost:3000', 'https://app.example.com'];
209
+
210
+ function validateOrigin(origin: string | null): boolean {
211
+ return !!origin && ALLOWED_ORIGINS.includes(origin);
212
+ }
213
+ ```
214
+
215
+ 2. **Normalize paths and trim trailing slashes**
216
+ ```typescript
217
+ function normalizePath(path: string): string {
218
+ return path.replace(/\/+$/, '').toLowerCase();
219
+ }
220
+ ```
221
+
222
+ 3. **Use explicit status codes**
223
+ ```typescript
224
+ // ✅ GOOD
225
+ return new ExpoResponse(JSON.stringify(error), {
226
+ status: 400,
227
+ headers: { 'Content-Type': 'application/json' },
228
+ });
229
+ ```
230
+
231
+ 4. **Handle disallowed paths explicitly**
232
+ ```typescript
233
+ const DISALLOWED_PREFIXES = ['/keys', '/credentials'];
234
+
235
+ for (const prefix of DISALLOWED_PREFIXES) {
236
+ if (operationPath.startsWith(prefix)) {
237
+ return new ExpoResponse(
238
+ JSON.stringify({ error: 'Forbidden' }),
239
+ { status: 403 }
240
+ );
241
+ }
242
+ }
243
+ ```
244
+
245
+ 5. **Support OPTIONS preflight requests**
246
+ ```typescript
247
+ if (request.method === 'OPTIONS') {
248
+ return new ExpoResponse(null, {
249
+ status: 204,
250
+ headers: corsHeaders,
251
+ });
252
+ }
253
+ ```
254
+
255
+ ### ❌ DON'T
256
+
257
+ 1. **Don't skip CORS validation**
258
+ ```typescript
259
+ // ❌ BAD - allows any origin
260
+ headers: { 'Access-Control-Allow-Origin': '*' }
261
+
262
+ // ✅ GOOD - restrict to known origins
263
+ if (ALLOWED_ORIGINS.includes(origin)) {
264
+ headers: { 'Access-Control-Allow-Origin': origin }
265
+ }
266
+ ```
267
+
268
+ 2. **Don't expose sensitive environment variables**
269
+ ```typescript
270
+ // ❌ BAD
271
+ headers: process.env.API_SECRET
272
+
273
+ // ✅ GOOD - only use server-side secrets
274
+ const secret = process.env.API_SECRET; // Used in headers, not exposed
275
+ ```
276
+
277
+ 3. **Don't pass unchecked path segments to upstream**
278
+ ```typescript
279
+ // ❌ BAD - could be exploited
280
+ const upstreamUrl = baseUrl + segments;
281
+
282
+ // ✅ GOOD - validate and normalize
283
+ const validatedPath = normalizePath(segments);
284
+ const upstreamUrl = baseUrl + validatedPath;
285
+ ```
286
+
287
+ 4. **Don't forget to handle errors**
288
+ ```typescript
289
+ // ❌ BAD - uncaught errors crash request
290
+ const data = await request.json();
291
+
292
+ // ✅ GOOD - catch and return error response
293
+ try {
294
+ const data = await request.json();
295
+ } catch (error) {
296
+ return new ExpoResponse(
297
+ JSON.stringify({ error: 'Invalid JSON' }),
298
+ { status: 400 }
299
+ );
300
+ }
301
+ ```
302
+
303
+ ## Related Patterns
304
+
305
+ - [File-Based Routing](./file-based-routing.md) — Route file structure
306
+ - [Error Handling](../api/error-handling.md) — Custom error classes
307
+ - [CORS Configuration](../api/cors-configuration.md) — Origin validation
308
+ - [Health Endpoints](../api/health-endpoints.md) — Health check patterns
309
+
310
+ ---
311
+
312
+ *Pattern extracted from production repositories: DJsPortfolio, PokePages*
313
+ *Files: DJsPortfolio/src\app\api\quantum-backend\[...segments]+api.ts*
314
+ *Lines 1-40 of catch-all route pattern with path normalization*