@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.
- package/node_modules/@layer0/core/README.md +50 -0
- package/node_modules/@layer0/core/build.d.ts +5 -0
- package/node_modules/@layer0/core/build.js +17 -0
- package/node_modules/@layer0/core/cache/Cache.d.ts +56 -0
- package/node_modules/@layer0/core/cache/Cache.js +138 -0
- package/node_modules/@layer0/core/cache/CacheKey.d.ts +52 -0
- package/node_modules/@layer0/core/cache/CacheKey.js +95 -0
- package/node_modules/@layer0/core/config.d.ts +53 -0
- package/node_modules/@layer0/core/config.js +113 -0
- package/node_modules/@layer0/core/constants.d.ts +214 -0
- package/node_modules/@layer0/core/constants.js +216 -0
- package/node_modules/@layer0/core/context.d.ts +38 -0
- package/node_modules/@layer0/core/context.js +98 -0
- package/node_modules/@layer0/core/default-app/all/layer0.config.js +60 -0
- package/node_modules/@layer0/core/default-app/js/routes.js +27 -0
- package/node_modules/@layer0/core/default-app/ts/routes.ts +27 -0
- package/node_modules/@layer0/core/deploy/DeploymentBuilder.d.ts +177 -0
- package/node_modules/@layer0/core/deploy/DeploymentBuilder.js +543 -0
- package/node_modules/@layer0/core/deploy/bundle-esbuild.d.ts +11 -0
- package/node_modules/@layer0/core/deploy/bundle-esbuild.js +67 -0
- package/node_modules/@layer0/core/deploy/bundle-ncc.d.ts +8 -0
- package/node_modules/@layer0/core/deploy/bundle-ncc.js +30 -0
- package/node_modules/@layer0/core/deploy/bundle-nft.d.ts +7 -0
- package/node_modules/@layer0/core/deploy/bundle-nft.js +41 -0
- package/node_modules/@layer0/core/deploy/bundle-webpack.d.ts +11 -0
- package/node_modules/@layer0/core/deploy/bundle-webpack.js +127 -0
- package/node_modules/@layer0/core/deploy/index.d.ts +19 -0
- package/node_modules/@layer0/core/deploy/index.js +6 -0
- package/node_modules/@layer0/core/deploy/listSources.d.ts +11 -0
- package/node_modules/@layer0/core/deploy/listSources.js +18 -0
- package/node_modules/@layer0/core/deploy/paths.d.ts +27 -0
- package/node_modules/@layer0/core/deploy/paths.js +33 -0
- package/node_modules/@layer0/core/deploy/resolveInPackage.d.ts +7 -0
- package/node_modules/@layer0/core/deploy/resolveInPackage.js +21 -0
- package/node_modules/@layer0/core/deploy/routeToFiles.d.ts +14 -0
- package/node_modules/@layer0/core/deploy/routeToFiles.js +24 -0
- package/node_modules/@layer0/core/dev/DevServerOptions.d.ts +33 -0
- package/node_modules/@layer0/core/dev/DevServerOptions.js +2 -0
- package/node_modules/@layer0/core/dev/createDevServer.d.ts +8 -0
- package/node_modules/@layer0/core/dev/createDevServer.js +114 -0
- package/node_modules/@layer0/core/dev/index.d.ts +3 -0
- package/node_modules/@layer0/core/dev/index.js +6 -0
- package/node_modules/@layer0/core/dev/runLocalApp.d.ts +8 -0
- package/node_modules/@layer0/core/dev/runLocalApp.js +73 -0
- package/node_modules/@layer0/core/dev/serverlessCompatibilityMiddleware.d.ts +11 -0
- package/node_modules/@layer0/core/dev/serverlessCompatibilityMiddleware.js +30 -0
- package/node_modules/@layer0/core/edge/EdgeErrorPageResponseWriter.d.ts +12 -0
- package/node_modules/@layer0/core/edge/EdgeErrorPageResponseWriter.js +22 -0
- package/node_modules/@layer0/core/edge/EdgeResponseWriter.d.ts +122 -0
- package/node_modules/@layer0/core/edge/EdgeResponseWriter.js +451 -0
- package/node_modules/@layer0/core/edge/createEdgeConfig.d.ts +16 -0
- package/node_modules/@layer0/core/edge/createEdgeConfig.js +284 -0
- package/node_modules/@layer0/core/environment.d.ts +24 -0
- package/node_modules/@layer0/core/environment.js +42 -0
- package/node_modules/@layer0/core/errors/BackendFetchError.d.ts +11 -0
- package/node_modules/@layer0/core/errors/BackendFetchError.js +17 -0
- package/node_modules/@layer0/core/errors/FrameworkBuildError.d.ts +7 -0
- package/node_modules/@layer0/core/errors/FrameworkBuildError.js +26 -0
- package/node_modules/@layer0/core/index.d.ts +3 -0
- package/node_modules/@layer0/core/index.js +9 -0
- package/node_modules/@layer0/core/init/addToPackageJson.d.ts +7 -0
- package/node_modules/@layer0/core/init/addToPackageJson.js +22 -0
- package/node_modules/@layer0/core/init/index.d.ts +2 -0
- package/node_modules/@layer0/core/init/index.js +6 -0
- package/node_modules/@layer0/core/init/init.d.ts +5 -0
- package/node_modules/@layer0/core/init/init.js +17 -0
- package/node_modules/@layer0/core/lambda/JSBackend.d.ts +30 -0
- package/node_modules/@layer0/core/lambda/JSBackend.js +51 -0
- package/node_modules/@layer0/core/lambda/RequestListenerJSBackend.d.ts +8 -0
- package/node_modules/@layer0/core/lambda/RequestListenerJSBackend.js +19 -0
- package/node_modules/@layer0/core/lambda/adaptResponseEncodingToDownstream.d.ts +17 -0
- package/node_modules/@layer0/core/lambda/adaptResponseEncodingToDownstream.js +132 -0
- package/node_modules/@layer0/core/lambda/consoleWrapper.d.ts +16 -0
- package/node_modules/@layer0/core/lambda/consoleWrapper.js +127 -0
- package/node_modules/@layer0/core/lambda/getBodyLoggingData.d.ts +10 -0
- package/node_modules/@layer0/core/lambda/getBodyLoggingData.js +108 -0
- package/node_modules/@layer0/core/lambda/handler.d.ts +1 -0
- package/node_modules/@layer0/core/lambda/handler.js +91 -0
- package/node_modules/@layer0/core/lambda/handler.js.map +7 -0
- package/node_modules/@layer0/core/lambda/innerHandler.d.ts +7 -0
- package/node_modules/@layer0/core/lambda/innerHandler.js +181 -0
- package/node_modules/@layer0/core/lambda/lambdaInstance.d.ts +29 -0
- package/node_modules/@layer0/core/lambda/lambdaInstance.js +71 -0
- package/node_modules/@layer0/core/lambda/reqResMapper.d.ts +24 -0
- package/node_modules/@layer0/core/lambda/reqResMapper.js +286 -0
- package/node_modules/@layer0/core/lambda/stdStreamsWrapper.d.ts +31 -0
- package/node_modules/@layer0/core/lambda/stdStreamsWrapper.js +196 -0
- package/node_modules/@layer0/core/lambda/toJSBackend.d.ts +10 -0
- package/node_modules/@layer0/core/lambda/toJSBackend.js +23 -0
- package/node_modules/@layer0/core/node_modules/esbuild/LICENSE.md +21 -0
- package/node_modules/@layer0/core/node_modules/esbuild/README.md +3 -0
- package/node_modules/@layer0/core/node_modules/esbuild/bin/esbuild +223 -0
- package/node_modules/@layer0/core/node_modules/esbuild/install.js +301 -0
- package/node_modules/@layer0/core/node_modules/esbuild/lib/main.d.ts +716 -0
- package/node_modules/@layer0/core/node_modules/esbuild/lib/main.js +2532 -0
- package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/README.md +3 -0
- package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/bin/esbuild +0 -0
- package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/esbuild.wasm +0 -0
- package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/package.json +28 -0
- package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/wasm_exec.js +575 -0
- package/node_modules/@layer0/core/node_modules/esbuild/node_modules/@ohos-ports/esbuild-openharmony-arm64/wasm_exec_node.js +40 -0
- package/node_modules/@layer0/core/node_modules/esbuild/package.json +78 -0
- package/node_modules/@layer0/core/package.json +132 -0
- package/node_modules/@layer0/core/parser/parseRouter.d.ts +13 -0
- package/node_modules/@layer0/core/parser/parseRouter.js +77 -0
- package/node_modules/@layer0/core/plugins/PluginBase.d.ts +18 -0
- package/node_modules/@layer0/core/plugins/PluginBase.js +19 -0
- package/node_modules/@layer0/core/router/Backend.d.ts +69 -0
- package/node_modules/@layer0/core/router/Backend.js +176 -0
- package/node_modules/@layer0/core/router/BackendOptions.d.ts +11 -0
- package/node_modules/@layer0/core/router/BackendOptions.js +2 -0
- package/node_modules/@layer0/core/router/BaseResponseWriter.d.ts +65 -0
- package/node_modules/@layer0/core/router/BaseResponseWriter.js +81 -0
- package/node_modules/@layer0/core/router/BehindEdgeResponseWriterProxy.d.ts +4 -0
- package/node_modules/@layer0/core/router/BehindEdgeResponseWriterProxy.js +89 -0
- package/node_modules/@layer0/core/router/CacheManifest.d.ts +80 -0
- package/node_modules/@layer0/core/router/CacheManifest.js +83 -0
- package/node_modules/@layer0/core/router/CacheOptions.d.ts +171 -0
- package/node_modules/@layer0/core/router/CacheOptions.js +2 -0
- package/node_modules/@layer0/core/router/CustomCacheKey.d.ts +140 -0
- package/node_modules/@layer0/core/router/CustomCacheKey.js +272 -0
- package/node_modules/@layer0/core/router/GroupConfig.d.ts +7 -0
- package/node_modules/@layer0/core/router/GroupConfig.js +20 -0
- package/node_modules/@layer0/core/router/Groups.d.ts +7 -0
- package/node_modules/@layer0/core/router/Groups.js +21 -0
- package/node_modules/@layer0/core/router/Headers.d.ts +3 -0
- package/node_modules/@layer0/core/router/Headers.js +2 -0
- package/node_modules/@layer0/core/router/PathMatcher.d.ts +51 -0
- package/node_modules/@layer0/core/router/PathMatcher.js +73 -0
- package/node_modules/@layer0/core/router/Preload.d.ts +16 -0
- package/node_modules/@layer0/core/router/Preload.js +2 -0
- package/node_modules/@layer0/core/router/PreloadRequests.d.ts +23 -0
- package/node_modules/@layer0/core/router/PreloadRequests.js +66 -0
- package/node_modules/@layer0/core/router/Request.d.ts +74 -0
- package/node_modules/@layer0/core/router/Request.js +2 -0
- package/node_modules/@layer0/core/router/RequestContext.d.ts +12 -0
- package/node_modules/@layer0/core/router/RequestContext.js +11 -0
- package/node_modules/@layer0/core/router/Response.d.ts +55 -0
- package/node_modules/@layer0/core/router/Response.js +2 -0
- package/node_modules/@layer0/core/router/ResponseWriter.d.ts +1011 -0
- package/node_modules/@layer0/core/router/ResponseWriter.js +1341 -0
- package/node_modules/@layer0/core/router/Route.d.ts +124 -0
- package/node_modules/@layer0/core/router/Route.js +280 -0
- package/node_modules/@layer0/core/router/RouteCriteria.d.ts +68 -0
- package/node_modules/@layer0/core/router/RouteCriteria.js +2 -0
- package/node_modules/@layer0/core/router/RouteGroup.d.ts +157 -0
- package/node_modules/@layer0/core/router/RouteGroup.js +223 -0
- package/node_modules/@layer0/core/router/RouteGroupList.d.ts +45 -0
- package/node_modules/@layer0/core/router/RouteGroupList.js +73 -0
- package/node_modules/@layer0/core/router/Router.d.ts +439 -0
- package/node_modules/@layer0/core/router/Router.js +907 -0
- package/node_modules/@layer0/core/router/addBuiltInRoutes.d.ts +2 -0
- package/node_modules/@layer0/core/router/addBuiltInRoutes.js +58 -0
- package/node_modules/@layer0/core/router/addPreloadHeaders.d.ts +7 -0
- package/node_modules/@layer0/core/router/addPreloadHeaders.js +57 -0
- package/node_modules/@layer0/core/router/cacheManifestFromRouter.d.ts +6 -0
- package/node_modules/@layer0/core/router/cacheManifestFromRouter.js +85 -0
- package/node_modules/@layer0/core/router/createCustomCacheKey.d.ts +27 -0
- package/node_modules/@layer0/core/router/createCustomCacheKey.js +35 -0
- package/node_modules/@layer0/core/router/deriveSurrogateKeysFromJson.d.ts +36 -0
- package/node_modules/@layer0/core/router/deriveSurrogateKeysFromJson.js +70 -0
- package/node_modules/@layer0/core/router/index.d.ts +11 -0
- package/node_modules/@layer0/core/router/index.js +17 -0
- package/node_modules/@layer0/core/router/interpolateParams.d.ts +21 -0
- package/node_modules/@layer0/core/router/interpolateParams.js +150 -0
- package/node_modules/@layer0/core/router/loadRouter.d.ts +13 -0
- package/node_modules/@layer0/core/router/loadRouter.js +29 -0
- package/node_modules/@layer0/core/router/mergeQueryString.d.ts +1 -0
- package/node_modules/@layer0/core/router/mergeQueryString.js +23 -0
- package/node_modules/@layer0/core/router/normalizeCriteria.d.ts +6 -0
- package/node_modules/@layer0/core/router/normalizeCriteria.js +20 -0
- package/node_modules/@layer0/core/router/stream.d.ts +12 -0
- package/node_modules/@layer0/core/router/stream.js +30 -0
- package/node_modules/@layer0/core/router/universalRouteUtils.d.ts +55 -0
- package/node_modules/@layer0/core/router/universalRouteUtils.js +114 -0
- package/node_modules/@layer0/core/router/validateWriter.d.ts +41 -0
- package/node_modules/@layer0/core/router/validateWriter.js +120 -0
- package/node_modules/@layer0/core/serverless.yml +30 -0
- package/node_modules/@layer0/core/source.d.ts +9 -0
- package/node_modules/@layer0/core/source.js +17 -0
- package/node_modules/@layer0/core/test-utils/MockRequest.d.ts +15 -0
- package/node_modules/@layer0/core/test-utils/MockRequest.js +36 -0
- package/node_modules/@layer0/core/test-utils/MockResponse.d.ts +7 -0
- package/node_modules/@layer0/core/test-utils/MockResponse.js +24 -0
- package/node_modules/@layer0/core/test-utils/expect-extend.d.ts +1 -0
- package/node_modules/@layer0/core/test-utils/expect-extend.js +11 -0
- package/node_modules/@layer0/core/test-utils/index.d.ts +4 -0
- package/node_modules/@layer0/core/test-utils/index.js +11 -0
- package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheBrowser.d.ts +19 -0
- package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheBrowser.js +45 -0
- package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheEdge.d.ts +19 -0
- package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheEdge.js +38 -0
- package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheServiceWorker.d.ts +19 -0
- package/node_modules/@layer0/core/test-utils/matchers/toBeCachedByTheServiceWorker.js +45 -0
- package/node_modules/@layer0/core/test-utils/matchers/toBeServedStale.d.ts +20 -0
- package/node_modules/@layer0/core/test-utils/matchers/toBeServedStale.js +50 -0
- package/node_modules/@layer0/core/test-utils/matchers/toHaveBody.d.ts +18 -0
- package/node_modules/@layer0/core/test-utils/matchers/toHaveBody.js +40 -0
- package/node_modules/@layer0/core/test-utils/matchers/toHaveHeader.d.ts +19 -0
- package/node_modules/@layer0/core/test-utils/matchers/toHaveHeader.js +43 -0
- package/node_modules/@layer0/core/test-utils/matchers.d.ts +6 -0
- package/node_modules/@layer0/core/test-utils/matchers.js +18 -0
- package/node_modules/@layer0/core/test-utils/utils/createRouteMock.d.ts +21 -0
- package/node_modules/@layer0/core/test-utils/utils/createRouteMock.js +27 -0
- package/node_modules/@layer0/core/test-utils/utils/getBackend.d.ts +13 -0
- package/node_modules/@layer0/core/test-utils/utils/getBackend.js +30 -0
- package/node_modules/@layer0/core/test-utils/utils/getMessage.d.ts +1 -0
- package/node_modules/@layer0/core/test-utils/utils/getMessage.js +16 -0
- package/node_modules/@layer0/core/test-utils/utils/runRoute.d.ts +10 -0
- package/node_modules/@layer0/core/test-utils/utils/runRoute.js +39 -0
- package/node_modules/@layer0/core/timing.d.ts +115 -0
- package/node_modules/@layer0/core/timing.js +200 -0
- package/node_modules/@layer0/core/transform/index.d.ts +1 -0
- package/node_modules/@layer0/core/transform/index.js +4 -0
- package/node_modules/@layer0/core/transform/transformMethod.d.ts +40 -0
- package/node_modules/@layer0/core/transform/transformMethod.js +58 -0
- package/node_modules/@layer0/core/types.d.ts +102 -0
- package/node_modules/@layer0/core/types.js +30 -0
- package/node_modules/@layer0/core/utils/Tasks.d.ts +32 -0
- package/node_modules/@layer0/core/utils/Tasks.js +80 -0
- package/node_modules/@layer0/core/utils/Validator.d.ts +18 -0
- package/node_modules/@layer0/core/utils/Validator.js +178 -0
- package/node_modules/@layer0/core/utils/bindParamsToPath.d.ts +15 -0
- package/node_modules/@layer0/core/utils/bindParamsToPath.js +49 -0
- package/node_modules/@layer0/core/utils/convertParamsToBackReferences.d.ts +16 -0
- package/node_modules/@layer0/core/utils/convertParamsToBackReferences.js +45 -0
- package/node_modules/@layer0/core/utils/convertStringMatcherToRegex.d.ts +7 -0
- package/node_modules/@layer0/core/utils/convertStringMatcherToRegex.js +26 -0
- package/node_modules/@layer0/core/utils/decode.d.ts +9 -0
- package/node_modules/@layer0/core/utils/decode.js +44 -0
- package/node_modules/@layer0/core/utils/esImport.d.ts +1 -0
- package/node_modules/@layer0/core/utils/esImport.js +9 -0
- package/node_modules/@layer0/core/utils/fs.d.ts +1 -0
- package/node_modules/@layer0/core/utils/fs.js +7 -0
- package/node_modules/@layer0/core/utils/get.d.ts +6 -0
- package/node_modules/@layer0/core/utils/get.js +19 -0
- package/node_modules/@layer0/core/utils/getPackageVersion.d.ts +7 -0
- package/node_modules/@layer0/core/utils/getPackageVersion.js +19 -0
- package/node_modules/@layer0/core/utils/has.d.ts +6 -0
- package/node_modules/@layer0/core/utils/has.js +15 -0
- package/node_modules/@layer0/core/utils/identity.d.ts +7 -0
- package/node_modules/@layer0/core/utils/identity.js +12 -0
- package/node_modules/@layer0/core/utils/index.d.ts +1 -0
- package/node_modules/@layer0/core/utils/index.js +4 -0
- package/node_modules/@layer0/core/utils/isEmpty.d.ts +6 -0
- package/node_modules/@layer0/core/utils/isEmpty.js +20 -0
- package/node_modules/@layer0/core/utils/isString.d.ts +1 -0
- package/node_modules/@layer0/core/utils/isString.js +6 -0
- package/node_modules/@layer0/core/utils/logo.d.ts +5 -0
- package/node_modules/@layer0/core/utils/logo.js +27 -0
- package/node_modules/@layer0/core/utils/mapValues.d.ts +7 -0
- package/node_modules/@layer0/core/utils/mapValues.js +16 -0
- package/node_modules/@layer0/core/utils/minBy.d.ts +8 -0
- package/node_modules/@layer0/core/utils/minBy.js +41 -0
- package/node_modules/@layer0/core/utils/nonWebpackRequire.d.ts +11 -0
- package/node_modules/@layer0/core/utils/nonWebpackRequire.js +28 -0
- package/node_modules/@layer0/core/utils/packageUtils.d.ts +12 -0
- package/node_modules/@layer0/core/utils/packageUtils.js +22 -0
- package/node_modules/@layer0/core/utils/ports.d.ts +5 -0
- package/node_modules/@layer0/core/utils/ports.js +7 -0
- package/node_modules/@layer0/core/utils/readBody.d.ts +7 -0
- package/node_modules/@layer0/core/utils/readBody.js +15 -0
- package/node_modules/@layer0/core/utils/readRawBody.d.ts +9 -0
- package/node_modules/@layer0/core/utils/readRawBody.js +21 -0
- package/node_modules/@layer0/core/utils/regExpEscape.d.ts +12 -0
- package/node_modules/@layer0/core/utils/regExpEscape.js +20 -0
- package/node_modules/@layer0/core/utils/requireInternal.d.ts +7 -0
- package/node_modules/@layer0/core/utils/requireInternal.js +24 -0
- package/node_modules/@layer0/core/utils/set.d.ts +10 -0
- package/node_modules/@layer0/core/utils/set.js +21 -0
- package/node_modules/@layer0/core/utils/toEdgeRegex.d.ts +11 -0
- package/node_modules/@layer0/core/utils/toEdgeRegex.js +23 -0
- package/node_modules/@layer0/core/utils/toHttpMethodMatchRegex.d.ts +12 -0
- package/node_modules/@layer0/core/utils/toHttpMethodMatchRegex.js +17 -0
- package/node_modules/@layer0/core/utils/toLower.d.ts +1 -0
- package/node_modules/@layer0/core/utils/toLower.js +9 -0
- package/node_modules/@layer0/core/utils/validateBackends.d.ts +5 -0
- package/node_modules/@layer0/core/utils/validateBackends.js +30 -0
- package/node_modules/@layer0/core/utils/watch.d.ts +10 -0
- package/node_modules/@layer0/core/utils/watch.js +17 -0
- 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
|
+
}
|