@hyperframes/producer 0.8.153 → 0.8.155

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.
@@ -93,7 +93,7 @@ import {
93
93
  LOW_MEMORY_TOTAL_MB_THRESHOLD,
94
94
  assertConfiguredFfmpegBinariesExist,
95
95
  resolveBrowserGpuMode,
96
- resolveHeadlessShellPath as resolveHeadlessShellPath2,
96
+ resolveHeadlessShellPath,
97
97
  compositionRequiresWebGpu as compositionRequiresWebGpu2,
98
98
  applyConcreteGpuScreenshotClamp,
99
99
  explainDrawElementDisabled,
@@ -1200,7 +1200,6 @@ import {
1200
1200
  getCapturePerfSummary as getCapturePerfSummary2,
1201
1201
  initializeSession as initializeSession2,
1202
1202
  prepareCaptureSessionForReuse as prepareCaptureSessionForReuse2,
1203
- resolveHeadlessShellPath,
1204
1203
  shouldDisableBrowserPoolForParallelWorker,
1205
1204
  spawnStreamingEncoder as spawnStreamingEncoder2
1206
1205
  } from "@hyperframes/engine";
@@ -8517,12 +8516,20 @@ import {
8517
8516
  appendRenderProvenanceArgs,
8518
8517
  applyFaststart as applyFaststart2,
8519
8518
  describeFfmpegFailure as describeFfmpegFailure2,
8519
+ getEncoderPreset as getEncoderPreset5,
8520
8520
  MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME4,
8521
8521
  muxVideoWithAudio as muxVideoWithAudio2,
8522
8522
  resolveConfig as resolveConfig4,
8523
8523
  runFfmpeg as runFfmpeg5
8524
8524
  } from "@hyperframes/engine";
8525
8525
  import { fpsToFfmpegArg as fpsToFfmpegArg3 } from "@hyperframes/core";
8526
+ function cfrRateControlArgs(locked) {
8527
+ const preset = getEncoderPreset5(locked.quality ?? "standard", "mp4");
8528
+ if (locked.crf == null && locked.bitrate) {
8529
+ return ["-preset", preset.preset, "-b:v", locked.bitrate];
8530
+ }
8531
+ return ["-preset", preset.preset, "-crf", String(locked.crf ?? preset.quality)];
8532
+ }
8526
8533
  async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
8527
8534
  const start = Date.now();
8528
8535
  const log = options?.logger ?? defaultLogger;
@@ -8619,7 +8626,9 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
8619
8626
  if (!existsSync16(encoderJsonPath)) {
8620
8627
  throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
8621
8628
  }
8622
- const encoderJson = JSON.parse(readFileSync10(encoderJsonPath, "utf-8"));
8629
+ const encoderJson = JSON.parse(
8630
+ readFileSync10(encoderJsonPath, "utf-8")
8631
+ );
8623
8632
  if (encoderJson.encoder === "libx265-software") {
8624
8633
  throw new Error(
8625
8634
  `[assemble] cfr=true is not yet supported with codec: "h265". The cfr re-encode pass uses libx264 and would silently transcode the h265 chunks. Either disable cfr or render with codec: "h264".`
@@ -8631,10 +8640,7 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
8631
8640
  concatOutputPath,
8632
8641
  "-c:v",
8633
8642
  "libx264",
8634
- "-preset",
8635
- "medium",
8636
- "-crf",
8637
- "18",
8643
+ ...cfrRateControlArgs(encoderJson),
8638
8644
  "-pix_fmt",
8639
8645
  "yuv420p",
8640
8646
  "-fps_mode",
@@ -8786,7 +8792,7 @@ import {
8786
8792
  createVideoFrameInjector as createVideoFrameInjector2,
8787
8793
  deriveBeginFrameProbeTimeTicks as deriveBeginFrameProbeTimeTicks2,
8788
8794
  extractVideoFramesRange,
8789
- getEncoderPreset as getEncoderPreset5,
8795
+ getEncoderPreset as getEncoderPreset6,
8790
8796
  initializeSession as initializeSession8,
8791
8797
  probeBeginFrameLiveness as probeBeginFrameLiveness2,
8792
8798
  readWebGlVendorInfoFromCanvas,
@@ -9337,7 +9343,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
9337
9343
  const isPngSequence = plan2.dimensions.format === "png-sequence";
9338
9344
  outputKind = isPngSequence ? "frame-dir" : "file";
9339
9345
  const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
9340
- const basePreset = getEncoderPreset5(job.config.quality, presetFormat, void 0);
9346
+ const basePreset = getEncoderPreset6(job.config.quality, presetFormat, void 0);
9341
9347
  const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
9342
9348
  const effectiveQuality = encoder.crf ?? preset.quality;
9343
9349
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  LOW_MEMORY_TOTAL_MB_THRESHOLD,
37
37
  assertConfiguredFfmpegBinariesExist,
38
38
  resolveBrowserGpuMode,
39
- resolveHeadlessShellPath as resolveHeadlessShellPath2,
39
+ resolveHeadlessShellPath,
40
40
  compositionRequiresWebGpu as compositionRequiresWebGpu2,
41
41
  applyConcreteGpuScreenshotClamp,
42
42
  explainDrawElementDisabled,
@@ -1837,7 +1837,6 @@ import {
1837
1837
  getCapturePerfSummary as getCapturePerfSummary2,
1838
1838
  initializeSession as initializeSession2,
1839
1839
  prepareCaptureSessionForReuse as prepareCaptureSessionForReuse2,
1840
- resolveHeadlessShellPath,
1841
1840
  shouldDisableBrowserPoolForParallelWorker,
1842
1841
  spawnStreamingEncoder as spawnStreamingEncoder2
1843
1842
  } from "@hyperframes/engine";
@@ -4081,10 +4080,7 @@ async function openSegmentedSession(input) {
4081
4080
  const videoInjector = input.createRenderVideoFrameInjector();
4082
4081
  const captureOptions = input.buildCaptureOptions();
4083
4082
  const ownBrowser = shouldDisableBrowserPoolForParallelWorker({
4084
- parallel: (input.workerCount ?? 1) > 1,
4085
- platform: process.platform,
4086
- deviceScaleFactor: captureOptions.deviceScaleFactor,
4087
- headlessShellPath: resolveHeadlessShellPath(captureCfg)
4083
+ parallel: (input.workerCount ?? 1) > 1
4088
4084
  });
4089
4085
  return createCaptureSession2(
4090
4086
  input.fileServer.url,
@@ -12209,7 +12205,7 @@ async function executeRenderPipeline(input) {
12209
12205
  code: "start_browsers"
12210
12206
  });
12211
12207
  const resolvedBrowserGpuMode = await resolveBrowserGpuMode(cfg.browserGpuMode, {
12212
- chromePath: resolveHeadlessShellPath2(cfg),
12208
+ chromePath: resolveHeadlessShellPath(cfg),
12213
12209
  browserTimeout: cfg.browserTimeout
12214
12210
  });
12215
12211
  captureForceScreenshot = applyConcreteGpuScreenshotClamp(
@@ -12554,7 +12550,7 @@ async function executeRenderPipeline(input) {
12554
12550
  }
12555
12551
  const parallelCaptureMode = resolveParallelCaptureMode({
12556
12552
  platform: process.platform,
12557
- headlessShell: resolveHeadlessShellPath2(cfg) !== null,
12553
+ headlessShell: resolveHeadlessShellPath(cfg) !== null,
12558
12554
  forceScreenshot: captureForceScreenshot,
12559
12555
  deviceScaleFactor: captureOptions.deviceScaleFactor
12560
12556
  });
@@ -14241,12 +14237,12 @@ function resolveWorkerEntry2() {
14241
14237
  }
14242
14238
 
14243
14239
  // src/server.ts
14244
- import { isVideoFrameFormat as isVideoFrameFormat3 } from "@hyperframes/engine";
14245
14240
  import {
14246
14241
  parseFps,
14247
14242
  normalizeResolutionFlag,
14248
14243
  isAspectAgnosticResolutionAlias
14249
14244
  } from "@hyperframes/core";
14245
+ import { parseEncoderOptions } from "@hyperframes/studio-server/encoder-options";
14250
14246
  var DEFAULT_SERVER_FPS = { num: 30, den: 1 };
14251
14247
  var SAFE_RENDER_ERROR_CODES = /* @__PURE__ */ new Set([
14252
14248
  "ASSET_MEDIA_TYPE_MISMATCH",
@@ -14320,13 +14316,16 @@ function parseRenderOptions(body) {
14320
14316
  const entryFile = nonEmptyString(body.entryFile);
14321
14317
  const format = parseServerFormat(body.format);
14322
14318
  const outputDynamicRange = parseServerOutputDynamicRange(body.outputDynamicRange) ?? fromRenderHdrMode(parseLegacyServerHdrMode(body.hdrMode));
14323
- const videoFrameFormat = isVideoFrameFormat3(body.videoFrameFormat) ? body.videoFrameFormat : void 0;
14319
+ const encoder = parseEncoderOptions(body, format ?? "mp4");
14320
+ const { crf, videoBitrate, videoFrameFormat } = encoder.ok ? encoder.options : {};
14324
14321
  const { variables, outputResolution, outputResolutionAspectAgnostic } = parseRenderOverrides(body);
14325
14322
  return {
14326
14323
  outputPath,
14327
14324
  fps,
14328
14325
  quality,
14329
14326
  workers,
14327
+ crf,
14328
+ videoBitrate,
14330
14329
  useGpu,
14331
14330
  debug,
14332
14331
  strictness,
@@ -14358,6 +14357,8 @@ function buildRenderJobConfig(input, outputPath, log) {
14358
14357
  quality: input.quality,
14359
14358
  format: input.format ?? "mp4",
14360
14359
  workers: input.workers,
14360
+ crf: input.crf,
14361
+ videoBitrate: input.videoBitrate,
14361
14362
  useGpu: input.useGpu,
14362
14363
  debug: input.debug,
14363
14364
  strictness: input.strictness,
@@ -14379,6 +14380,8 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
14379
14380
  return { input, cleanupProjectDir, absoluteOutputPath };
14380
14381
  }
14381
14382
  function validateRenderOverrides(body) {
14383
+ const encoder = parseEncoderOptions(body, parseServerFormat(body.format) ?? "mp4");
14384
+ if (!encoder.ok) return encoder.error;
14382
14385
  if (body.variables !== void 0 && !isPlainObject2(body.variables)) {
14383
14386
  return 'variables must be a JSON object keyed by variable id (e.g. {"title":"Hello"})';
14384
14387
  }
@@ -17083,12 +17086,20 @@ import {
17083
17086
  appendRenderProvenanceArgs,
17084
17087
  applyFaststart as applyFaststart2,
17085
17088
  describeFfmpegFailure as describeFfmpegFailure2,
17089
+ getEncoderPreset as getEncoderPreset5,
17086
17090
  MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME4,
17087
17091
  muxVideoWithAudio as muxVideoWithAudio2,
17088
17092
  resolveConfig as resolveConfig6,
17089
17093
  runFfmpeg as runFfmpeg5
17090
17094
  } from "@hyperframes/engine";
17091
17095
  import { fpsToFfmpegArg as fpsToFfmpegArg3 } from "@hyperframes/core";
17096
+ function cfrRateControlArgs(locked) {
17097
+ const preset = getEncoderPreset5(locked.quality ?? "standard", "mp4");
17098
+ if (locked.crf == null && locked.bitrate) {
17099
+ return ["-preset", preset.preset, "-b:v", locked.bitrate];
17100
+ }
17101
+ return ["-preset", preset.preset, "-crf", String(locked.crf ?? preset.quality)];
17102
+ }
17092
17103
  async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
17093
17104
  const start = Date.now();
17094
17105
  const log = options?.logger ?? defaultLogger;
@@ -17185,7 +17196,9 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
17185
17196
  if (!existsSync25(encoderJsonPath)) {
17186
17197
  throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
17187
17198
  }
17188
- const encoderJson = JSON.parse(readFileSync13(encoderJsonPath, "utf-8"));
17199
+ const encoderJson = JSON.parse(
17200
+ readFileSync13(encoderJsonPath, "utf-8")
17201
+ );
17189
17202
  if (encoderJson.encoder === "libx265-software") {
17190
17203
  throw new Error(
17191
17204
  `[assemble] cfr=true is not yet supported with codec: "h265". The cfr re-encode pass uses libx264 and would silently transcode the h265 chunks. Either disable cfr or render with codec: "h264".`
@@ -17197,10 +17210,7 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
17197
17210
  concatOutputPath,
17198
17211
  "-c:v",
17199
17212
  "libx264",
17200
- "-preset",
17201
- "medium",
17202
- "-crf",
17203
- "18",
17213
+ ...cfrRateControlArgs(encoderJson),
17204
17214
  "-pix_fmt",
17205
17215
  "yuv420p",
17206
17216
  "-fps_mode",
@@ -17352,7 +17362,7 @@ import {
17352
17362
  createVideoFrameInjector as createVideoFrameInjector2,
17353
17363
  deriveBeginFrameProbeTimeTicks as deriveBeginFrameProbeTimeTicks2,
17354
17364
  extractVideoFramesRange,
17355
- getEncoderPreset as getEncoderPreset5,
17365
+ getEncoderPreset as getEncoderPreset6,
17356
17366
  initializeSession as initializeSession9,
17357
17367
  probeBeginFrameLiveness as probeBeginFrameLiveness2,
17358
17368
  readWebGlVendorInfoFromCanvas,
@@ -17903,7 +17913,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
17903
17913
  const isPngSequence = plan2.dimensions.format === "png-sequence";
17904
17914
  outputKind = isPngSequence ? "frame-dir" : "file";
17905
17915
  const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
17906
- const basePreset = getEncoderPreset5(job.config.quality, presetFormat, void 0);
17916
+ const basePreset = getEncoderPreset6(job.config.quality, presetFormat, void 0);
17907
17917
  const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
17908
17918
  const effectiveQuality = encoder.crf ?? preset.quality;
17909
17919
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
@@ -56,7 +56,7 @@ import {
56
56
  LOW_MEMORY_TOTAL_MB_THRESHOLD,
57
57
  assertConfiguredFfmpegBinariesExist,
58
58
  resolveBrowserGpuMode,
59
- resolveHeadlessShellPath as resolveHeadlessShellPath2,
59
+ resolveHeadlessShellPath,
60
60
  compositionRequiresWebGpu as compositionRequiresWebGpu2,
61
61
  applyConcreteGpuScreenshotClamp,
62
62
  explainDrawElementDisabled,
@@ -1856,7 +1856,6 @@ import {
1856
1856
  getCapturePerfSummary as getCapturePerfSummary2,
1857
1857
  initializeSession as initializeSession2,
1858
1858
  prepareCaptureSessionForReuse as prepareCaptureSessionForReuse2,
1859
- resolveHeadlessShellPath,
1860
1859
  shouldDisableBrowserPoolForParallelWorker,
1861
1860
  spawnStreamingEncoder as spawnStreamingEncoder2
1862
1861
  } from "@hyperframes/engine";
@@ -4100,10 +4099,7 @@ async function openSegmentedSession(input) {
4100
4099
  const videoInjector = input.createRenderVideoFrameInjector();
4101
4100
  const captureOptions = input.buildCaptureOptions();
4102
4101
  const ownBrowser = shouldDisableBrowserPoolForParallelWorker({
4103
- parallel: (input.workerCount ?? 1) > 1,
4104
- platform: process.platform,
4105
- deviceScaleFactor: captureOptions.deviceScaleFactor,
4106
- headlessShellPath: resolveHeadlessShellPath(captureCfg)
4102
+ parallel: (input.workerCount ?? 1) > 1
4107
4103
  });
4108
4104
  return createCaptureSession2(
4109
4105
  input.fileServer.url,
@@ -12192,7 +12188,7 @@ async function executeRenderPipeline(input) {
12192
12188
  code: "start_browsers"
12193
12189
  });
12194
12190
  const resolvedBrowserGpuMode = await resolveBrowserGpuMode(cfg.browserGpuMode, {
12195
- chromePath: resolveHeadlessShellPath2(cfg),
12191
+ chromePath: resolveHeadlessShellPath(cfg),
12196
12192
  browserTimeout: cfg.browserTimeout
12197
12193
  });
12198
12194
  captureForceScreenshot = applyConcreteGpuScreenshotClamp(
@@ -12537,7 +12533,7 @@ async function executeRenderPipeline(input) {
12537
12533
  }
12538
12534
  const parallelCaptureMode = resolveParallelCaptureMode({
12539
12535
  platform: process.platform,
12540
- headlessShell: resolveHeadlessShellPath2(cfg) !== null,
12536
+ headlessShell: resolveHeadlessShellPath(cfg) !== null,
12541
12537
  forceScreenshot: captureForceScreenshot,
12542
12538
  deviceScaleFactor: captureOptions.deviceScaleFactor
12543
12539
  });
@@ -13703,12 +13699,12 @@ function resolveWorkerEntry2() {
13703
13699
  }
13704
13700
 
13705
13701
  // src/server.ts
13706
- import { isVideoFrameFormat as isVideoFrameFormat3 } from "@hyperframes/engine";
13707
13702
  import {
13708
13703
  parseFps,
13709
13704
  normalizeResolutionFlag,
13710
13705
  isAspectAgnosticResolutionAlias
13711
13706
  } from "@hyperframes/core";
13707
+ import { parseEncoderOptions } from "@hyperframes/studio-server/encoder-options";
13712
13708
 
13713
13709
  // src/renderRequest.ts
13714
13710
  import {
@@ -14018,13 +14014,16 @@ function parseRenderOptions(body) {
14018
14014
  const entryFile = nonEmptyString(body.entryFile);
14019
14015
  const format = parseServerFormat(body.format);
14020
14016
  const outputDynamicRange = parseServerOutputDynamicRange(body.outputDynamicRange) ?? fromRenderHdrMode(parseLegacyServerHdrMode(body.hdrMode));
14021
- const videoFrameFormat = isVideoFrameFormat3(body.videoFrameFormat) ? body.videoFrameFormat : void 0;
14017
+ const encoder = parseEncoderOptions(body, format ?? "mp4");
14018
+ const { crf, videoBitrate, videoFrameFormat } = encoder.ok ? encoder.options : {};
14022
14019
  const { variables, outputResolution, outputResolutionAspectAgnostic } = parseRenderOverrides(body);
14023
14020
  return {
14024
14021
  outputPath,
14025
14022
  fps,
14026
14023
  quality,
14027
14024
  workers,
14025
+ crf,
14026
+ videoBitrate,
14028
14027
  useGpu,
14029
14028
  debug,
14030
14029
  strictness,
@@ -14056,6 +14055,8 @@ function buildRenderJobConfig(input, outputPath, log) {
14056
14055
  quality: input.quality,
14057
14056
  format: input.format ?? "mp4",
14058
14057
  workers: input.workers,
14058
+ crf: input.crf,
14059
+ videoBitrate: input.videoBitrate,
14059
14060
  useGpu: input.useGpu,
14060
14061
  debug: input.debug,
14061
14062
  strictness: input.strictness,
@@ -14077,6 +14078,8 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
14077
14078
  return { input, cleanupProjectDir, absoluteOutputPath };
14078
14079
  }
14079
14080
  function validateRenderOverrides(body) {
14081
+ const encoder = parseEncoderOptions(body, parseServerFormat(body.format) ?? "mp4");
14082
+ if (!encoder.ok) return encoder.error;
14080
14083
  if (body.variables !== void 0 && !isPlainObject2(body.variables)) {
14081
14084
  return 'variables must be a JSON object keyed by variable id (e.g. {"title":"Hello"})';
14082
14085
  }
package/dist/server.d.ts CHANGED
@@ -43,6 +43,8 @@ interface RenderInput {
43
43
  videoFrameFormat?: RenderConfig["videoFrameFormat"];
44
44
  outputDynamicRange?: "auto" | "hdr" | "sdr";
45
45
  workers?: number;
46
+ crf?: number;
47
+ videoBitrate?: string;
46
48
  useGpu: boolean;
47
49
  debug: boolean;
48
50
  strictness: RenderConfig["strictness"];
@@ -23,6 +23,7 @@
23
23
  * for moving `outputPath` to its orchestration-level storage.
24
24
  */
25
25
  import { type ProducerLogger } from "../../logger.js";
26
+ import type { LockedRenderConfig } from "../render/stages/freezePlan.js";
26
27
  /**
27
28
  * Result of {@link assemble}. `fileSize` reflects the final file on disk
28
29
  * (mp4/mov) or the cumulative byte total of the frame directory
@@ -34,6 +35,8 @@ export interface AssembleResult {
34
35
  framesEncoded: number;
35
36
  fileSize: number;
36
37
  }
38
+ type CfrRateControlSource = Partial<Pick<LockedRenderConfig, "quality" | "crf" | "bitrate">>;
39
+ export declare function cfrRateControlArgs(locked: CfrRateControlSource): string[];
37
40
  /**
38
41
  * Assemble the chunk outputs into a single deliverable.
39
42
  *
@@ -60,10 +63,11 @@ export declare function assemble(planDir: string, chunkPaths: readonly string[],
60
63
  * duration lands on the requested `frameCount / fps` to ms
61
64
  * precision, with no PTS-derived drift). Trade-off: ~2-5x the
62
65
  * stitch time for a 60s 1080p clip plus second-generation H.264
63
- * quality loss (negligible at `-crf 18` but non-zero). Default
66
+ * quality loss (at the chunks' own CRF or bitrate, non-zero). Default
64
67
  * `false` preserves the existing `-c copy` behavior. mp4 only
65
68
  * (libx264); webm / mov pass through unchanged because their
66
69
  * stream-copy paths don't exhibit the same avg-frame-rate drift.
67
70
  */
68
71
  cfr?: boolean;
69
72
  }): Promise<AssembleResult>;
73
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.8.153",
3
+ "version": "0.8.155",
4
4
  "description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,11 +35,11 @@
35
35
  "postcss": "^8.4.0",
36
36
  "puppeteer": "^25.2.1",
37
37
  "puppeteer-core": "^25.2.1",
38
- "@hyperframes/engine": "0.8.153",
39
- "@hyperframes/lint": "0.8.153",
40
- "@hyperframes/parsers": "0.8.153",
41
- "@hyperframes/studio-server": "0.8.153",
42
- "@hyperframes/core": "0.8.153"
38
+ "@hyperframes/core": "0.8.155",
39
+ "@hyperframes/engine": "0.8.155",
40
+ "@hyperframes/lint": "0.8.155",
41
+ "@hyperframes/parsers": "0.8.155",
42
+ "@hyperframes/studio-server": "0.8.155"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^25.0.10",
@@ -48,7 +48,7 @@
48
48
  "tsx": "^4.21.0",
49
49
  "typescript": "^5.7.2",
50
50
  "vitest": "^4.1.11",
51
- "@hyperframes/aws-lambda": "0.8.153"
51
+ "@hyperframes/aws-lambda": "0.8.155"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"