exisjs 0.5.10 → 0.5.11

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 (157) hide show
  1. package/dist/auth/index.d.ts +3 -1
  2. package/dist/auth/index.d.ts.map +1 -1
  3. package/dist/auth/index.js +4 -1
  4. package/dist/auth/index.js.map +1 -1
  5. package/dist/auth/oauth/core.d.ts +96 -0
  6. package/dist/auth/oauth/core.d.ts.map +1 -0
  7. package/dist/auth/oauth/core.js +203 -0
  8. package/dist/auth/oauth/core.js.map +1 -0
  9. package/dist/auth/oauth/index.d.ts +45 -0
  10. package/dist/auth/oauth/index.d.ts.map +1 -0
  11. package/dist/auth/oauth/index.js +64 -0
  12. package/dist/auth/oauth/index.js.map +1 -0
  13. package/dist/auth/oauth/middleware.d.ts +63 -0
  14. package/dist/auth/oauth/middleware.d.ts.map +1 -0
  15. package/dist/auth/oauth/middleware.js +164 -0
  16. package/dist/auth/oauth/middleware.js.map +1 -0
  17. package/dist/auth/oauth/providers/custom.d.ts +41 -0
  18. package/dist/auth/oauth/providers/custom.d.ts.map +1 -0
  19. package/dist/auth/oauth/providers/custom.js +204 -0
  20. package/dist/auth/oauth/providers/custom.js.map +1 -0
  21. package/dist/auth/oauth/providers/discord.d.ts +23 -0
  22. package/dist/auth/oauth/providers/discord.d.ts.map +1 -0
  23. package/dist/auth/oauth/providers/discord.js +130 -0
  24. package/dist/auth/oauth/providers/discord.js.map +1 -0
  25. package/dist/auth/oauth/providers/facebook.d.ts +28 -0
  26. package/dist/auth/oauth/providers/facebook.d.ts.map +1 -0
  27. package/dist/auth/oauth/providers/facebook.js +124 -0
  28. package/dist/auth/oauth/providers/facebook.js.map +1 -0
  29. package/dist/auth/oauth/providers/github.d.ts +25 -0
  30. package/dist/auth/oauth/providers/github.d.ts.map +1 -0
  31. package/dist/auth/oauth/providers/github.js +132 -0
  32. package/dist/auth/oauth/providers/github.js.map +1 -0
  33. package/dist/auth/oauth/providers/google.d.ts +23 -0
  34. package/dist/auth/oauth/providers/google.d.ts.map +1 -0
  35. package/dist/auth/oauth/providers/google.js +134 -0
  36. package/dist/auth/oauth/providers/google.js.map +1 -0
  37. package/dist/auth/oauth/providers/microsoft.d.ts +28 -0
  38. package/dist/auth/oauth/providers/microsoft.d.ts.map +1 -0
  39. package/dist/auth/oauth/providers/microsoft.js +136 -0
  40. package/dist/auth/oauth/providers/microsoft.js.map +1 -0
  41. package/dist/auth/oauth/types.d.ts +158 -0
  42. package/dist/auth/oauth/types.d.ts.map +1 -0
  43. package/dist/auth/oauth/types.js +4 -0
  44. package/dist/auth/oauth/types.js.map +1 -0
  45. package/dist/cli/commands/dev.d.ts.map +1 -1
  46. package/dist/cli/commands/dev.js +0 -1
  47. package/dist/cli/commands/dev.js.map +1 -1
  48. package/dist/database/adapters/mongodb.d.ts +58 -0
  49. package/dist/database/adapters/mongodb.d.ts.map +1 -0
  50. package/dist/database/adapters/mongodb.js +264 -0
  51. package/dist/database/adapters/mongodb.js.map +1 -0
  52. package/dist/database/adapters/mysql.d.ts +27 -0
  53. package/dist/database/adapters/mysql.d.ts.map +1 -0
  54. package/dist/database/adapters/mysql.js +188 -0
  55. package/dist/database/adapters/mysql.js.map +1 -0
  56. package/dist/database/adapters/postgres.d.ts +28 -0
  57. package/dist/database/adapters/postgres.d.ts.map +1 -0
  58. package/dist/database/adapters/postgres.js +174 -0
  59. package/dist/database/adapters/postgres.js.map +1 -0
  60. package/dist/database/adapters/sqlite.d.ts +27 -0
  61. package/dist/database/adapters/sqlite.d.ts.map +1 -0
  62. package/dist/database/adapters/sqlite.js +149 -0
  63. package/dist/database/adapters/sqlite.js.map +1 -0
  64. package/dist/database/index.d.ts +42 -0
  65. package/dist/database/index.d.ts.map +1 -0
  66. package/dist/database/index.js +58 -0
  67. package/dist/database/index.js.map +1 -0
  68. package/dist/database/manager.d.ts +114 -0
  69. package/dist/database/manager.d.ts.map +1 -0
  70. package/dist/database/manager.js +269 -0
  71. package/dist/database/manager.js.map +1 -0
  72. package/dist/database/migration.d.ts +57 -0
  73. package/dist/database/migration.d.ts.map +1 -0
  74. package/dist/database/migration.js +329 -0
  75. package/dist/database/migration.js.map +1 -0
  76. package/dist/database/query-builder.d.ts +89 -0
  77. package/dist/database/query-builder.d.ts.map +1 -0
  78. package/dist/database/query-builder.js +353 -0
  79. package/dist/database/query-builder.js.map +1 -0
  80. package/dist/database/types.d.ts +184 -0
  81. package/dist/database/types.d.ts.map +1 -0
  82. package/dist/database/types.js +4 -0
  83. package/dist/database/types.js.map +1 -0
  84. package/dist/docs/ARCHITECTURE.md +349 -0
  85. package/dist/docs/BENCHMARKS.md +36 -0
  86. package/dist/docs/README.md +34 -0
  87. package/dist/docs/content/README.md +44 -0
  88. package/dist/docs/content/adapters.mdx +199 -0
  89. package/dist/docs/content/cache.mdx +239 -0
  90. package/dist/docs/content/circuit-breaker.mdx +130 -0
  91. package/dist/docs/content/cli/commands.mdx +214 -0
  92. package/dist/docs/content/cli/overview.mdx +92 -0
  93. package/dist/docs/content/cli/usage.mdx +61 -0
  94. package/dist/docs/content/config.mdx +214 -0
  95. package/dist/docs/content/controllers.mdx +241 -0
  96. package/dist/docs/content/cron.mdx +109 -0
  97. package/dist/docs/content/database.mdx +209 -0
  98. package/dist/docs/content/dataloader.mdx +170 -0
  99. package/dist/docs/content/decorators.mdx +204 -0
  100. package/dist/docs/content/errors.mdx +344 -0
  101. package/dist/docs/content/gateways.mdx +376 -0
  102. package/dist/docs/content/installation.mdx +210 -0
  103. package/dist/docs/content/introduction.mdx +96 -0
  104. package/dist/docs/content/middleware.mdx +441 -0
  105. package/dist/docs/content/modules.mdx +144 -0
  106. package/dist/docs/content/oauth.mdx +179 -0
  107. package/dist/docs/content/providers.mdx +147 -0
  108. package/dist/docs/content/queues.mdx +177 -0
  109. package/dist/docs/content/requests.mdx +293 -0
  110. package/dist/docs/content/responses.mdx +271 -0
  111. package/dist/docs/content/routing.mdx +541 -0
  112. package/dist/docs/content/rpc.mdx +146 -0
  113. package/dist/docs/content/security.mdx +294 -0
  114. package/dist/docs/content/sse.mdx +159 -0
  115. package/dist/docs/content/structure.mdx +116 -0
  116. package/dist/docs/content/testing.mdx +139 -0
  117. package/dist/docs/content/validation.mdx +249 -0
  118. package/dist/docs/content/websockets.mdx +289 -0
  119. package/dist/docs/integrations/README.md +23 -0
  120. package/dist/docs/integrations/jwt.mdx +151 -0
  121. package/dist/docs/integrations/mongo.mdx +183 -0
  122. package/dist/docs/integrations/observability.mdx +125 -0
  123. package/dist/docs/integrations/openai.mdx +108 -0
  124. package/dist/docs/integrations/postgres.mdx +190 -0
  125. package/dist/docs/integrations/posthog.mdx +122 -0
  126. package/dist/docs/integrations/redis.mdx +141 -0
  127. package/dist/docs/integrations/resend.mdx +117 -0
  128. package/dist/docs/integrations/s3.mdx +122 -0
  129. package/dist/docs/integrations/swagger.mdx +113 -0
  130. package/dist/docs/reference/README.md +3 -0
  131. package/dist/docs/reference/adapters.mdx +130 -0
  132. package/dist/docs/reference/auth.mdx +183 -0
  133. package/dist/docs/reference/cache.mdx +132 -0
  134. package/dist/docs/reference/circuit-breaker.mdx +71 -0
  135. package/dist/docs/reference/config.mdx +118 -0
  136. package/dist/docs/reference/context.mdx +168 -0
  137. package/dist/docs/reference/dataloader.mdx +91 -0
  138. package/dist/docs/reference/decorators.mdx +293 -0
  139. package/dist/docs/reference/di.mdx +123 -0
  140. package/dist/docs/reference/error.mdx +170 -0
  141. package/dist/docs/reference/exis.mdx +124 -0
  142. package/dist/docs/reference/middleware.mdx +304 -0
  143. package/dist/docs/reference/plugin.mdx +113 -0
  144. package/dist/docs/reference/queue.mdx +155 -0
  145. package/dist/docs/reference/router.mdx +248 -0
  146. package/dist/docs/reference/sanitize.mdx +130 -0
  147. package/dist/docs/reference/storage.mdx +116 -0
  148. package/dist/docs/reference/swagger.mdx +132 -0
  149. package/dist/docs/reference/testing.mdx +145 -0
  150. package/dist/docs/reference/validator.mdx +186 -0
  151. package/dist/index.d.ts +4 -0
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.js +10 -1
  154. package/dist/index.js.map +1 -1
  155. package/dist/server/bootstrapper.js +1 -1
  156. package/dist/server/bootstrapper.js.map +1 -1
  157. package/package.json +8 -1
@@ -0,0 +1,118 @@
1
+ ---
2
+ title: "Configuration"
3
+ description: "API Reference for the global exis.config.ts file (`exisjs/config`)."
4
+ ---
5
+
6
+ # Configuration (`exisjs/config`)
7
+
8
+ ExisJS is designed to be highly zero-config out of the box. However, for advanced tuning, security, and clustering, you can define an `exis.config.ts` file in the root of your project.
9
+
10
+ This configuration file deeply integrates with the framework's core, managing everything from global middleware (like CORS and Helmet) to underlying Server backends and Worker Threads.
11
+
12
+ ---
13
+
14
+ ## 1. Standard Configuration
15
+
16
+ For most applications, you can simply export a static configuration object typed with `ExisConfig`.
17
+
18
+ ```typescript filename="exis.config.ts"
19
+ import type { ExisConfig } from 'exisjs/config'
20
+
21
+ const config: ExisConfig = {
22
+ port: Number(process.env.PORT) || 4000,
23
+ host: '0.0.0.0',
24
+
25
+ cors: {
26
+ origin: process.env.CORS_ORIGIN || '*',
27
+ credentials: true,
28
+ },
29
+
30
+ logger: {
31
+ level: 'debug',
32
+ pretty: process.env.NODE_ENV !== 'production',
33
+ },
34
+
35
+ queue: {
36
+ driver: 'memory',
37
+ },
38
+
39
+ helmet: { enabled: true },
40
+ compression: true,
41
+
42
+ test: {
43
+ include: ['tests/**/*.test.ts'],
44
+ },
45
+ }
46
+
47
+ export default config
48
+ ```
49
+
50
+ ---
51
+
52
+ ## 2. Advanced Configuration (Phases)
53
+
54
+ If you need dynamic configuration (such as spawning worker threads only in production or altering middleware depending on the environment), you can use `defineConfig()` and Phase execution.
55
+
56
+ ```typescript filename="exis.config.ts"
57
+ import { defineConfig, PHASE_DEVELOPMENT_SERVER } from 'exisjs/config'
58
+
59
+ export default async (phase: string, { defaultConfig }) => {
60
+ const isDev = phase === PHASE_DEVELOPMENT_SERVER
61
+
62
+ return defineConfig({
63
+ port: Number(process.env.PORT) || 4000,
64
+
65
+ // Auto-spawn Worker threads based on CPU cores!
66
+ workers: isDev ? 1 : 'safe',
67
+
68
+ // Globally configure native middleware
69
+ cors: {
70
+ origin: isDev ? '*' : 'https://myapp.com',
71
+ credentials: true
72
+ },
73
+ helmet: { enabled: true },
74
+ logger: isDev ? { level: 'debug', pretty: true } : false,
75
+
76
+ // Native plugin registration
77
+ plugins: [
78
+ // ... your plugins here
79
+ ]
80
+ })
81
+ }
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Configuration Phases
87
+
88
+ If you export a function from `exis.config.ts`, ExisJS will inject the current "Phase" string as the first argument, allowing you to dynamically adjust settings based on whether you are running a dev server, running tests, or building for production.
89
+
90
+ - `PHASE_DEVELOPMENT_SERVER`: Emitted during `exis dev`
91
+ - `PHASE_PRODUCTION_BUILD`: Emitted during `exis build`
92
+ - `PHASE_PRODUCTION_SERVER`: Emitted during `exis start`
93
+ - `PHASE_TEST`: Emitted during test runs
94
+
95
+ ---
96
+
97
+ ## Configuration Options
98
+
99
+ Here is the full list of options supported natively by the ExisJS configuration engine:
100
+
101
+ ### Server & Network
102
+ - **`port`** *(number)*: The port the server listens on (default: `4000`).
103
+ - **`host`** *(string)*: The host interface to bind to (default: `'0.0.0.0'`).
104
+ - **`server`** *(`'node' | 'uws' | 'auto'`)*: ExisJS supports running on standard Node `http`, or incredibly fast C++ `uWebSockets.js` (`uws`). Default is `'auto'` (uses `uws` if installed, falls back to `node`).
105
+ - **`workers`** *(number | `'safe'` | `'max'`)*: Number of Node.js Cluster workers to spawn. `'max'` uses all CPU cores. `'safe'` uses all cores minus 1 (to leave room for the OS).
106
+ - **`trustProxy`** *(boolean)*: Enable if running behind a reverse proxy (like Nginx or AWS ALB) to trust the `X-Forwarded-For` headers.
107
+
108
+ ### Security & Middlewares
109
+ - **`cors`** *(boolean | object)*: Enable and configure Cross-Origin Resource Sharing globally.
110
+ - **`helmet`** *(boolean | object)*: Enable strict security headers. Enabled by default.
111
+ - **`logger`** *(boolean | object)*: Configure the high-performance Pino request logger.
112
+ - **`compression`** *(boolean | object)*: Enable gzip/brotli response compression.
113
+ - **`bodyLimit`** *(number)*: Maximum JSON/URL-encoded payload size in bytes (default: `1MB`).
114
+
115
+ ### Advanced Integrations
116
+ - **`queue`**: Define your background queue driver (`memory` or `redis`) and configure `maxConcurrent` processing limits.
117
+ - **`plugins`**: An array of `ExisPlugin` instances to register natively into the framework.
118
+ - **`test`**: Configure testing environments (`include`, `exclude`, `setupFiles`, `coverage`).
@@ -0,0 +1,168 @@
1
+ ---
2
+ title: "Context & Requests"
3
+ description: "API Reference for ExisJS Context, Requests, and Async Hooks."
4
+ ---
5
+
6
+ # Context & Requests
7
+
8
+ ExisJS leverages Node.js `AsyncLocalStorage` to provide request-scoped state without needing to manually pass request objects down through your entire service layer. This enables globally accessible contextual data anywhere in your application lifecycle.
9
+
10
+ ---
11
+
12
+ ## Global Request & Response
13
+
14
+ In deeply nested utilities (like DataLoaders or generic services), it is often cumbersome to pass the `req` object through every function call. ExisJS allows you to pull the active HTTP Request and Response objects directly from the asynchronous context.
15
+
16
+ ### `getRequest()`
17
+
18
+ Returns the active ExisJS `Request` object.
19
+
20
+ ```typescript filename="src/loaders/index.ts"
21
+ import { defineLoaders, Dataloader } from 'exisjs/dataloader'
22
+ import { getRequest } from 'exisjs/router'
23
+
24
+ export const { loaderMiddleware, getLoaders } = defineLoaders({
25
+ user: () => new Dataloader(
26
+ async (keys: readonly string[]) => {
27
+ // Access the request deep inside the dataloader!
28
+ const req = getRequest()
29
+ console.log(`Batch fetching users for RequestId: ${req.requestId}`)
30
+
31
+ return keys.map(key => db.users.find(key))
32
+ }
33
+ )
34
+ })
35
+ ```
36
+
37
+ ### `getResponse()`
38
+
39
+ Returns the active ExisJS `Response` object.
40
+
41
+ ```typescript filename="src/utils/auth.ts"
42
+ import { getResponse } from 'exisjs/router'
43
+
44
+ export function forceLogout() {
45
+ const res = getResponse()
46
+ res.clearCookie('session_id')
47
+ }
48
+ ```
49
+
50
+ ### `getActiveApp()`
51
+
52
+ Returns the active ExisJS `App` instance processing the request. This is useful for resolving dependencies or accessing the application logger from deep within a service.
53
+
54
+ ```typescript filename="src/services/billing.ts"
55
+ import { getActiveApp } from 'exisjs/app'
56
+
57
+ export function processRefund(transactionId: string) {
58
+ const app = getActiveApp()
59
+ const stripe = app.resolve('StripeClient')
60
+
61
+ app.log.info(`Processing refund for ${transactionId}`)
62
+ return stripe.refunds.create({ charge: transactionId })
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Async Context API
69
+
70
+ The core context API allows you to read and write arbitrary state associated with the current HTTP request.
71
+
72
+ ### `setContext(key, value)`
73
+
74
+ Sets a value in the current request's state object. This is typically used by middleware to attach parsed data, user sessions, or tenant IDs.
75
+
76
+ ```typescript filename="src/middleware/tenant.ts"
77
+ import { setContext } from 'exisjs/router'
78
+
79
+ export async function tenantMiddleware(req, res, next) {
80
+ const tenantId = req.headers['x-tenant-id']
81
+
82
+ // Store the tenant ID in the async context
83
+ setContext('tenantId', tenantId)
84
+ next()
85
+ }
86
+ ```
87
+
88
+ ### `getContext<T>()`
89
+
90
+ Retrieves the current request's state object.
91
+
92
+ ```typescript filename="src/services/database.ts"
93
+ import { getContext } from 'exisjs/router'
94
+
95
+ function getTenantDatabase() {
96
+ // Retrieve the state set by the middleware!
97
+ const state = getContext<{ tenantId: string }>()
98
+
99
+ return new DatabaseConnection({ schema: state.tenantId })
100
+ }
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Background Tasks
106
+
107
+ ### `after(callback)`
108
+
109
+ Queues a function to execute in the background **after** the response has been completely sent to the client. This is the perfect place to put non-blocking tasks like sending welcome emails, flushing analytics, or writing audit logs.
110
+
111
+ ```typescript filename="src/http/users/route.ts"
112
+ import { controller, route } from 'exisjs/router'
113
+ import { after } from 'exisjs/router'
114
+ import { tex } from 'exisjs/validator'
115
+
116
+ export default controller({
117
+ createUser: route.post('/', {
118
+ body: tex.object({ email: tex.email() }),
119
+ async handle({ body }) {
120
+
121
+ // Respond to the client immediately
122
+ after(async () => {
123
+ // This runs after the client has received the HTTP response
124
+ await emailService.sendWelcome(body.email)
125
+ })
126
+
127
+ return { success: true, email: body.email }
128
+ }
129
+ })
130
+ })
131
+ ```
132
+
133
+
134
+ > [!NOTE]
135
+ > Tasks queued with `after()` do not block the HTTP request. If the task throws an error, it will be logged by the application logger, but it will not crash the server or affect the already-completed client response.
136
+
137
+ ---
138
+
139
+ ## Class-Based Context (OOP)
140
+
141
+ If you are using the Object-Oriented paradigm, ExisJS provides dedicated decorators to inject the request and response directly into your controller methods. This removes the need to manually call `getRequest()` or `getResponse()` in the controller layer.
142
+
143
+ ### `@Req()` & `@Res()`
144
+
145
+ Inject the raw ExisJS request or response objects.
146
+
147
+ ```typescript filename="src/http/users/route.ts"
148
+ import { Controller, Get, Req, Res } from 'exisjs/decorators'
149
+ import type { Request, Response } from 'exisjs/router'
150
+
151
+ @Controller('/users')
152
+ export default class UserController {
153
+
154
+ @Get('/ip')
155
+ getIp(@Req() req: Request) {
156
+ return { ip: req.ip }
157
+ }
158
+
159
+ @Get('/logout')
160
+ logout(@Res() res: Response) {
161
+ res.clearCookie('session_id')
162
+ res.json({ success: true })
163
+ }
164
+ }
165
+ ```
166
+
167
+ > [!TIP]
168
+ > For deep services inside an OOP architecture, you can still seamlessly use `getContext()`, `getRequest()`, or `after()` from `exisjs/router` to access the async context without prop-drilling!
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: "Data Loader"
3
+ description: "Strict API Reference for the N+1 batching utility."
4
+ ---
5
+
6
+ # Data Loader (`exisjs/dataloader`)
7
+
8
+ This document provides the strict API signatures and TypeScript interfaces exported by the `exisjs/dataloader` module. For high-level guides on solving N+1 query problems, see the [Data Loader Guide](/docs/dataloader).
9
+
10
+ ---
11
+
12
+ ## `Dataloader<K, V, C>` Class
13
+
14
+ The `Dataloader` class batches and caches requests to a backend data source.
15
+
16
+ ```typescript
17
+ import { Dataloader } from 'exisjs/dataloader'
18
+
19
+ class Dataloader<K, V, C = K> {
20
+ constructor(batchFn: BatchLoadFn<K, V>, options?: DataloaderOptions<K, C>)
21
+
22
+ // Enqueues a key to be batched, returning a Promise that resolves to the value
23
+ load(key: K): Promise<V>
24
+
25
+ // Loads multiple keys, returning an array of Promises
26
+ loadMany(keys: readonly K[]): Promise<V[]>
27
+
28
+ // Clears the value at `key` from the cache
29
+ clear(key: K): this
30
+
31
+ // Clears all values from the cache
32
+ clearAll(): this
33
+
34
+ // Primes the cache with a predefined value or Error for a specific key
35
+ prime(key: K, value: V | Error): this
36
+ }
37
+ ```
38
+
39
+ ### `BatchLoadFn<K, V>`
40
+
41
+ The function responsible for loading the batched keys. **It must return an array of values (or Errors) that are precisely the same length and in the exact same order as the provided keys array.**
42
+
43
+ ```typescript
44
+ type BatchLoadFn<K, V> = (
45
+ keys: readonly K[]
46
+ ) => Promise<readonly (V | Error)[]>
47
+ ```
48
+
49
+ ### `DataloaderOptions<K, C>`
50
+
51
+ ```typescript
52
+ interface DataloaderOptions<K, C = K> {
53
+ // Whether to cache resolved keys within this Dataloader instance. Default: true
54
+ cache?: boolean
55
+
56
+ // The maximum number of keys to include in a single batch. Default: 1000
57
+ maxBatchSize?: number
58
+
59
+ // A function to generate a unique cache key from the provided load key
60
+ cacheKeyFn?: (key: K) => C
61
+ }
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Context Registry (`defineLoaders`)
67
+
68
+ Because `Dataloader` instances are inherently stateful (they cache data), they **must not** be shared across different HTTP requests, or else users might read each other's cached data.
69
+
70
+ The `defineLoaders` function securely isolates Dataloader instances per-request by binding them to the ExisJS asynchronous context.
71
+
72
+ ```typescript
73
+ import { defineLoaders } from 'exisjs/dataloader'
74
+
75
+ function defineLoaders<
76
+ T extends Record<string, () => Dataloader<any, any>>
77
+ >(factories: T): {
78
+ // Middleware to register onto your router/app
79
+ loaderMiddleware: (req, res, next) => void;
80
+
81
+ // Hook to retrieve the isolated loaders anywhere in the request lifecycle
82
+ getLoaders: () => { [K in keyof T]: ReturnType<T[K]> }
83
+ }
84
+ ```
85
+
86
+ ### Usage Signature
87
+ ```typescript
88
+ const { loaderMiddleware, getLoaders } = defineLoaders({
89
+ userLoader: () => new Dataloader(async (keys) => { /* ... */ })
90
+ })
91
+ ```
@@ -0,0 +1,293 @@
1
+ ---
2
+ title: "Decorators"
3
+ description: "API Reference for ExisJS Class-Based Routing and Decorators."
4
+ ---
5
+
6
+ # Decorators (OOP)
7
+
8
+ ExisJS provides a robust Object-Oriented Programming (OOP) paradigm via the `exisjs/decorators` module.
9
+
10
+ Unlike many other frameworks (such as NestJS or TypeDI), ExisJS decorators are built completely on standard ECMAScript decorators and **do not require `reflect-metadata`**. This makes them incredibly lightweight, extremely fast, and completely free of legacy metadata bloat.
11
+
12
+ ---
13
+
14
+ ## 1. Controller & Routing
15
+
16
+ ### `@Controller(prefixOrOptions?)`
17
+
18
+ Marks a class as a Controller. All route handler methods within the class will be grouped under the specified prefix.
19
+
20
+ ```typescript filename="src/http/users/route.ts"
21
+ import { Controller } from 'exisjs/decorators'
22
+
23
+ // Routes will be mounted at /api/users
24
+ @Controller('/api/users')
25
+ export default class UserController {
26
+ // ...
27
+ }
28
+ ```
29
+
30
+ ### HTTP Methods
31
+ Defines an HTTP route handler on a class method. You can optionally pass a schema to validate incoming requests.
32
+
33
+ - `@Get(path?, schema?)`
34
+ - `@Post(path?, schema?)`
35
+ - `@Put(path?, schema?)`
36
+ - `@Patch(path?, schema?)`
37
+ - `@Delete(path?, schema?)`
38
+ - `@Options(path?, schema?)`
39
+ - `@Head(path?, schema?)`
40
+ - `@Connect(path?, schema?)`
41
+ - `@Trace(path?, schema?)`
42
+ - `@All(path?, schema?)`
43
+ - `@Query(path?, schema?)`: Can be used as a method decorator to handle custom `QUERY` method requests (e.g., RPC or GraphQL-like endpoints).
44
+
45
+ ```typescript filename="src/http/users/route.ts"
46
+ import { Controller, Get, Post } from 'exisjs/decorators'
47
+ import { tex } from 'exisjs/validator'
48
+
49
+ @Controller('/users')
50
+ export default class UserController {
51
+ @Get('/:id')
52
+ getUser() {
53
+ return { id: 1 }
54
+ }
55
+
56
+ @Post('/', { body: tex.object({ name: tex.string() }) })
57
+ createUser() {
58
+ return { success: true }
59
+ }
60
+ }
61
+ ```
62
+
63
+ ### Real-Time Methods
64
+ - `@Ws(path?, schema?)`: Maps the handler to a WebSocket connection.
65
+ - `@Sse(path?, schema?)`: Maps the handler to a Server-Sent Events stream.
66
+
67
+ ---
68
+
69
+ ## 2. Parameter Injection
70
+
71
+ Parameter decorators automatically extract data from the incoming request and inject them directly into your method arguments, drastically reducing boilerplate.
72
+
73
+ - `@Body()`: Parses and injects the JSON request body.
74
+ - `@Param(name?)`: Injects a URL path parameter (e.g., `id` from `/:id`).
75
+ - `@Query(name?)`: Injects a query string parameter. (Note: `@Query` doubles as a route decorator if used on a method).
76
+ - `@Headers(name?)`: Injects an HTTP header by name.
77
+ - `@HostParam(name?)`: Injects a subdomain parameter (e.g., extracted from `@Hosts(':tenant.example.com')`).
78
+ - `@Req()`: Injects the raw ExisJS Request object.
79
+ - `@Res(options?)`: Injects the raw ExisJS Response object. Use `@Res({ passthrough: true })` if you want ExisJS to still handle the final response serialization.
80
+ - `@Ip()`: Injects the client's IP address.
81
+ - `@Session()`: Injects the active session object (requires session middleware).
82
+ - `@Next()`: Injects the Next function.
83
+ - `@Socket()`: Injects the active WebSocket instance (for `@Ws`).
84
+ - `@Stream()`: Injects the active SSE instance (for `@Sse`).
85
+ - `@UploadedFile() / @UploadedFiles()`: Injects multipart/form-data files.
86
+
87
+ ```typescript filename="src/http/users/route.ts"
88
+ import { Controller, Get, Post, Param, Body, Ip } from 'exisjs/decorators'
89
+
90
+ @Controller('/users')
91
+ export default class UserController {
92
+ @Get('/:id')
93
+ getUser(@Param('id') id: string, @Ip() ip: string) {
94
+ return { id, requestedBy: ip }
95
+ }
96
+
97
+ @Post('/')
98
+ createUser(@Body() body: { email: string }) {
99
+ return { email: body.email }
100
+ }
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ## 3. Middleware & Configuration
107
+
108
+ ### `@Use(...middlewares)`
109
+ Applies Express-style middleware functions to a specific route handler or an entire controller class.
110
+
111
+ ```typescript filename="src/http/secure/route.ts"
112
+ import { Controller, Get, Use } from 'exisjs/decorators'
113
+ import { authMiddleware } from '../middleware/auth'
114
+
115
+ @Controller('/secure')
116
+ @Use(authMiddleware)
117
+ export default class SecureController {
118
+ @Get('/')
119
+ getSecureData() {
120
+ return { secure: true }
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### `@Cache(options)`
126
+ Automatically caches `GET` responses in memory or Redis.
127
+
128
+ ### `@Idempotent(options)`
129
+ Enforces idempotency based on the `Idempotency-Key` header, preventing duplicate mutations (e.g., duplicate payments).
130
+
131
+ ```typescript filename="src/http/payments/route.ts"
132
+ import { Controller, Post, Idempotent } from 'exisjs/decorators'
133
+
134
+ @Controller('/payments')
135
+ export default class PaymentController {
136
+ @Post('/checkout')
137
+ @Idempotent()
138
+ checkout() {
139
+ return { status: 'processing' }
140
+ }
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## 4. Responses & Metadata
147
+
148
+ ### `@HttpCode(code)`
149
+ Forces a specific HTTP status code for successful responses.
150
+
151
+ ### `@Header(name, value)`
152
+ Automatically injects a specific response header before sending the response.
153
+
154
+ ### `@Redirect(url, statusCode?)`
155
+ Redirects the request to the specified URL.
156
+
157
+ ### `@Returns(schema)`
158
+ Explicitly defines the response schema for OpenAPI (Swagger) generation.
159
+
160
+ ```typescript filename="src/http/documents/route.ts"
161
+ import { Controller, Post, HttpCode, Header } from 'exisjs/decorators'
162
+
163
+ @Controller('/documents')
164
+ export default class DocumentController {
165
+ @Post('/')
166
+ @HttpCode(201)
167
+ @Header('X-Custom-Status', 'Processed')
168
+ createDoc() {
169
+ return { created: true }
170
+ }
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ## 5. Security & Lifecycle
177
+
178
+ These decorators allow you to attach ExisJS lifecycle hooks to your OOP routes.
179
+
180
+ - `@Permissions(...flags)`: Restricts the route to users with specific permission flags.
181
+ - `@Hosts(...hosts)`: Restricts the route to a specific subdomain/host.
182
+ - `@UseGuards(...guards)`: Attaches Guards (authentication/authorization checks).
183
+ - `@UseInterceptors(...interceptors)`: Attaches Interceptors (request/response mutation).
184
+ - `@UseFilters(...filters)`: Attaches Exception Filters (custom error handling).
185
+
186
+ ---
187
+
188
+ ## 6. Global Setup
189
+
190
+ ### `@Server(options?)`
191
+ Marks a class as the root Server entry point. Used to register global configuration and dependencies.
192
+
193
+ **Options Interface:**
194
+ ```typescript
195
+ interface ServerConfig {
196
+ plugins?: any[]
197
+ providers?: any[]
198
+ cron?: any[]
199
+ queue?: any[]
200
+ }
201
+ ```
202
+
203
+ ```typescript filename="src/http/server.ts"
204
+ import { Server } from 'exisjs/decorators'
205
+ import { corsPlugin } from 'exisjs/middleware'
206
+
207
+ @Server({
208
+ plugins: [corsPlugin]
209
+ })
210
+ export default class RootServer {
211
+ async onStart(app) {
212
+ app.log.info("App booted!")
213
+ }
214
+ }
215
+ ```
216
+
217
+ ### `@Gateway(options?)`
218
+ Marks a class as a Gateway module. Gateways are used to apply massive configuration blocks across an entire directory of routes (e.g., applying an auth guard to all routes inside `src/http/admin`).
219
+
220
+ **Options Interface:**
221
+ ```typescript
222
+ interface GatewayConfig {
223
+ exclude?: any[]
224
+ middleware?: any[]
225
+ filters?: any[]
226
+ guards?: any[]
227
+ interceptors?: any[]
228
+ timeout?: number | ((req: any) => number)
229
+ metadata?: Record<string, any>
230
+ cors?: any
231
+ headers?: Record<string, string>
232
+ imports?: any[]
233
+ providers?: any[]
234
+ }
235
+ ```
236
+
237
+ ```typescript filename="src/http/gateway.ts"
238
+ import { Gateway } from 'exisjs/decorators'
239
+ import { helmet, hpp } from 'exisjs/middleware'
240
+
241
+ /**
242
+ * Root Gateway (gatekeeper for the entire application)
243
+ *
244
+ * Gateways apply configuration to all routes within their directory and subdirectories.
245
+ * This acts as the global security and infrastructure layer.
246
+ */
247
+ @Gateway({
248
+ // 1. Cross-Origin Resource Sharing (CORS)
249
+ cors: {
250
+ origin: ['https://myapp.com', /localhost:\d+/],
251
+ credentials: true,
252
+ },
253
+
254
+ // 2. Global Headers
255
+ headers: {
256
+ 'X-Powered-By': 'ExisJS',
257
+ 'X-Application-Version': '1.0.0',
258
+ },
259
+
260
+ // 3. Global Middleware
261
+ middleware: [
262
+ helmet(), // Sets secure HTTP headers
263
+ hpp(), // Prevents HTTP Parameter Pollution
264
+ ],
265
+
266
+ // 4. Exclude Paths
267
+ exclude: [{ path: '/health', method: 'GET' }],
268
+
269
+ // 5. Dependency Injection Providers
270
+ providers: [
271
+ ['ConfigService', { useValue: { apiKey: process.env.API_KEY || 'default' } }],
272
+ ],
273
+
274
+ // 6. Global Metadata
275
+ metadata: {
276
+ tenant: 'root',
277
+ },
278
+
279
+ // 7. Request Timeout
280
+ timeout: 10000,
281
+ })
282
+ export default class RootGateway {}
283
+ ```
284
+
285
+ ### `@Injectable(options?)`
286
+ Marks a class as a provider for Dependency Injection, meaning it can be automatically injected into controllers or other services by the IoC container.
287
+
288
+ **Options Interface:**
289
+ ```typescript
290
+ interface InjectableOptions {
291
+ scope?: 'singleton' | 'request'
292
+ }
293
+ ```