@hyperframes/aws-lambda 0.7.36 → 0.7.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/aws-lambda",
3
- "version": "0.7.36",
3
+ "version": "0.7.38",
4
4
  "description": "AWS Lambda adapter for HyperFrames distributed rendering — handler, client-side SDK, and CDK construct.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -45,7 +45,7 @@
45
45
  "ffprobe-static": "^3.1.0",
46
46
  "puppeteer-core": "^24.39.1",
47
47
  "tar": "^7.4.3",
48
- "@hyperframes/producer": "^0.7.36"
48
+ "@hyperframes/producer": "^0.7.38"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/aws-lambda": "^8.10.146",
@@ -0,0 +1,30 @@
1
+ /**
2
+ * CJS-interop banner prepended to the ESM handler bundle.
3
+ *
4
+ * Lambda's Node 22 runtime treats `.mjs` as ESM, which has no `require`,
5
+ * `__filename`, or `__dirname`. The handler bundle inlines CJS deps that
6
+ * assume all three exist at module scope:
7
+ * - postcss et al. call top-level `require(...)`
8
+ * - wawoff2's emscripten build (pulled in unconditionally via
9
+ * producer → fontCompression) reads `__dirname` at module scope
10
+ * Without the shims the handler throws "Dynamic require of <X> is not
11
+ * supported" / "__dirname is not defined in ES module scope" at import time,
12
+ * before it can run — which is exactly how a freshly deployed stack crashed
13
+ * on every render (#1932).
14
+ *
15
+ * This mirrors the producer's own CJS banner (packages/producer/build.mjs);
16
+ * the handler bundle inlines producer source, so it needs the same shim.
17
+ *
18
+ * Kept in its own module (not inline in build-zip.ts, which self-executes on
19
+ * import) so build-zip.test.ts can import the exact banner, bundle a fixture
20
+ * with it, and assert the globals actually resolve.
21
+ */
22
+ export const HANDLER_BANNER = [
23
+ "// hyperframes-aws-lambda handler bundle",
24
+ 'import { createRequire as __hf_createRequire } from "module";',
25
+ 'import { fileURLToPath as __hf_fileURLToPath } from "url";',
26
+ 'import { dirname as __hf_dirname } from "path";',
27
+ "const require = __hf_createRequire(import.meta.url);",
28
+ "const __filename = __hf_fileURLToPath(import.meta.url);",
29
+ "const __dirname = __hf_dirname(__filename);",
30
+ ].join("\n");
@@ -1,14 +1,77 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
2
+ import { spawnSync } from "node:child_process";
3
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { pathToFileURL } from "node:url";
7
+ import * as esbuild from "esbuild";
8
+ import { HANDLER_BANNER } from "./_handlerBanner.js";
4
9
 
10
+ // The handler ships as ESM (.mjs) but inlines CJS deps that assume Node's CJS
11
+ // globals exist at module scope: postcss et al. call top-level `require(...)`,
12
+ // and wawoff2's emscripten build reads `__dirname`. A freshly deployed stack
13
+ // crashed on every render (#1932) with "__dirname is not defined in ES module
14
+ // scope"; the fix is the require/__filename/__dirname shim in HANDLER_BANNER.
15
+ //
16
+ // This bundles a fixture touching all three globals with the REAL banner and
17
+ // imports the output, so it catches a dropped/renamed shim behaviourally
18
+ // rather than by grepping for literals (which survives a broken refactor).
19
+ //
20
+ // The import MUST run under Node, not the `bun test` runtime: Bun defines
21
+ // `__dirname`/`__filename` even in ESM, which would mask a missing shim and
22
+ // green-light a broken bundle. Lambda runs Node, so we spawn `node` (guaranteed
23
+ // present in CI alongside bun) to reproduce the deploy target faithfully.
5
24
  describe("build-zip handler banner", () => {
6
- it("defines CommonJS path globals for inlined CJS dependencies", () => {
7
- const source = readFileSync(fileURLToPath(new URL("./build-zip.ts", import.meta.url)), "utf8");
25
+ it("shims require/__filename/__dirname so inlined CJS deps import under Node", () => {
26
+ const dir = mkdtempSync(join(tmpdir(), "hf-banner-test-"));
27
+ try {
28
+ const entry = join(dir, "fixture.ts");
29
+ const outfile = join(dir, "out.mjs");
30
+ // Reference each CJS global at module top level, the way inlined deps do.
31
+ // If any shim is missing, importing `out.mjs` throws at eval time.
32
+ writeFileSync(
33
+ entry,
34
+ [
35
+ "const cjsDir = __dirname;",
36
+ "const cjsFile = __filename;",
37
+ "const path = require('node:path');",
38
+ "if (typeof cjsDir !== 'string') throw new Error('__dirname missing');",
39
+ "if (typeof cjsFile !== 'string') throw new Error('__filename missing');",
40
+ "if (typeof path.join !== 'function') throw new Error('require missing');",
41
+ "console.log('BANNER_OK');",
42
+ ].join("\n"),
43
+ );
8
44
 
9
- expect(source).toContain('import { fileURLToPath as __hf_fileURLToPath } from "url";');
10
- expect(source).toContain('import { dirname as __hf_dirname } from "path";');
11
- expect(source).toContain("const __filename = __hf_fileURLToPath(import.meta.url);");
12
- expect(source).toContain("const __dirname = __hf_dirname(__filename);");
45
+ esbuild.buildSync({
46
+ bundle: true,
47
+ platform: "node",
48
+ target: "node22",
49
+ format: "esm",
50
+ entryPoints: [entry],
51
+ outfile,
52
+ banner: { js: HANDLER_BANNER },
53
+ });
54
+
55
+ // Import under real Node — Lambda's runtime — not the bun test runtime.
56
+ const res = spawnSync(
57
+ "node",
58
+ [
59
+ "--input-type=module",
60
+ "-e",
61
+ `await import(${JSON.stringify(pathToFileURL(outfile).href)});`,
62
+ ],
63
+ { encoding: "utf8" },
64
+ );
65
+
66
+ // A missing shim surfaces as a non-zero exit + ReferenceError on stderr.
67
+ // Guard against a silent skip if `node` isn't on PATH (it is in CI).
68
+ expect(res.error).toBeUndefined();
69
+ expect(res.stderr).not.toContain("is not defined in ES module scope");
70
+ expect(res.stderr).not.toContain("Dynamic require");
71
+ expect(res.status).toBe(0);
72
+ expect(res.stdout).toContain("BANNER_OK");
73
+ } finally {
74
+ rmSync(dir, { recursive: true, force: true });
75
+ }
13
76
  });
14
77
  });
@@ -48,6 +48,7 @@ import { dirname, join, resolve } from "node:path";
48
48
  import { fileURLToPath } from "node:url";
49
49
  import * as esbuild from "esbuild";
50
50
  import { formatBytes } from "./_formatBytes.js";
51
+ import { HANDLER_BANNER } from "./_handlerBanner.js";
51
52
 
52
53
  const scriptDir = dirname(fileURLToPath(import.meta.url));
53
54
  const packageRoot = resolve(scriptDir, "..");
@@ -239,24 +240,10 @@ async function bundleHandler(stagingDir: string): Promise<void> {
239
240
  sourcemap: false,
240
241
  entryPoints: [entry],
241
242
  outfile,
242
- // Lambda's Node 22 runtime treats `.mjs` as ESM. Inject a real `require`
243
- // via `createRequire` so esbuild's `__require` shim resolves to it
244
- // instead of throwing "Dynamic require of <X> is not supported" on
245
- // CommonJS modules in the dependency graph (postcss, etc. that ship
246
- // top-level `require('path')` calls). The shim does
247
- // `typeof require !== "undefined" ? require : <throwing-proxy>`, so
248
- // making `require` a real value in module scope flips it onto the
249
- // happy path.
243
+ // See HANDLER_BANNER (_handlerBanner.ts) for why the ESM bundle needs the
244
+ // CJS require/__filename/__dirname shims.
250
245
  banner: {
251
- js: [
252
- "// hyperframes-aws-lambda handler bundle",
253
- 'import { createRequire as __hf_createRequire } from "module";',
254
- "const require = __hf_createRequire(import.meta.url);",
255
- 'import { fileURLToPath as __hf_fileURLToPath } from "url";',
256
- 'import { dirname as __hf_dirname } from "path";',
257
- "const __filename = __hf_fileURLToPath(import.meta.url);",
258
- "const __dirname = __hf_dirname(__filename);",
259
- ].join("\n"),
246
+ js: HANDLER_BANNER,
260
247
  },
261
248
  });
262
249
  console.log(`[build-zip] bundled handler → ${outfile}`);