@ohos-ports/layer0-prefetch 4.20.0-beta.0 → 4.20.0-beta.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 (281) hide show
  1. package/node_modules/@layer0/core/README.md +50 -0
  2. package/node_modules/@layer0/core/build.d.ts +5 -0
  3. package/node_modules/@layer0/core/build.js +17 -0
  4. package/node_modules/@layer0/core/cache/Cache.d.ts +56 -0
  5. package/node_modules/@layer0/core/cache/Cache.js +138 -0
  6. package/node_modules/@layer0/core/cache/CacheKey.d.ts +52 -0
  7. package/node_modules/@layer0/core/cache/CacheKey.js +95 -0
  8. package/node_modules/@layer0/core/config.d.ts +53 -0
  9. package/node_modules/@layer0/core/config.js +113 -0
  10. package/node_modules/@layer0/core/constants.d.ts +214 -0
  11. package/node_modules/@layer0/core/constants.js +216 -0
  12. package/node_modules/@layer0/core/context.d.ts +38 -0
  13. package/node_modules/@layer0/core/context.js +98 -0
  14. package/node_modules/@layer0/core/default-app/all/layer0.config.js +60 -0
  15. package/node_modules/@layer0/core/default-app/js/routes.js +27 -0
  16. package/node_modules/@layer0/core/default-app/ts/routes.ts +27 -0
  17. package/node_modules/@layer0/core/deploy/DeploymentBuilder.d.ts +177 -0
  18. package/node_modules/@layer0/core/deploy/DeploymentBuilder.js +543 -0
  19. package/node_modules/@layer0/core/deploy/bundle-esbuild.d.ts +11 -0
  20. package/node_modules/@layer0/core/deploy/bundle-esbuild.js +67 -0
  21. package/node_modules/@layer0/core/deploy/bundle-ncc.d.ts +8 -0
  22. package/node_modules/@layer0/core/deploy/bundle-ncc.js +30 -0
  23. package/node_modules/@layer0/core/deploy/bundle-nft.d.ts +7 -0
  24. package/node_modules/@layer0/core/deploy/bundle-nft.js +41 -0
  25. package/node_modules/@layer0/core/deploy/bundle-webpack.d.ts +11 -0
  26. package/node_modules/@layer0/core/deploy/bundle-webpack.js +127 -0
  27. package/node_modules/@layer0/core/deploy/index.d.ts +19 -0
  28. package/node_modules/@layer0/core/deploy/index.js +6 -0
  29. package/node_modules/@layer0/core/deploy/listSources.d.ts +11 -0
  30. package/node_modules/@layer0/core/deploy/listSources.js +18 -0
  31. package/node_modules/@layer0/core/deploy/paths.d.ts +27 -0
  32. package/node_modules/@layer0/core/deploy/paths.js +33 -0
  33. package/node_modules/@layer0/core/deploy/resolveInPackage.d.ts +7 -0
  34. package/node_modules/@layer0/core/deploy/resolveInPackage.js +21 -0
  35. package/node_modules/@layer0/core/deploy/routeToFiles.d.ts +14 -0
  36. package/node_modules/@layer0/core/deploy/routeToFiles.js +24 -0
  37. package/node_modules/@layer0/core/dev/DevServerOptions.d.ts +33 -0
  38. package/node_modules/@layer0/core/dev/DevServerOptions.js +2 -0
  39. package/node_modules/@layer0/core/dev/createDevServer.d.ts +8 -0
  40. package/node_modules/@layer0/core/dev/createDevServer.js +114 -0
  41. package/node_modules/@layer0/core/dev/index.d.ts +3 -0
  42. package/node_modules/@layer0/core/dev/index.js +6 -0
  43. package/node_modules/@layer0/core/dev/runLocalApp.d.ts +8 -0
  44. package/node_modules/@layer0/core/dev/runLocalApp.js +73 -0
  45. package/node_modules/@layer0/core/dev/serverlessCompatibilityMiddleware.d.ts +11 -0
  46. package/node_modules/@layer0/core/dev/serverlessCompatibilityMiddleware.js +30 -0
  47. package/node_modules/@layer0/core/edge/EdgeErrorPageResponseWriter.d.ts +12 -0
  48. package/node_modules/@layer0/core/edge/EdgeErrorPageResponseWriter.js +22 -0
  49. package/node_modules/@layer0/core/edge/EdgeResponseWriter.d.ts +122 -0
  50. package/node_modules/@layer0/core/edge/EdgeResponseWriter.js +451 -0
  51. package/node_modules/@layer0/core/edge/createEdgeConfig.d.ts +16 -0
  52. package/node_modules/@layer0/core/edge/createEdgeConfig.js +284 -0
  53. package/node_modules/@layer0/core/environment.d.ts +24 -0
  54. package/node_modules/@layer0/core/environment.js +42 -0
  55. package/node_modules/@layer0/core/errors/BackendFetchError.d.ts +11 -0
  56. package/node_modules/@layer0/core/errors/BackendFetchError.js +17 -0
  57. package/node_modules/@layer0/core/errors/FrameworkBuildError.d.ts +7 -0
  58. package/node_modules/@layer0/core/errors/FrameworkBuildError.js +26 -0
  59. package/node_modules/@layer0/core/index.d.ts +3 -0
  60. package/node_modules/@layer0/core/index.js +9 -0
  61. package/node_modules/@layer0/core/init/addToPackageJson.d.ts +7 -0
  62. package/node_modules/@layer0/core/init/addToPackageJson.js +22 -0
  63. package/node_modules/@layer0/core/init/index.d.ts +2 -0
  64. package/node_modules/@layer0/core/init/index.js +6 -0
  65. package/node_modules/@layer0/core/init/init.d.ts +5 -0
  66. package/node_modules/@layer0/core/init/init.js +17 -0
  67. package/node_modules/@layer0/core/lambda/JSBackend.d.ts +30 -0
  68. package/node_modules/@layer0/core/lambda/JSBackend.js +51 -0
  69. package/node_modules/@layer0/core/lambda/RequestListenerJSBackend.d.ts +8 -0
  70. package/node_modules/@layer0/core/lambda/RequestListenerJSBackend.js +19 -0
  71. package/node_modules/@layer0/core/lambda/adaptResponseEncodingToDownstream.d.ts +17 -0
  72. package/node_modules/@layer0/core/lambda/adaptResponseEncodingToDownstream.js +132 -0
  73. package/node_modules/@layer0/core/lambda/consoleWrapper.d.ts +16 -0
  74. package/node_modules/@layer0/core/lambda/consoleWrapper.js +127 -0
  75. package/node_modules/@layer0/core/lambda/getBodyLoggingData.d.ts +10 -0
  76. package/node_modules/@layer0/core/lambda/getBodyLoggingData.js +108 -0
  77. package/node_modules/@layer0/core/lambda/handler.d.ts +1 -0
  78. package/node_modules/@layer0/core/lambda/handler.js +91 -0
  79. package/node_modules/@layer0/core/lambda/handler.js.map +7 -0
  80. package/node_modules/@layer0/core/lambda/innerHandler.d.ts +7 -0
  81. package/node_modules/@layer0/core/lambda/innerHandler.js +181 -0
  82. package/node_modules/@layer0/core/lambda/lambdaInstance.d.ts +29 -0
  83. package/node_modules/@layer0/core/lambda/lambdaInstance.js +71 -0
  84. package/node_modules/@layer0/core/lambda/reqResMapper.d.ts +24 -0
  85. package/node_modules/@layer0/core/lambda/reqResMapper.js +286 -0
  86. package/node_modules/@layer0/core/lambda/stdStreamsWrapper.d.ts +31 -0
  87. package/node_modules/@layer0/core/lambda/stdStreamsWrapper.js +196 -0
  88. package/node_modules/@layer0/core/lambda/toJSBackend.d.ts +10 -0
  89. package/node_modules/@layer0/core/lambda/toJSBackend.js +23 -0
  90. package/node_modules/@layer0/core/node_modules/esbuild/LICENSE.md +21 -0
  91. package/node_modules/@layer0/core/node_modules/esbuild/README.md +3 -0
  92. package/node_modules/@layer0/core/node_modules/esbuild/bin/esbuild +223 -0
  93. package/node_modules/@layer0/core/node_modules/esbuild/install.js +301 -0
  94. package/node_modules/@layer0/core/node_modules/esbuild/lib/main.d.ts +716 -0
  95. package/node_modules/@layer0/core/node_modules/esbuild/lib/main.js +2532 -0
  96. package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/README.md +3 -0
  97. package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/bin/esbuild +0 -0
  98. package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/esbuild.wasm +0 -0
  99. package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/package.json +28 -0
  100. package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/wasm_exec.js +575 -0
  101. package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/wasm_exec_node.js +40 -0
  102. package/node_modules/@layer0/core/node_modules/esbuild/package.json +78 -0
  103. package/node_modules/@layer0/core/package.json +132 -0
  104. package/node_modules/@layer0/core/parser/parseRouter.d.ts +13 -0
  105. package/node_modules/@layer0/core/parser/parseRouter.js +77 -0
  106. package/node_modules/@layer0/core/plugins/PluginBase.d.ts +18 -0
  107. package/node_modules/@layer0/core/plugins/PluginBase.js +19 -0
  108. package/node_modules/@layer0/core/router/Backend.d.ts +69 -0
  109. package/node_modules/@layer0/core/router/Backend.js +176 -0
  110. package/node_modules/@layer0/core/router/BackendOptions.d.ts +11 -0
  111. package/node_modules/@layer0/core/router/BackendOptions.js +2 -0
  112. package/node_modules/@layer0/core/router/BaseResponseWriter.d.ts +65 -0
  113. package/node_modules/@layer0/core/router/BaseResponseWriter.js +81 -0
  114. package/node_modules/@layer0/core/router/BehindEdgeResponseWriterProxy.d.ts +4 -0
  115. package/node_modules/@layer0/core/router/BehindEdgeResponseWriterProxy.js +89 -0
  116. package/node_modules/@layer0/core/router/CacheManifest.d.ts +80 -0
  117. package/node_modules/@layer0/core/router/CacheManifest.js +83 -0
  118. package/node_modules/@layer0/core/router/CacheOptions.d.ts +171 -0
  119. package/node_modules/@layer0/core/router/CacheOptions.js +2 -0
  120. package/node_modules/@layer0/core/router/CustomCacheKey.d.ts +140 -0
  121. package/node_modules/@layer0/core/router/CustomCacheKey.js +272 -0
  122. package/node_modules/@layer0/core/router/GroupConfig.d.ts +7 -0
  123. package/node_modules/@layer0/core/router/GroupConfig.js +20 -0
  124. package/node_modules/@layer0/core/router/Groups.d.ts +7 -0
  125. package/node_modules/@layer0/core/router/Groups.js +21 -0
  126. package/node_modules/@layer0/core/router/Headers.d.ts +3 -0
  127. package/node_modules/@layer0/core/router/Headers.js +2 -0
  128. package/node_modules/@layer0/core/router/PathMatcher.d.ts +51 -0
  129. package/node_modules/@layer0/core/router/PathMatcher.js +73 -0
  130. package/node_modules/@layer0/core/router/Preload.d.ts +16 -0
  131. package/node_modules/@layer0/core/router/Preload.js +2 -0
  132. package/node_modules/@layer0/core/router/PreloadRequests.d.ts +23 -0
  133. package/node_modules/@layer0/core/router/PreloadRequests.js +66 -0
  134. package/node_modules/@layer0/core/router/Request.d.ts +74 -0
  135. package/node_modules/@layer0/core/router/Request.js +2 -0
  136. package/node_modules/@layer0/core/router/RequestContext.d.ts +12 -0
  137. package/node_modules/@layer0/core/router/RequestContext.js +11 -0
  138. package/node_modules/@layer0/core/router/Response.d.ts +55 -0
  139. package/node_modules/@layer0/core/router/Response.js +2 -0
  140. package/node_modules/@layer0/core/router/ResponseWriter.d.ts +1011 -0
  141. package/node_modules/@layer0/core/router/ResponseWriter.js +1341 -0
  142. package/node_modules/@layer0/core/router/Route.d.ts +124 -0
  143. package/node_modules/@layer0/core/router/Route.js +280 -0
  144. package/node_modules/@layer0/core/router/RouteCriteria.d.ts +68 -0
  145. package/node_modules/@layer0/core/router/RouteCriteria.js +2 -0
  146. package/node_modules/@layer0/core/router/RouteGroup.d.ts +157 -0
  147. package/node_modules/@layer0/core/router/RouteGroup.js +223 -0
  148. package/node_modules/@layer0/core/router/RouteGroupList.d.ts +45 -0
  149. package/node_modules/@layer0/core/router/RouteGroupList.js +73 -0
  150. package/node_modules/@layer0/core/router/Router.d.ts +439 -0
  151. package/node_modules/@layer0/core/router/Router.js +907 -0
  152. package/node_modules/@layer0/core/router/addBuiltInRoutes.d.ts +2 -0
  153. package/node_modules/@layer0/core/router/addBuiltInRoutes.js +58 -0
  154. package/node_modules/@layer0/core/router/addPreloadHeaders.d.ts +7 -0
  155. package/node_modules/@layer0/core/router/addPreloadHeaders.js +57 -0
  156. package/node_modules/@layer0/core/router/cacheManifestFromRouter.d.ts +6 -0
  157. package/node_modules/@layer0/core/router/cacheManifestFromRouter.js +85 -0
  158. package/node_modules/@layer0/core/router/createCustomCacheKey.d.ts +27 -0
  159. package/node_modules/@layer0/core/router/createCustomCacheKey.js +35 -0
  160. package/node_modules/@layer0/core/router/deriveSurrogateKeysFromJson.d.ts +36 -0
  161. package/node_modules/@layer0/core/router/deriveSurrogateKeysFromJson.js +70 -0
  162. package/node_modules/@layer0/core/router/index.d.ts +11 -0
  163. package/node_modules/@layer0/core/router/index.js +17 -0
  164. package/node_modules/@layer0/core/router/interpolateParams.d.ts +21 -0
  165. package/node_modules/@layer0/core/router/interpolateParams.js +150 -0
  166. package/node_modules/@layer0/core/router/loadRouter.d.ts +13 -0
  167. package/node_modules/@layer0/core/router/loadRouter.js +29 -0
  168. package/node_modules/@layer0/core/router/mergeQueryString.d.ts +1 -0
  169. package/node_modules/@layer0/core/router/mergeQueryString.js +23 -0
  170. package/node_modules/@layer0/core/router/normalizeCriteria.d.ts +6 -0
  171. package/node_modules/@layer0/core/router/normalizeCriteria.js +20 -0
  172. package/node_modules/@layer0/core/router/stream.d.ts +12 -0
  173. package/node_modules/@layer0/core/router/stream.js +30 -0
  174. package/node_modules/@layer0/core/router/universalRouteUtils.d.ts +55 -0
  175. package/node_modules/@layer0/core/router/universalRouteUtils.js +114 -0
  176. package/node_modules/@layer0/core/router/validateWriter.d.ts +41 -0
  177. package/node_modules/@layer0/core/router/validateWriter.js +120 -0
  178. package/node_modules/@layer0/core/serverless.yml +30 -0
  179. package/node_modules/@layer0/core/source.d.ts +9 -0
  180. package/node_modules/@layer0/core/source.js +17 -0
  181. package/node_modules/@layer0/core/test-utils/MockRequest.d.ts +15 -0
  182. package/node_modules/@layer0/core/test-utils/MockRequest.js +36 -0
  183. package/node_modules/@layer0/core/test-utils/MockResponse.d.ts +7 -0
  184. package/node_modules/@layer0/core/test-utils/MockResponse.js +24 -0
  185. package/node_modules/@layer0/core/test-utils/expect-extend.d.ts +1 -0
  186. package/node_modules/@layer0/core/test-utils/expect-extend.js +11 -0
  187. package/node_modules/@layer0/core/test-utils/index.d.ts +4 -0
  188. package/node_modules/@layer0/core/test-utils/index.js +11 -0
  189. package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheBrowser.d.ts +19 -0
  190. package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheBrowser.js +45 -0
  191. package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheEdge.d.ts +19 -0
  192. package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheEdge.js +38 -0
  193. package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheServiceWorker.d.ts +19 -0
  194. package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheServiceWorker.js +45 -0
  195. package/node_modules/@layer0/core/test-utils/matchers/toBeServedStale.d.ts +20 -0
  196. package/node_modules/@layer0/core/test-utils/matchers/toBeServedStale.js +50 -0
  197. package/node_modules/@layer0/core/test-utils/matchers/toHaveBody.d.ts +18 -0
  198. package/node_modules/@layer0/core/test-utils/matchers/toHaveBody.js +40 -0
  199. package/node_modules/@layer0/core/test-utils/matchers/toHaveHeader.d.ts +19 -0
  200. package/node_modules/@layer0/core/test-utils/matchers/toHaveHeader.js +43 -0
  201. package/node_modules/@layer0/core/test-utils/matchers.d.ts +6 -0
  202. package/node_modules/@layer0/core/test-utils/matchers.js +18 -0
  203. package/node_modules/@layer0/core/test-utils/utils/createRouteMock.d.ts +21 -0
  204. package/node_modules/@layer0/core/test-utils/utils/createRouteMock.js +27 -0
  205. package/node_modules/@layer0/core/test-utils/utils/getBackend.d.ts +13 -0
  206. package/node_modules/@layer0/core/test-utils/utils/getBackend.js +30 -0
  207. package/node_modules/@layer0/core/test-utils/utils/getMessage.d.ts +1 -0
  208. package/node_modules/@layer0/core/test-utils/utils/getMessage.js +16 -0
  209. package/node_modules/@layer0/core/test-utils/utils/runRoute.d.ts +10 -0
  210. package/node_modules/@layer0/core/test-utils/utils/runRoute.js +39 -0
  211. package/node_modules/@layer0/core/timing.d.ts +115 -0
  212. package/node_modules/@layer0/core/timing.js +200 -0
  213. package/node_modules/@layer0/core/transform/index.d.ts +1 -0
  214. package/node_modules/@layer0/core/transform/index.js +4 -0
  215. package/node_modules/@layer0/core/transform/transformMethod.d.ts +40 -0
  216. package/node_modules/@layer0/core/transform/transformMethod.js +58 -0
  217. package/node_modules/@layer0/core/types.d.ts +102 -0
  218. package/node_modules/@layer0/core/types.js +30 -0
  219. package/node_modules/@layer0/core/utils/Tasks.d.ts +32 -0
  220. package/node_modules/@layer0/core/utils/Tasks.js +80 -0
  221. package/node_modules/@layer0/core/utils/Validator.d.ts +18 -0
  222. package/node_modules/@layer0/core/utils/Validator.js +178 -0
  223. package/node_modules/@layer0/core/utils/bindParamsToPath.d.ts +15 -0
  224. package/node_modules/@layer0/core/utils/bindParamsToPath.js +49 -0
  225. package/node_modules/@layer0/core/utils/convertParamsToBackReferences.d.ts +16 -0
  226. package/node_modules/@layer0/core/utils/convertParamsToBackReferences.js +45 -0
  227. package/node_modules/@layer0/core/utils/convertStringMatcherToRegex.d.ts +7 -0
  228. package/node_modules/@layer0/core/utils/convertStringMatcherToRegex.js +26 -0
  229. package/node_modules/@layer0/core/utils/decode.d.ts +9 -0
  230. package/node_modules/@layer0/core/utils/decode.js +44 -0
  231. package/node_modules/@layer0/core/utils/esImport.d.ts +1 -0
  232. package/node_modules/@layer0/core/utils/esImport.js +9 -0
  233. package/node_modules/@layer0/core/utils/fs.d.ts +1 -0
  234. package/node_modules/@layer0/core/utils/fs.js +7 -0
  235. package/node_modules/@layer0/core/utils/get.d.ts +6 -0
  236. package/node_modules/@layer0/core/utils/get.js +19 -0
  237. package/node_modules/@layer0/core/utils/getPackageVersion.d.ts +7 -0
  238. package/node_modules/@layer0/core/utils/getPackageVersion.js +19 -0
  239. package/node_modules/@layer0/core/utils/has.d.ts +6 -0
  240. package/node_modules/@layer0/core/utils/has.js +15 -0
  241. package/node_modules/@layer0/core/utils/identity.d.ts +7 -0
  242. package/node_modules/@layer0/core/utils/identity.js +12 -0
  243. package/node_modules/@layer0/core/utils/index.d.ts +1 -0
  244. package/node_modules/@layer0/core/utils/index.js +4 -0
  245. package/node_modules/@layer0/core/utils/isEmpty.d.ts +6 -0
  246. package/node_modules/@layer0/core/utils/isEmpty.js +20 -0
  247. package/node_modules/@layer0/core/utils/isString.d.ts +1 -0
  248. package/node_modules/@layer0/core/utils/isString.js +6 -0
  249. package/node_modules/@layer0/core/utils/logo.d.ts +5 -0
  250. package/node_modules/@layer0/core/utils/logo.js +27 -0
  251. package/node_modules/@layer0/core/utils/mapValues.d.ts +7 -0
  252. package/node_modules/@layer0/core/utils/mapValues.js +16 -0
  253. package/node_modules/@layer0/core/utils/minBy.d.ts +8 -0
  254. package/node_modules/@layer0/core/utils/minBy.js +41 -0
  255. package/node_modules/@layer0/core/utils/nonWebpackRequire.d.ts +11 -0
  256. package/node_modules/@layer0/core/utils/nonWebpackRequire.js +28 -0
  257. package/node_modules/@layer0/core/utils/packageUtils.d.ts +12 -0
  258. package/node_modules/@layer0/core/utils/packageUtils.js +22 -0
  259. package/node_modules/@layer0/core/utils/ports.d.ts +5 -0
  260. package/node_modules/@layer0/core/utils/ports.js +7 -0
  261. package/node_modules/@layer0/core/utils/readBody.d.ts +7 -0
  262. package/node_modules/@layer0/core/utils/readBody.js +15 -0
  263. package/node_modules/@layer0/core/utils/readRawBody.d.ts +9 -0
  264. package/node_modules/@layer0/core/utils/readRawBody.js +21 -0
  265. package/node_modules/@layer0/core/utils/regExpEscape.d.ts +12 -0
  266. package/node_modules/@layer0/core/utils/regExpEscape.js +20 -0
  267. package/node_modules/@layer0/core/utils/requireInternal.d.ts +7 -0
  268. package/node_modules/@layer0/core/utils/requireInternal.js +24 -0
  269. package/node_modules/@layer0/core/utils/set.d.ts +10 -0
  270. package/node_modules/@layer0/core/utils/set.js +21 -0
  271. package/node_modules/@layer0/core/utils/toEdgeRegex.d.ts +11 -0
  272. package/node_modules/@layer0/core/utils/toEdgeRegex.js +23 -0
  273. package/node_modules/@layer0/core/utils/toHttpMethodMatchRegex.d.ts +12 -0
  274. package/node_modules/@layer0/core/utils/toHttpMethodMatchRegex.js +17 -0
  275. package/node_modules/@layer0/core/utils/toLower.d.ts +1 -0
  276. package/node_modules/@layer0/core/utils/toLower.js +9 -0
  277. package/node_modules/@layer0/core/utils/validateBackends.d.ts +5 -0
  278. package/node_modules/@layer0/core/utils/validateBackends.js +30 -0
  279. package/node_modules/@layer0/core/utils/watch.d.ts +10 -0
  280. package/node_modules/@layer0/core/utils/watch.js +17 -0
  281. package/package.json +47 -3
@@ -0,0 +1,1341 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = require("path");
8
+ const bindParamsToPath_1 = __importDefault(require("../utils/bindParamsToPath"));
9
+ const config_1 = __importDefault(require("../config"));
10
+ const Backend_1 = __importDefault(require("./Backend"));
11
+ const interpolateParams_1 = __importDefault(require("./interpolateParams"));
12
+ const stream_1 = __importDefault(require("./stream"));
13
+ const constants_1 = require("../constants");
14
+ const environment_1 = require("../environment");
15
+ const validateWriter_1 = require("./validateWriter");
16
+ const Route_1 = __importDefault(require("./Route"));
17
+ const decode_1 = __importDefault(require("../utils/decode"));
18
+ const Cache_1 = require("../cache/Cache");
19
+ const CacheKey_1 = __importDefault(require("../cache/CacheKey"));
20
+ const timing_1 = require("../timing");
21
+ const PathMatcher_1 = __importDefault(require("./PathMatcher"));
22
+ const nonWebpackRequire_1 = __importDefault(require("../utils/nonWebpackRequire"));
23
+ const logo_1 = require("../utils/logo");
24
+ const BaseResponseWriter_1 = require("./BaseResponseWriter");
25
+ const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
26
+ const mergeQueryString_1 = __importDefault(require("./mergeQueryString"));
27
+ const slash_1 = __importDefault(require("slash"));
28
+ exports.FAR_FUTURE_TTL = 60 * 60 * 24 * 365 * 10;
29
+ /**
30
+ * The API that is provided to route callbacks.
31
+ */
32
+ class ResponseWriter extends BaseResponseWriter_1.BaseResponseWriter {
33
+ /**
34
+ * @param req The request
35
+ * @param res The response
36
+ * @param route The route
37
+ * @param defaultBackend The default backend for proxy when none is provided
38
+ */
39
+ constructor(req, res, route, context) {
40
+ super(req, res, route);
41
+ this.params = {}; // Merged/All params
42
+ this.streaming = false;
43
+ // request/response phases
44
+ this.preCacheRequestActions = [];
45
+ this.postCacheRequestActions = [];
46
+ this.preCacheResponseActions = [];
47
+ this.postCacheResponseActions = [];
48
+ /**
49
+ * Helper function to simplify calling interpolateParams()
50
+ */
51
+ this.interpolateParams = (value) => {
52
+ return interpolateParams_1.default(this.request, this.response, value);
53
+ };
54
+ /**
55
+ * Redirects the browser to a new location.
56
+ * Query params from the original request are added to the redirect URL
57
+ *
58
+ * **Examples**
59
+ *
60
+ * ```
61
+ * new Router()
62
+ * .get('/p/:productId', ({ redirect }) => {
63
+ * return redirect('/products/:productId', { statusCode: 301 })
64
+ * })
65
+ *
66
+ * // Extract id from route and apply as query string
67
+ * new Router()
68
+ * .get('/p/:productId', ({ redirect }) => {
69
+ * return redirect('/product', { query: { id: ':productId' }})
70
+ * })
71
+ *
72
+ * // Extract id from query string and apply to route
73
+ * new Router()
74
+ * .get({ path: '/p', query: { id: ':id' } }, ({ redirect }) => {
75
+ * return redirect('/product/:id')
76
+ * })
77
+ * ```
78
+ *
79
+ * @param to The URL to which the browser will be redirected.
80
+ * @param options
81
+ * @param options.statusCode The HTTP status to return. Defaults to 301
82
+ * @param options.query Key-value object of query strings to be added to the redirect url
83
+ */
84
+ this.redirect = (to, options = {}) => {
85
+ const { to: toPath, statusCode, query } = this._readRedirectParams(to, options);
86
+ const redirectLocation = mergeQueryString_1.default(this.request.url, bindParamsToPath_1.default(
87
+ // We do interpolation here while running in browser since URL works differently in browser and server.
88
+ // This works without interpolating here and doing it later in patchResWriteHead when running on server but
89
+ // not in browser (e.g in fiddle). This is because browser is encoding the URL in some cases where server doesn't.
90
+ //
91
+ // Example:
92
+ // Server: new URL('https://www.example.com${url}', 'http://foo.com').toString() == 'https://www.example.com${url}/'
93
+ // Browser: new URL('https://www.example.com${url}', 'http://foo.com').toString() == 'https://www.example.com%24%7Burl%7D/'
94
+ environment_1.isBrowser()
95
+ ? this.interpolateParams(Route_1.default.pathWithQueryString(toPath, query))
96
+ : Route_1.default.pathWithQueryString(toPath, query), this.request.params));
97
+ this.setResponseHeader(constants_1.HTTP_HEADERS.location, redirectLocation);
98
+ this.send('', statusCode);
99
+ };
100
+ /**
101
+ * Relays the request to the specified backend.
102
+ *
103
+ * **Example**
104
+ *
105
+ * ```
106
+ * new Router()
107
+ * .get('/some/path/with/:variable', ({ proxy }) => {
108
+ * proxy('legacy', { path: '/some/other/path/with/:variable' })
109
+ * })
110
+ * ```
111
+ *
112
+ * In this example, we relay the request to the "legacy" backend. In this case, `layer0.config.js` must
113
+ * contain a definition for the `legacy` backend. For example:
114
+ *
115
+ * ```
116
+ * // layer0.config.js
117
+ *
118
+ * module.exports = {
119
+ * backends: {
120
+ * legacy: {
121
+ * domainOrIp: 'legacy.domain.com',
122
+ * hostHeader: 'domain.com'
123
+ * }
124
+ * }
125
+ * }
126
+ * ```
127
+ *
128
+ * @param backend The name of one of the backends in your `layer0.config.js` file.
129
+ * @param options
130
+ * @returns A promise the resolves once the response has been fetched from the upstream site.
131
+ */
132
+ this.proxy = async (backend, options) => {
133
+ const backendConfig = config_1.default.get(`backends.${backend}`);
134
+ await this.proxyHost(backendConfig, options);
135
+ };
136
+ /**
137
+ * Calls the provided callback to send a response.
138
+ * @param cb Render callback function
139
+ * @deprecated
140
+ */
141
+ this.render = (cb) => {
142
+ console.warn(`${logo_1.warnLogLabel} Warning ResponseWriter#render is deprecated.`);
143
+ cb(this.request, this.proxy, this.request.params);
144
+ };
145
+ /**
146
+ * Renders a result in Layer0's serverless cloud using your application. Use this method
147
+ * to respond with an SSR or API result from your application.
148
+ * @param options Options for transforming the request sent to your app and the response received from it.
149
+ */
150
+ this.renderWithApp = (options) => this.proxy(constants_1.BACKENDS.js, options);
151
+ /**
152
+ * Streams the result from a backend. Can only be used in development for things
153
+ * like hot-module reloading.
154
+ * @param backend The name of the backend to connect to
155
+ * @private
156
+ */
157
+ this.stream = (backend) => {
158
+ if (process.env.NODE_ENV === 'production') {
159
+ throw new Error('stream can only be used in development');
160
+ }
161
+ let backendConfig = normalizeBackendConfig(config_1.default.get(`backends.${backend}`));
162
+ this.response.setHeader(constants_1.HTTP_HEADERS.contentType, 'text/event-stream');
163
+ this.streaming = true;
164
+ stream_1.default(this.request, this.response, backendConfig, Function.prototype);
165
+ this.setCreateResponseAction(() => Promise.resolve()); // to prevent the router from continuing on to the next route
166
+ };
167
+ /**
168
+ * Proxies the response from a given host
169
+ * @private
170
+ * @param backendConfig The backend config
171
+ * @param options
172
+ */
173
+ this.proxyHost = async (backendConfig, { path, transformRequest, transformResponse, headers, removeEmptySearchParamValues, } = {}) => {
174
+ backendConfig = normalizeBackendConfig(backendConfig);
175
+ const backend = Backend_1.default.create(backendConfig);
176
+ let pathString;
177
+ if (typeof path === 'function') {
178
+ pathString = path();
179
+ }
180
+ else {
181
+ pathString = path;
182
+ }
183
+ pathString = pathString ? bindParamsToPath_1.default(pathString, this.request.params) : pathString;
184
+ await this.setCreateResponseAction(async () => {
185
+ var _a, _b;
186
+ try {
187
+ if (transformRequest) {
188
+ const transformRequestTime = new timing_1.InternalTiming(timing_1.InternalNames.transformRequest).start();
189
+ try {
190
+ await this.runImmediately(() => transformRequest(this.request));
191
+ }
192
+ finally {
193
+ transformRequestTime.end();
194
+ }
195
+ }
196
+ const fetchTime = new timing_1.InternalTiming(timing_1.InternalNames.proxying).start();
197
+ try {
198
+ await backend.fetch(this.request, this.response, {
199
+ path: pathString || this.request.url,
200
+ disableCheckCert: backendConfig.disableCheckCert,
201
+ headers,
202
+ removeEmptySearchParamValues,
203
+ });
204
+ }
205
+ finally {
206
+ fetchTime.end();
207
+ }
208
+ if (transformResponse) {
209
+ const transformResponseTime = new timing_1.InternalTiming(timing_1.InternalNames.transformResponse).start();
210
+ try {
211
+ decode_1.default(this.response);
212
+ await this.runImmediately(() => transformResponse(this.response, this.request));
213
+ }
214
+ finally {
215
+ transformResponseTime.end();
216
+ }
217
+ // We need to recalculate the actual content-length size when the app is running in development mode
218
+ // as the transformResponse function may change the response size, adaptResponseEncodingToDownstream is not used in this mode
219
+ // and development express server doesn't support compression
220
+ if (!environment_1.isCloud()) {
221
+ this.internalSetResponseHeader(constants_1.HTTP_HEADERS.contentLength, Buffer.byteLength((_b = (_a = this.response.body) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '', 'utf8').toString(), false, this.postCacheResponseActions);
222
+ }
223
+ }
224
+ }
225
+ catch (e) {
226
+ if (e.type === 'BackendFetchError') {
227
+ // will get here when there is a connection error with the backend
228
+ console.error(`${logo_1.errorLogLabel} Could not connect to backend ${backendConfig.domainOrIp}:`, e.message);
229
+ this.response.statusCode = 503;
230
+ }
231
+ else {
232
+ console.error(`${logo_1.errorLogLabel} Error in ResponseWriter:`, e.message, e.stack);
233
+ this.response.statusCode = 500;
234
+ }
235
+ }
236
+ });
237
+ };
238
+ /**
239
+ * Rewrites the request path.
240
+ *
241
+ * Example:
242
+ *
243
+ * ```js
244
+ * router.get('/products/:id', ({ updatePath }) => {
245
+ * updatePath('/p/:id')
246
+ * })
247
+ * ```
248
+ *
249
+ * @param destination a new route path, which can include params captured from the original path
250
+ */
251
+ this.updatePath = (destination) => {
252
+ const path = bindParamsToPath_1.default(destination, this.request.params);
253
+ // IncomingMessage defines path as only having a getter, so we need to redefine it, we can't just set path = destination, which would result in an error in development.
254
+ Object.defineProperty(this.request, 'path', {
255
+ get: () => path,
256
+ });
257
+ };
258
+ /**
259
+ * Updates the request's parameters based on a new path and route pattern. This is, in effect,
260
+ * an implementation of Next.js rewrites, put here instead of NextRoutes in anticipation of other frameworks
261
+ * implementing the same spec as many of them "follow the leader".
262
+ *
263
+ * **example**:
264
+ *
265
+ * ```js
266
+ * res.rewrite('/category/boys/:slug*', '/category/:slug*')
267
+ * // result: request.params will be { slug: ['category', 'boys'] }
268
+ * ```
269
+ * @private
270
+ * @param destination A destination path, which may contain path variables.
271
+ * @param pattern The path pattern for the destination route we're emulating
272
+ */
273
+ this.rewrite = (destination, pattern) => {
274
+ const path = bindParamsToPath_1.default(destination, this.request.params);
275
+ const newUrl = new URL(this.request.url, 'http://host');
276
+ newUrl.pathname = path;
277
+ this.request.url = newUrl.pathname + newUrl.search;
278
+ this.request.path = path;
279
+ this.request.params = new PathMatcher_1.default(pattern).match(path);
280
+ };
281
+ /**
282
+ * Responds with a static asset from the specified path.
283
+ *
284
+ * **Example**
285
+ *
286
+ * ```js
287
+ * serveStatic('path/to/asset/from/app/root')
288
+ * ```
289
+ *
290
+ * You can also use variables in the asset path. For example, to return files under the `assets` directory
291
+ * when the url starts with `/static`:
292
+ *
293
+ * ```
294
+ * new Router()
295
+ * .get('/static/:path*', ({ serveStatic }) => {
296
+ * serveStatic('assets/:path*')
297
+ * })
298
+ * ```
299
+ *
300
+ * Notes:
301
+ * - if a folder contains an 'index.html', that page is also served when requesting the parent folder route.
302
+ * Let's take the example of /posts/1 that will render /posts/1/index.html)
303
+ * They are multiple components involved in this rewrite to make it work with S3 storage:
304
+ * 1. DeploymentBuilder creates and bundles an assets-aliases.json file, that indicates that
305
+ * '/posts/1' should point to '/posts/1/index.html' asset file.
306
+ * 2. layer0-subaccount-infra/components/deploy-lambda/src/deploy.js re-uploads '/posts/1/index.html'
307
+ * as '/posts/1' s3 key, which works because S3 is a key based storage and not a filesystem.
308
+ * It's a bit of a hack and takes extra space, but allows root document rendering.
309
+ * 3. serveStatic(), both in ResponseWriter ad EdgeResponseWriter, remove any trailing slash on path
310
+ * so that both '/posts/1/' and '/posts/1' match the '/posts/1' S3 file
311
+ *
312
+ * - We did not use S3 built-in Static Website feature for multiple reasons:
313
+ * 1. It is http only
314
+ * 2. /build-slug/public/posts/ renders /build-slug/public/posts/index.html correctly, but /build-slug/public/posts
315
+ * without a trailing slash redirects to /build-slug/public/posts/.
316
+ * While it's easy to rewrite the path and remove trailing slashes, we actually do, it's not easy
317
+ * to add missing trailing slash as we don't know beforehand if the route is a directory or a file.
318
+ * Having a redirect on every route is not acceptable either.
319
+ * The Location header would also need to be rewritten to be relative to the actual website and
320
+ * not the bucket prefix, but that's manageable.
321
+ *
322
+ * - ISG/ISR:
323
+ * When handling a request for a static asset that is not in S3, the request follows this path:
324
+ *
325
+ *```
326
+ * Browser => Edge => S3 (requested asset, which results in a 404)
327
+ * Edge => Buffer Proxy => S3 (options.loadingPage, if provided) => Response returned
328
+ * => Edge => Buffer Proxy => Lambda => BehindEdgeResponseWriter => options.onNotFound => SSR (then this response is cached for the next request)
329
+ *```
330
+ *
331
+ * Note that BehindEdgeResponseWriter is the key here. It overrides serveStatic and if options.onNotFound is provided, it will always be called.
332
+ *
333
+ * @param path The relative path to the asset from the app's root directory. You can reference path variables using `:variable`.
334
+ * @param options The different options to serving static assets including fallback to compute
335
+ * @returns A promise the resolves once the asset has been fetched from storage.
336
+ */
337
+ this.serveStatic = async (path, options = {}) => {
338
+ let filePath = path && bindParamsToPath_1.default(path, this.request.params);
339
+ const { permanent, exclude } = options;
340
+ // Remove trailing '/' so that both /path/ and /path are
341
+ // served by /path file.
342
+ filePath = filePath.replace(/\/$/, '');
343
+ if (exclude === null || exclude === void 0 ? void 0 : exclude.some(excludedFile => filePath.indexOf(excludedFile) >= 0)) {
344
+ return this.send('404 - Not found', 404);
345
+ }
346
+ if (environment_1.isCloud()) {
347
+ const staticBackend = config_1.default.get(`backends.${permanent ? constants_1.BACKENDS.permanentStatic : constants_1.BACKENDS.static}`);
348
+ // AWS
349
+ // Static assets are stored in s3 in production, so we just need to proxy
350
+ // s3 for all of the correct headers to be sent and the correct file to be served.
351
+ await this.proxyHost(staticBackend, {
352
+ path: slash_1.default(filePath),
353
+ transformResponse: async (res) => {
354
+ // In production on the edge files served from AmazonS3 without any defined cache have default cache-control header.
355
+ // We want to simulate this header on local too.
356
+ res.setHeader(constants_1.HTTP_HEADERS.cacheControl, `s-maxage=${exports.FAR_FUTURE_TTL}`);
357
+ // S3 returns 403 on actual 404. We do the same thing in our local static file server.
358
+ if (res.statusCode === 403) {
359
+ if (options.loadingPage) {
360
+ await this.proxyHost(config_1.default.get(`backends.${constants_1.BACKENDS.static}`), {
361
+ path: bindParamsToPath_1.default(options.loadingPage, this.request.params),
362
+ });
363
+ }
364
+ else if (options.onNotFound) {
365
+ await options.onNotFound(this);
366
+ }
367
+ else {
368
+ // We translate the S3 403 into a 404. The edge does the same thing.
369
+ res.statusCode = 404;
370
+ res.statusMessage = 'Not Found';
371
+ }
372
+ }
373
+ // Set the optional status code and message to the static response.
374
+ if (options.statusCode) {
375
+ res.statusCode = options.statusCode;
376
+ }
377
+ if (options.statusMessage) {
378
+ res.statusMessage = options.statusMessage;
379
+ }
380
+ },
381
+ });
382
+ }
383
+ else {
384
+ // local development
385
+ if (!fs_1.default.existsSync(filePath)) {
386
+ return this.send('404 - Not found', 404);
387
+ }
388
+ // Try serving root document "($path)/index.html" for paths matching
389
+ // a directory.
390
+ // We achieve the same result on S3 via assets-aliases.json.
391
+ if (fs_1.default.lstatSync(filePath).isDirectory()) {
392
+ filePath = path_1.join(filePath, './index.html');
393
+ }
394
+ const stat = fs_1.default.statSync(filePath);
395
+ // not needed in production, so we leave it out to keep the bundle small
396
+ const mime = nonWebpackRequire_1.default('mime-types');
397
+ const chardet = nonWebpackRequire_1.default('chardet');
398
+ let contentType = mime.lookup(filePath);
399
+ // connascence with xdn-subaccount-infra - s3Bucket.js
400
+ if (contentType) {
401
+ try {
402
+ const encodingAnalysisResult = chardet.analyse(fs_1.default.readFileSync(filePath));
403
+ if (encodingAnalysisResult.length > 0) {
404
+ // chardet analyse returns array of possible encodings starting with most confident result
405
+ const { name: encoding, confidence } = encodingAnalysisResult[0];
406
+ if (encoding && confidence > 90) {
407
+ contentType += `; charset=${encoding}`;
408
+ }
409
+ }
410
+ }
411
+ catch (e) {
412
+ // will get here if a charset fails to be detected
413
+ }
414
+ }
415
+ else {
416
+ contentType = 'application/octet-stream';
417
+ }
418
+ // Using setUpstreamResponseHeader gives developers the opportunity to override this,
419
+ // just like they would in production, where the content-type header comes from S3 based
420
+ // on the metadata that is associated with the file at the time of upload.
421
+ this.setUpstreamResponseHeader(constants_1.HTTP_HEADERS.contentType, contentType);
422
+ // We don't allow developers to manipulate content-length header through the router
423
+ // (it's prohibited on the edge as it could lead to broken responses)
424
+ // In this case we know we are not running in the cloud so our code is free to do so.
425
+ this.internalSetResponseHeader(constants_1.HTTP_HEADERS.contentLength, stat.size.toString(), false, this.postCacheResponseActions);
426
+ if (options.statusCode) {
427
+ this.response.statusCode = options.statusCode;
428
+ this.response.statusMessage = options.statusMessage;
429
+ }
430
+ await this.setCreateResponseAction(() => {
431
+ return new Promise((resolve, reject) => {
432
+ fs_1.default.readFile(filePath, (err, data) => {
433
+ /* istanbul ignore if */
434
+ if (err) {
435
+ reject(err);
436
+ }
437
+ else {
438
+ this.response.body = data;
439
+ resolve();
440
+ }
441
+ });
442
+ });
443
+ });
444
+ }
445
+ };
446
+ /**
447
+ * Sets the caching behavior for both browser and edge.
448
+ *
449
+ * **Example**
450
+ *
451
+ * ```
452
+ * new Router()
453
+ * .get('/p/:productId', ({ cache, proxy }) => {
454
+ * cache({
455
+ * browser: {
456
+ * maxAgeSeconds: 0,
457
+ * serviceWorkerSeconds: 60 * 60, // 1 hour
458
+ * },
459
+ * edge: {
460
+ * maxAgeSeconds: 60 * 60 * 24, // 24 hours
461
+ * staleWhileRevalidateSeconds: 60 * 60 // 1 hour
462
+ * }
463
+ * })
464
+ * proxy('origin')
465
+ * })
466
+ * ```
467
+ *
468
+ * The `cache()` method can be called in the same route where the response is sent, or any prior route. For example,
469
+ * with Next.js, it is common to use the next plugin to automatically inherit page routes based on Next.js conventions,
470
+ * and use Layer0 router simply to add caching:
471
+ *
472
+ * ```
473
+ * import { Router } = from '@layer0/core/router'
474
+ * import { createNextPlugin } from '@layer0/next'
475
+ *
476
+ * const { nextMiddleware, renderNext } = createNextPlugin()
477
+ *
478
+ * new Router()
479
+ * .get('/p/:productId', ({ cache, proxy }) => {
480
+ * cache({
481
+ * browser: {
482
+ * maxAgeSeconds: 0,
483
+ * serviceWorkerSeconds: 60 * 60, // 1 hour
484
+ * },
485
+ * edge: {
486
+ * maxAgeSeconds: 60 * 60 * 24, // 24 hours
487
+ * staleWhileRevalidateSeconds: 60 * 60 // 1 hour
488
+ * }
489
+ * })
490
+ * proxy('origin')
491
+ * })
492
+ * .use(nextMiddleware)
493
+ * ```
494
+ */
495
+ this.cache = (options) => {
496
+ const { browser, edge, key } = options;
497
+ if (edge) {
498
+ const { maxAgeSeconds, staleWhileRevalidateSeconds, forcePrivateCaching } = edge;
499
+ const value = [];
500
+ // Flip force-private-caching only if it is explicitly set.
501
+ if (typeof forcePrivateCaching !== 'undefined') {
502
+ this.context.forcePrivateCaching = forcePrivateCaching === true;
503
+ }
504
+ this.cacheKey = key;
505
+ if (typeof this.route.criteria.method === 'string') {
506
+ this.context.cacheRouteMethod = this.route.criteria.method;
507
+ }
508
+ if (maxAgeSeconds) {
509
+ value.push('max-age=' + maxAgeSeconds);
510
+ if (staleWhileRevalidateSeconds) {
511
+ value.push('stale-while-revalidate=' + staleWhileRevalidateSeconds);
512
+ }
513
+ }
514
+ if (value.length) {
515
+ this.internalSetResponseHeader(constants_1.HTTP_HEADERS.x0CacheControl, value.join(', '), false, this.preCacheResponseActions);
516
+ }
517
+ }
518
+ if (browser || browser === false) {
519
+ const { maxAgeSeconds, serviceWorkerSeconds } = browser || { maxAgeSeconds: 0 };
520
+ if (serviceWorkerSeconds && maxAgeSeconds && maxAgeSeconds > 0) {
521
+ console.error(`${logo_1.errorLogLabel} Within the caching configuration of your "${this.route.criteria.path}" route, when service worker caching is set, the browser max age must be set to zero. Refer to https://docs.layer0.co/guides/caching#section_caching_a_response`);
522
+ throw new Error('When service worker caching is set, the browser max age must be set to zero. Refer to https://docs.layer0.co/guides/caching#section_caching_a_response');
523
+ }
524
+ if (maxAgeSeconds === 0) {
525
+ this.setResponseHeader(constants_1.HTTP_HEADERS.cacheControl, 'private, no-store, no-cache');
526
+ }
527
+ else if (maxAgeSeconds) {
528
+ // Here we use "private" instead of "public" to prevent downstream CDNs from caching the asset
529
+ // Since we already have an explicit way to cache at edge, we choose to keep browser and edge
530
+ // caching totally separate.
531
+ this.setResponseHeader(constants_1.HTTP_HEADERS.cacheControl, `private, max-age=${maxAgeSeconds}`);
532
+ }
533
+ if (serviceWorkerSeconds) {
534
+ this.setResponseHeader(constants_1.HTTP_HEADERS.xSwCacheControl, `max-age=${serviceWorkerSeconds}`);
535
+ }
536
+ }
537
+ };
538
+ /**
539
+ * Adds or replaces a response header immediately before delivering the response downstream.
540
+ *
541
+ * **Example**
542
+ *
543
+ * ```
544
+ * new Router()
545
+ * .get('/some/path', ({ setResponseHeader, proxy }) => {
546
+ * proxy('origin')
547
+ * setResponseHeader('some-header', 'some-value')
548
+ * })
549
+ * ```
550
+ *
551
+ * @param name The case-insensitive name of the response header
552
+ * @param value The value to set
553
+ */
554
+ this.setResponseHeader = (name, value) => {
555
+ this.internalSetResponseHeader(name, value, true, this.postCacheResponseActions);
556
+ };
557
+ this.internalSetResponseHeader = (name, value, validate, phase) => {
558
+ if (validate) {
559
+ validateWriter_1.validateSetResponseHeader(name);
560
+ }
561
+ this.scheduleExecution(phase, () => {
562
+ const interpolatedValue = this.interpolateParams(value);
563
+ this.response.setHeader(name, interpolatedValue);
564
+ });
565
+ };
566
+ /**
567
+ * Alters a response header immediately before delivering the response downstream.
568
+ * Use this method to derive the new header value from the existing one.
569
+ *
570
+ * **Example**
571
+ *
572
+ * ```
573
+ * new Router()
574
+ * .get('/some/path', ({ updateResponseHeader, proxy }) => {
575
+ * proxy('origin')
576
+ * updateResponseHeader('some-header', /some-.*-part/gi, 'some-replacement')
577
+ * })
578
+ * ```
579
+ *
580
+ * @param name The case-insensitive name of the response header
581
+ * @param match Regex to find the part that should be replaced.
582
+ * @param replace Value that will replace the matched part.
583
+ */
584
+ this.updateResponseHeader = (name, match, replace) => {
585
+ this.internalUpdateResponseHeader(name, match, replace, this.preCacheResponseActions);
586
+ };
587
+ this.internalUpdateResponseHeader = (name, match, replace, phase) => {
588
+ validateWriter_1.validateUpdateResponseHeader(name);
589
+ this.scheduleExecution(phase, () => {
590
+ const currentValue = this.response.getHeader(name);
591
+ const interpolatedReplace = this.interpolateParams(replace);
592
+ if (currentValue) {
593
+ this.response.setHeader(name, currentValue.replace(match, interpolatedReplace));
594
+ }
595
+ });
596
+ };
597
+ /**
598
+ * Removes a response header immediately before delivering the response downstream.
599
+ *
600
+ * **Example**
601
+ *
602
+ * ```
603
+ * new Router()
604
+ * .get('/some/path', ({ removeResponseHeader, proxy }) => {
605
+ * proxy('origin')
606
+ * removeResponseHeader('some-header')
607
+ * })
608
+ * ```
609
+ * @param name The case-insensitive name of the response header
610
+ */
611
+ this.removeResponseHeader = (name) => {
612
+ this.internalRemoveResponseHeader(name, this.postCacheResponseActions);
613
+ };
614
+ this.internalRemoveResponseHeader = (name, phase) => {
615
+ validateWriter_1.validateRemoveResponseHeader(name);
616
+ this.scheduleExecution(phase, () => {
617
+ this.response.removeHeader(name);
618
+ });
619
+ };
620
+ /**
621
+ * Returns an array of values associated with all `set-cookie` headers.
622
+ */
623
+ this.getSetCookieValues = () => {
624
+ const headers = this.response.getHeader(constants_1.HTTP_HEADERS.setCookie);
625
+ if (typeof headers === 'string') {
626
+ return [headers];
627
+ }
628
+ return headers || [];
629
+ };
630
+ /**
631
+ * Returns action function for the "add response cookie" operation.
632
+ */
633
+ this.getAddResponseCookieAction = (name, value, options = {}) => {
634
+ return () => {
635
+ const setCookies = this.getSetCookieValues();
636
+ const interpolatedValue = this.interpolateParams(value);
637
+ let cookie = `${name}=${interpolatedValue}`;
638
+ let key;
639
+ for (key in options) {
640
+ const opt = options[key];
641
+ if (typeof opt === 'boolean' && opt) {
642
+ cookie += `; ${key}`;
643
+ }
644
+ else {
645
+ cookie += `; ${key}=${opt}`;
646
+ }
647
+ }
648
+ this.response.setHeader(constants_1.HTTP_HEADERS.setCookie, [...setCookies, cookie]);
649
+ };
650
+ };
651
+ /**
652
+ * Adds a `set-cookie` header to the response before attempting to cache the response.
653
+ * This does not replace any cookies with the same name - for that you should use `updateUpstreamResponseCookie`.
654
+ * Furthermore, Layer0 does not cache responses with `set-cookie` so if you wish to add a cookie
655
+ * *after* the response is cached, you should use `addResponseCookie`.
656
+ *
657
+ * **Example**
658
+ *
659
+ * ```
660
+ * new Router()
661
+ * .get('/some/path', ({ addUpstreamResponseCookie, proxy }) => {
662
+ * proxy('origin')
663
+ * addUpstreamResponseCookie('my-cookie', 'my-cookie-value', { domain: 'test.com' })
664
+ * })
665
+ * ```
666
+ *
667
+ * @param name Name of the cookie to add.
668
+ * @param value Value to set
669
+ * @param options Optional options to add to cookie
670
+ */
671
+ this.addUpstreamResponseCookie = (name, value, options) => {
672
+ validateWriter_1.validateCookieName(name);
673
+ this.scheduleExecution(this.preCacheResponseActions, this.getAddResponseCookieAction(name, value, options));
674
+ };
675
+ /**
676
+ * Adds a `set-cookie` header to the response.
677
+ * This does not replace any cookies with the same name - for that you should use `updateResponseCookie`.
678
+ *
679
+ * **Example**
680
+ *
681
+ * ```
682
+ * new Router()
683
+ * .get('/some/path', ({ addResponseCookie, proxy }) => {
684
+ * proxy('origin')
685
+ * addResponseCookie('my-cookie', 'my-cookie-value', { domain: 'test.com' })
686
+ * })
687
+ * ```
688
+ *
689
+ * @param name Name of the cookie to add.
690
+ * @param value Value to set
691
+ * @param options Optional options to add to cookie
692
+ */
693
+ this.addResponseCookie = (name, value, options) => {
694
+ validateWriter_1.validateCookieName(name);
695
+ this.scheduleExecution(this.postCacheResponseActions, this.getAddResponseCookieAction(name, value, options));
696
+ };
697
+ /**
698
+ * Returns action function for the "update response cookie" operation.
699
+ *
700
+ * Notes:
701
+ * - Cookie names are case sensitive so we use case sensitive comparison.
702
+ */
703
+ this.getUpdateResponseCookieAction = (name, match, replace) => {
704
+ return () => {
705
+ const setCookies = this.getSetCookieValues().map((cookie) => {
706
+ let [cookieName, ...cookieValues] = cookie.split('=');
707
+ if (cookieName !== name) {
708
+ return cookie;
709
+ }
710
+ const cookieValue = cookieValues.join('=').replace(match, replace);
711
+ return [cookieName, cookieValue].join('=');
712
+ });
713
+ this.response.setHeader(constants_1.HTTP_HEADERS.setCookie, setCookies);
714
+ };
715
+ };
716
+ /**
717
+ * Updates a `set-cookie` header in the response before attempting to cache the response.
718
+ * If there are multiple cookies with the same name, all of them will be updated.
719
+ *
720
+ * **Example**
721
+ *
722
+ * ```
723
+ * new Router()
724
+ * .get('/some/path', ({ updateUpstreamResponseCookie, proxy }) => {
725
+ * proxy('origin')
726
+ * updateUpstreamResponseCookie('my-cookie', /Domain=.+;/, 'Domain=mydomain.com;')
727
+ * })
728
+ * ```
729
+ *
730
+ * @param name Case-insensitive name of the cookie to update
731
+ * @param match Regex to find the part that should be replaced.
732
+ * @param replace Value that will replace the matched part.
733
+ */
734
+ this.updateUpstreamResponseCookie = (name, match, replace) => {
735
+ validateWriter_1.validateCookieName(name);
736
+ this.scheduleExecution(this.preCacheResponseActions, this.getUpdateResponseCookieAction(name, match, replace));
737
+ };
738
+ /**
739
+ * Updates a `set-cookie` header in the response. If there are multiple cookies
740
+ * with the same name, all of them will be updated.
741
+ *
742
+ * **Example**
743
+ *
744
+ * ```
745
+ * new Router()
746
+ * .get('/some/path', ({ updateResponseCookie, proxy }) => {
747
+ * proxy('origin')
748
+ * updateResponseCookie('my-cookie', /Domain=.+;/, 'Domain=mydomain.com;')
749
+ * })
750
+ * ```
751
+ *
752
+ * @param name Case-insensitive name of the cookie to update
753
+ * @param match Regex to find the part that should be replaced.
754
+ * @param replace Value that will replace the matched part.
755
+ */
756
+ this.updateResponseCookie = (name, match, replace) => {
757
+ validateWriter_1.validateCookieName(name);
758
+ this.scheduleExecution(this.postCacheResponseActions, this.getUpdateResponseCookieAction(name, match, replace));
759
+ };
760
+ /**
761
+ * Returns action function for the "remove response cookie" operation.
762
+ *
763
+ * Notes:
764
+ * - Cookie names are case sensitive so we use case sensitive comparison.
765
+ */
766
+ this.getRemoveResponseCookieAction = (name) => {
767
+ return () => {
768
+ const setCookies = this.getSetCookieValues().filter((cookie) => cookie.split('=')[0] !== name);
769
+ this.response.setHeader(constants_1.HTTP_HEADERS.setCookie, setCookies);
770
+ };
771
+ };
772
+ /**
773
+ * Removes a specific `set-cookie` header from the response before attempting to cache the response.
774
+ * If you wish to remove all such headers use `removeUpstreamHeader`.
775
+ *
776
+ * **Example**
777
+ *
778
+ * ```
779
+ * new Router()
780
+ * .get('/some/path', ({ removeUpstreamResponseCookie, proxy }) => {
781
+ * proxy('origin')
782
+ * removeUpstreamResponseCookie('my-cookie')
783
+ * })
784
+ * ```
785
+ * @param name The case-insensitive name of the cookie
786
+ */
787
+ this.removeUpstreamResponseCookie = (name) => {
788
+ validateWriter_1.validateCookieName(name);
789
+ this.scheduleExecution(this.preCacheResponseActions, this.getRemoveResponseCookieAction(name));
790
+ };
791
+ /**
792
+ * Removes a specific `set-cookie` header from the response.
793
+ * If you wish to remove all such headers use `removeHeader`.
794
+ *
795
+ * **Example**
796
+ *
797
+ * ```
798
+ * new Router()
799
+ * .get('/some/path', ({ removeResponseCookie, proxy }) => {
800
+ * proxy('origin')
801
+ * removeResponseCookie('my-cookie')
802
+ * })
803
+ * ```
804
+ * @param name The case-insensitive name of the cookie
805
+ */
806
+ this.removeResponseCookie = (name) => {
807
+ validateWriter_1.validateCookieName(name);
808
+ this.scheduleExecution(this.postCacheResponseActions, this.getRemoveResponseCookieAction(name));
809
+ };
810
+ /**
811
+ * Adds or replaces an upstream response header before attempting to cache the response.
812
+ * In local development, this is the same as calling `setResponseHeader` but on edge, it adds
813
+ * a header immediately after the upstream fetch has finished.
814
+ *
815
+ * **Example**
816
+ *
817
+ * ```
818
+ * new Router()
819
+ * .get('/some/path', ({ setUpstreamResponseHeader, proxy }) => {
820
+ * proxy('origin')
821
+ * setUpstreamResponseHeader('x-0-surrogate-key', 'proxy=origin')
822
+ * })
823
+ * ```
824
+ *
825
+ * @param name The case-insensitive name of the response header
826
+ * @param value The value to set
827
+ */
828
+ this.setUpstreamResponseHeader = (name, value) => {
829
+ const validate = name.toLowerCase() != constants_1.HTTP_HEADERS.x0SurrogateKey;
830
+ return this.internalSetResponseHeader(name, value, validate, this.preCacheResponseActions);
831
+ };
832
+ /**
833
+ * Alters an upstream response header before attempting to cache the response.
834
+ * Use this method to derive the new header value from the existing one.
835
+ * In local development this is same as calling `updateResponseHeader` but on
836
+ * edge it updates a header immediately after the upstream fetch has finished.
837
+ *
838
+ * **Example**
839
+ *
840
+ * ```
841
+ * new Router()
842
+ * .get('/some/path', ({ updateUpstreamResponseHeader, proxy }) => {
843
+ * proxy('origin')
844
+ * updateUpstreamResponseHeader('cache-control', /public/gi, 'private')
845
+ * })
846
+ * ```
847
+ *
848
+ * @param name The case-insensitive name of the response header
849
+ * @param match Regex to find the part that should be replaced.
850
+ * @param replace Value that will replace the matched part.
851
+ */
852
+ this.updateUpstreamResponseHeader = (name, match, replace) => {
853
+ return this.internalUpdateResponseHeader(name, match, replace, this.preCacheResponseActions);
854
+ };
855
+ /**
856
+ * Removes a response header before attempting to cache the response.
857
+ * In local development this is same as calling `updateUpstreamResponseHeader` but
858
+ * on edge it removes a header immediately after the upstream fetch has finished.
859
+ *
860
+ * **Example**
861
+ *
862
+ * ```
863
+ * new Router()
864
+ * .get('/some/path', ({ removeUpstreamResponseHeader, proxy }) => {
865
+ * proxy('origin')
866
+ * removeUpstreamResponseHeader(HTTP_HEADERS.setCookie)
867
+ * })
868
+ * ```
869
+ * @param name The case-insensitive name of the response header
870
+ */
871
+ this.removeUpstreamResponseHeader = (name) => {
872
+ return this.internalRemoveResponseHeader(name, this.preCacheResponseActions);
873
+ };
874
+ /**
875
+ * Adds or replaces a request header.
876
+ *
877
+ * **Example**
878
+ *
879
+ * ```
880
+ * new Router()
881
+ * .get('/some/path', ({ setRequestHeader, proxy }) => {
882
+ * setRequestHeader('some-header', 'some-value')
883
+ * proxy('origin')
884
+ * })
885
+ * ```
886
+ *
887
+ * @param name The case-insensitive name of the request header
888
+ * @param value The value to set
889
+ */
890
+ this.setRequestHeader = (name, value) => {
891
+ validateWriter_1.validateSetRequestHeader(name);
892
+ this.scheduleExecution(this.postCacheRequestActions, () => {
893
+ this.request.headers[name.toLowerCase()] = value;
894
+ });
895
+ };
896
+ /**
897
+ * Keeps count of the number of times we have started to execute any
898
+ * workload immediately. When the counter is at zero, we are not in
899
+ * the immediate execution mode.
900
+ */
901
+ this.runImmediatelyDepthCounter = 0;
902
+ /**
903
+ * Runs the given callback immediately, switching the response writer object
904
+ * into immediate execution mode.
905
+ *
906
+ * @param callback
907
+ */
908
+ this.runImmediately = async (callback) => {
909
+ try {
910
+ ++this.runImmediatelyDepthCounter;
911
+ await callback();
912
+ }
913
+ finally {
914
+ --this.runImmediatelyDepthCounter;
915
+ }
916
+ };
917
+ /**
918
+ * Either schedules the action for the later execution or, when running immediately,
919
+ * executes it right away.
920
+ *
921
+ * @param phase Array of Functions belonging to the specific execution phase (pre-cache request, post-cache request, etc)
922
+ * @param action Action to be executed either immediately or put into the execution array
923
+ */
924
+ this.scheduleExecution = (phase, action) => {
925
+ if (this.runningImmediately) {
926
+ action();
927
+ }
928
+ else {
929
+ phase.push(action);
930
+ }
931
+ };
932
+ /**
933
+ * Alters a request header. Use this method to derive the new header value from the existing one.
934
+ *
935
+ * **Example**
936
+ *
937
+ * ```
938
+ * new Router()
939
+ * .get('/some/path', ({ updateRequestHeader, proxy }) => {
940
+ * updateRequestHeader('some-header', /some-.*-part/gi, 'some-replacement')
941
+ * proxy('origin')
942
+ * })
943
+ * ```
944
+ *
945
+ * @param name The case-insensitive name of the request header
946
+ * @param match Regex to find the part that should be replaced.
947
+ * @param replace Value that will replace the matched part.
948
+ */
949
+ this.updateRequestHeader = (name, match, replace) => {
950
+ validateWriter_1.validateUpdateRequestHeader(name);
951
+ this.scheduleExecution(this.postCacheRequestActions, () => {
952
+ const key = name.toLowerCase();
953
+ const currentValue = this.request.headers[key];
954
+ const interpolatedReplace = this.interpolateParams(replace);
955
+ if (Array.isArray(currentValue)) {
956
+ this.request.headers[key] = currentValue.map(v => v.replace(match, interpolatedReplace));
957
+ }
958
+ else if (currentValue) {
959
+ this.request.headers[key] = currentValue.replace(match, interpolatedReplace);
960
+ }
961
+ });
962
+ };
963
+ /**
964
+ * Removes a request header.
965
+ *
966
+ * **Example**
967
+ *
968
+ * ```
969
+ * new Router()
970
+ * .get('/some/path', async ({ removeRequestHeader, proxy }) => {
971
+ * removeRequestHeader('some-header')
972
+ * proxy('origin')
973
+ * })
974
+ * ```
975
+ * @param name The case-insensitive name of the request header
976
+ */
977
+ this.removeRequestHeader = (name) => {
978
+ validateWriter_1.validateRemoveRequestHeader(name);
979
+ this.scheduleExecution(this.postCacheRequestActions, () => {
980
+ delete this.request.headers[name.toLowerCase()];
981
+ });
982
+ };
983
+ /**
984
+ * Returns the service worker with proper edge and browser cache headers
985
+ * @param path The path to the service worker relative to the root directory of your app
986
+ *
987
+ * **Example**
988
+ *
989
+ * ```js
990
+ * new Router()
991
+ * .get('/service-worker.js', ({ serviceWorker }) => {
992
+ * serviceWorker('dist/service-worker.js')
993
+ * })
994
+ * ```
995
+ */
996
+ this.serviceWorker = (path) => {
997
+ this.cache({
998
+ edge: {
999
+ maxAgeSeconds: 30758400,
1000
+ },
1001
+ browser: {
1002
+ // It is critical that the service worker is never cached in the browser or it may never update.
1003
+ // This can be catastrophic if a broken service worker is installed since it can prevent the site
1004
+ // from being accessed at all
1005
+ maxAgeSeconds: 0,
1006
+ },
1007
+ });
1008
+ this.serveStatic(path);
1009
+ };
1010
+ /**
1011
+ * Serves an HTML app shell from a static file.
1012
+ * @param indexHtmlPath The path to the app shell html file
1013
+ *
1014
+ * **Example**
1015
+ *
1016
+ * ```js
1017
+ * new Router()
1018
+ * .fallback(({ appShell }) => {
1019
+ * appShell('dist/index.html')
1020
+ * })
1021
+ * ```
1022
+ */
1023
+ this.appShell = (indexHtmlPath) => {
1024
+ this.setResponseHeader(constants_1.HTTP_HEADERS.contentType, 'text/html');
1025
+ this.serveStatic(indexHtmlPath);
1026
+ };
1027
+ /**
1028
+ * Sends the necessary response headers to allow CORS
1029
+ * @param config
1030
+ *
1031
+ * **Example**
1032
+ *
1033
+ * ```js
1034
+ * new Router()
1035
+ * .match('/api/:path*', ({ allowCors }) => {
1036
+ * allowCors({
1037
+ * origin: '*', // this is the default
1038
+ * methods: ['get', 'post'],
1039
+ * headers: ['x-some-header'],
1040
+ * maxAge: 60 * 60, // one hour
1041
+ * credentials: true
1042
+ * })
1043
+ * })
1044
+ * ```
1045
+ */
1046
+ this.allowCors = (config = {}) => {
1047
+ this.setResponseHeader('Access-Control-Allow-Origin', config.origin || '*');
1048
+ if (config.methods) {
1049
+ this.setResponseHeader('Access-Control-Allow-Methods', config.methods.join(', '));
1050
+ }
1051
+ if (config.headers) {
1052
+ this.setResponseHeader('Access-Control-Allow-Headers', config.headers.join(', '));
1053
+ }
1054
+ if (config.maxAge) {
1055
+ this.setResponseHeader('Access-Control-Max-Age', config.maxAge.toString());
1056
+ }
1057
+ if (config.credentials != null) {
1058
+ this.setResponseHeader('Access-Control-Allow-Credentials', config.credentials.toString());
1059
+ }
1060
+ };
1061
+ /**
1062
+ * Sends content back to client. If content is a string, the respons will be sent
1063
+ * directly from the edge. If it is a function, the request will be computed by a JavaScript worker.
1064
+ *
1065
+ * **Example**
1066
+ *
1067
+ * ```
1068
+ * new Router()
1069
+ * .get('/some/path', ({ send }) => {
1070
+ * return send('<html><body>Hello World!</body></html>', 200, 'OK')
1071
+ * })
1072
+ * ```
1073
+ * @param content The response body as a string
1074
+ * @param statusCode The status to send
1075
+ * @param statusMessage The status message to send
1076
+ */
1077
+ this.send = (content, statusCode = 200, statusMessage) => {
1078
+ this.setCreateResponseAction(async () => {
1079
+ this.response.statusCode = statusCode;
1080
+ this.response.statusMessage = statusMessage;
1081
+ if (typeof content === 'function') {
1082
+ this.response.body = content();
1083
+ }
1084
+ else {
1085
+ this.response.body = content;
1086
+ }
1087
+ });
1088
+ };
1089
+ /**
1090
+ * Execute the provided callback function in the cloud. The callback is passed the request and the response.
1091
+ *
1092
+ * Use this method when you need to compute a response in the cloud rather than at the edge or at build time. A common example
1093
+ * is when the response must be computed based on request parameters, headers, or cookies.
1094
+ *
1095
+ * A common example is to look up the destination for a redirect from an external API:
1096
+ *
1097
+ * ```js
1098
+ * new Router()
1099
+ * .get('/products/:id', ({ redirect, compute }) => {
1100
+ * compute(async (request, response) => {
1101
+ * const destination = await getDestinationFromAPI(request.params.id)
1102
+ * redirect(destination)
1103
+ * })
1104
+ * })
1105
+ * ```
1106
+ *
1107
+ * This method can be combined with `cache` to compute responses in the cloud and cache them at edge:
1108
+ *
1109
+ * ```js
1110
+ * new Router()
1111
+ * .get('/products/:id', ({ cache, redirect, compute }) => {
1112
+ * cache({
1113
+ * edge: {
1114
+ * maxAgeSeconds: 60 * 60 * 24,
1115
+ * staleWhileRevalidateSeconds: 60 * 60,
1116
+ * }
1117
+ * })
1118
+ * compute(async (request, response) => {
1119
+ * const destination = await getDestinationFromAPI(request.params.id)
1120
+ * redirect(destination)
1121
+ * })
1122
+ * })
1123
+ * ```
1124
+ *
1125
+ * Note: when calling `compute` in combination with `serveStatic`, the result of the `serveStatic` must be
1126
+ * awaited in order to get the result from `serveStatic`, otherwise only reslut of the `compute` method
1127
+ * will be returned.
1128
+ *
1129
+ * ```js
1130
+ * new Router()
1131
+ * .get('/products/:id', ({ redirect, compute }) => {
1132
+ * compute(async (request, response) => {
1133
+ * const destination = await getDestinationFromAPI(request.params.id)
1134
+ * await serveStatic(destination)
1135
+ * })
1136
+ * })
1137
+ * ```
1138
+ *
1139
+ * @param callback A function to run in the cloud to compute the response
1140
+ */
1141
+ this.compute = (callback) => {
1142
+ this.setCreateResponseAction(async () => {
1143
+ // We need to clear createResponseAction because the compute callback will likely send it's own response
1144
+ // If we don't clear createResponseAction, setCreateResponseAction will throw a validation error which prevents multiple responses from being sent
1145
+ await this.clearResponseAction().runImmediately(() => callback(this.request, this.response));
1146
+ });
1147
+ };
1148
+ /**
1149
+ * Send json error response to client
1150
+ * @private
1151
+ * @param err
1152
+ */
1153
+ this.onRouteError = (err) => {
1154
+ let errorResponse = {
1155
+ status: 'error',
1156
+ message: err.message,
1157
+ };
1158
+ // Some errors are marked as user errors. We log those for higher visibility.
1159
+ const isUserError = err.isUserError;
1160
+ if (isUserError) {
1161
+ console.error(`${logo_1.errorLogLabel} ${err.message}`);
1162
+ }
1163
+ if (process.env.NODE_ENV !== 'production') {
1164
+ errorResponse = {
1165
+ message: `An error occured while running route ${this.route.toString()}: ${err.message}`,
1166
+ // We don't send the stack for user errors as they are raised from our code
1167
+ // but indicate issues in user's code.
1168
+ stack: !isUserError ? err.stack : undefined,
1169
+ url: this.request.url,
1170
+ };
1171
+ }
1172
+ try {
1173
+ this.response.setHeader(constants_1.HTTP_HEADERS.contentType, 'application/json');
1174
+ this.response.writeHead(500);
1175
+ this.response.end(JSON.stringify(errorResponse));
1176
+ }
1177
+ catch (e) {
1178
+ console.error(`${logo_1.errorLogLabel} Could not write error response because headers have already been sent.`, errorResponse);
1179
+ }
1180
+ };
1181
+ this.request = req;
1182
+ this.response = res;
1183
+ this.route = route;
1184
+ this.context = context;
1185
+ }
1186
+ /**
1187
+ * Called at the end of the request to send the response back to the browser. Methods like proxy,
1188
+ * serveStatic, and render will replace this method with one that actually sends a response.
1189
+ * @private
1190
+ * @returns True if there send response was defined and invoked, otherwise false.
1191
+ */
1192
+ async sendResponse({ beforeResponse = () => true } = {}) {
1193
+ let sendResponse = false;
1194
+ for (let action of this.preCacheRequestActions) {
1195
+ action();
1196
+ }
1197
+ let cachedResponse, cacheKey;
1198
+ if (Cache_1.isCacheEnabled()) {
1199
+ cacheKey = this.context.cacheKey || CacheKey_1.default.create(this.request, this.cacheKey);
1200
+ cachedResponse = Cache_1.getCachedResponse(cacheKey);
1201
+ if (this.cacheKey) {
1202
+ // we put the custom cache key on the context so that it can be used by a later route which generates the response. This is common, for example, with Next and Nuxt apps, which generate response via middleware.
1203
+ this.context.cacheKey = cacheKey;
1204
+ }
1205
+ if (!this.validatePrefetch(cachedResponse)) {
1206
+ console.log(`${logo_1.infoLogLabel} throttle ${this.request.url}`);
1207
+ return true;
1208
+ }
1209
+ }
1210
+ if (cachedResponse) {
1211
+ sendResponse = true;
1212
+ new timing_1.InternalTiming(timing_1.InternalNames.edgeCache).set(timing_1.CacheValue.hit);
1213
+ this.applyCachedResponse(cachedResponse);
1214
+ }
1215
+ else {
1216
+ for (let action of this.postCacheRequestActions) {
1217
+ action();
1218
+ }
1219
+ if (this.createResponseAction) {
1220
+ await this.createResponseAction();
1221
+ sendResponse = true;
1222
+ }
1223
+ for (let action of this.preCacheResponseActions) {
1224
+ action();
1225
+ }
1226
+ // This will evaluate the error routes based on the received status code.
1227
+ // When this finds a match it will return that response was sent. That means
1228
+ // That before response did take care of the response and issued a paged matching the error route.
1229
+ const shouldContinue = await beforeResponse(this.response);
1230
+ if (!shouldContinue) {
1231
+ return sendResponse;
1232
+ }
1233
+ }
1234
+ if (sendResponse && !this.streaming && !cachedResponse && Cache_1.isCacheEnabled()) {
1235
+ const cached = Cache_1.maybeCacheResponse({
1236
+ response: this.response,
1237
+ // @ts-ignore - cacheKey can't actually be undefined here because it will be set if isCacheEnabled() is true
1238
+ cacheKey,
1239
+ context: this.context,
1240
+ });
1241
+ new timing_1.InternalTiming(timing_1.InternalNames.edgeCache).set(cached ? timing_1.CacheValue.cached : timing_1.CacheValue.pass);
1242
+ }
1243
+ for (let action of this.postCacheResponseActions) {
1244
+ action();
1245
+ }
1246
+ if (sendResponse && !this.streaming) {
1247
+ this.logCacheStatus();
1248
+ timing_1.addTimings(this.response);
1249
+ if (this.response.statusCode) {
1250
+ this.response.writeHead(this.response.statusCode, this.response.statusMessage || undefined);
1251
+ }
1252
+ this.response.end(this.response.body);
1253
+ }
1254
+ return sendResponse;
1255
+ }
1256
+ /**
1257
+ * Logs the cache status when running locally with caching is enabled
1258
+ */
1259
+ logCacheStatus() {
1260
+ if (Cache_1.isCacheEnabled()) {
1261
+ // for now we only add x-0-t when running with --cache because the only measurement we include is ecc=cached|pass|hit
1262
+ const status = this.response.getHeader(constants_1.HTTP_HEADERS.x0CachingStatus);
1263
+ const hitOrMiss = timing_1.getInternalTiming(timing_1.InternalNames.edgeCache);
1264
+ const { url } = this.request;
1265
+ console.log(`${logo_1.infoLogLabel} ${hitOrMiss} ${url}${status ? ` (${status})` : ''}`);
1266
+ }
1267
+ }
1268
+ /**
1269
+ * Copies the body, headers, and status from the cached response
1270
+ * to the outgoing response.
1271
+ * @param cachedResponse The cached response
1272
+ */
1273
+ applyCachedResponse(cachedResponse) {
1274
+ this.response.statusCode = cachedResponse.statusCode;
1275
+ this.response.statusMessage = cachedResponse.statusMessage;
1276
+ this.response.body = lodash_clonedeep_1.default(cachedResponse.body);
1277
+ for (let name of Object.keys(cachedResponse.headers)) {
1278
+ // We have to cloneDeep because some headers are arrays rather than just strings.
1279
+ this.response.setHeader(name, lodash_clonedeep_1.default(cachedResponse.headers[name]));
1280
+ }
1281
+ }
1282
+ /**
1283
+ * Rejects the request with status 412 and returns false if ?layer0_prefetch is present and
1284
+ * no cached response was found.
1285
+ */
1286
+ validatePrefetch(cachedResponse) {
1287
+ const isThrottled = Object.keys(this.request.query || {}).includes(constants_1.THROTTLED_QUERY_PARAM);
1288
+ if (isThrottled && !cachedResponse) {
1289
+ this.response.writeHead(constants_1.LAYER0_UNCACHED_PREFETCH_STATUS);
1290
+ this.response.end();
1291
+ return false;
1292
+ }
1293
+ else {
1294
+ return true;
1295
+ }
1296
+ }
1297
+ /**
1298
+ * @param action
1299
+ */
1300
+ async setCreateResponseAction(action) {
1301
+ if (this.runningImmediately) {
1302
+ await action();
1303
+ }
1304
+ else {
1305
+ if (this.createResponseAction != null) {
1306
+ throw new Error('You may only call one of the following once in your route handler: proxy, serveStatic, serviceWorker, redirect, send.');
1307
+ }
1308
+ this.createResponseAction = action;
1309
+ }
1310
+ }
1311
+ /**
1312
+ * Returns true if the object is in the running immediately mode.
1313
+ * When object is in the running immediately mode, all otherwise
1314
+ * scheduled actions like header transform operations, are immediately
1315
+ * executed.
1316
+ * @private
1317
+ */
1318
+ get runningImmediately() {
1319
+ return this.runImmediatelyDepthCounter > 0;
1320
+ }
1321
+ /**
1322
+ * Clears response action. This is required for compute and error pages. Error pages must alter the initial response and execute the error routes separately.
1323
+ *
1324
+ * @private
1325
+ * @returns
1326
+ */
1327
+ clearResponseAction() {
1328
+ this.createResponseAction = undefined;
1329
+ return this;
1330
+ }
1331
+ }
1332
+ exports.default = ResponseWriter;
1333
+ /**
1334
+ * @private
1335
+ * @param backendConfig
1336
+ */
1337
+ function normalizeBackendConfig(backendConfig) {
1338
+ if (typeof backendConfig === 'string')
1339
+ backendConfig = { domainOrIp: backendConfig };
1340
+ return backendConfig;
1341
+ }