exisjs 0.8.1 → 0.9.1

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 (190) hide show
  1. package/dist/cli/commands/build.d.ts.map +1 -1
  2. package/dist/cli/commands/build.js +20 -1
  3. package/dist/cli/commands/build.js.map +1 -1
  4. package/dist/cli/commands/dev.d.ts +8 -0
  5. package/dist/cli/commands/dev.d.ts.map +1 -1
  6. package/dist/cli/commands/dev.js +96 -36
  7. package/dist/cli/commands/dev.js.map +1 -1
  8. package/dist/cli/index.js +1 -0
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/manifest.d.ts.map +1 -1
  11. package/dist/cli/manifest.js +2 -1
  12. package/dist/cli/manifest.js.map +1 -1
  13. package/dist/cli/routes.d.ts +2 -0
  14. package/dist/cli/routes.d.ts.map +1 -1
  15. package/dist/cli/routes.js +14 -5
  16. package/dist/cli/routes.js.map +1 -1
  17. package/dist/config/config.d.ts +3 -1
  18. package/dist/config/config.d.ts.map +1 -1
  19. package/dist/config/config.js.map +1 -1
  20. package/dist/config/types.d.ts +11 -1
  21. package/dist/config/types.d.ts.map +1 -1
  22. package/dist/cron/job.d.ts +2 -0
  23. package/dist/cron/job.d.ts.map +1 -1
  24. package/dist/cron/job.js +33 -1
  25. package/dist/cron/job.js.map +1 -1
  26. package/dist/cron/manager.d.ts +12 -1
  27. package/dist/cron/manager.d.ts.map +1 -1
  28. package/dist/cron/manager.js +7 -1
  29. package/dist/cron/manager.js.map +1 -1
  30. package/dist/cron/types.d.ts +24 -0
  31. package/dist/cron/types.d.ts.map +1 -1
  32. package/dist/database/lifecycle.d.ts +19 -2
  33. package/dist/database/lifecycle.d.ts.map +1 -1
  34. package/dist/database/lifecycle.js +92 -53
  35. package/dist/database/lifecycle.js.map +1 -1
  36. package/dist/decorators/index.d.ts +1 -0
  37. package/dist/decorators/index.d.ts.map +1 -1
  38. package/dist/decorators/index.js +5 -0
  39. package/dist/decorators/index.js.map +1 -1
  40. package/dist/decorators/pipes.d.ts +2 -0
  41. package/dist/decorators/pipes.d.ts.map +1 -1
  42. package/dist/decorators/pipes.js +39 -18
  43. package/dist/decorators/pipes.js.map +1 -1
  44. package/dist/decorators/transaction.d.ts +0 -21
  45. package/dist/decorators/transaction.d.ts.map +1 -1
  46. package/dist/decorators/transaction.js +52 -32
  47. package/dist/decorators/transaction.js.map +1 -1
  48. package/dist/di/container.d.ts +6 -0
  49. package/dist/di/container.d.ts.map +1 -1
  50. package/dist/di/container.js +73 -57
  51. package/dist/di/container.js.map +1 -1
  52. package/dist/docs/content/boundaries.mdx +2 -0
  53. package/dist/docs/content/cli/commands.mdx +2 -1
  54. package/dist/docs/content/config.mdx +6 -4
  55. package/dist/docs/content/cron.mdx +25 -1
  56. package/dist/docs/content/decorators.mdx +5 -5
  57. package/dist/docs/content/errors.mdx +37 -36
  58. package/dist/docs/content/installation.mdx +3 -4
  59. package/dist/docs/content/introduction.mdx +1 -1
  60. package/dist/docs/content/middleware.mdx +4 -2
  61. package/dist/docs/content/providers.mdx +2 -0
  62. package/dist/docs/content/requests.mdx +4 -2
  63. package/dist/docs/content/responses.mdx +72 -0
  64. package/dist/docs/content/security.mdx +93 -4
  65. package/dist/docs/content/testing.mdx +38 -0
  66. package/dist/docs/content/validation.mdx +34 -1
  67. package/dist/docs/reference/README.md +16 -16
  68. package/dist/docs/reference/config.mdx +1 -1
  69. package/dist/docs/reference/database.mdx +3 -2
  70. package/dist/docs/reference/decorators.mdx +2 -4
  71. package/dist/docs/reference/middleware.mdx +1 -1
  72. package/dist/docs/reference/router.mdx +5 -7
  73. package/dist/docs/reference/validator.mdx +3 -0
  74. package/dist/error/errors.d.ts +9 -1
  75. package/dist/error/errors.d.ts.map +1 -1
  76. package/dist/error/errors.js +24 -15
  77. package/dist/error/errors.js.map +1 -1
  78. package/dist/index.d.ts +11 -2
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/lib/inspect-routes.js +2 -0
  81. package/dist/lib/inspect-routes.js.map +1 -1
  82. package/dist/middleware/compression.d.ts.map +1 -1
  83. package/dist/middleware/compression.js +6 -5
  84. package/dist/middleware/compression.js.map +1 -1
  85. package/dist/middleware/idempotency.d.ts +25 -23
  86. package/dist/middleware/idempotency.d.ts.map +1 -1
  87. package/dist/middleware/idempotency.js +70 -35
  88. package/dist/middleware/idempotency.js.map +1 -1
  89. package/dist/middleware/middleware.d.ts.map +1 -1
  90. package/dist/middleware/middleware.js +48 -34
  91. package/dist/middleware/middleware.js.map +1 -1
  92. package/dist/middleware/rate-limit.d.ts +36 -0
  93. package/dist/middleware/rate-limit.d.ts.map +1 -1
  94. package/dist/middleware/rate-limit.js +33 -19
  95. package/dist/middleware/rate-limit.js.map +1 -1
  96. package/dist/middleware/security.d.ts.map +1 -1
  97. package/dist/middleware/security.js +12 -5
  98. package/dist/middleware/security.js.map +1 -1
  99. package/dist/router/authorize.d.ts +43 -0
  100. package/dist/router/authorize.d.ts.map +1 -0
  101. package/dist/router/authorize.js +66 -0
  102. package/dist/router/authorize.js.map +1 -0
  103. package/dist/router/controller-registrar.d.ts.map +1 -1
  104. package/dist/router/controller-registrar.js +121 -156
  105. package/dist/router/controller-registrar.js.map +1 -1
  106. package/dist/router/route-scanner.d.ts +3 -0
  107. package/dist/router/route-scanner.d.ts.map +1 -1
  108. package/dist/router/route-scanner.js +32 -21
  109. package/dist/router/route-scanner.js.map +1 -1
  110. package/dist/router/router.d.ts +5 -0
  111. package/dist/router/router.d.ts.map +1 -1
  112. package/dist/router/router.js +83 -75
  113. package/dist/router/router.js.map +1 -1
  114. package/dist/router/scanner/boundary-loader.d.ts.map +1 -1
  115. package/dist/router/scanner/boundary-loader.js +37 -38
  116. package/dist/router/scanner/boundary-loader.js.map +1 -1
  117. package/dist/router/scanner/cron-mounter.d.ts.map +1 -1
  118. package/dist/router/scanner/cron-mounter.js +32 -18
  119. package/dist/router/scanner/cron-mounter.js.map +1 -1
  120. package/dist/router/scanner/functional-compiler.d.ts.map +1 -1
  121. package/dist/router/scanner/functional-compiler.js +55 -156
  122. package/dist/router/scanner/functional-compiler.js.map +1 -1
  123. package/dist/router/send-result.d.ts +24 -0
  124. package/dist/router/send-result.d.ts.map +1 -0
  125. package/dist/router/send-result.js +81 -0
  126. package/dist/router/send-result.js.map +1 -0
  127. package/dist/server/app.d.ts +5 -0
  128. package/dist/server/app.d.ts.map +1 -1
  129. package/dist/server/app.js +48 -6
  130. package/dist/server/app.js.map +1 -1
  131. package/dist/server/engines/node-engine.d.ts.map +1 -1
  132. package/dist/server/engines/node-engine.js +6 -1
  133. package/dist/server/engines/node-engine.js.map +1 -1
  134. package/dist/server/helpers/body-parser.d.ts +1 -1
  135. package/dist/server/helpers/body-parser.d.ts.map +1 -1
  136. package/dist/server/helpers/body-parser.js +9 -7
  137. package/dist/server/helpers/body-parser.js.map +1 -1
  138. package/dist/server/helpers/conditional.d.ts +60 -0
  139. package/dist/server/helpers/conditional.d.ts.map +1 -0
  140. package/dist/server/helpers/conditional.js +189 -0
  141. package/dist/server/helpers/conditional.js.map +1 -0
  142. package/dist/server/helpers/etag.d.ts +0 -1
  143. package/dist/server/helpers/etag.d.ts.map +1 -1
  144. package/dist/server/helpers/etag.js +0 -10
  145. package/dist/server/helpers/etag.js.map +1 -1
  146. package/dist/server/helpers/index.d.ts +2 -1
  147. package/dist/server/helpers/index.d.ts.map +1 -1
  148. package/dist/server/helpers/index.js +16 -2
  149. package/dist/server/helpers/index.js.map +1 -1
  150. package/dist/server/request-handler.d.ts +2 -0
  151. package/dist/server/request-handler.d.ts.map +1 -1
  152. package/dist/server/request-handler.js +23 -4
  153. package/dist/server/request-handler.js.map +1 -1
  154. package/dist/server/request.d.ts +19 -4
  155. package/dist/server/request.d.ts.map +1 -1
  156. package/dist/server/request.js +60 -84
  157. package/dist/server/request.js.map +1 -1
  158. package/dist/server/response.d.ts +45 -2
  159. package/dist/server/response.d.ts.map +1 -1
  160. package/dist/server/response.js +157 -70
  161. package/dist/server/response.js.map +1 -1
  162. package/dist/swagger/mounter.d.ts.map +1 -1
  163. package/dist/swagger/mounter.js +2 -0
  164. package/dist/swagger/mounter.js.map +1 -1
  165. package/dist/testing/client.d.ts +32 -0
  166. package/dist/testing/client.d.ts.map +1 -1
  167. package/dist/testing/client.js +69 -0
  168. package/dist/testing/client.js.map +1 -1
  169. package/dist/testing/index.d.ts +10 -2
  170. package/dist/testing/index.d.ts.map +1 -1
  171. package/dist/testing/index.js +7 -2
  172. package/dist/testing/index.js.map +1 -1
  173. package/dist/validator/builder.d.ts +33 -16
  174. package/dist/validator/builder.d.ts.map +1 -1
  175. package/dist/validator/builder.js +11 -17
  176. package/dist/validator/builder.js.map +1 -1
  177. package/dist/validator/engine.d.ts +28 -1
  178. package/dist/validator/engine.d.ts.map +1 -1
  179. package/dist/validator/engine.js +212 -124
  180. package/dist/validator/engine.js.map +1 -1
  181. package/dist/validator/error.d.ts +4 -0
  182. package/dist/validator/error.d.ts.map +1 -1
  183. package/dist/validator/error.js +20 -4
  184. package/dist/validator/error.js.map +1 -1
  185. package/dist/validator/index.d.ts +12 -1
  186. package/dist/validator/index.d.ts.map +1 -1
  187. package/dist/validator/index.js.map +1 -1
  188. package/dist/validator/tex-types.d.ts +29 -2
  189. package/dist/validator/tex-types.d.ts.map +1 -1
  190. package/package.json +3 -5
@@ -21,6 +21,39 @@ class Container {
21
21
  this.providers = new Map();
22
22
  this.singletonCache = new Map();
23
23
  this.inFlight = new Set();
24
+ // One frame per class being built. A frame is marked when anything built
25
+ // inside it is request-scoped, so the class holding it is request-scoped
26
+ // too instead of a singleton that keeps the first request's instance.
27
+ this.scopeFrames = [];
28
+ }
29
+ markRequestScoped() {
30
+ const frame = this.scopeFrames[this.scopeFrames.length - 1];
31
+ if (frame)
32
+ frame.request = true;
33
+ }
34
+ build(create) {
35
+ const frame = { request: false };
36
+ this.scopeFrames.push(frame);
37
+ try {
38
+ return { value: create(), dependsOnRequest: frame.request };
39
+ }
40
+ finally {
41
+ this.scopeFrames.pop();
42
+ }
43
+ }
44
+ store(token, value, scope, dependsOnRequest, requestCache) {
45
+ if (scope === 'request' || dependsOnRequest)
46
+ this.markRequestScoped();
47
+ if (scope === 'transient')
48
+ return;
49
+ if (scope === 'request' || dependsOnRequest) {
50
+ if (!requestCache) {
51
+ throw new Error(`Cannot resolve request-scoped provider '${String(token)}' outside of a request context. This usually happens if you try to resolve a request-scoped dependency during app startup, inside a background job, or without passing the request context cache.`);
52
+ }
53
+ requestCache.set(token, value);
54
+ return;
55
+ }
56
+ this.singletonCache.set(token, value);
24
57
  }
25
58
  provide(tokenOrProvider, provider) {
26
59
  if (provider === undefined &&
@@ -42,6 +75,10 @@ class Container {
42
75
  this.providers.set(unwrappedToken, provider);
43
76
  this.singletonCache.delete(unwrappedToken);
44
77
  }
78
+ /** The cached singleton for a token, without building anything. */
79
+ peekSingleton(token) {
80
+ return this.singletonCache.get(token);
81
+ }
45
82
  clearCache() {
46
83
  this.singletonCache.clear();
47
84
  this.inFlight.clear();
@@ -179,6 +216,7 @@ class Container {
179
216
  }
180
217
  // 2. Check request cache
181
218
  if (requestCache && requestCache.has(unwrappedToken)) {
219
+ this.markRequestScoped();
182
220
  return requestCache.get(unwrappedToken);
183
221
  }
184
222
  // 3. Circular Dependency Resolution: Return Lazy Proxy
@@ -210,19 +248,8 @@ class Container {
210
248
  this.inFlight.add(unwrappedToken);
211
249
  try {
212
250
  const scope = unwrappedToken.prototype?.[exports.SCOPE_METADATA] || 'singleton';
213
- const instance = this.instantiateClass(unwrappedToken, requestCache);
214
- if (scope === 'transient') {
215
- return instance;
216
- }
217
- if (scope === 'request') {
218
- if (!requestCache) {
219
- throw new Error(`Cannot resolve request-scoped provider '${String(unwrappedToken)}' outside of a request context. This usually happens if you try to resolve a request-scoped dependency during app startup, inside a background job, or without passing the request context cache.`);
220
- }
221
- requestCache.set(unwrappedToken, instance);
222
- }
223
- else {
224
- this.singletonCache.set(unwrappedToken, instance);
225
- }
251
+ const { value: instance, dependsOnRequest } = this.build(() => this.instantiateClass(unwrappedToken, requestCache));
252
+ this.store(unwrappedToken, instance, scope, dependsOnRequest, requestCache);
226
253
  return instance;
227
254
  }
228
255
  catch (err) {
@@ -237,55 +264,44 @@ class Container {
237
264
  }
238
265
  throw new Error('Provider not found for token: ' + String(unwrappedToken));
239
266
  }
240
- let resolvedValue;
241
267
  let scope = 'singleton';
242
268
  this.inFlight.add(unwrappedToken);
243
269
  try {
244
- if (typeof provider === 'function') {
245
- const classScope = provider.prototype?.[exports.SCOPE_METADATA] || 'singleton';
246
- scope = classScope;
247
- resolvedValue = this.instantiateClass(provider, requestCache);
248
- }
249
- else if (provider && typeof provider === 'object') {
250
- if ('scope' in provider && provider.scope) {
251
- scope = provider.scope;
252
- }
253
- if ('useValue' in provider) {
254
- resolvedValue = provider.useValue;
270
+ const { value: resolvedValue, dependsOnRequest } = this.build(() => {
271
+ if (typeof provider === 'function') {
272
+ scope = provider.prototype?.[exports.SCOPE_METADATA] || 'singleton';
273
+ return this.instantiateClass(provider, requestCache);
255
274
  }
256
- else if ('useExisting' in provider) {
257
- resolvedValue = this.resolve(provider.useExisting, requestCache);
258
- }
259
- else if ('useFactory' in provider) {
260
- const factoryProv = provider;
261
- const injectTokens = Array.isArray(factoryProv.inject)
262
- ? factoryProv.inject
263
- : [];
264
- const injectedDeps = injectTokens.map((depToken) => this.resolve(depToken, requestCache));
265
- resolvedValue = factoryProv.useFactory(...injectedDeps);
266
- }
267
- else if ('useClass' in provider) {
268
- resolvedValue = this.instantiateClass(provider.useClass, requestCache);
269
- }
270
- else {
271
- resolvedValue = provider;
272
- }
273
- }
274
- else {
275
- resolvedValue = provider;
276
- }
277
- if (scope === 'transient') {
278
- return resolvedValue;
279
- }
280
- if (scope === 'request') {
281
- if (!requestCache) {
282
- throw new Error(`Cannot resolve request-scoped provider '${String(unwrappedToken)}' outside of a request context. This usually happens if you try to resolve a request-scoped dependency during app startup, inside a background job, or without passing the request context cache.`);
275
+ if (provider && typeof provider === 'object') {
276
+ if ('scope' in provider && provider.scope) {
277
+ scope = provider.scope;
278
+ }
279
+ if ('useValue' in provider) {
280
+ return provider.useValue;
281
+ }
282
+ if ('useExisting' in provider) {
283
+ return this.resolve(provider.useExisting, requestCache);
284
+ }
285
+ if ('useFactory' in provider) {
286
+ const factoryProv = provider;
287
+ const injectTokens = Array.isArray(factoryProv.inject)
288
+ ? factoryProv.inject
289
+ : [];
290
+ const injectedDeps = injectTokens.map((depToken) => this.resolve(depToken, requestCache));
291
+ return factoryProv.useFactory(...injectedDeps);
292
+ }
293
+ if ('useClass' in provider) {
294
+ // Without an explicit scope, the class's @Injectable({ scope }) applies
295
+ if (!('scope' in provider && provider.scope)) {
296
+ scope =
297
+ provider.useClass.prototype?.[exports.SCOPE_METADATA] || 'singleton';
298
+ }
299
+ return this.instantiateClass(provider.useClass, requestCache);
300
+ }
283
301
  }
284
- requestCache.set(unwrappedToken, resolvedValue);
285
- }
286
- else {
287
- this.singletonCache.set(unwrappedToken, resolvedValue);
288
- }
302
+ return provider;
303
+ });
304
+ this.store(unwrappedToken, resolvedValue, scope, dependsOnRequest, requestCache);
289
305
  return resolvedValue;
290
306
  }
291
307
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/di/container.ts"],"names":[],"mappings":";;;AAAA,+CAAmE;AAsCtD,QAAA,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;AACpD,QAAA,wBAAwB,GAAG,MAAM,CAAC,GAAG,CAChD,+BAA+B,CAChC,CAAA;AACY,QAAA,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAA;AACxD,QAAA,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;AAExD,4EAA4E;AAC5E,wEAAwE;AACxE,uEAAuE;AACvE,2BAA2B;AACd,QAAA,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAC9D,MAAM,iBAAiB,GAAG,MAAM,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;AAEhE,SAAS,kBAAkB,CAAC,WAAgB;IAC1C,OAAO,CACL,WAAW,CAAC,uBAAe,CAAC,KAAK,IAAI;QACrC,WAAW,CAAC,SAAS,EAAE,CAAC,iBAAiB,CAAC,KAAK,SAAS,CACzD,CAAA;AACH,CAAC;AAED,MAAa,SAAS;IAAtB;QACU,cAAS,GAAG,IAAI,GAAG,EAAY,CAAA;QAC/B,mBAAc,GAAG,IAAI,GAAG,EAAY,CAAA;QACpC,aAAQ,GAAG,IAAI,GAAG,EAAO,CAAA;IAwXnC,CAAC;IAtXC,OAAO,CACL,eAAqD,EACrD,QAAgC;QAEhC,IACE,QAAQ,KAAK,SAAS;YACtB,eAAe;YACf,OAAO,eAAe,KAAK,QAAQ;YACnC,SAAS,IAAI,eAAe,EAC5B,CAAC;YACD,MAAM,UAAU,GAAG,eAAoC,CAAA;YACvD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAQ,CAAA;YACpC,MAAM,cAAc,GAAG,IAAA,0BAAY,EAAC,QAAQ,CAAC;gBAC3C,CAAC,CAAE,QAAgC,CAAC,UAAU,EAAE;gBAChD,CAAC,CAAC,QAAQ,CAAA;YACZ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,CAAC,CAAA;YAC9C,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;YAC1C,OAAM;QACR,CAAC;QAED,MAAM,cAAc,GAAG,IAAA,0BAAY,EAAC,eAAe,CAAC;YAClD,CAAC,CAAE,eAAuC,CAAC,UAAU,EAAE;YACvD,CAAC,CAAC,eAAe,CAAA;QACnB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAA;QAC5C,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;IAC5C,CAAC;IAED,UAAU;QACR,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAA;QAC3B,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;IACvB,CAAC;IAED,gBAAgB,CACd,WAAsC,EACtC,YAA4B;QAE5B,MAAM,YAAY,GAAkC;YAClD,GAAG,CAAC,CAAC,WAAW,CAAC,SAAS;gBACvB,WAAW,CAAC,SAAiB,CAAC,uBAAe,CAAC,CAAC;gBAChD,EAAE,CAAC;YACL,GAAG,CAAE,WAAmB,CAAC,uBAAe,CAAC,IAAI,EAAE,CAAC;SACjD,CAAA;QACD,MAAM,cAAc,GACjB,WAAmB,CAAC,yBAAiB,CAAC;YACvC,CAAC,WAAW,CAAC,SAAS;gBACnB,WAAW,CAAC,SAAiB,CAAC,yBAAiB,CAAC,CAAC;YACpD,IAAI,GAAG,EAAE,CAAA;QAEX,iFAAiF;QACjF,MAAM,iBAAiB,GACrB,CAAC,OAAQ,UAAkB,CAAC,OAAO,EAAE,WAAW,KAAK,UAAU;YAC7D,CAAC,CAAE,UAAkB,CAAC,OAAO,CAAC,WAAW,CACrC,mBAAmB,EACnB,WAAW,CACZ;gBACD,CAAC,WAAW,CAAC,SAAS;oBACpB,CAAC,CAAE,UAAkB,CAAC,OAAO,CAAC,WAAW,CACrC,mBAAmB,EACnB,WAAW,CAAC,SAAS,CACtB;oBACH,CAAC,CAAC,SAAS,CAAC;YAChB,CAAC,CAAC,SAAS,CAAC;YACb,WAAmB,CAAC,mBAAmB,CAAC;YACxC,WAAW,CAAC,SAAiB,EAAE,CAAC,mBAAmB,CAAC;YACrD,EAAE,CAAA;QAEJ,mFAAmF;QACnF,IAAI,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,iBAAiB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAClD,MAAM,SAAS,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAA;gBACtC,IACE,YAAY,CAAC,CAAC,CAAC,KAAK,SAAS;oBAC7B,OAAO,SAAS,KAAK,UAAU;oBAC/B,SAAS,KAAK,MAAM;oBACpB,SAAS,KAAK,MAAM;oBACpB,SAAS,KAAK,MAAM;oBACpB,SAAS,KAAK,OAAO;oBACrB,SAAS,KAAK,MAAM;oBACpB,SAAS,KAAK,KAAK;oBACnB,SAAS,KAAK,QAAQ,EACtB,CAAC;oBACD,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;gBAC7B,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAC1D,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAEzE,IAAI,QAAW,CAAA;QAEf,IACE,QAAQ,KAAK,CAAC,CAAC;YACf,cAAc,CAAC,IAAI,KAAK,CAAC;YACzB,CAAC,CAAC,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,EACjD,CAAC;YACD,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAA;QAC9B,CAAC;aAAM,CAAC;YACN,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC1B,QAAQ,GAAG,CAAC,EACZ,WAAW,CAAC,MAAM,IAAI,CAAC,EACvB,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CACpD,CAAA;YAED,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;gBACtB,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAA;YAC9B,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,GAAU,EAAE,CAAA;gBACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;oBACrC,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,CAAA;oBAC7B,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;oBAExC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;wBACxB,IAAI,CAAC;4BACH,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAA;wBAC9C,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,IAAI,UAAU,EAAE,CAAC;gCACf,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;4BACtB,CAAC;iCAAM,CAAC;gCACN,MAAM,GAAG,CAAA;4BACX,CAAC;wBACH,CAAC;oBACH,CAAC;yBAAM,IACL,UAAU;wBACV,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,CAAC;wBAC9B,CAAC,kBAAkB,CAAC,WAAW,CAAC,EAChC,CAAC;wBACD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;oBACtB,CAAC;yBAAM,CAAC;wBACN,6DAA6D;wBAC7D,qDAAqD;wBACrD,MAAM,IAAI,KAAK,CACb,yCAAyC,CAAC,GAAG,CAAC,OAAO,WAAW,CAAC,IAAI,IAAI,OAAO,wCAAwC;4BACtH,iIAAiI;4BACjI,yGAAyG,CAC5G,CAAA;oBACH,CAAC;gBACH,CAAC;gBACD,QAAQ,GAAG,IAAI,WAAW,CAAC,GAAG,IAAI,CAAC,CAAA;YACrC,CAAC;QACH,CAAC;QAED,6EAA6E;QAC7E,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;YAC1B,MAAM,UAAU,GAAU,EAAE,CAAA;YAC5B,IAAI,IAAI,GAAG,WAAW,CAAC,SAAS,CAAA;YAChC,OAAO,IAAI,IAAI,IAAI,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;gBACzC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;gBACxB,IAAI,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;YACpC,CAAC;YAED,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;gBAC3B,MAAM,cAAc,GAGd,CAAC,CAAC,gCAAwB,CAAC,IAAI,EAAE,CAAA;gBACvC,MAAM,QAAQ,GAAyB,CAAC,CAAC,yBAAiB,CAAC,IAAI,IAAI,GAAG,EAAE,CAAA;gBAExE,KAAK,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,cAAc,EAAE,CAAC;oBACpD,IAAI,aAAa,GAAG,KAAK,CAAA;oBACzB,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;wBAChC,MAAM,aAAa,GACjB,OAAQ,UAAkB,CAAC,OAAO,EAAE,WAAW,KAAK,UAAU;4BAC5D,CAAC,CAAE,UAAkB,CAAC,OAAO,CAAC,WAAW,CACrC,aAAa,EACb,CAAC,EACD,WAAW,CACZ;4BACH,CAAC,CAAC,SAAS,CAAA;wBACf,IAAI,aAAa;4BAAE,aAAa,GAAG,aAAa,CAAA;oBAClD,CAAC;oBAED,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;wBAChC,IAAI,CAAC;4BACH,CAAC;4BAAC,QAAgB,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,OAAO,CAC5C,aAAa,EACb,YAAY,CACb,CAAA;wBACH,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,IAAI,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;gCAC9B,CAAC;gCAAC,QAAgB,CAAC,WAAW,CAAC,GAAG,SAAS,CAAA;4BAC7C,CAAC;iCAAM,CAAC;gCACN,MAAM,GAAG,CAAA;4BACX,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAA;IACjB,CAAC;IAED,OAAO,CAAI,KAAuB,EAAE,YAA4B;QAC9D,MAAM,cAAc,GAAQ,IAAA,0BAAY,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;QAE5E,2BAA2B;QAC3B,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QAChD,CAAC;QAED,yBAAyB;QACzB,IAAI,YAAY,IAAI,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YACrD,OAAO,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QACzC,CAAC;QAED,uDAAuD;QACvD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YACtC,OAAO,IAAI,KAAK,CAAC,EAAS,EAAE;gBAC1B,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE;oBACf,MAAM,YAAY,GAChB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,CAAC;wBACvC,CAAC,YAAY,IAAI,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAA;oBACpD,IAAI,YAAY,EAAE,CAAC;wBACjB,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;wBAC9B,OAAO,OAAO,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;oBACjE,CAAC;oBACD,OAAO,SAAS,CAAA;gBAClB,CAAC;gBACD,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;oBACtB,MAAM,YAAY,GAChB,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,CAAC;wBACvC,CAAC,YAAY,IAAI,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAA;oBACpD,IAAI,YAAY,EAAE,CAAC;wBACjB,YAAY,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;wBAC1B,OAAO,IAAI,CAAA;oBACb,CAAC;oBACD,OAAO,KAAK,CAAA;gBACd,CAAC;aACF,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QACnD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,OAAO,cAAc,KAAK,UAAU,EAAE,CAAC;gBACzC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;gBACjC,IAAI,CAAC;oBACH,MAAM,KAAK,GACT,cAAc,CAAC,SAAS,EAAE,CAAC,sBAAc,CAAC,IAAI,WAAW,CAAA;oBAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CACpC,cAA2C,EAC3C,YAAY,CACb,CAAA;oBAED,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;wBAC1B,OAAO,QAAQ,CAAA;oBACjB,CAAC;oBAED,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;wBACxB,IAAI,CAAC,YAAY,EAAE,CAAC;4BAClB,MAAM,IAAI,KAAK,CACb,2CAA2C,MAAM,CAC/C,cAAc,CACf,mMAAmM,CACrM,CAAA;wBACH,CAAC;wBACD,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAA;oBAC5C,CAAC;yBAAM,CAAC;wBACN,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAA;oBACnD,CAAC;oBACD,OAAO,QAAQ,CAAA;gBACjB,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBAClB,MAAM,QAAQ,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;oBACjE,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,sCAAsC,MAAM,CAC1C,cAAc,CACf,kBAAkB,QAAQ,EAAE,CAC9B,CACA;oBAAC,KAAa,CAAC,KAAK,GAAG,GAAG,CAAA;oBAC3B,MAAM,KAAK,CAAA;gBACb,CAAC;wBAAS,CAAC;oBACT,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;gBACtC,CAAC;YACH,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAA;QAC5E,CAAC;QAED,IAAI,aAAkB,CAAA;QACtB,IAAI,KAAK,GAA0C,WAAW,CAAA;QAE9D,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QACjC,IAAI,CAAC;YACH,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACnC,MAAM,UAAU,GACb,QAAgB,CAAC,SAAS,EAAE,CAAC,sBAAc,CAAC,IAAI,WAAW,CAAA;gBAC9D,KAAK,GAAG,UAAU,CAAA;gBAClB,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAe,EAAE,YAAY,CAAC,CAAA;YACtE,CAAC;iBAAM,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACpD,IAAI,OAAO,IAAI,QAAQ,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBAC1C,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAA;gBACxB,CAAC;gBAED,IAAI,UAAU,IAAI,QAAQ,EAAE,CAAC;oBAC3B,aAAa,GAAI,QAA6B,CAAC,QAAQ,CAAA;gBACzD,CAAC;qBAAM,IAAI,aAAa,IAAI,QAAQ,EAAE,CAAC;oBACrC,aAAa,GAAG,IAAI,CAAC,OAAO,CACzB,QAAgC,CAAC,WAAW,EAC7C,YAAY,CACb,CAAA;gBACH,CAAC;qBAAM,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;oBACpC,MAAM,WAAW,GAAG,QAA8B,CAAA;oBAClD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC;wBACpD,CAAC,CAAC,WAAW,CAAC,MAAM;wBACpB,CAAC,CAAC,EAAE,CAAA;oBACN,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CACjD,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CACrC,CAAA;oBACD,aAAa,GAAG,WAAW,CAAC,UAAU,CAAC,GAAG,YAAY,CAAC,CAAA;gBACzD,CAAC;qBAAM,IAAI,UAAU,IAAI,QAAQ,EAAE,CAAC;oBAClC,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAClC,QAA6B,CAAC,QAAQ,EACvC,YAAY,CACb,CAAA;gBACH,CAAC;qBAAM,CAAC;oBACN,aAAa,GAAG,QAAQ,CAAA;gBAC1B,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,aAAa,GAAG,QAAQ,CAAA;YAC1B,CAAC;YAED,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;gBAC1B,OAAO,aAAa,CAAA;YACtB,CAAC;YAED,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,MAAM,IAAI,KAAK,CACb,2CAA2C,MAAM,CAC/C,cAAc,CACf,mMAAmM,CACrM,CAAA;gBACH,CAAC;gBACD,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,aAAa,CAAC,CAAA;YACjD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,cAAc,EAAE,aAAa,CAAC,CAAA;YACxD,CAAC;YAED,OAAO,aAAa,CAAA;QACtB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;QACtC,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,aAAa;QACxB,KAAK,MAAM,CAAC,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC/C,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC7C,IAAI,OAAO,QAAQ,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;oBAC1C,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAA;gBACzB,CAAC;gBACD,IAAI,OAAO,QAAQ,CAAC,YAAY,KAAK,UAAU,EAAE,CAAC;oBAChD,MAAM,QAAQ,CAAC,YAAY,EAAE,CAAA;gBAC/B,CAAC;gBACD,IAAI,OAAO,QAAQ,CAAC,sBAAsB,KAAK,UAAU,EAAE,CAAC;oBAC1D,MAAM,QAAQ,CAAC,sBAAsB,EAAE,CAAA;gBACzC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,gBAAgB,CAAC,MAAe;QAC3C,KAAK,MAAM,CAAC,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC/C,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC7C,IAAI,OAAO,QAAQ,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;oBAC7C,MAAM,QAAQ,CAAC,SAAS,EAAE,CAAA;gBAC5B,CAAC;gBACD,IAAI,OAAO,QAAQ,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;oBACnD,MAAM,QAAQ,CAAC,eAAe,EAAE,CAAA;gBAClC,CAAC;gBACD,IAAI,OAAO,QAAQ,CAAC,qBAAqB,KAAK,UAAU,EAAE,CAAC;oBACzD,MAAM,QAAQ,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAA;gBAC9C,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF;AA3XD,8BA2XC"}
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/di/container.ts"],"names":[],"mappings":"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"}
@@ -148,6 +148,8 @@ export default class AdminBoundary {
148
148
  }
149
149
  ```
150
150
 
151
+ `next()` resolves with what the route sent. Returning that value (or returning nothing) sends the route's response exactly as the route sent it, with the same type and `Content-Type`; a `Buffer` stays binary. Returning a different value replaces the response, and that value is sent like a handler's return value.
152
+
151
153
  ---
152
154
 
153
155
  ## Boundary Cascading & Execution Order
@@ -26,10 +26,11 @@ npx exisjs dev [options]
26
26
  | `-p, --port <port>` | `number` | `3000` | Port to listen on (overrides `.env` and `exis.config.ts`). |
27
27
  | `-H, --host <host>` | `string` | `localhost` | Host address to bind to. |
28
28
  | `-e, --entry <file>` | `string` | `src/http/server.ts` | Custom entry file path to bootstrap the application. |
29
+ | `--force` | `boolean` | `false` | Start even if a dev server for the same port appears to be running. |
29
30
 
30
31
  ### Behavior
31
32
  - **Port Collision Detection**: If the specified port is in use, the CLI automatically increments to the next open port.
32
- - **PID Lockfile**: Manages `.exis/dev.pid` to prevent duplicate dev server processes in the same workspace.
33
+ - **One server per port**: `.exis/dev-<port>.pid` stops a second `exisjs dev` on the same port. A second instance on another port (`--port 4010`, for example against a test database) is allowed. A lock left by a crashed process is ignored once its port is free; `--force` skips the check.
33
34
 
34
35
  ---
35
36
 
@@ -147,11 +147,13 @@ export default defineConfig({
147
147
  | Option | Type | Default | Description |
148
148
  | :--- | :--- | :--- | :--- |
149
149
  | `cors` | `boolean \| CorsConfig` | `{ origin: '*' }` | Global CORS policy. Can be overridden in boundaries or routes. `credentials: true` requires explicit origins: with `origin: '*'` it is ignored and a warning is logged, because browsers reject that combination. |
150
- | `helmet` | `boolean \| HelmetConfig` | `{ enabled: true }` | Security headers (CSP, X-Frame-Options, HSTS). |
150
+ | `helmet` | `boolean \| HelmetConfig` | `{ enabled: true }` | Security headers, including a same-origin `Content-Security-Policy`. `contentSecurityPolicy: false` sends no CSP; a string replaces the default. |
151
+ | `authorization` | `AuthorizationConfig` | `{}` | `defaultPolicy: 'authenticated'` closes controller routes that declare no rule unless they are `@Public()`. `superUser: (user) => boolean` lets matching users pass every `@Roles` / `@Permissions` rule. |
152
+ | `errors` | `{ legacyFields?: boolean }` | `{}` | `legacyFields: true` adds the pre-0.9 duplicate fields to error bodies. Deprecated. |
151
153
  | `compression` | `boolean` | `false` | Gzip/Brotli HTTP response compression. |
152
154
  | `bodyLimit` | `number` | `10485760` (10MB) | Maximum request payload body size in bytes. |
153
155
  | `blockSuspiciousProbes` | `boolean \| BlockProbesOptions` | `false` | Blocks known vulnerability scanners and malicious probes (`.php`, `wp-admin`). |
154
- | `etag` | `boolean` | `false` | Generates weak ETags for cache validation on responses. |
156
+ | `etag` | `boolean` | `false` | Weak ETags on successful GET/HEAD responses, with `304 Not Modified` for a matching `If-None-Match`. See [Caching & Conditional Requests](/docs/responses#caching--conditional-requests-etag-304-412). |
155
157
  | `transformResponse` | `boolean \| Function` | `false` | Standardizes handler response payload envelopes. |
156
158
 
157
159
  ### Logging & Diagnostics
@@ -161,8 +163,8 @@ export default defineConfig({
161
163
  | `logger` | `boolean \| LoggerConfig` | `false` | Pino logger settings (`level`, `pretty`, `redact`). |
162
164
  | `debugRouting` | `boolean` | `false` | Outputs detailed route resolution logs per request in development. |
163
165
  | `asyncContext` | `boolean` | `false` | Enables `AsyncLocalStorage` context tracking (`getContext()`). |
164
- | `docs` / `swagger` | `boolean \| SwaggerConfig` | `{ enabled: false }` | OpenAPI 3.1 specification and Swagger UI endpoint generation. Served in every environment when enabled; use `enabled: process.env.NODE_ENV !== 'production'` to hide it in production. |
165
- | `healthcheck` | `boolean \| HealthCheckConfig` | `{ enabled: false }` | System health endpoint (`path: '/_health'`). |
166
+ | `docs` / `swagger` | `boolean \| SwaggerConfig` | `{ enabled: false }` | OpenAPI 3.1 specification and Swagger UI endpoint generation. Served in every environment when enabled; the project template uses `enabled: process.env.NODE_ENV !== 'production'` so published API docs do not help attackers. |
167
+ | `healthcheck` | `boolean \| HealthCheckConfig` | `{ enabled: false }` | `path` (`/_health`): liveness, runs `checks`. `readyPath` (`/_ready`, `false` to disable): readiness, `503` unless `checks` pass, registered databases are healthy and the app is not shutting down. |
166
168
  | `metrics` | `boolean \| MetricsConfig` | `{ enabled: false }` | Prometheus metrics endpoint (`path: '/metrics'`). |
167
169
  | `telemetry` | `boolean \| TelemetryConfig` | `{ enabled: false }` | OpenTelemetry tracing and metrics export. |
168
170
 
@@ -239,13 +239,37 @@ CronExpression.EVERY_YEAR // '0 0 1 1 *'
239
239
  ### 1. Overlap Prevention (`preventOverlap: true`)
240
240
  If a job takes longer than its scheduling frequency (for example, a database migration running for 90 seconds on a 60-second interval), standard Node.js timers cause overlapping concurrent runs. ExisJS locks execution during active runs and skips overlapping ticks until the current run completes.
241
241
 
242
+ `preventOverlap` works inside one process. When the app runs on several instances, every instance runs every job; set `app.cron.lock` so only the instance holding the lock runs each tick:
243
+
244
+ ```typescript filename="src/http/server.ts"
245
+ import { randomUUID } from 'node:crypto'
246
+ const instanceId = randomUUID()
247
+
248
+ export default exis({
249
+ async onStart(app) {
250
+ app.cron.lock = {
251
+ acquire: async (job, ttlMs) =>
252
+ (await redis.set(`cron:${job}`, instanceId, 'PX', ttlMs, 'NX')) === 'OK',
253
+ release: async (job) => {
254
+ if ((await redis.get(`cron:${job}`)) === instanceId) await redis.del(`cron:${job}`)
255
+ },
256
+ }
257
+ },
258
+ })
259
+ ```
260
+
261
+ The lock is held for at most `lockTtlMs` (default: the interval, or 5 minutes for cron patterns). A Postgres advisory lock (`pg_try_advisory_lock`) works the same way. `app.cron.trigger(name)` runs a job without the lock.
262
+
242
263
  ### 2. Isolated Error Sandbox
243
264
  Exceptions thrown inside cron handlers are caught within an isolated execution boundary. An uncaught error in a background task will never crash the HTTP server or prevent subsequent scheduled runs. Errors are logged with execution duration and forwarded to your `onError` handler.
244
265
 
245
266
  ### 3. Accurate Timezone & DST Calculation
246
267
  Cron schedules evaluate IANA timezones (e.g. `'America/Chicago'`, `'Europe/Berlin'`, `'UTC'`) using native `Intl.DateTimeFormat` evaluations, adjusting accurately for Daylight Saving Time (DST) transitions.
247
268
 
248
- ### 4. Graceful Shutdown & Drain Window
269
+ ### 4. Quiet Under Tests
270
+ Under `NODE_ENV=test` jobs are registered but not started; see [Testing](/docs/testing). Each job file is loaded once: from `src/` during development and tests, and from the compiled `.exis/server/src/` in production.
271
+
272
+ ### 5. Graceful Shutdown & Drain Window
249
273
  When the server receives `SIGTERM` or `SIGINT`:
250
274
  1. The scheduler halts all future timer registrations.
251
275
  2. Active timers are unreferenced (`timer.unref()`).
@@ -312,7 +312,7 @@ export default class CheckoutController {
312
312
  @Post('/')
313
313
  @Timeout(15000)
314
314
  @Idempotent({ ttlMs: 86400000 })
315
- @Dedupe({ ttlMs: 5000 })
315
+ @Dedupe({ keyGenerator: (req) => req.user?.id ?? req.ip })
316
316
  @Compress()
317
317
  async processPayment() {
318
318
  return { status: 'processed' }
@@ -385,9 +385,9 @@ export default class RolesController {
385
385
 
386
386
  | Decorator | Target | Description |
387
387
  | :--- | :--- | :--- |
388
- | `@Roles(...roles: string[])` | Class / Method | Stores required role names in route metadata (`ROLES_METADATA`). |
389
- | `@Permissions(...perms: string[])` | Class / Method | Stores granular required permissions in route metadata (`PERMISSIONS_METADATA`). |
390
- | `@Public()` / `@IsPublic()` / `@AllowAnonymous()` | Class / Method | Marks route as unauthenticated. |
388
+ | `@Roles(...roles: string[])` | Class / Method | Requires `req.user` to have **one** of the roles (`user.roles` or `user.role`). No user: `401`; wrong role: `403`. |
389
+ | `@Permissions(...perms: string[])` | Class / Method | Requires **all** listed permissions in `req.user.permissions`. A role is never counted as a permission. No user: `401`; missing permission: `403`. |
390
+ | `@Public()` / `@IsPublic()` / `@AllowAnonymous()` | Class / Method | Marks route as unauthenticated. Wins over `@Roles` / `@Permissions` on the same route (a warning is logged at startup). |
391
391
 
392
392
  ---
393
393
 
@@ -475,7 +475,7 @@ export class OrderService implements OnInit, OnDestroy {
475
475
  | :--- | :--- | :--- |
476
476
  | `@Cacheable(options?)` | Method | Caches return value. Options: `{ ttl?: number, key?: string \| ((...args) => string) }`. |
477
477
  | `@CacheEvict(options)` | Method | Evicts cache keys on method completion. Options: `{ key: string \| ((...args) => string) }`. |
478
- | `@Transactional(options?)` | Method | Wraps execution in a database transaction (Prisma, Mongoose, Drizzle, SQL). |
478
+ | `@Transactional(options?)` | Method | Wraps execution in a database transaction (Prisma, Drizzle, Knex, SQL). Inside the call, the service's client property (`db`, `database`, `prisma`, `dataSource` or `connection`) is the transaction, including in methods it calls; concurrent requests using the same service keep the normal client. |
479
479
  | `@Profile(options?)` | Method | Measures execution time using `performance.now()`. |
480
480
  | `OnInit` (`onInit()`) | Class Interface | Invoked once after service instantiation. |
481
481
  | `OnDestroy` (`onDestroy()`) | Class Interface | Invoked once during graceful shutdown. |
@@ -139,15 +139,13 @@ Every error response has the same core shape, whatever caused it:
139
139
  | `statusCode` | Same as the HTTP status |
140
140
  | `error.code` | Stable machine-readable code: `NOT_FOUND`, `UNAUTHORIZED`, `VALIDATION_ERROR`, `INTERNAL_ERROR`... |
141
141
  | `error.message` | Human-readable message. For unexpected errors in production it is a generic message; internals are never exposed |
142
- | `error.details` | Optional extra data (validation issues, or the `details` passed to an `HttpError`) |
142
+ | `error.details` | Optional extra data (validation issues, or the `details` passed to an `HttpError`). Never copied to the top level |
143
143
 
144
- Clients can rely on `error.code` and `error.message` for every failed request.
144
+ Clients can rely on `error.code` and `error.message` for every failed request: thrown errors, validation, `@Roles` / `@Permissions` / guard rejections, `404` and `405` all use this shape. Codes the framework itself sends include `VALIDATION_ERROR`, `INVALID_JSON`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `METHOD_NOT_ALLOWED` (with an `Allow` header), `RATE_LIMIT_EXCEEDED`, `IDEMPOTENCY_KEY_REUSED`, `IDEMPOTENCY_IN_PROGRESS` and `INTERNAL_ERROR`. A handler that rejects with `null` or `undefined` is a `500`, never a `404`.
145
145
 
146
146
  ### Validation Error Responses
147
147
 
148
- When schema validation fails (via `tex`, Zod, or Yup), ExisJS intercepts the validation failure and generates a structured `400 Bad Request` payload containing field-level error mappings:
149
-
150
- The top-level `message`, `validationErrors`, `errors` and `details` fields are kept for existing clients; new code should read `error.details`. Values of credential fields (`password`, `token`, `secret`...) are replaced with `[Redacted]` in responses and logs.
148
+ When schema validation fails (via `tex`, Zod, Yup or a DTO's `validate()`), the response is a `400` with **one entry per failing field** in `error.details` (up to 100):
151
149
 
152
150
  ```json filename="HTTP 400 Validation Error Response"
153
151
  {
@@ -155,37 +153,40 @@ The top-level `message`, `validationErrors`, `errors` and `details` fields are k
155
153
  "statusCode": 400,
156
154
  "error": {
157
155
  "code": "VALIDATION_ERROR",
158
- "message": "Validation Error: email: Invalid email format, age: Minimum age is 18",
159
- "details": [ /* same entries as "details" below */ ]
160
- },
161
- "message": "Validation Error: email: Invalid email format, age: Minimum age is 18",
162
- "validationErrors": {
163
- "email": "Invalid email format",
164
- "age": "Minimum age is 18"
165
- },
166
- "errors": {
167
- "email": "Invalid email format",
168
- "age": "Minimum age is 18"
169
- },
170
- "details": [
171
- {
172
- "field": "email",
173
- "message": "Invalid email format",
174
- "expected": "Valid email address",
175
- "received": "invalid-email",
176
- "code": "INVALID_EMAIL",
177
- "httpPart": "body"
178
- },
179
- {
180
- "field": "age",
181
- "message": "Minimum age is 18",
182
- "expected": ">= 18",
183
- "received": "16",
184
- "code": "MIN_VALUE",
185
- "httpPart": "body"
186
- }
187
- ],
188
- "timestamp": "2026-10-02T12:45:00.000Z"
156
+ "message": "Validation Error",
157
+ "details": [
158
+ {
159
+ "field": "email",
160
+ "message": "Must be a valid email address",
161
+ "expected": "valid email address",
162
+ "received": "\"invalid-email\"",
163
+ "code": "INVALID_EMAIL",
164
+ "httpPart": "body"
165
+ },
166
+ {
167
+ "field": "age",
168
+ "message": "Must be at least 18",
169
+ "expected": "must be at least 18",
170
+ "received": "16",
171
+ "code": "VALUE_TOO_SMALL",
172
+ "httpPart": "body"
173
+ }
174
+ ]
175
+ }
176
+ }
177
+ ```
178
+
179
+ `received` never shows secrets: credential fields (`password`, `token`, `secret`, `apiKey`...) and connection settings (`DATABASE_URL`, `REDIS_URI`, `SENTRY_DSN`, `*_CONNECTION`...) are `[Redacted]`, and the password of any `scheme://user:password@host` value is masked as `***`, in responses, logs and `tex.env` startup errors.
180
+
181
+ **Status codes.** Malformed input that cannot be parsed at all is `400` (`INVALID_JSON` for a broken JSON body); input that parses but breaks the schema is `400` `VALIDATION_ERROR`. Use `HttpError.unprocessable()` (`422`) for business rules that the schema cannot express, such as "this email is already registered".
182
+
183
+ #### Pre-0.9 duplicate fields
184
+
185
+ Before 0.9, error bodies also carried `message`, `validationErrors`, `errors`, `details` and `timestamp` at the top level. They are gone by default. Clients that still read them can get them back while they migrate:
186
+
187
+ ```typescript filename="exis.config.ts"
188
+ export default {
189
+ errors: { legacyFields: true }, // deprecated, removed in 1.0
189
190
  }
190
191
  ```
191
192
 
@@ -205,9 +205,8 @@ const config: ExisConfig = {
205
205
  helmet: { enabled: true },
206
206
  compression: true,
207
207
 
208
- // Swagger UI at /docs; set enabled to
209
- // process.env.NODE_ENV !== 'production' to hide it in production
210
- docs: { enabled: true },
208
+ // Swagger UI at /docs, development only
209
+ docs: { enabled: process.env.NODE_ENV !== 'production' },
211
210
  }
212
211
 
213
212
  export default config
@@ -283,7 +282,7 @@ npm run dev
283
282
  Output:
284
283
 
285
284
  ```text filename="Terminal"
286
- [exis] starting development server (v0.8.1)...
285
+ [exis] starting development server (v0.9.0)...
287
286
  EXIS ready in 125 ms
288
287
 
289
288
  → Local: http://localhost:4000/
@@ -50,7 +50,7 @@ ExisJS combines convention-based file routing, built-in standard modules, and a
50
50
  The file-system routing engine scans the `src/http` directory and compiles route paths into a radix tree at startup. Purely static paths resolve through an exact-match table; parameterized paths walk the tree with no per-request parsing of the route definitions.
51
51
 
52
52
  ### 2. Dual-Paradigm Parity
53
- ExisJS provides feature parity across both functional and object-oriented paradigms. Both compile down to the same runtime execution pipeline:
53
+ ExisJS supports a functional and a class-based (OOP) paradigm. Both share the router, validation, boundaries, error handling, response handling and route authorization; a project uses **one** of them throughout (`exisjs dev` and `exisjs build` stop with the file names if they are mixed). Class-based controllers add decorator and dependency-injection work per request, so functional routes are somewhat faster.
54
54
  - **Functional**: `controller()`, `route.get()`, `route.post()`, `defineBoundary()`, `inject()`.
55
55
  - **Class-Based (OOP)**: `@Controller()`, `@Get()`, `@Post()`, `@Boundary()`, `@Injectable()`.
56
56
 
@@ -28,7 +28,7 @@ All standard middlewares can be imported directly from `exisjs/middleware`. Cust
28
28
  | `idempotent` | `idempotent(options?)` | Duplicate mutation caching keyed by `Idempotency-Key` headers. |
29
29
  | `dedupe` | `dedupe(options)` | Merges simultaneous identical in-flight `GET` requests into a single operation. |
30
30
  | `backpressure` | `backpressure(options?)` | Queues requests during high load and sheds excess traffic with `503 Service Unavailable`. |
31
- | `serveStatic` | `serveStatic(root, options?)` | Serves static assets from disk with strong ETags and Brotli/Gzip pre-compression. |
31
+ | `serveStatic` | `serveStatic(root, options?)` | Serves static assets from disk with ETag / Last-Modified revalidation and Brotli/Gzip pre-compression. |
32
32
  | `timeout` | `timeout(ms \| options)` | Terminates requests exceeding the duration limit with `503 Service Unavailable`. |
33
33
  | `fileUpload` | `fileUpload(options?)` | Streaming multipart/form-data parser supporting disk/memory storage and MIME checks. |
34
34
  | `fromExpress` | `fromExpress(middleware)` | Connect/Express bridge adapter running third-party middleware (e.g. Multer) directly. |
@@ -151,6 +151,8 @@ export const requireAuth = defineMiddleware<AuthContext>(async (req, res, next)
151
151
  })
152
152
  ```
153
153
 
154
+ Middleware may be `async`. ExisJS awaits the returned promise. If it throws, or rejects with an `HttpError` (such as `UnauthorizedException` above), that error becomes the response through the error handler, with the standard error envelope. You don't need to catch it and call `next(err)` yourself.
155
+
154
156
  ### 2. Using Typed Middleware in Functional Routes
155
157
 
156
158
  ```typescript filename="src/http/profile/route.ts"
@@ -287,4 +289,4 @@ export default exis({
287
289
  })
288
290
  ```
289
291
 
290
- `serveStatic` automatically handles MIME detection, conditional `If-None-Match` 304 caching, and serves pre-compressed `.br` or `.gz` files if present on disk.
292
+ `serveStatic` handles MIME detection and serves a pre-compressed `.br` or `.gz` file when one exists and the client accepts that encoding (`br;q=0` counts as a refusal). Every file gets an `ETag` (from its size and modification time; each encoded file has its own) and `Last-Modified`, and conditional requests are answered with `304` or `412` as described in [Caching & Conditional Requests](/docs/responses#caching--conditional-requests-etag-304-412). `Vary: Accept-Encoding` is always sent so shared caches keep the variants apart.
@@ -199,6 +199,8 @@ export class RequestContextService {
199
199
  ```
200
200
 
201
201
  > [!WARNING]
202
+ > **Scopes propagate**: a controller or service that depends on a request-scoped provider is itself created per request, so it can never keep one request's instance for the next. `transient` providers get a new instance for every injection.
203
+
202
204
  > **Request Scope Lifecycle**: Request-scoped providers can only be resolved within an active HTTP request context. Attempting to resolve a request-scoped provider during boot scripts, background cron tasks, or standalone worker processes will throw a container error.
203
205
 
204
206
  ---
@@ -33,9 +33,9 @@ ExisJS wraps the standard Node.js `IncomingMessage` in an `ExisRequest` instance
33
33
  | `req.signal` | `AbortSignal` | Standard `AbortSignal` triggered when client cancels connection |
34
34
  | `req.fresh` | `boolean` | `true` if client cache matches current `ETag` or `Last-Modified` |
35
35
  | `req.stale` | `boolean` | Inverse of `req.fresh` |
36
- | `req.user` | `ExisUser` | Authenticated user identity attached by auth guards |
36
+ | `req.user` | `ExisUser` | Authenticated user set by your auth middleware or guard. Type it once with `declare module 'exisjs' { interface ExisUser { id: string; roles: string[]; permissions: string[] } }` |
37
37
  | `req.session` | `Record<string, any>` | Session data store when session middleware is enabled |
38
- | `req.log` | `Logger` | Request-scoped Pino logger with trace metadata |
38
+ | `req.log` | `Logger` | Request-scoped Pino logger; every line carries `requestId` |
39
39
  | `req.requestId` | `string` | Unique request identifier header |
40
40
  | `req.raw` | `IncomingMessage` | Underlying Node.js `IncomingMessage` stream |
41
41
 
@@ -106,6 +106,8 @@ const data = await req.json<{ username: string }>()
106
106
  const { fields, files } = await req.formData()
107
107
  ```
108
108
 
109
+ The body is read from the network once; these methods share the result. `req.buffer()` returns the bytes exactly as the client sent them, so binary payloads (images, protobuf, compressed or signed data) are safe. A body over `bodyLimit` makes these calls fail with `413 Payload Too Large`.
110
+
109
111
  ### Webhooks with `@RawBody()` (Class Controllers)
110
112
 
111
113
  In class-based controllers, use the `@RawBody()` parameter decorator to inject the raw `Buffer` or decoded string directly:
@@ -51,6 +51,24 @@ export default class UsersController {
51
51
  }
52
52
  ```
53
53
 
54
+ ### What a Return Value Becomes
55
+
56
+ Both paradigms answer the same way:
57
+
58
+ | Handler returns | Response |
59
+ | :--- | :--- |
60
+ | Object, array, number, boolean, `null` | JSON (`null` is the JSON value `null`) |
61
+ | String | `text/plain` |
62
+ | `Buffer` / `Uint8Array` | `application/octet-stream` (set `Content-Type` first to change it) |
63
+ | Stream or async iterable | Streamed |
64
+ | `download(data, name)` | Attachment |
65
+ | `res`, or the result of `res.json()` / `res.send()` | Nothing more: the response was already sent |
66
+ | `undefined`, nothing sent | `204 No Content` (or the status set with `@HttpCode` / `res.status()`, with an empty body) |
67
+
68
+ Prefer returning a value. Call `res.json()` / `res.send()` yourself only when you need to, and do not do both. A handler that receives the response object (`@Res()`, or `(req, res)` with no decorators) may also answer later, for example from a callback; it is then responsible for sending exactly one response.
69
+
70
+ `HEAD` is answered by the matching `GET` route without a body. A path that exists under other methods answers `405 METHOD_NOT_ALLOWED` with an `Allow` header instead of `404`.
71
+
54
72
  ---
55
73
 
56
74
  ## Response Envelope Builders (`exisjs/response`)
@@ -433,6 +451,60 @@ export default controller({
433
451
  })
434
452
  ```
435
453
 
454
+ ## Caching & Conditional Requests (ETag, 304, 412)
455
+
456
+ ExisJS follows the conditional request rules of RFC 9110 §13 everywhere: for automatic ETags, in `res.checkPreconditions()`, for `serveStatic` and for `res.download(path)`.
457
+
458
+ | Request header | Checked against | Result when it does not hold |
459
+ | :--- | :--- | :--- |
460
+ | `If-Match` | `ETag`, strong comparison | `412 Precondition Failed` |
461
+ | `If-Unmodified-Since` (only without `If-Match`) | `Last-Modified` | `412 Precondition Failed` |
462
+ | `If-None-Match` | `ETag`, weak comparison; lists and `*` supported | Matches: `304` for GET/HEAD, `412` for writes |
463
+ | `If-Modified-Since` (only without `If-None-Match`, GET/HEAD) | `Last-Modified` | Unchanged: `304 Not Modified` |
464
+
465
+ A `304` carries the `ETag`, `Last-Modified`, `Cache-Control` and `Vary` of the `200` it replaces, and no body or `Content-Type`. Errors use the standard envelope (`PRECONDITION_FAILED`, `PRECONDITION_REQUIRED`).
466
+
467
+ ### Automatic ETags
468
+
469
+ With `etag: true` in `exis.config.ts` (or `conditionalGet()` on some routes), every successful `GET`/`HEAD` response sent with `res.json()`, `res.send()` or a returned value gets a weak ETag hashed from its body, and a matching `If-None-Match` is answered with `304`. The handler still runs, so this saves bandwidth, not work. Writes and error responses get no ETag.
470
+
471
+ ### Validators from your data: `res.checkPreconditions()`
472
+
473
+ When the resource has a version or update time, check it **before** doing the work. On reads this skips the query for the body and serialization; on writes it prevents lost updates: a client that edited an old copy gets `412` instead of overwriting someone else's change.
474
+
475
+ ```typescript filename="src/http/documents/[id]/route.ts"
476
+ import { controller, route } from 'exisjs/router'
477
+
478
+ export default controller({
479
+ read: route.get('/', {
480
+ async handle({ params, res }) {
481
+ const meta = await documents.meta(params.id) // cheap: version + updatedAt
482
+ // Sets ETag / Last-Modified; answers 304 and returns true when current
483
+ if (res.checkPreconditions({ etag: `v${meta.version}`, lastModified: meta.updatedAt })) return
484
+ return documents.find(params.id)
485
+ },
486
+ }),
487
+
488
+ update: route.put('/', {
489
+ async handle({ params, body, res }) {
490
+ const current = await documents.meta(params.id)
491
+ // 412 if the client's If-Match is stale; 428 if it sent no precondition
492
+ res.checkPreconditions({ etag: `v${current.version}` }, { required: true })
493
+ const updated = await documents.update(params.id, body)
494
+ res.setHeader('ETag', `"v${updated.version}"`) // the new state
495
+ return updated
496
+ },
497
+ }),
498
+ })
499
+ ```
500
+
501
+ - `etag` may be bare (`v3` is sent as `"v3"`) or a full tag (`W/"v3"`). Use a value that changes whenever the representation changes: a version column, a hash, `updatedAt`.
502
+ - `lastModified` takes a `Date`, a timestamp or an HTTP date. It is rounded to whole seconds, and a time in the future is capped at the current time.
503
+ - For "create only if absent" (`PUT` with `If-None-Match: *`), pass `exists: false` when the resource does not exist yet; an existing one answers `412`.
504
+ - On writes, the old validators are not sent back. Set the new `ETag` after the change, as above.
505
+
506
+ `req.fresh` (and `req.stale`) apply the same rules to the response headers set so far.
507
+
436
508
  ---
437
509
 
438
510
  ## Next Steps