cdk-nextjs 0.6.1 → 0.7.0
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/.jsii +2467 -2714
- package/.jsii.tabl.json +1 -1
- package/API.md +1725 -1909
- package/CLAUDE.md +2 -1
- package/README.md +429 -130
- package/assets/lambdas/post-deploy/post-deploy.lambda/index.js +152 -170
- package/assets/lambdas/redeploy-stage/redeploy-stage.lambda/index.js +875 -0
- package/docs/adapter-runtime.md +185 -0
- package/docs/breaking-changes.md +687 -4
- package/docs/caching-guide.md +156 -112
- package/docs/cdk-nextjs-cfn-exec-policy.json +2 -1
- package/docs/development-guide.md +1 -1
- package/docs/harness-coverage.md +598 -0
- package/docs/init-cache-deployment.md +32 -1
- package/docs/next-build-output-guide.md +30 -20
- package/examples/README.md +5 -1
- package/lib/adapter/__fixtures__/app-playground-base-path.json +4926 -0
- package/lib/adapter/__fixtures__/app-playground.json +4917 -0
- package/lib/adapter/__fixtures__/pages-i18n.json +1728 -0
- package/lib/adapter/adapter.mjs +1548 -185
- package/lib/adapter/adapter.test.js +106 -0
- package/lib/adapter/aws-cache-store.d.ts +489 -0
- package/lib/adapter/aws-cache-store.js +1004 -0
- package/lib/adapter/aws-cache-store.test.d.ts +1 -0
- package/lib/adapter/aws-cache-store.test.js +361 -0
- package/lib/adapter/build-outputs.d.ts +80 -0
- package/lib/adapter/build-outputs.js +852 -0
- package/lib/adapter/build-outputs.test.d.ts +1 -0
- package/lib/adapter/build-outputs.test.js +861 -0
- package/lib/adapter/cache-handler.d.ts +9 -2
- package/lib/adapter/cache-handler.js +72 -96
- package/lib/adapter/cache-handler.mjs +18956 -17774
- package/lib/adapter/cache-handler.test.js +263 -31
- package/lib/adapter/cache-kinds.d.ts +32 -0
- package/lib/adapter/cache-kinds.js +35 -0
- package/lib/adapter/cache-kinds.test.d.ts +1 -0
- package/lib/adapter/cache-kinds.test.js +69 -0
- package/lib/adapter/cache-utils.d.ts +90 -0
- package/lib/adapter/cache-utils.js +199 -4
- package/lib/adapter/cache-utils.test.d.ts +1 -0
- package/lib/adapter/cache-utils.test.js +179 -0
- package/lib/adapter/function-groups.d.ts +215 -0
- package/lib/adapter/function-groups.js +1099 -0
- package/lib/adapter/function-groups.test.d.ts +1 -0
- package/lib/adapter/function-groups.test.js +743 -0
- package/lib/adapter/init-cache.d.ts +6 -0
- package/lib/adapter/init-cache.js +245 -0
- package/lib/adapter/init-cache.test.d.ts +1 -0
- package/lib/adapter/init-cache.test.js +137 -0
- package/lib/adapter/local-file-cache-handler.d.ts +23 -0
- package/lib/adapter/local-file-cache-handler.js +52 -1
- package/lib/adapter/memory-cache-handler.d.ts +26 -17
- package/lib/adapter/memory-cache-handler.js +32 -50
- package/lib/adapter/memory-cache-handler.test.js +105 -85
- package/lib/adapter/s3-cache-handler.d.ts +179 -40
- package/lib/adapter/s3-cache-handler.js +807 -277
- package/lib/adapter/s3-cache-handler.test.js +1186 -31
- package/lib/adapter/use-cache-common.d.ts +90 -0
- package/lib/adapter/use-cache-common.js +250 -0
- package/lib/adapter/use-cache-common.test.d.ts +1 -0
- package/lib/adapter/use-cache-common.test.js +646 -0
- package/lib/adapter/use-cache-default-handler.d.ts +24 -0
- package/lib/adapter/use-cache-default-handler.js +90 -0
- package/lib/adapter/use-cache-default-handler.mjs +38645 -0
- package/lib/adapter/use-cache-handlers.test.d.ts +1 -0
- package/lib/adapter/use-cache-handlers.test.js +349 -0
- package/lib/adapter/use-cache-remote-handler.d.ts +26 -0
- package/lib/adapter/use-cache-remote-handler.js +186 -0
- package/lib/{image-optimization/handler.mjs → adapter/use-cache-remote-handler.mjs} +33572 -38798
- package/lib/generated-structs/OptionalNextjsApiProps.d.ts +59 -0
- package/lib/generated-structs/OptionalNextjsApiProps.js +3 -0
- package/lib/generated-structs/OptionalNextjsCacheProps.d.ts +15 -6
- package/lib/generated-structs/OptionalNextjsCacheProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsContainersProps.d.ts +25 -11
- package/lib/generated-structs/OptionalNextjsContainersProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +31 -9
- package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
- package/lib/generated-structs/OptionalNextjsPostDeployProps.d.ts +13 -0
- package/lib/generated-structs/OptionalNextjsPostDeployProps.js +1 -1
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.d.ts +11 -2
- package/lib/generated-structs/OptionalPostDeployCustomResourceProperties.js +1 -1
- package/lib/index.d.ts +6 -7
- package/lib/index.js +2 -4
- package/lib/lambdas/post-deploy/post-deploy.lambda.js +3 -2
- package/lib/lambdas/post-deploy/prune-cache-bucket.d.ts +7 -3
- package/lib/lambdas/post-deploy/prune-cache-bucket.js +57 -71
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-cache-bucket.test.js +114 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.js +23 -31
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-revalidation-table.test.js +71 -0
- package/lib/lambdas/post-deploy/prune-s3.d.ts +19 -3
- package/lib/lambdas/post-deploy/prune-s3.js +54 -15
- package/lib/lambdas/post-deploy/prune-s3.test.d.ts +1 -0
- package/lib/lambdas/post-deploy/prune-s3.test.js +255 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.d.ts +13 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage-function.js +55 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.d.ts +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.js +68 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.d.ts +20 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.lambda.js +24 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.d.ts +1 -0
- package/lib/lambdas/redeploy-stage/redeploy-stage.test.js +93 -0
- package/lib/nextjs-api.d.ts +107 -18
- package/lib/nextjs-api.js +349 -39
- package/lib/nextjs-api.test.js +582 -1
- package/lib/nextjs-build/global-containers.Dockerfile +15 -10
- package/lib/nextjs-build/nextjs-build.d.ts +339 -63
- package/lib/nextjs-build/nextjs-build.js +684 -327
- package/lib/nextjs-build/nextjs-build.test.d.ts +1 -0
- package/lib/nextjs-build/nextjs-build.test.js +412 -0
- package/lib/nextjs-build/patch-fetch.js +1 -1
- package/lib/nextjs-build/patch-fetch.test.js +166 -11
- package/lib/nextjs-build/regional-containers.Dockerfile +17 -10
- package/lib/nextjs-cache.d.ts +82 -4
- package/lib/nextjs-cache.js +186 -10
- package/lib/nextjs-cache.test.js +176 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.d.ts +21 -8
- package/lib/nextjs-compute/nextjs-compute-base-props.js +41 -1
- package/lib/nextjs-compute/nextjs-compute-base-props.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-compute-base-props.test.js +44 -0
- package/lib/nextjs-compute/nextjs-containers.d.ts +19 -0
- package/lib/nextjs-compute/nextjs-containers.js +57 -20
- package/lib/nextjs-compute/nextjs-functions.d.ts +122 -7
- package/lib/nextjs-compute/nextjs-functions.js +113 -67
- package/lib/nextjs-compute/nextjs-functions.test.d.ts +1 -0
- package/lib/nextjs-compute/nextjs-functions.test.js +99 -0
- package/lib/nextjs-distribution.d.ts +194 -23
- package/lib/nextjs-distribution.js +535 -62
- package/lib/nextjs-distribution.test.d.ts +1 -0
- package/lib/nextjs-distribution.test.js +697 -0
- package/lib/nextjs-post-deploy.d.ts +23 -3
- package/lib/nextjs-post-deploy.js +14 -10
- package/lib/nextjs-post-deploy.test.d.ts +1 -0
- package/lib/nextjs-post-deploy.test.js +34 -0
- package/lib/nextjs-static-assets.d.ts +23 -4
- package/lib/nextjs-static-assets.js +29 -6
- package/lib/nextjs-static-assets.test.js +20 -1
- package/lib/root-constructs/nextjs-base-construct.d.ts +105 -45
- package/lib/root-constructs/nextjs-base-construct.js +144 -34
- package/lib/root-constructs/nextjs-global-containers.d.ts +18 -20
- package/lib/root-constructs/nextjs-global-containers.js +20 -70
- package/lib/root-constructs/nextjs-global-functions.d.ts +26 -28
- package/lib/root-constructs/nextjs-global-functions.js +52 -69
- package/lib/root-constructs/nextjs-regional-containers.d.ts +25 -5
- package/lib/root-constructs/nextjs-regional-containers.js +17 -15
- package/lib/root-constructs/nextjs-regional-functions.d.ts +22 -18
- package/lib/root-constructs/nextjs-regional-functions.js +8 -25
- package/lib/runtime/api-gateway-path.d.ts +34 -0
- package/lib/runtime/api-gateway-path.js +71 -0
- package/lib/runtime/api-gateway-path.test.d.ts +1 -0
- package/lib/runtime/api-gateway-path.test.js +65 -0
- package/lib/runtime/case-redirect.d.ts +22 -0
- package/lib/runtime/case-redirect.js +63 -0
- package/lib/runtime/case-redirect.test.d.ts +1 -0
- package/lib/runtime/case-redirect.test.js +32 -0
- package/lib/runtime/core.d.ts +234 -0
- package/lib/runtime/core.js +1213 -0
- package/lib/runtime/core.test.d.ts +1 -0
- package/lib/runtime/core.test.js +1678 -0
- package/lib/runtime/deployment-root.d.ts +13 -0
- package/lib/runtime/deployment-root.js +29 -0
- package/lib/runtime/dispatch.d.ts +331 -0
- package/lib/runtime/dispatch.js +751 -0
- package/lib/runtime/dispatch.test.d.ts +1 -0
- package/lib/runtime/dispatch.test.js +853 -0
- package/lib/runtime/entrypoints.d.ts +57 -0
- package/lib/runtime/entrypoints.js +115 -0
- package/lib/runtime/http/abort-signal.d.ts +6 -0
- package/lib/runtime/http/abort-signal.js +19 -0
- package/lib/runtime/http/abort-signal.test.d.ts +1 -0
- package/lib/runtime/http/abort-signal.test.js +49 -0
- package/lib/runtime/http/node-server.d.ts +38 -0
- package/lib/runtime/http/node-server.js +99 -0
- package/lib/runtime/http/node-server.test.d.ts +1 -0
- package/lib/runtime/http/node-server.test.js +96 -0
- package/lib/runtime/http/node-sink.d.ts +16 -0
- package/lib/runtime/http/node-sink.js +48 -0
- package/lib/runtime/http/node-sink.test.d.ts +1 -0
- package/lib/runtime/http/node-sink.test.js +66 -0
- package/lib/runtime/http/request.d.ts +62 -0
- package/lib/runtime/http/request.js +134 -0
- package/lib/runtime/http/request.test.d.ts +1 -0
- package/lib/runtime/http/request.test.js +103 -0
- package/lib/runtime/http/response.d.ts +77 -0
- package/lib/runtime/http/response.js +229 -0
- package/lib/runtime/http/response.test.d.ts +1 -0
- package/lib/runtime/http/response.test.js +178 -0
- package/lib/runtime/http/sink.d.ts +73 -0
- package/lib/runtime/http/sink.js +188 -0
- package/lib/runtime/http/sink.test.d.ts +1 -0
- package/lib/runtime/http/sink.test.js +257 -0
- package/lib/runtime/image-utils.d.ts +78 -0
- package/lib/runtime/image-utils.js +129 -0
- package/lib/runtime/image-utils.test.d.ts +1 -0
- package/lib/runtime/image-utils.test.js +237 -0
- package/lib/runtime/image.d.ts +76 -0
- package/lib/runtime/image.js +296 -0
- package/lib/runtime/image.test.d.ts +1 -0
- package/lib/runtime/image.test.js +405 -0
- package/lib/runtime/lambda-event.d.ts +4 -0
- package/lib/runtime/lambda-event.js +113 -0
- package/lib/runtime/lambda-event.test.d.ts +1 -0
- package/lib/runtime/lambda-event.test.js +119 -0
- package/lib/runtime/lambda.mjs +41595 -0
- package/lib/runtime/load-module.d.ts +8 -0
- package/lib/runtime/load-module.js +35 -0
- package/lib/runtime/manifest.d.ts +215 -0
- package/lib/runtime/manifest.js +118 -0
- package/lib/runtime/middleware.d.ts +43 -0
- package/lib/runtime/middleware.js +113 -0
- package/lib/runtime/middleware.test.d.ts +1 -0
- package/lib/runtime/middleware.test.js +259 -0
- package/lib/runtime/next-modules.d.ts +55 -0
- package/lib/runtime/next-modules.js +124 -0
- package/lib/runtime/next-modules.test.d.ts +1 -0
- package/lib/runtime/next-modules.test.js +34 -0
- package/lib/runtime/public-files.d.ts +32 -0
- package/lib/runtime/public-files.js +117 -0
- package/lib/runtime/public-files.test.d.ts +1 -0
- package/lib/runtime/public-files.test.js +97 -0
- package/lib/runtime/server.mjs +41588 -0
- package/lib/runtime/static-files.d.ts +50 -0
- package/lib/runtime/static-files.js +266 -0
- package/lib/runtime/static-files.test.d.ts +1 -0
- package/lib/runtime/static-files.test.js +118 -0
- package/lib/runtime/util.d.ts +28 -0
- package/lib/runtime/util.js +79 -0
- package/lib/utils/base-path.d.ts +123 -11
- package/lib/utils/base-path.js +276 -39
- package/lib/utils/base-path.test.js +275 -50
- package/lib/utils/get-architecture.d.ts +7 -8
- package/lib/utils/get-architecture.js +12 -13
- package/lib/utils/zip-directory.d.ts +14 -0
- package/lib/utils/zip-directory.js +121 -0
- package/lib/utils/zip-directory.test.d.ts +1 -0
- package/lib/utils/zip-directory.test.js +99 -0
- package/package.json +22 -3
- package/scripts/capture-adapter-fixture.mjs +325 -0
- package/scripts/e2e-deploy.sh +398 -0
- package/scripts/e2e-harness/README.md +707 -0
- package/scripts/e2e-harness/app.js +370 -0
- package/scripts/e2e-harness/common.sh +249 -0
- package/scripts/e2e-harness/screen.mjs +302 -0
- package/scripts/e2e-harness/stage-proxy.mjs +145 -0
- package/scripts/e2e-harness/type-filters.mjs +47 -0
- package/scripts/e2e-logs.sh +75 -0
- package/scripts/e2e-offline.sh +91 -0
- package/scripts/e2e-sweep.sh +370 -0
- package/scripts/e2e-warm.sh +114 -0
- package/scripts/zero-config-build.mjs +418 -0
- package/docs/1k-concurrent-users-executions.png +0 -0
- package/docs/plans/adapter-runtime-release.md +0 -362
- package/docs/plans/phase-0-image-function-opt-in.md +0 -295
- package/lib/generated-structs/OptionalDockerImageFunctionProps.d.ts +0 -425
- package/lib/generated-structs/OptionalDockerImageFunctionProps.js +0 -3
- package/lib/image-optimization/handler-utils.d.ts +0 -34
- package/lib/image-optimization/handler-utils.js +0 -83
- package/lib/image-optimization/handler-utils.test.js +0 -178
- package/lib/nextjs-build/functions.Dockerfile +0 -31
- package/lib/nextjs-compute/nextjs-image-function.d.ts +0 -53
- package/lib/nextjs-compute/nextjs-image-function.js +0 -59
- package/lib/utils/experimental-flags.d.ts +0 -24
- package/lib/utils/experimental-flags.js +0 -30
- /package/lib/{image-optimization/handler-utils.test.d.ts → adapter/adapter.test.d.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
4
|
+
jest.mock("@aws-sdk/client-s3");
|
|
5
|
+
jest.mock("@aws-sdk/client-dynamodb");
|
|
6
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
7
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
8
|
+
const aws_cache_store_1 = require("./aws-cache-store");
|
|
9
|
+
/** The input an automocked SDK command was constructed with. */
|
|
10
|
+
const commandInput = (command, type) => {
|
|
11
|
+
const mock = type;
|
|
12
|
+
return mock.mock.calls[mock.mock.instances.indexOf(command)][0];
|
|
13
|
+
};
|
|
14
|
+
describe("resolveAwsCacheConfig", () => {
|
|
15
|
+
const env = process.env;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
process.env = { ...env };
|
|
18
|
+
});
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
process.env = env;
|
|
21
|
+
});
|
|
22
|
+
it("reads the environment the constructs set", () => {
|
|
23
|
+
process.env.CDK_NEXTJS_CACHE_BUCKET_NAME = "bucket";
|
|
24
|
+
process.env.CDK_NEXTJS_REVALIDATION_TABLE_NAME = "table";
|
|
25
|
+
process.env.CDK_NEXTJS_BUILD_ID = "build";
|
|
26
|
+
process.env.AWS_REGION = "eu-west-1";
|
|
27
|
+
expect((0, aws_cache_store_1.resolveAwsCacheConfig)()).toEqual({
|
|
28
|
+
bucketName: "bucket",
|
|
29
|
+
tableName: "table",
|
|
30
|
+
buildId: "build",
|
|
31
|
+
region: "eu-west-1",
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
it("reports what is unset as empty", () => {
|
|
35
|
+
delete process.env.CDK_NEXTJS_CACHE_BUCKET_NAME;
|
|
36
|
+
delete process.env.CDK_NEXTJS_REVALIDATION_TABLE_NAME;
|
|
37
|
+
delete process.env.CDK_NEXTJS_BUILD_ID;
|
|
38
|
+
delete process.env.AWS_REGION;
|
|
39
|
+
expect((0, aws_cache_store_1.resolveAwsCacheConfig)()).toEqual({
|
|
40
|
+
bucketName: "",
|
|
41
|
+
tableName: "",
|
|
42
|
+
buildId: "",
|
|
43
|
+
region: "us-east-1",
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe("S3 keys", () => {
|
|
48
|
+
it("keeps the incremental cache's {buildId}/{key}.json convention", () => {
|
|
49
|
+
expect((0, aws_cache_store_1.buildS3Key)("b", "/")).toBe("b/index.json");
|
|
50
|
+
expect((0, aws_cache_store_1.buildS3Key)("b", "")).toBe("b/index.json");
|
|
51
|
+
expect((0, aws_cache_store_1.buildS3Key)("b", "/blog/post")).toBe("b/blog/post.json");
|
|
52
|
+
expect((0, aws_cache_store_1.buildS3Key)("b", "abc123")).toBe("b/abc123.json");
|
|
53
|
+
});
|
|
54
|
+
it("puts 'use cache' entries where no incremental key can land", () => {
|
|
55
|
+
const key = (0, aws_cache_store_1.useCacheS3Key)("b", "f".repeat(64));
|
|
56
|
+
expect(key).toBe(`b/_use-cache/${"f".repeat(64)}.entry`);
|
|
57
|
+
// Even a route spelled like the use-cache key gets `.json` appended.
|
|
58
|
+
expect((0, aws_cache_store_1.buildS3Key)("b", `/_use-cache/${"f".repeat(64)}.entry`)).not.toBe(key);
|
|
59
|
+
expect(key.endsWith(".json")).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe("tag markers", () => {
|
|
63
|
+
it("writes what markerFor applies locally", () => {
|
|
64
|
+
for (const durations of [undefined, {}, { expire: 60 }]) {
|
|
65
|
+
const update = (0, aws_cache_store_1.markerUpdate)(1000, durations);
|
|
66
|
+
const local = (0, aws_cache_store_1.markerFor)(1000, durations);
|
|
67
|
+
const written = {};
|
|
68
|
+
for (const [name, value] of Object.entries(update.ExpressionAttributeValues)) {
|
|
69
|
+
written[name.slice(1)] = Number(value.N);
|
|
70
|
+
}
|
|
71
|
+
expect(written).toEqual(local);
|
|
72
|
+
}
|
|
73
|
+
expect((0, aws_cache_store_1.markerFor)(1000, { expire: 60 })).toEqual({
|
|
74
|
+
staleAt: 1000,
|
|
75
|
+
expiredAt: 61000,
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
it("keeps an earlier expiredAt a profile with no expire leaves", () => {
|
|
79
|
+
// The row keeps it (`SET staleAt` only), so an instance learning of the
|
|
80
|
+
// second revalidation from the log has to keep it too.
|
|
81
|
+
const first = (0, aws_cache_store_1.markerFor)(1000, { expire: 60 });
|
|
82
|
+
const second = (0, aws_cache_store_1.markerFor)(2000, {});
|
|
83
|
+
expect((0, aws_cache_store_1.mergeMarkers)(first, second)).toEqual({
|
|
84
|
+
staleAt: 2000,
|
|
85
|
+
expiredAt: 61000,
|
|
86
|
+
});
|
|
87
|
+
expect((0, aws_cache_store_1.mergeMarkers)(second, first)).toEqual({
|
|
88
|
+
staleAt: 2000,
|
|
89
|
+
expiredAt: 61000,
|
|
90
|
+
});
|
|
91
|
+
// A newer `expire` still replaces it.
|
|
92
|
+
expect((0, aws_cache_store_1.mergeMarkers)(first, (0, aws_cache_store_1.markerFor)(2000, { expire: 1 }))).toEqual({
|
|
93
|
+
staleAt: 2000,
|
|
94
|
+
expiredAt: 3000,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
it("judges an entry the way the incremental cache always has", () => {
|
|
98
|
+
// Expired outright after creation.
|
|
99
|
+
expect((0, aws_cache_store_1.markerState)({ revalidatedAt: 20 }, 10, 30)).toBe("expired");
|
|
100
|
+
// Revalidated before the entry was created: no effect.
|
|
101
|
+
expect((0, aws_cache_store_1.markerState)({ revalidatedAt: 5 }, 10, 30)).toBe("fresh");
|
|
102
|
+
// A profile: stale now, expired once `expiredAt` passes.
|
|
103
|
+
expect((0, aws_cache_store_1.markerState)({ staleAt: 20, expiredAt: 50 }, 10, 30)).toBe("stale");
|
|
104
|
+
expect((0, aws_cache_store_1.markerState)({ staleAt: 20, expiredAt: 50 }, 10, 60)).toBe("expired");
|
|
105
|
+
// `expire: 0`: stale and expired at once.
|
|
106
|
+
expect((0, aws_cache_store_1.markerState)({ staleAt: 20, expiredAt: 20 }, 10, 30)).toBe("expired");
|
|
107
|
+
expect((0, aws_cache_store_1.markerState)({}, 10, 30)).toBe("fresh");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe("TagMarkerTable", () => {
|
|
111
|
+
const send = jest.fn();
|
|
112
|
+
let table;
|
|
113
|
+
beforeEach(() => {
|
|
114
|
+
send.mockReset();
|
|
115
|
+
client_dynamodb_1.DynamoDBClient.mockImplementation(() => ({ send }));
|
|
116
|
+
table = new aws_cache_store_1.TagMarkerTable(new client_dynamodb_1.DynamoDBClient({}), "tbl", "build");
|
|
117
|
+
});
|
|
118
|
+
it("writes the marker row under the build", async () => {
|
|
119
|
+
send.mockResolvedValue({});
|
|
120
|
+
await table.write("posts", 1000, { expire: 1 });
|
|
121
|
+
const input = commandInput(send.mock.calls[0][0], client_dynamodb_1.UpdateItemCommand);
|
|
122
|
+
expect(input).toEqual({
|
|
123
|
+
TableName: "tbl",
|
|
124
|
+
Key: { pk: { S: "build" }, sk: { S: "posts" } },
|
|
125
|
+
UpdateExpression: "SET staleAt = :staleAt, expiredAt = :expiredAt",
|
|
126
|
+
ExpressionAttributeValues: {
|
|
127
|
+
":staleAt": { N: "1000" },
|
|
128
|
+
":expiredAt": { N: "2000" },
|
|
129
|
+
},
|
|
130
|
+
ReturnValues: "ALL_NEW",
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
it("reads markers in batches of 100 and parses them", async () => {
|
|
134
|
+
send.mockImplementation((command) => {
|
|
135
|
+
const { RequestItems } = commandInput(command, client_dynamodb_1.BatchGetItemCommand);
|
|
136
|
+
const keys = RequestItems.tbl.Keys;
|
|
137
|
+
return Promise.resolve({
|
|
138
|
+
Responses: {
|
|
139
|
+
tbl: keys
|
|
140
|
+
.filter(({ sk }) => sk.S.endsWith("0"))
|
|
141
|
+
.map(({ sk }) => ({ sk, revalidatedAt: { N: "7" } })),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
const tags = Array.from({ length: 150 }, (_, i) => `t${i}`);
|
|
146
|
+
const markers = await table.read([...tags, "t0"]);
|
|
147
|
+
expect(send).toHaveBeenCalledTimes(2);
|
|
148
|
+
expect(markers.size).toBe(15);
|
|
149
|
+
expect(markers.get("t10")).toEqual({
|
|
150
|
+
revalidatedAt: 7,
|
|
151
|
+
staleAt: undefined,
|
|
152
|
+
expiredAt: undefined,
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
// A read-back after the write is eventually consistent, so it can return
|
|
156
|
+
// the marker from before it: the writer tracks what the write returned.
|
|
157
|
+
it("returns the row as the write left it", async () => {
|
|
158
|
+
send.mockResolvedValueOnce({
|
|
159
|
+
Attributes: {
|
|
160
|
+
sk: { S: "posts" },
|
|
161
|
+
revalidatedAt: { N: "5000" },
|
|
162
|
+
staleAt: { N: "10" },
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
expect(await table.write("posts", 5000, undefined)).toEqual({
|
|
166
|
+
revalidatedAt: 5000,
|
|
167
|
+
staleAt: 10,
|
|
168
|
+
expiredAt: undefined,
|
|
169
|
+
});
|
|
170
|
+
send.mockResolvedValueOnce({});
|
|
171
|
+
expect(await table.write("posts", 6000, undefined)).toBeUndefined();
|
|
172
|
+
});
|
|
173
|
+
it("asks again for unprocessed keys, and gives up after three tries", async () => {
|
|
174
|
+
const unprocessed = {
|
|
175
|
+
UnprocessedKeys: {
|
|
176
|
+
tbl: { Keys: [{ pk: { S: "build" }, sk: { S: "a" } }] },
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
send.mockResolvedValueOnce(unprocessed).mockResolvedValueOnce({
|
|
180
|
+
Responses: { tbl: [{ sk: { S: "a" }, staleAt: { N: "3" } }] },
|
|
181
|
+
});
|
|
182
|
+
expect((await table.read(["a"])).get("a")?.staleAt).toBe(3);
|
|
183
|
+
send.mockResolvedValue(unprocessed);
|
|
184
|
+
await expect(table.read(["a"])).rejects.toThrow(/unread after retrying/);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
describe("RevalidationLog", () => {
|
|
188
|
+
const send = jest.fn();
|
|
189
|
+
let log;
|
|
190
|
+
beforeEach(() => {
|
|
191
|
+
send.mockReset();
|
|
192
|
+
client_dynamodb_1.DynamoDBClient.mockImplementation(() => ({ send }));
|
|
193
|
+
log = new aws_cache_store_1.RevalidationLog(new client_dynamodb_1.DynamoDBClient({}), "tbl", "build");
|
|
194
|
+
});
|
|
195
|
+
it("puts one row per revalidation under the build's log, with a TTL", async () => {
|
|
196
|
+
send.mockResolvedValue({});
|
|
197
|
+
const at = 1_727_000_000_123;
|
|
198
|
+
await log.put("user#42", at, { staleAt: 5, expiredAt: 6 });
|
|
199
|
+
expect(commandInput(send.mock.calls[0][0], client_dynamodb_1.PutItemCommand)).toEqual({
|
|
200
|
+
TableName: "tbl",
|
|
201
|
+
Item: {
|
|
202
|
+
pk: { S: "build#log" },
|
|
203
|
+
sk: { S: "001727000000123#user#42" },
|
|
204
|
+
ttl: { N: String(Math.ceil((at + aws_cache_store_1.REVALIDATION_LOG_TTL_MS) / 1000)) },
|
|
205
|
+
staleAt: { N: "5" },
|
|
206
|
+
expiredAt: { N: "6" },
|
|
207
|
+
},
|
|
208
|
+
ConditionExpression: "attribute_not_exists(sk)",
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
it("moves a row to the next millisecond rather than overwrite another revalidation", async () => {
|
|
212
|
+
// `updateTag('x')` and `revalidateTag('x', 'max')` in one millisecond.
|
|
213
|
+
const taken = Object.assign(new Error("taken"), {
|
|
214
|
+
name: "ConditionalCheckFailedException",
|
|
215
|
+
});
|
|
216
|
+
send.mockRejectedValueOnce(taken).mockResolvedValueOnce({});
|
|
217
|
+
await log.put("x", 1000.5, { revalidatedAt: 5 });
|
|
218
|
+
expect(send.mock.calls.map(([command]) => commandInput(command, client_dynamodb_1.PutItemCommand).Item.sk.S)).toEqual(["000000000001000#x", "000000000001001#x"]);
|
|
219
|
+
});
|
|
220
|
+
it("gives up after a bounded number of collisions, and on other errors at once", async () => {
|
|
221
|
+
const taken = Object.assign(new Error("taken"), {
|
|
222
|
+
name: "ConditionalCheckFailedException",
|
|
223
|
+
});
|
|
224
|
+
send.mockRejectedValue(taken);
|
|
225
|
+
await expect(log.put("x", 1000, {})).rejects.toBe(taken);
|
|
226
|
+
expect(send).toHaveBeenCalledTimes(5);
|
|
227
|
+
send.mockReset();
|
|
228
|
+
const throttled = new Error("throttled");
|
|
229
|
+
send.mockRejectedValue(throttled);
|
|
230
|
+
await expect(log.put("x", 1000, {})).rejects.toBe(throttled);
|
|
231
|
+
expect(send).toHaveBeenCalledTimes(1);
|
|
232
|
+
});
|
|
233
|
+
it("queries from a zero-padded cursor and parses the tag after it", async () => {
|
|
234
|
+
send
|
|
235
|
+
.mockResolvedValueOnce({
|
|
236
|
+
Items: [
|
|
237
|
+
{ sk: { S: "000000000001000#a" }, revalidatedAt: { N: "7" } },
|
|
238
|
+
{ sk: { S: "not a log row" } },
|
|
239
|
+
],
|
|
240
|
+
LastEvaluatedKey: { pk: { S: "build#log" }, sk: { S: "x" } },
|
|
241
|
+
})
|
|
242
|
+
.mockResolvedValueOnce({
|
|
243
|
+
Items: [{ sk: { S: "000000000002000#user#42" }, staleAt: { N: "8" } }],
|
|
244
|
+
});
|
|
245
|
+
const { rows, truncated } = await log.query(999.5);
|
|
246
|
+
const first = commandInput(send.mock.calls[0][0], client_dynamodb_1.QueryCommand);
|
|
247
|
+
expect(first).toMatchObject({
|
|
248
|
+
TableName: "tbl",
|
|
249
|
+
KeyConditionExpression: "pk = :pk AND sk >= :since",
|
|
250
|
+
ExpressionAttributeValues: {
|
|
251
|
+
":pk": { S: "build#log" },
|
|
252
|
+
":since": { S: "000000000000999" },
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
expect(commandInput(send.mock.calls[1][0], client_dynamodb_1.QueryCommand).ExclusiveStartKey).toEqual({ pk: { S: "build#log" }, sk: { S: "x" } });
|
|
256
|
+
expect(truncated).toBe(false);
|
|
257
|
+
expect(rows).toEqual([
|
|
258
|
+
{
|
|
259
|
+
sk: "000000000001000#a",
|
|
260
|
+
at: 1000,
|
|
261
|
+
tag: "a",
|
|
262
|
+
marker: { revalidatedAt: 7, staleAt: undefined, expiredAt: undefined },
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
sk: "000000000002000#user#42",
|
|
266
|
+
at: 2000,
|
|
267
|
+
tag: "user#42",
|
|
268
|
+
marker: { revalidatedAt: undefined, staleAt: 8, expiredAt: undefined },
|
|
269
|
+
},
|
|
270
|
+
]);
|
|
271
|
+
});
|
|
272
|
+
it("stops after its page bound and says so", async () => {
|
|
273
|
+
send.mockResolvedValue({
|
|
274
|
+
Items: [],
|
|
275
|
+
LastEvaluatedKey: { pk: { S: "build#log" }, sk: { S: "x" } },
|
|
276
|
+
});
|
|
277
|
+
expect((await log.query(0)).truncated).toBe(true);
|
|
278
|
+
expect(send).toHaveBeenCalledTimes(aws_cache_store_1.REVALIDATION_LOG_MAX_PAGES);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
describe("CacheBucket", () => {
|
|
282
|
+
const send = jest.fn();
|
|
283
|
+
let bucket;
|
|
284
|
+
beforeEach(() => {
|
|
285
|
+
send.mockReset();
|
|
286
|
+
client_s3_1.S3Client.mockImplementation(() => ({ send }));
|
|
287
|
+
bucket = new aws_cache_store_1.CacheBucket(new client_s3_1.S3Client({}), "bkt");
|
|
288
|
+
});
|
|
289
|
+
it("reads an object as text", async () => {
|
|
290
|
+
send.mockResolvedValue({
|
|
291
|
+
Body: { transformToString: jest.fn().mockResolvedValue("{}") },
|
|
292
|
+
ContentType: "application/json",
|
|
293
|
+
});
|
|
294
|
+
expect(await bucket.get("k")).toBe("{}");
|
|
295
|
+
expect(commandInput(send.mock.calls[0][0], client_s3_1.GetObjectCommand)).toEqual({
|
|
296
|
+
Bucket: "bkt",
|
|
297
|
+
Key: "k",
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
it("answers a missing object or body with undefined, and throws the rest", async () => {
|
|
301
|
+
send.mockRejectedValueOnce(new client_s3_1.NoSuchKey({ message: "missing", $metadata: {} }));
|
|
302
|
+
expect(await bucket.get("k")).toBeUndefined();
|
|
303
|
+
send.mockResolvedValueOnce({ Body: null });
|
|
304
|
+
expect(await bucket.get("k")).toBeUndefined();
|
|
305
|
+
send.mockRejectedValueOnce(new Error("AccessDenied"));
|
|
306
|
+
await expect(bucket.get("k")).rejects.toThrow("AccessDenied");
|
|
307
|
+
});
|
|
308
|
+
it("writes JSON", async () => {
|
|
309
|
+
send.mockResolvedValue({});
|
|
310
|
+
await bucket.putJson("k", "{}");
|
|
311
|
+
expect(commandInput(send.mock.calls[0][0], client_s3_1.PutObjectCommand)).toEqual({
|
|
312
|
+
Bucket: "bkt",
|
|
313
|
+
Key: "k",
|
|
314
|
+
Body: "{}",
|
|
315
|
+
ContentType: "application/json; charset=utf-8",
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
describe("TrackedTagMarkers", () => {
|
|
320
|
+
it("does not skip a row for an untracked tag that its first read missed", async () => {
|
|
321
|
+
// The writer puts the log row and the marker at the same time, and the
|
|
322
|
+
// marker read is eventually consistent: `refresh` can see the row, then
|
|
323
|
+
// `ensure` read the marker from before it. Marked applied while the tag
|
|
324
|
+
// was untracked, the row was skipped by every query the lookback returned
|
|
325
|
+
// it to, and the instance served the stale entry until the rolling re-read.
|
|
326
|
+
let clock = 1_000_000;
|
|
327
|
+
const revalidatedAt = 999_000;
|
|
328
|
+
const row = {
|
|
329
|
+
sk: `${String(clock).padStart(15, "0")}#posts`,
|
|
330
|
+
at: clock,
|
|
331
|
+
tag: "posts",
|
|
332
|
+
marker: { revalidatedAt },
|
|
333
|
+
};
|
|
334
|
+
const query = jest.fn(async (since) => ({
|
|
335
|
+
rows: [row].filter((r) => r.at >= since),
|
|
336
|
+
truncated: false,
|
|
337
|
+
}));
|
|
338
|
+
// A replica that has not seen the write yet.
|
|
339
|
+
const read = jest.fn(async () => new Map());
|
|
340
|
+
const markers = new aws_cache_store_1.TrackedTagMarkers({
|
|
341
|
+
markers: { read },
|
|
342
|
+
log: { query },
|
|
343
|
+
refreshIntervalMs: 1000,
|
|
344
|
+
clock: () => clock,
|
|
345
|
+
random: () => 0,
|
|
346
|
+
});
|
|
347
|
+
// Something tracked, so the refresh queries the log.
|
|
348
|
+
await markers.ensure(["other"]);
|
|
349
|
+
clock += 1000;
|
|
350
|
+
await markers.refresh();
|
|
351
|
+
await markers.ensure(["posts"]);
|
|
352
|
+
expect(markers.get("posts")).toEqual({});
|
|
353
|
+
clock += 1000;
|
|
354
|
+
await markers.refresh();
|
|
355
|
+
expect(query).toHaveBeenCalledTimes(2);
|
|
356
|
+
expect(markers.get("posts")).toEqual({ revalidatedAt });
|
|
357
|
+
// Only the one read each tag's first use costs.
|
|
358
|
+
expect(read).toHaveBeenCalledTimes(2);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXdzLWNhY2hlLXN0b3JlLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYWRhcHRlci9hd3MtY2FjaGUtc3RvcmUudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLHNEQUFzRDtBQUN0RCxJQUFJLENBQUMsSUFBSSxDQUFDLG9CQUFvQixDQUFDLENBQUM7QUFDaEMsSUFBSSxDQUFDLElBQUksQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO0FBRXRDLDhEQU1rQztBQUNsQyxrREFLNEI7QUFDNUIsdURBZ0IyQjtBQUUzQixnRUFBZ0U7QUFDaEUsTUFBTSxZQUFZLEdBQUcsQ0FBQyxPQUFnQixFQUFFLElBQWEsRUFBRSxFQUFFO0lBQ3ZELE1BQU0sSUFBSSxHQUFHLElBQWlCLENBQUM7SUFDL0IsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNsRSxDQUFDLENBQUM7QUFFRixRQUFRLENBQUMsdUJBQXVCLEVBQUUsR0FBRyxFQUFFO0lBQ3JDLE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUM7SUFDeEIsVUFBVSxDQUFDLEdBQUcsRUFBRTtRQUNkLE9BQU8sQ0FBQyxHQUFHLEdBQUcsRUFBRSxHQUFHLEdBQUcsRUFBRSxDQUFDO0lBQzNCLENBQUMsQ0FBQyxDQUFDO0lBQ0gsUUFBUSxDQUFDLEdBQUcsRUFBRTtRQUNaLE9BQU8sQ0FBQyxHQUFHLEdBQUcsR0FBRyxDQUFDO0lBQ3BCLENBQUMsQ0FBQyxDQUFDO0lBRUgsRUFBRSxDQUFDLDBDQUEwQyxFQUFFLEdBQUcsRUFBRTtRQUNsRCxPQUFPLENBQUMsR0FBRyxDQUFDLDRCQUE0QixHQUFHLFFBQVEsQ0FBQztRQUNwRCxPQUFPLENBQUMsR0FBRyxDQUFDLGtDQUFrQyxHQUFHLE9BQU8sQ0FBQztR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QUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQ2hDLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBRXpDLEtBQUssSUFBSSxJQUFJLENBQUM7UUFDZCxNQUFNLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUN4QixNQUFNLENBQUMsS0FBSyxDQUFDLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDdkMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsRUFBRSxhQUFhLEVBQUUsQ0FBQyxDQUFDO1FBQ3hELGdEQUFnRDtRQUNoRCxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDeEMsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llcyAqL1xuamVzdC5tb2NrKFwiQGF3cy1zZGsvY2xpZW50LXMzXCIpO1xuamVzdC5tb2NrKFwiQGF3cy1zZGsvY2xpZW50LWR5bmFtb2RiXCIpO1xuXG5pbXBvcnQge1xuICBCYXRjaEdldEl0ZW1Db21tYW5kLFxuICBEeW5hbW9EQkNsaWVudCxcbiAgUHV0SXRlbUNvbW1hbmQsXG4gIFF1ZXJ5Q29tbWFuZCxcbiAgVXBkYXRlSXRlbUNvbW1hbmQsXG59IGZyb20gXCJAYXdzLXNkay9jbGllbnQtZHluYW1vZGJcIjtcbmltcG9ydCB7XG4gIEdldE9iamVjdENvbW1hbmQsXG4gIE5vU3VjaEtleSxcbiAgUHV0T2JqZWN0Q29tbWFuZCxcbiAgUzNDbGllbnQsXG59IGZyb20gXCJAYXdzLXNkay9jbGllbnQtczNcIjtcbmltcG9ydCB7XG4gIGJ1aWxkUzNLZXksXG4gIENhY2hlQnVja2V0LFxuICBtYXJrZXJGb3IsXG4gIG1hcmtlclN0YXRlLFxuICBtYXJrZXJVcGRhdGUsXG4gIG1lcmdlTWFya2VycyxcbiAgcmVzb2x2ZUF3c0NhY2hlQ29uZmlnLFxuICBSRVZBTElEQVRJT05fTE9HX01BWF9QQUdFUyxcbiAgUkVWQUxJREFUSU9OX0xPR19UVExfTVMsXG4gIFJldmFsaWRhdGlvbkxvZyxcbiAgUmV2YWxpZGF0aW9uTG9nUm93LFxuICBUYWdNYXJrZXIsXG4gIFRhZ01hcmtlclRhYmxlLFxuICBUcmFja2VkVGFnTWFya2VycyxcbiAgdXNlQ2FjaGVTM0tleSxcbn0gZnJvbSBcIi4vYXdzLWNhY2hlLXN0b3JlXCI7XG5cbi8qKiBUaGUgaW5wdXQgYW4gYXV0b21vY2tlZCBTREsgY29tbWFuZCB3YXMgY29uc3RydWN0ZWQgd2l0aC4gKi9cbmNvbnN0IGNvbW1hbmRJbnB1dCA9IChjb21tYW5kOiB1bmtub3duLCB0eXBlOiB1bmtub3duKSA9PiB7XG4gIGNvbnN0IG1vY2sgPSB0eXBlIGFzIGplc3QuTW9jaztcbiAgcmV0dXJuIG1vY2subW9jay5jYWxsc1ttb2NrLm1vY2suaW5zdGFuY2VzLmluZGV4T2YoY29tbWFuZCldWzBdO1xufTtcblxuZGVzY3JpYmUoXCJyZXNvbHZlQXdzQ2FjaGVDb25maWdcIiwgKCkgPT4ge1xuICBjb25zdCBlbnYgPSBwcm9jZXNzLmVudjtcbiAgYmVmb3JlRWFjaCgoKSA9PiB7XG4gICAgcHJvY2Vzcy5lbnYgPSB7IC4uLmVudiB9O1xuICB9KTtcbiAgYWZ0ZXJBbGwoKCkgPT4ge1xuICAgIHByb2Nlc3MuZW52ID0gZW52O1xuICB9KTtcblxuICBpdChcInJlYWRzIHRoZSBlbnZpcm9ubWVudCB0aGUgY29uc3RydWN0cyBzZXRcIiwgKCkgPT4ge1xuICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQ0FDSEVfQlVDS0VUX05BTUUgPSBcImJ1Y2tldFwiO1xuICAgIHByb2Nlc3MuZW52LkNES19ORVhUSlNfUkVWQUxJREFUSU9OX1RBQkxFX05BTUUgPSBcInRhYmxlXCI7XG4gICAgcHJvY2Vzcy5lbnYuQ0RLX05FWFRKU19CVUlMRF9JRCA9IFwiYnVpbGRcIjtcbiAgICBwcm9jZXNzLmVudi5BV1NfUkVHSU9OID0gXCJldS13ZXN0LTFcIjtcbiAgICBleHBlY3QocmVzb2x2ZUF3c0NhY2hlQ29uZmlnKCkpLnRvRXF1YWwoe1xuICAgICAgYnVja2V0TmFtZTogXCJidWNrZXRcIixcbiAgICAgIHRhYmxlTmFtZTogXCJ0YWJsZVwiLFxuICAgICAgYnVpbGRJZDogXCJidWlsZFwiLFxuICAgICAgcmVnaW9uOiBcImV1LXdlc3QtMVwiLFxuICAgIH0pO1xuICB9KTtcblxuICBpdChcInJlcG9ydHMgd2hhdCBpcyB1bnNldCBhcyBlbXB0eVwiLCAoKSA9PiB7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQ0FDSEVfQlVDS0VUX05BTUU7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfUkVWQUxJREFUSU9OX1RBQkxFX05BTUU7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkNES19ORVhUSlNfQlVJTERfSUQ7XG4gICAgZGVsZXRlIHByb2Nlc3MuZW52LkFXU19SRUdJT047XG4gICAgZXhwZWN0KHJlc29sdmVBd3NDYWNoZUNvbmZpZygpKS50b0VxdWFsKHtcbiAgICAgIGJ1Y2tldE5hbWU6IFwiXCIsXG4gICAgICB0YWJsZU5hbWU6IFwiXCIsXG4gICAgICBidWlsZElkOiBcIlwiLFxuICAgICAgcmVnaW9uOiBcInVzLWVhc3QtMVwiLFxuICAgIH0pO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZShcIlMzIGtleXNcIiwgKCkgPT4ge1xuICBpdChcImtlZXBzIHRoZSBpbmNyZW1lbnRhbCBjYWNoZSdzIHtidWlsZElkfS97a2V5fS5qc29uIGNvbnZlbnRpb25cIiwgKCkgPT4ge1xuICAgIGV4cGVjdChidWlsZFMzS2V5KFwiYlwiLCBcIi9cIikpLnRvQmUoXCJiL2luZGV4Lmpzb25cIik7XG4gICAgZXhwZWN0KGJ1aWxkUzNLZXkoXCJiXCIsIFwiXCIpKS50b0JlKFwiYi9pbmRleC5qc29uXCIpO1xuICAgIGV4cGVjdChidWlsZFMzS2V5KFwiYlwiLCBcIi9ibG9nL3Bvc3RcIikpLnRvQmUoXCJiL2Jsb2cvcG9zdC5qc29uXCIpO1xuICAgIGV4cGVjdChidWlsZFMzS2V5KFwiYlwiLCBcImFiYzEyM1wiKSkudG9CZShcImIvYWJjMTIzLmpzb25cIik7XG4gIH0pO1xuXG4gIGl0KFwicHV0cyAndXNlIGNhY2hlJyBlbnRyaWVzIHdoZXJlIG5vIGluY3JlbWVudGFsIGtleSBjYW4gbGFuZFwiLCAoKSA9PiB7XG4gICAgY29uc3Qga2V5ID0gdXNlQ2FjaGVTM0tleShcImJcIiwgXCJmXCIucmVwZWF0KDY0KSk7XG4gICAgZXhwZWN0KGtleSkudG9CZShgYi9fdXNlLWNhY2hlLyR7XCJmXCIucmVwZWF0KDY0KX0uZW50cnlgKTtcbiAgICAvLyBFdmVuIGEgcm91dGUgc3BlbGxlZCBsaWtlIHRoZSB1c2UtY2FjaGUga2V5IGdldHMgYC5qc29uYCBhcHBlbmRlZC5cbiAgICBleHBlY3QoYnVpbGRTM0tleShcImJcIiwgYC9fdXNlLWNhY2hlLyR7XCJmXCIucmVwZWF0KDY0KX0uZW50cnlgKSkubm90LnRvQmUoXG4gICAgICBrZXksXG4gICAgKTtcbiAgICBleHBlY3Qoa2V5LmVuZHNXaXRoKFwiLmpzb25cIikpLnRvQmUoZmFsc2UpO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZShcInRhZyBtYXJrZXJzXCIsICgpID0+IHtcbiAgaXQoXCJ3cml0ZXMgd2hhdCBtYXJrZXJGb3IgYXBwbGllcyBsb2NhbGx5XCIsICgpID0+IHtcbiAgICBmb3IgKGNvbnN0IGR1cmF0aW9ucyBvZiBbdW5kZWZpbmVkLCB7fSwgeyBleHBpcmU6IDYwIH1dKSB7XG4gICAgICBjb25zdCB1cGRhdGUgPSBtYXJrZXJVcGRhdGUoMTAwMCwgZHVyYXRpb25zKTtcbiAgICAgIGNvbnN0IGxvY2FsID0gbWFya2VyRm9yKDEwMDAsIGR1cmF0aW9ucyk7XG4gICAgICBjb25zdCB3cml0dGVuOiBSZWNvcmQ8c3RyaW5nLCBudW1iZXI+ID0ge307XG4gICAgICBmb3IgKGNvbnN0IFtuYW1lLCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoXG4gICAgICAgIHVwZGF0ZS5FeHByZXNzaW9uQXR0cmlidXRlVmFsdWVzISxcbiAgICAgICkpIHtcbiAgICAgICAgd3JpdHRlbltuYW1lLnNsaWNlKDEpXSA9IE51bWJlcih2YWx1ZS5OKTtcbiAgICAgIH1cbiAgICAgIGV4cGVjdCh3cml0dGVuKS50b0VxdWFsKGxvY2FsKTtcbiAgICB9XG4gICAgZXhwZWN0KG1hcmtlckZvcigxMDAwLCB7IGV4cGlyZTogNjAgfSkpLnRvRXF1YWwoe1xuICAgICAgc3RhbGVBdDogMTAwMCxcbiAgICAgIGV4cGlyZWRBdDogNjEwMDAsXG4gICAgfSk7XG4gIH0pO1xuXG4gIGl0KFwia2VlcHMgYW4gZWFybGllciBleHBpcmVkQXQgYSBwcm9maWxlIHdpdGggbm8gZXhwaXJlIGxlYXZlc1wiLCAoKSA9PiB7XG4gICAgLy8gVGhlIHJvdyBrZWVwcyBpdCAoYFNFVCBzdGFsZUF0YCBvbmx5KSwgc28gYW4gaW5zdGFuY2UgbGVhcm5pbmcgb2YgdGhlXG4gICAgLy8gc2Vjb25kIHJldmFsaWRhdGlvbiBmcm9tIHRoZSBsb2cgaGFzIHRvIGtlZXAgaXQgdG9vLlxuICAgIGNvbnN0IGZpcnN0ID0gbWFya2VyRm9yKDEwMDAsIHsgZXhwaXJlOiA2MCB9KTtcbiAgICBjb25zdCBzZWNvbmQgPSBtYXJrZXJGb3IoMjAwMCwge30pO1xuICAgIGV4cGVjdChtZXJnZU1hcmtlcnMoZmlyc3QsIHNlY29uZCkpLnRvRXF1YWwoe1xuICAgICAgc3RhbGVBdDogMjAwMCxcbiAgICAgIGV4cGlyZWRBdDogNjEwMDAsXG4gICAgfSk7XG4gICAgZXhwZWN0KG1lcmdlTWFya2VycyhzZWNvbmQsIGZpcnN0KSkudG9FcXVhbCh7XG4gICAgICBzdGFsZUF0OiAyMDAwLFxuICAgICAgZXhwaXJlZEF0OiA2MTAwMCxcbiAgICB9KTtcbiAgICAvLyBBIG5ld2VyIGBleHBpcmVgIHN0aWxsIHJlcGxhY2VzIGl0LlxuICAgIGV4cGVjdChtZXJnZU1hcmtlcnMoZmlyc3QsIG1hcmtlckZvcigyMDAwLCB7IGV4cGlyZTogMSB9KSkpLnRvRXF1YWwoe1xuICAgICAgc3RhbGVBdDogMjAwMCxcbiAgICAgIGV4cGlyZWRBdDogMzAwMCxcbiAgICB9KTtcbiAgfSk7XG5cbiAgaXQoXCJqdWRnZXMgYW4gZW50cnkgdGhlIHdheSB0aGUgaW5jcmVtZW50YWwgY2FjaGUgYWx3YXlzIGhhc1wiLCAoKSA9PiB7XG4gICAgLy8gRXhwaXJlZCBvdXRyaWdodCBhZnRlciBjcmVhdGlvbi5cbiAgICBleHBlY3QobWFya2VyU3RhdGUoeyByZXZhbGlkYXRlZEF0OiAyMCB9LCAxMCwgMzApKS50b0JlKFwiZXhwaXJlZFwiKTtcbiAgICAvLyBSZXZhbGlkYXRlZCBiZWZvcmUgdGhlIGVudHJ5IHdhcyBjcmVhdGVkOiBubyBlZmZlY3QuXG4gICAgZXhwZWN0KG1hcmtlclN0YXRlKHsgcmV2YWxpZGF0ZWRBdDogNSB9LCAxMCwgMzApKS50b0JlKFwiZnJlc2hcIik7XG4gICAgLy8gQSBwcm9maWxlOiBzdGFsZSBub3csIGV4cGlyZWQgb25jZSBgZXhwaXJlZEF0YCBwYXNzZXMuXG4gICAgZXhwZWN0KG1hcmtlclN0YXRlKHsgc3RhbGVBdDogMjAsIGV4cGlyZWRBdDogNTAgfSwgMTAsIDMwKSkudG9CZShcInN0YWxlXCIpO1xuICAgIGV4cGVjdChtYXJrZXJTdGF0ZSh7IHN0YWxlQXQ6IDIwLCBleHBpcmVkQXQ6IDUwIH0sIDEwLCA2MCkpLnRvQmUoXCJleHBpcmVkXCIpO1xuICAgIC8vIGBleHBpcmU6IDBgOiBzdGFsZSBhbmQgZXhwaXJlZCBhdCBvbmNlLlxuICAgIGV4cGVjdChtYXJrZXJTdGF0ZSh7IHN0YWxlQXQ6IDIwLCBleHBpcmVkQXQ6IDIwIH0sIDEwLCAzMCkpLnRvQmUoXCJleHBpcmVkXCIpO1xuICAgIGV4cGVjdChtYXJrZXJTdGF0ZSh7fSwgMTAsIDMwKSkudG9CZShcImZyZXNoXCIpO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZShcIlRhZ01hcmtlclRhYmxlXCIsICgpID0+IHtcbiAgY29uc3Qgc2VuZCA9IGplc3QuZm4oKTtcbiAgbGV0IHRhYmxlOiBUYWdNYXJrZXJUYWJsZTtcblxuICBiZWZvcmVFYWNoKCgpID0+IHtcbiAgICBzZW5kLm1vY2tSZXNldCgpO1xuICAgIChEeW5hbW9EQkNsaWVudCBhcyBqZXN0Lk1vY2spLm1vY2tJbXBsZW1lbnRhdGlvbigoKSA9PiAoeyBzZW5kIH0pKTtcbiAgICB0YWJsZSA9IG5ldyBUYWdNYXJrZXJUYWJsZShuZXcgRHluYW1vREJDbGllbnQoe30pLCBcInRibFwiLCBcImJ1aWxkXCIpO1xuICB9KTtcblxuICBpdChcIndyaXRlcyB0aGUgbWFya2VyIHJvdyB1bmRlciB0aGUgYnVpbGRcIiwgYXN5bmMgKCkgPT4ge1xuICAgIHNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuICAgIGF3YWl0IHRhYmxlLndyaXRlKFwicG9zdHNcIiwgMTAwMCwgeyBleHBpcmU6IDEgfSk7XG4gICAgY29uc3QgaW5wdXQgPSBjb21tYW5kSW5wdXQoc2VuZC5tb2NrLmNhbGxzWzBdWzBdLCBVcGRhdGVJdGVtQ29tbWFuZCk7XG4gICAgZXhwZWN0KGlucHV0KS50b0VxdWFsKHtcbiAgICAgIFRhYmxlTmFtZTogXCJ0YmxcIixcbiAgICAgIEtleTogeyBwazogeyBTOiBcImJ1aWxkXCIgfSwgc2s6IHsgUzogXCJwb3N0c1wiIH0gfSxcbiAgICAgIFVwZGF0ZUV4cHJlc3Npb246IFwiU0VUIHN0YWxlQXQgPSA6c3RhbGVBdCwgZXhwaXJlZEF0ID0gOmV4cGlyZWRBdFwiLFxuICAgICAgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlczoge1xuICAgICAgICBcIjpzdGFsZUF0XCI6IHsgTjogXCIxMDAwXCIgfSxcbiAgICAgICAgXCI6ZXhwaXJlZEF0XCI6IHsgTjogXCIyMDAwXCIgfSxcbiAgICAgIH0sXG4gICAgICBSZXR1cm5WYWx1ZXM6IFwiQUxMX05FV1wiLFxuICAgIH0pO1xuICB9KTtcblxuICBpdChcInJlYWRzIG1hcmtlcnMgaW4gYmF0Y2hlcyBvZiAxMDAgYW5kIHBhcnNlcyB0aGVtXCIsIGFzeW5jICgpID0+IHtcbiAgICBzZW5kLm1vY2tJbXBsZW1lbnRhdGlvbigoY29tbWFuZDogdW5rbm93bikgPT4ge1xuICAgICAgY29uc3QgeyBSZXF1ZXN0SXRlbXMgfSA9IGNvbW1hbmRJbnB1dChjb21tYW5kLCBCYXRjaEdldEl0ZW1Db21tYW5kKTtcbiAgICAgIGNvbnN0IGtleXMgPSBSZXF1ZXN0SXRlbXMudGJsLktleXMgYXMgeyBzazogeyBTOiBzdHJpbmcgfSB9W107XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHtcbiAgICAgICAgUmVzcG9uc2VzOiB7XG4gICAgICAgICAgdGJsOiBrZXlzXG4gICAgICAgICAgICAuZmlsdGVyKCh7IHNrIH0pID0+IHNrLlMuZW5kc1dpdGgoXCIwXCIpKVxuICAgICAgICAgICAgLm1hcCgoeyBzayB9KSA9PiAoeyBzaywgcmV2YWxpZGF0ZWRBdDogeyBOOiBcIjdcIiB9IH0pKSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuICAgIH0pO1xuICAgIGNvbnN0IHRhZ3MgPSBBcnJheS5mcm9tKHsgbGVuZ3RoOiAxNTAgfSwgKF8sIGkpID0+IGB0JHtpfWApO1xuICAgIGNvbnN0IG1hcmtlcnMgPSBhd2FpdCB0YWJsZS5yZWFkKFsuLi50YWdzLCBcInQwXCJdKTtcbiAgICBleHBlY3Qoc2VuZCkudG9IYXZlQmVlbkNhbGxlZFRpbWVzKDIpO1xuICAgIGV4cGVjdChtYXJrZXJzLnNpemUpLnRvQmUoMTUpO1xuICAgIGV4cGVjdChtYXJrZXJzLmdldChcInQxMFwiKSkudG9FcXVhbCh7XG4gICAgICByZXZhbGlkYXRlZEF0OiA3LFxuICAgICAgc3RhbGVBdDogdW5kZWZpbmVkLFxuICAgICAgZXhwaXJlZEF0OiB1bmRlZmluZWQsXG4gICAgfSk7XG4gIH0pO1xuXG4gIC8vIEEgcmVhZC1iYWNrIGFmdGVyIHRoZSB3cml0ZSBpcyBldmVudHVhbGx5IGNvbnNpc3RlbnQsIHNvIGl0IGNhbiByZXR1cm5cbiAgLy8gdGhlIG1hcmtlciBmcm9tIGJlZm9yZSBpdDogdGhlIHdyaXRlciB0cmFja3Mgd2hhdCB0aGUgd3JpdGUgcmV0dXJuZWQuXG4gIGl0KFwicmV0dXJucyB0aGUgcm93IGFzIHRoZSB3cml0ZSBsZWZ0IGl0XCIsIGFzeW5jICgpID0+IHtcbiAgICBzZW5kLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICBBdHRyaWJ1dGVzOiB7XG4gICAgICAgIHNrOiB7IFM6IFwicG9zdHNcIiB9LFxuICAgICAgICByZXZhbGlkYXRlZEF0OiB7IE46IFwiNTAwMFwiIH0sXG4gICAgICAgIHN0YWxlQXQ6IHsgTjogXCIxMFwiIH0sXG4gICAgICB9LFxuICAgIH0pO1xuICAgIGV4cGVjdChhd2FpdCB0YWJsZS53cml0ZShcInBvc3RzXCIsIDUwMDAsIHVuZGVmaW5lZCkpLnRvRXF1YWwoe1xuICAgICAgcmV2YWxpZGF0ZWRBdDogNTAwMCxcbiAgICAgIHN0YWxlQXQ6IDEwLFxuICAgICAgZXhwaXJlZEF0OiB1bmRlZmluZWQsXG4gICAgfSk7XG4gICAgc2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe30pO1xuICAgIGV4cGVjdChhd2FpdCB0YWJsZS53cml0ZShcInBvc3RzXCIsIDYwMDAsIHVuZGVmaW5lZCkpLnRvQmVVbmRlZmluZWQoKTtcbiAgfSk7XG5cbiAgaXQoXCJhc2tzIGFnYWluIGZvciB1bnByb2Nlc3NlZCBrZXlzLCBhbmQgZ2l2ZXMgdXAgYWZ0ZXIgdGhyZWUgdHJpZXNcIiwgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHVucHJvY2Vzc2VkID0ge1xuICAgICAgVW5wcm9jZXNzZWRLZXlzOiB7XG4gICAgICAgIHRibDogeyBLZXlzOiBbeyBwazogeyBTOiBcImJ1aWxkXCIgfSwgc2s6IHsgUzogXCJhXCIgfSB9XSB9LFxuICAgICAgfSxcbiAgICB9O1xuICAgIHNlbmQubW9ja1Jlc29sdmVkVmFsdWVPbmNlKHVucHJvY2Vzc2VkKS5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe1xuICAgICAgUmVzcG9uc2VzOiB7IHRibDogW3sgc2s6IHsgUzogXCJhXCIgfSwgc3RhbGVBdDogeyBOOiBcIjNcIiB9IH1dIH0sXG4gICAgfSk7XG4gICAgZXhwZWN0KChhd2FpdCB0YWJsZS5yZWFkKFtcImFcIl0pKS5nZXQoXCJhXCIpPy5zdGFsZUF0KS50b0JlKDMpO1xuXG4gICAgc2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh1bnByb2Nlc3NlZCk7XG4gICAgYXdhaXQgZXhwZWN0KHRhYmxlLnJlYWQoW1wiYVwiXSkpLnJlamVjdHMudG9UaHJvdygvdW5yZWFkIGFmdGVyIHJldHJ5aW5nLyk7XG4gIH0pO1xufSk7XG5cbmRlc2NyaWJlKFwiUmV2YWxpZGF0aW9uTG9nXCIsICgpID0+IHtcbiAgY29uc3Qgc2VuZCA9IGplc3QuZm4oKTtcbiAgbGV0IGxvZzogUmV2YWxpZGF0aW9uTG9nO1xuXG4gIGJlZm9yZUVhY2goKCkgPT4ge1xuICAgIHNlbmQubW9ja1Jlc2V0KCk7XG4gICAgKER5bmFtb0RCQ2xpZW50IGFzIGplc3QuTW9jaykubW9ja0ltcGxlbWVudGF0aW9uKCgpID0+ICh7IHNlbmQgfSkpO1xuICAgIGxvZyA9IG5ldyBSZXZhbGlkYXRpb25Mb2cobmV3IER5bmFtb0RCQ2xpZW50KHt9KSwgXCJ0YmxcIiwgXCJidWlsZFwiKTtcbiAgfSk7XG5cbiAgaXQoXCJwdXRzIG9uZSByb3cgcGVyIHJldmFsaWRhdGlvbiB1bmRlciB0aGUgYnVpbGQncyBsb2csIHdpdGggYSBUVExcIiwgYXN5bmMgKCkgPT4ge1xuICAgIHNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuICAgIGNvbnN0IGF0ID0gMV83MjdfMDAwXzAwMF8xMjM7XG4gICAgYXdhaXQgbG9nLnB1dChcInVzZXIjNDJcIiwgYXQsIHsgc3RhbGVBdDogNSwgZXhwaXJlZEF0OiA2IH0pO1xuICAgIGV4cGVjdChjb21tYW5kSW5wdXQoc2VuZC5tb2NrLmNhbGxzWzBdWzBdLCBQdXRJdGVtQ29tbWFuZCkpLnRvRXF1YWwoe1xuICAgICAgVGFibGVOYW1lOiBcInRibFwiLFxuICAgICAgSXRlbToge1xuICAgICAgICBwazogeyBTOiBcImJ1aWxkI2xvZ1wiIH0sXG4gICAgICAgIHNrOiB7IFM6IFwiMDAxNzI3MDAwMDAwMTIzI3VzZXIjNDJcIiB9LFxuICAgICAgICB0dGw6IHsgTjogU3RyaW5nKE1hdGguY2VpbCgoYXQgKyBSRVZBTElEQVRJT05fTE9HX1RUTF9NUykgLyAxMDAwKSkgfSxcbiAgICAgICAgc3RhbGVBdDogeyBOOiBcIjVcIiB9LFxuICAgICAgICBleHBpcmVkQXQ6IHsgTjogXCI2XCIgfSxcbiAgICAgIH0sXG4gICAgICBDb25kaXRpb25FeHByZXNzaW9uOiBcImF0dHJpYnV0ZV9ub3RfZXhpc3RzKHNrKVwiLFxuICAgIH0pO1xuICB9KTtcblxuICBpdChcIm1vdmVzIGEgcm93IHRvIHRoZSBuZXh0IG1pbGxpc2Vjb25kIHJhdGhlciB0aGFuIG92ZXJ3cml0ZSBhbm90aGVyIHJldmFsaWRhdGlvblwiLCBhc3luYyAoKSA9PiB7XG4gICAgLy8gYHVwZGF0ZVRhZygneCcpYCBhbmQgYHJldmFsaWRhdGVUYWcoJ3gnLCAnbWF4JylgIGluIG9uZSBtaWxsaXNlY29uZC5cbiAgICBjb25zdCB0YWtlbiA9IE9iamVjdC5hc3NpZ24obmV3IEVycm9yKFwidGFrZW5cIiksIHtcbiAgICAgIG5hbWU6IFwiQ29uZGl0aW9uYWxDaGVja0ZhaWxlZEV4Y2VwdGlvblwiLFxuICAgIH0pO1xuICAgIHNlbmQubW9ja1JlamVjdGVkVmFsdWVPbmNlKHRha2VuKS5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2Uoe30pO1xuICAgIGF3YWl0IGxvZy5wdXQoXCJ4XCIsIDEwMDAuNSwgeyByZXZhbGlkYXRlZEF0OiA1IH0pO1xuICAgIGV4cGVjdChcbiAgICAgIHNlbmQubW9jay5jYWxscy5tYXAoXG4gICAgICAgIChbY29tbWFuZF0pID0+IGNvbW1hbmRJbnB1dChjb21tYW5kLCBQdXRJdGVtQ29tbWFuZCkuSXRlbS5zay5TLFxuICAgICAgKSxcbiAgICApLnRvRXF1YWwoW1wiMDAwMDAwMDAwMDAxMDAwI3hcIiwgXCIwMDAwMDAwMDAwMDEwMDEjeFwiXSk7XG4gIH0pO1xuXG4gIGl0KFwiZ2l2ZXMgdXAgYWZ0ZXIgYSBib3VuZGVkIG51bWJlciBvZiBjb2xsaXNpb25zLCBhbmQgb24gb3RoZXIgZXJyb3JzIGF0IG9uY2VcIiwgYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IHRha2VuID0gT2JqZWN0LmFzc2lnbihuZXcgRXJyb3IoXCJ0YWtlblwiKSwge1xuICAgICAgbmFtZTogXCJDb25kaXRpb25hbENoZWNrRmFpbGVkRXhjZXB0aW9uXCIsXG4gICAgfSk7XG4gICAgc2VuZC5tb2NrUmVqZWN0ZWRWYWx1ZSh0YWtlbik7XG4gICAgYXdhaXQgZXhwZWN0KGxvZy5wdXQoXCJ4XCIsIDEwMDAsIHt9KSkucmVqZWN0cy50b0JlKHRha2VuKTtcbiAgICBleHBlY3Qoc2VuZCkudG9IYXZlQmVlbkNhbGxlZFRpbWVzKDUpO1xuXG4gICAgc2VuZC5tb2NrUmVzZXQoKTtcbiAgICBjb25zdCB0aHJvdHRsZWQgPSBuZXcgRXJyb3IoXCJ0aHJvdHRsZWRcIik7XG4gICAgc2VuZC5tb2NrUmVqZWN0ZWRWYWx1ZSh0aHJvdHRsZWQpO1xuICAgIGF3YWl0IGV4cGVjdChsb2cucHV0KFwieFwiLCAxMDAwLCB7fSkpLnJlamVjdHMudG9CZSh0aHJvdHRsZWQpO1xuICAgIGV4cGVjdChzZW5kKS50b0hhdmVCZWVuQ2FsbGVkVGltZXMoMSk7XG4gIH0pO1xuXG4gIGl0KFwicXVlcmllcyBmcm9tIGEgemVyby1wYWRkZWQgY3Vyc29yIGFuZCBwYXJzZXMgdGhlIHRhZyBhZnRlciBpdFwiLCBhc3luYyAoKSA9PiB7XG4gICAgc2VuZFxuICAgICAgLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICAgIEl0ZW1zOiBbXG4gICAgICAgICAgeyBzazogeyBTOiBcIjAwMDAwMDAwMDAwMTAwMCNhXCIgfSwgcmV2YWxpZGF0ZWRBdDogeyBOOiBcIjdcIiB9IH0sXG4gICAgICAgICAgeyBzazogeyBTOiBcIm5vdCBhIGxvZyByb3dcIiB9IH0sXG4gICAgICAgIF0sXG4gICAgICAgIExhc3RFdmFsdWF0ZWRLZXk6IHsgcGs6IHsgUzogXCJidWlsZCNsb2dcIiB9LCBzazogeyBTOiBcInhcIiB9IH0sXG4gICAgICB9KVxuICAgICAgLm1vY2tSZXNvbHZlZFZhbHVlT25jZSh7XG4gICAgICAgIEl0ZW1zOiBbeyBzazogeyBTOiBcIjAwMDAwMDAwMDAwMjAwMCN1c2VyIzQyXCIgfSwgc3RhbGVBdDogeyBOOiBcIjhcIiB9IH1dLFxuICAgICAgfSk7XG4gICAgY29uc3QgeyByb3dzLCB0cnVuY2F0ZWQgfSA9IGF3YWl0IGxvZy5xdWVyeSg5OTkuNSk7XG5cbiAgICBjb25zdCBmaXJzdCA9IGNvbW1hbmRJbnB1dChzZW5kLm1vY2suY2FsbHNbMF1bMF0sIFF1ZXJ5Q29tbWFuZCk7XG4gICAgZXhwZWN0KGZpcnN0KS50b01hdGNoT2JqZWN0KHtcbiAgICAgIFRhYmxlTmFtZTogXCJ0YmxcIixcbiAgICAgIEtleUNvbmRpdGlvbkV4cHJlc3Npb246IFwicGsgPSA6cGsgQU5EIHNrID49IDpzaW5jZVwiLFxuICAgICAgRXhwcmVzc2lvbkF0dHJpYnV0ZVZhbHVlczoge1xuICAgICAgICBcIjpwa1wiOiB7IFM6IFwiYnVpbGQjbG9nXCIgfSxcbiAgICAgICAgXCI6c2luY2VcIjogeyBTOiBcIjAwMDAwMDAwMDAwMDk5OVwiIH0sXG4gICAgICB9LFxuICAgIH0pO1xuICAgIGV4cGVjdChcbiAgICAgIGNvbW1hbmRJbnB1dChzZW5kLm1vY2suY2FsbHNbMV1bMF0sIFF1ZXJ5Q29tbWFuZCkuRXhjbHVzaXZlU3RhcnRLZXksXG4gICAgKS50b0VxdWFsKHsgcGs6IHsgUzogXCJidWlsZCNsb2dcIiB9LCBzazogeyBTOiBcInhcIiB9IH0pO1xuICAgIGV4cGVjdCh0cnVuY2F0ZWQpLnRvQmUoZmFsc2UpO1xuICAgIGV4cGVjdChyb3dzKS50b0VxdWFsKFtcbiAgICAgIHtcbiAgICAgICAgc2s6IFwiMDAwMDAwMDAwMDAxMDAwI2FcIixcbiAgICAgICAgYXQ6IDEwMDAsXG4gICAgICAgIHRhZzogXCJhXCIsXG4gICAgICAgIG1hcmtlcjogeyByZXZhbGlkYXRlZEF0OiA3LCBzdGFsZUF0OiB1bmRlZmluZWQsIGV4cGlyZWRBdDogdW5kZWZpbmVkIH0sXG4gICAgICB9LFxuICAgICAge1xuICAgICAgICBzazogXCIwMDAwMDAwMDAwMDIwMDAjdXNlciM0MlwiLFxuICAgICAgICBhdDogMjAwMCxcbiAgICAgICAgdGFnOiBcInVzZXIjNDJcIixcbiAgICAgICAgbWFya2VyOiB7IHJldmFsaWRhdGVkQXQ6IHVuZGVmaW5lZCwgc3RhbGVBdDogOCwgZXhwaXJlZEF0OiB1bmRlZmluZWQgfSxcbiAgICAgIH0sXG4gICAgXSk7XG4gIH0pO1xuXG4gIGl0KFwic3RvcHMgYWZ0ZXIgaXRzIHBhZ2UgYm91bmQgYW5kIHNheXMgc29cIiwgYXN5bmMgKCkgPT4ge1xuICAgIHNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgSXRlbXM6IFtdLFxuICAgICAgTGFzdEV2YWx1YXRlZEtleTogeyBwazogeyBTOiBcImJ1aWxkI2xvZ1wiIH0sIHNrOiB7IFM6IFwieFwiIH0gfSxcbiAgICB9KTtcbiAgICBleHBlY3QoKGF3YWl0IGxvZy5xdWVyeSgwKSkudHJ1bmNhdGVkKS50b0JlKHRydWUpO1xuICAgIGV4cGVjdChzZW5kKS50b0hhdmVCZWVuQ2FsbGVkVGltZXMoUkVWQUxJREFUSU9OX0xPR19NQVhfUEFHRVMpO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZShcIkNhY2hlQnVja2V0XCIsICgpID0+IHtcbiAgY29uc3Qgc2VuZCA9IGplc3QuZm4oKTtcbiAgbGV0IGJ1Y2tldDogQ2FjaGVCdWNrZXQ7XG5cbiAgYmVmb3JlRWFjaCgoKSA9PiB7XG4gICAgc2VuZC5tb2NrUmVzZXQoKTtcbiAgICAoUzNDbGllbnQgYXMgamVzdC5Nb2NrKS5tb2NrSW1wbGVtZW50YXRpb24oKCkgPT4gKHsgc2VuZCB9KSk7XG4gICAgYnVja2V0ID0gbmV3IENhY2hlQnVja2V0KG5ldyBTM0NsaWVudCh7fSksIFwiYmt0XCIpO1xuICB9KTtcblxuICBpdChcInJlYWRzIGFuIG9iamVjdCBhcyB0ZXh0XCIsIGFzeW5jICgpID0+IHtcbiAgICBzZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHtcbiAgICAgIEJvZHk6IHsgdHJhbnNmb3JtVG9TdHJpbmc6IGplc3QuZm4oKS5tb2NrUmVzb2x2ZWRWYWx1ZShcInt9XCIpIH0sXG4gICAgICBDb250ZW50VHlwZTogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgfSk7XG4gICAgZXhwZWN0KGF3YWl0IGJ1Y2tldC5nZXQoXCJrXCIpKS50b0JlKFwie31cIik7XG4gICAgZXhwZWN0KGNvbW1hbmRJbnB1dChzZW5kLm1vY2suY2FsbHNbMF1bMF0sIEdldE9iamVjdENvbW1hbmQpKS50b0VxdWFsKHtcbiAgICAgIEJ1Y2tldDogXCJia3RcIixcbiAgICAgIEtleTogXCJrXCIsXG4gICAgfSk7XG4gIH0pO1xuXG4gIGl0KFwiYW5zd2VycyBhIG1pc3Npbmcgb2JqZWN0IG9yIGJvZHkgd2l0aCB1bmRlZmluZWQsIGFuZCB0aHJvd3MgdGhlIHJlc3RcIiwgYXN5bmMgKCkgPT4ge1xuICAgIHNlbmQubW9ja1JlamVjdGVkVmFsdWVPbmNlKFxuICAgICAgbmV3IE5vU3VjaEtleSh7IG1lc3NhZ2U6IFwibWlzc2luZ1wiLCAkbWV0YWRhdGE6IHt9IH0pLFxuICAgICk7XG4gICAgZXhwZWN0KGF3YWl0IGJ1Y2tldC5nZXQoXCJrXCIpKS50b0JlVW5kZWZpbmVkKCk7XG4gICAgc2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZU9uY2UoeyBCb2R5OiBudWxsIH0pO1xuICAgIGV4cGVjdChhd2FpdCBidWNrZXQuZ2V0KFwia1wiKSkudG9CZVVuZGVmaW5lZCgpO1xuICAgIHNlbmQubW9ja1JlamVjdGVkVmFsdWVPbmNlKG5ldyBFcnJvcihcIkFjY2Vzc0RlbmllZFwiKSk7XG4gICAgYXdhaXQgZXhwZWN0KGJ1Y2tldC5nZXQoXCJrXCIpKS5yZWplY3RzLnRvVGhyb3coXCJBY2Nlc3NEZW5pZWRcIik7XG4gIH0pO1xuXG4gIGl0KFwid3JpdGVzIEpTT05cIiwgYXN5bmMgKCkgPT4ge1xuICAgIHNlbmQubW9ja1Jlc29sdmVkVmFsdWUoe30pO1xuICAgIGF3YWl0IGJ1Y2tldC5wdXRKc29uKFwia1wiLCBcInt9XCIpO1xuICAgIGV4cGVjdChjb21tYW5kSW5wdXQoc2VuZC5tb2NrLmNhbGxzWzBdWzBdLCBQdXRPYmplY3RDb21tYW5kKSkudG9FcXVhbCh7XG4gICAgICBCdWNrZXQ6IFwiYmt0XCIsXG4gICAgICBLZXk6IFwia1wiLFxuICAgICAgQm9keTogXCJ7fVwiLFxuICAgICAgQ29udGVudFR5cGU6IFwiYXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOFwiLFxuICAgIH0pO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZShcIlRyYWNrZWRUYWdNYXJrZXJzXCIsICgpID0+IHtcbiAgaXQoXCJkb2VzIG5vdCBza2lwIGEgcm93IGZvciBhbiB1bnRyYWNrZWQgdGFnIHRoYXQgaXRzIGZpcnN0IHJlYWQgbWlzc2VkXCIsIGFzeW5jICgpID0+IHtcbiAgICAvLyBUaGUgd3JpdGVyIHB1dHMgdGhlIGxvZyByb3cgYW5kIHRoZSBtYXJrZXIgYXQgdGhlIHNhbWUgdGltZSwgYW5kIHRoZVxuICAgIC8vIG1hcmtlciByZWFkIGlzIGV2ZW50dWFsbHkgY29uc2lzdGVudDogYHJlZnJlc2hgIGNhbiBzZWUgdGhlIHJvdywgdGhlblxuICAgIC8vIGBlbnN1cmVgIHJlYWQgdGhlIG1hcmtlciBmcm9tIGJlZm9yZSBpdC4gTWFya2VkIGFwcGxpZWQgd2hpbGUgdGhlIHRhZ1xuICAgIC8vIHdhcyB1bnRyYWNrZWQsIHRoZSByb3cgd2FzIHNraXBwZWQgYnkgZXZlcnkgcXVlcnkgdGhlIGxvb2tiYWNrIHJldHVybmVkXG4gICAgLy8gaXQgdG8sIGFuZCB0aGUgaW5zdGFuY2Ugc2VydmVkIHRoZSBzdGFsZSBlbnRyeSB1bnRpbCB0aGUgcm9sbGluZyByZS1yZWFkLlxuICAgIGxldCBjbG9jayA9IDFfMDAwXzAwMDtcbiAgICBjb25zdCByZXZhbGlkYXRlZEF0ID0gOTk5XzAwMDtcbiAgICBjb25zdCByb3c6IFJldmFsaWRhdGlvbkxvZ1JvdyA9IHtcbiAgICAgIHNrOiBgJHtTdHJpbmcoY2xvY2spLnBhZFN0YXJ0KDE1LCBcIjBcIil9I3Bvc3RzYCxcbiAgICAgIGF0OiBjbG9jayxcbiAgICAgIHRhZzogXCJwb3N0c1wiLFxuICAgICAgbWFya2VyOiB7IHJldmFsaWRhdGVkQXQgfSxcbiAgICB9O1xuICAgIGNvbnN0IHF1ZXJ5ID0gamVzdC5mbihhc3luYyAoc2luY2U6IG51bWJlcikgPT4gKHtcbiAgICAgIHJvd3M6IFtyb3ddLmZpbHRlcigocikgPT4gci5hdCA+PSBzaW5jZSksXG4gICAgICB0cnVuY2F0ZWQ6IGZhbHNlLFxuICAgIH0pKTtcbiAgICAvLyBBIHJlcGxpY2EgdGhhdCBoYXMgbm90IHNlZW4gdGhlIHdyaXRlIHlldC5cbiAgICBjb25zdCByZWFkID0gamVzdC5mbihhc3luYyAoKSA9PiBuZXcgTWFwPHN0cmluZywgVGFnTWFya2VyPigpKTtcbiAgICBjb25zdCBtYXJrZXJzID0gbmV3IFRyYWNrZWRUYWdNYXJrZXJzKHtcbiAgICAgIG1hcmtlcnM6IHsgcmVhZCB9IGFzIHVua25vd24gYXMgVGFnTWFya2VyVGFibGUsXG4gICAgICBsb2c6IHsgcXVlcnkgfSBhcyB1bmtub3duIGFzIFJldmFsaWRhdGlvbkxvZyxcbiAgICAgIHJlZnJlc2hJbnRlcnZhbE1zOiAxMDAwLFxuICAgICAgY2xvY2s6ICgpID0+IGNsb2NrLFxuICAgICAgcmFuZG9tOiAoKSA9PiAwLFxuICAgIH0pO1xuICAgIC8vIFNvbWV0aGluZyB0cmFja2VkLCBzbyB0aGUgcmVmcmVzaCBxdWVyaWVzIHRoZSBsb2cuXG4gICAgYXdhaXQgbWFya2Vycy5lbnN1cmUoW1wib3RoZXJcIl0pO1xuXG4gICAgY2xvY2sgKz0gMTAwMDtcbiAgICBhd2FpdCBtYXJrZXJzLnJlZnJlc2goKTtcbiAgICBhd2FpdCBtYXJrZXJzLmVuc3VyZShbXCJwb3N0c1wiXSk7XG4gICAgZXhwZWN0KG1hcmtlcnMuZ2V0KFwicG9zdHNcIikpLnRvRXF1YWwoe30pO1xuXG4gICAgY2xvY2sgKz0gMTAwMDtcbiAgICBhd2FpdCBtYXJrZXJzLnJlZnJlc2goKTtcbiAgICBleHBlY3QocXVlcnkpLnRvSGF2ZUJlZW5DYWxsZWRUaW1lcygyKTtcbiAgICBleHBlY3QobWFya2Vycy5nZXQoXCJwb3N0c1wiKSkudG9FcXVhbCh7IHJldmFsaWRhdGVkQXQgfSk7XG4gICAgLy8gT25seSB0aGUgb25lIHJlYWQgZWFjaCB0YWcncyBmaXJzdCB1c2UgY29zdHMuXG4gICAgZXhwZWN0KHJlYWQpLnRvSGF2ZUJlZW5DYWxsZWRUaW1lcygyKTtcbiAgfSk7XG59KTtcbiJdfQ==
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { NextAdapter } from "next";
|
|
2
|
+
import { FunctionGroupSpec } from "./function-groups";
|
|
3
|
+
import { AdapterManifest } from "../runtime/manifest";
|
|
4
|
+
/**
|
|
5
|
+
* The `onBuildComplete` argument. Derived from the `next` typings rather than
|
|
6
|
+
* restated so a `next` bump surfaces as a compile error here.
|
|
7
|
+
*/
|
|
8
|
+
export type BuildCompleteContext = Parameters<NonNullable<NextAdapter["onBuildComplete"]>>[0];
|
|
9
|
+
/**
|
|
10
|
+
* Staging key (repo-root-relative POSIX) → absolute source path on the build
|
|
11
|
+
* machine. The deduped union that replaces the standalone tree.
|
|
12
|
+
*/
|
|
13
|
+
export type StagingPlan = ReadonlyMap<string, string>;
|
|
14
|
+
export interface BuildOutputsOptions {
|
|
15
|
+
/**
|
|
16
|
+
* The directory `next build` was invoked from. Defaults to `process.cwd()`,
|
|
17
|
+
* which is the real answer inside `onBuildComplete`; a parameter only so the
|
|
18
|
+
* unit tests can drive the fixtures' synthetic project dirs.
|
|
19
|
+
*/
|
|
20
|
+
readonly buildCwd?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Resolved `functionGroups`. Defaults to {@link FUNCTION_GROUPS_ENV_VAR},
|
|
23
|
+
* which is how the constructs get them here: `onBuildComplete` runs inside
|
|
24
|
+
* `next build` and cannot read CDK props.
|
|
25
|
+
*
|
|
26
|
+
* `undefined` means one deployment root holding every route, which is the
|
|
27
|
+
* default and the only thing the Containers types ever do.
|
|
28
|
+
*/
|
|
29
|
+
readonly functionGroups?: FunctionGroupSpec[];
|
|
30
|
+
}
|
|
31
|
+
/** One deployment root: the staging plan for it, and where it goes. */
|
|
32
|
+
export interface StagedGroup {
|
|
33
|
+
/**
|
|
34
|
+
* Group name, `default` for the implicit group. Always present — a build with
|
|
35
|
+
* no `functionGroups` produces exactly one {@link StagedGroup} named `default`,
|
|
36
|
+
* so callers never branch on "is this split".
|
|
37
|
+
*/
|
|
38
|
+
readonly name: string;
|
|
39
|
+
/**
|
|
40
|
+
* Directory inside `cdk-nextjs-adapter`, POSIX: `app` when not splitting,
|
|
41
|
+
* `groups/<name>` when splitting.
|
|
42
|
+
*/
|
|
43
|
+
readonly dirName: string;
|
|
44
|
+
readonly staging: StagingPlan;
|
|
45
|
+
}
|
|
46
|
+
export interface BuildAdapterManifestResult {
|
|
47
|
+
readonly manifest: AdapterManifest;
|
|
48
|
+
/** One entry per deployment root to stage. Length 1 unless splitting. */
|
|
49
|
+
readonly groups: StagedGroup[];
|
|
50
|
+
}
|
|
51
|
+
/** A staged deployment root on disk. */
|
|
52
|
+
export interface StagedGroupResult {
|
|
53
|
+
readonly name: string;
|
|
54
|
+
/** Absolute path to the deployment root. */
|
|
55
|
+
readonly path: string;
|
|
56
|
+
readonly fileCount: number;
|
|
57
|
+
}
|
|
58
|
+
export interface WriteBuildOutputsResult extends BuildAdapterManifestResult {
|
|
59
|
+
/** Absolute path to `<distDir>/cdk-nextjs-adapter`. */
|
|
60
|
+
readonly adapterDir: string;
|
|
61
|
+
/** Absolute path to the written manifest. */
|
|
62
|
+
readonly manifestPath: string;
|
|
63
|
+
/** One per deployment root, in the order they were staged. */
|
|
64
|
+
readonly stagedGroups: StagedGroupResult[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build the manifest and staging plan, stage the files, and write the manifest.
|
|
68
|
+
* Called from `onBuildComplete`, which is the only place that holds the build
|
|
69
|
+
* outputs.
|
|
70
|
+
*/
|
|
71
|
+
export declare function writeBuildOutputs(ctx: BuildCompleteContext, options?: BuildOutputsOptions): Promise<WriteBuildOutputsResult>;
|
|
72
|
+
/**
|
|
73
|
+
* The pure half of {@link writeBuildOutputs}: everything derived from the build
|
|
74
|
+
* outputs, with no writes. Split out so it can be unit tested against committed
|
|
75
|
+
* `onBuildComplete` fixtures.
|
|
76
|
+
*
|
|
77
|
+
* Throws on any non-Node output and on any staging key two outputs map to
|
|
78
|
+
* different content.
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildAdapterManifest(ctx: BuildCompleteContext, options?: BuildOutputsOptions): BuildAdapterManifestResult;
|