@venizia/ignis-boot 0.0.4-0 → 0.0.4

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 (54) hide show
  1. package/README.md +184 -117
  2. package/dist/cjs/base/base-artifact-booter.d.ts.map +1 -1
  3. package/dist/cjs/base/base-artifact-booter.js +2 -8
  4. package/dist/cjs/base/base-artifact-booter.js.map +1 -1
  5. package/dist/cjs/booters/controller.booter.d.ts.map +1 -1
  6. package/dist/cjs/booters/controller.booter.js +0 -3
  7. package/dist/cjs/booters/controller.booter.js.map +1 -1
  8. package/dist/cjs/booters/datasource.booter.d.ts.map +1 -1
  9. package/dist/cjs/booters/datasource.booter.js +0 -3
  10. package/dist/cjs/booters/datasource.booter.js.map +1 -1
  11. package/dist/cjs/booters/repository.booter.d.ts.map +1 -1
  12. package/dist/cjs/booters/repository.booter.js +0 -3
  13. package/dist/cjs/booters/repository.booter.js.map +1 -1
  14. package/dist/cjs/booters/service.booter.d.ts.map +1 -1
  15. package/dist/cjs/booters/service.booter.js +0 -3
  16. package/dist/cjs/booters/service.booter.js.map +1 -1
  17. package/dist/cjs/bootstrapper.d.ts +0 -8
  18. package/dist/cjs/bootstrapper.d.ts.map +1 -1
  19. package/dist/cjs/bootstrapper.js +0 -12
  20. package/dist/cjs/bootstrapper.js.map +1 -1
  21. package/dist/cjs/common/types.d.ts +0 -8
  22. package/dist/cjs/common/types.d.ts.map +1 -1
  23. package/dist/cjs/common/types.js.map +1 -1
  24. package/dist/cjs/utilities/boot.utility.d.ts +3 -20
  25. package/dist/cjs/utilities/boot.utility.d.ts.map +1 -1
  26. package/dist/cjs/utilities/boot.utility.js +3 -20
  27. package/dist/cjs/utilities/boot.utility.js.map +1 -1
  28. package/dist/esm/base/base-artifact-booter.d.ts.map +1 -1
  29. package/dist/esm/base/base-artifact-booter.js +2 -8
  30. package/dist/esm/base/base-artifact-booter.js.map +1 -1
  31. package/dist/esm/booters/controller.booter.d.ts.map +1 -1
  32. package/dist/esm/booters/controller.booter.js +0 -3
  33. package/dist/esm/booters/controller.booter.js.map +1 -1
  34. package/dist/esm/booters/datasource.booter.d.ts.map +1 -1
  35. package/dist/esm/booters/datasource.booter.js +0 -3
  36. package/dist/esm/booters/datasource.booter.js.map +1 -1
  37. package/dist/esm/booters/repository.booter.d.ts.map +1 -1
  38. package/dist/esm/booters/repository.booter.js +0 -3
  39. package/dist/esm/booters/repository.booter.js.map +1 -1
  40. package/dist/esm/booters/service.booter.d.ts.map +1 -1
  41. package/dist/esm/booters/service.booter.js +0 -3
  42. package/dist/esm/booters/service.booter.js.map +1 -1
  43. package/dist/esm/bootstrapper.d.ts +0 -8
  44. package/dist/esm/bootstrapper.d.ts.map +1 -1
  45. package/dist/esm/bootstrapper.js +0 -12
  46. package/dist/esm/bootstrapper.js.map +1 -1
  47. package/dist/esm/common/types.d.ts +0 -8
  48. package/dist/esm/common/types.d.ts.map +1 -1
  49. package/dist/esm/common/types.js.map +1 -1
  50. package/dist/esm/utilities/boot.utility.d.ts +3 -20
  51. package/dist/esm/utilities/boot.utility.d.ts.map +1 -1
  52. package/dist/esm/utilities/boot.utility.js +3 -20
  53. package/dist/esm/utilities/boot.utility.js.map +1 -1
  54. package/package.json +2 -2
package/README.md CHANGED
@@ -1,12 +1,28 @@
1
- # @venizia/ignis-boot
1
+ <div align="center">
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@venizia/ignis-boot.svg)](https://www.npmjs.com/package/@venizia/ignis-boot)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
3
+ # :fire: IGNIS - @venizia/ignis-boot
6
4
 
7
- Convention-based **auto-discovery and bootstrapping** system for the [Ignis Framework](https://github.com/VENIZIA-AI/ignis). Discovers artifact files (controllers, services, repositories, datasources) by glob patterns and registers them into the IoC container during application startup.
5
+ **Convention-based auto-discovery and bootstrapping for the Ignis Framework**
8
6
 
9
- Inspired by [LoopBack 4's boot system](https://loopback.io/doc/en/lb4/Booting-an-Application.html), this package provides a structured, three-phase lifecycle for discovering and loading application artifacts -- giving you convention over configuration without sacrificing control.
7
+ [![npm](https://img.shields.io/npm/v/@venizia/ignis-boot.svg?style=flat-square&color=cb3837)](https://www.npmjs.com/package/@venizia/ignis-boot)
8
+ [![License](https://img.shields.io/badge/License-MIT-3DA639.svg?style=flat-square)](https://opensource.org/licenses/MIT)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6.svg?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
10
+
11
+ Discovers artifact files (controllers, services, repositories, datasources) by glob patterns and registers them into the IoC container during application startup. Three-phase lifecycle (configure, discover, load) with the Template Method pattern. Inspired by [LoopBack 4's boot system](https://loopback.io/doc/en/lb4/Booting-an-Application.html).
12
+
13
+ [Installation](#installation) &#8226; [Quick Start](#quick-start) &#8226; [API Reference](#core-concepts) &#8226; [Documentation](https://venizia-ai.github.io/ignis)
14
+
15
+ </div>
16
+
17
+ ## Highlights
18
+
19
+ | | Feature | |
20
+ | :---: | :--- | :--- |
21
+ | **1** | **Three-Phase Lifecycle** | Configure, discover, load -- structured and predictable |
22
+ | **2** | **Convention Over Configuration** | Default dirs and extensions just work out of the box |
23
+ | **3** | **4 Built-in Booters** | Controllers, services, repositories, and datasources |
24
+ | **4** | **Template Method Pattern** | Extend `BaseArtifactBooter` for custom artifact types |
25
+ | **5** | **Performance Timing** | Built-in `performance.now()` boot report |
10
26
 
11
27
  ---
12
28
 
@@ -188,12 +204,12 @@ The base class handles all the common logic (option merging, glob execution, fil
188
204
 
189
205
  The package ships with four built-in booters covering the most common artifact types:
190
206
 
191
- | Booter | Default Directory | Default Extension | Namespace | Scope | Binding Key Example |
192
- |---|---|---|---|---|---|
193
- | `ControllerBooter` | `controllers/` | `.controller.js` | `controllers` | transient | `controllers.UserController` |
194
- | `ServiceBooter` | `services/` | `.service.js` | `services` | transient | `services.AuthService` |
195
- | `RepositoryBooter` | `repositories/` | `.repository.js` | `repositories` | transient | `repositories.UserRepository` |
196
- | `DatasourceBooter` | `datasources/` | `.datasource.js` | `datasources` | **singleton** | `datasources.PostgresDataSource` |
207
+ | Booter | Default Directory | Default Extension | Namespace | Scope | Binding Key Example |
208
+ | -------------------- | ----------------- | ------------------- | -------------- | ------------- | -------------------------------- |
209
+ | `ControllerBooter` | `controllers/` | `.controller.js` | `controllers` | transient | `controllers.UserController` |
210
+ | `ServiceBooter` | `services/` | `.service.js` | `services` | transient | `services.AuthService` |
211
+ | `RepositoryBooter` | `repositories/` | `.repository.js` | `repositories` | transient | `repositories.UserRepository` |
212
+ | `DatasourceBooter` | `datasources/` | `.datasource.js` | `datasources` | **singleton** | `datasources.PostgresDataSource` |
197
213
 
198
214
  **Why are datasources singletons?** Datasources manage connection pools and shared resources (database connections, Redis clients, etc.). Creating new instances per injection would leak connections and defeat pool sharing. All other artifact types default to transient scope.
199
215
 
@@ -292,14 +308,14 @@ class MyApp extends BootMixin(Container) {
292
308
 
293
309
  **What it registers in the constructor:**
294
310
 
295
- | Binding Key | Value | Tags | Scope |
296
- |---|---|---|---|
297
- | `@app/boot-options` | User's `bootOptions` object | -- | -- |
298
- | `booter.DatasourceBooter` | `DatasourceBooter` class | `booter` | transient |
299
- | `booter.RepositoryBooter` | `RepositoryBooter` class | `booter` | transient |
300
- | `booter.ServiceBooter` | `ServiceBooter` class | `booter` | transient |
301
- | `booter.ControllerBooter` | `ControllerBooter` class | `booter` | transient |
302
- | `bootstrapper` | `Bootstrapper` class | -- | singleton |
311
+ | Binding Key | Value | Tags | Scope |
312
+ | --------------------------- | ----------------------- | -------- | --------- |
313
+ | `@app/boot-options` | User's `bootOptions` | -- | -- |
314
+ | `booter.DatasourceBooter` | `DatasourceBooter` class | `booter` | transient |
315
+ | `booter.RepositoryBooter` | `RepositoryBooter` class | `booter` | transient |
316
+ | `booter.ServiceBooter` | `ServiceBooter` class | `booter` | transient |
317
+ | `booter.ControllerBooter` | `ControllerBooter` class | `booter` | transient |
318
+ | `bootstrapper` | `Bootstrapper` class | -- | singleton |
303
319
 
304
320
  **What it adds to the class:**
305
321
 
@@ -363,18 +379,19 @@ interface IArtifactOptions {
363
379
  }
364
380
  ```
365
381
 
366
- | Option | Type | Default | Description |
367
- |---|---|---|---|
368
- | `dirs` | `string[]` | Booter-specific | Directories to scan, relative to project root |
369
- | `extensions` | `string[]` | Booter-specific | File extension patterns to match |
370
- | `isNested` | `boolean` | `true` | Whether to recurse into subdirectories |
371
- | `glob` | `string` | `undefined` | Custom glob pattern; if set, `dirs` and `extensions` are ignored |
382
+ | Option | Type | Default | Description |
383
+ | ------------ | ---------- | ---------------- | --------------------------------------------------------------------- |
384
+ | `dirs` | `string[]` | Booter-specific | Directories to scan, relative to project root |
385
+ | `extensions` | `string[]` | Booter-specific | File extension patterns to match |
386
+ | `isNested` | `boolean` | `true` | Whether to recurse into subdirectories |
387
+ | `glob` | `string` | `undefined` | Custom glob pattern; if set, `dirs` and `extensions` are ignored |
372
388
 
373
389
  ### Pattern Generation
374
390
 
375
391
  When no custom `glob` is provided, `BaseArtifactBooter.getPattern()` builds a glob pattern from `dirs`, `extensions`, and `isNested`:
376
392
 
377
393
  **Single directory, single extension:**
394
+
378
395
  ```
379
396
  dirs: ['repositories']
380
397
  extensions: ['.repository.js']
@@ -384,6 +401,7 @@ Result: repositories/{**/*,*}.repository.js
384
401
  ```
385
402
 
386
403
  **Multiple directories or extensions:**
404
+
387
405
  ```
388
406
  dirs: ['dir1', 'dir2']
389
407
  extensions: ['.ext1.js', '.ext2.js']
@@ -393,6 +411,7 @@ Result: {dir1,dir2}/{**/*,*}.{ext1.js,ext2.js}
393
411
  ```
394
412
 
395
413
  **Non-nested (single level only):**
414
+
396
415
  ```
397
416
  dirs: ['controllers']
398
417
  extensions: ['.controller.js']
@@ -402,6 +421,7 @@ Result: controllers/*.controller.js
402
421
  ```
403
422
 
404
423
  **Custom glob (overrides everything):**
424
+
405
425
  ```
406
426
  glob: 'custom/glob/pattern/**/*.js'
407
427
 
@@ -535,13 +555,13 @@ The `Bootstrapper` returns an `IBootReport` object. The current implementation r
535
555
 
536
556
  After boot completes, the IoC container holds:
537
557
 
538
- | Binding Key | Class | Scope | Tags |
539
- |---|---|---|---|
540
- | `datasources.PostgresDataSource` | `PostgresDataSource` | singleton | `datasources` |
541
- | `repositories.UserRepository` | `UserRepository` | transient | `repositories` |
542
- | `services.AuthService` | `AuthService` | transient | `services` |
543
- | `controllers.UserController` | `UserController` | transient | `controllers` |
544
- | `controllers.AdminController` | `AdminController` | transient | `controllers` |
558
+ | Binding Key | Class | Scope | Tags |
559
+ | ---------------------------------- | -------------------- | --------- | ------------- |
560
+ | `datasources.PostgresDataSource` | `PostgresDataSource` | singleton | `datasources` |
561
+ | `repositories.UserRepository` | `UserRepository` | transient | `repositories`|
562
+ | `services.AuthService` | `AuthService` | transient | `services` |
563
+ | `controllers.UserController` | `UserController` | transient | `controllers` |
564
+ | `controllers.AdminController` | `AdminController` | transient | `controllers` |
545
565
 
546
566
  These can now be resolved anywhere via `@inject({ key: 'services.AuthService' })` or `app.get({ key: 'controllers.UserController' })`.
547
567
 
@@ -567,12 +587,12 @@ export abstract class BaseArtifactBooter extends BaseHelper implements IBooter {
567
587
  }
568
588
  ```
569
589
 
570
- | Property | Type | Initial Value | Description |
571
- |---|---|---|---|
572
- | `root` | `string` | `''` | Absolute path to the project root directory. Set from constructor `opts.root`. Glob patterns are resolved relative to this path. |
573
- | `artifactOptions` | `IArtifactOptions` | `{}` | The merged configuration after `configure()` runs. Before `configure()`, holds whatever the user passed (or `{}`). After `configure()`, has all fields populated with defaults. |
574
- | `discoveredFiles` | `string[]` | `[]` | Array of absolute file paths populated by `discover()`. Reset to `[]` at the start of each `discover()` call. |
575
- | `loadedClasses` | `TClass<any>[]` | `[]` | Array of class constructors extracted from discovered files by `load()`. Reset to `[]` at the start of each `load()` call. |
590
+ | Property | Type | Initial Value | Description |
591
+ | ----------------- | ----------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
592
+ | `root` | `string` | `''` | Absolute path to the project root directory. Set from constructor `opts.root`. Glob patterns are resolved relative to this path. |
593
+ | `artifactOptions` | `IArtifactOptions` | `{}` | The merged configuration after `configure()` runs. Before `configure()`, holds whatever the user passed (or `{}`). After `configure()`, has all fields populated with defaults. |
594
+ | `discoveredFiles` | `string[]` | `[]` | Array of absolute file paths populated by `discover()`. Reset to `[]` at the start of each `discover()` call. |
595
+ | `loadedClasses` | `TClass<any>[]` | `[]` | Array of class constructors extracted from discovered files by `load()`. Reset to `[]` at the start of each `load()` call. |
576
596
 
577
597
  ### BaseArtifactBooter Constructor
578
598
 
@@ -587,11 +607,11 @@ constructor(opts: IBooterOptions) {
587
607
 
588
608
  The constructor receives an `IBooterOptions` object with three fields:
589
609
 
590
- | Field | Type | Description |
591
- |---|---|---|
592
- | `scope` | `string` | Logger scope name, typically `BooterClassName.name` (e.g., `"ControllerBooter"`) |
593
- | `root` | `string` | Absolute path to project root (injected as `@app/project_root`) |
594
- | `artifactOptions` | `IArtifactOptions` | User-provided options from `bootOptions.controllers` (or whichever artifact key) |
610
+ | Field | Type | Description |
611
+ | ------------------ | ------------------ | ---------------------------------------------------------------------------------------------- |
612
+ | `scope` | `string` | Logger scope name, typically `BooterClassName.name` (e.g., `"ControllerBooter"`) |
613
+ | `root` | `string` | Absolute path to project root (injected as `@app/project_root`) |
614
+ | `artifactOptions` | `IArtifactOptions` | User-provided options from `bootOptions.controllers` (or whichever artifact key) |
595
615
 
596
616
  The `super({ scope })` call initializes the `BaseHelper` which sets up scoped logging via `LoggerFactory.getLogger([scope])`.
597
617
 
@@ -741,17 +761,20 @@ protected getPattern(): string {
741
761
  ### Single Dir + Single Extension
742
762
 
743
763
  **Input:**
764
+
744
765
  ```typescript
745
766
  { dirs: ['controllers'], extensions: ['.controller.js'], isNested: true }
746
767
  ```
747
768
 
748
769
  **Processing:**
770
+
749
771
  - `dirs.join(',')` => `"controllers"`
750
772
  - Extensions: `.controller.js` => strip dot => `"controller.js"`
751
773
  - `dirs.length === 1 && extensions.length === 1` => simple format
752
774
  - `nested = '{**/*,*}'`
753
775
 
754
776
  **Output:**
777
+
755
778
  ```
756
779
  controllers/{**/*,*}.controller.js
757
780
  ```
@@ -763,16 +786,19 @@ controllers/{**/*,*}.controller.js
763
786
  ### Multiple Dirs + Multiple Extensions
764
787
 
765
788
  **Input:**
789
+
766
790
  ```typescript
767
791
  { dirs: ['private-controllers', 'public-controllers'], extensions: ['.controller.js', '.ctrl.js'], isNested: true }
768
792
  ```
769
793
 
770
794
  **Processing:**
795
+
771
796
  - `dirs.join(',')` => `"private-controllers,public-controllers"`
772
797
  - Extensions: `.controller.js` => `controller.js`, `.ctrl.js` => `ctrl.js` => `"controller.js,ctrl.js"`
773
798
  - `dirs.length === 2 || extensions.length === 2` => brace expansion format
774
799
 
775
800
  **Output:**
801
+
776
802
  ```
777
803
  {private-controllers,public-controllers}/{**/*,*}.{controller.js,ctrl.js}
778
804
  ```
@@ -782,14 +808,17 @@ controllers/{**/*,*}.controller.js
782
808
  ### Multiple Dirs + Single Extension
783
809
 
784
810
  **Input:**
811
+
785
812
  ```typescript
786
813
  { dirs: ['api', 'admin'], extensions: ['.controller.js'], isNested: true }
787
814
  ```
788
815
 
789
816
  **Processing:**
817
+
790
818
  - `dirs.length === 2` => brace expansion triggered
791
819
 
792
820
  **Output:**
821
+
793
822
  ```
794
823
  {api,admin}/{**/*,*}.controller.js
795
824
  ```
@@ -797,14 +826,17 @@ controllers/{**/*,*}.controller.js
797
826
  ### Single Dir + Multiple Extensions
798
827
 
799
828
  **Input:**
829
+
800
830
  ```typescript
801
831
  { dirs: ['services'], extensions: ['.service.js', '.svc.js'], isNested: true }
802
832
  ```
803
833
 
804
834
  **Processing:**
835
+
805
836
  - `extensions.length === 2` => brace expansion triggered
806
837
 
807
838
  **Output:**
839
+
808
840
  ```
809
841
  {services}/{**/*,*}.{service.js,svc.js}
810
842
  ```
@@ -812,33 +844,43 @@ controllers/{**/*,*}.controller.js
812
844
  ### Nested vs Non-Nested
813
845
 
814
846
  **Nested (default, `isNested: true`):**
847
+
815
848
  ```typescript
816
849
  { dirs: ['repositories'], extensions: ['.repository.js'], isNested: true }
817
850
  ```
851
+
818
852
  **Output:**
853
+
819
854
  ```
820
855
  repositories/{**/*,*}.repository.js
821
856
  ```
857
+
822
858
  The `{**/*,*}` alternation ensures files are matched at any depth, including the root of the directory.
823
859
 
824
860
  **Non-nested (`isNested: false`):**
861
+
825
862
  ```typescript
826
863
  { dirs: ['repositories'], extensions: ['.repository.js'], isNested: false }
827
864
  ```
865
+
828
866
  **Output:**
867
+
829
868
  ```
830
869
  repositories/*.repository.js
831
870
  ```
871
+
832
872
  Only matches files directly inside `repositories/` -- no subdirectory scanning.
833
873
 
834
874
  ### Custom Glob Override
835
875
 
836
876
  **Input:**
877
+
837
878
  ```typescript
838
879
  { dirs: ['ignored'], extensions: ['.ignored.js'], glob: 'modules/**/handlers/*.handler.js' }
839
880
  ```
840
881
 
841
882
  **Output:**
883
+
842
884
  ```
843
885
  modules/**/handlers/*.handler.js
844
886
  ```
@@ -855,26 +897,26 @@ const exts = this.artifactOptions.extensions
855
897
  .join(',');
856
898
  ```
857
899
 
858
- | Input Extension | After Stripping | In Pattern |
859
- |---|---|---|
860
- | `.controller.js` | `controller.js` | `*.controller.js` |
861
- | `.service.js` | `service.js` | `*.service.js` |
862
- | `handler.js` | `handler.js` (no dot to strip) | `*.handler.js` |
863
- | `.my.custom.ext.js` | `my.custom.ext.js` | `*.my.custom.ext.js` |
900
+ | Input Extension | After Stripping | In Pattern |
901
+ | --------------------- | --------------------- | ----------------------- |
902
+ | `.controller.js` | `controller.js` | `*.controller.js` |
903
+ | `.service.js` | `service.js` | `*.service.js` |
904
+ | `handler.js` | `handler.js` (no dot) | `*.handler.js` |
905
+ | `.my.custom.ext.js` | `my.custom.ext.js` | `*.my.custom.ext.js` |
864
906
 
865
907
  This means files need a dot before the extension in their filename. For example, the pattern `*.controller.js` matches `user.controller.js` but does NOT match `usercontrollerjs` or `user-controller.js`.
866
908
 
867
909
  ### Pattern Summary Table
868
910
 
869
- | dirs | extensions | isNested | glob | Generated Pattern |
870
- |---|---|---|---|---|
871
- | `['controllers']` | `['.controller.js']` | `true` | -- | `controllers/{**/*,*}.controller.js` |
872
- | `['controllers']` | `['.controller.js']` | `false` | -- | `controllers/*.controller.js` |
873
- | `['api', 'admin']` | `['.controller.js']` | `true` | -- | `{api,admin}/{**/*,*}.controller.js` |
874
- | `['services']` | `['.service.js', '.svc.js']` | `true` | -- | `{services}/{**/*,*}.{service.js,svc.js}` |
875
- | `['a', 'b']` | `['.x.js', '.y.js']` | `true` | -- | `{a,b}/{**/*,*}.{x.js,y.js}` |
876
- | `['a', 'b']` | `['.x.js', '.y.js']` | `false` | -- | `{a,b}/*.{x.js,y.js}` |
877
- | any | any | any | `'custom/**/*.js'` | `custom/**/*.js` |
911
+ | dirs | extensions | isNested | glob | Generated Pattern |
912
+ | --------------------- | ---------------------------------- | -------- | ------------------- | ------------------------------------------------------ |
913
+ | `['controllers']` | `['.controller.js']` | `true` | -- | `controllers/{**/*,*}.controller.js` |
914
+ | `['controllers']` | `['.controller.js']` | `false` | -- | `controllers/*.controller.js` |
915
+ | `['api', 'admin']` | `['.controller.js']` | `true` | -- | `{api,admin}/{**/*,*}.controller.js` |
916
+ | `['services']` | `['.service.js', '.svc.js']` | `true` | -- | `{services}/{**/*,*}.{service.js,svc.js}` |
917
+ | `['a', 'b']` | `['.x.js', '.y.js']` | `true` | -- | `{a,b}/{**/*,*}.{x.js,y.js}` |
918
+ | `['a', 'b']` | `['.x.js', '.y.js']` | `false` | -- | `{a,b}/*.{x.js,y.js}` |
919
+ | any | any | any | `'custom/**/*.js'` | `custom/**/*.js` |
878
920
 
879
921
  ---
880
922
 
@@ -882,11 +924,11 @@ This means files need a dot before the extension in their filename. For example,
882
924
 
883
925
  All four built-in booters share the same structure. They differ only in their default directories, default extensions, binding namespace, and binding scope. Each one receives three constructor-injected values:
884
926
 
885
- | Injection Key | Type | Description |
886
- |---|---|---|
887
- | `@app/project_root` | `string` | Absolute path to the project's build output directory |
888
- | `@app/instance` | `IApplication` | The application container instance (for binding classes) |
889
- | `@app/boot-options` | `IBootOptions` | The user's boot options object |
927
+ | Injection Key | Type | Description |
928
+ | -------------------- | -------------- | -------------------------------------------------------- |
929
+ | `@app/project_root` | `string` | Absolute path to the project's build output directory |
930
+ | `@app/instance` | `IApplication` | The application container instance (for binding classes) |
931
+ | `@app/boot-options` | `IBootOptions` | The user's boot options object |
890
932
 
891
933
  ### ControllerBooter
892
934
 
@@ -920,14 +962,14 @@ export class ControllerBooter extends BaseArtifactBooter {
920
962
  }
921
963
  ```
922
964
 
923
- | Property | Value |
924
- |---|---|
925
- | Default dirs | `['controllers']` |
926
- | Default extensions | `['.controller.js']` |
927
- | Namespace | `controllers` |
928
- | Binding key format | `controllers.{ClassName}` (e.g., `controllers.UserController`) |
929
- | Scope | transient (default) |
930
- | Tags | `controllers` |
965
+ | Property | Value |
966
+ | -------------------- | -------------------------------------------------------------- |
967
+ | Default dirs | `['controllers']` |
968
+ | Default extensions | `['.controller.js']` |
969
+ | Namespace | `controllers` |
970
+ | Binding key format | `controllers.{ClassName}` (e.g., `controllers.UserController`) |
971
+ | Scope | transient (default) |
972
+ | Tags | `controllers` |
931
973
 
932
974
  ### ServiceBooter
933
975
 
@@ -961,14 +1003,14 @@ export class ServiceBooter extends BaseArtifactBooter {
961
1003
  }
962
1004
  ```
963
1005
 
964
- | Property | Value |
965
- |---|---|
966
- | Default dirs | `['services']` |
967
- | Default extensions | `['.service.js']` |
968
- | Namespace | `services` |
969
- | Binding key format | `services.{ClassName}` (e.g., `services.AuthService`) |
970
- | Scope | transient (default) |
971
- | Tags | `services` |
1006
+ | Property | Value |
1007
+ | -------------------- | ---------------------------------------------------------- |
1008
+ | Default dirs | `['services']` |
1009
+ | Default extensions | `['.service.js']` |
1010
+ | Namespace | `services` |
1011
+ | Binding key format | `services.{ClassName}` (e.g., `services.AuthService`) |
1012
+ | Scope | transient (default) |
1013
+ | Tags | `services` |
972
1014
 
973
1015
  ### RepositoryBooter
974
1016
 
@@ -1002,14 +1044,14 @@ export class RepositoryBooter extends BaseArtifactBooter {
1002
1044
  }
1003
1045
  ```
1004
1046
 
1005
- | Property | Value |
1006
- |---|---|
1007
- | Default dirs | `['repositories']` |
1008
- | Default extensions | `['.repository.js']` |
1009
- | Namespace | `repositories` |
1010
- | Binding key format | `repositories.{ClassName}` (e.g., `repositories.UserRepository`) |
1011
- | Scope | transient (default) |
1012
- | Tags | `repositories` |
1047
+ | Property | Value |
1048
+ | -------------------- | ---------------------------------------------------------------------- |
1049
+ | Default dirs | `['repositories']` |
1050
+ | Default extensions | `['.repository.js']` |
1051
+ | Namespace | `repositories` |
1052
+ | Binding key format | `repositories.{ClassName}` (e.g., `repositories.UserRepository`) |
1053
+ | Scope | transient (default) |
1054
+ | Tags | `repositories` |
1013
1055
 
1014
1056
  ### DatasourceBooter
1015
1057
 
@@ -1043,14 +1085,14 @@ export class DatasourceBooter extends BaseArtifactBooter {
1043
1085
  }
1044
1086
  ```
1045
1087
 
1046
- | Property | Value |
1047
- |---|---|
1048
- | Default dirs | `['datasources']` |
1049
- | Default extensions | `['.datasource.js']` |
1050
- | Namespace | `datasources` |
1051
- | Binding key format | `datasources.{ClassName}` (e.g., `datasources.PostgresDataSource`) |
1052
- | Scope | **singleton** |
1053
- | Tags | `datasources` |
1088
+ | Property | Value |
1089
+ | -------------------- | -------------------------------------------------------------------------- |
1090
+ | Default dirs | `['datasources']` |
1091
+ | Default extensions | `['.datasource.js']` |
1092
+ | Namespace | `datasources` |
1093
+ | Binding key format | `datasources.{ClassName}` (e.g., `datasources.PostgresDataSource`) |
1094
+ | Scope | **singleton** |
1095
+ | Tags | `datasources` |
1054
1096
 
1055
1097
  ### Why Are Datasources Singletons?
1056
1098
 
@@ -1120,12 +1162,15 @@ export const BootMixin = <T extends TMixinTarget<Container>>(baseClass: T) => {
1120
1162
  When the `BootMixin` constructor runs, it performs exactly 6 binding registrations in this order:
1121
1163
 
1122
1164
  **1. Boot options binding:**
1165
+
1123
1166
  ```typescript
1124
1167
  this.bind({ key: '@app/boot-options' }).toValue(this.bootOptions ?? {});
1125
1168
  ```
1169
+
1126
1170
  Binds the user's `bootOptions` object (or `{}` if undefined) as a plain value. This is injected into every booter's constructor.
1127
1171
 
1128
1172
  **2-5. Booter class registrations (in dependency order):**
1173
+
1129
1174
  ```typescript
1130
1175
  this.bind({ key: 'booter.DatasourceBooter' }).toClass(DatasourceBooter).setTags('booter');
1131
1176
  this.bind({ key: 'booter.RepositoryBooter' }).toClass(RepositoryBooter).setTags('booter');
@@ -1134,6 +1179,7 @@ this.bind({ key: 'booter.ControllerBooter' }).toClass(ControllerBooter).setTags(
1134
1179
  ```
1135
1180
 
1136
1181
  Each booter is:
1182
+
1137
1183
  - Bound as a **class** (not an instance) -- the container instantiates it on resolution with constructor injection
1138
1184
  - Tagged with `'booter'` -- this is how the `Bootstrapper` discovers them via `findByTag({ tag: 'booter' })`
1139
1185
  - Registered in **dependency order** -- datasources before repositories before services before controllers
@@ -1141,9 +1187,11 @@ Each booter is:
1141
1187
  The registration order determines execution order within each phase. This ensures that during the LOAD phase, datasources are bound to the container before repositories try to resolve them.
1142
1188
 
1143
1189
  **6. Bootstrapper singleton:**
1190
+
1144
1191
  ```typescript
1145
1192
  this.bind({ key: 'bootstrapper' }).toClass(Bootstrapper).setScope(BindingScopes.SINGLETON);
1146
1193
  ```
1194
+
1147
1195
  The `Bootstrapper` is a singleton because it maintains internal state (the booter list, phase timings). Multiple `boot()` calls resolve the same `Bootstrapper` instance.
1148
1196
 
1149
1197
  ### boot() Method
@@ -1156,6 +1204,7 @@ boot(): Promise<IBootReport> {
1156
1204
  ```
1157
1205
 
1158
1206
  The `boot()` method:
1207
+
1159
1208
  1. Resolves the `Bootstrapper` singleton from the container (which triggers `@inject({ key: '@app/instance' })` constructor injection)
1160
1209
  2. Calls `bootstrapper.boot({})` with an empty options object, which means all phases run on all booters
1161
1210
  3. Returns the `IBootReport` promise
@@ -1209,6 +1258,7 @@ private async discoverBooters(): Promise<void> {
1209
1258
  ```
1210
1259
 
1211
1260
  This method:
1261
+
1212
1262
  1. Calls `this.application.findByTag({ tag: 'booter' })` which returns all `Binding` objects that have the `'booter'` tag
1213
1263
  2. For each binding, calls `binding.getValue(this.application)` which:
1214
1264
  - Instantiates the booter class (since booters are bound via `.toClass()`)
@@ -1300,11 +1350,13 @@ throw getError({
1300
1350
  ```
1301
1351
 
1302
1352
  Example error message:
1353
+
1303
1354
  ```
1304
1355
  [Bootstrapper][runPhase] Error during phase 'discover' on booter 'ControllerBooter': [discover] Failed to discover files using pattern: controllers/{**/*,*}.controller.js | Error: ENOENT: no such file or directory
1305
1356
  ```
1306
1357
 
1307
1358
  This multi-layer error wrapping makes it clear:
1359
+
1308
1360
  1. Which component threw (`Bootstrapper`)
1309
1361
  2. Which phase was running (`discover`)
1310
1362
  3. Which booter failed (`ControllerBooter`)
@@ -1471,6 +1523,7 @@ export class MiddlewareBooter extends BaseArtifactBooter {
1471
1523
  ```
1472
1524
 
1473
1525
  **Registration:**
1526
+
1474
1527
  ```typescript
1475
1528
  class MyApp extends BootMixin(Container) {
1476
1529
  bootOptions: IBootOptions = {
@@ -1526,6 +1579,7 @@ export class MigrationBooter extends BaseArtifactBooter {
1526
1579
  ```
1527
1580
 
1528
1581
  **Registration with custom extensions:**
1582
+
1529
1583
  ```typescript
1530
1584
  class MyApp extends BootMixin(Container) {
1531
1585
  bootOptions: IBootOptions = {
@@ -1615,6 +1669,7 @@ const bootOptions: IBootOptions = {
1615
1669
  },
1616
1670
  };
1617
1671
  ```
1672
+
1618
1673
  Generated pattern: `{api-controllers,admin-controllers}/{**/*,*}.controller.js`
1619
1674
 
1620
1675
  #### Override extensions only
@@ -1627,6 +1682,7 @@ const bootOptions: IBootOptions = {
1627
1682
  },
1628
1683
  };
1629
1684
  ```
1685
+
1630
1686
  Generated pattern: `{services}/{**/*,*}.{service.js,provider.js}`
1631
1687
 
1632
1688
  #### Disable nested scanning
@@ -1639,6 +1695,7 @@ const bootOptions: IBootOptions = {
1639
1695
  },
1640
1696
  };
1641
1697
  ```
1698
+
1642
1699
  Generated pattern: `repositories/*.repository.js`
1643
1700
 
1644
1701
  #### Full override with custom glob
@@ -1651,6 +1708,7 @@ const bootOptions: IBootOptions = {
1651
1708
  },
1652
1709
  };
1653
1710
  ```
1711
+
1654
1712
  Generated pattern: `config/datasources/*.datasource.js`
1655
1713
 
1656
1714
  #### Mixed overrides across artifact types
@@ -1813,6 +1871,7 @@ async load(): Promise<void> {
1813
1871
  ```
1814
1872
 
1815
1873
  **Debug output:**
1874
+
1816
1875
  ```
1817
1876
  [ServiceBooter][discover] Root: /app/dist | Using pattern: services/{**/*,*}.service.js | Discovered file: []
1818
1877
  [ServiceBooter][load] No files discovered to load.
@@ -1825,11 +1884,13 @@ This is common during early development when you may not yet have files for ever
1825
1884
  If a discovered file exports only non-class values (arrow functions, constants, objects, primitives), `loadClasses()` will filter them all out. The `loadedClasses` array will be empty, and `bind()` will execute but do nothing (it iterates an empty array).
1826
1885
 
1827
1886
  **Example file** (`non.repository.ts`):
1887
+
1828
1888
  ```typescript
1829
1889
  // LET THIS FILE BE EMPTY
1830
1890
  ```
1831
1891
 
1832
1892
  Or a file that exports only constants:
1893
+
1833
1894
  ```typescript
1834
1895
  export const CONFIG = { host: 'localhost' };
1835
1896
  export const helper = () => 'helper';
@@ -1842,6 +1903,7 @@ Both result in zero loaded classes -- no error, no binding.
1842
1903
  If your glob pattern is too broad (e.g., `**/*.js`), you may discover files that are not artifact classes. The `isClass()` check prevents non-class exports from being bound, but unexpected classes could still be registered.
1843
1904
 
1844
1905
  **Prevention strategies:**
1906
+
1845
1907
  - Use specific file extensions: `.controller.js`, `.service.js`
1846
1908
  - Use dedicated directories: `controllers/`, `services/`
1847
1909
  - Avoid overly broad custom globs
@@ -1993,14 +2055,17 @@ for (const binding of datasourceBindings) {
1993
2055
  For large codebases with many files, overly broad glob patterns can slow down the DISCOVER phase. Here are strategies to optimize:
1994
2056
 
1995
2057
  **1. Disable nested scanning when not needed:**
2058
+
1996
2059
  ```typescript
1997
2060
  repositories: {
1998
2061
  isNested: false, // Only scan the top-level directory
1999
2062
  }
2000
2063
  ```
2064
+
2001
2065
  This changes the pattern from `repositories/{**/*,*}.repository.js` to `repositories/*.repository.js`, which is significantly faster for directories with deep structures.
2002
2066
 
2003
2067
  **2. Use specific directory lists instead of nested scanning:**
2068
+
2004
2069
  ```typescript
2005
2070
  controllers: {
2006
2071
  dirs: ['controllers/api', 'controllers/admin'],
@@ -2010,6 +2075,7 @@ controllers: {
2010
2075
  ```
2011
2076
 
2012
2077
  **3. Use custom glob patterns for surgical targeting:**
2078
+
2013
2079
  ```typescript
2014
2080
  controllers: {
2015
2081
  glob: 'controllers/*.controller.js',
@@ -2065,12 +2131,12 @@ However, the standard Ignis workflow uses compiled output, so `.js` is the corre
2065
2131
 
2066
2132
  The convention follows the pattern: `{name}.{artifact-type}.{extension}`
2067
2133
 
2068
- | Artifact Type | Convention | Example |
2069
- |---|---|---|
2070
- | Controller | `{name}.controller.ts` | `user.controller.ts` |
2071
- | Service | `{name}.service.ts` | `auth.service.ts` |
2072
- | Repository | `{name}.repository.ts` | `user.repository.ts` |
2073
- | DataSource | `{name}.datasource.ts` | `postgres.datasource.ts` |
2134
+ | Artifact Type | Convention | Example |
2135
+ | ------------- | ------------------------ | -------------------------- |
2136
+ | Controller | `{name}.controller.ts` | `user.controller.ts` |
2137
+ | Service | `{name}.service.ts` | `auth.service.ts` |
2138
+ | Repository | `{name}.repository.ts` | `user.repository.ts` |
2139
+ | DataSource | `{name}.datasource.ts` | `postgres.datasource.ts` |
2074
2140
 
2075
2141
  These conventions are not enforced by the framework -- they are what the default extensions expect. You can use any naming scheme by overriding `extensions` or `glob` in your boot options.
2076
2142
 
@@ -2079,11 +2145,11 @@ These conventions are not enforced by the framework -- they are what the default
2079
2145
  The default directory names match the artifact types:
2080
2146
 
2081
2147
  | Artifact Type | Default Directory |
2082
- |---|---|
2083
- | Controllers | `controllers/` |
2084
- | Services | `services/` |
2085
- | Repositories | `repositories/` |
2086
- | DataSources | `datasources/` |
2148
+ | ------------- | ----------------- |
2149
+ | Controllers | `controllers/` |
2150
+ | Services | `services/` |
2151
+ | Repositories | `repositories/` |
2152
+ | DataSources | `datasources/` |
2087
2153
 
2088
2154
  These are relative to the project root (the `@app/project_root` binding). In a typical Ignis application built with `tsc`, the project root is the `dist/cjs/` directory.
2089
2155
 
@@ -2173,6 +2239,7 @@ const isClass: <T>(target: AnyType) => target is TClass<T>;
2173
2239
  ```
2174
2240
 
2175
2241
  **Implementation:**
2242
+
2176
2243
  ```typescript
2177
2244
  export const isClass = <T>(target: AnyType): target is TClass<T> => {
2178
2245
  return typeof target === 'function' && target.prototype !== undefined;
@@ -2419,16 +2486,16 @@ const BOOT_PHASES: TBootPhase[] = ['configure', 'discover', 'load'];
2419
2486
 
2420
2487
  ### DI Binding Keys Used by the Boot System
2421
2488
 
2422
- | Key | Bound By | Type | Description |
2423
- |---|---|---|---|
2424
- | `@app/project_root` | Application | `string` | Absolute path to project's build output root |
2425
- | `@app/instance` | Application | `IApplication` | The application container itself |
2426
- | `@app/boot-options` | BootMixin / BaseApplication | `IBootOptions` | User's boot configuration |
2427
- | `booter.DatasourceBooter` | BootMixin | `DatasourceBooter` class | Tagged `'booter'` |
2428
- | `booter.RepositoryBooter` | BootMixin | `RepositoryBooter` class | Tagged `'booter'` |
2429
- | `booter.ServiceBooter` | BootMixin | `ServiceBooter` class | Tagged `'booter'` |
2430
- | `booter.ControllerBooter` | BootMixin | `ControllerBooter` class | Tagged `'booter'` |
2431
- | `bootstrapper` | BootMixin / BaseApplication | `Bootstrapper` class | Singleton scope |
2489
+ | Key | Bound By | Type | Description |
2490
+ | --------------------------- | --------------------------- | ----------------------- | ------------------------------------------------ |
2491
+ | `@app/project_root` | Application | `string` | Absolute path to project's build output root |
2492
+ | `@app/instance` | Application | `IApplication` | The application container itself |
2493
+ | `@app/boot-options` | BootMixin / BaseApplication | `IBootOptions` | User's boot configuration |
2494
+ | `booter.DatasourceBooter` | BootMixin | `DatasourceBooter` class | Tagged `'booter'` |
2495
+ | `booter.RepositoryBooter` | BootMixin | `RepositoryBooter` class | Tagged `'booter'` |
2496
+ | `booter.ServiceBooter` | BootMixin | `ServiceBooter` class | Tagged `'booter'` |
2497
+ | `booter.ControllerBooter` | BootMixin | `ControllerBooter` class | Tagged `'booter'` |
2498
+ | `bootstrapper` | BootMixin / BaseApplication | `Bootstrapper` class | Singleton scope |
2432
2499
 
2433
2500
  Note: When used with `BaseApplication`, the booter keys follow the pattern `booters.{ClassName}` (using the `BindingNamespaces.BOOTERS` namespace) instead of `booter.{ClassName}`.
2434
2501