@hyperframes/producer 0.8.179 → 0.8.180

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/dist/index.js CHANGED
@@ -13927,6 +13927,7 @@ var FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
13927
13927
  var PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
13928
13928
  var MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
13929
13929
  var CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
13930
+ var CHUNK_FRAME_RANGE_INVALID = "CHUNK_FRAME_RANGE_INVALID";
13930
13931
  var MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
13931
13932
  var RenderChunkValidationError = class extends Error {
13932
13933
  code;
@@ -14178,7 +14179,8 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
14178
14179
  accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
14179
14180
  acceptsLegacyV1WithoutDescriptor: true
14180
14181
  })
14181
- })
14182
+ }),
14183
+ chunkFrameRange: true
14182
14184
  });
14183
14185
  function getDistributedRenderCapabilities() {
14184
14186
  return DISTRIBUTED_RENDER_CAPABILITIES;
@@ -14893,6 +14895,18 @@ function audioCutForChunk(grid, chunks, index, fps) {
14893
14895
  const durationSeconds = end === grid.packetCount ? null : cutSeconds(end) - (seekSeconds ?? 0);
14894
14896
  return { seekSeconds, durationSeconds, firstPacket: first, packetCount: end - first };
14895
14897
  }
14898
+ function chunksWithSubRange(chunks, index, range) {
14899
+ const slice = chunks[index];
14900
+ const parts = [
14901
+ { startFrame: slice.startFrame, endFrame: range.startFrame },
14902
+ range,
14903
+ { startFrame: range.endFrame, endFrame: slice.endFrame }
14904
+ ].filter((part) => part.endFrame > part.startFrame);
14905
+ const split = [...chunks.slice(0, index), ...parts, ...chunks.slice(index + 1)].map(
14906
+ (chunk, i) => ({ index: i, startFrame: chunk.startFrame, endFrame: chunk.endFrame })
14907
+ );
14908
+ return { chunks: split, index: range.startFrame > slice.startFrame ? index + 1 : index };
14909
+ }
14896
14910
  async function probeAacPacketGrid(audioPath) {
14897
14911
  const probe = await runFfprobeJson([
14898
14912
  "-v",
@@ -18234,11 +18248,12 @@ function cfrRateControlArgs(locked) {
18234
18248
  }
18235
18249
  return ["-preset", preset.preset, "-crf", String(locked.crf ?? preset.quality)];
18236
18250
  }
18237
- async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
18251
+ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
18238
18252
  const start = Date.now();
18239
18253
  const log = options?.logger ?? defaultLogger;
18240
18254
  const cfr = options?.cfr === true;
18241
- const plan2 = readAssemblePlan(planDir, chunkPaths);
18255
+ const plan2 = readAssemblePlan(planDir, chunkOutputs);
18256
+ const chunkPaths = chunkOutputs.flat();
18242
18257
  if (plan2.dimensions.format === "png-sequence") {
18243
18258
  return mergePngFrameDirs(chunkPaths, outputPath, plan2.totalFrames, audioPath, start);
18244
18259
  }
@@ -18321,7 +18336,12 @@ function readAssemblePlan(planDir, chunkPaths) {
18321
18336
  `[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
18322
18337
  );
18323
18338
  }
18324
- for (const path of chunkPaths) {
18339
+ for (const [index, entry] of chunkPaths.entries()) {
18340
+ if (typeof entry !== "string" && entry.length === 0) {
18341
+ throw new Error(`[assemble] chunk ${index} has an empty list of parts`);
18342
+ }
18343
+ }
18344
+ for (const path of chunkPaths.flat()) {
18325
18345
  if (!existsSync27(path)) {
18326
18346
  throw new Error(`[assemble] chunk path does not exist: ${path}`);
18327
18347
  }
@@ -18540,6 +18560,17 @@ function validatePlanVideosForChunk(value) {
18540
18560
  );
18541
18561
  }
18542
18562
  }
18563
+ function resolveChunkFrameRange(slice, chunkIndex, frameRange) {
18564
+ if (frameRange === void 0) return { startFrame: slice.startFrame, endFrame: slice.endFrame };
18565
+ const { startFrame, endFrame } = frameRange;
18566
+ if (!Number.isSafeInteger(startFrame) || !Number.isSafeInteger(endFrame) || startFrame < slice.startFrame || endFrame > slice.endFrame || endFrame <= startFrame) {
18567
+ throw new RenderChunkValidationError(
18568
+ CHUNK_FRAME_RANGE_INVALID,
18569
+ `[renderChunk] frameRange ${JSON.stringify(frameRange)} must be a non-empty integer range inside chunk ${chunkIndex}'s slice [${slice.startFrame}, ${slice.endFrame})`
18570
+ );
18571
+ }
18572
+ return { startFrame, endFrame };
18573
+ }
18543
18574
  var distributedCaptureSessionDependencies = {
18544
18575
  createCaptureSession: createCaptureSession10,
18545
18576
  assertSwiftShader,
@@ -18610,7 +18641,7 @@ function resolveLockedVp9CpuUsed(lockedEncoder) {
18610
18641
  if (lockedEncoder.encoder !== "libvpx-vp9-software") return void 0;
18611
18642
  return lockedEncoder.vp9CpuUsed ?? LEGACY_DISTRIBUTED_VP9_CPU_USED;
18612
18643
  }
18613
- async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18644
+ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
18614
18645
  const start = Date.now();
18615
18646
  const log = defaultLogger;
18616
18647
  const planJsonPath = join42(planDir, "plan.json");
@@ -18664,13 +18695,15 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18664
18695
  if (slice === void 0) {
18665
18696
  throw new RenderChunkValidationError(CHUNK_INDEX_OUT_OF_RANGE, "[renderChunk] missing slice");
18666
18697
  }
18667
- const framesInChunk = slice.endFrame - slice.startFrame;
18668
- if (framesInChunk <= 0) {
18698
+ const sliceFrames = slice.endFrame - slice.startFrame;
18699
+ if (sliceFrames <= 0) {
18669
18700
  throw new RenderChunkValidationError(
18670
18701
  CHUNK_INDEX_OUT_OF_RANGE,
18671
- `[renderChunk] chunk ${chunkIndex} has non-positive frame count: ${framesInChunk}`
18702
+ `[renderChunk] chunk ${chunkIndex} has non-positive frame count: ${sliceFrames}`
18672
18703
  );
18673
18704
  }
18705
+ const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
18706
+ const framesInChunk = range.endFrame - range.startFrame;
18674
18707
  const compiledDir = join42(planDir, "compiled");
18675
18708
  if (!existsSync28(compiledDir)) {
18676
18709
  throw new RenderChunkValidationError(
@@ -18735,10 +18768,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18735
18768
  const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
18736
18769
  planDir,
18737
18770
  planVideos,
18738
- frames: Array.from(
18739
- { length: slice.endFrame - slice.startFrame },
18740
- (_, i) => slice.startFrame + i
18741
- ),
18771
+ frames: Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i),
18742
18772
  fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
18743
18773
  outputRoot: videoFramesDir,
18744
18774
  cfg,
@@ -18894,7 +18924,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18894
18924
  abortSignal: void 0,
18895
18925
  assertNotAborted: () => {
18896
18926
  },
18897
- frameRange: { startFrame: slice.startFrame, endFrame: slice.endFrame }
18927
+ frameRange: range
18898
18928
  });
18899
18929
  const observedModes = new Set(capturePerfs.map((perf) => perf.captureMode));
18900
18930
  if (observedModes.size !== 1 || ![...observedModes].every(isCaptureMode)) {
@@ -18988,12 +19018,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18988
19018
  });
18989
19019
  if (encoder.chunksCarryAudio) {
18990
19020
  const audioScratchPath = `${outputChunkPath}.audio.m4a`;
19021
+ const audioChunks = options?.frameRange ? chunksWithSubRange(chunks, chunkIndex, range) : { chunks, index: chunkIndex };
18991
19022
  await muxChunkWithAudio({
18992
19023
  videoPath: videoOnlyPath,
18993
19024
  audioPath: chunkAudioSource(planDir, chunkIndex, audioScratchPath),
18994
19025
  outputPath: outputChunkPath,
18995
- chunks,
18996
- chunkIndex,
19026
+ chunks: audioChunks.chunks,
19027
+ chunkIndex: audioChunks.index,
18997
19028
  fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
18998
19029
  timeoutMs: cfg.ffmpegProcessTimeout
18999
19030
  });
@@ -19022,8 +19053,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
19022
19053
  const perfPayload = {
19023
19054
  planHash: plan2.planHash,
19024
19055
  chunkIndex,
19025
- startFrame: slice.startFrame,
19026
- endFrame: slice.endFrame,
19056
+ startFrame: range.startFrame,
19057
+ endFrame: range.endFrame,
19027
19058
  framesEncoded,
19028
19059
  durationMs,
19029
19060
  planHashMs,
@@ -19052,6 +19083,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
19052
19083
  outputPath: outputChunkPath,
19053
19084
  outputKind,
19054
19085
  framesEncoded,
19086
+ frameRange: range,
19055
19087
  sha256,
19056
19088
  durationMs,
19057
19089
  planHashMs,
@@ -19068,12 +19100,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
19068
19100
  }
19069
19101
 
19070
19102
  // src/services/distributed/planV2Execution.ts
19071
- async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath) {
19103
+ async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
19072
19104
  const workRoot = mkdtempSync9(join43(tmpdir5(), "hf-plan-v2-chunk-"));
19073
19105
  const materializedPlanDir = join43(workRoot, "plan");
19074
19106
  try {
19075
19107
  materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
19076
- return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath);
19108
+ return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath, options);
19077
19109
  } finally {
19078
19110
  rmSync17(workRoot, { recursive: true, force: true });
19079
19111
  }
@@ -13845,7 +13845,8 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
13845
13845
  accepts: Object.freeze([PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2]),
13846
13846
  acceptsLegacyV1WithoutDescriptor: true
13847
13847
  })
13848
- })
13848
+ }),
13849
+ chunkFrameRange: true
13849
13850
  });
13850
13851
 
13851
13852
  // src/services/render/stages/planHash.ts
@@ -31,6 +31,7 @@ export interface AssembleResult {
31
31
  framesEncoded: number;
32
32
  fileSize: number;
33
33
  }
34
+ export type AssembleChunkPaths = readonly (string | readonly string[])[];
34
35
  type CfrRateControlSource = Partial<Pick<LockedRenderConfig, "quality" | "crf" | "bitrate">>;
35
36
  export declare function cfrRateControlArgs(locked: CfrRateControlSource): string[];
36
37
  /**
@@ -39,7 +40,8 @@ export declare function cfrRateControlArgs(locked: CfrRateControlSource): string
39
40
  * @param planDir — absolute path to the planDir produced by `plan()`.
40
41
  * @param chunkPaths — ordered chunk outputs, length === `chunks.json` length.
41
42
  * For mp4/mov each entry is a path to an encoded chunk file; for
42
- * png-sequence each entry is a path to a directory of frames.
43
+ * png-sequence each entry is a path to a directory of frames. An entry
44
+ * may instead be the ordered parts of that chunk (see {@link AssembleChunkPaths}).
43
45
  * @param audioPath — `<planDir>/<MIXED_AUDIO_FILENAME>` for mux'd formats. Pass `null`
44
46
  * when the composition has no audio (or `assemble` is being called for a
45
47
  * format whose audio is muxed elsewhere). `assemble` always normalizes
@@ -48,7 +50,7 @@ export declare function cfrRateControlArgs(locked: CfrRateControlSource): string
48
50
  * @param outputPath — final on-disk output (file for mp4/mov; directory
49
51
  * for png-sequence — created if missing).
50
52
  */
51
- export declare function assemble(planDir: string, chunkPaths: readonly string[], audioPath: string | null, outputPath: string, options?: {
53
+ export declare function assemble(planDir: string, chunkOutputs: AssembleChunkPaths, audioPath: string | null, outputPath: string, options?: {
52
54
  logger?: ProducerLogger;
53
55
  abortSignal?: AbortSignal;
54
56
  /**
@@ -15,6 +15,14 @@ interface AudioCut {
15
15
  packetCount: number;
16
16
  }
17
17
  export declare function audioCutForChunk(grid: AacPacketGrid, chunks: readonly ChunkSliceJson[], index: number, fps: Fps): AudioCut | null;
18
+ /** Chunk `index` split around `range`, so `audioCutForChunk` treats `range` as a chunk. */
19
+ export declare function chunksWithSubRange(chunks: readonly ChunkSliceJson[], index: number, range: {
20
+ startFrame: number;
21
+ endFrame: number;
22
+ }): {
23
+ chunks: ChunkSliceJson[];
24
+ index: number;
25
+ };
18
26
  export declare function chunkAudioSlicePath(index: number): string;
19
27
  /** Which chunks need a sliced-audio file: `"all"`, one index, or `null` if it is not one. */
20
28
  export declare function chunkAudioSliceTarget(path: string): "all" | number | null;
@@ -7,9 +7,10 @@ export declare const FFMPEG_VERSION_MISMATCH = "FFMPEG_VERSION_MISMATCH";
7
7
  export declare const PLAN_HASH_MISMATCH = "PLAN_HASH_MISMATCH";
8
8
  export declare const MISSING_PLAN_ARTIFACT = "MISSING_PLAN_ARTIFACT";
9
9
  export declare const CHUNK_INDEX_OUT_OF_RANGE = "CHUNK_INDEX_OUT_OF_RANGE";
10
+ export declare const CHUNK_FRAME_RANGE_INVALID = "CHUNK_FRAME_RANGE_INVALID";
10
11
  export declare const MISSING_RUNTIME_ENV_SNAPSHOT = "MISSING_RUNTIME_ENV_SNAPSHOT";
11
12
  export { INVALID_VIDEO_METADATA };
12
- export type RenderChunkValidationCode = typeof FFMPEG_VERSION_MISMATCH | typeof PLAN_HASH_MISMATCH | typeof MISSING_PLAN_ARTIFACT | typeof CHUNK_INDEX_OUT_OF_RANGE | typeof MISSING_RUNTIME_ENV_SNAPSHOT | typeof INVALID_VIDEO_METADATA | typeof BROWSER_GPU_NOT_SOFTWARE;
13
+ export type RenderChunkValidationCode = typeof FFMPEG_VERSION_MISMATCH | typeof PLAN_HASH_MISMATCH | typeof MISSING_PLAN_ARTIFACT | typeof CHUNK_INDEX_OUT_OF_RANGE | typeof CHUNK_FRAME_RANGE_INVALID | typeof MISSING_RUNTIME_ENV_SNAPSHOT | typeof INVALID_VIDEO_METADATA | typeof BROWSER_GPU_NOT_SOFTWARE;
13
14
  /** A malformed planDir or a worker that does not match the plan's fingerprint; adapters key retries off `code`. */
14
15
  export declare class RenderChunkValidationError extends Error {
15
16
  readonly code: RenderChunkValidationCode;
@@ -53,6 +53,7 @@ export interface DistributedRenderCapabilities {
53
53
  chunk: Readonly<PlanProtocolConsumerCapabilities>;
54
54
  assembler: Readonly<PlanProtocolConsumerCapabilities>;
55
55
  }>;
56
+ readonly chunkFrameRange?: boolean;
56
57
  }
57
58
  /** Serializable capability payload for fleet rollout and worker handshakes. */
58
59
  export declare const DISTRIBUTED_RENDER_CAPABILITIES: Readonly<DistributedRenderCapabilities>;
@@ -1,12 +1,12 @@
1
- import { assemble, type AssembleResult } from "./assemble.js";
2
- import { type EffectiveChunkResult } from "./renderChunk.js";
1
+ import { assemble, type AssembleChunkPaths, type AssembleResult } from "./assemble.js";
2
+ import { type EffectiveChunkResult, type RenderChunkOptions } from "./renderChunk.js";
3
3
  /**
4
4
  * Direct v2 chunk-role entry point. Storage adapters may instead download the
5
5
  * selected blobs themselves and call `materializePlanV2Target` + `renderChunk`.
6
6
  */
7
- export declare function renderChunkV2(planV2Dir: string, chunkIndex: number, outputChunkPath: string): Promise<EffectiveChunkResult>;
7
+ export declare function renderChunkV2(planV2Dir: string, chunkIndex: number, outputChunkPath: string, options?: RenderChunkOptions): Promise<EffectiveChunkResult>;
8
8
  /**
9
9
  * Direct v2 assembler-role entry point. Audio is resolved exclusively from
10
10
  * the assembler dependency set and never materialized by chunk workers.
11
11
  */
12
- export declare function assembleV2(planV2Dir: string, chunkPaths: readonly string[], outputPath: string, options?: Parameters<typeof assemble>[4]): Promise<AssembleResult>;
12
+ export declare function assembleV2(planV2Dir: string, chunkPaths: AssembleChunkPaths, outputPath: string, options?: Parameters<typeof assemble>[4]): Promise<AssembleResult>;
@@ -34,9 +34,9 @@
34
34
  * In-process renders (`executeRenderJob`) leave them off.
35
35
  */
36
36
  import { assertSwiftShader, type CaptureOptions, type CaptureMode, type CaptureSession, closeCaptureSession, createCaptureSession, type EngineConfig, initializeSession, probeBeginFrameLiveness, readWebGlVendorInfoFromCanvas } from "@hyperframes/engine";
37
- import { type LockedRenderConfig } from "../render/stages/freezePlan.js";
37
+ import { type ChunkSliceJson, type LockedRenderConfig } from "../render/stages/freezePlan.js";
38
38
  import { type PlanVideosJson } from "./shared.js";
39
- export { CHUNK_INDEX_OUT_OF_RANGE, createChunkVideoFrameInjectorFactory, extractDeferredVideoFramesForChunk, FFMPEG_VERSION_MISMATCH, INVALID_VIDEO_METADATA, MISSING_PLAN_ARTIFACT, MISSING_RUNTIME_ENV_SNAPSHOT, PLAN_HASH_MISMATCH, rebuildExtractedFramesFromPlanDir, RenderChunkValidationError, } from "./chunkFrames.js";
39
+ export { CHUNK_FRAME_RANGE_INVALID, CHUNK_INDEX_OUT_OF_RANGE, createChunkVideoFrameInjectorFactory, extractDeferredVideoFramesForChunk, FFMPEG_VERSION_MISMATCH, INVALID_VIDEO_METADATA, MISSING_PLAN_ARTIFACT, MISSING_RUNTIME_ENV_SNAPSHOT, PLAN_HASH_MISMATCH, rebuildExtractedFramesFromPlanDir, RenderChunkValidationError, } from "./chunkFrames.js";
40
40
  /** Validate the shared video contract before any v1 chunk can inject frames. */
41
41
  export declare function validatePlanVideosForChunk(value: unknown): PlanVideosJson;
42
42
  /**
@@ -51,6 +51,7 @@ export interface ChunkResult {
51
51
  /** `"file"` for mp4/mov; `"frame-dir"` for png-sequence. */
52
52
  outputKind: "file" | "frame-dir";
53
53
  framesEncoded: number;
54
+ frameRange?: ChunkFrameRange;
54
55
  sha256: string;
55
56
  durationMs: number;
56
57
  /**
@@ -92,7 +93,26 @@ export interface ChunkResult {
92
93
  /** Result returned by the built-in renderer, which always observes its mode. */
93
94
  export interface EffectiveChunkResult extends ChunkResult {
94
95
  captureMode: CaptureMode;
96
+ frameRange: ChunkFrameRange;
95
97
  }
98
+ /** Absolute composition frame indices, end exclusive, like a chunk's slice. */
99
+ export interface ChunkFrameRange {
100
+ startFrame: number;
101
+ endFrame: number;
102
+ }
103
+ export interface RenderChunkOptions {
104
+ /**
105
+ * Render only these frames of the chunk, e.g. to retry a slow chunk as
106
+ * parts rendered in parallel. Must be a non-empty range inside the chunk's
107
+ * slice. Each part is a self-contained output (its own IDR keyframe, frame
108
+ * names from zero, the audio packets it owns); pass the parts of a chunk,
109
+ * in order, as one `chunkPaths` entry to `assemble`. Omitted renders the
110
+ * whole slice.
111
+ */
112
+ frameRange?: ChunkFrameRange;
113
+ }
114
+ /** The frames a chunk renders: its slice, or a validated sub-range of it. */
115
+ export declare function resolveChunkFrameRange(slice: ChunkSliceJson, chunkIndex: number, frameRange: ChunkFrameRange | undefined): ChunkFrameRange;
96
116
  /** Compatibility-safe adapter seam for built-in or injected chunk renderers. */
97
117
  export type ChunkRenderer = (planDir: string, chunkIndex: number, outputChunkPath: string) => Promise<ChunkResult>;
98
118
  interface DistributedCaptureSessionDependencies {
@@ -159,4 +179,4 @@ export declare function resolveLockedVp9CpuUsed(lockedEncoder: Pick<LockedRender
159
179
  * outputs — the caller picks the right shape based on `meta/encoder.json`.
160
180
  * `renderChunk` enforces the same choice via `outputKind` on the result.
161
181
  */
162
- export declare function renderChunk(planDir: string, chunkIndex: number, outputChunkPath: string): Promise<EffectiveChunkResult>;
182
+ export declare function renderChunk(planDir: string, chunkIndex: number, outputChunkPath: string, options?: RenderChunkOptions): Promise<EffectiveChunkResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.8.179",
3
+ "version": "0.8.180",
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/core": "0.8.179",
39
- "@hyperframes/engine": "0.8.179",
40
- "@hyperframes/lint": "0.8.179",
41
- "@hyperframes/studio-server": "0.8.179",
42
- "@hyperframes/parsers": "0.8.179"
38
+ "@hyperframes/engine": "0.8.180",
39
+ "@hyperframes/lint": "0.8.180",
40
+ "@hyperframes/parsers": "0.8.180",
41
+ "@hyperframes/core": "0.8.180",
42
+ "@hyperframes/studio-server": "0.8.180"
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.179"
51
+ "@hyperframes/aws-lambda": "0.8.180"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"