compatibleAPI.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. "use strict";
  2. /** @typedef {import("../index.js").IncomingMessage} IncomingMessage */
  3. /** @typedef {import("../index.js").ServerResponse} ServerResponse */
  4. /** @typedef {import("../index").OutputFileSystem} OutputFileSystem */
  5. /**
  6. * @typedef {object} ExpectedIncomingMessage
  7. * @property {((name: string) => string | string[] | undefined)=} getHeader get header extra method
  8. * @property {(() => string | undefined)=} getMethod get method extra method
  9. * @property {(() => string | undefined)=} getURL get URL extra method
  10. */
  11. // eslint-disable-next-line jsdoc/no-restricted-syntax
  12. /**
  13. * @typedef {object} ExpectedServerResponse
  14. * @property {((status: number) => void)=} setStatusCode set status code
  15. * @property {(() => number)=} getStatusCode get status code
  16. * @property {((name: string) => string | string[] | undefined | number)} getHeader get header
  17. * @property {((name: string, value: number | string | Readonly<string[]>) => ExpectedServerResponse)=} setHeader set header
  18. * @property {((name: string) => void)=} removeHeader remove header
  19. * @property {((data: string | Buffer) => void)=} send send
  20. * @property {((data?: string | Buffer) => void)=} finish finish
  21. * @property {(() => string[])=} getResponseHeaders get response header
  22. * @property {(() => boolean)=} getHeadersSent get headers sent
  23. * @property {((data: any) => void)=} stream stream
  24. * @property {(() => any)=} getOutgoing get outgoing
  25. * @property {((name: string, value: any) => void)=} setState set state
  26. */
  27. /**
  28. * @template {IncomingMessage & ExpectedIncomingMessage} Request
  29. * @param {Request} req req
  30. * @param {string} name name
  31. * @returns {string | string[] | undefined} request header
  32. */
  33. function getRequestHeader(req, name) {
  34. // Pseudo API
  35. if (typeof req.getHeader === "function") {
  36. return req.getHeader(name);
  37. }
  38. return req.headers[name];
  39. }
  40. /**
  41. * @template {IncomingMessage & ExpectedIncomingMessage} Request
  42. * @param {Request} req req
  43. * @returns {string | undefined} request method
  44. */
  45. function getRequestMethod(req) {
  46. // Pseudo API
  47. if (typeof req.getMethod === "function") {
  48. return req.getMethod();
  49. }
  50. return req.method;
  51. }
  52. /**
  53. * @template {IncomingMessage & ExpectedIncomingMessage} Request
  54. * @param {Request} req req
  55. * @returns {string | undefined} request URL
  56. */
  57. function getRequestURL(req) {
  58. // Pseudo API
  59. if (typeof req.getURL === "function") {
  60. return req.getURL();
  61. }
  62. return req.url;
  63. }
  64. /**
  65. * @template {ServerResponse & ExpectedServerResponse} Response
  66. * @param {Response} res res
  67. * @param {number} code code
  68. * @returns {void}
  69. */
  70. function setStatusCode(res, code) {
  71. // Pseudo API
  72. if (typeof res.setStatusCode === "function") {
  73. res.setStatusCode(code);
  74. return;
  75. }
  76. // Node.js API
  77. res.statusCode = code;
  78. }
  79. /**
  80. * @template {ServerResponse & ExpectedServerResponse} Response
  81. * @param {Response} res res
  82. * @returns {number} status code
  83. */
  84. function getStatusCode(res) {
  85. // Pseudo API
  86. if (typeof res.getStatusCode === "function") {
  87. return res.getStatusCode();
  88. }
  89. return res.statusCode;
  90. }
  91. /**
  92. * @template {ServerResponse & ExpectedServerResponse} Response
  93. * @param {Response} res res
  94. * @param {string} name name
  95. * @returns {string | string[] | undefined | number} header
  96. */
  97. function getResponseHeader(res, name) {
  98. // Real and Pseudo API
  99. return res.getHeader(name);
  100. }
  101. /**
  102. * @template {ServerResponse & ExpectedServerResponse} Response
  103. * @param {Response} res res
  104. * @param {string} name name
  105. * @param {number | string | Readonly<string[]>} value value
  106. * @returns {Response} response
  107. */
  108. function setResponseHeader(res, name, value) {
  109. // Real and Pseudo API
  110. return res.setHeader(name, value);
  111. }
  112. /**
  113. * @template {ServerResponse & ExpectedServerResponse} Response
  114. * @param {Response} res res
  115. * @param {string} name name
  116. * @returns {void}
  117. */
  118. function removeResponseHeader(res, name) {
  119. // Real and Pseudo API
  120. res.removeHeader(name);
  121. }
  122. /**
  123. * @template {ServerResponse & ExpectedServerResponse} Response
  124. * @param {Response} res res
  125. * @returns {string[]} header names
  126. */
  127. function getResponseHeaders(res) {
  128. // Pseudo API
  129. if (typeof res.getResponseHeaders === "function") {
  130. return res.getResponseHeaders();
  131. }
  132. return res.getHeaderNames();
  133. }
  134. /**
  135. * @template {ServerResponse & ExpectedServerResponse} Response
  136. * @param {Response} res res
  137. * @returns {boolean} true when headers were sent, otherwise false
  138. */
  139. function getHeadersSent(res) {
  140. // Pseudo API
  141. if (typeof res.getHeadersSent === "function") {
  142. return res.getHeadersSent();
  143. }
  144. return res.headersSent;
  145. }
  146. /**
  147. * @template {ServerResponse & ExpectedServerResponse} Response
  148. * @param {Response} res res
  149. * @param {import("fs").ReadStream} bufferOrStream buffer or stream
  150. */
  151. function pipe(res, bufferOrStream) {
  152. // Pseudo API and Koa API
  153. if (typeof res.stream === "function") {
  154. // Writable stream into Readable stream
  155. res.stream(bufferOrStream);
  156. return;
  157. }
  158. // Node.js API and Express API and Hapi API
  159. bufferOrStream.pipe(res);
  160. }
  161. /**
  162. * @template {ServerResponse & ExpectedServerResponse} Response
  163. * @param {Response} res res
  164. * @param {string | Buffer} bufferOrString buffer or string
  165. * @returns {void}
  166. */
  167. function send(res, bufferOrString) {
  168. // Pseudo API and Express API and Koa API
  169. if (typeof res.send === "function") {
  170. res.send(bufferOrString);
  171. return;
  172. }
  173. res.end(bufferOrString);
  174. }
  175. /**
  176. * @template {ServerResponse & ExpectedServerResponse} Response
  177. * @param {Response} res res
  178. * @param {(string | Buffer)=} data data
  179. */
  180. function finish(res, data) {
  181. // Pseudo API and Express API and Koa API
  182. if (typeof res.finish === "function") {
  183. res.finish(data);
  184. return;
  185. }
  186. // Pseudo API and Express API and Koa API
  187. res.end(data);
  188. }
  189. /**
  190. * @param {string} filename filename
  191. * @param {OutputFileSystem} outputFileSystem output file system
  192. * @param {number} start start
  193. * @param {number} end end
  194. * @returns {{ bufferOrStream: (Buffer | import("fs").ReadStream), byteLength: number }} result with buffer or stream and byte length
  195. */
  196. function createReadStreamOrReadFileSync(filename, outputFileSystem, start, end) {
  197. /** @type {string | Buffer | import("fs").ReadStream} */
  198. let bufferOrStream;
  199. /** @type {number} */
  200. let byteLength;
  201. // Stream logic
  202. const isFsSupportsStream = typeof outputFileSystem.createReadStream === "function";
  203. if (isFsSupportsStream) {
  204. bufferOrStream = /** @type {import("fs").createReadStream} */
  205. outputFileSystem.createReadStream(filename, {
  206. start,
  207. end
  208. });
  209. // Handle files with zero bytes
  210. byteLength = end === 0 ? 0 : end - start + 1;
  211. } else {
  212. bufferOrStream = outputFileSystem.readFileSync(filename);
  213. ({
  214. byteLength
  215. } = bufferOrStream);
  216. }
  217. return {
  218. bufferOrStream,
  219. byteLength
  220. };
  221. }
  222. /**
  223. * @template {ServerResponse & ExpectedServerResponse} Response
  224. * @param {Response} res res
  225. * @returns {Response} res res
  226. */
  227. function getOutgoing(res) {
  228. // Pseudo API and Express API and Koa API
  229. if (typeof res.getOutgoing === "function") {
  230. return res.getOutgoing();
  231. }
  232. return res;
  233. }
  234. /**
  235. * @template {ServerResponse & ExpectedServerResponse} Response
  236. * @param {Response} res res
  237. */
  238. function initState(res) {
  239. if (typeof res.setState === "function") {
  240. return;
  241. }
  242. // fixes #282. credit @cexoso. in certain edge situations res.locals is undefined.
  243. res.locals || (res.locals = {});
  244. }
  245. // eslint-disable-next-line jsdoc/no-restricted-syntax
  246. /**
  247. * @template {ServerResponse & ExpectedServerResponse} Response
  248. * @param {Response} res res
  249. * @param {string} name name
  250. * @param {any} value state
  251. * @returns {void}
  252. */
  253. function setState(res, name, value) {
  254. if (typeof res.setState === "function") {
  255. res.setState(name, value);
  256. return;
  257. }
  258. // eslint-disable-next-line jsdoc/no-restricted-syntax
  259. /** @type {any} */
  260. res.locals[name] = value;
  261. }
  262. module.exports = {
  263. createReadStreamOrReadFileSync,
  264. finish,
  265. getHeadersSent,
  266. getOutgoing,
  267. getRequestHeader,
  268. getRequestMethod,
  269. getRequestURL,
  270. getResponseHeader,
  271. getResponseHeaders,
  272. getStatusCode,
  273. initState,
  274. pipe,
  275. removeResponseHeader,
  276. send,
  277. setResponseHeader,
  278. setState,
  279. setStatusCode
  280. };