cdk-nextjs 0.5.15 → 0.6.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.
Files changed (53) hide show
  1. package/.jsii +821 -253
  2. package/.jsii.tabl.json +1 -1
  3. package/API.md +787 -18
  4. package/README.md +3 -3
  5. package/docs/breaking-changes.md +86 -0
  6. package/docs/caching-guide.md +6 -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/generated-structs/OptionalNextjsDistributionProps.d.ts +13 -0
  10. package/lib/generated-structs/OptionalNextjsDistributionProps.js +1 -1
  11. package/lib/image-optimization/handler-utils.d.ts +34 -0
  12. package/lib/image-optimization/handler-utils.js +83 -0
  13. package/lib/image-optimization/handler-utils.test.d.ts +1 -0
  14. package/lib/image-optimization/handler-utils.test.js +178 -0
  15. package/lib/image-optimization/handler.mjs +56126 -0
  16. package/lib/index.d.ts +2 -1
  17. package/lib/index.js +4 -2
  18. package/lib/nextjs-api.d.ts +44 -0
  19. package/lib/nextjs-api.js +64 -20
  20. package/lib/nextjs-api.test.d.ts +1 -0
  21. package/lib/nextjs-api.test.js +144 -0
  22. package/lib/nextjs-build/nextjs-build.d.ts +60 -0
  23. package/lib/nextjs-build/nextjs-build.js +184 -27
  24. package/lib/nextjs-cache.js +1 -1
  25. package/lib/nextjs-compute/nextjs-containers.js +1 -1
  26. package/lib/nextjs-compute/nextjs-functions.js +1 -1
  27. package/lib/nextjs-compute/nextjs-image-function.d.ts +53 -0
  28. package/lib/nextjs-compute/nextjs-image-function.js +59 -0
  29. package/lib/nextjs-distribution.d.ts +33 -0
  30. package/lib/nextjs-distribution.js +39 -11
  31. package/lib/nextjs-post-deploy.js +1 -1
  32. package/lib/nextjs-static-assets.d.ts +18 -0
  33. package/lib/nextjs-static-assets.js +33 -6
  34. package/lib/nextjs-static-assets.test.d.ts +1 -0
  35. package/lib/nextjs-static-assets.test.js +58 -0
  36. package/lib/root-constructs/nextjs-base-construct.d.ts +62 -2
  37. package/lib/root-constructs/nextjs-base-construct.js +81 -3
  38. package/lib/root-constructs/nextjs-global-containers.d.ts +6 -1
  39. package/lib/root-constructs/nextjs-global-containers.js +11 -5
  40. package/lib/root-constructs/nextjs-global-functions.d.ts +16 -5
  41. package/lib/root-constructs/nextjs-global-functions.js +23 -21
  42. package/lib/root-constructs/nextjs-regional-containers.js +1 -1
  43. package/lib/root-constructs/nextjs-regional-functions.d.ts +11 -5
  44. package/lib/root-constructs/nextjs-regional-functions.js +17 -21
  45. package/lib/utils/base-path.d.ts +40 -0
  46. package/lib/utils/base-path.js +123 -0
  47. package/lib/utils/base-path.test.d.ts +1 -0
  48. package/lib/utils/base-path.test.js +163 -0
  49. package/lib/utils/experimental-flags.d.ts +24 -0
  50. package/lib/utils/experimental-flags.js +30 -0
  51. package/lib/utils/get-architecture.d.ts +10 -0
  52. package/lib/utils/get-architecture.js +17 -2
  53. package/package.json +3 -1
@@ -0,0 +1,295 @@
1
+ # Phase 0 — Make the dedicated image optimization Lambda opt-in
2
+
3
+ Handoff plan. Self-contained; assumes no context from the session that wrote it.
4
+
5
+ ## Goal
6
+
7
+ Land the `feat-nextjs-img-opt` branch **with zero breaking changes** by making the
8
+ dedicated image optimization Lambda opt-in (`dedicatedImageFunction`, default
9
+ `false`). The default stays what `main` does today: `_next/image` is served by the
10
+ Next.js server function.
11
+
12
+ ## Why
13
+
14
+ The branch currently routes `_next/image` to a dedicated Lambda unconditionally.
15
+ That Lambda is not the Next.js server, so **Next.js middleware no longer runs for
16
+ image requests** — a real breaking change for anyone whose middleware matcher
17
+ covers `/_next/image` (the default matcher `/:path*` does; the matcher
18
+ `create-next-app` scaffolds does not).
19
+
20
+ Making it opt-in means:
21
+
22
+ - No breaking change, no migration doc.
23
+ - The sharp fix in `40b7837` ships now. Without it the dedicated Lambda serves
24
+ every image as unoptimized original bytes with an HTTP 200 and nothing in the
25
+ logs — see "Background" below.
26
+ - The flag is not throwaway. A later phase adds a middleware runner to the image
27
+ Lambda (using `outputs.middleware` from the Next.js adapters API), and *that*
28
+ release flips the default and deprecates the flag.
29
+
30
+ ## Starting state
31
+
32
+ Branch `feat-nextjs-img-opt`. `HEAD` = `40b7837 fix: ship a loadable sharp to the
33
+ image optimization Lambda`.
34
+
35
+ Ten files are **uncommitted** and complete — a second batch of code-review fixes.
36
+ All checks were green when they were written (`tsc`, `jest src/` 51 passing,
37
+ `eslint`, `jsii`):
38
+
39
+ | File | What changed |
40
+ | --- | --- |
41
+ | `src/image-optimization/handler-utils.ts` + `.test.ts` | S3 key derivation now *ensures* the `basePath` prefix instead of stripping it (`NextjsStaticAssets` uploads under `basePath`), and matches `basePath` only on a path boundary so `/basement/logo.png` isn't read as `/base` + `ment/...` |
42
+ | `src/image-optimization/handler.mts` | 304 responses only write the API-Gateway-appeasing space byte on the API Gateway path (RFC 9110 forbids a body on 304); adds `isApiGatewayEvent` type guard |
43
+ | `src/utils/get-architecture.ts` | `getNodeArchitecture()` as the single source of truth, replacing two inline `process.arch.startsWith("arm")` copies |
44
+ | `src/nextjs-build/nextjs-build.ts` | Uses `getNodeArchitecture()` |
45
+ | `src/root-constructs/nextjs-base-construct.ts`, `nextjs-global-functions.ts`, `nextjs-regional-functions.ts`, `src/index.ts` | New `NextjsFunctionsConstructOverrides` holds `nextjsFunctionsProps` / `nextjsImageFunctionProps`, which the Containers constructs previously accepted and silently ignored |
46
+ | `docs/breaking-changes.md` | Two new `## 0.6.0` sections |
47
+
48
+ ### Step 0 — commit them first
49
+
50
+ ```
51
+ git add -A && git commit
52
+ ```
53
+
54
+ Suggested message: `fix: correct image optimization basePath keys, 304 bodies, and Functions-only overrides`
55
+
56
+ Commit as-is even though Step 5 deletes one of the breaking-changes sections it
57
+ adds — each commit stays internally consistent, and the sections are removed in
58
+ the same PR.
59
+
60
+ ## Design decisions (already made — don't relitigate)
61
+
62
+ **Flag name:** `dedicatedImageFunction?: boolean`, `@default false`.
63
+
64
+ **Where it goes:** three sibling interfaces.
65
+
66
+ - `NextjsBaseConstructProps` (`src/root-constructs/nextjs-base-construct.ts:124`)
67
+ — so `NextjsBaseConstruct` can read `this.baseProps.dedicatedImageFunction`
68
+ when it builds `NextjsBuild`.
69
+ - `NextjsGlobalFunctionsProps` (`src/root-constructs/nextjs-global-functions.ts:47`)
70
+ - `NextjsRegionalFunctionsProps` (`src/root-constructs/nextjs-regional-functions.ts:43`)
71
+
72
+ **Not** on `NextjsBaseProps` — the Containers constructs would accept it and
73
+ silently ignore it, which is the exact anti-pattern the uncommitted
74
+ `NextjsFunctionsConstructOverrides` change fixes.
75
+
76
+ > JSII note: all three interfaces extend `NextjsBaseProps` but none extends
77
+ > another, so declaring the same member on all three is legal. Declaring it on
78
+ > both a parent and a child interface is **not** — that produces
79
+ > `Interface ... re-declares member`, the error documented at
80
+ > `nextjs-base-construct.ts:120-123`.
81
+
82
+ **Why a boolean and not an enum:** JSII rejects string-literal union types, so a
83
+ `"server" | "dedicated"` choice would need a real exported enum. Not worth it for
84
+ a flag that gets deprecated in two releases.
85
+
86
+ ## Tasks
87
+
88
+ ### 1. `src/nextjs-build/nextjs-build.ts` — the part that will bite
89
+
90
+ Add `dedicatedImageFunction?: boolean` to `NextjsBuildProps`, then rework the
91
+ constructor block at **lines 132-152**.
92
+
93
+ Current logic strips whatever platform sharp binaries output file tracing
94
+ bundled, then installs musl replacements **only for Containers**, on the
95
+ assumption that Functions deployments never invoke sharp from the standalone
96
+ server. That assumption is exactly what this flag breaks.
97
+
98
+ ```ts
99
+ const useDedicatedImageFunction =
100
+ isFunctions && props.dedicatedImageFunction === true;
101
+
102
+ this.removeExistingSharpBinaries(standalonePath);
103
+ // The standalone server serves `_next/image` itself unless a dedicated image
104
+ // Lambda takes that route, and it runs on node:24-alpine (see
105
+ // functions.Dockerfile), so it needs musl binaries.
106
+ if (!useDedicatedImageFunction) {
107
+ this.downloadAndInstallSharpBinaries();
108
+ }
109
+ if (useDedicatedImageFunction) {
110
+ this.imageOptimizationAssetPath =
111
+ this.prepareImageOptimizationAssets(standalonePath);
112
+ }
113
+ ```
114
+
115
+ **Get this right or Phase 0 ships broken image optimization on the default
116
+ path.** `removeExistingSharpBinaries()` runs unconditionally and deletes the
117
+ host's (macOS/glibc) binaries. If the musl install doesn't run for Functions
118
+ deployments that no longer have a dedicated image Lambda, `require('sharp')`
119
+ throws inside the server, Next.js's `imageOptimizer` catches it internally and
120
+ returns the *original* buffer, and you get an HTTP 200 with plausible headers
121
+ serving unoptimized images. Silent. This is the same failure the branch already
122
+ fixed once for the dedicated Lambda; don't reintroduce it on the default path.
123
+
124
+ Also update the comment at lines 137-143, which currently states the now-
125
+ conditional claim as fact.
126
+
127
+ Adding a prop to `NextjsBuildProps` means `OptionalNextjsBuildProps` must be
128
+ regenerated — see Verification.
129
+
130
+ ### 2. `src/root-constructs/nextjs-base-construct.ts`
131
+
132
+ - Add the prop to `NextjsBaseConstructProps`.
133
+ - Pass it into `NextjsBuild` in `createNextjsBuild()` (line 195).
134
+ - `createNextjsImageFunction()` (line 251) keeps throwing when
135
+ `imageOptimizationAssetPath` is missing — that's still a genuine invariant
136
+ violation, since callers only reach it when the flag is on.
137
+
138
+ ### 3. The two Functions root constructs
139
+
140
+ Make the field optional and conditional:
141
+
142
+ ```ts
143
+ nextjsImageFunction?: NextjsImageFunction;
144
+ // ...
145
+ if (props.dedicatedImageFunction) {
146
+ this.nextjsImageFunction = this.createNextjsImageFunction(
147
+ this.props.overrides?.nextjsImageFunction,
148
+ );
149
+ }
150
+ ```
151
+
152
+ - `nextjs-global-functions.ts:67` (field), `:83` (creation), `:95` — pass
153
+ `imageFunctionUrl: this.nextjsImageFunction?.functionUrl`.
154
+ - `nextjs-regional-functions.ts:56` (field), `:76` (creation), `:87` — pass
155
+ `imageFunction: this.nextjsImageFunction?.function`.
156
+
157
+ An optional class property is fine for JSII (`NextjsBuild.imageOptimizationAssetPath`
158
+ already is one).
159
+
160
+ ### 4. Make the two consumers tolerate a missing image function
161
+
162
+ **`src/nextjs-api.ts`** — revert this branch's tightening:
163
+
164
+ - `validateProps` (line 110-114): drop the "imageFunction must be set when
165
+ serverFunction is set" throw.
166
+ - Constructor (line 97-100): call `createImageIntegration` only when
167
+ `props.imageFunction` is set. With no image integration, `_next/image` falls
168
+ through to the `{proxy+}` catch-all on the server function — which is what
169
+ `main` does.
170
+ - Update the `imageFunction` doc comment (line 53-62): no longer "Required if
171
+ `serverFunction` is set".
172
+
173
+ **`src/nextjs-distribution.ts`** — `createImageOrigin()` (line 201-215) already
174
+ falls back to `this.dynamicOrigin` for non-Functions compute. Extend that
175
+ fallback instead of throwing:
176
+
177
+ ```ts
178
+ if (!this.isFunctionCompute || !this.props.imageFunctionUrl) {
179
+ return this.dynamicOrigin;
180
+ }
181
+ ```
182
+
183
+ Keep the dedicated `_next/image*` behavior and its image-specific cache policy
184
+ in both cases — pointing that behavior at the dynamic origin is strictly better
185
+ than dropping it, because the image cache policy (`queryStringBehavior: all()`,
186
+ `accept` in the cache key) is the right one for image requests either way. Update
187
+ the doc comment on `imageFunctionUrl` (line 79-83) and on `createImageOrigin`.
188
+
189
+ ### 5. `docs/breaking-changes.md`
190
+
191
+ From the `## 0.6.0` section added in Step 0:
192
+
193
+ - **Delete** "`_next/image` no longer runs Next.js middleware (Functions only)".
194
+ It isn't true once the flag defaults off.
195
+ - **Rewrite** "NextjsApi: `imageFunction` now required alongside
196
+ `serverFunction`" — it's optional again. Either delete it or restate it as a
197
+ non-breaking addition.
198
+ - **Keep** "Functions-only overrides moved off the Containers constructs". That
199
+ one is a real (tiny) breaking change and it ships.
200
+
201
+ Document the trade-off on the new prop's JSDoc, since that's what users will
202
+ read:
203
+
204
+ - **off (default)** — middleware runs for image requests; on Regional Functions
205
+ the response goes through API Gateway buffered (6 MB cap); images share the
206
+ server function's memory and concurrency; optimized images are cached via the
207
+ configured `cacheHandler`.
208
+ - **on** — dedicated Lambda with response streaming and independent sizing; no
209
+ middleware for `_next/image`; caching relies on CloudFront.
210
+ - Note that a future release will default this on once middleware support lands.
211
+
212
+ ### 6. Examples and e2e
213
+
214
+ Set `dedicatedImageFunction: true` in **`examples/global-functions/app.ts`** only.
215
+ Leave `examples/regional-functions/app.ts` on the default. That way CI covers
216
+ both paths: `glbl-fns` exercises the dedicated Lambda, `rgnl-fns` exercises the
217
+ server-function path.
218
+
219
+ `examples/e2e-tests/src/image-optimization.test.ts` should pass unchanged on both
220
+ — including the `content-type: image/webp` assertion added in `40b7837`, since
221
+ the server function re-encodes with sharp too (that's what Task 1 guarantees).
222
+ If it fails on `rgnl-fns`, suspect the musl install, not the test.
223
+
224
+ ### 7. Stretch — first construct test
225
+
226
+ `test/` currently holds only a `tsconfig.json`; there are no CDK assertion tests.
227
+ A worthwhile addition: with the flag off, assert the synthesized template has one
228
+ Lambda and the `_next/image*` cache behavior targets the server origin; with it
229
+ on, two Lambdas. The obstacle is that `NextjsBuild` runs `next build` in its
230
+ constructor, so a test needs `skipBuild: true` plus a committed fixture `.next`
231
+ directory. Skip if it balloons; it's not a Phase 0 blocker.
232
+
233
+ ## Verification
234
+
235
+ ```bash
236
+ npx tsc --noEmit -p tsconfig.json
237
+ npx jest src/ # 51 tests before this change
238
+ ESLINT_USE_FLAT_CONFIG=false npx eslint --fix --ext .ts src/
239
+ npx jsii --silence-warnings=reserved-word
240
+ ```
241
+
242
+ Then, because `NextjsBuildProps` changed, in this order (the build-order quirk in
243
+ `CLAUDE.md`):
244
+
245
+ ```bash
246
+ pnpm compile # writes .jsii
247
+ pnpm projen # regenerates src/generated-structs/OptionalNextjsBuildProps.ts from .jsii
248
+ pnpm build
249
+ npx jsii-docgen -o API.md # API.md is committed and generated from .jsii
250
+ ```
251
+
252
+ Notes:
253
+
254
+ - `pnpm eslint` and `pnpm projen` shell out to `pnpm dlx projen`, which needs
255
+ network access to `registry.npmjs.org`. In a sandboxed session that fails with
256
+ `ERR_PNPM_FETCH_403`; the `npx` forms above work around it.
257
+ - `src/image-optimization/handler.mts` cannot be linted — pre-existing, the
258
+ ESLint project service only covers `.ts`/`.tsx`.
259
+ - `API.md` is 11,973 lines and committed. Forgetting to regenerate it will show
260
+ up as a CI self-mutation diff.
261
+
262
+ ## Out of scope
263
+
264
+ - Any middleware execution. That's the next phase: copy
265
+ `ctx.outputs.middleware.filePath` + `assets` in `onBuildComplete`, match on
266
+ `config.matchers[].sourceRegex` with `has`/`missing`, invoke the entrypoint's
267
+ exported `handler(req, res, ctx)`. Node runtime only — edge is deprecated in
268
+ Next.js 16.
269
+ - Replacing the server function with adapter entrypoints.
270
+ - Widening the image cache policy's `cookieBehavior` (currently `none()` at
271
+ `nextjs-distribution.ts:338`). Only matters once middleware makes
272
+ cookie-dependent decisions for image requests.
273
+
274
+ ## Background: the bug that started this
275
+
276
+ `next/dist/server/image-optimizer.js` reports a failed optimization by
277
+ **returning** `{ buffer: upstreamBuffer, ..., error }` rather than throwing. The
278
+ handler dropped `error`, so a broken sharp produced a valid HTTP 200 serving
279
+ full-size originals forever, with clean logs. It got through four green e2e jobs
280
+ and a live CloudFront endpoint that rendered all three test images correctly.
281
+
282
+ It was caught by comparing bytes: the `_next/image` response was byte-identical
283
+ to the raw S3 asset (31,283 bytes, md5 `ac38a73ecb20a904d52ebc9b28aa171f`),
284
+ returned `content-type: image/png` despite `Accept: image/webp`, and carried
285
+ S3's MD5 ETag rather than Next.js's sha256/base64url one.
286
+
287
+ Root cause: `next build`'s output file tracing preserves the installer's layout,
288
+ and under pnpm there is never a hoisted `node_modules/sharp` — only
289
+ `node_modules/.pnpm/sharp@<version>/node_modules/sharp`. The old flat
290
+ `existsSync` check never matched, so sharp was never bundled. Fixed in `40b7837`
291
+ along with logging the swallowed `error`.
292
+
293
+ The lesson for every later phase: the e2e suite asserts status codes, so it
294
+ cannot see semantically wrong but syntactically fine responses. Assertions on
295
+ bytes and headers are what caught this.
@@ -9,6 +9,16 @@ export interface OptionalNextjsDistributionProps {
9
9
  * @stability stable
10
10
  */
11
11
  readonly loadBalancer?: aws_elasticloadbalancingv2.IApplicationLoadBalancer;
12
+ /**
13
+ * Function URL of the dedicated image optimization Lambda.
14
+ * Only applicable
15
+ * to `NextjsType.GLOBAL_FUNCTIONS`, and only when the dedicated image
16
+ * function is enabled. When omitted, the `_next/image*` behavior points at
17
+ * the dynamic origin, which serves image optimization from the Next.js
18
+ * server itself.
19
+ * @stability stable
20
+ */
21
+ readonly imageFunctionUrl?: aws_lambda.IFunctionUrl;
12
22
  /**
13
23
  * Required if `NextjsType.GLOBAL_FUNCTIONS`.
14
24
  * @stability stable
@@ -24,6 +34,9 @@ export interface OptionalNextjsDistributionProps {
24
34
  */
25
35
  readonly certificate?: aws_certificatemanager.ICertificate;
26
36
  /**
37
+ * URI path prefix the app is served at.
38
+ * Surrounding slashes are normalized
39
+ * away, so "/base", "base" and "/base/" all produce the same cache behaviors.
27
40
  * @stability stable
28
41
  */
29
42
  readonly basePath?: string;
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3B0aW9uYWxOZXh0anNEaXN0cmlidXRpb25Qcm9wcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9nZW5lcmF0ZWQtc3RydWN0cy9PcHRpb25hbE5leHRqc0Rpc3RyaWJ1dGlvblByb3BzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIvLyB+fiBHZW5lcmF0ZWQgYnkgcHJvamVuLiBUbyBtb2RpZnksIGVkaXQgLnByb2plbnJjLnRzIGFuZCBydW4gXCJwbnBtIGRseCBwcm9qZW5cIi5cbmltcG9ydCB0eXBlIHsgYXdzX2NlcnRpZmljYXRlbWFuYWdlciwgYXdzX2Nsb3VkZnJvbnQsIGF3c19lbGFzdGljbG9hZGJhbGFuY2luZ3YyLCBhd3NfbGFtYmRhLCBhd3NfczMgfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgdHlwZSB7IE5leHRqc1R5cGUsIFB1YmxpY0RpckVudHJ5IH0gZnJvbSAnLi4vJztcblxuLyoqXG4gKiBPcHRpb25hbE5leHRqc0Rpc3RyaWJ1dGlvblByb3BzXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgT3B0aW9uYWxOZXh0anNEaXN0cmlidXRpb25Qcm9wcyB7XG4gIC8qKlxuICAgKiBSZXF1aXJlZCBpZiBgTmV4dGpzVHlwZS5HTE9CQUxfQ09OVEFJTkVSU2Agb3IgYE5leHRqc1R5cGUuUkVHSU9OQUxfQ09OVEFJTkVSU2AuXG4gICAqIEBzdGFiaWxpdHkgc3RhYmxlXG4gICAqL1xuICByZWFkb25seSBsb2FkQmFsYW5jZXI/OiBhd3NfZWxhc3RpY2xvYWRiYWxhbmNpbmd2Mi5JQXBwbGljYXRpb25Mb2FkQmFsYW5jZXI7XG4gIC8qKlxuICAgKiBSZXF1aXJlZCBpZiBgTmV4dGpzVHlwZS5HTE9CQUxfRlVOQ1RJT05TYC5cbiAgICogQHN0YWJpbGl0eSBzdGFibGVcbiAgICovXG4gIHJlYWRvbmx5IGZ1bmN0aW9uVXJsPzogYXdzX2xhbWJkYS5JRnVuY3Rpb25Vcmw7XG4gIC8qKlxuICAgKiBAc3RhYmlsaXR5IHN0YWJsZVxuICAgKi9cbiAgcmVhZG9ubHkgZGlzdHJpYnV0aW9uPzogYXdzX2Nsb3VkZnJvbnQuRGlzdHJpYnV0aW9uO1xuICAvKipcbiAgICogT3B0aW9uYWwgYnV0IG9ubHkgYXBwbGljYWJsZSBmb3IgYE5leHRqc1R5cGUuR0xPQkFMX0NPTlRBSU5FUlNgLlxuICAgKiBAc3RhYmlsaXR5IHN0YWJsZVxuICAgKi9cbiAgcmVhZG9ubHkgY2VydGlmaWNhdGU/OiBhd3NfY2VydGlmaWNhdGVtYW5hZ2VyLklDZXJ0aWZpY2F0ZTtcbiAgLyoqXG4gICAqIEBzdGFiaWxpdHkgc3RhYmxlXG4gICAqL1xuICByZWFkb25seSBiYXNlUGF0aD86IHN0cmluZztcbiAgLyoqXG4gICAqIEVudHJpZXMgKGZpbGVzL2RpcmVjdG9yaWVzKSB3aXRoaW4gTmV4dC5qcyBhcHAncyBwdWJsaWMgZGlyZWN0b3J5LiBVc2VkIHRvIGFkZCBzdGF0aWMgYmVoYXZpb3JzIHRvIGRpc3RyaWJ1dGlvbi5cbiAgICogQHN0YWJpbGl0eSBzdGFibGVcbiAgICovXG4gIHJlYWRvbmx5IHB1YmxpY0RpckVudHJpZXM/OiBBcnJheTxQdWJsaWNEaXJFbnRyeT47XG4gIC8qKlxuICAgKiBAc3RhYmlsaXR5IHN0YWJsZVxuICAgKi9cbiAgcmVhZG9ubHkgbmV4dGpzVHlwZT86IE5leHRqc1R5cGU7XG4gIC8qKlxuICAgKiBCdWNrZXQgY29udGFpbmluZyBzdGF0aWMgYXNzZXRzLlxuICAgKiBNdXN0IGJlIHByb3ZpZGVkIGlmIHlvdSB3YW50IHRvIHNlcnZlIHN0YXRpYyBmaWxlcy5cbiAgICogQHN0YWJpbGl0eSBzdGFibGVcbiAgICovXG4gIHJlYWRvbmx5IGFzc2V0c0J1Y2tldD86IGF3c19zMy5JQnVja2V0O1xufVxuIl19
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3B0aW9uYWxOZXh0anNEaXN0cmlidXRpb25Qcm9wcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9nZW5lcmF0ZWQtc3RydWN0cy9PcHRpb25hbE5leHRqc0Rpc3RyaWJ1dGlvblByb3BzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIvLyB+fiBHZW5lcmF0ZWQgYnkgcHJvamVuLiBUbyBtb2RpZnksIGVkaXQgLnByb2plbnJjLnRzIGFuZCBydW4gXCJwbnBtIGRseCBwcm9qZW5cIi5cbmltcG9ydCB0eXBlIHsgYXdzX2NlcnRpZmljYXRlbWFuYWdlciwgYXdzX2Nsb3VkZnJvbnQsIGF3c19lbGFzdGljbG9hZGJhbGFuY2luZ3YyLCBhd3NfbGFtYmRhLCBhd3NfczMgfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgdHlwZSB7IE5leHRqc1R5cGUsIFB1YmxpY0RpckVudHJ5IH0gZnJvbSAnLi4vJztcblxuLyoqXG4gKiBPcHRpb25hbE5leHRqc0Rpc3RyaWJ1dGlvblByb3BzXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgT3B0aW9uYWxOZXh0anNEaXN0cmlidXRpb25Qcm9wcyB7XG4gIC8qKlxuICAgKiBSZXF1aXJlZCBpZiBgTmV4dGpzVHlwZS5HTE9CQUxfQ09OVEFJTkVSU2Agb3IgYE5leHRqc1R5cGUuUkVHSU9OQUxfQ09OVEFJTkVSU2AuXG4gICAqIEBzdGFiaWxpdHkgc3RhYmxlXG4gICAqL1xuICByZWFkb25seSBsb2FkQmFsYW5jZXI/OiBhd3NfZWxhc3RpY2xvYWRiYWxhbmNpbmd2Mi5JQXBwbGljYXRpb25Mb2FkQmFsYW5jZXI7XG4gIC8qKlxuICAgKiBGdW5jdGlvbiBVUkwgb2YgdGhlIGRlZGljYXRlZCBpbWFnZSBvcHRpbWl6YXRpb24gTGFtYmRhLlxuICAgKiBPbmx5IGFwcGxpY2FibGVcbiAgICogdG8gYE5leHRqc1R5cGUuR0xPQkFMX0ZVTkNUSU9OU2AsIGFuZCBvbmx5IHdoZW4gdGhlIGRlZGljYXRlZCBpbWFnZVxuICAgKiBmdW5jdGlvbiBpcyBlbmFibGVkLiBXaGVuIG9taXR0ZWQsIHRoZSBgX25leHQvaW1hZ2UqYCBiZWhhdmlvciBwb2ludHMgYXRcbiAgICogdGhlIGR5bmFtaWMgb3JpZ2luLCB3aGljaCBzZXJ2ZXMgaW1hZ2Ugb3B0aW1pemF0aW9uIGZyb20gdGhlIE5leHQuanNcbiAgICogc2VydmVyIGl0c2VsZi5cbiAgICogQHN0YWJpbGl0eSBzdGFibGVcbiAgICovXG4gIHJlYWRvbmx5IGltYWdlRnVuY3Rpb25Vcmw/OiBhd3NfbGFtYmRhLklGdW5jdGlvblVybDtcbiAgLyoqXG4gICAqIFJlcXVpcmVkIGlmIGBOZXh0anNUeXBlLkdMT0JBTF9GVU5DVElPTlNgLlxuICAgKiBAc3RhYmlsaXR5IHN0YWJsZVxuICAgKi9cbiAgcmVhZG9ubHkgZnVuY3Rpb25Vcmw/OiBhd3NfbGFtYmRhLklGdW5jdGlvblVybDtcbiAgLyoqXG4gICAqIEBzdGFiaWxpdHkgc3RhYmxlXG4gICAqL1xuICByZWFkb25seSBkaXN0cmlidXRpb24/OiBhd3NfY2xvdWRmcm9udC5EaXN0cmlidXRpb247XG4gIC8qKlxuICAgKiBPcHRpb25hbCBidXQgb25seSBhcHBsaWNhYmxlIGZvciBgTmV4dGpzVHlwZS5HTE9CQUxfQ09OVEFJTkVSU2AuXG4gICAqIEBzdGFiaWxpdHkgc3RhYmxlXG4gICAqL1xuICByZWFkb25seSBjZXJ0aWZpY2F0ZT86IGF3c19jZXJ0aWZpY2F0ZW1hbmFnZXIuSUNlcnRpZmljYXRlO1xuICAvKipcbiAgICogVVJJIHBhdGggcHJlZml4IHRoZSBhcHAgaXMgc2VydmVkIGF0LlxuICAgKiBTdXJyb3VuZGluZyBzbGFzaGVzIGFyZSBub3JtYWxpemVkXG4gICAqIGF3YXksIHNvIFwiL2Jhc2VcIiwgXCJiYXNlXCIgYW5kIFwiL2Jhc2UvXCIgYWxsIHByb2R1Y2UgdGhlIHNhbWUgY2FjaGUgYmVoYXZpb3JzLlxuICAgKiBAc3RhYmlsaXR5IHN0YWJsZVxuICAgKi9cbiAgcmVhZG9ubHkgYmFzZVBhdGg/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBFbnRyaWVzIChmaWxlcy9kaXJlY3Rvcmllcykgd2l0aGluIE5leHQuanMgYXBwJ3MgcHVibGljIGRpcmVjdG9yeS4gVXNlZCB0byBhZGQgc3RhdGljIGJlaGF2aW9ycyB0byBkaXN0cmlidXRpb24uXG4gICAqIEBzdGFiaWxpdHkgc3RhYmxlXG4gICAqL1xuICByZWFkb25seSBwdWJsaWNEaXJFbnRyaWVzPzogQXJyYXk8UHVibGljRGlyRW50cnk+O1xuICAvKipcbiAgICogQHN0YWJpbGl0eSBzdGFibGVcbiAgICovXG4gIHJlYWRvbmx5IG5leHRqc1R5cGU/OiBOZXh0anNUeXBlO1xuICAvKipcbiAgICogQnVja2V0IGNvbnRhaW5pbmcgc3RhdGljIGFzc2V0cy5cbiAgICogTXVzdCBiZSBwcm92aWRlZCBpZiB5b3Ugd2FudCB0byBzZXJ2ZSBzdGF0aWMgZmlsZXMuXG4gICAqIEBzdGFiaWxpdHkgc3RhYmxlXG4gICAqL1xuICByZWFkb25seSBhc3NldHNCdWNrZXQ/OiBhd3NfczMuSUJ1Y2tldDtcbn1cbiJdfQ==
@@ -0,0 +1,34 @@
1
+ import { S3Client } from "@aws-sdk/client-s3";
2
+ /**
3
+ * Fetches a non-absolute (local) image referenced by an `<Image>` from S3.
4
+ *
5
+ * `url` is inconsistent: next-image-loader bakes the app's own `basePath` into
6
+ * the href for statically imported images, while plain string paths are passed
7
+ * through as literally written. So `nextBasePath` (the app's config) is stripped
8
+ * off, then `staticAssetsKeyPrefix` (`NextjsStaticAssets.keyPrefix`) applied.
9
+ * The two are separate arguments because nothing requires them to be the same
10
+ * value — e.g. an app whose `basePath` is an API Gateway stage, deployed without
11
+ * the CDK `basePath` prop.
12
+ */
13
+ export declare function fetchFromS3(s3: S3Client, bucket: string, url: string, nextBasePath: string, staticAssetsKeyPrefix?: string): Promise<{
14
+ buffer: Buffer;
15
+ contentType: string | null;
16
+ etag: string;
17
+ }>;
18
+ /** Mirrors Next.js's own `getFileNameWithExtension` in image-optimizer.js. */
19
+ export declare function getFileNameWithExtension(url: string, contentType: string | null): string;
20
+ /**
21
+ * Maps an error thrown while processing an image request to the HTTP
22
+ * status/message it should produce, preserving the status Next.js's own
23
+ * `ImageError`/`fetchExternalImage` attach. A missing S3 object is mapped to
24
+ * the same 400 Next.js's own local-image fetch path (`fetchInternalImage` in
25
+ * image-optimizer.js) produces for a missing file: it never inspects the
26
+ * internal request's status code, so a 404 there just flows into the normal
27
+ * "not a valid image" content-type check as if it were malformed image
28
+ * bytes. This mirrors that behavior instead of surfacing a 404, so Functions
29
+ * and Containers deployments respond identically for this case.
30
+ */
31
+ export declare function resolveErrorResponse(error: unknown): {
32
+ statusCode: number;
33
+ message: string;
34
+ };
@@ -0,0 +1,83 @@
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
+ const base_path_1 = require("../utils/base-path");
11
+ /**
12
+ * Fetches a non-absolute (local) image referenced by an `<Image>` from S3.
13
+ *
14
+ * `url` is inconsistent: next-image-loader bakes the app's own `basePath` into
15
+ * the href for statically imported images, while plain string paths are passed
16
+ * through as literally written. So `nextBasePath` (the app's config) is stripped
17
+ * off, then `staticAssetsKeyPrefix` (`NextjsStaticAssets.keyPrefix`) applied.
18
+ * The two are separate arguments because nothing requires them to be the same
19
+ * value — e.g. an app whose `basePath` is an API Gateway stage, deployed without
20
+ * the CDK `basePath` prop.
21
+ */
22
+ async function fetchFromS3(s3, bucket, url, nextBasePath, staticAssetsKeyPrefix = "") {
23
+ // Matching on a path boundary keeps a sibling like "/basement/logo.png" from
24
+ // being treated as nextBasePath "/base" plus "ment/logo.png". It's still
25
+ // indistinguishable from a real `public/base/` directory, which loses; that's
26
+ // the right trade, since every statically imported image carries the prefix
27
+ // and the alternative costs an S3 round trip per request to detect it.
28
+ const hasNextBasePath = !!nextBasePath &&
29
+ (url === nextBasePath || url.startsWith(`${nextBasePath}/`));
30
+ const key = (0, base_path_1.joinPath)(staticAssetsKeyPrefix, hasNextBasePath ? url.slice(nextBasePath.length) : url);
31
+ const response = await s3.send(new client_s3_1.GetObjectCommand({
32
+ Bucket: bucket,
33
+ Key: key,
34
+ }));
35
+ const body = response.Body;
36
+ if (!body) {
37
+ throw new Error(`Empty response from S3 for key: ${key}`);
38
+ }
39
+ const chunks = [];
40
+ for await (const chunk of body) {
41
+ chunks.push(Buffer.from(chunk));
42
+ }
43
+ return {
44
+ buffer: Buffer.concat(chunks),
45
+ contentType: response.ContentType || null,
46
+ etag: response.ETag || "",
47
+ };
48
+ }
49
+ /** Mirrors Next.js's own `getFileNameWithExtension` in image-optimizer.js. */
50
+ function getFileNameWithExtension(url, contentType) {
51
+ const [urlWithoutQueryParams] = url.split("?", 1);
52
+ const fileNameWithExtension = urlWithoutQueryParams.split("/").pop();
53
+ if (!contentType || !fileNameWithExtension) {
54
+ return "image.bin";
55
+ }
56
+ const [fileName] = fileNameWithExtension.split(".", 1);
57
+ const extension = (0, serve_static_js_1.getExtension)(contentType);
58
+ return `${fileName}.${extension}`;
59
+ }
60
+ /**
61
+ * Maps an error thrown while processing an image request to the HTTP
62
+ * status/message it should produce, preserving the status Next.js's own
63
+ * `ImageError`/`fetchExternalImage` attach. A missing S3 object is mapped to
64
+ * the same 400 Next.js's own local-image fetch path (`fetchInternalImage` in
65
+ * image-optimizer.js) produces for a missing file: it never inspects the
66
+ * internal request's status code, so a 404 there just flows into the normal
67
+ * "not a valid image" content-type check as if it were malformed image
68
+ * bytes. This mirrors that behavior instead of surfacing a 404, so Functions
69
+ * and Containers deployments respond identically for this case.
70
+ */
71
+ function resolveErrorResponse(error) {
72
+ if (error instanceof image_optimizer_js_1.ImageError) {
73
+ return { statusCode: error.statusCode, message: error.message };
74
+ }
75
+ if (error instanceof Error && error.name === "NoSuchKey") {
76
+ return {
77
+ statusCode: 400,
78
+ message: "The requested resource isn't a valid image.",
79
+ };
80
+ }
81
+ return { statusCode: 500, message: "Internal Server Error" };
82
+ }
83
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlci11dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pbWFnZS1vcHRpbWl6YXRpb24vaGFuZGxlci11dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQWlCQSxrQ0EwQ0M7QUFHRCw0REFZQztBQWFELG9EQWNDO0FBckdELHNEQUFzRDtBQUN0RCxrREFBZ0U7QUFDaEUsNEVBQWlFO0FBQ2pFLHNFQUFnRTtBQUNoRSxrREFBOEM7QUFFOUM7Ozs7Ozs7Ozs7R0FVRztBQUNJLEtBQUssVUFBVSxXQUFXLENBQy9CLEVBQVksRUFDWixNQUFjLEVBQ2QsR0FBVyxFQUNYLFlBQW9CLEVBQ3BCLHdCQUFnQyxFQUFFO0lBRWxDLDZFQUE2RTtJQUM3RSx5RUFBeUU7SUFDekUsOEVBQThFO0lBQzlFLDRFQUE0RTtJQUM1RSx1RUFBdUU7SUFDdkUsTUFBTSxlQUFlLEdBQ25CLENBQUMsQ0FBQyxZQUFZO1FBQ2QsQ0FBQyxHQUFHLEtBQUssWUFBWSxJQUFJLEdBQUcsQ0FBQyxVQUFVLENBQUMsR0FBRyxZQUFZLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDL0QsTUFBTSxHQUFHLEdBQUcsSUFBQSxvQkFBUSxFQUNsQixxQkFBcUIsRUFDckIsZUFBZSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUN2RCxDQUFDO0lBRUYsTUFBTSxRQUFRLEdBQUcsTUFBTSxFQUFFLENBQUMsSUFBSSxDQUM1QixJQUFJLDRCQUFnQixDQUFDO1FBQ25CLE1BQU0sRUFBRSxNQUFNO1FBQ2QsR0FBRyxFQUFFLEdBQUc7S0FDVCxDQUFDLENBQ0gsQ0FBQztJQUVGLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUM7SUFDM0IsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1YsTUFBTSxJQUFJLEtBQUssQ0FBQyxtQ0FBbUMsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUM1RCxDQUFDO0lBRUQsTUFBTSxNQUFNLEdBQWEsRUFBRSxDQUFDO0lBQzVCLElBQUksS0FBSyxFQUFFLE1BQU0sS0FBSyxJQUFJLElBQWlDLEVBQUUsQ0FBQztRQUM1RCxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUNsQyxDQUFDO0lBRUQsT0FBTztRQUNMLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUM3QixXQUFXLEVBQUUsUUFBUSxDQUFDLFdBQVcsSUFBSSxJQUFJO1FBQ3pDLElBQUksRUFBRSxRQUFRLENBQUMsSUFBSSxJQUFJLEVBQUU7S0FDMUIsQ0FBQztBQUNKLENBQUM7QUFFRCw4RUFBOEU7QUFDOUUsU0FBZ0Isd0JBQXdCLENBQ3RDLEdBQVcsRUFDWCxXQUEwQjtJQUUxQixNQUFNLENBQUMscUJBQXFCLENBQUMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNsRCxNQUFNLHFCQUFxQixHQUFHLHFCQUFxQixDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUNyRSxJQUFJLENBQUMsV0FBVyxJQUFJLENBQUMscUJBQXFCLEVBQUUsQ0FBQztRQUMzQyxPQUFPLFdBQVcsQ0FBQztJQUNyQixDQUFDO0lBQ0QsTUFBTSxDQUFDLFFBQVEsQ0FBQyxHQUFHLHFCQUFxQixDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDdkQsTUFBTSxTQUFTLEdBQUcsSUFBQSw4QkFBWSxFQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQzVDLE9BQU8sR0FBRyxRQUFRLElBQUksU0FBUyxFQUFFLENBQUM7QUFDcEMsQ0FBQztBQUVEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxTQUFnQixvQkFBb0IsQ0FBQyxLQUFjO0lBSWpELElBQUksS0FBSyxZQUFZLCtCQUFVLEVBQUUsQ0FBQztRQUNoQyxPQUFPLEVBQUUsVUFBVSxFQUFFLEtBQUssQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNsRSxDQUFDO0lBQ0QsSUFBSSxLQUFLLFlBQVksS0FBSyxJQUFJLEtBQUssQ0FBQyxJQUFJLEtBQUssV0FBVyxFQUFFLENBQUM7UUFDekQsT0FBTztZQUNMLFVBQVUsRUFBRSxHQUFHO1lBQ2YsT0FBTyxFQUFFLDZDQUE2QztTQUN2RCxDQUFDO0lBQ0osQ0FBQztJQUNELE9BQU8sRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxDQUFDO0FBQy9ELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXMgKi9cbmltcG9ydCB7IEdldE9iamVjdENvbW1hbmQsIFMzQ2xpZW50IH0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1zM1wiO1xuaW1wb3J0IHsgSW1hZ2VFcnJvciB9IGZyb20gXCJuZXh0L2Rpc3Qvc2VydmVyL2ltYWdlLW9wdGltaXplci5qc1wiO1xuaW1wb3J0IHsgZ2V0RXh0ZW5zaW9uIH0gZnJvbSBcIm5leHQvZGlzdC9zZXJ2ZXIvc2VydmUtc3RhdGljLmpzXCI7XG5pbXBvcnQgeyBqb2luUGF0aCB9IGZyb20gXCIuLi91dGlscy9iYXNlLXBhdGhcIjtcblxuLyoqXG4gKiBGZXRjaGVzIGEgbm9uLWFic29sdXRlIChsb2NhbCkgaW1hZ2UgcmVmZXJlbmNlZCBieSBhbiBgPEltYWdlPmAgZnJvbSBTMy5cbiAqXG4gKiBgdXJsYCBpcyBpbmNvbnNpc3RlbnQ6IG5leHQtaW1hZ2UtbG9hZGVyIGJha2VzIHRoZSBhcHAncyBvd24gYGJhc2VQYXRoYCBpbnRvXG4gKiB0aGUgaHJlZiBmb3Igc3RhdGljYWxseSBpbXBvcnRlZCBpbWFnZXMsIHdoaWxlIHBsYWluIHN0cmluZyBwYXRocyBhcmUgcGFzc2VkXG4gKiB0aHJvdWdoIGFzIGxpdGVyYWxseSB3cml0dGVuLiBTbyBgbmV4dEJhc2VQYXRoYCAodGhlIGFwcCdzIGNvbmZpZykgaXMgc3RyaXBwZWRcbiAqIG9mZiwgdGhlbiBgc3RhdGljQXNzZXRzS2V5UHJlZml4YCAoYE5leHRqc1N0YXRpY0Fzc2V0cy5rZXlQcmVmaXhgKSBhcHBsaWVkLlxuICogVGhlIHR3byBhcmUgc2VwYXJhdGUgYXJndW1lbnRzIGJlY2F1c2Ugbm90aGluZyByZXF1aXJlcyB0aGVtIHRvIGJlIHRoZSBzYW1lXG4gKiB2YWx1ZSDigJQgZS5nLiBhbiBhcHAgd2hvc2UgYGJhc2VQYXRoYCBpcyBhbiBBUEkgR2F0ZXdheSBzdGFnZSwgZGVwbG95ZWQgd2l0aG91dFxuICogdGhlIENESyBgYmFzZVBhdGhgIHByb3AuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmZXRjaEZyb21TMyhcbiAgczM6IFMzQ2xpZW50LFxuICBidWNrZXQ6IHN0cmluZyxcbiAgdXJsOiBzdHJpbmcsXG4gIG5leHRCYXNlUGF0aDogc3RyaW5nLFxuICBzdGF0aWNBc3NldHNLZXlQcmVmaXg6IHN0cmluZyA9IFwiXCIsXG4pOiBQcm9taXNlPHsgYnVmZmVyOiBCdWZmZXI7IGNvbnRlbnRUeXBlOiBzdHJpbmcgfCBudWxsOyBldGFnOiBzdHJpbmcgfT4ge1xuICAvLyBNYXRjaGluZyBvbiBhIHBhdGggYm91bmRhcnkga2VlcHMgYSBzaWJsaW5nIGxpa2UgXCIvYmFzZW1lbnQvbG9nby5wbmdcIiBmcm9tXG4gIC8vIGJlaW5nIHRyZWF0ZWQgYXMgbmV4dEJhc2VQYXRoIFwiL2Jhc2VcIiBwbHVzIFwibWVudC9sb2dvLnBuZ1wiLiBJdCdzIHN0aWxsXG4gIC8vIGluZGlzdGluZ3Vpc2hhYmxlIGZyb20gYSByZWFsIGBwdWJsaWMvYmFzZS9gIGRpcmVjdG9yeSwgd2hpY2ggbG9zZXM7IHRoYXQnc1xuICAvLyB0aGUgcmlnaHQgdHJhZGUsIHNpbmNlIGV2ZXJ5IHN0YXRpY2FsbHkgaW1wb3J0ZWQgaW1hZ2UgY2FycmllcyB0aGUgcHJlZml4XG4gIC8vIGFuZCB0aGUgYWx0ZXJuYXRpdmUgY29zdHMgYW4gUzMgcm91bmQgdHJpcCBwZXIgcmVxdWVzdCB0byBkZXRlY3QgaXQuXG4gIGNvbnN0IGhhc05leHRCYXNlUGF0aCA9XG4gICAgISFuZXh0QmFzZVBhdGggJiZcbiAgICAodXJsID09PSBuZXh0QmFzZVBhdGggfHwgdXJsLnN0YXJ0c1dpdGgoYCR7bmV4dEJhc2VQYXRofS9gKSk7XG4gIGNvbnN0IGtleSA9IGpvaW5QYXRoKFxuICAgIHN0YXRpY0Fzc2V0c0tleVByZWZpeCxcbiAgICBoYXNOZXh0QmFzZVBhdGggPyB1cmwuc2xpY2UobmV4dEJhc2VQYXRoLmxlbmd0aCkgOiB1cmwsXG4gICk7XG5cbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBzMy5zZW5kKFxuICAgIG5ldyBHZXRPYmplY3RDb21tYW5kKHtcbiAgICAgIEJ1Y2tldDogYnVja2V0LFxuICAgICAgS2V5OiBrZXksXG4gICAgfSksXG4gICk7XG5cbiAgY29uc3QgYm9keSA9IHJlc3BvbnNlLkJvZHk7XG4gIGlmICghYm9keSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgRW1wdHkgcmVzcG9uc2UgZnJvbSBTMyBmb3Iga2V5OiAke2tleX1gKTtcbiAgfVxuXG4gIGNvbnN0IGNodW5rczogQnVmZmVyW10gPSBbXTtcbiAgZm9yIGF3YWl0IChjb25zdCBjaHVuayBvZiBib2R5IGFzIEFzeW5jSXRlcmFibGU8VWludDhBcnJheT4pIHtcbiAgICBjaHVua3MucHVzaChCdWZmZXIuZnJvbShjaHVuaykpO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBidWZmZXI6IEJ1ZmZlci5jb25jYXQoY2h1bmtzKSxcbiAgICBjb250ZW50VHlwZTogcmVzcG9uc2UuQ29udGVudFR5cGUgfHwgbnVsbCxcbiAgICBldGFnOiByZXNwb25zZS5FVGFnIHx8IFwiXCIsXG4gIH07XG59XG5cbi8qKiBNaXJyb3JzIE5leHQuanMncyBvd24gYGdldEZpbGVOYW1lV2l0aEV4dGVuc2lvbmAgaW4gaW1hZ2Utb3B0aW1pemVyLmpzLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEZpbGVOYW1lV2l0aEV4dGVuc2lvbihcbiAgdXJsOiBzdHJpbmcsXG4gIGNvbnRlbnRUeXBlOiBzdHJpbmcgfCBudWxsLFxuKTogc3RyaW5nIHtcbiAgY29uc3QgW3VybFdpdGhvdXRRdWVyeVBhcmFtc10gPSB1cmwuc3BsaXQoXCI/XCIsIDEpO1xuICBjb25zdCBmaWxlTmFtZVdpdGhFeHRlbnNpb24gPSB1cmxXaXRob3V0UXVlcnlQYXJhbXMuc3BsaXQoXCIvXCIpLnBvcCgpO1xuICBpZiAoIWNvbnRlbnRUeXBlIHx8ICFmaWxlTmFtZVdpdGhFeHRlbnNpb24pIHtcbiAgICByZXR1cm4gXCJpbWFnZS5iaW5cIjtcbiAgfVxuICBjb25zdCBbZmlsZU5hbWVdID0gZmlsZU5hbWVXaXRoRXh0ZW5zaW9uLnNwbGl0KFwiLlwiLCAxKTtcbiAgY29uc3QgZXh0ZW5zaW9uID0gZ2V0RXh0ZW5zaW9uKGNvbnRlbnRUeXBlKTtcbiAgcmV0dXJuIGAke2ZpbGVOYW1lfS4ke2V4dGVuc2lvbn1gO1xufVxuXG4vKipcbiAqIE1hcHMgYW4gZXJyb3IgdGhyb3duIHdoaWxlIHByb2Nlc3NpbmcgYW4gaW1hZ2UgcmVxdWVzdCB0byB0aGUgSFRUUFxuICogc3RhdHVzL21lc3NhZ2UgaXQgc2hvdWxkIHByb2R1Y2UsIHByZXNlcnZpbmcgdGhlIHN0YXR1cyBOZXh0LmpzJ3Mgb3duXG4gKiBgSW1hZ2VFcnJvcmAvYGZldGNoRXh0ZXJuYWxJbWFnZWAgYXR0YWNoLiBBIG1pc3NpbmcgUzMgb2JqZWN0IGlzIG1hcHBlZCB0b1xuICogdGhlIHNhbWUgNDAwIE5leHQuanMncyBvd24gbG9jYWwtaW1hZ2UgZmV0Y2ggcGF0aCAoYGZldGNoSW50ZXJuYWxJbWFnZWAgaW5cbiAqIGltYWdlLW9wdGltaXplci5qcykgcHJvZHVjZXMgZm9yIGEgbWlzc2luZyBmaWxlOiBpdCBuZXZlciBpbnNwZWN0cyB0aGVcbiAqIGludGVybmFsIHJlcXVlc3QncyBzdGF0dXMgY29kZSwgc28gYSA0MDQgdGhlcmUganVzdCBmbG93cyBpbnRvIHRoZSBub3JtYWxcbiAqIFwibm90IGEgdmFsaWQgaW1hZ2VcIiBjb250ZW50LXR5cGUgY2hlY2sgYXMgaWYgaXQgd2VyZSBtYWxmb3JtZWQgaW1hZ2VcbiAqIGJ5dGVzLiBUaGlzIG1pcnJvcnMgdGhhdCBiZWhhdmlvciBpbnN0ZWFkIG9mIHN1cmZhY2luZyBhIDQwNCwgc28gRnVuY3Rpb25zXG4gKiBhbmQgQ29udGFpbmVycyBkZXBsb3ltZW50cyByZXNwb25kIGlkZW50aWNhbGx5IGZvciB0aGlzIGNhc2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlRXJyb3JSZXNwb25zZShlcnJvcjogdW5rbm93bik6IHtcbiAgc3RhdHVzQ29kZTogbnVtYmVyO1xuICBtZXNzYWdlOiBzdHJpbmc7XG59IHtcbiAgaWYgKGVycm9yIGluc3RhbmNlb2YgSW1hZ2VFcnJvcikge1xuICAgIHJldHVybiB7IHN0YXR1c0NvZGU6IGVycm9yLnN0YXR1c0NvZGUsIG1lc3NhZ2U6IGVycm9yLm1lc3NhZ2UgfTtcbiAgfVxuICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvciAmJiBlcnJvci5uYW1lID09PSBcIk5vU3VjaEtleVwiKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHN0YXR1c0NvZGU6IDQwMCxcbiAgICAgIG1lc3NhZ2U6IFwiVGhlIHJlcXVlc3RlZCByZXNvdXJjZSBpc24ndCBhIHZhbGlkIGltYWdlLlwiLFxuICAgIH07XG4gIH1cbiAgcmV0dXJuIHsgc3RhdHVzQ29kZTogNTAwLCBtZXNzYWdlOiBcIkludGVybmFsIFNlcnZlciBFcnJvclwiIH07XG59XG4iXX0=
@@ -0,0 +1 @@
1
+ export {};