@oalacea/demon 1.0.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.
@@ -0,0 +1,558 @@
1
+ # Dependency Efficiency Analysis Guide
2
+
3
+ This prompt is included by EXECUTE.md. It provides detailed guidance for analyzing dependency usage patterns.
4
+
5
+ ---
6
+
7
+ ## TanStack Router Analysis
8
+
9
+ ```typescript
10
+ // Check for TanStack Router patterns
11
+ describe('TanStack Router Efficiency', () => {
12
+ it('should have type-safe routes', () => {
13
+ // Check if routes are using typed params
14
+ const routes = findFiles('routes', '.tsx');
15
+
16
+ routes.forEach((route) => {
17
+ const content = readFile(route);
18
+
19
+ // Should use $params or useParams with types
20
+ expect(content).toMatch(/useParams.*</);
21
+ });
22
+ });
23
+
24
+ it('should use loaders for data fetching', () => {
25
+ const routes = findFiles('routes', '.tsx');
26
+
27
+ routes.forEach((route) => {
28
+ const content = readFile(route);
29
+
30
+ // Should have loader if route needs data
31
+ if (content.includes('useQuery') || content.includes('useFetch')) {
32
+ expect(content).toMatch(/loader.*=/);
33
+ }
34
+ });
35
+ });
36
+
37
+ it('should have error boundaries', () => {
38
+ const routes = findFiles('routes', '.tsx');
39
+
40
+ routes.forEach((route) => {
41
+ const content = readFile(route);
42
+
43
+ // Should have errorBoundary component
44
+ if (content.includes('loader')) {
45
+ expect(content).toMatch(/errorComponent|ErrorBoundary/);
46
+ }
47
+ });
48
+ });
49
+
50
+ it('should enable link prefetching', () => {
51
+ const components = findFiles('components', '.tsx');
52
+
53
+ components.forEach((comp) => {
54
+ const content = readFile(comp);
55
+
56
+ // Navigation links should have prefetch
57
+ if (content.includes('<Link')) {
58
+ expect(content).toMatch(/prefetch=|prefetchIntent=/);
59
+ }
60
+ });
61
+ });
62
+ });
63
+ ```
64
+
65
+ ### Findings Template
66
+
67
+ ```markdown
68
+ ### TanStack Router Analysis
69
+
70
+ **Good:**
71
+ - ✓ All routes use typed params
72
+ - ✓ Loaders properly implemented for data fetching
73
+ - ✓ Search parameters are typed
74
+
75
+ **Issues Found:**
76
+ - ✗ 3 routes missing error boundaries
77
+ - routes/dashboard.tsx
78
+ - routes/posts.tsx
79
+ - routes/users.tsx
80
+
81
+ - ✗ Navigation links not using prefetch
82
+ - components/Nav.tsx - main menu links
83
+ - components/Footer.tsx - footer links
84
+
85
+ **Recommendations:**
86
+ 1. Add errorComponent to routes with loaders
87
+ 2. Enable prefetch for frequently accessed navigation links
88
+ 3. Consider using preload for critical routes
89
+ ```
90
+
91
+ ---
92
+
93
+ ## React Query Analysis
94
+
95
+ ```typescript
96
+ // Check for React Query patterns
97
+ describe('React Query Efficiency', () => {
98
+ it('should have proper cache keys', () => {
99
+ const hooks = findFiles('hooks', '.ts');
100
+ const components = findFiles('components', '.tsx');
101
+
102
+ const allFiles = [...hooks, ...components];
103
+
104
+ allFiles.forEach((file) => {
105
+ const content = readFile(file);
106
+
107
+ if (content.includes('useQuery') || content.includes('useInfiniteQuery')) {
108
+ // Should use array-based keys for proper serialization
109
+ expect(content).toMatch(/\[['"`]\w+['"`],/);
110
+ }
111
+ });
112
+ });
113
+
114
+ it('should configure staleTime appropriately', () => {
115
+ const hooks = findFiles('hooks', '.ts');
116
+
117
+ hooks.forEach((hook) => {
118
+ const content = readFile(hook);
119
+
120
+ if (content.includes('useQuery')) {
121
+ // Should have staleTime configured
122
+ expect(content).toMatch(/staleTime/);
123
+ }
124
+ });
125
+ });
126
+
127
+ it('should have proper invalidation', () => {
128
+ const mutations = findFiles('mutations', '.ts');
129
+
130
+ mutations.forEach((mutation) => {
131
+ const content = readFile(mutation);
132
+
133
+ if (content.includes('useMutation')) {
134
+ // Should invalidate related queries on success
135
+ expect(content).toMatch(/invalidateQueries/);
136
+ }
137
+ });
138
+ });
139
+
140
+ it('should use suspense when beneficial', () => {
141
+ const components = findFiles('components', '.tsx');
142
+
143
+ components.forEach((comp) => {
144
+ const content = readFile(comp);
145
+
146
+ // Server components or suspense boundary
147
+ if (content.includes('useSuspenseQuery') || content.includes('Suspense')) {
148
+ expect(content).toBeTruthy();
149
+ }
150
+ });
151
+ });
152
+ });
153
+ ```
154
+
155
+ ### Findings Template
156
+
157
+ ```markdown
158
+ ### React Query Analysis
159
+
160
+ **Good:**
161
+ - ✓ All queries use array-based cache keys
162
+ - ✓ Mutations properly invalidate related queries
163
+ - ✓ QueryClient configured with reasonable defaults
164
+
165
+ **Issues Found:**
166
+ - ✗ 5 queries missing staleTime configuration
167
+ - hooks/useUsers.ts - will refetch on focus
168
+ - hooks/usePosts.ts - no cache duration
169
+ - hooks/useComments.ts - no cache duration
170
+ - hooks/useAuth.ts - no cache duration
171
+ - hooks/useSettings.ts - no cache duration
172
+
173
+ - ✗ No retry configuration for failed queries
174
+ - ✗ Missing optimistic updates for like/comment mutations
175
+
176
+ **Recommendations:**
177
+ 1. Add staleTime: 30000 for semi-static data
178
+ 2. Configure retry: 1 for user-facing queries
179
+ 3. Implement optimistic updates for social interactions
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Prisma Analysis
185
+
186
+ ```typescript
187
+ // Check for Prisma patterns
188
+ describe('Prisma Efficiency', () => {
189
+ it('should use select for partial data', () => {
190
+ const files = findFiles(['src', 'lib'], '.ts');
191
+
192
+ files.forEach((file) => {
193
+ const content = readFile(file);
194
+
195
+ // If only using some fields, should use select
196
+ if (content.includes('prisma.') && content.includes('findMany')) {
197
+ // Check if result is destructured
198
+ if (content.match(/findMany.*\{[^}]*\}/)) {
199
+ expect(content).toMatch(/select:/);
200
+ }
201
+ }
202
+ });
203
+ });
204
+
205
+ it('should not have N+1 queries', () => {
206
+ const files = findFiles(['src', 'lib'], '.ts');
207
+
208
+ files.forEach((file) => {
209
+ const content = readFile(file);
210
+
211
+ // Look for potential N+1 pattern
212
+ if (content.includes('findMany') && content.includes('forEach')) {
213
+ // If iterating over results and querying inside loop
214
+ const lines = content.split('\n');
215
+ let inFindMany = false;
216
+
217
+ for (const line of lines) {
218
+ if (line.includes('findMany')) inFindMany = true;
219
+ if (inFindMany && line.includes('forEach')) {
220
+ // Check if prisma call inside forEach
221
+ expect(line).not.toMatch(/prisma\.\w+\.find/);
222
+ }
223
+ }
224
+ }
225
+ });
226
+ });
227
+
228
+ it('should use indexes for filtered fields', () => {
229
+ const schema = readFile('prisma/schema.prisma');
230
+
231
+ // Check for common filter fields without indexes
232
+ const models = schema.match(/model \w+ {([^}]+)}/g);
233
+
234
+ models?.forEach((model) => {
235
+ const fields = model.match(/(\w+)\s+\w+/g);
236
+
237
+ // Common filter fields that should be indexed
238
+ const filterFields = ['email', 'username', 'slug', 'status', 'published'];
239
+
240
+ fields?.forEach((field) => {
241
+ const [name] = field.split(' ');
242
+ if (filterFields.includes(name)) {
243
+ expect(model).toMatch(/@@index/);
244
+ }
245
+ });
246
+ });
247
+ });
248
+ });
249
+ ```
250
+
251
+ ### Findings Template
252
+
253
+ ```markdown
254
+ ### Prisma Analysis
255
+
256
+ **Good:**
257
+ - ✓ Using select for API responses
258
+ - ✓ Transactions for multi-step operations
259
+ - ✓ Proper connection pooling configured
260
+
261
+ **Issues Found:**
262
+ - ✗ N+1 query in dashboard data loader
263
+ - lib/dashboard.ts - Loading user posts in loop
264
+ - Fix: Use include or separate query with where
265
+
266
+ - ✗ Missing index on User.email
267
+ - Frequently filtered but not indexed
268
+ - Fix: Add @@index([email]) to User model
269
+
270
+ - ✗ Missing index on Post.slug
271
+ - Used for routing but not indexed
272
+ - Fix: Add @@unique([slug]) or @@index([slug])
273
+
274
+ - ✗ Not using select for list views
275
+ - API returns full objects when only partial needed
276
+ - Fix: Add select to prisma.user.findMany()
277
+
278
+ **Recommendations:**
279
+ 1. Add indexes to frequently queried fields
280
+ 2. Use include instead of separate queries for relations
281
+ 3. Implement select for all public API endpoints
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Zustand Store Analysis
287
+
288
+ ```typescript
289
+ // Check for Zustand patterns
290
+ describe('Zustand Efficiency', () => {
291
+ it('should use selectors for component subscriptions', () => {
292
+ const components = findFiles('components', '.tsx');
293
+
294
+ components.forEach((comp) => {
295
+ const content = readFile(comp);
296
+
297
+ // Should not subscribe to entire store
298
+ if (content.includes('useStore')) {
299
+ // Check if destructuring specific fields
300
+ expect(content).toMatch(/useStore\(state\s*=>\s*state\.\w+/);
301
+ }
302
+ });
303
+ });
304
+
305
+ it('should avoid unnecessary re-renders', () => {
306
+ const stores = findFiles('stores', '.ts');
307
+
308
+ stores.forEach((store) => {
309
+ const content = readFile(store);
310
+
311
+ // Should have shallow comparison for objects
312
+ if (content.includes('subscribeWithSelector')) {
313
+ expect(content).toMatch(/shallow/);
314
+ }
315
+ });
316
+ });
317
+
318
+ it('should split stores by domain', () => {
319
+ const stores = findFiles('stores', '.ts');
320
+
321
+ // Check if store file is too large (>500 lines)
322
+ stores.forEach((store) => {
323
+ const lines = readFile(store).split('\n');
324
+ expect(lines.length).toBeLessThan(500);
325
+ });
326
+ });
327
+ });
328
+ ```
329
+
330
+ ---
331
+
332
+ ## React Compiler Analysis
333
+
334
+ ```typescript
335
+ // Check for React Compiler readiness
336
+ describe('React Compiler Readiness', () => {
337
+ it('should remove unnecessary useMemo', () => {
338
+ const components = findFiles('components', '.tsx');
339
+
340
+ components.forEach((comp) => {
341
+ const content = readFile(comp);
342
+
343
+ // Simple useMemo can be removed by compiler
344
+ const simpleMemo = content.match(
345
+ /useMemo\(\(\)\s*=>\s*([^,]+),\s*\[[^\]]*\]\)/g
346
+ );
347
+
348
+ simpleMemo?.forEach((memo) => {
349
+ const value = memo.match(/=>\s*(.+),/)?.[1];
350
+ // If value is a simple primitive or object literal
351
+ if (value && !value.includes('()') && !value.includes('function')) {
352
+ // Mark for potential removal
353
+ expect(comp).toBeDefined();
354
+ }
355
+ });
356
+ });
357
+ });
358
+
359
+ it('should use valid dependency arrays', () => {
360
+ const components = findFiles('components', '.tsx');
361
+
362
+ components.forEach((comp) => {
363
+ const content = readFile(comp);
364
+
365
+ // Check useEffect and useMemo
366
+ const hooks = content.match(/use(?:Effect|Memo|Callback)\([^)]+\)/g);
367
+
368
+ hooks?.forEach((hook) => {
369
+ const deps = hook.match(/\[([^\]]*)\]/)?.[1];
370
+
371
+ if (deps) {
372
+ // Should not have empty deps when using values
373
+ const usedValues = hook.match(/[\w.]+/g);
374
+ const depArray = deps.split(',').map((d) => d.trim());
375
+
376
+ // Basic check - if values used, deps shouldn't be empty
377
+ if (usedValues && usedValues.length > 1 && depArray.length === 0) {
378
+ expect(deps).toBeTruthy();
379
+ }
380
+ }
381
+ });
382
+ });
383
+ });
384
+
385
+ it('should avoid expensive renders', () => {
386
+ const components = findFiles('components', '.tsx');
387
+
388
+ components.forEach((comp) => {
389
+ const content = readFile(comp);
390
+
391
+ // Large inline objects should be memoized or moved outside
392
+ const largeObjects = content.match(
393
+ /{{[\s\S]{500,}}}/g
394
+ );
395
+
396
+ expect(largeObjects).toBeNull();
397
+ });
398
+ });
399
+ });
400
+ ```
401
+
402
+ ---
403
+
404
+ ## Bundle Size Analysis
405
+
406
+ ```typescript
407
+ // Check for bundle optimization
408
+ describe('Bundle Size', () => {
409
+ it('should use tree-shakeable imports', () => {
410
+ const files = findFiles(['src', 'lib'], '.ts');
411
+
412
+ files.forEach((file) => {
413
+ const content = readFile(file);
414
+
415
+ // Should use named imports instead of namespace imports
416
+ expect(content).not.toMatch(/\* as \w+ from/);
417
+ });
418
+ });
419
+
420
+ it('should avoid duplicate dependencies', () => {
421
+ const pkg = readFile('package.json');
422
+ const { dependencies, devDependencies } = JSON.parse(pkg);
423
+
424
+ const allDeps = { ...dependencies, ...devDependencies };
425
+
426
+ // Check for duplicate packages with different versions
427
+ const dupes = Object.entries(allDeps).filter(([name]) => {
428
+ return name.startsWith('@types/') && name.substring(6) in allDeps;
429
+ });
430
+
431
+ expect(dupes).toHaveLength(0);
432
+ });
433
+
434
+ it('should use dynamic imports for large libraries', () => {
435
+ const components = findFiles('components', '.tsx');
436
+
437
+ components.forEach((comp) => {
438
+ const content = readFile(comp);
439
+
440
+ // Large libraries should use dynamic import
441
+ const largeLibs = ['monaco-editor', 'codemirror', 'pdfjs-dist'];
442
+
443
+ largeLibs.forEach((lib) => {
444
+ if (content.includes(lib)) {
445
+ expect(content).toMatch(/dynamic\(|React\.lazy\(/);
446
+ }
447
+ });
448
+ });
449
+ });
450
+ });
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Complete Analysis Template
456
+
457
+ ```typescript
458
+ // tests/deps/efficiency-analysis.test.ts
459
+ import { describe, it, expect } from 'vitest';
460
+ import fs from 'fs';
461
+ import path from 'path';
462
+
463
+ function findFiles(dir: string, ext: string): string[] {
464
+ const files: string[] = [];
465
+
466
+ function traverse(currentDir: string) {
467
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
468
+
469
+ for (const entry of entries) {
470
+ const fullPath = path.join(currentDir, entry.name);
471
+
472
+ if (entry.isDirectory()) {
473
+ if (['node_modules', '.next', 'dist'].includes(entry.name)) continue;
474
+ traverse(fullPath);
475
+ } else if (entry.isFile() && entry.name.endsWith(ext)) {
476
+ files.push(fullPath);
477
+ }
478
+ }
479
+ }
480
+
481
+ traverse(dir);
482
+ return files;
483
+ }
484
+
485
+ function readFile(filePath: string): string {
486
+ return fs.readFileSync(filePath, 'utf-8');
487
+ }
488
+
489
+ describe('Dependency Efficiency Analysis', () => {
490
+ describe('TanStack Router', () => {
491
+ it('should have typed routes', () => {
492
+ const routes = findFiles('src/routes', '.tsx');
493
+
494
+ routes.forEach((route) => {
495
+ const content = readFile(route);
496
+ // Checks for proper typing patterns
497
+ if (content.includes('$')) {
498
+ expect(content).toMatch(/UseParams|useParams/);
499
+ }
500
+ });
501
+ });
502
+ });
503
+
504
+ describe('React Query', () => {
505
+ it('should use array cache keys', () => {
506
+ const hooks = findFiles('src/hooks', '.ts');
507
+
508
+ hooks.forEach((hook) => {
509
+ const content = readFile(hook);
510
+
511
+ if (content.includes('useQuery')) {
512
+ expect(content).toMatch(/\['/, 'Should use array-based cache keys');
513
+ }
514
+ });
515
+ });
516
+ });
517
+
518
+ describe('Prisma', () => {
519
+ it('should avoid N+1 queries', () => {
520
+ const files = findFiles('src', '.ts');
521
+
522
+ let hasN1Warning = false;
523
+
524
+ files.forEach((file) => {
525
+ const content = readFile(file);
526
+
527
+ // Pattern: findMany followed by forEach with query inside
528
+ if (content.includes('findMany') && content.includes('forEach')) {
529
+ const lines = content.split('\n');
530
+ for (let i = 0; i < lines.length; i++) {
531
+ if (lines[i].includes('findMany')) {
532
+ // Check next 10 lines for forEach with prisma query
533
+ for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
534
+ if (lines[j].includes('forEach') && lines[j].includes('prisma.')) {
535
+ hasN1Warning = true;
536
+ console.warn(`Potential N+1 in ${file}:${i + 1}`);
537
+ }
538
+ }
539
+ }
540
+ }
541
+ }
542
+ });
543
+ });
544
+ });
545
+ });
546
+ ```
547
+
548
+ ---
549
+
550
+ ## Running Analysis
551
+
552
+ ```bash
553
+ # Run dependency analysis
554
+ docker exec demon-tools npm test -- tests/deps/efficiency-analysis.test.ts
555
+
556
+ # Generate bundle analysis
557
+ docker exec demon-tools npm run build -- --analyze
558
+ ```