@onroad/core 4.0.0-alpha.1 → 4.0.0-alpha.2

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 (2) hide show
  1. package/README.md +99 -1
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > TypeScript backend framework for multi-tenant Express APIs — DI Container, Filter Chain, EventBus, Provider Pattern.
4
4
 
5
- **v4.0.0-alpha.0** — Full TypeScript rewrite of the onRoad framework.
5
+ **v4.0.0-alpha.1** — Full TypeScript rewrite of the onRoad framework.
6
6
 
7
7
  ---
8
8
 
@@ -27,6 +27,7 @@
27
27
  - [Graceful Shutdown](#graceful-shutdown)
28
28
  - [Testing](#testing)
29
29
  - [Subpath Exports](#subpath-exports)
30
+ - [Known Pitfalls](#known-pitfalls)
30
31
 
31
32
  ---
32
33
 
@@ -665,6 +666,103 @@ import { StorageProvider } from "@onroad/core/storage" // Storag
665
666
 
666
667
  ---
667
668
 
669
+ ## Known Pitfalls
670
+
671
+ ### Controller base path must not be empty
672
+
673
+ Always pass `/` or a non-empty path to `@Controller()`. An empty string is falsy and previously caused the route guard to **silently drop all routes** of that controller.
674
+
675
+ ```ts
676
+ // ❌ Wrong — all routes silently skipped
677
+ @Controller("")
678
+ class UserController extends AbstractController<UserService> { ... }
679
+
680
+ // ✅ Correct — root-level routes
681
+ @Controller("/")
682
+ class UserController extends AbstractController<UserService> { ... }
683
+
684
+ // ✅ Correct — prefixed routes
685
+ @Controller("/users")
686
+ class UserController extends AbstractController<UserService> { ... }
687
+ ```
688
+
689
+ > Since **v4.0.0-alpha.1** the path guard was tightened (`basePath === undefined || basePath === null`) and paths are normalized (`//auth` → `/auth`), so `@Controller("/")` + route `"/auth"` resolves correctly to `/auth` instead of `//auth`.
690
+
691
+ ---
692
+
693
+ ### Circular service dependencies
694
+
695
+ When two services reference each other (e.g. `UserService ↔ CompanyService`), eager instantiation inside `configDependencies()` causes a `RangeError: Maximum call stack size exceeded` at startup because the constructors recurse infinitely.
696
+
697
+ ```ts
698
+ // ❌ Wrong — infinite recursion on startup
699
+ @Service()
700
+ class UserService extends AbstractService<UserRepository> {
701
+ companySVC!: CompanyService
702
+
703
+ configDependencies() {
704
+ this.companySVC = new CompanyService() // → CompanyService() → new UserService() → ...
705
+ }
706
+ }
707
+ ```
708
+
709
+ Fix: replace the eager field with a **lazy getter**. The getter body only runs on first access, never during construction, which breaks the cycle:
710
+
711
+ ```ts
712
+ // ✅ Correct — lazy getter breaks the circular chain
713
+ @Service()
714
+ class UserService extends AbstractService<UserRepository> {
715
+ private _companySVC?: CompanyService
716
+
717
+ protected get companySVC(): CompanyService {
718
+ if (!this._companySVC) this._companySVC = new CompanyService()
719
+ return this._companySVC
720
+ }
721
+ }
722
+
723
+ @Service()
724
+ class CompanyService extends AbstractService<CompanyRepository> {
725
+ private _userSVC?: UserService
726
+
727
+ protected get userSVC(): UserService {
728
+ if (!this._userSVC) this._userSVC = new UserService()
729
+ return this._userSVC
730
+ }
731
+ }
732
+ ```
733
+
734
+ > **Why it was masked before**: the `if (!basePath)` bug meant controllers (and therefore services) were never instantiated during `buildServer()`. The circular dep crash only surfaced after fixing the route guard.
735
+
736
+ ---
737
+
738
+ ### Overriding `fullAssociation` in Repositories
739
+
740
+ When using `@onroad/core`, the `@Repository` decorator automatically builds relations and injects the `fullAssociation` property populated with the appropriate Sequelize Models. Manually overriding this property in your repository class using raw ES classes will cause crashes during runtime (e.g., `TypeError: include.model.getTableName is not a function`) because Sequelize expects internal model objects, not raw classes.
741
+
742
+ ```ts
743
+ // ❌ Wrong — overriding fullAssociation with raw ES classes crashes Sequelize
744
+ @Repository({ entity: User })
745
+ export class UserRepository extends BaseRepository<User> {
746
+ readonly fullAssociation = [{ model: FilterPref, as: "filters" }]
747
+ }
748
+ ```
749
+
750
+ **Fix:** Remove the hardcoded override. If you are extending a custom `BaseRepository` and need TypeScript to recognize the property without emitting an overriding value in the compiled JavaScript, use the `declare` modifier:
751
+
752
+ ```ts
753
+ // ✅ Correct — Let the decorator inject the value and use `declare` for typings
754
+ export abstract class BaseRepository<TEntity = unknown> extends SequelizeRepository<TEntity> {
755
+ declare readonly fullAssociation: any[] // Tells TS it exists without overriding it
756
+ }
757
+
758
+ @Repository({ entity: User })
759
+ export class UserRepository extends BaseRepository<User> {
760
+ // No fullAssociation override here!
761
+ }
762
+ ```
763
+
764
+ ---
765
+
668
766
  ## License
669
767
 
670
768
  UNLICENSED — HashCodeTI-Brasil
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@onroad/core",
3
- "version": "4.0.0-alpha.1",
4
- "description": "TypeScript backend framework \u2014 DI Container, Filter Chain, EventBus, Provider Pattern",
3
+ "version": "4.0.0-alpha.2",
4
+ "description": "TypeScript backend framework DI Container, Filter Chain, EventBus, Provider Pattern",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",