@venizia/ignis-docs 0.0.5-0 → 0.0.6-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 (99) hide show
  1. package/package.json +2 -2
  2. package/wiki/best-practices/architecture-decisions.md +0 -8
  3. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  4. package/wiki/best-practices/performance-optimization.md +3 -3
  5. package/wiki/best-practices/security-guidelines.md +2 -2
  6. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  7. package/wiki/guides/core-concepts/components-guide.md +1 -1
  8. package/wiki/guides/core-concepts/components.md +2 -2
  9. package/wiki/guides/core-concepts/dependency-injection.md +1 -1
  10. package/wiki/guides/core-concepts/services.md +1 -1
  11. package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
  12. package/wiki/guides/tutorials/ecommerce-api.md +2 -2
  13. package/wiki/guides/tutorials/realtime-chat.md +725 -461
  14. package/wiki/guides/tutorials/testing.md +1 -1
  15. package/wiki/references/base/bootstrapping.md +0 -2
  16. package/wiki/references/base/components.md +2 -2
  17. package/wiki/references/base/controllers.md +0 -1
  18. package/wiki/references/base/datasources.md +1 -1
  19. package/wiki/references/base/dependency-injection.md +1 -1
  20. package/wiki/references/base/filter-system/quick-reference.md +0 -14
  21. package/wiki/references/base/middlewares.md +24 -80
  22. package/wiki/references/base/providers.md +0 -9
  23. package/wiki/references/base/services.md +0 -1
  24. package/wiki/references/components/authentication/api.md +444 -0
  25. package/wiki/references/components/authentication/errors.md +177 -0
  26. package/wiki/references/components/authentication/index.md +571 -0
  27. package/wiki/references/components/authentication/usage.md +781 -0
  28. package/wiki/references/components/health-check.md +292 -103
  29. package/wiki/references/components/index.md +14 -12
  30. package/wiki/references/components/mail/api.md +505 -0
  31. package/wiki/references/components/mail/errors.md +176 -0
  32. package/wiki/references/components/mail/index.md +535 -0
  33. package/wiki/references/components/mail/usage.md +404 -0
  34. package/wiki/references/components/request-tracker.md +229 -25
  35. package/wiki/references/components/socket-io/api.md +1051 -0
  36. package/wiki/references/components/socket-io/errors.md +119 -0
  37. package/wiki/references/components/socket-io/index.md +410 -0
  38. package/wiki/references/components/socket-io/usage.md +322 -0
  39. package/wiki/references/components/static-asset/api.md +261 -0
  40. package/wiki/references/components/static-asset/errors.md +89 -0
  41. package/wiki/references/components/static-asset/index.md +617 -0
  42. package/wiki/references/components/static-asset/usage.md +364 -0
  43. package/wiki/references/components/swagger.md +390 -110
  44. package/wiki/references/components/template/api-page.md +125 -0
  45. package/wiki/references/components/template/errors-page.md +100 -0
  46. package/wiki/references/components/template/index.md +104 -0
  47. package/wiki/references/components/template/setup-page.md +134 -0
  48. package/wiki/references/components/template/single-page.md +132 -0
  49. package/wiki/references/components/template/usage-page.md +127 -0
  50. package/wiki/references/components/websocket/api.md +508 -0
  51. package/wiki/references/components/websocket/errors.md +123 -0
  52. package/wiki/references/components/websocket/index.md +453 -0
  53. package/wiki/references/components/websocket/usage.md +475 -0
  54. package/wiki/references/helpers/cron/index.md +224 -0
  55. package/wiki/references/helpers/crypto/index.md +537 -0
  56. package/wiki/references/helpers/env/index.md +214 -0
  57. package/wiki/references/helpers/error/index.md +232 -0
  58. package/wiki/references/helpers/index.md +16 -15
  59. package/wiki/references/helpers/inversion/index.md +608 -0
  60. package/wiki/references/helpers/logger/index.md +600 -0
  61. package/wiki/references/helpers/network/api.md +986 -0
  62. package/wiki/references/helpers/network/index.md +620 -0
  63. package/wiki/references/helpers/queue/index.md +589 -0
  64. package/wiki/references/helpers/redis/index.md +495 -0
  65. package/wiki/references/helpers/socket-io/api.md +497 -0
  66. package/wiki/references/helpers/socket-io/index.md +513 -0
  67. package/wiki/references/helpers/storage/api.md +705 -0
  68. package/wiki/references/helpers/storage/index.md +583 -0
  69. package/wiki/references/helpers/template/index.md +66 -0
  70. package/wiki/references/helpers/template/single-page.md +126 -0
  71. package/wiki/references/helpers/testing/index.md +510 -0
  72. package/wiki/references/helpers/types/index.md +512 -0
  73. package/wiki/references/helpers/uid/index.md +272 -0
  74. package/wiki/references/helpers/websocket/api.md +736 -0
  75. package/wiki/references/helpers/websocket/index.md +574 -0
  76. package/wiki/references/helpers/worker-thread/index.md +470 -0
  77. package/wiki/references/quick-reference.md +6 -23
  78. package/wiki/references/src-details/core.md +1 -2
  79. package/wiki/references/utilities/jsx.md +1 -8
  80. package/wiki/references/utilities/statuses.md +4 -9
  81. package/wiki/references/components/authentication.md +0 -476
  82. package/wiki/references/components/mail.md +0 -687
  83. package/wiki/references/components/socket-io.md +0 -145
  84. package/wiki/references/components/static-asset.md +0 -1277
  85. package/wiki/references/helpers/cron.md +0 -108
  86. package/wiki/references/helpers/crypto.md +0 -132
  87. package/wiki/references/helpers/env.md +0 -83
  88. package/wiki/references/helpers/error.md +0 -97
  89. package/wiki/references/helpers/inversion.md +0 -176
  90. package/wiki/references/helpers/logger.md +0 -296
  91. package/wiki/references/helpers/network.md +0 -396
  92. package/wiki/references/helpers/queue.md +0 -150
  93. package/wiki/references/helpers/redis.md +0 -142
  94. package/wiki/references/helpers/socket-io.md +0 -122
  95. package/wiki/references/helpers/storage.md +0 -665
  96. package/wiki/references/helpers/testing.md +0 -133
  97. package/wiki/references/helpers/types.md +0 -167
  98. package/wiki/references/helpers/uid.md +0 -167
  99. package/wiki/references/helpers/worker-thread.md +0 -178
@@ -703,7 +703,7 @@ class CreateAndUpdateAndDeleteHandler extends TestCaseHandler {
703
703
 
704
704
  ## Next Steps
705
705
 
706
- - [Testing Reference](../../references/helpers/testing.md) - Complete API documentation
706
+ - [Testing Reference](../../references/helpers/testing/) - Complete API documentation
707
707
  - [Best Practices](../../best-practices/code-style-standards/) - Code quality standards
708
708
  - [Troubleshooting](../../best-practices/troubleshooting-tips.md) - Common issues
709
709
 
@@ -773,8 +773,6 @@ await app.boot();
773
773
  ```
774
774
 
775
775
 
776
- ---
777
-
778
776
  ## See Also
779
777
 
780
778
  - **Related References:**
@@ -589,10 +589,10 @@ export * from './controller';
589
589
  - [Dependency Injection](/guides/core-concepts/dependency-injection) - Component bindings
590
590
 
591
591
  - **Built-in Components:**
592
- - [Authentication Component](/references/components/authentication) - JWT authentication
592
+ - [Authentication Component](/references/components/authentication/) - JWT authentication
593
593
  - [Health Check Component](/references/components/health-check) - Health endpoints
594
594
  - [Swagger Component](/references/components/swagger) - API documentation
595
- - [Socket.IO Component](/references/components/socket-io) - WebSocket support
595
+ - [Socket.IO Component](/references/components/socket-io/) - WebSocket support
596
596
 
597
597
  - **Best Practices:**
598
598
  - [Architectural Patterns](/best-practices/architectural-patterns) - Component design patterns
@@ -632,7 +632,6 @@ export class ConfigurationController extends _Controller {
632
632
  }
633
633
  ```
634
634
 
635
- ---
636
635
 
637
636
  ## See Also
638
637
 
@@ -341,7 +341,7 @@ try {
341
341
  }
342
342
  ```
343
343
 
344
- > **Note:** For most use cases, prefer using `repository.beginTransaction()` which provides a higher-level API. See [Repositories Reference](./repositories/.md#transactions) for details.
344
+ > **Note:** For most use cases, prefer using `repository.beginTransaction()` which provides a higher-level API. See [Repositories Reference](./repositories/#transactions) for details.
345
345
 
346
346
  This architecture ensures that datasources are configured consistently and that the fully-initialized Drizzle connector, aware of all schemas and relations, is available to repositories for querying.
347
347
 
@@ -201,7 +201,7 @@ class UserController {
201
201
  - [Providers](/references/base/providers) - Factory pattern for dynamic injection
202
202
 
203
203
  - **References:**
204
- - [Inversion Helper](/references/helpers/inversion) - DI container utilities
204
+ - [Inversion Helper](/references/helpers/inversion/) - DI container utilities
205
205
  - [Bootstrapping API](/references/base/bootstrapping) - Auto-discovery and DI
206
206
  - [Glossary](/guides/reference/glossary#dependency-injection-di) - DI concepts explained
207
207
 
@@ -22,7 +22,6 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
22
22
 
23
23
  **See:** [Comparison Operators Guide](./comparison-operators.md)
24
24
 
25
- ---
26
25
 
27
26
  ## Range Operators
28
27
 
@@ -33,7 +32,6 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
33
32
 
34
33
  **See:** [Range Operators Guide](./range-operators.md)
35
34
 
36
- ---
37
35
 
38
36
  ## List Operators
39
37
 
@@ -44,7 +42,6 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
44
42
 
45
43
  **See:** [List Operators Guide](./list-operators.md)
46
44
 
47
- ---
48
45
 
49
46
  ## Pattern Matching Operators
50
47
 
@@ -65,7 +62,6 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
65
62
 
66
63
  **See:** [Pattern Matching Guide](./pattern-matching.md)
67
64
 
68
- ---
69
65
 
70
66
  ## Null Check Operators
71
67
 
@@ -83,7 +79,6 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
83
79
 
84
80
  **See:** [Null Operators Guide](./null-operators.md)
85
81
 
86
- ---
87
82
 
88
83
  ## Logical Operators
89
84
 
@@ -106,7 +101,6 @@ Complete single-page reference for all IGNIS filter operators. For detailed expl
106
101
 
107
102
  **See:** [Logical Operators Guide](./logical-operators.md)
108
103
 
109
- ---
110
104
 
111
105
  ## PostgreSQL Array Operators
112
106
 
@@ -122,7 +116,6 @@ These operators work with PostgreSQL array columns (`varchar[]`, `text[]`, `inte
122
116
 
123
117
  **See:** [Array Operators Guide](./array-operators.md)
124
118
 
125
- ---
126
119
 
127
120
  ## JSON/JSONB Operators (PostgreSQL)
128
121
 
@@ -166,7 +159,6 @@ All comparison operators work with JSON path queries:
166
159
 
167
160
  **See:** [JSON Filtering Guide](./json-filtering.md)
168
161
 
169
- ---
170
162
 
171
163
  ## Fields, Ordering & Pagination
172
164
 
@@ -211,7 +203,6 @@ const users = await userRepo.find({
211
203
 
212
204
  **See:** [Fields, Ordering & Pagination Guide](./fields-order-pagination.md)
213
205
 
214
- ---
215
206
 
216
207
  ## Default Filters
217
208
 
@@ -250,7 +241,6 @@ await userRepo.find({
250
241
 
251
242
  **See:** [Default Filter Guide](./default-filter.md)
252
243
 
253
- ---
254
244
 
255
245
  ## Common Filter Patterns
256
246
 
@@ -314,7 +304,6 @@ await userRepo.find({
314
304
  }
315
305
  ```
316
306
 
317
- ---
318
307
 
319
308
  ## Operator Precedence
320
309
 
@@ -339,7 +328,6 @@ Use explicit parentheses (via nested `and`/`or`) for clarity:
339
328
  }
340
329
  ```
341
330
 
342
- ---
343
331
 
344
332
  ## Type Safety
345
333
 
@@ -371,7 +359,6 @@ await userRepo.find({
371
359
  });
372
360
  ```
373
361
 
374
- ---
375
362
 
376
363
  ## Performance Tips
377
364
 
@@ -407,7 +394,6 @@ await userRepo.find({
407
394
  }
408
395
  ```
409
396
 
410
- ---
411
397
 
412
398
  ## See Also
413
399
 
@@ -7,7 +7,7 @@ lastUpdated: 2026-01-03
7
7
 
8
8
  # Middlewares Reference
9
9
 
10
- IGNIS provides a collection of built-in middlewares for common application needs including error handling, request logging, request normalization, and favicon serving.
10
+ IGNIS provides a collection of built-in middlewares for common application needs including error handling, request logging, and favicon serving.
11
11
 
12
12
  **Files:**
13
13
  - `packages/core/src/base/middlewares/*.ts`
@@ -24,16 +24,14 @@ IGNIS provides a collection of built-in middlewares for common application needs
24
24
  |------------|---------|-------------|
25
25
  | `appErrorHandler` | Catches and formats application errors | `logger` |
26
26
  | `notFoundHandler` | Handles 404 Not Found responses | `logger` |
27
- | `requestNormalize` | Pre-parses JSON request bodies | None |
28
- | `RequestSpyMiddleware` | Logs request lifecycle and timing | None |
27
+ | `RequestSpyMiddleware` | Logs request lifecycle, timing, and parses request body | None |
29
28
  | `emojiFavicon` | Serves an emoji as favicon | `icon` |
30
29
 
31
30
  ## Table of Contents
32
31
 
33
32
  - [Error Handler (`appErrorHandler`)](#error-handler-apporerrorhandler)
34
33
  - [Not Found Handler (`notFoundHandler`)](#not-found-handler-notfoundhandler)
35
- - [Request Normalizer (`requestNormalize`)](#request-normalizer-requestnormalize)
36
- - [Request Spy (Debug)](#request-spy-debug)
34
+ - [Request Spy (`RequestSpyMiddleware`)](#request-spy-requestspymiddleware)
37
35
  - [Emoji Favicon](#emoji-favicon)
38
36
  - [Creating Custom Middleware](#creating-custom-middleware)
39
37
  - [Middleware Order & Priority](#middleware-order--priority)
@@ -189,7 +187,6 @@ async getUser(c: TRouteContext) {
189
187
  }
190
188
  ```
191
189
 
192
- ---
193
190
 
194
191
  ### Not Found Handler (`notFoundHandler`)
195
192
 
@@ -235,67 +232,24 @@ app.notFound(notFoundHandler({
235
232
 
236
233
  **Returns:** `NotFoundHandler` - Hono not found handler function
237
234
 
238
- ---
239
-
240
- ### Request Normalizer (`requestNormalize`)
241
-
242
- Pre-parses JSON request bodies to ensure consistent request handling and prevent common parsing issues.
243
-
244
- **File:** `packages/core/src/base/middlewares/request-normalize.middleware.ts`
245
235
 
246
- #### How It Works
247
-
248
- 1. **Skip for GET/OPTIONS**: No normalization for read-only requests
249
- 2. **Check Content-Length**: Skip if no body is present (`Content-Length: 0`)
250
- 3. **Check Content-Type**: Only process `application/json` requests
251
- 4. **Pre-parse JSON**: Calls `context.req.json()` to cache the parsed body
252
-
253
- #### Benefits
236
+ ### Request Spy (`RequestSpyMiddleware`)
254
237
 
255
- - Prevents multiple body parsing attempts
256
- - Ensures body is available for all subsequent middleware/handlers
257
- - Catches JSON parsing errors early in the request lifecycle
258
-
259
- #### Usage
260
-
261
- ```typescript
262
- import { requestNormalize } from '@venizia/ignis';
263
-
264
- const app = new IgnisApplication({
265
- // ...
266
- });
267
-
268
- // Register as early middleware
269
- app.use(requestNormalize());
270
- ```
271
-
272
- :::tip Why Pre-parse?
273
- Hono's request body can only be read once. This middleware ensures the body is parsed and cached early, making it available to all downstream handlers.
274
- :::
275
-
276
- #### API Reference
277
-
278
- ##### `requestNormalize()`
279
-
280
- **Parameters:** None
281
-
282
- **Returns:** `MiddlewareHandler` - Hono middleware function
283
-
284
- ---
285
-
286
- ### Request Spy (Debug)
287
-
288
- Logs detailed information about each request including timing, IP address, method, path, and query parameters.
238
+ Logs detailed information about each request including timing, IP address, method, path, query parameters, and request body. Also handles request body parsing for JSON, form data, and text content types.
289
239
 
290
240
  **File:** `packages/core/src/base/middlewares/request-spy.middleware.ts`
291
241
 
292
242
  #### Features
293
243
 
294
- - Request lifecycle logging (START/DONE)
244
+ - Request lifecycle logging (incoming/outgoing)
295
245
  - Performance timing tracking
296
246
  - IP address extraction (supports `x-real-ip` and `x-forwarded-for` headers)
297
247
  - Request ID tracking
298
- - Query and body parameter logging
248
+ - Query and body parameter logging (body only logged in non-production)
249
+ - **Request body parsing**: Automatically parses and caches request bodies:
250
+ - `application/json` → `req.json()`
251
+ - `multipart/form-data`, `application/x-www-form-urlencoded` → `req.parseBody()`
252
+ - Other content types (text, html, xml) → `req.text()`
299
253
 
300
254
  #### Usage
301
255
 
@@ -363,7 +317,6 @@ async example(c: TRouteContext) {
363
317
  Request spy logs every request detail. Consider disabling or reducing verbosity in production environments with high traffic.
364
318
  :::
365
319
 
366
- ---
367
320
 
368
321
  ### Emoji Favicon
369
322
 
@@ -416,7 +369,6 @@ app.use(emojiFavicon({ icon: '🌟' })); // Star
416
369
  SVG favicons are supported in all modern browsers. Fallback to a traditional `.ico` file if you need to support legacy browsers.
417
370
  :::
418
371
 
419
- ---
420
372
 
421
373
  ## Creating Custom Middleware
422
374
 
@@ -494,7 +446,6 @@ const myMiddleware = app.get(MyMiddleware);
494
446
  app.use(myMiddleware.value());
495
447
  ```
496
448
 
497
- ---
498
449
 
499
450
  ## Middleware Order & Priority
500
451
 
@@ -511,49 +462,44 @@ app.use(cors());
511
462
  // 2. Request ID generation
512
463
  app.use(requestId());
513
464
 
514
- // 3. Request spy/logging
465
+ // 3. Request spy/logging (also handles body parsing)
515
466
  const requestSpy = new RequestSpyMiddleware();
516
467
  app.use(requestSpy.value());
517
468
 
518
- // 4. Request normalization
519
- app.use(requestNormalize());
520
-
521
- // 5. Security middleware (helmet, etc.)
469
+ // 4. Security middleware (helmet, etc.)
522
470
  app.use(helmet());
523
471
 
524
- // 6. Rate limiting
472
+ // 5. Rate limiting
525
473
  app.use(rateLimit());
526
474
 
527
- // 7. Authentication
475
+ // 6. Authentication
528
476
  app.use('/api/*', authenticate());
529
477
 
530
- // 8. Favicon (can be early or late)
478
+ // 7. Favicon (can be early or late)
531
479
  app.use(emojiFavicon({ icon: '🚀' }));
532
480
 
533
- // 9. Application routes
481
+ // 8. Application routes
534
482
  app.mountControllers();
535
483
 
536
- // 10. Error handler (LAST in chain)
484
+ // 9. Error handler (LAST in chain)
537
485
  app.onError(appErrorHandler({ logger: app.logger }));
538
486
 
539
- // 11. Not found handler (AFTER error handler)
487
+ // 10. Not found handler (AFTER error handler)
540
488
  app.notFound(notFoundHandler({ logger: app.logger }));
541
489
  ```
542
490
 
543
491
  ### Key Principles
544
492
 
545
493
  1. **Request ID First**: Generate request ID before logging
546
- 2. **Logging Early**: Log requests before normalization/parsing
547
- 3. **Normalization Before Business Logic**: Parse bodies before they're needed
548
- 4. **Security Middleware Before Routes**: Protect routes with security checks
549
- 5. **Error Handler Last**: Catch all errors from previous middleware
550
- 6. **404 Handler After Error Handler**: Ensure unhandled routes return 404
494
+ 2. **Request Spy Early**: Log and parse request bodies before business logic
495
+ 3. **Security Middleware Before Routes**: Protect routes with security checks
496
+ 4. **Error Handler Last**: Catch all errors from previous middleware
497
+ 5. **404 Handler After Error Handler**: Ensure unhandled routes return 404
551
498
 
552
499
  :::warning Order Matters
553
500
  Placing error handler before routes will prevent it from catching route errors. Always register error handlers last.
554
501
  :::
555
502
 
556
- ---
557
503
 
558
504
  ## Common Patterns
559
505
 
@@ -584,14 +530,13 @@ app.use('/api/public/*', rateLimitMiddleware());
584
530
  const apiMiddleware = (): MiddlewareHandler => {
585
531
  return createMiddleware(async (context, next) => {
586
532
  // Run multiple middleware in sequence
587
- await requestNormalize()(context, async () => {
533
+ await rateLimit()(context, async () => {
588
534
  await authenticate()(context, next);
589
535
  });
590
536
  });
591
537
  };
592
538
  ```
593
539
 
594
- ---
595
540
 
596
541
  ## Performance Considerations
597
542
 
@@ -616,7 +561,6 @@ Error handlers log every error. For high error rates, consider:
616
561
  - Error aggregation services (Sentry, Rollbar)
617
562
  - Rate-limited logging
618
563
 
619
- ---
620
564
 
621
565
  ## See Also
622
566
 
@@ -49,7 +49,6 @@ A **Provider** is a class that implements the Factory pattern, responsible for c
49
49
  - **Multi-Tenant**: Provide tenant-specific instances
50
50
  - **Feature Flags**: Enable/disable features at runtime
51
51
 
52
- ---
53
52
 
54
53
  ## BaseProvider Class
55
54
 
@@ -99,7 +98,6 @@ The `value` method is where you implement your factory logic.
99
98
  - Create and configure instances based on logic
100
99
  - Return factories for deferred instantiation
101
100
 
102
- ---
103
101
 
104
102
  ## Provider vs Service
105
103
 
@@ -185,7 +183,6 @@ class OrderService extends BaseService {
185
183
  - **Need to implement business logic?** → Use a Service
186
184
  :::
187
185
 
188
- ---
189
186
 
190
187
  ## Creating Custom Providers
191
188
 
@@ -297,7 +294,6 @@ export class DatabaseProvider extends BaseProvider<Database> {
297
294
  }
298
295
  ```
299
296
 
300
- ---
301
297
 
302
298
  ## Provider Lifecycle
303
299
 
@@ -354,7 +350,6 @@ export class FactoryDatabaseProvider extends BaseProvider<Database> {
354
350
  }
355
351
  ```
356
352
 
357
- ---
358
353
 
359
354
  ## Real-World Examples
360
355
 
@@ -498,7 +493,6 @@ const requestSpy = new RequestSpyMiddleware();
498
493
  app.use(requestSpy.value());
499
494
  ```
500
495
 
501
- ---
502
496
 
503
497
  ## Common Patterns
504
498
 
@@ -589,7 +583,6 @@ export class CacheProvider extends BaseProvider<ICache> {
589
583
  }
590
584
  ```
591
585
 
592
- ---
593
586
 
594
587
  ## Common Pitfalls
595
588
 
@@ -664,7 +657,6 @@ value(container: Container): IMailTransport {
664
657
  }
665
658
  ```
666
659
 
667
- ---
668
660
 
669
661
  ## Performance Considerations
670
662
 
@@ -710,7 +702,6 @@ export class ConfigProvider extends BaseProvider<Config> {
710
702
  }
711
703
  ```
712
704
 
713
- ---
714
705
 
715
706
  ## See Also
716
707
 
@@ -102,7 +102,6 @@ export class UserService extends BaseService {
102
102
 
103
103
  By adhering to this pattern, you keep your code organized, testable, and maintainable. You can easily test `UserService` by providing a mock `UserRepository` without needing a real database connection.
104
104
 
105
- ---
106
105
 
107
106
  ## See Also
108
107