compatibleAPI.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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 {() => boolean} [getHeadersSent]
  22. * @property {(data: any) => void} [stream]
  23. * @property {() => any} [getOutgoing]
  24. * @property {(name: string, value: any) => void} [setState]
  25. * @property {() => "ready" | "open" | "readable"} [getReadyReadableStreamState]
  26. */
  27. /**
  28. * @template {IncomingMessage & ExpectedIncomingMessage} Request
  29. * @param {Request} req
  30. * @param {string} name
  31. * @returns {string | string[] | undefined}
  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
  43. * @returns {string | undefined}
  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
  55. * @returns {string | undefined}
  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
  67. * @param {number} code
  68. */
  69. function setStatusCode(res, code) {
  70. // Pseudo API
  71. if (typeof res.setStatusCode === "function") {
  72. res.setStatusCode(code);
  73. return;
  74. }
  75. // Node.js API
  76. // eslint-disable-next-line no-param-reassign
  77. res.statusCode = code;
  78. }
  79. /**
  80. * @template {ServerResponse & ExpectedServerResponse} Response
  81. * @param {Response} res
  82. * @returns {number}
  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
  94. * @param {string} name
  95. * @returns {string | string[] | undefined | number}
  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
  104. * @param {string} name
  105. * @param {number | string | Readonly<string[]>} value
  106. * @returns {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
  115. * @param {string} name
  116. */
  117. function removeResponseHeader(res, name) {
  118. // Real and Pseudo API
  119. res.removeHeader(name);
  120. }
  121. /**
  122. * @template {ServerResponse & ExpectedServerResponse} Response
  123. * @param {Response} res
  124. * @returns {string[]}
  125. */
  126. function getResponseHeaders(res) {
  127. // Pseudo API
  128. if (typeof res.getResponseHeaders === "function") {
  129. return res.getResponseHeaders();
  130. }
  131. return res.getHeaderNames();
  132. }
  133. /**
  134. * @template {ServerResponse & ExpectedServerResponse} Response
  135. * @param {Response} res
  136. * @returns {boolean}
  137. */
  138. function getHeadersSent(res) {
  139. // Pseudo API
  140. if (typeof res.getHeadersSent === "function") {
  141. return res.getHeadersSent();
  142. }
  143. return res.headersSent;
  144. }
  145. /**
  146. * @template {ServerResponse & ExpectedServerResponse} Response
  147. * @param {Response} res
  148. * @param {import("fs").ReadStream} bufferOrStream
  149. */
  150. function pipe(res, bufferOrStream) {
  151. // Pseudo API and Koa API
  152. if (typeof res.stream === "function") {
  153. // Writable stream into Readable stream
  154. res.stream(bufferOrStream);
  155. return;
  156. }
  157. // Node.js API and Express API and Hapi API
  158. bufferOrStream.pipe(res);
  159. }
  160. /**
  161. * @template {ServerResponse & ExpectedServerResponse} Response
  162. * @param {Response} res
  163. * @param {string | Buffer} bufferOrString
  164. */
  165. function send(res, bufferOrString) {
  166. // Pseudo API and Express API and Koa API
  167. if (typeof res.send === "function") {
  168. res.send(bufferOrString);
  169. return;
  170. }
  171. res.end(bufferOrString);
  172. }
  173. /**
  174. * @template {ServerResponse & ExpectedServerResponse} Response
  175. * @param {Response} res
  176. * @param {string | Buffer} [data]
  177. */
  178. function finish(res, data) {
  179. // Pseudo API and Express API and Koa API
  180. if (typeof res.finish === "function") {
  181. res.finish(data);
  182. return;
  183. }
  184. // Pseudo API and Express API and Koa API
  185. res.end(data);
  186. }
  187. /**
  188. * @param {string} filename
  189. * @param {OutputFileSystem} outputFileSystem
  190. * @param {number} start
  191. * @param {number} end
  192. * @returns {{ bufferOrStream: (Buffer | import("fs").ReadStream), byteLength: number }}
  193. */
  194. function createReadStreamOrReadFileSync(filename, outputFileSystem, start, end) {
  195. /** @type {string | Buffer | import("fs").ReadStream} */
  196. let bufferOrStream;
  197. /** @type {number} */
  198. let byteLength;
  199. // Stream logic
  200. const isFsSupportsStream = typeof outputFileSystem.createReadStream === "function";
  201. if (isFsSupportsStream) {
  202. bufferOrStream = /** @type {import("fs").createReadStream} */
  203. outputFileSystem.createReadStream(filename, {
  204. start,
  205. end
  206. });
  207. // Handle files with zero bytes
  208. byteLength = end === 0 ? 0 : end - start + 1;
  209. } else {
  210. bufferOrStream = outputFileSystem.readFileSync(filename);
  211. ({
  212. byteLength
  213. } = bufferOrStream);
  214. }
  215. return {
  216. bufferOrStream,
  217. byteLength
  218. };
  219. }
  220. /**
  221. * @template {ServerResponse & ExpectedServerResponse} Response
  222. * @param {Response} res
  223. * @returns {Response} res
  224. */
  225. function getOutgoing(res) {
  226. // Pseudo API and Express API and Koa API
  227. if (typeof res.getOutgoing === "function") {
  228. return res.getOutgoing();
  229. }
  230. return res;
  231. }
  232. /**
  233. * @template {ServerResponse & ExpectedServerResponse} Response
  234. * @param {Response} res
  235. */
  236. function initState(res) {
  237. if (typeof res.setState === "function") {
  238. return;
  239. }
  240. // fixes #282. credit @cexoso. in certain edge situations res.locals is undefined.
  241. // eslint-disable-next-line no-param-reassign
  242. res.locals = res.locals || {};
  243. }
  244. /**
  245. * @template {ServerResponse & ExpectedServerResponse} Response
  246. * @param {Response} res
  247. * @param {string} name
  248. * @param {any} value
  249. */
  250. function setState(res, name, value) {
  251. if (typeof res.setState === "function") {
  252. res.setState(name, value);
  253. return;
  254. }
  255. /** @type {any} */
  256. // eslint-disable-next-line no-param-reassign
  257. res.locals[name] = value;
  258. }
  259. /**
  260. * @template {ServerResponse & ExpectedServerResponse} Response
  261. * @param {Response} res
  262. * @returns {"ready" | "open" | "readable"}
  263. */
  264. function getReadyReadableStreamState(res) {
  265. // Pseudo API and Express API and Koa API
  266. if (typeof res.getReadyReadableStreamState === "function") {
  267. return res.getReadyReadableStreamState();
  268. }
  269. return "ready";
  270. }
  271. module.exports = {
  272. setStatusCode,
  273. getStatusCode,
  274. getRequestHeader,
  275. getRequestMethod,
  276. getRequestURL,
  277. getResponseHeader,
  278. setResponseHeader,
  279. removeResponseHeader,
  280. getResponseHeaders,
  281. getHeadersSent,
  282. pipe,
  283. send,
  284. finish,
  285. createReadStreamOrReadFileSync,
  286. getOutgoing,
  287. initState,
  288. setState,
  289. getReadyReadableStreamState
  290. };