exisjs 0.5.9 → 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 (213) 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/auth/session.d.ts +4 -1
  46. package/dist/auth/session.d.ts.map +1 -1
  47. package/dist/auth/session.js +33 -5
  48. package/dist/auth/session.js.map +1 -1
  49. package/dist/cache/store.d.ts +11 -3
  50. package/dist/cache/store.d.ts.map +1 -1
  51. package/dist/cache/store.js +87 -19
  52. package/dist/cache/store.js.map +1 -1
  53. package/dist/cli/commands/dev.d.ts.map +1 -1
  54. package/dist/cli/commands/dev.js +4 -6
  55. package/dist/cli/commands/dev.js.map +1 -1
  56. package/dist/database/adapters/mongodb.d.ts +58 -0
  57. package/dist/database/adapters/mongodb.d.ts.map +1 -0
  58. package/dist/database/adapters/mongodb.js +264 -0
  59. package/dist/database/adapters/mongodb.js.map +1 -0
  60. package/dist/database/adapters/mysql.d.ts +27 -0
  61. package/dist/database/adapters/mysql.d.ts.map +1 -0
  62. package/dist/database/adapters/mysql.js +188 -0
  63. package/dist/database/adapters/mysql.js.map +1 -0
  64. package/dist/database/adapters/postgres.d.ts +28 -0
  65. package/dist/database/adapters/postgres.d.ts.map +1 -0
  66. package/dist/database/adapters/postgres.js +174 -0
  67. package/dist/database/adapters/postgres.js.map +1 -0
  68. package/dist/database/adapters/sqlite.d.ts +27 -0
  69. package/dist/database/adapters/sqlite.d.ts.map +1 -0
  70. package/dist/database/adapters/sqlite.js +149 -0
  71. package/dist/database/adapters/sqlite.js.map +1 -0
  72. package/dist/database/index.d.ts +42 -0
  73. package/dist/database/index.d.ts.map +1 -0
  74. package/dist/database/index.js +58 -0
  75. package/dist/database/index.js.map +1 -0
  76. package/dist/database/manager.d.ts +114 -0
  77. package/dist/database/manager.d.ts.map +1 -0
  78. package/dist/database/manager.js +269 -0
  79. package/dist/database/manager.js.map +1 -0
  80. package/dist/database/migration.d.ts +57 -0
  81. package/dist/database/migration.d.ts.map +1 -0
  82. package/dist/database/migration.js +329 -0
  83. package/dist/database/migration.js.map +1 -0
  84. package/dist/database/query-builder.d.ts +89 -0
  85. package/dist/database/query-builder.d.ts.map +1 -0
  86. package/dist/database/query-builder.js +353 -0
  87. package/dist/database/query-builder.js.map +1 -0
  88. package/dist/database/types.d.ts +184 -0
  89. package/dist/database/types.d.ts.map +1 -0
  90. package/dist/database/types.js +4 -0
  91. package/dist/database/types.js.map +1 -0
  92. package/dist/docs/ARCHITECTURE.md +349 -0
  93. package/dist/docs/BENCHMARKS.md +36 -0
  94. package/dist/docs/README.md +34 -0
  95. package/dist/docs/content/README.md +44 -0
  96. package/dist/docs/content/adapters.mdx +199 -0
  97. package/dist/docs/content/cache.mdx +239 -0
  98. package/dist/docs/content/circuit-breaker.mdx +130 -0
  99. package/dist/docs/content/cli/commands.mdx +214 -0
  100. package/dist/docs/content/cli/overview.mdx +92 -0
  101. package/dist/docs/content/cli/usage.mdx +61 -0
  102. package/dist/docs/content/config.mdx +214 -0
  103. package/dist/docs/content/controllers.mdx +241 -0
  104. package/dist/docs/content/cron.mdx +109 -0
  105. package/dist/docs/content/database.mdx +209 -0
  106. package/dist/docs/content/dataloader.mdx +170 -0
  107. package/dist/docs/content/decorators.mdx +204 -0
  108. package/dist/docs/content/errors.mdx +344 -0
  109. package/dist/docs/content/gateways.mdx +376 -0
  110. package/dist/docs/content/installation.mdx +210 -0
  111. package/dist/docs/content/introduction.mdx +96 -0
  112. package/dist/docs/content/middleware.mdx +441 -0
  113. package/dist/docs/content/modules.mdx +144 -0
  114. package/dist/docs/content/oauth.mdx +179 -0
  115. package/dist/docs/content/providers.mdx +147 -0
  116. package/dist/docs/content/queues.mdx +177 -0
  117. package/dist/docs/content/requests.mdx +293 -0
  118. package/dist/docs/content/responses.mdx +271 -0
  119. package/dist/docs/content/routing.mdx +541 -0
  120. package/dist/docs/content/rpc.mdx +146 -0
  121. package/dist/docs/content/security.mdx +294 -0
  122. package/dist/docs/content/sse.mdx +159 -0
  123. package/dist/docs/content/structure.mdx +116 -0
  124. package/dist/docs/content/testing.mdx +139 -0
  125. package/dist/docs/content/validation.mdx +249 -0
  126. package/dist/docs/content/websockets.mdx +289 -0
  127. package/dist/docs/integrations/README.md +23 -0
  128. package/dist/docs/integrations/jwt.mdx +151 -0
  129. package/dist/docs/integrations/mongo.mdx +183 -0
  130. package/dist/docs/integrations/observability.mdx +125 -0
  131. package/dist/docs/integrations/openai.mdx +108 -0
  132. package/dist/docs/integrations/postgres.mdx +190 -0
  133. package/dist/docs/integrations/posthog.mdx +122 -0
  134. package/dist/docs/integrations/redis.mdx +141 -0
  135. package/dist/docs/integrations/resend.mdx +117 -0
  136. package/dist/docs/integrations/s3.mdx +122 -0
  137. package/dist/docs/integrations/swagger.mdx +113 -0
  138. package/dist/docs/reference/README.md +3 -0
  139. package/dist/docs/reference/adapters.mdx +130 -0
  140. package/dist/docs/reference/auth.mdx +183 -0
  141. package/dist/docs/reference/cache.mdx +132 -0
  142. package/dist/docs/reference/circuit-breaker.mdx +71 -0
  143. package/dist/docs/reference/config.mdx +118 -0
  144. package/dist/docs/reference/context.mdx +168 -0
  145. package/dist/docs/reference/dataloader.mdx +91 -0
  146. package/dist/docs/reference/decorators.mdx +293 -0
  147. package/dist/docs/reference/di.mdx +123 -0
  148. package/dist/docs/reference/error.mdx +170 -0
  149. package/dist/docs/reference/exis.mdx +124 -0
  150. package/dist/docs/reference/middleware.mdx +304 -0
  151. package/dist/docs/reference/plugin.mdx +113 -0
  152. package/dist/docs/reference/queue.mdx +155 -0
  153. package/dist/docs/reference/router.mdx +248 -0
  154. package/dist/docs/reference/sanitize.mdx +130 -0
  155. package/dist/docs/reference/storage.mdx +116 -0
  156. package/dist/docs/reference/swagger.mdx +132 -0
  157. package/dist/docs/reference/testing.mdx +145 -0
  158. package/dist/docs/reference/validator.mdx +186 -0
  159. package/dist/index.d.ts +4 -0
  160. package/dist/index.d.ts.map +1 -1
  161. package/dist/index.js +10 -1
  162. package/dist/index.js.map +1 -1
  163. package/dist/middleware/backpressure.d.ts.map +1 -1
  164. package/dist/middleware/backpressure.js +21 -12
  165. package/dist/middleware/backpressure.js.map +1 -1
  166. package/dist/middleware/cache.d.ts +6 -0
  167. package/dist/middleware/cache.d.ts.map +1 -1
  168. package/dist/middleware/cache.js +84 -28
  169. package/dist/middleware/cache.js.map +1 -1
  170. package/dist/middleware/idempotency.d.ts +4 -1
  171. package/dist/middleware/idempotency.d.ts.map +1 -1
  172. package/dist/middleware/idempotency.js +29 -4
  173. package/dist/middleware/idempotency.js.map +1 -1
  174. package/dist/middleware/ip-filter.d.ts.map +1 -1
  175. package/dist/middleware/ip-filter.js +16 -0
  176. package/dist/middleware/ip-filter.js.map +1 -1
  177. package/dist/middleware/rate-limit.d.ts.map +1 -1
  178. package/dist/middleware/rate-limit.js +29 -12
  179. package/dist/middleware/rate-limit.js.map +1 -1
  180. package/dist/queue/drivers/MemoryDriver.d.ts +6 -3
  181. package/dist/queue/drivers/MemoryDriver.d.ts.map +1 -1
  182. package/dist/queue/drivers/MemoryDriver.js +76 -23
  183. package/dist/queue/drivers/MemoryDriver.js.map +1 -1
  184. package/dist/server/bootstrapper.d.ts +3 -0
  185. package/dist/server/bootstrapper.d.ts.map +1 -1
  186. package/dist/server/bootstrapper.js +56 -8
  187. package/dist/server/bootstrapper.js.map +1 -1
  188. package/dist/server/bun-adapter.d.ts +2 -0
  189. package/dist/server/bun-adapter.d.ts.map +1 -1
  190. package/dist/server/bun-adapter.js +23 -12
  191. package/dist/server/bun-adapter.js.map +1 -1
  192. package/dist/server/request-handler.d.ts +0 -2
  193. package/dist/server/request-handler.d.ts.map +1 -1
  194. package/dist/server/request-handler.js +2 -24
  195. package/dist/server/request-handler.js.map +1 -1
  196. package/dist/server/request.d.ts.map +1 -1
  197. package/dist/server/request.js +24 -11
  198. package/dist/server/request.js.map +1 -1
  199. package/dist/server/uws-adapter.d.ts +80 -0
  200. package/dist/server/uws-adapter.d.ts.map +1 -0
  201. package/dist/server/uws-adapter.js +357 -0
  202. package/dist/server/uws-adapter.js.map +1 -0
  203. package/dist/server/ws-orchestrator.d.ts +1 -0
  204. package/dist/server/ws-orchestrator.d.ts.map +1 -1
  205. package/dist/server/ws-orchestrator.js +83 -0
  206. package/dist/server/ws-orchestrator.js.map +1 -1
  207. package/dist/types.d.ts +1 -1
  208. package/dist/types.d.ts.map +1 -1
  209. package/dist/utils/config.js.map +1 -1
  210. package/dist/validator/tex.d.ts +21 -16
  211. package/dist/validator/tex.d.ts.map +1 -1
  212. package/dist/validator/tex.js.map +1 -1
  213. package/package.json +11 -3
@@ -0,0 +1,113 @@
1
+ ---
2
+ nextUrl: "/docs/integrations/s3"
3
+ nextTitle: "AWS S3"
4
+ ---
5
+
6
+ # Swagger OpenAPI
7
+
8
+ ExisJS comes with a native **Swagger UI & OpenAPI 3.0** generator built directly into the framework. It parses your routes, path parameters, and schema validations to generate an API explorer.
9
+
10
+ You do **not** need to maintain OpenAPI YAML files or write decorators.
11
+
12
+ ---
13
+
14
+ ## Usage
15
+
16
+ To enable Swagger UI, simply import `serveSwagger` and call it inside the `onStart` hook in your `server.ts`.
17
+
18
+ ```typescript filename="src/http/server.ts"
19
+ import { exis } from 'exisjs'
20
+ import { serveSwagger } from 'exisjs/swagger'
21
+
22
+ export default exis({
23
+ async onStart(app) {
24
+ // Generate and mount Swagger UI
25
+ serveSwagger(app, {
26
+ path: '/api-docs', // Defaults to '/docs'
27
+ title: 'My Startup API',
28
+ version: '1.0.0',
29
+ components: {
30
+ securitySchemes: {
31
+ bearerAuth: {
32
+ type: 'http',
33
+ scheme: 'bearer',
34
+ bearerFormat: 'JWT'
35
+ }
36
+ }
37
+ }
38
+ })
39
+ }
40
+ })
41
+ ```
42
+
43
+ Once your server starts, navigate to `http://localhost:4000/api-docs` to see your auto-generated Swagger UI. The raw OpenAPI JSON payload will be available at `/api-docs/json`.
44
+
45
+ ---
46
+
47
+ ## Automatic Schema Generation
48
+
49
+ ExisJS integrates with its internal `v` validator. Any validation schemas attached to your routes (whether Functional or OOP) are automatically translated into OpenAPI specifications.
50
+
51
+ #### Functional Paradigm
52
+
53
+ ```typescript filename="src/http/users/route.ts"
54
+ import { controller, route } from 'exisjs/router'
55
+ import { tex } from 'exisjs/validator'
56
+
57
+ export default controller({
58
+ createUser: route.post('/', {
59
+ // This schema is automatically translated to OpenAPI request body definitions
60
+ body: tex.object({
61
+ name: tex.string(),
62
+ age: tex.number({ optional: true })
63
+ }),
64
+ async handle(req) {
65
+ return { success: true, name: req.body.name }
66
+ }
67
+ })
68
+ })
69
+ ```
70
+
71
+ #### Class-Based (OOP) Paradigm
72
+
73
+ ```typescript filename="src/http/users/route.ts"
74
+ import { Controller, Post, Body } from 'exisjs/decorators'
75
+ import { tex } from 'exisjs/validator'
76
+
77
+ const CreateUserSchema = tex.object({
78
+ name: tex.string(),
79
+ age: tex.number({ optional: true })
80
+ })
81
+
82
+ @Controller('/users')
83
+ export default class UsersController {
84
+ @Post('/', { body: CreateUserSchema })
85
+ async createUser(@Body() body: any) {
86
+ return { success: true, name: body.name }
87
+ }
88
+ }
89
+ ```
90
+
91
+ When you view Swagger UI, the `POST /users` endpoint will show the JSON body required, properly displaying the string/number types and required/optional flags.
92
+
93
+ ---
94
+
95
+ ## Gateway Cascading Metadata
96
+
97
+ If you want to apply OpenAPI tags or security schemas to an entire folder of routes without decorating every endpoint, you can use ExisJS Gateways.
98
+
99
+ ```typescript filename="src/http/admin/gateway.ts"
100
+ import { defineGateway } from 'exisjs/router'
101
+
102
+ export default defineGateway({
103
+ // Every route in the /admin folder automatically inherits these OpenAPI tags
104
+ metadata: {
105
+ openapi: {
106
+ tags: ['Admin Management API'],
107
+ security: ['bearerAuth']
108
+ }
109
+ }
110
+ })
111
+ ```
112
+
113
+ Now, every route under `/admin/*` will be grouped under the "Admin Management API" section in your Swagger UI.
@@ -0,0 +1,3 @@
1
+ # Reference
2
+
3
+ This folder contains detailed API references, configuration options (`exis.config.ts`), and advanced framework capabilities for Exis JS.
@@ -0,0 +1,130 @@
1
+ ---
2
+ title: "Adapters"
3
+ description: "Strict API Reference for all Serverless and Edge adapters."
4
+ ---
5
+
6
+ # Adapters (`exisjs/adapters`)
7
+
8
+ This document provides the strict API signatures and TypeScript interfaces exported by the `exisjs/adapters` module. For high-level deployment guides, see the [Adapters Guide](/docs/adapters).
9
+
10
+ ---
11
+
12
+ ## `aws`
13
+
14
+ Converts API Gateway events into a native Node.js HTTP stream.
15
+
16
+ ```typescript
17
+ function aws(app: App): (event: APIGatewayEvent) => Promise<APIGatewayResult>
18
+ ```
19
+
20
+ ### `APIGatewayEvent`
21
+ ```typescript
22
+ interface APIGatewayEvent {
23
+ httpMethod: string;
24
+ path: string;
25
+ headers: Record<string, string | undefined>;
26
+ body: string | null;
27
+ isBase64Encoded: boolean;
28
+ queryStringParameters?: Record<string, string | undefined>;
29
+ }
30
+ ```
31
+
32
+ ### `APIGatewayResult`
33
+ ```typescript
34
+ interface APIGatewayResult {
35
+ statusCode: number;
36
+ headers: Record<string, string>;
37
+ body: string;
38
+ isBase64Encoded: boolean;
39
+ }
40
+ ```
41
+
42
+ ---
43
+
44
+ ## `vercel`
45
+
46
+ Provides a bridge between Vercel's Node.js Serverless runtime and ExisJS.
47
+
48
+ ```typescript
49
+ function vercel(
50
+ app: App,
51
+ options?: { cwd?: string }
52
+ ): (req: VercelRequest, res: VercelResponse) => void | Promise<void>
53
+ ```
54
+
55
+ ### `VercelRequest` & `VercelResponse`
56
+ These are re-exported from `@vercel/node`. The adapter intercepts these and patches the internal socket state to satisfy the ExisJS pipeline.
57
+
58
+ ---
59
+
60
+ ## `fetch`
61
+
62
+ The universal Web Standard adapter. Converts standard Web `Request` objects into internal HTTP pipelines and resolves back to a Web `Response`.
63
+
64
+ ```typescript
65
+ function fetch(
66
+ app: App
67
+ ): (request: Request, env?: any, ctx?: any) => Promise<Response>
68
+ ```
69
+
70
+ *Note: The `env` and `ctx` arguments are seamlessly injected into the ExisJS routing context, allowing you to access platform-specific bindings (e.g. Cloudflare KV).*
71
+
72
+ ---
73
+
74
+ ## `cloudflare`
75
+
76
+ A dedicated wrapper for Cloudflare Workers that returns the expected ES Module format.
77
+
78
+ ```typescript
79
+ function cloudflare(
80
+ app: App
81
+ ): { fetch: (request: Request, env: any, ctx: ExecutionContext) => Promise<Response> }
82
+ ```
83
+
84
+ ---
85
+
86
+ ## `bun`
87
+
88
+ A dedicated wrapper for the `Bun.serve()` configuration object.
89
+
90
+ ```typescript
91
+ function bun(
92
+ app: App
93
+ ): { fetch: (request: Request) => Promise<Response> }
94
+ ```
95
+
96
+ ---
97
+
98
+ ## `deno`
99
+
100
+ A dedicated wrapper for `Deno.serve()`.
101
+
102
+ ```typescript
103
+ function deno(
104
+ app: App
105
+ ): (request: Request) => Promise<Response>
106
+ ```
107
+
108
+ ---
109
+
110
+ ## `netlify`
111
+
112
+ A dedicated wrapper for Netlify Edge Functions.
113
+
114
+ ```typescript
115
+ function netlify(
116
+ app: App
117
+ ): (request: Request, context: NetlifyContext) => Promise<Response>
118
+ ```
119
+
120
+ ---
121
+
122
+ ## `fastly`
123
+
124
+ A dedicated wrapper for Fastly Compute@Edge which binds to the global `addEventListener` fetch event.
125
+
126
+ ```typescript
127
+ function fastly(
128
+ app: App
129
+ ): (event: FetchEvent) => void
130
+ ```
@@ -0,0 +1,183 @@
1
+ ---
2
+ title: "Authentication"
3
+ description: "API Reference for ExisJS Authentication utilities (`exisjs/auth`)."
4
+ ---
5
+
6
+ # Authentication (`exisjs/auth`)
7
+
8
+ ExisJS ships with a completely native, dependency-free `auth` module that covers the most critical security implementations: Scrypt Password Hashing, JWT (JSON Web Tokens), Session Management, and Role-Based Access Control (RBAC).
9
+
10
+ ---
11
+
12
+ ## 1. Password Hashing (Scrypt)
13
+
14
+ ExisJS natively uses Node's highly secure `scrypt` algorithm to hash passwords. It automatically generates a cryptographically secure salt and stores the parameters in a single string format (`scrypt:N:r:p:keylen:salt:hash`) so you never have to manage them separately.
15
+
16
+ ```typescript filename="src/services/AuthService.ts"
17
+ import { Password } from 'exisjs/auth'
18
+
19
+ // Hashing a new password
20
+ const hashedPassword = await Password.hashPassword('my-super-secret-password', {
21
+ cost: 16384, // Higher cost = harder to brute force, but slower to compute
22
+ })
23
+
24
+ // Verifying a login attempt
25
+ const isValid = await Password.verifyPassword('my-super-secret-password', hashedPassword)
26
+ ```
27
+
28
+ ---
29
+
30
+ ## 2. JSON Web Tokens (JWT)
31
+
32
+ You can instantly sign and verify JWTs without needing third-party libraries like `jsonwebtoken`.
33
+
34
+ ```typescript
35
+ import { JWT } from 'exisjs/auth'
36
+
37
+ // Sign a token valid for 2 hours
38
+ const token = JWT.signJWT(
39
+ { userId: '123', role: 'admin' },
40
+ process.env.JWT_SECRET,
41
+ { expiresIn: 2 * 60 * 60 }
42
+ )
43
+
44
+ try {
45
+ // Verifying token returns the exact typed payload
46
+ const payload = JWT.verifyJWT<{ userId: string, role: string }>(token, process.env.JWT_SECRET)
47
+ console.log(payload.userId)
48
+ } catch (err) {
49
+ if (err.name === 'TokenExpiredError') {
50
+ // Handle expired token
51
+ }
52
+ }
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 3. Session Middleware
58
+
59
+ If you prefer stateful authentication, ExisJS provides extremely robust session middleware out of the box. It leverages signed HTTP-only cookies to track users and automatically synchronizes session state back to the store (Memory, Redis, etc.) when the request ends.
60
+
61
+ ### Global Setup via Plugin (`exis.config.ts`)
62
+
63
+ Because ExisJS utilizes a file-based architecture with no manual server entry point, global middlewares like `session` should be attached using a Plugin.
64
+
65
+ ```typescript filename="src/plugins/session.ts"
66
+ import { definePlugin } from 'exisjs/plugin'
67
+ import { session } from 'exisjs/auth'
68
+
69
+ export const sessionPlugin = definePlugin({
70
+ name: 'global-session',
71
+ register(app) {
72
+ app.use(session({
73
+ secret: process.env.SESSION_SECRET || 'fallback-secret-minimum-32-chars-long!',
74
+ cookieName: 'exis_sid',
75
+ ttl: 86400 * 1000, // 1 day in milliseconds
76
+ // store: new RedisSessionStore(...)
77
+ }))
78
+ }
79
+ })
80
+ ```
81
+
82
+ Then, natively register your plugin in `exis.config.ts`:
83
+
84
+ ```typescript filename="exis.config.ts"
85
+ import { defineConfig } from 'exisjs/config'
86
+ import { sessionPlugin } from './src/plugins/session'
87
+
88
+ export default defineConfig({
89
+ plugins: [
90
+ sessionPlugin()
91
+ ]
92
+ })
93
+ ```
94
+
95
+ Once registered, you can read and write to `req.session` anywhere in your application!
96
+
97
+ ### Functional Example
98
+
99
+ ```typescript filename="src/http/auth/route.ts"
100
+ import { controller, route } from 'exisjs/router'
101
+ import { HttpError } from 'exisjs/error'
102
+
103
+ export default controller({
104
+ login: route.post('/login', {
105
+ async handle(ctx) {
106
+ const user = await db.users.findByEmail(ctx.body.email)
107
+ if (!user) throw HttpError.unauthorized('Invalid credentials')
108
+
109
+ // Login the user by attaching data to the session
110
+ ctx.req.session.userId = user.id
111
+ ctx.req.session.role = user.role
112
+
113
+ return { success: true }
114
+ }
115
+ })
116
+ })
117
+ ```
118
+
119
+ ### Class-Based (OOP) Example
120
+
121
+ ```typescript filename="src/http/auth/route.ts"
122
+ import { Controller, Post, Body, Req } from 'exisjs/decorators'
123
+ import type { Request } from 'exisjs'
124
+
125
+ @Controller('/auth')
126
+ export default class AuthController {
127
+
128
+ @Post('/login')
129
+ async login(@Body() body: any, @Req() req: Request) {
130
+ const user = await db.users.findByEmail(body.email)
131
+
132
+ // Attach to session
133
+ req.session.userId = user.id
134
+ req.session.role = user.role
135
+
136
+ return { success: true }
137
+ }
138
+ }
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 4. Role-Based Access Control (RBAC)
144
+
145
+ The `requireRole` middleware checks if the current authenticated user has the necessary permissions to access a route.
146
+
147
+ *Note: This middleware expects that `req.user.role` was populated by an earlier authentication middleware (like a JWT Verifier or a Session Restorer).*
148
+
149
+ ### Functional Example
150
+
151
+ ```typescript filename="src/http/admin/route.ts"
152
+ import { controller, route } from 'exisjs/router'
153
+ import { requireRole } from 'exisjs/auth'
154
+
155
+ export default controller({
156
+ getDashboard: route.get('/dashboard', {
157
+ middleware: [
158
+ // Only users with 'admin' or 'superadmin' roles can access this route
159
+ requireRole(['admin', 'superadmin'])
160
+ ],
161
+ async handle() {
162
+ return { metrics: [] }
163
+ }
164
+ })
165
+ })
166
+ ```
167
+
168
+ ### Class-Based (OOP) Example
169
+
170
+ ```typescript filename="src/http/admin/route.ts"
171
+ import { Controller, Get, Use } from 'exisjs/decorators'
172
+ import { requireRole } from 'exisjs/auth'
173
+
174
+ @Controller('/admin')
175
+ export default class AdminController {
176
+
177
+ @Get('/dashboard')
178
+ @Use(requireRole(['admin', 'superadmin']))
179
+ async getDashboard() {
180
+ return { metrics: [] }
181
+ }
182
+ }
183
+ ```
@@ -0,0 +1,132 @@
1
+ ---
2
+ title: "Caching"
3
+ description: "API Reference for ExisJS Caching and Revalidation (`exisjs/cache`)."
4
+ ---
5
+
6
+ # Caching (`exisjs/cache`)
7
+
8
+ ExisJS comes with a highly sophisticated built-in caching system that natively supports **stale-while-revalidate** semantics, intelligent `tags`-based cache invalidation, and multiple backing stores (Memory, FileSystem, and Redis).
9
+
10
+ ---
11
+
12
+ ## `cacheMiddleware(options)`
13
+
14
+ The primary way to use the cache is by attaching the `cacheMiddleware` directly to your GET routes. It intercepts responses, stores them in the cache, and serves them blazingly fast on subsequent identical requests.
15
+
16
+ ExisJS automatically injects an `X-Exis-Cache: HIT` or `X-Exis-Cache: MISS` header into the HTTP response so you can debug your cache behavior instantly!
17
+
18
+ **Class-Based (OOP)**
19
+ ```typescript filename="src/http/stats/route.ts"
20
+ import { Controller, Get, Use } from 'exisjs/decorators'
21
+ import { cacheMiddleware } from 'exisjs/cache'
22
+
23
+ @Controller('/stats')
24
+ export default class StatsController {
25
+
26
+ @Get('/')
27
+ @Use(
28
+ cacheMiddleware({
29
+ ttlMs: 60 * 1000, // 60 seconds
30
+ keyGenerator: (req) => `stats:${req.query.period || 'all'}`
31
+ })
32
+ )
33
+ async getStats() {
34
+ return db.queryHeavyStats()
35
+ }
36
+ }
37
+ ```
38
+
39
+ **Functional**
40
+ ```typescript filename="src/http/stats/route.ts"
41
+ import { controller, route } from 'exisjs/router'
42
+ import { cacheMiddleware } from 'exisjs/cache'
43
+
44
+ export default controller({
45
+ getStats: route.get('/', {
46
+ middleware: [
47
+ cacheMiddleware({
48
+ ttlMs: 60 * 1000, // 60 seconds
49
+
50
+ // You MUST provide a keyGenerator to prevent data leaking between users!
51
+ keyGenerator: (req) => `stats:${req.query.period || 'all'}`
52
+ })
53
+ ],
54
+ async handle() {
55
+ // This heavy query only runs once every 60 seconds per period
56
+ return db.queryHeavyStats()
57
+ }
58
+ })
59
+ })
60
+ ```
61
+
62
+ ### Options
63
+
64
+ - **`keyGenerator`** `(req => string)`: **Required.** Generates a unique cache key for the request. Never use a static key if the response varies by User ID or Query Parameters.
65
+ - **`ttlMs`** `(number)`: Time to live in milliseconds. If omitted, the cache never inherently expires based on time (relies on manual tags instead).
66
+ - **`tags`** `(string[] | function)`: A list of tags to associate with this cache entry, used for Next.js-style on-demand revalidation.
67
+
68
+ ---
69
+
70
+ ## Tag-Based Cache Invalidation
71
+
72
+ Instead of waiting for a TTL to expire, you can invalidate caches instantly using tags. This is exactly how Next.js App Router caching works.
73
+
74
+ When you cache a route, assign it a tag:
75
+
76
+ ```typescript filename="src/http/products/route.ts"
77
+ import { cacheMiddleware } from 'exisjs/cache'
78
+
79
+ route.get('/:id', {
80
+ middleware: [
81
+ cacheMiddleware({
82
+ // We tag this cache entry with the specific product ID
83
+ tags: (req) => [`product-${req.params.id}`, 'all-products'],
84
+ keyGenerator: (req) => `product:${req.params.id}`
85
+ })
86
+ ],
87
+ async handle(ctx) {
88
+ return db.products.find(ctx.params.id)
89
+ }
90
+ })
91
+ ```
92
+
93
+ Later, when a user updates the product via a `POST` or `PUT` route, you can instantly bust all caches tied to that tag using `revalidateTag()`!
94
+
95
+ ```typescript filename="src/http/products/route.ts"
96
+ import { revalidateTag } from 'exisjs/cache'
97
+
98
+ route.put('/:id', {
99
+ async handle(ctx) {
100
+ await db.products.update(ctx.params.id, ctx.body)
101
+
102
+ // Instantly purge the cache across the entire cluster!
103
+ await revalidateTag(`product-${ctx.params.id}`)
104
+
105
+ return { success: true }
106
+ }
107
+ })
108
+ ```
109
+
110
+ ---
111
+
112
+ ## Cache Drivers
113
+
114
+ ExisJS natively provides three driver implementations:
115
+
116
+ 1. **`FileSystemCacheStore`**: (Default). Writes cache data to `.exis/cache/*.json` on the disk. Extremely fast for static data and survives server reboots.
117
+ 2. **`MemoryCacheStore`**: Keeps caches purely in RAM. Perfect for development or extremely high-throughput, short-TTL data. (Includes automatic broadcast sync if running in an ExisJS Cluster).
118
+ 3. **`RedisCacheStore`**: The standard for production. Uses a minimal Redis client to handle distributed caching across massive fleets of servers.
119
+
120
+ ### Switching Stores Globally
121
+
122
+ While you can pass a `store` option directly into `cacheMiddleware()`, it is usually better to set the global cache store during application boot:
123
+
124
+ ```typescript filename="src/server.ts"
125
+ import { exis } from 'exisjs'
126
+ import { setCacheStore, RedisCacheStore } from 'exisjs/cache'
127
+
128
+ const app = exis()
129
+
130
+ // Use Redis instead of the default FileSystem cache
131
+ setCacheStore(new RedisCacheStore(myRedisClient))
132
+ ```
@@ -0,0 +1,71 @@
1
+ ---
2
+ title: "Circuit Breaker"
3
+ description: "Strict API Reference for the Circuit Breaker resilience utility."
4
+ ---
5
+
6
+ # Circuit Breaker (`exisjs/app`)
7
+
8
+ This document provides the strict API signatures and TypeScript interfaces exported for the `CircuitBreaker` utility. For high-level resilience guides, see the [Circuit Breaker Guide](/docs/circuit-breaker).
9
+
10
+ ---
11
+
12
+ ## `CircuitBreaker` Class
13
+
14
+ The `CircuitBreaker` class is a state machine that monitors the execution of async actions to prevent cascading failures.
15
+
16
+ ```typescript
17
+ import { CircuitBreaker } from 'exisjs/app'
18
+
19
+ class CircuitBreaker {
20
+ constructor(options?: CircuitBreakerOptions)
21
+
22
+ // The current state of the breaker
23
+ public state: CircuitState
24
+
25
+ // Executes the provided async action through the breaker
26
+ fire<T>(action: () => Promise<T>): Promise<T>
27
+ }
28
+ ```
29
+
30
+ ### `CircuitBreakerOptions`
31
+
32
+ ```typescript
33
+ interface CircuitBreakerOptions {
34
+ /** Maximum number of sequential failures before the breaker transitions to OPEN */
35
+ failureThreshold?: number // Default: 5
36
+
37
+ /** Time to wait (in ms) before transitioning from OPEN to HALF_OPEN to test recovery */
38
+ resetTimeoutMs?: number // Default: 10000 (10 seconds)
39
+ }
40
+ ```
41
+
42
+ ---
43
+
44
+ ## `CircuitState` Enum
45
+
46
+ The circuit breaker operates in one of three states.
47
+
48
+ ```typescript
49
+ enum CircuitState {
50
+ // Normal operation, all requests pass
51
+ CLOSED = 'CLOSED',
52
+
53
+ // Failing, all requests are instantly rejected
54
+ OPEN = 'OPEN',
55
+
56
+ // Testing recovery, exactly ONE request is allowed through
57
+ HALF_OPEN = 'HALF_OPEN',
58
+ }
59
+ ```
60
+
61
+ ---
62
+
63
+ ## `CircuitBreakerError`
64
+
65
+ If `fire()` is called while the breaker is `OPEN` (or if it is `HALF_OPEN` and a probe is already in flight), the breaker will instantly reject the promise with a `CircuitBreakerError`.
66
+
67
+ ```typescript
68
+ class CircuitBreakerError extends Error {
69
+ constructor(message: string)
70
+ }
71
+ ```