cdk-nextjs 0.5.14 → 0.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.jsii +719 -258
  2. package/.jsii.tabl.json +1 -1
  3. package/API.md +543 -14
  4. package/README.md +1 -1
  5. package/docs/breaking-changes.md +33 -0
  6. package/docs/caching-guide.md +9 -0
  7. package/docs/plans/adapter-runtime-release.md +362 -0
  8. package/docs/plans/phase-0-image-function-opt-in.md +295 -0
  9. package/lib/adapter/cache-handler.mjs +50916 -13061
  10. package/lib/adapter/memory-cache-handler.d.ts +1 -1
  11. package/lib/adapter/memory-cache-handler.js +17 -11
  12. package/lib/adapter/memory-cache-handler.test.js +57 -1
  13. package/lib/adapter/s3-cache-handler.d.ts +33 -0
  14. package/lib/adapter/s3-cache-handler.js +96 -1
  15. package/lib/adapter/s3-cache-handler.test.js +66 -1
  16. package/lib/generated-structs/OptionalNextjsDistributionProps.d.ts +10 -0
  17. package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
  18. package/lib/image-optimization/handler-utils.d.ts +32 -0
  19. package/lib/image-optimization/handler-utils.js +77 -0
  20. package/lib/image-optimization/handler-utils.test.d.ts +1 -0
  21. package/lib/image-optimization/handler-utils.test.js +142 -0
  22. package/lib/image-optimization/handler.mjs +56112 -0
  23. package/lib/index.d.ts +2 -1
  24. package/lib/index.js +4 -2
  25. package/lib/nextjs-api.d.ts +18 -0
  26. package/lib/nextjs-api.js +25 -9
  27. package/lib/nextjs-build/nextjs-build.d.ts +52 -0
  28. package/lib/nextjs-build/nextjs-build.js +174 -27
  29. package/lib/nextjs-cache.js +1 -1
  30. package/lib/nextjs-compute/nextjs-containers.js +1 -1
  31. package/lib/nextjs-compute/nextjs-functions.js +1 -1
  32. package/lib/nextjs-compute/nextjs-image-function.d.ts +43 -0
  33. package/lib/nextjs-compute/nextjs-image-function.js +58 -0
  34. package/lib/nextjs-distribution.d.ts +22 -0
  35. package/lib/nextjs-distribution.js +25 -3
  36. package/lib/nextjs-post-deploy.js +1 -1
  37. package/lib/nextjs-static-assets.js +1 -1
  38. package/lib/root-constructs/nextjs-base-construct.d.ts +24 -1
  39. package/lib/root-constructs/nextjs-base-construct.js +42 -2
  40. package/lib/root-constructs/nextjs-global-containers.d.ts +15 -0
  41. package/lib/root-constructs/nextjs-global-containers.js +57 -2
  42. package/lib/root-constructs/nextjs-global-functions.d.ts +27 -5
  43. package/lib/root-constructs/nextjs-global-functions.js +67 -17
  44. package/lib/root-constructs/nextjs-regional-containers.js +1 -1
  45. package/lib/root-constructs/nextjs-regional-functions.d.ts +11 -5
  46. package/lib/root-constructs/nextjs-regional-functions.js +14 -18
  47. package/lib/utils/experimental-flags.d.ts +24 -0
  48. package/lib/utils/experimental-flags.js +30 -0
  49. package/lib/utils/get-architecture.d.ts +10 -0
  50. package/lib/utils/get-architecture.js +17 -2
  51. package/package.json +4 -1
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchFromS3 = fetchFromS3;
4
+ exports.getFileNameWithExtension = getFileNameWithExtension;
5
+ exports.resolveErrorResponse = resolveErrorResponse;
6
+ /* eslint-disable import/no-extraneous-dependencies */
7
+ const client_s3_1 = require("@aws-sdk/client-s3");
8
+ const image_optimizer_js_1 = require("next/dist/server/image-optimizer.js");
9
+ const serve_static_js_1 = require("next/dist/server/serve-static.js");
10
+ /**
11
+ * Fetches a non-absolute (local) image referenced by an `<Image>` from S3.
12
+ *
13
+ * `NextjsStaticAssets` uploads under `basePath` as a key prefix, so every key
14
+ * includes it. `url` is inconsistent: next-image-loader bakes `basePath` into
15
+ * the href for statically imported images, while plain string paths are passed
16
+ * through as literally written by the app. So the prefix is normalized to
17
+ * exactly one occurrence rather than simply added or removed.
18
+ */
19
+ async function fetchFromS3(s3, bucket, url, basePath) {
20
+ // Matching on a path boundary keeps a sibling like "/basement/logo.png"
21
+ // from being treated as basePath "/base" plus "ment/logo.png".
22
+ const hasBasePath = !!basePath && (url === basePath || url.startsWith(`${basePath}/`));
23
+ const withoutBasePath = hasBasePath ? url.slice(basePath.length) : url;
24
+ const key = `${basePath}${withoutBasePath}`.replace(/^\//, "");
25
+ const response = await s3.send(new client_s3_1.GetObjectCommand({
26
+ Bucket: bucket,
27
+ Key: key,
28
+ }));
29
+ const body = response.Body;
30
+ if (!body) {
31
+ throw new Error(`Empty response from S3 for key: ${key}`);
32
+ }
33
+ const chunks = [];
34
+ for await (const chunk of body) {
35
+ chunks.push(Buffer.from(chunk));
36
+ }
37
+ return {
38
+ buffer: Buffer.concat(chunks),
39
+ contentType: response.ContentType || null,
40
+ etag: response.ETag || "",
41
+ };
42
+ }
43
+ /** Mirrors Next.js's own `getFileNameWithExtension` in image-optimizer.js. */
44
+ function getFileNameWithExtension(url, contentType) {
45
+ const [urlWithoutQueryParams] = url.split("?", 1);
46
+ const fileNameWithExtension = urlWithoutQueryParams.split("/").pop();
47
+ if (!contentType || !fileNameWithExtension) {
48
+ return "image.bin";
49
+ }
50
+ const [fileName] = fileNameWithExtension.split(".", 1);
51
+ const extension = (0, serve_static_js_1.getExtension)(contentType);
52
+ return `${fileName}.${extension}`;
53
+ }
54
+ /**
55
+ * Maps an error thrown while processing an image request to the HTTP
56
+ * status/message it should produce, preserving the status Next.js's own
57
+ * `ImageError`/`fetchExternalImage` attach. A missing S3 object is mapped to
58
+ * the same 400 Next.js's own local-image fetch path (`fetchInternalImage` in
59
+ * image-optimizer.js) produces for a missing file: it never inspects the
60
+ * internal request's status code, so a 404 there just flows into the normal
61
+ * "not a valid image" content-type check as if it were malformed image
62
+ * bytes. This mirrors that behavior instead of surfacing a 404, so Functions
63
+ * and Containers deployments respond identically for this case.
64
+ */
65
+ function resolveErrorResponse(error) {
66
+ if (error instanceof image_optimizer_js_1.ImageError) {
67
+ return { statusCode: error.statusCode, message: error.message };
68
+ }
69
+ if (error instanceof Error && error.name === "NoSuchKey") {
70
+ return {
71
+ statusCode: 400,
72
+ message: "The requested resource isn't a valid image.",
73
+ };
74
+ }
75
+ return { statusCode: 500, message: "Internal Server Error" };
76
+ }
77
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlci11dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pbWFnZS1vcHRpbWl6YXRpb24vaGFuZGxlci11dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQWNBLGtDQW1DQztBQUdELDREQVlDO0FBYUQsb0RBY0M7QUEzRkQsc0RBQXNEO0FBQ3RELGtEQUFnRTtBQUNoRSw0RUFBaUU7QUFDakUsc0VBQWdFO0FBRWhFOzs7Ozs7OztHQVFHO0FBQ0ksS0FBSyxVQUFVLFdBQVcsQ0FDL0IsRUFBWSxFQUNaLE1BQWMsRUFDZCxHQUFXLEVBQ1gsUUFBZ0I7SUFFaEIsd0VBQXdFO0lBQ3hFLCtEQUErRDtJQUMvRCxNQUFNLFdBQVcsR0FDZixDQUFDLENBQUMsUUFBUSxJQUFJLENBQUMsR0FBRyxLQUFLLFFBQVEsSUFBSSxHQUFHLENBQUMsVUFBVSxDQUFDLEdBQUcsUUFBUSxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3JFLE1BQU0sZUFBZSxHQUFHLFdBQVcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztJQUN2RSxNQUFNLEdBQUcsR0FBRyxHQUFHLFFBQVEsR0FBRyxlQUFlLEVBQUUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBRS9ELE1BQU0sUUFBUSxHQUFHLE1BQU0sRUFBRSxDQUFDLElBQUksQ0FDNUIsSUFBSSw0QkFBZ0IsQ0FBQztRQUNuQixNQUFNLEVBQUUsTUFBTTtRQUNkLEdBQUcsRUFBRSxHQUFHO0tBQ1QsQ0FBQyxDQUNILENBQUM7SUFFRixNQUFNLElBQUksR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDO0lBQzNCLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNWLE1BQU0sSUFBSSxLQUFLLENBQUMsbUNBQW1DLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFhLEVBQUUsQ0FBQztJQUM1QixJQUFJLEtBQUssRUFBRSxNQUFNLEtBQUssSUFBSSxJQUFpQyxFQUFFLENBQUM7UUFDNUQsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFDbEMsQ0FBQztJQUVELE9BQU87UUFDTCxNQUFNLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUM7UUFDN0IsV0FBVyxFQUFFLFFBQVEsQ0FBQyxXQUFXLElBQUksSUFBSTtRQUN6QyxJQUFJLEVBQUUsUUFBUSxDQUFDLElBQUksSUFBSSxFQUFFO0tBQzFCLENBQUM7QUFDSixDQUFDO0FBRUQsOEVBQThFO0FBQzlFLFNBQWdCLHdCQUF3QixDQUN0QyxHQUFXLEVBQ1gsV0FBMEI7SUFFMUIsTUFBTSxDQUFDLHFCQUFxQixDQUFDLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbEQsTUFBTSxxQkFBcUIsR0FBRyxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDckUsSUFBSSxDQUFDLFdBQVcsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDM0MsT0FBTyxXQUFXLENBQUM7SUFDckIsQ0FBQztJQUNELE1BQU0sQ0FBQyxRQUFRLENBQUMsR0FBRyxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ3ZELE1BQU0sU0FBUyxHQUFHLElBQUEsOEJBQVksRUFBQyxXQUFXLENBQUMsQ0FBQztJQUM1QyxPQUFPLEdBQUcsUUFBUSxJQUFJLFNBQVMsRUFBRSxDQUFDO0FBQ3BDLENBQUM7QUFFRDs7Ozs7Ozs7OztHQVVHO0FBQ0gsU0FBZ0Isb0JBQW9CLENBQUMsS0FBYztJQUlqRCxJQUFJLEtBQUssWUFBWSwrQkFBVSxFQUFFLENBQUM7UUFDaEMsT0FBTyxFQUFFLFVBQVUsRUFBRSxLQUFLLENBQUMsVUFBVSxFQUFFLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7SUFDbEUsQ0FBQztJQUNELElBQUksS0FBSyxZQUFZLEtBQUssSUFBSSxLQUFLLENBQUMsSUFBSSxLQUFLLFdBQVcsRUFBRSxDQUFDO1FBQ3pELE9BQU87WUFDTCxVQUFVLEVBQUUsR0FBRztZQUNmLE9BQU8sRUFBRSw2Q0FBNkM7U0FDdkQsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsQ0FBQztBQUMvRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzICovXG5pbXBvcnQgeyBHZXRPYmplY3RDb21tYW5kLCBTM0NsaWVudCB9IGZyb20gXCJAYXdzLXNkay9jbGllbnQtczNcIjtcbmltcG9ydCB7IEltYWdlRXJyb3IgfSBmcm9tIFwibmV4dC9kaXN0L3NlcnZlci9pbWFnZS1vcHRpbWl6ZXIuanNcIjtcbmltcG9ydCB7IGdldEV4dGVuc2lvbiB9IGZyb20gXCJuZXh0L2Rpc3Qvc2VydmVyL3NlcnZlLXN0YXRpYy5qc1wiO1xuXG4vKipcbiAqIEZldGNoZXMgYSBub24tYWJzb2x1dGUgKGxvY2FsKSBpbWFnZSByZWZlcmVuY2VkIGJ5IGFuIGA8SW1hZ2U+YCBmcm9tIFMzLlxuICpcbiAqIGBOZXh0anNTdGF0aWNBc3NldHNgIHVwbG9hZHMgdW5kZXIgYGJhc2VQYXRoYCBhcyBhIGtleSBwcmVmaXgsIHNvIGV2ZXJ5IGtleVxuICogaW5jbHVkZXMgaXQuIGB1cmxgIGlzIGluY29uc2lzdGVudDogbmV4dC1pbWFnZS1sb2FkZXIgYmFrZXMgYGJhc2VQYXRoYCBpbnRvXG4gKiB0aGUgaHJlZiBmb3Igc3RhdGljYWxseSBpbXBvcnRlZCBpbWFnZXMsIHdoaWxlIHBsYWluIHN0cmluZyBwYXRocyBhcmUgcGFzc2VkXG4gKiB0aHJvdWdoIGFzIGxpdGVyYWxseSB3cml0dGVuIGJ5IHRoZSBhcHAuIFNvIHRoZSBwcmVmaXggaXMgbm9ybWFsaXplZCB0b1xuICogZXhhY3RseSBvbmUgb2NjdXJyZW5jZSByYXRoZXIgdGhhbiBzaW1wbHkgYWRkZWQgb3IgcmVtb3ZlZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZldGNoRnJvbVMzKFxuICBzMzogUzNDbGllbnQsXG4gIGJ1Y2tldDogc3RyaW5nLFxuICB1cmw6IHN0cmluZyxcbiAgYmFzZVBhdGg6IHN0cmluZyxcbik6IFByb21pc2U8eyBidWZmZXI6IEJ1ZmZlcjsgY29udGVudFR5cGU6IHN0cmluZyB8IG51bGw7IGV0YWc6IHN0cmluZyB9PiB7XG4gIC8vIE1hdGNoaW5nIG9uIGEgcGF0aCBib3VuZGFyeSBrZWVwcyBhIHNpYmxpbmcgbGlrZSBcIi9iYXNlbWVudC9sb2dvLnBuZ1wiXG4gIC8vIGZyb20gYmVpbmcgdHJlYXRlZCBhcyBiYXNlUGF0aCBcIi9iYXNlXCIgcGx1cyBcIm1lbnQvbG9nby5wbmdcIi5cbiAgY29uc3QgaGFzQmFzZVBhdGggPVxuICAgICEhYmFzZVBhdGggJiYgKHVybCA9PT0gYmFzZVBhdGggfHwgdXJsLnN0YXJ0c1dpdGgoYCR7YmFzZVBhdGh9L2ApKTtcbiAgY29uc3Qgd2l0aG91dEJhc2VQYXRoID0gaGFzQmFzZVBhdGggPyB1cmwuc2xpY2UoYmFzZVBhdGgubGVuZ3RoKSA6IHVybDtcbiAgY29uc3Qga2V5ID0gYCR7YmFzZVBhdGh9JHt3aXRob3V0QmFzZVBhdGh9YC5yZXBsYWNlKC9eXFwvLywgXCJcIik7XG5cbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBzMy5zZW5kKFxuICAgIG5ldyBHZXRPYmplY3RDb21tYW5kKHtcbiAgICAgIEJ1Y2tldDogYnVja2V0LFxuICAgICAgS2V5OiBrZXksXG4gICAgfSksXG4gICk7XG5cbiAgY29uc3QgYm9keSA9IHJlc3BvbnNlLkJvZHk7XG4gIGlmICghYm9keSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgRW1wdHkgcmVzcG9uc2UgZnJvbSBTMyBmb3Iga2V5OiAke2tleX1gKTtcbiAgfVxuXG4gIGNvbnN0IGNodW5rczogQnVmZmVyW10gPSBbXTtcbiAgZm9yIGF3YWl0IChjb25zdCBjaHVuayBvZiBib2R5IGFzIEFzeW5jSXRlcmFibGU8VWludDhBcnJheT4pIHtcbiAgICBjaHVua3MucHVzaChCdWZmZXIuZnJvbShjaHVuaykpO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBidWZmZXI6IEJ1ZmZlci5jb25jYXQoY2h1bmtzKSxcbiAgICBjb250ZW50VHlwZTogcmVzcG9uc2UuQ29udGVudFR5cGUgfHwgbnVsbCxcbiAgICBldGFnOiByZXNwb25zZS5FVGFnIHx8IFwiXCIsXG4gIH07XG59XG5cbi8qKiBNaXJyb3JzIE5leHQuanMncyBvd24gYGdldEZpbGVOYW1lV2l0aEV4dGVuc2lvbmAgaW4gaW1hZ2Utb3B0aW1pemVyLmpzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEZpbGVOYW1lV2l0aEV4dGVuc2lvbihcbiAgdXJsOiBzdHJpbmcsXG4gIGNvbnRlbnRUeXBlOiBzdHJpbmcgfCBudWxsLFxuKTogc3RyaW5nIHtcbiAgY29uc3QgW3VybFdpdGhvdXRRdWVyeVBhcmFtc10gPSB1cmwuc3BsaXQoXCI/XCIsIDEpO1xuICBjb25zdCBmaWxlTmFtZVdpdGhFeHRlbnNpb24gPSB1cmxXaXRob3V0UXVlcnlQYXJhbXMuc3BsaXQoXCIvXCIpLnBvcCgpO1xuICBpZiAoIWNvbnRlbnRUeXBlIHx8ICFmaWxlTmFtZVdpdGhFeHRlbnNpb24pIHtcbiAgICByZXR1cm4gXCJpbWFnZS5iaW5cIjtcbiAgfVxuICBjb25zdCBbZmlsZU5hbWVdID0gZmlsZU5hbWVXaXRoRXh0ZW5zaW9uLnNwbGl0KFwiLlwiLCAxKTtcbiAgY29uc3QgZXh0ZW5zaW9uID0gZ2V0RXh0ZW5zaW9uKGNvbnRlbnRUeXBlKTtcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke2V4dGVuc2lvbn1gO1xufVxuXG4vKipcbiAqIE1hcHMgYW4gZXJyb3IgdGhyb3duIHdoaWxlIHByb2Nlc3NpbmcgYW4gaW1hZ2UgcmVxdWVzdCB0byB0aGUgSFRUUFxuICogc3RhdHVzL21lc3NhZ2UgaXQgc2hvdWxkIHByb2R1Y2UsIHByZXNlcnZpbmcgdGhlIHN0YXR1cyBOZXh0LmpzJ3Mgb3duXG4gKiBgSW1hZ2VFcnJvcmAvYGZldGNoRXh0ZXJuYWxJbWFnZWAgYXR0YWNoLiBBIG1pc3NpbmcgUzMgb2JqZWN0IGlzIG1hcHBlZCB0b1xuICogdGhlIHNhbWUgNDAwIE5leHQuanMncyBvd24gbG9jYWwtaW1hZ2UgZmV0Y2ggcGF0aCAoYGZldGNoSW50ZXJuYWxJbWFnZWAgaW5cbiAqIGltYWdlLW9wdGltaXplci5qcykgcHJvZHVjZXMgZm9yIGEgbWlzc2luZyBmaWxlOiBpdCBuZXZlciBpbnNwZWN0cyB0aGVcbiAqIGludGVybmFsIHJlcXVlc3QncyBzdGF0dXMgY29kZSwgc28gYSA0MDQgdGhlcmUganVzdCBmbG93cyBpbnRvIHRoZSBub3JtYWxcbiAqIFwibm90IGEgdmFsaWQgaW1hZ2VcIiBjb250ZW50LXR5cGUgY2hlY2sgYXMgaWYgaXQgd2VyZSBtYWxmb3JtZWQgaW1hZ2VcbiAqIGJ5dGVzLiBUaGlzIG1pcnJvcnMgdGhhdCBiZWhhdmlvciBpbnN0ZWFkIG9mIHN1cmZhY2luZyBhIDQwNCwgc28gRnVuY3Rpb25zXG4gKiBhbmQgQ29udGFpbmVycyBkZXBsb3ltZW50cyByZXNwb25kIGlkZW50aWNhbGx5IGZvciB0aGlzIGNhc2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlRXJyb3JSZXNwb25zZShlcnJvcjogdW5rbm93bik6IHtcbiAgc3RhdHVzQ29kZTogbnVtYmVyO1xuICBtZXNzYWdlOiBzdHJpbmc7XG59IHtcbiAgaWYgKGVycm9yIGluc3RhbmNlb2YgSW1hZ2VFcnJvcikge1xuICAgIHJldHVybiB7IHN0YXR1c0NvZGU6IGVycm9yLnN0YXR1c0NvZGUsIG1lc3NhZ2U6IGVycm9yLm1lc3NhZ2UgfTtcbiAgfVxuICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvciAmJiBlcnJvci5uYW1lID09PSBcIk5vU3VjaEtleVwiKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHN0YXR1c0NvZGU6IDQwMCxcbiAgICAgIG1lc3NhZ2U6IFwiVGhlIHJlcXVlc3RlZCByZXNvdXJjZSBpc24ndCBhIHZhbGlkIGltYWdlLlwiLFxuICAgIH07XG4gIH1cbiAgcmV0dXJuIHsgc3RhdHVzQ29kZTogNTAwLCBtZXNzYWdlOiBcIkludGVybmFsIFNlcnZlciBFcnJvclwiIH07XG59XG4iXX0=
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,142 @@
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
+ const client_s3_1 = require("@aws-sdk/client-s3");
6
+ const image_optimizer_js_1 = require("next/dist/server/image-optimizer.js");
7
+ const handler_utils_1 = require("./handler-utils");
8
+ function asyncIterableFrom(chunks) {
9
+ return {
10
+ [Symbol.asyncIterator]() {
11
+ let i = 0;
12
+ return {
13
+ next: async () => i < chunks.length
14
+ ? { done: false, value: chunks[i++] }
15
+ : { done: true, value: undefined },
16
+ };
17
+ },
18
+ };
19
+ }
20
+ describe("fetchFromS3", () => {
21
+ const mockSend = jest.fn();
22
+ const s3 = new client_s3_1.S3Client({});
23
+ beforeEach(() => {
24
+ s3.send = mockSend;
25
+ mockSend.mockReset();
26
+ });
27
+ it("strips the leading slash and uses url as-is for the S3 key", async () => {
28
+ mockSend.mockResolvedValue({
29
+ Body: asyncIterableFrom([Buffer.from("data")]),
30
+ ContentType: "image/png",
31
+ ETag: '"abc123"',
32
+ });
33
+ await (0, handler_utils_1.fetchFromS3)(s3, "my-bucket", "/base/foo.png", "");
34
+ const params = client_s3_1.GetObjectCommand.mock.calls[0][0];
35
+ expect(params).toEqual({
36
+ Bucket: "my-bucket",
37
+ Key: "base/foo.png",
38
+ });
39
+ });
40
+ it("keeps a single basePath prefix when the url already includes it", async () => {
41
+ mockSend.mockResolvedValue({
42
+ Body: asyncIterableFrom([Buffer.from("data")]),
43
+ ContentType: "image/png",
44
+ ETag: '"abc123"',
45
+ });
46
+ // next-image-loader bakes "/base" into the href for statically imported
47
+ // images, and NextjsStaticAssets uploads under the same prefix, so the
48
+ // key must carry it exactly once.
49
+ await (0, handler_utils_1.fetchFromS3)(s3, "my-bucket", "/base/_next/static/media/a.png", "/base");
50
+ const params = client_s3_1.GetObjectCommand.mock.calls[0][0];
51
+ expect(params.Key).toBe("base/_next/static/media/a.png");
52
+ });
53
+ it("adds the basePath prefix when the url omits it", async () => {
54
+ mockSend.mockResolvedValue({
55
+ Body: asyncIterableFrom([Buffer.from("data")]),
56
+ ContentType: "image/png",
57
+ ETag: '"abc123"',
58
+ });
59
+ // Plain string paths (e.g. `<Image src="/static/foo.jpg">`) are passed
60
+ // through by next/image as written, without basePath baked in, but the
61
+ // S3 key still has it.
62
+ await (0, handler_utils_1.fetchFromS3)(s3, "my-bucket", "/static/foo.jpg", "/base");
63
+ const params = client_s3_1.GetObjectCommand.mock.calls[0][0];
64
+ expect(params.Key).toBe("base/static/foo.jpg");
65
+ });
66
+ it("only matches basePath on a path boundary", async () => {
67
+ mockSend.mockResolvedValue({
68
+ Body: asyncIterableFrom([Buffer.from("data")]),
69
+ ContentType: "image/png",
70
+ ETag: '"abc123"',
71
+ });
72
+ // "/basement" merely shares a prefix with basePath "/base"; treating it
73
+ // as a match would produce the key "base/ment/logo.png".
74
+ await (0, handler_utils_1.fetchFromS3)(s3, "my-bucket", "/basement/logo.png", "/base");
75
+ const params = client_s3_1.GetObjectCommand.mock.calls[0][0];
76
+ expect(params.Key).toBe("base/basement/logo.png");
77
+ });
78
+ it("returns the concatenated buffer, content type, and etag", async () => {
79
+ mockSend.mockResolvedValue({
80
+ Body: asyncIterableFrom([Buffer.from("hel"), Buffer.from("lo")]),
81
+ ContentType: "image/jpeg",
82
+ ETag: '"the-etag"',
83
+ });
84
+ const result = await (0, handler_utils_1.fetchFromS3)(s3, "my-bucket", "/foo.jpg", "");
85
+ expect(result.buffer.toString()).toBe("hello");
86
+ expect(result.contentType).toBe("image/jpeg");
87
+ expect(result.etag).toBe('"the-etag"');
88
+ });
89
+ it("returns an empty etag when S3 doesn't provide one", async () => {
90
+ mockSend.mockResolvedValue({
91
+ Body: asyncIterableFrom([Buffer.from("data")]),
92
+ ContentType: null,
93
+ ETag: undefined,
94
+ });
95
+ const result = await (0, handler_utils_1.fetchFromS3)(s3, "my-bucket", "/foo.png", "");
96
+ expect(result.etag).toBe("");
97
+ expect(result.contentType).toBeNull();
98
+ });
99
+ it("throws when S3 returns no body", async () => {
100
+ mockSend.mockResolvedValue({ Body: undefined });
101
+ await expect((0, handler_utils_1.fetchFromS3)(s3, "my-bucket", "/missing.png", "")).rejects.toThrow(/Empty response from S3/);
102
+ });
103
+ });
104
+ describe("getFileNameWithExtension", () => {
105
+ it("derives filename and extension from the url and content type", () => {
106
+ expect((0, handler_utils_1.getFileNameWithExtension)("/foo/bar.png?w=100&q=75", "image/webp")).toBe("bar.webp");
107
+ });
108
+ it("falls back to image.bin when contentType is missing", () => {
109
+ expect((0, handler_utils_1.getFileNameWithExtension)("/foo/bar.png", null)).toBe("image.bin");
110
+ });
111
+ it("falls back to image.bin when the url has no filename segment", () => {
112
+ expect((0, handler_utils_1.getFileNameWithExtension)("/", "image/png")).toBe("image.bin");
113
+ });
114
+ });
115
+ describe("resolveErrorResponse", () => {
116
+ it("preserves the status code and message from an ImageError", () => {
117
+ const error = new image_optimizer_js_1.ImageError(400, '"url" parameter is not allowed');
118
+ expect((0, handler_utils_1.resolveErrorResponse)(error)).toEqual({
119
+ statusCode: 400,
120
+ message: '"url" parameter is not allowed',
121
+ });
122
+ });
123
+ it("maps a missing S3 object to the same 400 Next.js's own local-image fetch produces", () => {
124
+ const error = new Error("NoSuchKey: does not exist");
125
+ error.name = "NoSuchKey";
126
+ expect((0, handler_utils_1.resolveErrorResponse)(error)).toEqual({
127
+ statusCode: 400,
128
+ message: "The requested resource isn't a valid image.",
129
+ });
130
+ });
131
+ it("falls back to 500 for unrecognized errors", () => {
132
+ expect((0, handler_utils_1.resolveErrorResponse)(new Error("boom"))).toEqual({
133
+ statusCode: 500,
134
+ message: "Internal Server Error",
135
+ });
136
+ expect((0, handler_utils_1.resolveErrorResponse)("not an error")).toEqual({
137
+ statusCode: 500,
138
+ message: "Internal Server Error",
139
+ });
140
+ });
141
+ });
142
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlci11dGlscy50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ltYWdlLW9wdGltaXphdGlvbi9oYW5kbGVyLXV0aWxzLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxzREFBc0Q7QUFDdEQsSUFBSSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0FBRWhDLGtEQUFnRTtBQUNoRSw0RUFBaUU7QUFDakUsbURBSXlCO0FBRXpCLFNBQVMsaUJBQWlCLENBQUMsTUFBb0I7SUFDN0MsT0FBTztRQUNMLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQztZQUNwQixJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDVixPQUFPO2dCQUNMLElBQUksRUFBRSxLQUFLLElBQUksRUFBRSxDQUNmLENBQUMsR0FBRyxNQUFNLENBQUMsTUFBTTtvQkFDZixDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRTtvQkFDckMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFO2FBQ3ZDLENBQUM7UUFDSixDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRCxRQUFRLENBQUMsYUFBYSxFQUFFLEdBQUcsRUFBRTtJQUMzQixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsRUFBRSxFQUFFLENBQUM7SUFDM0IsTUFBTSxFQUFFLEdBQUcsSUFBSSxvQkFBUSxDQUFDLEVBQUUsQ0FBd0IsQ0FBQztJQUVuRCxVQUFVLENBQUMsR0FBRyxFQUFFO1FBQ2IsRUFBMkMsQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDO1FBQzdELFFBQVEsQ0FBQyxTQUFTLEVBQUUsQ0FBQztJQUN2QixDQUFDLENBQUMsQ0FBQztJQUVILEVBQUUsQ0FBQyw0REFBNEQsRUFBRSxLQUFLLElBQUksRUFBRTtRQUMxRSxRQUFR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QUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmplc3QubW9jayhcIkBhd3Mtc2RrL2NsaWVudC1zM1wiKTtcblxuaW1wb3J0IHsgR2V0T2JqZWN0Q29tbWFuZCwgUzNDbGllbnQgfSBmcm9tIFwiQGF3cy1zZGsvY2xpZW50LXMzXCI7XG5pbXBvcnQgeyBJbWFnZUVycm9yIH0gZnJvbSBcIm5leHQvZGlzdC9zZXJ2ZXIvaW1hZ2Utb3B0aW1pemVyLmpzXCI7XG5pbXBvcnQge1xuICBmZXRjaEZyb21TMyxcbiAgZ2V0RmlsZU5hbWVXaXRoRXh0ZW5zaW9uLFxuICByZXNvbHZlRXJyb3JSZXNwb25zZSxcbn0gZnJvbSBcIi4vaGFuZGxlci11dGlsc1wiO1xuXG5mdW5jdGlvbiBhc3luY0l0ZXJhYmxlRnJvbShjaHVua3M6IFVpbnQ4QXJyYXlbXSk6IEFzeW5jSXRlcmFibGU8VWludDhBcnJheT4ge1xuICByZXR1cm4ge1xuICAgIFtTeW1ib2wuYXN5bmNJdGVyYXRvcl0oKSB7XG4gICAgICBsZXQgaSA9IDA7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBuZXh0OiBhc3luYyAoKSA9PlxuICAgICAgICAgIGkgPCBjaHVua3MubGVuZ3RoXG4gICAgICAgICAgICA/IHsgZG9uZTogZmFsc2UsIHZhbHVlOiBjaHVua3NbaSsrXSB9XG4gICAgICAgICAgICA6IHsgZG9uZTogdHJ1ZSwgdmFsdWU6IHVuZGVmaW5lZCB9LFxuICAgICAgfTtcbiAgICB9LFxuICB9O1xufVxuXG5kZXNjcmliZShcImZldGNoRnJvbVMzXCIsICgpID0+IHtcbiAgY29uc3QgbW9ja1NlbmQgPSBqZXN0LmZuKCk7XG4gIGNvbnN0IHMzID0gbmV3IFMzQ2xpZW50KHt9KSBhcyB1bmtub3duIGFzIFMzQ2xpZW50O1xuXG4gIGJlZm9yZUVhY2goKCkgPT4ge1xuICAgIChzMyBhcyB1bmtub3duIGFzIHsgc2VuZDogdHlwZW9mIG1vY2tTZW5kIH0pLnNlbmQgPSBtb2NrU2VuZDtcbiAgICBtb2NrU2VuZC5tb2NrUmVzZXQoKTtcbiAgfSk7XG5cbiAgaXQoXCJzdHJpcHMgdGhlIGxlYWRpbmcgc2xhc2ggYW5kIHVzZXMgdXJsIGFzLWlzIGZvciB0aGUgUzMga2V5XCIsIGFzeW5jICgpID0+IHtcbiAgICBtb2NrU2VuZC5tb2NrUmVzb2x2ZWRWYWx1ZSh7XG4gICAgICBCb2R5OiBhc3luY0l0ZXJhYmxlRnJvbShbQnVmZmVyLmZyb20oXCJkYXRhXCIpXSksXG4gICAgICBDb250ZW50VHlwZTogXCJpbWFnZS9wbmdcIixcbiAgICAgIEVUYWc6ICdcImFiYzEyM1wiJyxcbiAgICB9KTtcblxuICAgIGF3YWl0IGZldGNoRnJvbVMzKHMzLCBcIm15LWJ1Y2tldFwiLCBcIi9iYXNlL2Zvby5wbmdcIiwgXCJcIik7XG5cbiAgICBjb25zdCBwYXJhbXMgPSAoR2V0T2JqZWN0Q29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9jaykubW9jay5jYWxsc1swXVswXTtcbiAgICBleHBlY3QocGFyYW1zKS50b0VxdWFsKHtcbiAgICAgIEJ1Y2tldDogXCJteS1idWNrZXRcIixcbiAgICAgIEtleTogXCJiYXNlL2Zvby5wbmdcIixcbiAgICB9KTtcbiAgfSk7XG5cbiAgaXQoXCJrZWVwcyBhIHNpbmdsZSBiYXNlUGF0aCBwcmVmaXggd2hlbiB0aGUgdXJsIGFscmVhZHkgaW5jbHVkZXMgaXRcIiwgYXN5bmMgKCkgPT4ge1xuICAgIG1vY2tTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHtcbiAgICAgIEJvZHk6IGFzeW5jSXRlcmFibGVGcm9tKFtCdWZmZXIuZnJvbShcImRhdGFcIildKSxcbiAgICAgIENvbnRlbnRUeXBlOiBcImltYWdlL3BuZ1wiLFxuICAgICAgRVRhZzogJ1wiYWJjMTIzXCInLFxuICAgIH0pO1xuXG4gICAgLy8gbmV4dC1pbWFnZS1sb2FkZXIgYmFrZXMgXCIvYmFzZVwiIGludG8gdGhlIGhyZWYgZm9yIHN0YXRpY2FsbHkgaW1wb3J0ZWRcbiAgICAvLyBpbWFnZXMsIGFuZCBOZXh0anNTdGF0aWNBc3NldHMgdXBsb2FkcyB1bmRlciB0aGUgc2FtZSBwcmVmaXgsIHNvIHRoZVxuICAgIC8vIGtleSBtdXN0IGNhcnJ5IGl0IGV4YWN0bHkgb25jZS5cbiAgICBhd2FpdCBmZXRjaEZyb21TMyhcbiAgICAgIHMzLFxuICAgICAgXCJteS1idWNrZXRcIixcbiAgICAgIFwiL2Jhc2UvX25leHQvc3RhdGljL21lZGlhL2EucG5nXCIsXG4gICAgICBcIi9iYXNlXCIsXG4gICAgKTtcblxuICAgIGNvbnN0IHBhcmFtcyA9IChHZXRPYmplY3RDb21tYW5kIGFzIHVua25vd24gYXMgamVzdC5Nb2NrKS5tb2NrLmNhbGxzWzBdWzBdO1xuICAgIGV4cGVjdChwYXJhbXMuS2V5KS50b0JlKFwiYmFzZS9fbmV4dC9zdGF0aWMvbWVkaWEvYS5wbmdcIik7XG4gIH0pO1xuXG4gIGl0KFwiYWRkcyB0aGUgYmFzZVBhdGggcHJlZml4IHdoZW4gdGhlIHVybCBvbWl0cyBpdFwiLCBhc3luYyAoKSA9PiB7XG4gICAgbW9ja1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgQm9keTogYXN5bmNJdGVyYWJsZUZyb20oW0J1ZmZlci5mcm9tKFwiZGF0YVwiKV0pLFxuICAgICAgQ29udGVudFR5cGU6IFwiaW1hZ2UvcG5nXCIsXG4gICAgICBFVGFnOiAnXCJhYmMxMjNcIicsXG4gICAgfSk7XG5cbiAgICAvLyBQbGFpbiBzdHJpbmcgcGF0aHMgKGUuZy4gYDxJbWFnZSBzcmM9XCIvc3RhdGljL2Zvby5qcGdcIj5gKSBhcmUgcGFzc2VkXG4gICAgLy8gdGhyb3VnaCBieSBuZXh0L2ltYWdlIGFzIHdyaXR0ZW4sIHdpdGhvdXQgYmFzZVBhdGggYmFrZWQgaW4sIGJ1dCB0aGVcbiAgICAvLyBTMyBrZXkgc3RpbGwgaGFzIGl0LlxuICAgIGF3YWl0IGZldGNoRnJvbVMzKHMzLCBcIm15LWJ1Y2tldFwiLCBcIi9zdGF0aWMvZm9vLmpwZ1wiLCBcIi9iYXNlXCIpO1xuXG4gICAgY29uc3QgcGFyYW1zID0gKEdldE9iamVjdENvbW1hbmQgYXMgdW5rbm93biBhcyBqZXN0Lk1vY2spLm1vY2suY2FsbHNbMF1bMF07XG4gICAgZXhwZWN0KHBhcmFtcy5LZXkpLnRvQmUoXCJiYXNlL3N0YXRpYy9mb28uanBnXCIpO1xuICB9KTtcblxuICBpdChcIm9ubHkgbWF0Y2hlcyBiYXNlUGF0aCBvbiBhIHBhdGggYm91bmRhcnlcIiwgYXN5bmMgKCkgPT4ge1xuICAgIG1vY2tTZW5kLm1vY2tSZXNvbHZlZFZhbHVlKHtcbiAgICAgIEJvZHk6IGFzeW5jSXRlcmFibGVGcm9tKFtCdWZmZXIuZnJvbShcImRhdGFcIildKSxcbiAgICAgIENvbnRlbnRUeXBlOiBcImltYWdlL3BuZ1wiLFxuICAgICAgRVRhZzogJ1wiYWJjMTIzXCInLFxuICAgIH0pO1xuXG4gICAgLy8gXCIvYmFzZW1lbnRcIiBtZXJlbHkgc2hhcmVzIGEgcHJlZml4IHdpdGggYmFzZVBhdGggXCIvYmFzZVwiOyB0cmVhdGluZyBpdFxuICAgIC8vIGFzIGEgbWF0Y2ggd291bGQgcHJvZHVjZSB0aGUga2V5IFwiYmFzZS9tZW50L2xvZ28ucG5nXCIuXG4gICAgYXdhaXQgZmV0Y2hGcm9tUzMoczMsIFwibXktYnVja2V0XCIsIFwiL2Jhc2VtZW50L2xvZ28ucG5nXCIsIFwiL2Jhc2VcIik7XG5cbiAgICBjb25zdCBwYXJhbXMgPSAoR2V0T2JqZWN0Q29tbWFuZCBhcyB1bmtub3duIGFzIGplc3QuTW9jaykubW9jay5jYWxsc1swXVswXTtcbiAgICBleHBlY3QocGFyYW1zLktleSkudG9CZShcImJhc2UvYmFzZW1lbnQvbG9nby5wbmdcIik7XG4gIH0pO1xuXG4gIGl0KFwicmV0dXJucyB0aGUgY29uY2F0ZW5hdGVkIGJ1ZmZlciwgY29udGVudCB0eXBlLCBhbmQgZXRhZ1wiLCBhc3luYyAoKSA9PiB7XG4gICAgbW9ja1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgQm9keTogYXN5bmNJdGVyYWJsZUZyb20oW0J1ZmZlci5mcm9tKFwiaGVsXCIpLCBCdWZmZXIuZnJvbShcImxvXCIpXSksXG4gICAgICBDb250ZW50VHlwZTogXCJpbWFnZS9qcGVnXCIsXG4gICAgICBFVGFnOiAnXCJ0aGUtZXRhZ1wiJyxcbiAgICB9KTtcblxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGZldGNoRnJvbVMzKHMzLCBcIm15LWJ1Y2tldFwiLCBcIi9mb28uanBnXCIsIFwiXCIpO1xuXG4gICAgZXhwZWN0KHJlc3VsdC5idWZmZXIudG9TdHJpbmcoKSkudG9CZShcImhlbGxvXCIpO1xuICAgIGV4cGVjdChyZXN1bHQuY29udGVudFR5cGUpLnRvQmUoXCJpbWFnZS9qcGVnXCIpO1xuICAgIGV4cGVjdChyZXN1bHQuZXRhZykudG9CZSgnXCJ0aGUtZXRhZ1wiJyk7XG4gIH0pO1xuXG4gIGl0KFwicmV0dXJucyBhbiBlbXB0eSBldGFnIHdoZW4gUzMgZG9lc24ndCBwcm92aWRlIG9uZVwiLCBhc3luYyAoKSA9PiB7XG4gICAgbW9ja1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgQm9keTogYXN5bmNJdGVyYWJsZUZyb20oW0J1ZmZlci5mcm9tKFwiZGF0YVwiKV0pLFxuICAgICAgQ29udGVudFR5cGU6IG51bGwsXG4gICAgICBFVGFnOiB1bmRlZmluZWQsXG4gICAgfSk7XG5cbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBmZXRjaEZyb21TMyhzMywgXCJteS1idWNrZXRcIiwgXCIvZm9vLnBuZ1wiLCBcIlwiKTtcblxuICAgIGV4cGVjdChyZXN1bHQuZXRhZykudG9CZShcIlwiKTtcbiAgICBleHBlY3QocmVzdWx0LmNvbnRlbnRUeXBlKS50b0JlTnVsbCgpO1xuICB9KTtcblxuICBpdChcInRocm93cyB3aGVuIFMzIHJldHVybnMgbm8gYm9keVwiLCBhc3luYyAoKSA9PiB7XG4gICAgbW9ja1NlbmQubW9ja1Jlc29sdmVkVmFsdWUoeyBCb2R5OiB1bmRlZmluZWQgfSk7XG5cbiAgICBhd2FpdCBleHBlY3QoXG4gICAgICBmZXRjaEZyb21TMyhzMywgXCJteS1idWNrZXRcIiwgXCIvbWlzc2luZy5wbmdcIiwgXCJcIiksXG4gICAgKS5yZWplY3RzLnRvVGhyb3coL0VtcHR5IHJlc3BvbnNlIGZyb20gUzMvKTtcbiAgfSk7XG59KTtcblxuZGVzY3JpYmUoXCJnZXRGaWxlTmFtZVdpdGhFeHRlbnNpb25cIiwgKCkgPT4ge1xuICBpdChcImRlcml2ZXMgZmlsZW5hbWUgYW5kIGV4dGVuc2lvbiBmcm9tIHRoZSB1cmwgYW5kIGNvbnRlbnQgdHlwZVwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KFxuICAgICAgZ2V0RmlsZU5hbWVXaXRoRXh0ZW5zaW9uKFwiL2Zvby9iYXIucG5nP3c9MTAwJnE9NzVcIiwgXCJpbWFnZS93ZWJwXCIpLFxuICAgICkudG9CZShcImJhci53ZWJwXCIpO1xuICB9KTtcblxuICBpdChcImZhbGxzIGJhY2sgdG8gaW1hZ2UuYmluIHdoZW4gY29udGVudFR5cGUgaXMgbWlzc2luZ1wiLCAoKSA9PiB7XG4gICAgZXhwZWN0KGdldEZpbGVOYW1lV2l0aEV4dGVuc2lvbihcIi9mb28vYmFyLnBuZ1wiLCBudWxsKSkudG9CZShcImltYWdlLmJpblwiKTtcbiAgfSk7XG5cbiAgaXQoXCJmYWxscyBiYWNrIHRvIGltYWdlLmJpbiB3aGVuIHRoZSB1cmwgaGFzIG5vIGZpbGVuYW1lIHNlZ21lbnRcIiwgKCkgPT4ge1xuICAgIGV4cGVjdChnZXRGaWxlTmFtZVdpdGhFeHRlbnNpb24oXCIvXCIsIFwiaW1hZ2UvcG5nXCIpKS50b0JlKFwiaW1hZ2UuYmluXCIpO1xuICB9KTtcbn0pO1xuXG5kZXNjcmliZShcInJlc29sdmVFcnJvclJlc3BvbnNlXCIsICgpID0+IHtcbiAgaXQoXCJwcmVzZXJ2ZXMgdGhlIHN0YXR1cyBjb2RlIGFuZCBtZXNzYWdlIGZyb20gYW4gSW1hZ2VFcnJvclwiLCAoKSA9PiB7XG4gICAgY29uc3QgZXJyb3IgPSBuZXcgSW1hZ2VFcnJvcig0MDAsICdcInVybFwiIHBhcmFtZXRlciBpcyBub3QgYWxsb3dlZCcpO1xuXG4gICAgZXhwZWN0KHJlc29sdmVFcnJvclJlc3BvbnNlKGVycm9yKSkudG9FcXVhbCh7XG4gICAgICBzdGF0dXNDb2RlOiA0MDAsXG4gICAgICBtZXNzYWdlOiAnXCJ1cmxcIiBwYXJhbWV0ZXIgaXMgbm90IGFsbG93ZWQnLFxuICAgIH0pO1xuICB9KTtcblxuICBpdChcIm1hcHMgYSBtaXNzaW5nIFMzIG9iamVjdCB0byB0aGUgc2FtZSA0MDAgTmV4dC5qcydzIG93biBsb2NhbC1pbWFnZSBmZXRjaCBwcm9kdWNlc1wiLCAoKSA9PiB7XG4gICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoXCJOb1N1Y2hLZXk6IGRvZXMgbm90IGV4aXN0XCIpO1xuICAgIGVycm9yLm5hbWUgPSBcIk5vU3VjaEtleVwiO1xuXG4gICAgZXhwZWN0KHJlc29sdmVFcnJvclJlc3BvbnNlKGVycm9yKSkudG9FcXVhbCh7XG4gICAgICBzdGF0dXNDb2RlOiA0MDAsXG4gICAgICBtZXNzYWdlOiBcIlRoZSByZXF1ZXN0ZWQgcmVzb3VyY2UgaXNuJ3QgYSB2YWxpZCBpbWFnZS5cIixcbiAgICB9KTtcbiAgfSk7XG5cbiAgaXQoXCJmYWxscyBiYWNrIHRvIDUwMCBmb3IgdW5yZWNvZ25pemVkIGVycm9yc1wiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlc29sdmVFcnJvclJlc3BvbnNlKG5ldyBFcnJvcihcImJvb21cIikpKS50b0VxdWFsKHtcbiAgICAgIHN0YXR1c0NvZGU6IDUwMCxcbiAgICAgIG1lc3NhZ2U6IFwiSW50ZXJuYWwgU2VydmVyIEVycm9yXCIsXG4gICAgfSk7XG4gICAgZXhwZWN0KHJlc29sdmVFcnJvclJlc3BvbnNlKFwibm90IGFuIGVycm9yXCIpKS50b0VxdWFsKHtcbiAgICAgIHN0YXR1c0NvZGU6IDUwMCxcbiAgICAgIG1lc3NhZ2U6IFwiSW50ZXJuYWwgU2VydmVyIEVycm9yXCIsXG4gICAgfSk7XG4gIH0pO1xufSk7XG4iXX0=