exisjs 0.7.6 → 0.7.8

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 (211) hide show
  1. package/dist/cli/commands/build.d.ts.map +1 -1
  2. package/dist/cli/commands/build.js +12 -0
  3. package/dist/cli/commands/build.js.map +1 -1
  4. package/dist/cli/commands/clean.d.ts +5 -0
  5. package/dist/cli/commands/clean.d.ts.map +1 -0
  6. package/dist/cli/commands/clean.js +30 -0
  7. package/dist/cli/commands/clean.js.map +1 -0
  8. package/dist/cli/commands/dev.d.ts.map +1 -1
  9. package/dist/cli/commands/dev.js +15 -1
  10. package/dist/cli/commands/dev.js.map +1 -1
  11. package/dist/cli/commands/generate.d.ts +6 -1
  12. package/dist/cli/commands/generate.d.ts.map +1 -1
  13. package/dist/cli/commands/generate.js +57 -2
  14. package/dist/cli/commands/generate.js.map +1 -1
  15. package/dist/cli/index.js +16 -2
  16. package/dist/cli/index.js.map +1 -1
  17. package/dist/cli/manifest.d.ts +1 -0
  18. package/dist/cli/manifest.d.ts.map +1 -1
  19. package/dist/cli/manifest.js +161 -23
  20. package/dist/cli/manifest.js.map +1 -1
  21. package/dist/config/config.d.ts.map +1 -1
  22. package/dist/config/config.js +9 -0
  23. package/dist/config/config.js.map +1 -1
  24. package/dist/config/types.d.ts +4 -0
  25. package/dist/config/types.d.ts.map +1 -1
  26. package/dist/cron/expressions.d.ts +45 -0
  27. package/dist/cron/expressions.d.ts.map +1 -0
  28. package/dist/cron/expressions.js +87 -0
  29. package/dist/cron/expressions.js.map +1 -0
  30. package/dist/cron/index.d.ts +6 -0
  31. package/dist/cron/index.d.ts.map +1 -0
  32. package/dist/cron/index.js +22 -0
  33. package/dist/cron/index.js.map +1 -0
  34. package/dist/cron/job.d.ts +50 -0
  35. package/dist/cron/job.d.ts.map +1 -0
  36. package/dist/cron/job.js +247 -0
  37. package/dist/cron/job.js.map +1 -0
  38. package/dist/cron/manager.d.ts +105 -0
  39. package/dist/cron/manager.d.ts.map +1 -0
  40. package/dist/cron/manager.js +245 -0
  41. package/dist/cron/manager.js.map +1 -0
  42. package/dist/cron/parser.d.ts +22 -0
  43. package/dist/cron/parser.d.ts.map +1 -0
  44. package/dist/cron/parser.js +269 -0
  45. package/dist/cron/parser.js.map +1 -0
  46. package/dist/cron/types.d.ts +76 -0
  47. package/dist/cron/types.d.ts.map +1 -0
  48. package/dist/cron/types.js +3 -0
  49. package/dist/cron/types.js.map +1 -0
  50. package/dist/decorators/constants.d.ts +2 -0
  51. package/dist/decorators/constants.d.ts.map +1 -1
  52. package/dist/decorators/constants.js +3 -1
  53. package/dist/decorators/constants.js.map +1 -1
  54. package/dist/decorators/core.d.ts +29 -11
  55. package/dist/decorators/core.d.ts.map +1 -1
  56. package/dist/decorators/core.js +49 -11
  57. package/dist/decorators/core.js.map +1 -1
  58. package/dist/decorators/cron.d.ts +57 -0
  59. package/dist/decorators/cron.d.ts.map +1 -0
  60. package/dist/decorators/cron.js +143 -0
  61. package/dist/decorators/cron.js.map +1 -0
  62. package/dist/decorators/index.d.ts +2 -0
  63. package/dist/decorators/index.d.ts.map +1 -1
  64. package/dist/decorators/index.js +2 -0
  65. package/dist/decorators/index.js.map +1 -1
  66. package/dist/decorators/params.d.ts +97 -2
  67. package/dist/decorators/params.d.ts.map +1 -1
  68. package/dist/decorators/params.js +94 -2
  69. package/dist/decorators/params.js.map +1 -1
  70. package/dist/decorators/routing.d.ts +81 -16
  71. package/dist/decorators/routing.d.ts.map +1 -1
  72. package/dist/decorators/routing.js +81 -16
  73. package/dist/decorators/routing.js.map +1 -1
  74. package/dist/docs/content/cron.mdx +196 -0
  75. package/dist/docs/content/errors.mdx +58 -9
  76. package/dist/docs/content/structure.mdx +1 -1
  77. package/dist/docs/content/swagger.mdx +289 -0
  78. package/dist/docs/reference/cron.mdx +300 -0
  79. package/dist/docs/reference/decorators.mdx +8 -0
  80. package/dist/docs/reference/error.mdx +44 -0
  81. package/dist/docs/reference/swagger.mdx +226 -0
  82. package/dist/docs/reference/validator.mdx +1 -1
  83. package/dist/router/boundary.d.ts +24 -2
  84. package/dist/router/boundary.d.ts.map +1 -1
  85. package/dist/router/boundary.js +24 -2
  86. package/dist/router/boundary.js.map +1 -1
  87. package/dist/router/controller-registrar.d.ts.map +1 -1
  88. package/dist/router/controller-registrar.js +52 -3
  89. package/dist/router/controller-registrar.js.map +1 -1
  90. package/dist/router/route-builder.d.ts +196 -33
  91. package/dist/router/route-builder.d.ts.map +1 -1
  92. package/dist/router/route-builder.js +171 -11
  93. package/dist/router/route-builder.js.map +1 -1
  94. package/dist/router/route-scanner.d.ts +6 -0
  95. package/dist/router/route-scanner.d.ts.map +1 -1
  96. package/dist/router/route-scanner.js +69 -812
  97. package/dist/router/route-scanner.js.map +1 -1
  98. package/dist/router/router.d.ts.map +1 -1
  99. package/dist/router/router.js +27 -0
  100. package/dist/router/router.js.map +1 -1
  101. package/dist/router/scanner/boundary-loader.d.ts +18 -0
  102. package/dist/router/scanner/boundary-loader.d.ts.map +1 -0
  103. package/dist/router/scanner/boundary-loader.js +539 -0
  104. package/dist/router/scanner/boundary-loader.js.map +1 -0
  105. package/dist/router/scanner/cron-mounter.d.ts +15 -0
  106. package/dist/router/scanner/cron-mounter.d.ts.map +1 -0
  107. package/dist/router/scanner/cron-mounter.js +216 -0
  108. package/dist/router/scanner/cron-mounter.js.map +1 -0
  109. package/dist/router/scanner/error-mounter.d.ts +14 -0
  110. package/dist/router/scanner/error-mounter.d.ts.map +1 -0
  111. package/dist/router/scanner/error-mounter.js +200 -0
  112. package/dist/router/scanner/error-mounter.js.map +1 -0
  113. package/dist/router/scanner/fs-scanner.d.ts +25 -0
  114. package/dist/router/scanner/fs-scanner.d.ts.map +1 -0
  115. package/dist/router/scanner/fs-scanner.js +115 -0
  116. package/dist/router/scanner/fs-scanner.js.map +1 -0
  117. package/dist/router/scanner/functional-compiler.d.ts +9 -0
  118. package/dist/router/scanner/functional-compiler.d.ts.map +1 -0
  119. package/dist/router/scanner/functional-compiler.js +233 -0
  120. package/dist/router/scanner/functional-compiler.js.map +1 -0
  121. package/dist/router/scanner/index.d.ts +9 -0
  122. package/dist/router/scanner/index.d.ts.map +1 -0
  123. package/dist/router/scanner/index.js +19 -0
  124. package/dist/router/scanner/index.js.map +1 -0
  125. package/dist/router/types.d.ts +15 -0
  126. package/dist/router/types.d.ts.map +1 -1
  127. package/dist/server/app.d.ts +2 -0
  128. package/dist/server/app.d.ts.map +1 -1
  129. package/dist/server/app.js +3 -0
  130. package/dist/server/app.js.map +1 -1
  131. package/dist/server/helpers/body-parser.d.ts +18 -0
  132. package/dist/server/helpers/body-parser.d.ts.map +1 -0
  133. package/dist/server/helpers/body-parser.js +246 -0
  134. package/dist/server/helpers/body-parser.js.map +1 -0
  135. package/dist/server/helpers/cookie.d.ts +4 -0
  136. package/dist/server/helpers/cookie.d.ts.map +1 -0
  137. package/dist/server/helpers/cookie.js +61 -0
  138. package/dist/server/helpers/cookie.js.map +1 -0
  139. package/dist/server/helpers/etag.d.ts +3 -0
  140. package/dist/server/helpers/etag.d.ts.map +1 -0
  141. package/dist/server/helpers/etag.js +25 -0
  142. package/dist/server/helpers/etag.js.map +1 -0
  143. package/dist/server/helpers/index.d.ts +6 -0
  144. package/dist/server/helpers/index.d.ts.map +1 -0
  145. package/dist/server/helpers/index.js +21 -0
  146. package/dist/server/helpers/index.js.map +1 -0
  147. package/dist/server/helpers/ip.d.ts +9 -0
  148. package/dist/server/helpers/ip.d.ts.map +1 -0
  149. package/dist/server/helpers/ip.js +62 -0
  150. package/dist/server/helpers/ip.js.map +1 -0
  151. package/dist/server/helpers/stream.d.ts +4 -0
  152. package/dist/server/helpers/stream.d.ts.map +1 -0
  153. package/dist/server/helpers/stream.js +193 -0
  154. package/dist/server/helpers/stream.js.map +1 -0
  155. package/dist/server/request.d.ts +1 -139
  156. package/dist/server/request.d.ts.map +1 -1
  157. package/dist/server/request.js +19 -433
  158. package/dist/server/request.js.map +1 -1
  159. package/dist/server/response.d.ts +105 -158
  160. package/dist/server/response.d.ts.map +1 -1
  161. package/dist/server/response.js +121 -423
  162. package/dist/server/response.js.map +1 -1
  163. package/dist/swagger/decorators.d.ts +81 -0
  164. package/dist/swagger/decorators.d.ts.map +1 -0
  165. package/dist/swagger/decorators.js +172 -0
  166. package/dist/swagger/decorators.js.map +1 -0
  167. package/dist/swagger/generator.d.ts +19 -0
  168. package/dist/swagger/generator.d.ts.map +1 -0
  169. package/dist/swagger/generator.js +344 -0
  170. package/dist/swagger/generator.js.map +1 -0
  171. package/dist/swagger/index.d.ts +31 -0
  172. package/dist/swagger/index.d.ts.map +1 -0
  173. package/dist/swagger/index.js +53 -0
  174. package/dist/swagger/index.js.map +1 -0
  175. package/dist/swagger/mounter.d.ts +7 -0
  176. package/dist/swagger/mounter.d.ts.map +1 -0
  177. package/dist/swagger/mounter.js +56 -0
  178. package/dist/swagger/mounter.js.map +1 -0
  179. package/dist/swagger/types.d.ts +190 -0
  180. package/dist/swagger/types.d.ts.map +1 -0
  181. package/dist/swagger/types.js +3 -0
  182. package/dist/swagger/types.js.map +1 -0
  183. package/dist/swagger/ui.d.ts +6 -0
  184. package/dist/swagger/ui.d.ts.map +1 -0
  185. package/dist/swagger/ui.js +118 -0
  186. package/dist/swagger/ui.js.map +1 -0
  187. package/dist/validator/builder.d.ts +223 -0
  188. package/dist/validator/builder.d.ts.map +1 -0
  189. package/dist/validator/builder.js +517 -0
  190. package/dist/validator/builder.js.map +1 -0
  191. package/dist/validator/engine.d.ts +19 -0
  192. package/dist/validator/engine.d.ts.map +1 -0
  193. package/dist/validator/engine.js +568 -0
  194. package/dist/validator/engine.js.map +1 -0
  195. package/dist/validator/error.d.ts +12 -0
  196. package/dist/validator/error.d.ts.map +1 -0
  197. package/dist/validator/error.js +63 -0
  198. package/dist/validator/error.js.map +1 -0
  199. package/dist/validator/pagination.d.ts +40 -0
  200. package/dist/validator/pagination.d.ts.map +1 -0
  201. package/dist/validator/pagination.js +60 -0
  202. package/dist/validator/pagination.js.map +1 -0
  203. package/dist/validator/tex.d.ts +6 -158
  204. package/dist/validator/tex.d.ts.map +1 -1
  205. package/dist/validator/tex.js +14 -971
  206. package/dist/validator/tex.js.map +1 -1
  207. package/dist/validator/types.d.ts +83 -0
  208. package/dist/validator/types.d.ts.map +1 -0
  209. package/dist/validator/types.js +3 -0
  210. package/dist/validator/types.js.map +1 -0
  211. package/package.json +9 -1
@@ -8,30 +8,11 @@ const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const index_1 = require("../logger/index");
10
10
  const sse_1 = require("./sse");
11
+ const helpers_1 = require("./helpers");
11
12
  // eslint-disable-next-line @typescript-eslint/no-require-imports
12
13
  const contentDisposition = require('content-disposition');
13
14
  // eslint-disable-next-line @typescript-eslint/no-require-imports
14
15
  const mime = require('mime-types');
15
- const rs_1 = require("@exisjs/rs");
16
- function generateETag(content) {
17
- return (0, rs_1.generateEtag)(content);
18
- }
19
- function nativeStringify(data) {
20
- if (data === null || data === undefined) {
21
- return 'null';
22
- }
23
- // If native rust serialization is available, use it
24
- if (typeof rs_1.fastJsonStringifyBuffer === 'function') {
25
- // Validate object can be serialized without throwing V8 check failure on circularity
26
- const str = JSON.stringify(data);
27
- if (str.length > 512) {
28
- // For larger payloads, Rust serde serialization into Buffer gives zero V8 GC overhead
29
- return (0, rs_1.fastJsonStringifyBuffer)(data);
30
- }
31
- return str;
32
- }
33
- return JSON.stringify(data);
34
- }
35
16
  class ExisResponse {
36
17
  constructor(raw) {
37
18
  this.raw = raw;
@@ -96,11 +77,15 @@ class ExisResponse {
96
77
  }
97
78
  }
98
79
  /**
99
- * Set status `code`.
80
+ * Sets the HTTP response status code.
81
+ *
82
+ * @param code HTTP status code (e.g. `200`, `201`, `400`, `404`, `500`)
83
+ * @returns Response instance for chaining
100
84
  *
101
- * @param {number} code
102
- * @return {this}
103
- * @public
85
+ * @example
86
+ * ```ts
87
+ * res.status(201).json({ created: true })
88
+ * ```
104
89
  */
105
90
  status(code) {
106
91
  if (this.raw.destroyed || this.raw.headersSent)
@@ -109,51 +94,24 @@ class ExisResponse {
109
94
  return this;
110
95
  }
111
96
  /**
112
- * Set header `field` to `val`, or pass
113
- * an object of header fields.
114
- *
115
- * Examples:
116
- *
117
- * res.set('Foo', ['bar', 'baz']);
118
- * res.set('Accept', 'application/json');
97
+ * Sets a response header field to a specific value.
119
98
  *
120
- * Aliased as `res.header()`.
121
- *
122
- * @param {string} header
123
- * @param {string | string[]} value
124
- * @return {this}
125
- * @public
99
+ * @param header Header name
100
+ * @param value Header value or array of values
101
+ * @returns Response instance for chaining
126
102
  */
127
103
  set(header, value) {
128
104
  this.setHeader(header, value);
129
105
  return this;
130
106
  }
131
107
  /**
132
- * Set header `field` to `val`, or pass
133
- * an object of header fields.
134
- *
135
- * Alias for `res.set()`.
136
- *
137
- * @param {string} name
138
- * @param {string | string[]} value
139
- * @return {this}
140
- * @public
108
+ * Sets a response header field (alias for `res.set()`).
141
109
  */
142
110
  header(name, value) {
143
111
  return this.set(name, value);
144
112
  }
145
113
  /**
146
- * Send given HTTP status code.
147
- *
148
- * Sets the response status to `code` and the body
149
- * to the string representation of the `code`.
150
- *
151
- * Examples:
152
- *
153
- * res.sendStatus(200);
154
- *
155
- * @param {number} code
156
- * @public
114
+ * Sets status code and sends its numeric string representation.
157
115
  */
158
116
  sendStatus(code) {
159
117
  this.statusCode = code;
@@ -170,18 +128,7 @@ class ExisResponse {
170
128
  return this;
171
129
  }
172
130
  /**
173
- * Append additional header `field` with value `val`.
174
- *
175
- * Example:
176
- *
177
- * res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
178
- * res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
179
- * res.append('Warning', '199 Miscellaneous warning');
180
- *
181
- * @param {string} field
182
- * @param {string | string[]} value
183
- * @return {this}
184
- * @public
131
+ * Appends an additional value to an existing header field (e.g. `Set-Cookie`, `Link`, `Warning`).
185
132
  */
186
133
  append(field, value) {
187
134
  const prev = this.getHeader(field);
@@ -195,16 +142,14 @@ class ExisResponse {
195
142
  return this;
196
143
  }
197
144
  /**
198
- * Send a response.
199
- *
200
- * Examples:
145
+ * Sends a string, Buffer, or object response to the client.
201
146
  *
202
- * res.send(Buffer.from('wahoo'));
203
- * res.send({ some: 'json' });
204
- * res.send('<p>some html</p>');
147
+ * @param body Response payload (string, Buffer, or object)
205
148
  *
206
- * @param {string | Buffer | object} body
207
- * @public
149
+ * @example
150
+ * ```ts
151
+ * res.send('Hello World')
152
+ * ```
208
153
  */
209
154
  send(body) {
210
155
  if (!this.isWritable)
@@ -221,7 +166,7 @@ class ExisResponse {
221
166
  const buf = isBuffer
222
167
  ? body
223
168
  : Buffer.from(body, 'utf8');
224
- this.raw.setHeader('ETag', generateETag(buf));
169
+ this.raw.setHeader('ETag', (0, helpers_1.generateETag)(buf));
225
170
  }
226
171
  if (this.req &&
227
172
  (this.raw.hasHeader('ETag') || this.raw.hasHeader('Last-Modified')) &&
@@ -230,23 +175,20 @@ class ExisResponse {
230
175
  this.end();
231
176
  return;
232
177
  }
233
- // Rely on Node.js core to automatically calculate Content-Length
234
- // for strings in res.end() rather than creating a Buffer here.
235
178
  if (isBuffer && !this.raw.hasHeader('Content-Length')) {
236
179
  this.raw.setHeader('Content-Length', body.length);
237
180
  }
238
181
  this.end(body);
239
182
  }
240
183
  /**
241
- * Send JSON response.
184
+ * Sends a JSON response with automatic native serialization and fast ETag support.
242
185
  *
243
- * Examples:
186
+ * @param data JSON-serializable data payload
244
187
  *
245
- * res.json(null);
246
- * res.json({ user: 'tj' });
247
- *
248
- * @param {unknown} data
249
- * @public
188
+ * @example
189
+ * ```ts
190
+ * res.json({ success: true, users: ['Alice', 'Bob'] })
191
+ * ```
250
192
  */
251
193
  json(data) {
252
194
  if (!this.isWritable)
@@ -254,7 +196,7 @@ class ExisResponse {
254
196
  let payload;
255
197
  try {
256
198
  const useSerializer = this._serializer && this.statusCode < 400;
257
- payload = useSerializer ? this._serializer(data) : nativeStringify(data);
199
+ payload = useSerializer ? this._serializer(data) : (0, helpers_1.nativeStringify)(data);
258
200
  }
259
201
  catch (err) {
260
202
  if (this.req && this.req.log) {
@@ -275,7 +217,7 @@ class ExisResponse {
275
217
  const buf = Buffer.isBuffer(payload)
276
218
  ? payload
277
219
  : Buffer.from(payload, 'utf8');
278
- this.raw.setHeader('ETag', generateETag(buf));
220
+ this.raw.setHeader('ETag', (0, helpers_1.generateETag)(buf));
279
221
  }
280
222
  if (this.req &&
281
223
  (this.raw.hasHeader('ETag') || this.raw.hasHeader('Last-Modified')) &&
@@ -287,14 +229,14 @@ class ExisResponse {
287
229
  this.end(payload);
288
230
  }
289
231
  /**
290
- * Send HTML response.
291
- *
292
- * Examples:
232
+ * Sends an HTML response with `Content-Type: text/html; charset=utf-8`.
293
233
  *
294
- * res.html('<h1>Hello</h1>');
234
+ * @param content HTML string
295
235
  *
296
- * @param {string} content
297
- * @public
236
+ * @example
237
+ * ```ts
238
+ * res.html('<h1>Welcome to ExisJS</h1>')
239
+ * ```
298
240
  */
299
241
  html(content) {
300
242
  if (!this.isWritable)
@@ -305,18 +247,15 @@ class ExisResponse {
305
247
  this.end(content);
306
248
  }
307
249
  /**
308
- * Redirect to the given `url` with optional response `status`
309
- * defaulting to 302.
310
- *
311
- * Examples:
250
+ * Performs an HTTP redirect to a given URL with an optional status code (defaults to 302).
312
251
  *
313
- * res.redirect('/foo/bar');
314
- * res.redirect('http://example.com');
315
- * res.redirect('http://example.com', 301);
252
+ * @param url Target destination URL
253
+ * @param code HTTP status code (`301` Moved Permanently, `302` Found, `307` Temporary Redirect, `308` Permanent Redirect)
316
254
  *
317
- * @param {string} url
318
- * @param {number} [code=302]
319
- * @public
255
+ * @example
256
+ * ```ts
257
+ * res.redirect('/login')
258
+ * ```
320
259
  */
321
260
  redirect(url, code = 302) {
322
261
  if (!this.isWritable)
@@ -326,197 +265,21 @@ class ExisResponse {
326
265
  this.end();
327
266
  }
328
267
  /**
329
- * Stream a Node.js Readable stream, Web standard ReadableStream, or AsyncIterable
330
- * to the client with native HTTP backpressure handling and automatic cancellation
331
- * if the client disconnects prematurely.
268
+ * Streams a Node.js Readable stream, Web standard `ReadableStream`, or `AsyncIterable` generator
269
+ * to the client with native backpressure handling and client disconnect auto-cancellation.
270
+ *
271
+ * @param readable Stream or async generator instance
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * // Streaming from an AI SDK or Web stream:
276
+ * res.sendStream(aiResponse.toReadableStream())
277
+ * ```
332
278
  */
333
279
  sendStream(readable) {
334
- if (!this.isWritable) {
335
- if (typeof readable.destroy === 'function') {
336
- ;
337
- readable.destroy();
338
- }
339
- else if (typeof readable.cancel === 'function') {
340
- ;
341
- readable.cancel().catch(() => {
342
- /* ignore */
343
- });
344
- }
345
- return;
346
- }
347
- if (!this.hasHeader('Content-Type')) {
348
- this.setHeader('Content-Type', 'application/octet-stream');
349
- }
350
- // 1. Web Standard ReadableStream (e.g. from fetch, OpenAI, Anthropic, AI SDKs)
351
- if (typeof readable.getReader === 'function') {
352
- const reader = readable.getReader();
353
- let closed = false;
354
- const cleanup = () => {
355
- if (closed)
356
- return;
357
- closed = true;
358
- try {
359
- reader.cancel().catch(() => {
360
- /* ignore */
361
- });
362
- }
363
- catch {
364
- // ignore
365
- }
366
- };
367
- this.raw.once('close', cleanup);
368
- if (this.req && this.req.raw) {
369
- ;
370
- this.req.raw.once('close', cleanup);
371
- this.req.raw.once('aborted', cleanup);
372
- }
373
- ;
374
- (async () => {
375
- try {
376
- while (true) {
377
- if (closed || this.raw.destroyed) {
378
- cleanup();
379
- break;
380
- }
381
- const { done, value } = await reader.read();
382
- if (done) {
383
- this.raw.removeListener('close', cleanup);
384
- if (!this.raw.writableEnded && !this.raw.destroyed) {
385
- this.end();
386
- }
387
- break;
388
- }
389
- if (value !== undefined && value !== null) {
390
- const ok = this.raw.write(value);
391
- if (!ok && !this.raw.destroyed && !this.raw.writableEnded) {
392
- await new Promise((resolve) => this.raw.once('drain', resolve));
393
- }
394
- }
395
- }
396
- }
397
- catch (err) {
398
- cleanup();
399
- if (this.req && this.req.log) {
400
- this.req.log.error({ err }, '[ExisJS] Error in Web ReadableStream');
401
- }
402
- else {
403
- index_1.logger.error({ err }, '[ExisJS] Error in Web ReadableStream');
404
- }
405
- if (this.isWritable && !this.headersSent) {
406
- this.statusCode = 500;
407
- this.end('{"error":"Stream transmission failed"}');
408
- }
409
- else if (!this.raw.destroyed) {
410
- this.raw.destroy(err);
411
- }
412
- }
413
- })();
414
- return;
415
- }
416
- // 2. AsyncIterable / Generator stream
417
- if (typeof readable[Symbol.asyncIterator] === 'function' &&
418
- typeof readable.pipe !== 'function') {
419
- let closed = false;
420
- const cleanup = () => {
421
- closed = true;
422
- if (typeof readable.return === 'function') {
423
- ;
424
- readable.return().catch(() => {
425
- /* ignore */
426
- });
427
- }
428
- };
429
- this.raw.once('close', cleanup);
430
- if (this.req && this.req.raw) {
431
- ;
432
- this.req.raw.once('close', cleanup);
433
- }
434
- ;
435
- (async () => {
436
- try {
437
- for await (const chunk of readable) {
438
- if (closed || this.raw.destroyed)
439
- break;
440
- if (chunk !== undefined && chunk !== null) {
441
- const payload = typeof chunk === 'string' || Buffer.isBuffer(chunk)
442
- ? chunk
443
- : JSON.stringify(chunk);
444
- const ok = this.raw.write(payload);
445
- if (!ok && !this.raw.destroyed && !this.raw.writableEnded) {
446
- await new Promise((resolve) => this.raw.once('drain', resolve));
447
- }
448
- }
449
- }
450
- this.raw.removeListener('close', cleanup);
451
- if (!this.raw.writableEnded && !this.raw.destroyed) {
452
- this.end();
453
- }
454
- }
455
- catch (err) {
456
- cleanup();
457
- if (this.req && this.req.log) {
458
- this.req.log.error({ err }, '[ExisJS] Error in AsyncIterable stream');
459
- }
460
- else {
461
- index_1.logger.error({ err }, '[ExisJS] Error in AsyncIterable stream');
462
- }
463
- if (this.isWritable && !this.headersSent) {
464
- this.statusCode = 500;
465
- this.end('{"error":"Stream transmission failed"}');
466
- }
467
- else if (!this.raw.destroyed) {
468
- this.raw.destroy(err);
469
- }
470
- }
471
- })();
472
- return;
473
- }
474
- // 3. Standard Node.js Readable Stream
475
- const cleanup = () => {
476
- if (typeof readable.destroy === 'function' &&
477
- !readable.destroyed) {
478
- ;
479
- readable.destroy();
480
- }
481
- };
482
- // Auto-destroy stream if client aborts or response closes early
483
- this.raw.once('close', cleanup);
484
- if (this.req && this.req.raw) {
485
- ;
486
- this.req.raw.once('close', cleanup);
487
- this.req.raw.once('aborted', cleanup);
488
- }
489
- // Handle stream error to prevent process crash
490
- if (typeof readable.on === 'function') {
491
- ;
492
- readable.once('error', (err) => {
493
- this.raw.removeListener('close', cleanup);
494
- if (this.req && this.req.log) {
495
- this.req.log.error({ err }, '[ExisJS] Error in sendStream');
496
- }
497
- else {
498
- index_1.logger.error({ err }, '[ExisJS] Error in sendStream');
499
- }
500
- if (this.isWritable && !this.headersSent) {
501
- this.statusCode = 500;
502
- this.end('{"error":"Stream transmission failed"}');
503
- }
504
- else {
505
- cleanup();
506
- if (!this.raw.destroyed) {
507
- this.raw.destroy(err);
508
- }
509
- }
510
- });
511
- }
512
- if (typeof readable.once === 'function') {
513
- ;
514
- readable.once('end', () => {
515
- this.raw.removeListener('close', cleanup);
516
- });
517
- }
518
- ;
519
- readable.pipe(this.raw);
280
+ (0, helpers_1.streamToResponse)(this.raw, readable, this.isWritable, this.headersSent, (name) => this.hasHeader(name), (name, val) => this.setHeader(name, val), (code) => {
281
+ this.statusCode = code;
282
+ }, (data) => this.end(data), this.req);
520
283
  }
521
284
  /**
522
285
  * Alias for `sendStream()`.
@@ -525,29 +288,33 @@ class ExisResponse {
525
288
  this.sendStream(readable);
526
289
  }
527
290
  /**
528
- * Initialize a Server-Sent Events (SSE) stream for real-time and AI streaming.
291
+ * Initializes a Server-Sent Events (SSE) stream for real-time data and AI token streaming.
529
292
  *
530
- * Automatically sets standard SSE headers:
293
+ * Sets standard SSE headers automatically:
531
294
  * - `Content-Type: text/event-stream; charset=utf-8`
532
295
  * - `Cache-Control: no-cache, no-transform`
533
296
  * - `Connection: keep-alive`
534
- * - `X-Accel-Buffering: no` (disables Nginx proxy buffering)
297
+ * - `X-Accel-Buffering: no`
535
298
  *
536
- * @param options Optional configuration (heartbeat interval, custom headers)
537
- * @param handler Optional callback function receiving the active `SSEStream`
299
+ * @param optionsOrHandler Optional SSE configuration or callback handler
300
+ * @param maybeHandler Callback handler when options object is provided
538
301
  * @returns Active `SSEStream` instance
539
302
  *
540
303
  * @example
541
- * // Using callback handler:
542
- * res.sse(async (sse) => {
543
- * sse.send({ event: 'message', data: 'hello' })
544
- * await sse.pipeFrom(openaiStream)
545
- * sse.close()
304
+ * ```ts
305
+ * // In route.ts
306
+ * export default controller({
307
+ * events: route.get('/events', {
308
+ * async handle({ res }) {
309
+ * res.sse(async (sse) => {
310
+ * sse.send({ event: 'ping', data: { time: Date.now() } })
311
+ * await sse.pipeFrom(openAiStream)
312
+ * sse.close()
313
+ * })
314
+ * }
315
+ * })
546
316
  * })
547
- *
548
- * // Or assigning to variable:
549
- * const sse = res.sse()
550
- * sse.send({ event: 'connected', data: { time: Date.now() } })
317
+ * ```
551
318
  */
552
319
  sse(optionsOrHandler, maybeHandler) {
553
320
  let options = {};
@@ -573,7 +340,6 @@ class ExisResponse {
573
340
  this.setHeader(key, value);
574
341
  }
575
342
  }
576
- // Flush headers immediately
577
343
  if (typeof this.raw.flushHeaders === 'function') {
578
344
  this.raw.flushHeaders();
579
345
  }
@@ -611,15 +377,16 @@ class ExisResponse {
611
377
  return stream;
612
378
  }
613
379
  /**
614
- * Transfer the file at the given `filePath` as an attachment.
380
+ * Transfers a file on disk as an attachment with automatic Content-Disposition and MIME lookup.
615
381
  *
616
- * Optionally providing an alternate attachment `filename`,
617
- * and optional `options`.
382
+ * @param filePath Absolute path to file on disk
383
+ * @param filename Optional override for downloaded filename
384
+ * @param options File stream options
618
385
  *
619
- * @param {string} filePath
620
- * @param {string} [filename]
621
- * @param {unknown} [options]
622
- * @public
386
+ * @example
387
+ * ```ts
388
+ * res.download('/data/reports/report.pdf', 'monthly-report.pdf')
389
+ * ```
623
390
  */
624
391
  download(filePath, filename, options) {
625
392
  if (this.headersSent)
@@ -644,20 +411,13 @@ class ExisResponse {
644
411
  stream.pipe(this.raw);
645
412
  }
646
413
  /**
647
- * Set _Content-Type_ response header with `type` through `mime.lookup()`
648
- * when it does not contain "/", or set the Content-Type to `type` otherwise.
414
+ * Sets Content-Type header using file extension or MIME type string.
649
415
  *
650
- * Examples:
651
- *
652
- * res.type('.html');
653
- * res.type('html');
654
- * res.type('json');
655
- * res.type('application/json');
656
- * res.type('png');
657
- *
658
- * @param {string} type
659
- * @return {this}
660
- * @public
416
+ * @example
417
+ * ```ts
418
+ * res.type('json') // sets 'application/json; charset=utf-8'
419
+ * res.type('png') // sets 'image/png'
420
+ * ```
661
421
  */
662
422
  type(type) {
663
423
  const mimeType = mime.contentType(type) || type;
@@ -665,18 +425,7 @@ class ExisResponse {
665
425
  return this;
666
426
  }
667
427
  /**
668
- * Set Link header field with the given `links`.
669
- *
670
- * Examples:
671
- *
672
- * res.links({
673
- * next: 'http://api.example.com/users?page=2',
674
- * last: 'http://api.example.com/users?page=5'
675
- * });
676
- *
677
- * @param {Record<string, string>} links
678
- * @return {this}
679
- * @public
428
+ * Sets Link header field with pagination or resource relations.
680
429
  */
681
430
  links(links) {
682
431
  let linkHeader = this.getHeader('Link') || '';
@@ -688,12 +437,7 @@ class ExisResponse {
688
437
  return this;
689
438
  }
690
439
  /**
691
- * Add `field` to Vary. If already present in the Vary set, then
692
- * this call is simply ignored.
693
- *
694
- * @param {string} field
695
- * @return {this}
696
- * @public
440
+ * Adds a field to the Vary header if not already present.
697
441
  */
698
442
  vary(field) {
699
443
  if (!field)
@@ -714,94 +458,48 @@ class ExisResponse {
714
458
  return this;
715
459
  }
716
460
  /**
717
- * Set cookie `name` to `value`, with the given `options`.
718
- *
719
- * Options:
720
- * - `maxAge` max-age in milliseconds, converted to `expires`
721
- * - `path` cookie path, defaults to '/'
722
- * - `domain` cookie domain
723
- * - `secure` secure cookie
724
- * - `httpOnly` httponly cookie
725
- * - `sameSite` samesite cookie
726
- *
727
- * Examples:
461
+ * Sets a cookie with specified security options.
728
462
  *
729
- * res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
730
- * res.cookie('cart', '1234');
463
+ * @param name Cookie name
464
+ * @param value Cookie value
465
+ * @param options Cookie options (httpOnly, secure, sameSite, maxAge, path, domain)
731
466
  *
732
- * @param {string} name
733
- * @param {string} value
734
- * @param {CookieOptions} options
735
- * @return {this}
736
- * @public
467
+ * @example
468
+ * ```ts
469
+ * res.cookie('session_token', token, {
470
+ * httpOnly: true,
471
+ * secure: true,
472
+ * sameSite: 'lax',
473
+ * maxAge: 86400000 // 1 day
474
+ * })
475
+ * ```
737
476
  */
738
477
  cookie(name, value, options = {}) {
739
- const parts = [
740
- `${encodeURIComponent(name)}=${encodeURIComponent(value)}`,
741
- ];
742
- if (options.maxAge !== undefined)
743
- parts.push(`Max-Age=${options.maxAge}`);
744
- if (options.expires)
745
- parts.push(`Expires=${options.expires.toUTCString()}`);
746
- if (options.path ?? true)
747
- parts.push(`Path=${options.path ?? '/'}`);
748
- if (options.domain)
749
- parts.push(`Domain=${options.domain}`);
750
- if (options.httpOnly)
751
- parts.push('HttpOnly');
752
- if (options.secure)
753
- parts.push('Secure');
754
- if (options.sameSite !== undefined && options.sameSite !== false) {
755
- if (options.sameSite === true) {
756
- parts.push('SameSite=Strict');
757
- }
758
- else {
759
- const str = String(options.sameSite).toLowerCase();
760
- if (str === 'strict')
761
- parts.push('SameSite=Strict');
762
- else if (str === 'lax')
763
- parts.push('SameSite=Lax');
764
- else if (str === 'none') {
765
- parts.push('SameSite=None');
766
- if (!options.secure && !parts.includes('Secure')) {
767
- parts.push('Secure');
768
- }
769
- }
770
- }
771
- }
772
- if (options.partitioned)
773
- parts.push('Partitioned');
774
- if (options.priority) {
775
- const p = options.priority.toLowerCase();
776
- if (p === 'low')
777
- parts.push('Priority=Low');
778
- else if (p === 'medium')
779
- parts.push('Priority=Medium');
780
- else if (p === 'high')
781
- parts.push('Priority=High');
782
- }
783
- this.append('Set-Cookie', parts.join('; '));
478
+ const serialized = (0, helpers_1.serializeCookie)(name, value, options);
479
+ this.append('Set-Cookie', serialized);
784
480
  if (process.env.NODE_ENV === 'development') {
785
- this.append('X-Set-Cookie', parts.join('; '));
481
+ this.append('X-Set-Cookie', serialized);
786
482
  }
787
483
  return this;
788
484
  }
789
485
  /**
790
- * Clear cookie `name`.
486
+ * Clears a cookie by setting its expiration to the past.
487
+ *
488
+ * @param name Cookie name
489
+ * @param options Cookie options (path, domain)
791
490
  *
792
- * @param {string} name
793
- * @param {CookieOptions} [options]
794
- * @return {this}
795
- * @public
491
+ * @example
492
+ * ```ts
493
+ * res.clearCookie('session_token')
494
+ * ```
796
495
  */
797
496
  clearCookie(name, options = {}) {
798
- const { maxAge: _, expires: __, ...rest } = options;
799
- return this.cookie(name, '', {
800
- httpOnly: true,
801
- ...rest,
802
- expires: new Date(0),
803
- maxAge: 0,
804
- });
497
+ const serialized = (0, helpers_1.serializeClearCookie)(name, options);
498
+ this.append('Set-Cookie', serialized);
499
+ if (process.env.NODE_ENV === 'development') {
500
+ this.append('X-Set-Cookie', serialized);
501
+ }
502
+ return this;
805
503
  }
806
504
  }
807
505
  exports.ExisResponse = ExisResponse;