compatibleAPI.js 7.5 KB

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