hyperframes 0.8.178 → 0.8.179

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 (118) hide show
  1. package/dist/{add-C4GN5ZDP.js → add-2CCBND75.js} +5 -5
  2. package/dist/api.js +8 -8
  3. package/dist/{asset-WTZ44Q7G.js → asset-7HOLVSD5.js} +2 -2
  4. package/dist/{audio-ZSWOKO3D.js → audio-356GVOLJ.js} +5 -5
  5. package/dist/{auth-EUSWPXME.js → auth-YHI5VR4X.js} +1 -1
  6. package/dist/{autoUpdate-WWXRJ2W7.js → autoUpdate-OP345EBV.js} +2 -2
  7. package/dist/{backgroundChecks-B2IJHAP6.js → backgroundChecks-6NVELE25.js} +3 -3
  8. package/dist/backgroundChecksWorker.js +3 -3
  9. package/dist/{beats-OJT75KQY.js → beats-DQH5SOHM.js} +4 -4
  10. package/dist/{benchmark-EZTB4ITW.js → benchmark-2W2NPNLJ.js} +6 -6
  11. package/dist/{browser-DNHBS5C7.js → browser-SCM75524.js} +3 -3
  12. package/dist/{bundleWithLocalizedFonts-GJAML42J.js → bundleWithLocalizedFonts-4F6HKLMZ.js} +1 -1
  13. package/dist/{catalog-YF23PXV2.js → catalog-MOSGL65T.js} +6 -6
  14. package/dist/{catch-up-YZZDUDRQ.js → catch-up-TCAZFTMO.js} +4 -4
  15. package/dist/{check-NVGQ5PMF.js → check-QVXSTHZB.js} +12 -12
  16. package/dist/{checkBrowser-JX22QQDH.js → checkBrowser-NF2CMWYR.js} +9 -9
  17. package/dist/chunk-2R5ZKWSV.js +26 -0
  18. package/dist/{chunk-HIP62XAW.js → chunk-2X4PQEYM.js} +1 -3
  19. package/dist/{chunk-HNMRB77F.js → chunk-5HZAIPHY.js} +1 -1
  20. package/dist/{chunk-DKGZR7QA.js → chunk-AEWZDKFS.js} +1 -1
  21. package/dist/{chunk-DNZMBQTH.js → chunk-AHPGAESX.js} +1 -1
  22. package/dist/{chunk-J4GE3BCG.js → chunk-BW2UQR4A.js} +4 -4
  23. package/dist/{chunk-Y6JLEN3P.js → chunk-C5XU53VP.js} +2 -2
  24. package/dist/{chunk-NL5F3AD4.js → chunk-CML25PAF.js} +2 -4
  25. package/dist/{chunk-U2AIXPAQ.js → chunk-COK5OUBX.js} +1 -1
  26. package/dist/{chunk-CJRGV2JW.js → chunk-CW3VIQMZ.js} +1 -1
  27. package/dist/{chunk-N6S6V2DY.js → chunk-DHKYJYOY.js} +1 -1
  28. package/dist/{chunk-C4FH67KI.js → chunk-DLF5KIBL.js} +2 -2
  29. package/dist/{chunk-FE2XTCTM.js → chunk-EEP5Y5IE.js} +1 -18
  30. package/dist/{chunk-YAPJOCSH.js → chunk-F3GML67X.js} +1 -1
  31. package/dist/{chunk-7IAVKLNC.js → chunk-HU4PBQGJ.js} +1 -1
  32. package/dist/{chunk-RTABHL2G.js → chunk-IHWI3KZY.js} +1 -1
  33. package/dist/{chunk-V6WIQWUB.js → chunk-ILLFG4RN.js} +2 -2
  34. package/dist/{chunk-O7PFPCQF.js → chunk-JRIXYXG3.js} +1334 -1252
  35. package/dist/{chunk-EBKIF46T.js → chunk-LBRJKTVY.js} +2 -2
  36. package/dist/{chunk-XBZOYP4D.js → chunk-M2DZX2DP.js} +1 -1
  37. package/dist/{chunk-G3M4BIFN.js → chunk-M5CJQ6EH.js} +1 -1
  38. package/dist/{chunk-74Z2T3YM.js → chunk-MK6D6HVT.js} +1 -1
  39. package/dist/{chunk-EINNFZAW.js → chunk-MXNKK7HY.js} +2 -2
  40. package/dist/{chunk-HFUMZZ4T.js → chunk-N3U7WHQK.js} +3 -3
  41. package/dist/{chunk-ZAO7FTQM.js → chunk-NTMCSIQB.js} +2 -2
  42. package/dist/{chunk-5PK3RLSB.js → chunk-OINJILMB.js} +1 -1
  43. package/dist/{chunk-62OXHKLS.js → chunk-OXP77SHD.js} +1 -1
  44. package/dist/{chunk-AR5MS234.js → chunk-Q73AGCTV.js} +3 -3
  45. package/dist/{chunk-AYEWZYYC.js → chunk-RJIJWETG.js} +1 -1
  46. package/dist/{chunk-UIXHQ4QG.js → chunk-VI7IQ2QF.js} +2 -2
  47. package/dist/{chunk-N66NVE65.js → chunk-WOXRCPJU.js} +1 -1
  48. package/dist/{chunk-PO2SDVQA.js → chunk-WQ6ZXHFQ.js} +3 -1
  49. package/dist/{chunk-UQEBSY5U.js → chunk-XX4ZYQR6.js} +1 -1
  50. package/dist/{clean-ODNP4ELD.js → clean-SJ2UIDKG.js} +4 -4
  51. package/dist/cli.js +52 -52
  52. package/dist/{cloud-2DACBBEP.js → cloud-DH7QGNPZ.js} +4 -4
  53. package/dist/{cloudrun-QSVGI2TT.js → cloudrun-ZA2ZH2VZ.js} +5 -3
  54. package/dist/{compare-HMMGOM4F.js → compare-5JNN5BHX.js} +4 -4
  55. package/dist/{component-C6OLXSIK.js → component-LJQWLOXZ.js} +3 -3
  56. package/dist/{compositions-MOGZ4ASK.js → compositions-UILNO2RF.js} +5 -5
  57. package/dist/{delete-MDAYHORQ.js → delete-WIO2MDPB.js} +2 -2
  58. package/dist/deploy-BUEYC7MP.js +196 -0
  59. package/dist/{destroy-BEL232I5.js → destroy-QBLMYDBF.js} +6 -3
  60. package/dist/{doctor-VEKKYMQT.js → doctor-5G2RXBQX.js} +3 -3
  61. package/dist/{events-CSKJXRJU.js → events-2LHNVY2F.js} +3 -3
  62. package/dist/{events-SKMTX6HF.js → events-BXODXMJ6.js} +2 -2
  63. package/dist/{feedback-64AKUFHZ.js → feedback-57END2CW.js} +4 -4
  64. package/dist/{figma-YEY76ZPG.js → figma-O5MO4GF5.js} +3 -3
  65. package/dist/fontLocalizeCli.js +2 -2
  66. package/dist/{get-YJS76PGZ.js → get-OEWPJ4HH.js} +2 -2
  67. package/dist/{grade-compare-35UEA52Z.js → grade-compare-PG2UOMHO.js} +4 -4
  68. package/dist/{help-I5TCDUVH.js → help-44J4SQTE.js} +1 -1
  69. package/dist/{history-7BKIWSP2.js → history-CUA53AOE.js} +6 -6
  70. package/dist/hyperframes-player.global.js +1 -1
  71. package/dist/{info-UEPOM2LV.js → info-UDXYDQVK.js} +5 -5
  72. package/dist/{init-HIB66K2S.js → init-CVT6HLW2.js} +16 -16
  73. package/dist/{inspect-634XIKDS.js → inspect-6F72RJQG.js} +6 -6
  74. package/dist/{keyframes-7BJX3EVE.js → keyframes-GJ7EEWW2.js} +5 -5
  75. package/dist/{lambda-Q4TRSMHZ.js → lambda-WFSBYU6U.js} +11 -16
  76. package/dist/{layout-6DUVSHEB.js → layout-SASENZOG.js} +6 -6
  77. package/dist/{lint-UPFD4JQZ.js → lint-JX57B52X.js} +6 -6
  78. package/dist/{list-M6OMQXJX.js → list-WG7NTE63.js} +2 -2
  79. package/dist/{login-QH6JDXDP.js → login-3NFLD65G.js} +4 -4
  80. package/dist/{manager-B4MSVFMK.js → manager-BKHRATMY.js} +2 -2
  81. package/dist/{media-treatment-SHFTIG4K.js → media-treatment-QI5F2SMU.js} +5 -5
  82. package/dist/{models-USEFHJFY.js → models-TXURF3J6.js} +4 -4
  83. package/dist/{normalize-audio-EP3HPQZB.js → normalize-audio-IEKL43N4.js} +4 -4
  84. package/dist/{open-HXZEXQ4X.js → open-GU3I75TB.js} +4 -4
  85. package/dist/{parakeetRunner-D5RSHQB2.js → parakeetRunner-S2QI3PCA.js} +5 -5
  86. package/dist/{pipeline-BDG52SPL.js → pipeline-OO2MYYIV.js} +2 -2
  87. package/dist/{play-LSRQ6R66.js → play-VL2WQCT3.js} +4 -4
  88. package/dist/{present-2VKAE63Q.js → present-YWSFX5CH.js} +4 -4
  89. package/dist/{preview-MWH5CHTN.js → preview-SMG6GXLY.js} +5 -5
  90. package/dist/{producer-W24FRIN7.js → producer-67RIQCWZ.js} +1 -1
  91. package/dist/{remove-background-JBOJAUIM.js → remove-background-HO2NBZCJ.js} +3 -3
  92. package/dist/{render-BROS2AIU.js → render-DH4CGGLO.js} +5 -5
  93. package/dist/{render-JCYFNDWT.js → render-MB7PW2YQ.js} +9 -9
  94. package/dist/{render-batch-FW56ZFGW.js → render-batch-4RPH66OX.js} +4 -1
  95. package/dist/{sam-IKPEJRRO.js → sam-MC24SZHC.js} +1 -1
  96. package/dist/{sherpa-GWYCXYEP.js → sherpa-LVXRZGE3.js} +4 -4
  97. package/dist/{sites-HZMADOA3.js → sites-3ZBYB5X4.js} +2 -1
  98. package/dist/{skills-YNM5SIHQ.js → skills-NDWQPEUD.js} +5 -5
  99. package/dist/{skillsUpdateCheck-FNC6VALY.js → skillsUpdateCheck-HZVZ3IUU.js} +3 -3
  100. package/dist/{snapshot-6EMEL4O3.js → snapshot-MCHWMAPH.js} +7 -7
  101. package/dist/{src-VUIKXEFU.js → src-E34ZBMAS.js} +1 -1
  102. package/dist/studio/assets/{hyperframes-player-Dfw61RJK.js → hyperframes-player-C0SyMC21.js} +1 -1
  103. package/dist/studio/assets/{index-eTRj5rY2.js → index-DHUassFP.js} +1 -1
  104. package/dist/studio/assets/{index-Bi6yVTq9.js → index-ueMRyAJ2.js} +150 -150
  105. package/dist/studio/index.html +1 -1
  106. package/dist/{studioServer-ZP2LJT7H.js → studioServer-XK5N6PIB.js} +8 -8
  107. package/dist/{synthesize-W2M7QGDV.js → synthesize-I2C24XJ5.js} +2 -2
  108. package/dist/{telemetry-HJQ7IO6R.js → telemetry-DD5ZIBOK.js} +3 -3
  109. package/dist/{timeline-CYW3YKMX.js → timeline-MJJEX5CU.js} +5 -5
  110. package/dist/{tokens-VOARLAAH.js → tokens-AKUZMYRE.js} +2 -2
  111. package/dist/{transcribe-F6UOG57Z.js → transcribe-ER7GGF4L.js} +3 -3
  112. package/dist/{transcribe-FJGNOR6R.js → transcribe-ZQI5P2LH.js} +9 -9
  113. package/dist/{tts-RE227GFP.js → tts-EDK3QS7M.js} +3 -3
  114. package/dist/{updateCheck-IOLCO3XV.js → updateCheck-GYFCXFWR.js} +2 -2
  115. package/dist/{upgrade-BVW67SB7.js → upgrade-UNWHMZP5.js} +2 -2
  116. package/dist/{validate-3NHIT4VT.js → validate-32CQZFNZ.js} +6 -6
  117. package/package.json +1 -1
  118. package/dist/deploy-3TTQZGTF.js +0 -116
@@ -13167,14 +13167,14 @@ async function executeRenderPipeline(input) {
13167
13167
  import {
13168
13168
  copyFileSync as copyFileSync4,
13169
13169
  cpSync as cpSync2,
13170
- existsSync as existsSync19,
13170
+ existsSync as existsSync20,
13171
13171
  linkSync,
13172
- mkdirSync as mkdirSync10,
13173
- renameSync as renameSync4,
13174
- rmSync as rmSync9,
13175
- writeFileSync as writeFileSync7
13172
+ mkdirSync as mkdirSync12,
13173
+ renameSync as renameSync5,
13174
+ rmSync as rmSync10,
13175
+ writeFileSync as writeFileSync8
13176
13176
  } from "fs";
13177
- import { extname as extname6, isAbsolute as isAbsolute2, join as join31, relative as relative5, sep as sep2 } from "path";
13177
+ import { extname as extname6, isAbsolute as isAbsolute2, join as join33, relative as relative5, sep as sep2 } from "path";
13178
13178
 
13179
13179
  // ../producer/src/services/distributed/planFrameCost.ts
13180
13180
  import { join as join28 } from "path";
@@ -14072,187 +14072,696 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
14072
14072
  };
14073
14073
  }
14074
14074
 
14075
- // ../producer/src/services/distributed/planSize.ts
14076
- import { createHash as createHash6 } from "crypto";
14077
- import { lstatSync as lstatSync2, readdirSync as readdirSync8 } from "fs";
14078
- import { extname as extname5, join as join30, relative as relative4 } from "path";
14079
- var MAX_TOP_COMPONENTS = 10;
14080
- var SOURCE_MEDIA_EXTENSIONS = /* @__PURE__ */ new Set([
14081
- ".aac",
14082
- ".aiff",
14083
- ".avi",
14084
- ".flac",
14085
- ".m4a",
14086
- ".m4v",
14087
- ".mkv",
14088
- ".mov",
14089
- ".mp3",
14090
- ".mp4",
14091
- ".mpeg",
14092
- ".mpg",
14093
- ".ogg",
14094
- ".ogv",
14095
- ".ts",
14096
- ".wav",
14097
- ".webm"
14098
- ]);
14099
- var PLAN_ROOT_CATEGORY = {
14100
- compiled: "compiled",
14101
- [PLAN_AUDIO_RELATIVE_PATH]: "audio",
14102
- meta: "metadata",
14103
- "plan.json": "metadata"
14104
- };
14105
- function hashComponent(value) {
14106
- return createHash6("sha256").update(value).digest("hex").slice(0, 12);
14075
+ // ../producer/src/services/distributed/chunkAudio.ts
14076
+ import { closeSync as closeSync6, existsSync as existsSync19, ftruncateSync, mkdirSync as mkdirSync11, openSync as openSync5, readFileSync as readFileSync10 } from "fs";
14077
+ import { writeFileSync as writeFileSync7, writeSync } from "fs";
14078
+ import { join as join31 } from "path";
14079
+
14080
+ // ../producer/src/services/distributed/mp4Stitch.ts
14081
+ import {
14082
+ createReadStream as createReadStream4,
14083
+ createWriteStream,
14084
+ mkdirSync as mkdirSync10,
14085
+ mkdtempSync as mkdtempSync4,
14086
+ renameSync as renameSync4,
14087
+ rmSync as rmSync9
14088
+ } from "fs";
14089
+ import { open } from "fs/promises";
14090
+ import { dirname as dirname11, join as join30 } from "path";
14091
+ import { finished, pipeline } from "stream/promises";
14092
+ function boxAt(src, off, at, limit) {
14093
+ let size = src.readUInt32BE(off);
14094
+ let bodyStart = at + 8;
14095
+ if (size === 1) {
14096
+ size = Number(src.readBigUInt64BE(off + 8));
14097
+ bodyStart = at + 16;
14098
+ }
14099
+ if (size < 8 || at + size > limit) throw new Error(`[mp4Stitch] malformed box at ${at}`);
14100
+ return { type: src.toString("latin1", off + 4, off + 8), start: at, bodyStart, end: at + size };
14107
14101
  }
14108
- function classifyFile(segments, rootKind) {
14109
- const rootName = segments[0] ?? "";
14110
- const extractedRootName = rootKind === "compiled" ? "__hyperframes_video_frames" : "video-frames";
14111
- if (rootName === extractedRootName) {
14112
- const videoKey = segments[1] ?? "unknown";
14113
- return {
14114
- category: "video-frames",
14115
- componentLabel: `video-frames:${hashComponent(videoKey)}`
14116
- };
14102
+ function childBoxes(buf, parent) {
14103
+ const boxes = [];
14104
+ for (let at = parent.bodyStart; at < parent.end; at = boxes.at(-1).end) {
14105
+ boxes.push(boxAt(buf, at, at, parent.end));
14117
14106
  }
14118
- const category = rootKind === "compiled" ? "compiled" : PLAN_ROOT_CATEGORY[rootName] ?? "other";
14119
- return { category, componentLabel: category };
14107
+ return boxes;
14120
14108
  }
14121
- function emptyBreakdown() {
14122
- return {
14123
- totalBytes: 0,
14124
- fileCount: 0,
14125
- compiledBytes: 0,
14126
- sourceMediaBytes: 0,
14127
- videoFramesBytes: 0,
14128
- videoFrameFileCount: 0,
14129
- audioBytes: 0,
14130
- metadataBytes: 0,
14131
- otherBytes: 0,
14132
- topComponents: []
14133
- };
14109
+ function findChild(buf, parent, type) {
14110
+ return childBoxes(buf, parent).find((box2) => box2.type === type);
14134
14111
  }
14135
- function addCategoryBytes(result, category, filePath, sizeBytes) {
14136
- if (category === "compiled") {
14137
- result.compiledBytes += sizeBytes;
14138
- if (SOURCE_MEDIA_EXTENSIONS.has(extname5(filePath).toLowerCase())) {
14139
- result.sourceMediaBytes += sizeBytes;
14140
- }
14141
- return;
14142
- }
14143
- if (category === "video-frames") {
14144
- result.videoFramesBytes += sizeBytes;
14145
- result.videoFrameFileCount += 1;
14146
- return;
14147
- }
14148
- if (category === "audio") {
14149
- result.audioBytes += sizeBytes;
14150
- return;
14151
- }
14152
- if (category === "metadata") {
14153
- result.metadataBytes += sizeBytes;
14154
- return;
14155
- }
14156
- result.otherBytes += sizeBytes;
14112
+ function child(buf, parent, type) {
14113
+ const found = findChild(buf, parent, type);
14114
+ if (!found) throw new Error(`[mp4Stitch] ${parent.type} has no ${type}`);
14115
+ return found;
14157
14116
  }
14158
- function recordFile(result, components, rootDir, rootKind, filePath) {
14159
- let sizeBytes;
14160
- try {
14161
- const stat = lstatSync2(filePath);
14162
- if (!stat.isFile()) return;
14163
- sizeBytes = stat.size;
14164
- } catch {
14165
- return;
14166
- }
14167
- const rel = relative4(rootDir, filePath);
14168
- const segments = rel.split(/[\\/]+/).filter(Boolean);
14169
- const classified = classifyFile(segments, rootKind);
14170
- result.totalBytes += sizeBytes;
14171
- result.fileCount += 1;
14172
- addCategoryBytes(result, classified.category, filePath, sizeBytes);
14173
- const component = components.get(classified.componentLabel) ?? {
14174
- label: classified.componentLabel,
14175
- sizeBytes: 0,
14176
- fileCount: 0
14117
+ var SCAN_BYTES = 64 * 1024;
14118
+ async function readMp4Layout(read) {
14119
+ const found = /* @__PURE__ */ new Map();
14120
+ let payload;
14121
+ let buf = Buffer.alloc(0);
14122
+ let bufAt = 0;
14123
+ const load = async (start, end) => {
14124
+ if (start >= bufAt && end <= bufAt + buf.length) return;
14125
+ buf = await read(start, Math.max(end, start + SCAN_BYTES));
14126
+ bufAt = start;
14177
14127
  };
14178
- component.sizeBytes += sizeBytes;
14179
- component.fileCount += 1;
14180
- components.set(classified.componentLabel, component);
14181
- }
14182
- function walkRegularFiles(dir, visit) {
14183
- let entries;
14184
- try {
14185
- entries = readdirSync8(dir, { withFileTypes: true });
14186
- } catch {
14187
- return;
14188
- }
14189
- for (const entry of entries) {
14190
- const full = join30(dir, entry.name);
14191
- if (entry.isDirectory()) {
14192
- walkRegularFiles(full, visit);
14193
- } else if (entry.isFile()) {
14194
- visit(full);
14128
+ for (let at = 0; !(found.has("ftyp") && found.has("moov") && payload); ) {
14129
+ await load(at, at + 16);
14130
+ if (at + 8 > bufAt + buf.length) {
14131
+ throw new Error(`[mp4Stitch] chunk needs ftyp, moov and mdat; found ${[...found.keys()]}`);
14132
+ }
14133
+ const box2 = boxAt(buf, at - bufAt, at, Number.MAX_SAFE_INTEGER);
14134
+ if (box2.type === "mdat") payload ??= box2;
14135
+ if (box2.type === "ftyp" || box2.type === "moov") {
14136
+ await load(at, box2.end);
14137
+ found.set(box2.type, buf.subarray(at - bufAt, box2.end - bufAt));
14195
14138
  }
14139
+ at = box2.end;
14196
14140
  }
14141
+ const [ftyp, moov] = [found.get("ftyp"), found.get("moov")];
14142
+ if (moov.length !== moovSize(moov)) throw new Error("[mp4Stitch] moov is cut off");
14143
+ return { ftyp, moov, payloadStart: payload.bodyStart, payloadEnd: payload.end };
14197
14144
  }
14198
- function measurePlanSizeBreakdown(rootDir, rootKind = "plan") {
14199
- const result = emptyBreakdown();
14200
- const components = /* @__PURE__ */ new Map();
14201
- walkRegularFiles(
14202
- rootDir,
14203
- (filePath) => recordFile(result, components, rootDir, rootKind, filePath)
14204
- );
14205
- result.topComponents = [...components.values()].sort((a, b) => b.sizeBytes - a.sizeBytes || a.label.localeCompare(b.label)).slice(0, MAX_TOP_COMPONENTS);
14206
- return result;
14207
- }
14208
-
14209
- // ../producer/src/services/distributed/plan.ts
14210
- function logPlanStage(log, stage, startedAt) {
14211
- log.info("[plan] stage", { stage, ms: Date.now() - startedAt });
14145
+ var moovSize = (moov) => boxAt(moov, 0, 0, Number.MAX_SAFE_INTEGER).end;
14146
+ function readTracks(moov) {
14147
+ const root = boxAt(moov, 0, 0, moov.length);
14148
+ return childBoxes(moov, root).filter((box2) => box2.type === "trak").map((trak) => readTrack(moov, trak));
14212
14149
  }
14213
- function applyDistributedAudioWarningPolicy(job, audioError, audioFailures = [], log = defaultLogger) {
14214
- const failureOwner = audioFailures.length === 0 ? void 0 : audioFailures.some((failure) => failure.owner === "system") ? "system" : "user";
14215
- const retryable = audioFailures.length === 0 ? void 0 : audioFailures.every((failure) => failure.retryable);
14216
- applyRenderWarningPolicy(
14217
- job,
14218
- [
14219
- {
14220
- code: "audio_processing_failed",
14221
- message: `Audio mix failed; output would be video-only: ${audioError}`,
14222
- details: {
14223
- mediaType: "audio",
14224
- audioFailures: audioFailureDiagnostics(audioFailures),
14225
- failureReasons: [...new Set(audioFailures.map((failure) => failure.reason))],
14226
- failureStages: [...new Set(audioFailures.map((failure) => failure.stage))],
14227
- failureOwner,
14228
- retryable
14229
- }
14230
- }
14231
- ],
14232
- log
14150
+ function rows(buf, table, columns, countAt = 4) {
14151
+ const count = buf.readUInt32BE(table.bodyStart + countAt);
14152
+ const first = table.bodyStart + countAt + 4;
14153
+ return Array.from(
14154
+ { length: count },
14155
+ (_, row) => Array.from(
14156
+ { length: columns },
14157
+ (_2, col) => buf.readUInt32BE(first + (row * columns + col) * 4)
14158
+ )
14233
14159
  );
14234
14160
  }
14235
- var PLAN_PROJECT_DIR_SKIP_SEGMENTS = /* @__PURE__ */ new Set([
14236
- "node_modules",
14237
- ".git",
14238
- ".cache",
14239
- "output",
14240
- "failures",
14241
- "dist",
14242
- ".next",
14243
- ".turbo"
14244
- ]);
14245
- var DEFAULT_MAX_PARALLEL_CHUNKS = 16;
14246
- var MIN_CHUNK_SIZE = 10;
14247
- var MAX_PARALLEL_CHUNKS_CEILING = 256;
14248
- var ESTIMATED_SECONDS_PER_MEGAPIXEL_FRAME = 0.11;
14249
- var ESTIMATED_CHUNK_OVERHEAD_SECONDS = 60;
14250
- var WORKER_TIMEOUT_MARGIN = 0.75;
14251
- function estimateFrameSeconds(megapixels, measuredSecondsPerFrame = 0) {
14252
- return Math.max(ESTIMATED_SECONDS_PER_MEGAPIXEL_FRAME * megapixels, measuredSecondsPerFrame);
14161
+ function readMediaTime(moov, trak) {
14162
+ const edts = findChild(moov, trak, "edts");
14163
+ if (!edts) return 0;
14164
+ const elst = child(moov, edts, "elst");
14165
+ const wide = moov[elst.bodyStart] === 1;
14166
+ const entries = moov.readUInt32BE(elst.bodyStart + 4);
14167
+ for (let i = 0, at = elst.bodyStart + 8; i < entries; i++, at += wide ? 20 : 12) {
14168
+ const offset = at + (wide ? 8 : 4);
14169
+ const mediaTime = wide ? Number(moov.readBigInt64BE(offset)) : moov.readInt32BE(offset);
14170
+ if (mediaTime !== -1) return mediaTime;
14171
+ }
14172
+ return 0;
14253
14173
  }
14254
- function estimateChunkSeconds(frames, megapixels, measuredSecondsPerFrame) {
14255
- return ESTIMATED_CHUNK_OVERHEAD_SECONDS + frames * estimateFrameSeconds(megapixels, measuredSecondsPerFrame);
14174
+ function readSizes(moov, stsz) {
14175
+ const fixedSize = moov.readUInt32BE(stsz.bodyStart + 4);
14176
+ if (!fixedSize) return rows(moov, stsz, 1, 8).map(([size]) => size);
14177
+ return Array(moov.readUInt32BE(stsz.bodyStart + 8)).fill(fixedSize);
14178
+ }
14179
+ function readSync4(moov, stbl, sampleCount) {
14180
+ const stss = findChild(moov, stbl, "stss");
14181
+ if (!stss) return null;
14182
+ const sync = Array(sampleCount).fill(false);
14183
+ for (const [n] of rows(moov, stss, 1)) sync[n - 1] = true;
14184
+ return sync;
14185
+ }
14186
+ function readChunkOffsets(moov, stbl) {
14187
+ const stco = findChild(moov, stbl, "stco");
14188
+ if (stco) return rows(moov, stco, 1).map(([offset]) => offset);
14189
+ return rows(moov, child(moov, stbl, "co64"), 2).map(([hi, lo]) => hi * 2 ** 32 + lo);
14190
+ }
14191
+ function readSamplesPerChunk(moov, stbl, chunkCount) {
14192
+ const stsc = rows(moov, child(moov, stbl, "stsc"), 3);
14193
+ return stsc.flatMap(([firstChunk, perChunk, description], i) => {
14194
+ if (description !== 1) throw new Error("[mp4Stitch] chunk uses more than one sample entry");
14195
+ const nextFirst = stsc[i + 1]?.[0] ?? chunkCount + 1;
14196
+ return Array(nextFirst - firstChunk).fill(perChunk);
14197
+ });
14198
+ }
14199
+ function readTrack(moov, trak) {
14200
+ const mdia = child(moov, trak, "mdia");
14201
+ const hdlr = child(moov, mdia, "hdlr");
14202
+ const stbl = child(moov, child(moov, mdia, "minf"), "stbl");
14203
+ if (findChild(moov, stbl, "ctts")) throw new Error("[mp4Stitch] chunk has ctts (B-frames)");
14204
+ const stts = rows(moov, child(moov, stbl, "stts"), 2);
14205
+ const durations = stts.flatMap(([n, delta]) => Array(n).fill(delta));
14206
+ const sizes = readSizes(moov, child(moov, stbl, "stsz"));
14207
+ const chunkOffsets = readChunkOffsets(moov, stbl);
14208
+ const samplesPerChunk = readSamplesPerChunk(moov, stbl, chunkOffsets.length);
14209
+ if (durations.length !== sizes.length || sum(samplesPerChunk) !== sizes.length) {
14210
+ throw new Error("[mp4Stitch] sample tables disagree on the sample count");
14211
+ }
14212
+ const stsd = child(moov, stbl, "stsd");
14213
+ return {
14214
+ trak,
14215
+ handler: moov.toString("latin1", hdlr.bodyStart + 8, hdlr.bodyStart + 12),
14216
+ timescale: timescaleOf(moov, child(moov, mdia, "mdhd")),
14217
+ mediaTime: readMediaTime(moov, trak),
14218
+ stsd: moov.subarray(stsd.start, stsd.end),
14219
+ durations,
14220
+ sizes,
14221
+ sync: readSync4(moov, stbl, sizes.length),
14222
+ samplesPerChunk,
14223
+ chunkOffsets
14224
+ };
14225
+ }
14226
+ function sum(values) {
14227
+ return values.reduce((total, value) => total + value, 0);
14228
+ }
14229
+ function box(type, ...body) {
14230
+ const header = Buffer.alloc(8);
14231
+ header.writeUInt32BE(8 + sum(body.map((part) => part.length)));
14232
+ header.write(type, 4, "latin1");
14233
+ return Buffer.concat([header, ...body]);
14234
+ }
14235
+ function fullBox(type, version, body) {
14236
+ return box(type, Buffer.from([version, 0, 0, 0]), body);
14237
+ }
14238
+ function u32s(values) {
14239
+ const buf = Buffer.alloc(values.length * 4);
14240
+ values.forEach((value, i) => buf.writeUInt32BE(value, i * 4));
14241
+ return buf;
14242
+ }
14243
+ function runs(values) {
14244
+ const out = [];
14245
+ for (const value of values) {
14246
+ const last = out.at(-1);
14247
+ if (last && last[1] === value) last[0]++;
14248
+ else out.push([1, value]);
14249
+ }
14250
+ return out;
14251
+ }
14252
+ function rebuild(buf, parent, replace) {
14253
+ const parts = childBoxes(buf, parent).map(
14254
+ (box2) => replace[box2.type]?.(box2) ?? buf.subarray(box2.start, box2.end)
14255
+ );
14256
+ return box(parent.type, ...parts);
14257
+ }
14258
+ var DURATION_AT = {
14259
+ mvhd: [16, 24],
14260
+ mdhd: [16, 24],
14261
+ tkhd: [20, 28]
14262
+ };
14263
+ function withDuration(buf, target, duration) {
14264
+ const copy = Buffer.from(buf.subarray(target.start, target.end));
14265
+ const body = target.bodyStart - target.start;
14266
+ const wide = copy[body] === 1;
14267
+ const at = body + DURATION_AT[target.type][wide ? 1 : 0];
14268
+ if (wide) copy.writeBigUInt64BE(BigInt(duration), at);
14269
+ else if (duration <= 4294967295) copy.writeUInt32BE(duration, at);
14270
+ else throw new Error(`[mp4Stitch] ${target.type} duration ${duration} needs a version 1 box`);
14271
+ return copy;
14272
+ }
14273
+ function timescaleOf(buf, header) {
14274
+ return buf.readUInt32BE(header.bodyStart + (buf[header.bodyStart] === 1 ? 20 : 12));
14275
+ }
14276
+ function buildStbl(stsd, track) {
14277
+ const stts = runs(track.durations);
14278
+ const stsc = [];
14279
+ let firstChunk = 1;
14280
+ for (const [count, perChunk] of runs(track.samplesPerChunk)) {
14281
+ stsc.push([firstChunk, perChunk, 1]);
14282
+ firstChunk += count;
14283
+ }
14284
+ const co64 = Buffer.alloc(4 + track.chunkOffsets.length * 8);
14285
+ co64.writeUInt32BE(track.chunkOffsets.length);
14286
+ track.chunkOffsets.forEach((offset, i) => co64.writeBigUInt64BE(BigInt(offset), 4 + i * 8));
14287
+ const syncSamples = track.sync?.flatMap((isSync, i) => isSync ? [i + 1] : []) ?? null;
14288
+ return box(
14289
+ "stbl",
14290
+ stsd,
14291
+ fullBox("stts", 0, u32s([stts.length, ...stts.flat()])),
14292
+ ...syncSamples ? [fullBox("stss", 0, u32s([syncSamples.length, ...syncSamples]))] : [],
14293
+ fullBox("stsc", 0, u32s([stsc.length, ...stsc.flat()])),
14294
+ fullBox("stsz", 0, u32s([0, track.sizes.length, ...track.sizes])),
14295
+ fullBox("co64", 0, co64)
14296
+ );
14297
+ }
14298
+ var codecOf = (track) => track.stsd.toString("latin1", 20, 24);
14299
+ var sameTrack = (track, plan2) => track.handler === plan2.handler && track.timescale === plan2.timescale && codecOf(track) === codecOf(plan2);
14300
+ function planTracksOf(tracks, plan2, index) {
14301
+ let next = 0;
14302
+ const matched = plan2.map((planTrack) => {
14303
+ const track = tracks[next];
14304
+ if (track && sameTrack(track, planTrack)) return tracks[next++];
14305
+ if (planTrack.handler === "soun") return null;
14306
+ throw new Error(`[mp4Stitch] chunk ${index} tracks differ from chunk 0`);
14307
+ });
14308
+ if (next !== tracks.length)
14309
+ throw new Error(`[mp4Stitch] chunk ${index} tracks differ from chunk 0`);
14310
+ for (const track of tracks) {
14311
+ if (track.handler === "vide" && track.mediaTime !== 0) {
14312
+ throw new Error(`[mp4Stitch] chunk ${index} video starts at media time ${track.mediaTime}`);
14313
+ }
14314
+ }
14315
+ return matched;
14316
+ }
14317
+ function buildStitchedHeader(layouts) {
14318
+ const plan2 = readTracks(layouts[0].moov);
14319
+ const chunks = layouts.map((layout, i) => planTracksOf(readTracks(layout.moov), plan2, i));
14320
+ const payloadBases = [];
14321
+ let payloadBytes = 0;
14322
+ for (const layout of layouts) {
14323
+ payloadBases.push(payloadBytes);
14324
+ payloadBytes += layout.payloadEnd - layout.payloadStart;
14325
+ }
14326
+ const template = layouts[0].moov;
14327
+ const root = boxAt(template, 0, 0, template.length);
14328
+ const movieTimescale = timescaleOf(template, child(template, root, "mvhd"));
14329
+ const buildMoov = (dataStart2) => {
14330
+ const traks = plan2.map((track, t) => {
14331
+ const parts = chunks.flatMap((tracks, chunk) => {
14332
+ const part = tracks[t];
14333
+ return part ? [{ ...part, chunk }] : [];
14334
+ });
14335
+ const merged = {
14336
+ durations: parts.flatMap((part) => part.durations),
14337
+ sizes: parts.flatMap((part) => part.sizes),
14338
+ sync: parts.every((part) => !part.sync) ? null : parts.flatMap((part) => part.sync ?? part.sizes.map(() => true)),
14339
+ samplesPerChunk: parts.flatMap((part) => part.samplesPerChunk),
14340
+ chunkOffsets: parts.flatMap(
14341
+ (part) => part.chunkOffsets.map((offset) => {
14342
+ const i = part.chunk;
14343
+ const { payloadStart, payloadEnd } = layouts[i];
14344
+ if (offset < payloadStart || offset >= payloadEnd) {
14345
+ throw new Error(`[mp4Stitch] chunk ${i} has samples outside its mdat`);
14346
+ }
14347
+ return offset - payloadStart + payloadBases[i] + dataStart2;
14348
+ })
14349
+ )
14350
+ };
14351
+ const mediaDuration = sum(merged.durations);
14352
+ const shown = Math.round(
14353
+ (mediaDuration - track.mediaTime) * movieTimescale / track.timescale
14354
+ );
14355
+ const elst = Buffer.alloc(24);
14356
+ elst.writeUInt32BE(1);
14357
+ elst.writeBigUInt64BE(BigInt(shown), 4);
14358
+ elst.writeBigInt64BE(BigInt(track.mediaTime), 12);
14359
+ elst.writeUInt32BE(65536, 20);
14360
+ const edts = track.mediaTime ? box("edts", fullBox("elst", 1, elst)) : Buffer.alloc(0);
14361
+ const trak = rebuild(template, track.trak, {
14362
+ tkhd: (tkhd) => Buffer.concat([withDuration(template, tkhd, shown), edts]),
14363
+ edts: () => Buffer.alloc(0),
14364
+ mdia: (mdia) => rebuild(template, mdia, {
14365
+ mdhd: (mdhd) => withDuration(template, mdhd, mediaDuration),
14366
+ minf: (minf) => rebuild(template, minf, { stbl: () => buildStbl(track.stsd, merged) })
14367
+ })
14368
+ });
14369
+ return { trak, shown };
14370
+ });
14371
+ let next = 0;
14372
+ return rebuild(template, root, {
14373
+ mvhd: (mvhd) => withDuration(template, mvhd, Math.max(...traks.map((t) => t.shown))),
14374
+ trak: () => traks[next++].trak
14375
+ });
14376
+ };
14377
+ const ftyp = layouts[0].ftyp;
14378
+ const dataStart = ftyp.length + buildMoov(0).length + 16;
14379
+ const mdat = Buffer.alloc(16);
14380
+ mdat.writeUInt32BE(1);
14381
+ mdat.write("mdat", 4, "latin1");
14382
+ mdat.writeBigUInt64BE(BigInt(16 + payloadBytes), 8);
14383
+ return Buffer.concat([ftyp, buildMoov(dataStart), mdat]);
14384
+ }
14385
+ function fileRange(path) {
14386
+ return async (start, end) => {
14387
+ const file = await open(path, "r", 384);
14388
+ try {
14389
+ const buf = Buffer.alloc(end - start);
14390
+ const { bytesRead } = await file.read(buf, 0, buf.length, start);
14391
+ return buf.subarray(0, bytesRead);
14392
+ } finally {
14393
+ await file.close();
14394
+ }
14395
+ };
14396
+ }
14397
+ async function copyPayload(path, layout, index, out) {
14398
+ const { payloadStart, payloadEnd } = layout;
14399
+ const payload = createReadStream4(path, { start: payloadStart, end: payloadEnd - 1 });
14400
+ await pipeline(payload, out, { end: false });
14401
+ if (payload.bytesRead !== payloadEnd - payloadStart) {
14402
+ throw new Error(
14403
+ `[mp4Stitch] chunk ${index} mdat holds ${payload.bytesRead} of ${payloadEnd - payloadStart} payload bytes`
14404
+ );
14405
+ }
14406
+ }
14407
+ async function writeStitchedMp4(chunkPaths, outputPath) {
14408
+ const layouts = await Promise.all(chunkPaths.map((path) => readMp4Layout(fileRange(path))));
14409
+ const header = buildStitchedHeader(layouts);
14410
+ mkdirSync10(dirname11(outputPath), { recursive: true });
14411
+ const workDir = mkdtempSync4(`${outputPath}.stitch-`);
14412
+ const out = createWriteStream(join30(workDir, "out.mp4"));
14413
+ try {
14414
+ out.write(header);
14415
+ for (const [i, path] of chunkPaths.entries()) await copyPayload(path, layouts[i], i, out);
14416
+ out.end();
14417
+ await finished(out);
14418
+ renameSync4(join30(workDir, "out.mp4"), outputPath);
14419
+ } finally {
14420
+ out.destroy();
14421
+ rmSync9(workDir, { recursive: true, force: true });
14422
+ }
14423
+ }
14424
+
14425
+ // ../producer/src/services/distributed/chunkAudio.ts
14426
+ function audioCutForChunk(grid, chunks, index, fps) {
14427
+ const firstPacketAt = (frame) => Math.min(
14428
+ grid.packetCount,
14429
+ Math.max(
14430
+ 0,
14431
+ Math.ceil(
14432
+ (frame * fps.den * grid.sampleRate - grid.firstStart * fps.num) / (grid.packetSamples * fps.num)
14433
+ )
14434
+ )
14435
+ );
14436
+ const first = index === 0 ? 0 : firstPacketAt(chunks[index].startFrame);
14437
+ const end = index === chunks.length - 1 ? grid.packetCount : firstPacketAt(chunks[index + 1].startFrame);
14438
+ if (end <= first) return null;
14439
+ const cutSeconds = (packet) => (grid.firstStart + packet * grid.packetSamples - grid.packetSamples / 2) / grid.sampleRate;
14440
+ const seekSeconds = index === 0 ? null : cutSeconds(first);
14441
+ const durationSeconds = end === grid.packetCount ? null : cutSeconds(end) - (seekSeconds ?? 0);
14442
+ return { seekSeconds, durationSeconds, firstPacket: first, packetCount: end - first };
14443
+ }
14444
+ async function probeAacPacketGrid(audioPath) {
14445
+ const probe = await runFfprobeJson([
14446
+ "-v",
14447
+ "error",
14448
+ "-select_streams",
14449
+ "a:0",
14450
+ "-show_entries",
14451
+ "stream=sample_rate,time_base,nb_frames:packet=pts,duration",
14452
+ "-read_intervals",
14453
+ "%+#1",
14454
+ "-of",
14455
+ "json",
14456
+ "--",
14457
+ audioPath
14458
+ ]);
14459
+ const stream = probe.streams?.[0] ?? {};
14460
+ const packet = probe.packets?.[0] ?? {};
14461
+ const sampleRate = Number(stream.sample_rate);
14462
+ const [tbNum = NaN, tbDen = NaN] = String(stream.time_base).split("/").map(Number);
14463
+ const toSamples = (ticks) => Number(ticks) * sampleRate * tbNum / tbDen;
14464
+ const grid = {
14465
+ sampleRate,
14466
+ firstStart: toSamples(packet.pts),
14467
+ packetSamples: toSamples(packet.duration),
14468
+ packetCount: Number(stream.nb_frames)
14469
+ };
14470
+ const counts = [grid.sampleRate, grid.packetSamples, grid.packetCount];
14471
+ if (!Number.isFinite(grid.firstStart) || !counts.every((n) => Number.isFinite(n) && n > 0)) {
14472
+ throw new Error(`[chunkAudio] could not read the AAC packet grid: ${JSON.stringify(probe)}`);
14473
+ }
14474
+ return grid;
14475
+ }
14476
+ var SLICE_DIR = "audio-chunks";
14477
+ var SLICE_FRAME = `${SLICE_DIR}/frame.m4a`;
14478
+ var SLICE_LAYOUT = `${SLICE_DIR}/layout.json`;
14479
+ function chunkAudioSlicePath(index) {
14480
+ return `${SLICE_DIR}/${String(index).padStart(4, "0")}.bin`;
14481
+ }
14482
+ function chunkAudioSliceTarget(path) {
14483
+ if (path === SLICE_FRAME || path === SLICE_LAYOUT) return "all";
14484
+ const match = /^audio-chunks\/(\d{4,})\.bin$/.exec(path);
14485
+ return match ? Number(match[1]) : null;
14486
+ }
14487
+ async function writeChunkAudioSlices(input) {
14488
+ const grid = await probeAacPacketGrid(input.audioPath);
14489
+ const { payloadStart, payloadEnd, moov } = await readMp4Layout(fileRange(input.audioPath));
14490
+ const [track] = readTracks(moov);
14491
+ const offsets = [];
14492
+ track.chunkOffsets.forEach((offset, chunk) => {
14493
+ for (let i = 0; i < track.samplesPerChunk[chunk]; i++) {
14494
+ offsets.push(offset);
14495
+ offset += track.sizes[offsets.length - 1];
14496
+ }
14497
+ });
14498
+ const bytes = readFileSync10(input.audioPath);
14499
+ const layout = { sizeBytes: bytes.length, payloadStart, payloadEnd, partStarts: {} };
14500
+ mkdirSync11(join31(input.planDir, SLICE_DIR), { recursive: true });
14501
+ input.chunks.forEach((_, index) => {
14502
+ const cut = audioCutForChunk(grid, input.chunks, index, input.fps);
14503
+ if (!cut) return;
14504
+ const last = cut.firstPacket + cut.packetCount - 1;
14505
+ const start = offsets[cut.firstPacket];
14506
+ layout.partStarts[index] = start;
14507
+ const end = offsets[last] + track.sizes[last];
14508
+ writeFileSync7(join31(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
14509
+ });
14510
+ const frame = [bytes.subarray(0, payloadStart), bytes.subarray(payloadEnd)];
14511
+ writeFileSync7(join31(input.planDir, SLICE_FRAME), Buffer.concat(frame));
14512
+ writeFileSync7(join31(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
14513
+ }
14514
+ function chunkAudioSource(planDir, chunkIndex, scratchPath) {
14515
+ const layoutPath = join31(planDir, SLICE_LAYOUT);
14516
+ if (!existsSync19(layoutPath)) return resolvePlanAudioPath(planDir);
14517
+ const layout = JSON.parse(readFileSync10(layoutPath, "utf-8"));
14518
+ const frame = readFileSync10(join31(planDir, SLICE_FRAME));
14519
+ const fd = openSync5(scratchPath, "w");
14520
+ try {
14521
+ writeSync(fd, frame, 0, layout.payloadStart, 0);
14522
+ writeSync(
14523
+ fd,
14524
+ frame,
14525
+ layout.payloadStart,
14526
+ frame.length - layout.payloadStart,
14527
+ layout.payloadEnd
14528
+ );
14529
+ const start = layout.partStarts[chunkIndex];
14530
+ if (start !== void 0) {
14531
+ const part = readFileSync10(join31(planDir, chunkAudioSlicePath(chunkIndex)));
14532
+ writeSync(fd, part, 0, part.length, start);
14533
+ }
14534
+ ftruncateSync(fd, layout.sizeBytes);
14535
+ } finally {
14536
+ closeSync6(fd);
14537
+ }
14538
+ return scratchPath;
14539
+ }
14540
+ function chunkMuxArgs(input, cut) {
14541
+ if (!cut) return ["-i", input.videoPath, "-map", "0:v:0"];
14542
+ const seek = cut.seekSeconds === null ? [] : ["-ss", cut.seekSeconds.toFixed(6)];
14543
+ const length = cut.durationSeconds === null ? [] : ["-t", cut.durationSeconds.toFixed(6)];
14544
+ const exact = seek.length ? ["-copypriorss", "0"] : [];
14545
+ return [
14546
+ ...["-i", input.videoPath, ...seek, ...length, "-i", input.audioPath],
14547
+ ...["-map", "0:v:0", "-map", "1:a:0", ...exact]
14548
+ ];
14549
+ }
14550
+ async function muxChunkWithAudio(input) {
14551
+ const grid = input.audioPath ? await probeAacPacketGrid(input.audioPath) : null;
14552
+ const cut = grid ? audioCutForChunk(grid, input.chunks, input.chunkIndex, input.fps) : null;
14553
+ const args = [...chunkMuxArgs(input, cut), "-c", "copy"];
14554
+ appendRenderProvenanceArgs(args, input.outputPath);
14555
+ args.push("-y", input.outputPath);
14556
+ const result = await runFfmpeg(args, { signal: input.signal, timeout: input.timeoutMs });
14557
+ if (!result.success) {
14558
+ throw new Error(
14559
+ `[chunkAudio] chunk mux failed: ${describeFfmpegFailure(result, input.timeoutMs)}`
14560
+ );
14561
+ }
14562
+ const slice = input.chunks[input.chunkIndex];
14563
+ const expected = { video: slice.endFrame - slice.startFrame };
14564
+ if (cut) expected.audio = cut.packetCount;
14565
+ const probe = await runFfprobeJson([
14566
+ "-v",
14567
+ "error",
14568
+ "-count_packets",
14569
+ "-show_entries",
14570
+ "stream=codec_type,nb_read_packets",
14571
+ "-of",
14572
+ "json",
14573
+ "--",
14574
+ input.outputPath
14575
+ ]);
14576
+ for (const [type, count] of Object.entries(expected)) {
14577
+ const packets = Number(probe.streams?.find((s) => s.codec_type === type)?.nb_read_packets ?? 0);
14578
+ if (packets !== count) {
14579
+ throw new Error(`[chunkAudio] ${type} packets in chunk ${packets}, expected ${count}`);
14580
+ }
14581
+ }
14582
+ }
14583
+
14584
+ // ../producer/src/services/distributed/planSize.ts
14585
+ import { createHash as createHash6 } from "crypto";
14586
+ import { lstatSync as lstatSync2, readdirSync as readdirSync8 } from "fs";
14587
+ import { extname as extname5, join as join32, relative as relative4 } from "path";
14588
+ var MAX_TOP_COMPONENTS = 10;
14589
+ var SOURCE_MEDIA_EXTENSIONS = /* @__PURE__ */ new Set([
14590
+ ".aac",
14591
+ ".aiff",
14592
+ ".avi",
14593
+ ".flac",
14594
+ ".m4a",
14595
+ ".m4v",
14596
+ ".mkv",
14597
+ ".mov",
14598
+ ".mp3",
14599
+ ".mp4",
14600
+ ".mpeg",
14601
+ ".mpg",
14602
+ ".ogg",
14603
+ ".ogv",
14604
+ ".ts",
14605
+ ".wav",
14606
+ ".webm"
14607
+ ]);
14608
+ var PLAN_ROOT_CATEGORY = {
14609
+ compiled: "compiled",
14610
+ [PLAN_AUDIO_RELATIVE_PATH]: "audio",
14611
+ meta: "metadata",
14612
+ "plan.json": "metadata"
14613
+ };
14614
+ function hashComponent(value) {
14615
+ return createHash6("sha256").update(value).digest("hex").slice(0, 12);
14616
+ }
14617
+ function classifyFile(segments, rootKind) {
14618
+ const rootName = segments[0] ?? "";
14619
+ const extractedRootName = rootKind === "compiled" ? "__hyperframes_video_frames" : "video-frames";
14620
+ if (rootName === extractedRootName) {
14621
+ const videoKey = segments[1] ?? "unknown";
14622
+ return {
14623
+ category: "video-frames",
14624
+ componentLabel: `video-frames:${hashComponent(videoKey)}`
14625
+ };
14626
+ }
14627
+ const category = rootKind === "compiled" ? "compiled" : PLAN_ROOT_CATEGORY[rootName] ?? "other";
14628
+ return { category, componentLabel: category };
14629
+ }
14630
+ function emptyBreakdown() {
14631
+ return {
14632
+ totalBytes: 0,
14633
+ fileCount: 0,
14634
+ compiledBytes: 0,
14635
+ sourceMediaBytes: 0,
14636
+ videoFramesBytes: 0,
14637
+ videoFrameFileCount: 0,
14638
+ audioBytes: 0,
14639
+ metadataBytes: 0,
14640
+ otherBytes: 0,
14641
+ topComponents: []
14642
+ };
14643
+ }
14644
+ function addCategoryBytes(result, category, filePath, sizeBytes) {
14645
+ if (category === "compiled") {
14646
+ result.compiledBytes += sizeBytes;
14647
+ if (SOURCE_MEDIA_EXTENSIONS.has(extname5(filePath).toLowerCase())) {
14648
+ result.sourceMediaBytes += sizeBytes;
14649
+ }
14650
+ return;
14651
+ }
14652
+ if (category === "video-frames") {
14653
+ result.videoFramesBytes += sizeBytes;
14654
+ result.videoFrameFileCount += 1;
14655
+ return;
14656
+ }
14657
+ if (category === "audio") {
14658
+ result.audioBytes += sizeBytes;
14659
+ return;
14660
+ }
14661
+ if (category === "metadata") {
14662
+ result.metadataBytes += sizeBytes;
14663
+ return;
14664
+ }
14665
+ result.otherBytes += sizeBytes;
14666
+ }
14667
+ function recordFile(result, components, rootDir, rootKind, filePath) {
14668
+ let sizeBytes;
14669
+ try {
14670
+ const stat = lstatSync2(filePath);
14671
+ if (!stat.isFile()) return;
14672
+ sizeBytes = stat.size;
14673
+ } catch {
14674
+ return;
14675
+ }
14676
+ const rel = relative4(rootDir, filePath);
14677
+ const segments = rel.split(/[\\/]+/).filter(Boolean);
14678
+ const classified = classifyFile(segments, rootKind);
14679
+ result.totalBytes += sizeBytes;
14680
+ result.fileCount += 1;
14681
+ addCategoryBytes(result, classified.category, filePath, sizeBytes);
14682
+ const component = components.get(classified.componentLabel) ?? {
14683
+ label: classified.componentLabel,
14684
+ sizeBytes: 0,
14685
+ fileCount: 0
14686
+ };
14687
+ component.sizeBytes += sizeBytes;
14688
+ component.fileCount += 1;
14689
+ components.set(classified.componentLabel, component);
14690
+ }
14691
+ function walkRegularFiles(dir, visit) {
14692
+ let entries;
14693
+ try {
14694
+ entries = readdirSync8(dir, { withFileTypes: true });
14695
+ } catch {
14696
+ return;
14697
+ }
14698
+ for (const entry of entries) {
14699
+ const full = join32(dir, entry.name);
14700
+ if (entry.isDirectory()) {
14701
+ walkRegularFiles(full, visit);
14702
+ } else if (entry.isFile()) {
14703
+ visit(full);
14704
+ }
14705
+ }
14706
+ }
14707
+ function measurePlanSizeBreakdown(rootDir, rootKind = "plan") {
14708
+ const result = emptyBreakdown();
14709
+ const components = /* @__PURE__ */ new Map();
14710
+ walkRegularFiles(
14711
+ rootDir,
14712
+ (filePath) => recordFile(result, components, rootDir, rootKind, filePath)
14713
+ );
14714
+ result.topComponents = [...components.values()].sort((a, b) => b.sizeBytes - a.sizeBytes || a.label.localeCompare(b.label)).slice(0, MAX_TOP_COMPONENTS);
14715
+ return result;
14716
+ }
14717
+
14718
+ // ../producer/src/services/distributed/plan.ts
14719
+ function logPlanStage(log, stage, startedAt) {
14720
+ log.info("[plan] stage", { stage, ms: Date.now() - startedAt });
14721
+ }
14722
+ function applyDistributedAudioWarningPolicy(job, audioError, audioFailures = [], log = defaultLogger) {
14723
+ const failureOwner = audioFailures.length === 0 ? void 0 : audioFailures.some((failure) => failure.owner === "system") ? "system" : "user";
14724
+ const retryable = audioFailures.length === 0 ? void 0 : audioFailures.every((failure) => failure.retryable);
14725
+ applyRenderWarningPolicy(
14726
+ job,
14727
+ [
14728
+ {
14729
+ code: "audio_processing_failed",
14730
+ message: `Audio mix failed; output would be video-only: ${audioError}`,
14731
+ details: {
14732
+ mediaType: "audio",
14733
+ audioFailures: audioFailureDiagnostics(audioFailures),
14734
+ failureReasons: [...new Set(audioFailures.map((failure) => failure.reason))],
14735
+ failureStages: [...new Set(audioFailures.map((failure) => failure.stage))],
14736
+ failureOwner,
14737
+ retryable
14738
+ }
14739
+ }
14740
+ ],
14741
+ log
14742
+ );
14743
+ }
14744
+ var PLAN_PROJECT_DIR_SKIP_SEGMENTS = /* @__PURE__ */ new Set([
14745
+ "node_modules",
14746
+ ".git",
14747
+ ".cache",
14748
+ "output",
14749
+ "failures",
14750
+ "dist",
14751
+ ".next",
14752
+ ".turbo"
14753
+ ]);
14754
+ var DEFAULT_MAX_PARALLEL_CHUNKS = 16;
14755
+ var MIN_CHUNK_SIZE = 10;
14756
+ var MAX_PARALLEL_CHUNKS_CEILING = 256;
14757
+ var ESTIMATED_SECONDS_PER_MEGAPIXEL_FRAME = 0.11;
14758
+ var ESTIMATED_CHUNK_OVERHEAD_SECONDS = 60;
14759
+ var WORKER_TIMEOUT_MARGIN = 0.75;
14760
+ function estimateFrameSeconds(megapixels, measuredSecondsPerFrame = 0) {
14761
+ return Math.max(ESTIMATED_SECONDS_PER_MEGAPIXEL_FRAME * megapixels, measuredSecondsPerFrame);
14762
+ }
14763
+ function estimateChunkSeconds(frames, megapixels, measuredSecondsPerFrame) {
14764
+ return ESTIMATED_CHUNK_OVERHEAD_SECONDS + frames * estimateFrameSeconds(megapixels, measuredSecondsPerFrame);
14256
14765
  }
14257
14766
  function planNeedsFrameCostMeasurement(html) {
14258
14767
  return new RegExp(`\\b${HF_COLOR_GRADING_ATTR}=`, "i").test(html);
@@ -14397,13 +14906,13 @@ function assertPositiveInteger2(name, value) {
14397
14906
  }
14398
14907
  var FREEZE_OWNED_PLAN_FILES = [
14399
14908
  "plan.json",
14400
- join31("meta", "composition.json"),
14401
- join31("meta", "encoder.json"),
14402
- join31("meta", "chunks.json")
14909
+ join33("meta", "composition.json"),
14910
+ join33("meta", "encoder.json"),
14911
+ join33("meta", "chunks.json")
14403
14912
  ];
14404
14913
  function removeFreezeOwnedPlanFiles(planDir) {
14405
14914
  for (const relativePath of FREEZE_OWNED_PLAN_FILES) {
14406
- rmSync9(join31(planDir, relativePath), { force: true });
14915
+ rmSync10(join33(planDir, relativePath), { force: true });
14407
14916
  }
14408
14917
  }
14409
14918
  function buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize) {
@@ -14534,19 +15043,19 @@ function createVideoSourceShipper(input) {
14534
15043
  const known = shipped.get(srcPath);
14535
15044
  if (known) return known;
14536
15045
  const stagedRel = pathInside(input.workVideoFramesDir, srcPath);
14537
- const current = stagedRel === null ? srcPath : join31(input.planDir, "video-frames", stagedRel);
15046
+ const current = stagedRel === null ? srcPath : join33(input.planDir, "video-frames", stagedRel);
14538
15047
  let result = null;
14539
15048
  for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
14540
15049
  const rel = pathInside(root, srcPath);
14541
- if (rel !== null && (existsSync19(join31(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
15050
+ if (rel !== null && (existsSync20(join33(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
14542
15051
  result = ["compiled", ...rel.split(sep2)].join("/");
14543
15052
  break;
14544
15053
  }
14545
15054
  }
14546
15055
  if (result === null) {
14547
15056
  result = `video-sources/${shipped.size}${extname6(current)}`;
14548
- const destination = join31(input.planDir, ...result.split("/"));
14549
- mkdirSync10(join31(input.planDir, "video-sources"), { recursive: true });
15057
+ const destination = join33(input.planDir, ...result.split("/"));
15058
+ mkdirSync12(join33(input.planDir, "video-sources"), { recursive: true });
14550
15059
  try {
14551
15060
  linkSync(current, destination);
14552
15061
  } catch {
@@ -14564,7 +15073,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14564
15073
  useGpu: false,
14565
15074
  browserGpuMode: "software"
14566
15075
  });
14567
- if (!existsSync19(planDir)) mkdirSync10(planDir, { recursive: true });
15076
+ if (!existsSync20(planDir)) mkdirSync12(planDir, { recursive: true });
14568
15077
  const log = config.logger ?? defaultLogger;
14569
15078
  const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
14570
15079
  const abortSignal = config.abortSignal;
@@ -14593,15 +15102,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14593
15102
  variables: config.variables
14594
15103
  });
14595
15104
  const entryFile = config.entryFile ?? "index.html";
14596
- const htmlPath = join31(projectDir, entryFile);
14597
- if (!existsSync19(htmlPath)) {
15105
+ const htmlPath = join33(projectDir, entryFile);
15106
+ if (!existsSync20(htmlPath)) {
14598
15107
  throw new Error(`[plan] entry file not found: ${htmlPath}`);
14599
15108
  }
14600
- const workDir = join31(planDir, ".plan-work");
14601
- rmSync9(workDir, { recursive: true, force: true });
14602
- mkdirSync10(workDir, { recursive: true });
14603
- const compiledDir = join31(workDir, "compiled");
14604
- mkdirSync10(compiledDir, { recursive: true });
15109
+ const workDir = join33(planDir, ".plan-work");
15110
+ rmSync10(workDir, { recursive: true, force: true });
15111
+ mkdirSync12(workDir, { recursive: true });
15112
+ const compiledDir = join33(workDir, "compiled");
15113
+ mkdirSync12(compiledDir, { recursive: true });
14605
15114
  let stageStarted = Date.now();
14606
15115
  cpSync2(projectDir, compiledDir, {
14607
15116
  recursive: true,
@@ -14614,10 +15123,10 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14614
15123
  }
14615
15124
  });
14616
15125
  logPlanStage(log, "copy", stageStarted);
14617
- const finalCompiledDir = join31(planDir, "compiled");
15126
+ const finalCompiledDir = join33(planDir, "compiled");
14618
15127
  const needsAlpha = outputNeedsAlpha(config.format);
14619
15128
  const remoteFiles = Object.entries(options.remoteFiles ?? {});
14620
- const remoteLocalPath = (rel) => join31(projectDir, ...rel.split("/"));
15129
+ const remoteLocalPath = (rel) => join33(projectDir, ...rel.split("/"));
14621
15130
  const urlByAbsentLocalPath = new Map(
14622
15131
  remoteFiles.map(([rel, file]) => [remoteLocalPath(rel), file.url])
14623
15132
  );
@@ -14722,18 +15231,18 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14722
15231
  if (extractResult.extractionResult) {
14723
15232
  assertVideoExtractionSucceeded(extractResult.extractionResult);
14724
15233
  }
14725
- const stagedVideoFrames = join31(compiledDir, "__hyperframes_video_frames");
14726
- const videoFramesDst = join31(planDir, "video-frames");
14727
- if (existsSync19(videoFramesDst)) rmSync9(videoFramesDst, { recursive: true, force: true });
14728
- const videoSourcesDir = join31(planDir, "video-sources");
14729
- rmSync9(videoSourcesDir, { recursive: true, force: true });
14730
- if (existsSync19(stagedVideoFrames)) {
14731
- renameSync4(stagedVideoFrames, videoFramesDst);
15234
+ const stagedVideoFrames = join33(compiledDir, "__hyperframes_video_frames");
15235
+ const videoFramesDst = join33(planDir, "video-frames");
15236
+ if (existsSync20(videoFramesDst)) rmSync10(videoFramesDst, { recursive: true, force: true });
15237
+ const videoSourcesDir = join33(planDir, "video-sources");
15238
+ rmSync10(videoSourcesDir, { recursive: true, force: true });
15239
+ if (existsSync20(stagedVideoFrames)) {
15240
+ renameSync5(stagedVideoFrames, videoFramesDst);
14732
15241
  } else {
14733
- mkdirSync10(videoFramesDst, { recursive: true });
15242
+ mkdirSync12(videoFramesDst, { recursive: true });
14734
15243
  }
14735
- if (existsSync19(finalCompiledDir)) rmSync9(finalCompiledDir, { recursive: true, force: true });
14736
- renameSync4(compiledDir, finalCompiledDir);
15244
+ if (existsSync20(finalCompiledDir)) rmSync10(finalCompiledDir, { recursive: true, force: true });
15245
+ renameSync5(compiledDir, finalCompiledDir);
14737
15246
  const shipVideoSource = createVideoSourceShipper({
14738
15247
  planDir,
14739
15248
  projectDir,
@@ -14762,7 +15271,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14762
15271
  };
14763
15272
  })
14764
15273
  });
14765
- mkdirSync10(join31(planDir, "meta"), { recursive: true });
15274
+ mkdirSync12(join33(planDir, "meta"), { recursive: true });
14766
15275
  writePlanRemoteFiles(
14767
15276
  planDir,
14768
15277
  remoteFiles.map(([rel, { sha256, sizeBytes }]) => ({
@@ -14771,8 +15280,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14771
15280
  sizeBytes
14772
15281
  }))
14773
15282
  );
14774
- writeFileSync7(
14775
- join31(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
15283
+ writeFileSync8(
15284
+ join33(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
14776
15285
  JSON.stringify(planVideosJson, null, 2),
14777
15286
  "utf-8"
14778
15287
  );
@@ -14846,11 +15355,11 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14846
15355
  if (audioResult.audioError) {
14847
15356
  applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
14848
15357
  }
14849
- const planAudioPath = join31(planDir, PLAN_AUDIO_RELATIVE_PATH);
14850
- if (audioResult.hasAudio && existsSync19(audioResult.audioOutputPath)) {
14851
- renameSync4(audioResult.audioOutputPath, planAudioPath);
15358
+ const planAudioPath = join33(planDir, PLAN_AUDIO_RELATIVE_PATH);
15359
+ if (audioResult.hasAudio && existsSync20(audioResult.audioOutputPath)) {
15360
+ renameSync5(audioResult.audioOutputPath, planAudioPath);
14852
15361
  }
14853
- if (chunksCarryAudio2 && existsSync19(planAudioPath)) {
15362
+ if (chunksCarryAudio2 && existsSync20(planAudioPath)) {
14854
15363
  stageStarted = Date.now();
14855
15364
  const peak = await constrainAacTruePeak({
14856
15365
  audioPath: planAudioPath,
@@ -14860,6 +15369,16 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14860
15369
  });
14861
15370
  if (!peak.success) throw encoderFailureError("[plan] audio true-peak check failed", peak);
14862
15371
  logPlanStage(log, "audio-peak", stageStarted);
15372
+ if (options.sliceChunkAudio) {
15373
+ stageStarted = Date.now();
15374
+ await writeChunkAudioSlices({
15375
+ audioPath: planAudioPath,
15376
+ planDir,
15377
+ chunks,
15378
+ fps: { num: config.fps, den: 1 }
15379
+ });
15380
+ logPlanStage(log, "audio-slices", stageStarted);
15381
+ }
14863
15382
  }
14864
15383
  const ffmpegVersion = await readFfmpegVersion();
14865
15384
  const producerVersion = readProducerVersion();
@@ -14893,7 +15412,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
14893
15412
  format: config.format
14894
15413
  };
14895
15414
  try {
14896
- rmSync9(workDir, { recursive: true, force: true });
15415
+ rmSync10(workDir, { recursive: true, force: true });
14897
15416
  } catch (err) {
14898
15417
  log.warn("[plan] failed to remove temp work dir", {
14899
15418
  workDir,
@@ -15435,15 +15954,15 @@ function renderRequestFromDistributedConfig(input) {
15435
15954
 
15436
15955
  // ../producer/src/server.ts
15437
15956
  import {
15438
- existsSync as existsSync22,
15439
- mkdirSync as mkdirSync11,
15957
+ existsSync as existsSync23,
15958
+ mkdirSync as mkdirSync13,
15440
15959
  statSync as statSync7,
15441
- mkdtempSync as mkdtempSync4,
15442
- writeFileSync as writeFileSync8,
15443
- rmSync as rmSync10,
15444
- createReadStream as createReadStream4
15960
+ mkdtempSync as mkdtempSync5,
15961
+ writeFileSync as writeFileSync9,
15962
+ rmSync as rmSync11,
15963
+ createReadStream as createReadStream5
15445
15964
  } from "fs";
15446
- import { resolve as resolve9, dirname as dirname12, join as join34 } from "path";
15965
+ import { resolve as resolve9, dirname as dirname13, join as join36 } from "path";
15447
15966
  import { tmpdir as tmpdir3 } from "os";
15448
15967
  import { parseArgs } from "util";
15449
15968
  import crypto from "crypto";
@@ -15453,15 +15972,15 @@ import { serve as serve2 } from "@hono/node-server";
15453
15972
 
15454
15973
  // ../producer/src/services/hyperframeLint.ts
15455
15974
  import {
15456
- existsSync as existsSync20,
15457
- readFileSync as readFileSync10,
15975
+ existsSync as existsSync21,
15976
+ readFileSync as readFileSync11,
15458
15977
  statSync as statSync6,
15459
- openSync as openSync5,
15978
+ openSync as openSync6,
15460
15979
  fstatSync as fstatSync4,
15461
- closeSync as closeSync6,
15980
+ closeSync as closeSync7,
15462
15981
  constants as constants3
15463
15982
  } from "fs";
15464
- import { resolve as resolve8, join as join32, relative as relative6, isAbsolute as isAbsolute3, sep as sep3 } from "path";
15983
+ import { resolve as resolve8, join as join34, relative as relative6, isAbsolute as isAbsolute3, sep as sep3 } from "path";
15465
15984
  function isStringRecord(value) {
15466
15985
  if (!value || typeof value !== "object" || Array.isArray(value)) {
15467
15986
  return false;
@@ -15490,7 +16009,7 @@ function pickEntryFile(files, preferredEntryFile) {
15490
16009
  function readEntryHtml(filePath) {
15491
16010
  let fd;
15492
16011
  try {
15493
- fd = openSync5(filePath, constants3.O_RDONLY | constants3.O_NONBLOCK);
16012
+ fd = openSync6(filePath, constants3.O_RDONLY | constants3.O_NONBLOCK);
15494
16013
  } catch (error) {
15495
16014
  if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
15496
16015
  return null;
@@ -15499,14 +16018,14 @@ function readEntryHtml(filePath) {
15499
16018
  }
15500
16019
  try {
15501
16020
  if (!fstatSync4(fd).isFile()) return null;
15502
- return readFileSync10(fd, "utf-8");
16021
+ return readFileSync11(fd, "utf-8");
15503
16022
  } finally {
15504
- closeSync6(fd);
16023
+ closeSync7(fd);
15505
16024
  }
15506
16025
  }
15507
16026
  function readProjectEntryFile(projectDir, preferredEntryFile) {
15508
16027
  const absProjectDir = resolve8(projectDir);
15509
- if (!existsSync20(absProjectDir) || !statSync6(absProjectDir).isDirectory()) {
16028
+ if (!existsSync21(absProjectDir) || !statSync6(absProjectDir).isDirectory()) {
15510
16029
  return { error: `Project directory not found: ${absProjectDir}` };
15511
16030
  }
15512
16031
  const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
@@ -15528,7 +16047,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
15528
16047
  }
15529
16048
  }
15530
16049
  return {
15531
- error: `No HTML entry file found in project directory: ${join32(absProjectDir, preferredEntryFile || "index.html")}`
16050
+ error: `No HTML entry file found in project directory: ${join34(absProjectDir, preferredEntryFile || "index.html")}`
15532
16051
  };
15533
16052
  }
15534
16053
  function prepareHyperframeLintBody(body) {
@@ -15574,8 +16093,8 @@ async function runHyperframeLint(prepared) {
15574
16093
  // ../producer/src/services/healthWorker.ts
15575
16094
  import { Worker as Worker2 } from "worker_threads";
15576
16095
  import { fileURLToPath as fileURLToPath6 } from "url";
15577
- import { dirname as dirname11, join as join33 } from "path";
15578
- import { existsSync as existsSync21 } from "fs";
16096
+ import { dirname as dirname12, join as join35 } from "path";
16097
+ import { existsSync as existsSync22 } from "fs";
15579
16098
  var DEFAULT_HEALTH_PORT = 9848;
15580
16099
  async function startHealthWorker(options = {}) {
15581
16100
  const log = options.logger ?? defaultLogger2();
@@ -15649,10 +16168,10 @@ function defaultLogger2() {
15649
16168
  };
15650
16169
  }
15651
16170
  function resolveWorkerEntry2() {
15652
- const here = dirname11(fileURLToPath6(import.meta.url));
15653
- const candidates = [join33(here, "healthWorkerThread.js"), join33(here, "healthWorkerThread.ts")];
16171
+ const here = dirname12(fileURLToPath6(import.meta.url));
16172
+ const candidates = [join35(here, "healthWorkerThread.js"), join35(here, "healthWorkerThread.ts")];
15654
16173
  for (const candidate of candidates) {
15655
- if (existsSync21(candidate)) return candidate;
16174
+ if (existsSync22(candidate)) return candidate;
15656
16175
  }
15657
16176
  return void 0;
15658
16177
  }
@@ -15790,8 +16309,8 @@ function buildRenderJobConfig(input, outputPath, log) {
15790
16309
  function resolvePreparedRenderOutput(prepared, rendersDir, log) {
15791
16310
  const { input, cleanupProjectDir } = prepared;
15792
16311
  const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
15793
- const outputDir = dirname12(absoluteOutputPath);
15794
- if (!existsSync22(outputDir)) mkdirSync11(outputDir, { recursive: true });
16312
+ const outputDir = dirname13(absoluteOutputPath);
16313
+ if (!existsSync23(outputDir)) mkdirSync13(outputDir, { recursive: true });
15795
16314
  return { input, cleanupProjectDir, absoluteOutputPath };
15796
16315
  }
15797
16316
  function validateRenderOverrides(body) {
@@ -15831,11 +16350,11 @@ function prepareProjectDirectory(projectDir, options) {
15831
16350
  const candidate = nonEmptyString(projectDir);
15832
16351
  if (!candidate) return null;
15833
16352
  const absProjectDir = resolve9(candidate);
15834
- if (!existsSync22(absProjectDir) || !statSync7(absProjectDir).isDirectory()) {
16353
+ if (!existsSync23(absProjectDir) || !statSync7(absProjectDir).isDirectory()) {
15835
16354
  return { error: `Project directory not found: ${absProjectDir}` };
15836
16355
  }
15837
16356
  const entry = options.entryFile || "index.html";
15838
- if (!existsSync22(resolve9(absProjectDir, entry))) {
16357
+ if (!existsSync23(resolve9(absProjectDir, entry))) {
15839
16358
  return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
15840
16359
  }
15841
16360
  return { prepared: { input: { projectDir: absProjectDir, ...options } } };
@@ -15860,8 +16379,8 @@ async function resolveInlineRenderHtml(body) {
15860
16379
  }
15861
16380
  function materializeInlineProject(html, options) {
15862
16381
  const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
15863
- const tempProjectDir = mkdtempSync4(join34(tempRoot, "producer-project-"));
15864
- writeFileSync8(join34(tempProjectDir, "index.html"), html, "utf-8");
16382
+ const tempProjectDir = mkdtempSync5(join36(tempRoot, "producer-project-"));
16383
+ writeFileSync9(join36(tempProjectDir, "index.html"), html, "utf-8");
15865
16384
  return {
15866
16385
  prepared: {
15867
16386
  input: { projectDir: tempProjectDir, ...options },
@@ -15918,7 +16437,7 @@ function createArtifactStore(ttlMs) {
15918
16437
  function cleanupTempDir(dir, log) {
15919
16438
  if (!dir) return;
15920
16439
  try {
15921
- rmSync10(dir, { recursive: true, force: true });
16440
+ rmSync11(dir, { recursive: true, force: true });
15922
16441
  } catch (error) {
15923
16442
  log.warn("Failed to cleanup temp project dir", {
15924
16443
  cleanupProjectDir: dir,
@@ -15927,7 +16446,7 @@ function cleanupTempDir(dir, log) {
15927
16446
  }
15928
16447
  }
15929
16448
  function outputFileSize(path) {
15930
- return existsSync22(path) ? statSync7(path).size : 0;
16449
+ return existsSync23(path) ? statSync7(path).size : 0;
15931
16450
  }
15932
16451
  function createBlockingProgressReporter(log, requestId) {
15933
16452
  let lastLoggedPct = -10;
@@ -16223,12 +16742,12 @@ function createRenderHandlers(options = {}) {
16223
16742
  if (!artifact) {
16224
16743
  return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
16225
16744
  }
16226
- if (!existsSync22(artifact.path)) {
16745
+ if (!existsSync23(artifact.path)) {
16227
16746
  store.delete(token);
16228
16747
  return c.json({ success: false, error: "Output artifact file missing" }, 404);
16229
16748
  }
16230
16749
  const stats = statSync7(artifact.path);
16231
- return new Response(createReadStream4(artifact.path), {
16750
+ return new Response(createReadStream5(artifact.path), {
16232
16751
  headers: {
16233
16752
  "content-type": "video/mp4",
16234
16753
  "content-length": String(stats.size),
@@ -16308,33 +16827,33 @@ if (isPublicServerEntry) {
16308
16827
  // ../producer/src/services/distributed/planV2.ts
16309
16828
  import {
16310
16829
  copyFileSync as copyFileSync6,
16311
- existsSync as existsSync24,
16830
+ existsSync as existsSync25,
16312
16831
  lstatSync as lstatSync3,
16313
- mkdirSync as mkdirSync13,
16314
- mkdtempSync as mkdtempSync6,
16315
- readFileSync as readFileSync11,
16316
- readdirSync as readdirSync9,
16317
- renameSync as renameSync6,
16318
- rmSync as rmSync12,
16832
+ mkdirSync as mkdirSync15,
16833
+ mkdtempSync as mkdtempSync7,
16834
+ readFileSync as readFileSync12,
16835
+ readdirSync as readdirSync9,
16836
+ renameSync as renameSync7,
16837
+ rmSync as rmSync13,
16319
16838
  statSync as statSync9,
16320
- writeFileSync as writeFileSync10
16839
+ writeFileSync as writeFileSync11
16321
16840
  } from "fs";
16322
16841
  import { tmpdir as tmpdir4 } from "os";
16323
- import { dirname as dirname14, isAbsolute as isAbsolute4, join as join37, relative as relative7, resolve as resolve10, sep as sep4 } from "path";
16842
+ import { dirname as dirname15, isAbsolute as isAbsolute4, join as join39, relative as relative7, resolve as resolve10, sep as sep4 } from "path";
16324
16843
 
16325
16844
  // ../producer/src/services/distributed/planV2Publisher.ts
16326
16845
  import {
16327
16846
  copyFileSync as copyFileSync5,
16328
- existsSync as existsSync23,
16847
+ existsSync as existsSync24,
16329
16848
  linkSync as linkSync2,
16330
- mkdirSync as mkdirSync12,
16331
- mkdtempSync as mkdtempSync5,
16332
- renameSync as renameSync5,
16333
- rmSync as rmSync11,
16849
+ mkdirSync as mkdirSync14,
16850
+ mkdtempSync as mkdtempSync6,
16851
+ renameSync as renameSync6,
16852
+ rmSync as rmSync12,
16334
16853
  statSync as statSync8,
16335
- writeFileSync as writeFileSync9
16854
+ writeFileSync as writeFileSync10
16336
16855
  } from "fs";
16337
- import { dirname as dirname13, join as join36 } from "path";
16856
+ import { dirname as dirname14, join as join38 } from "path";
16338
16857
 
16339
16858
  // ../producer/src/services/distributed/planV2Errors.ts
16340
16859
  var PLAN_V2_INTEGRITY_UNRECOVERABLE = "PLAN_V2_INTEGRITY_UNRECOVERABLE";
@@ -16349,7 +16868,7 @@ var PlanV2IntegrityError = class extends Error {
16349
16868
  };
16350
16869
 
16351
16870
  // ../producer/src/services/distributed/planV2Layout.ts
16352
- import { join as join35 } from "path";
16871
+ import { join as join37 } from "path";
16353
16872
  var SHA256_PATTERN = /^[0-9a-f]{64}$/;
16354
16873
  function assertPlanV2Sha256(value, field) {
16355
16874
  if (typeof value !== "string" || !SHA256_PATTERN.test(value)) {
@@ -16359,7 +16878,7 @@ function assertPlanV2Sha256(value, field) {
16359
16878
  }
16360
16879
  function planV2BlobPath(root, sha256) {
16361
16880
  const digest = assertPlanV2Sha256(sha256, "artifact sha256");
16362
- return join35(root, "artifacts", "sha256", digest.slice(0, 2), digest);
16881
+ return join37(root, "artifacts", "sha256", digest.slice(0, 2), digest);
16363
16882
  }
16364
16883
 
16365
16884
  // ../producer/src/services/distributed/planV2Publisher.ts
@@ -16394,13 +16913,13 @@ var LocalPlanV2ArtifactPublisher = class {
16394
16913
  #linkFile;
16395
16914
  #committed = false;
16396
16915
  constructor(destinationDir, options = {}) {
16397
- if (existsSync23(destinationDir)) {
16916
+ if (existsSync24(destinationDir)) {
16398
16917
  throw new PlanV2IntegrityError(`output directory already exists: ${destinationDir}`);
16399
16918
  }
16400
16919
  this.destinationDir = destinationDir;
16401
16920
  this.#linkFile = options.linkFile ?? linkSync2;
16402
- mkdirSync12(dirname13(destinationDir), { recursive: true });
16403
- this.temporaryDir = mkdtempSync5(join36(dirname13(destinationDir), ".plan-v2-publish-"));
16921
+ mkdirSync14(dirname14(destinationDir), { recursive: true });
16922
+ this.temporaryDir = mkdtempSync6(join38(dirname14(destinationDir), ".plan-v2-publish-"));
16404
16923
  }
16405
16924
  async putBlob(blob) {
16406
16925
  const digest = assertPlanV2Sha256(blob.sha256, "published blob sha256");
@@ -16411,10 +16930,10 @@ var LocalPlanV2ArtifactPublisher = class {
16411
16930
  );
16412
16931
  }
16413
16932
  const destinationPath = planV2BlobPath(this.temporaryDir, digest);
16414
- if (existsSync23(destinationPath)) return;
16415
- mkdirSync12(dirname13(destinationPath), { recursive: true });
16416
- const stagingDir = mkdtempSync5(join36(dirname13(destinationPath), ".plan-v2-blob-"));
16417
- const temporaryPath = join36(stagingDir, "blob");
16933
+ if (existsSync24(destinationPath)) return;
16934
+ mkdirSync14(dirname14(destinationPath), { recursive: true });
16935
+ const stagingDir = mkdtempSync6(join38(dirname14(destinationPath), ".plan-v2-blob-"));
16936
+ const temporaryPath = join38(stagingDir, "blob");
16418
16937
  try {
16419
16938
  try {
16420
16939
  this.#linkFile(blob.sourcePath, temporaryPath);
@@ -16422,26 +16941,26 @@ var LocalPlanV2ArtifactPublisher = class {
16422
16941
  if (!canFallbackToCopy(error)) throw error;
16423
16942
  copyFileSync5(blob.sourcePath, temporaryPath);
16424
16943
  }
16425
- renameSync5(temporaryPath, destinationPath);
16944
+ renameSync6(temporaryPath, destinationPath);
16426
16945
  } finally {
16427
- rmSync11(stagingDir, { recursive: true, force: true });
16946
+ rmSync12(stagingDir, { recursive: true, force: true });
16428
16947
  }
16429
16948
  }
16430
16949
  async commitManifest(manifestBytes) {
16431
16950
  for (const digest of new Set(manifestDigests(manifestBytes))) {
16432
- if (!existsSync23(planV2BlobPath(this.temporaryDir, digest))) {
16951
+ if (!existsSync24(planV2BlobPath(this.temporaryDir, digest))) {
16433
16952
  throw new PlanV2IntegrityError(
16434
16953
  `cannot commit manifest before referenced blob is durable: ${digest}`
16435
16954
  );
16436
16955
  }
16437
16956
  }
16438
- writeFileSync9(join36(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
16439
- renameSync5(this.temporaryDir, this.destinationDir);
16957
+ writeFileSync10(join38(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
16958
+ renameSync6(this.temporaryDir, this.destinationDir);
16440
16959
  this.#committed = true;
16441
16960
  }
16442
16961
  async abort() {
16443
16962
  if (!this.#committed) {
16444
- rmSync11(this.temporaryDir, { recursive: true, force: true });
16963
+ rmSync12(this.temporaryDir, { recursive: true, force: true });
16445
16964
  }
16446
16965
  }
16447
16966
  };
@@ -16460,7 +16979,7 @@ function isNonNegativeIntegerArray(value) {
16460
16979
  return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0);
16461
16980
  }
16462
16981
  function readJsonFile(path, label) {
16463
- const contents = readFileSync11(path, "utf-8");
16982
+ const contents = readFileSync12(path, "utf-8");
16464
16983
  try {
16465
16984
  const parsed = JSON.parse(contents);
16466
16985
  return parsed;
@@ -16480,7 +16999,7 @@ function listFiles(root) {
16480
16999
  const rootResolved = resolve10(root);
16481
17000
  function walk(dir) {
16482
17001
  for (const entry of readdirSync9(dir, { withFileTypes: true })) {
16483
- const absolutePath = join37(dir, entry.name);
17002
+ const absolutePath = join39(dir, entry.name);
16484
17003
  if (entry.isDirectory()) {
16485
17004
  walk(absolutePath);
16486
17005
  } else if (entry.isFile()) {
@@ -16507,13 +17026,13 @@ function assertValidExtractionCacheCompleteSentinel(path) {
16507
17026
  }
16508
17027
  }
16509
17028
  function validateExtractionCacheCompleteSentinels(executionPlanDir) {
16510
- const videoRoot = join37(executionPlanDir, "video-frames");
16511
- if (!existsSync24(videoRoot)) return;
17029
+ const videoRoot = join39(executionPlanDir, "video-frames");
17030
+ if (!existsSync25(videoRoot)) return;
16512
17031
  for (const videoEntry of readdirSync9(videoRoot, { withFileTypes: true })) {
16513
17032
  if (!videoEntry.isDirectory()) continue;
16514
- const videoDir = join37(videoRoot, videoEntry.name);
17033
+ const videoDir = join39(videoRoot, videoEntry.name);
16515
17034
  if (readdirSync9(videoDir).includes(EXTRACTION_CACHE_COMPLETE_SENTINEL)) {
16516
- const sentinelPath = join37(videoDir, EXTRACTION_CACHE_COMPLETE_SENTINEL);
17035
+ const sentinelPath = join39(videoDir, EXTRACTION_CACHE_COMPLETE_SENTINEL);
16517
17036
  assertValidExtractionCacheCompleteSentinel(sentinelPath);
16518
17037
  }
16519
17038
  }
@@ -16530,10 +17049,13 @@ function resolveExtractedVideoOutputDir(planDir, videoId) {
16530
17049
  }
16531
17050
  return outputDir;
16532
17051
  }
16533
- function artifactTargets(path, videoDependencies, chunksCarryAudio2) {
17052
+ function artifactTargets(path, videoDependencies, chunkAudio) {
16534
17053
  if (isPlanAudioArtifactPath(path)) {
16535
- return chunksCarryAudio2 ? { chunks: "all", assembler: false } : { chunks: [], assembler: true };
17054
+ if (chunkAudio === "none") return { chunks: [], assembler: true };
17055
+ return { chunks: chunkAudio === "whole" ? "all" : [], assembler: false };
16536
17056
  }
17057
+ const slice = chunkAudioSliceTarget(path);
17058
+ if (slice !== null) return { chunks: slice === "all" ? "all" : [slice], assembler: false };
16537
17059
  if (path === "plan.json" || path === "meta/chunks.json" || path === "meta/encoder.json") {
16538
17060
  return { chunks: "all", assembler: true };
16539
17061
  }
@@ -16557,7 +17079,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
16557
17079
  if (video.deferredRange) {
16558
17080
  const framePaths2 = /* @__PURE__ */ new Map();
16559
17081
  for (let index = 0; index < video.totalFrames; index++) {
16560
- framePaths2.set(index, join37(outputDir, String(index)));
17082
+ framePaths2.set(index, join39(outputDir, String(index)));
16561
17083
  }
16562
17084
  return { ...video, outputDir, framePaths: framePaths2, ownedByLookup: false };
16563
17085
  }
@@ -16565,7 +17087,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
16565
17087
  const framePaths = /* @__PURE__ */ new Map();
16566
17088
  for (const frameName of frameNames) {
16567
17089
  if (frameName === EXTRACTION_CACHE_COMPLETE_SENTINEL) {
16568
- assertValidExtractionCacheCompleteSentinel(join37(outputDir, frameName));
17090
+ assertValidExtractionCacheCompleteSentinel(join39(outputDir, frameName));
16569
17091
  continue;
16570
17092
  }
16571
17093
  const match = /(\d+)(?=\.[^.]+$)/.exec(frameName);
@@ -16584,7 +17106,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
16584
17106
  `duplicate extracted frame index ${frameIndex} in ${outputDir}`
16585
17107
  );
16586
17108
  }
16587
- framePaths.set(frameIndex, join37(outputDir, frameName));
17109
+ framePaths.set(frameIndex, join39(outputDir, frameName));
16588
17110
  }
16589
17111
  return {
16590
17112
  ...video,
@@ -16605,11 +17127,11 @@ function parsePlanVideosJson2(value) {
16605
17127
  }
16606
17128
  }
16607
17129
  function materializeExtractedVideoDirectories(planDir) {
16608
- const videosPath = join37(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
16609
- if (!existsSync24(videosPath)) return;
17130
+ const videosPath = join39(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
17131
+ if (!existsSync25(videosPath)) return;
16610
17132
  const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
16611
17133
  for (const video of videos.extracted) {
16612
- mkdirSync13(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
17134
+ mkdirSync15(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
16613
17135
  }
16614
17136
  }
16615
17137
  function parseChunkSlices(value) {
@@ -16634,13 +17156,13 @@ function parseChunkSlices(value) {
16634
17156
  });
16635
17157
  }
16636
17158
  function buildVideoChunkDependencies(executionPlanDir, dimensions) {
16637
- const videoRoot = join37(executionPlanDir, "video-frames");
16638
- const hasExtractedFrames = existsSync24(videoRoot) && listFiles(videoRoot).length > 0;
16639
- const videosPath = join37(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
16640
- if (!existsSync24(videosPath)) {
17159
+ const videoRoot = join39(executionPlanDir, "video-frames");
17160
+ const hasExtractedFrames = existsSync25(videoRoot) && listFiles(videoRoot).length > 0;
17161
+ const videosPath = join39(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
17162
+ if (!existsSync25(videosPath)) {
16641
17163
  return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
16642
17164
  }
16643
- const chunksPath = join37(executionPlanDir, "meta", "chunks.json");
17165
+ const chunksPath = join39(executionPlanDir, "meta", "chunks.json");
16644
17166
  const videos = readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH);
16645
17167
  const chunks = readJsonFile(chunksPath, "meta/chunks.json");
16646
17168
  const parsedVideos = parsePlanVideosJson2(videos);
@@ -16680,871 +17202,526 @@ function collectChunkVideoDependencies(input) {
16680
17202
  const path = input.deferredSources.get(videoId) ?? relative7(resolve10(input.executionPlanDir), payload.framePath).split(sep4).join("/");
16681
17203
  assertSafeRelativePath(path);
16682
17204
  const indexes = mutable.get(path) ?? /* @__PURE__ */ new Set();
16683
- indexes.add(chunk.index);
16684
- mutable.set(path, indexes);
16685
- }
16686
- }
16687
- }
16688
- return new Map([...mutable].map(([path, indexes]) => [path, [...indexes].sort((a, b) => a - b)]));
16689
- }
16690
- function manifestPayload(manifest) {
16691
- return manifest;
16692
- }
16693
- function computeManifestHash(manifest) {
16694
- return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
16695
- }
16696
- function writeBlob(sourcePath, destinationPath) {
16697
- if (existsSync24(destinationPath)) return;
16698
- mkdirSync13(dirname14(destinationPath), { recursive: true });
16699
- const temporaryDir = mkdtempSync6(join37(dirname14(destinationPath), ".plan-v2-blob-"));
16700
- const temporaryPath = join37(temporaryDir, "blob");
16701
- try {
16702
- copyFileSync6(sourcePath, temporaryPath);
16703
- renameSync6(temporaryPath, destinationPath);
16704
- } finally {
16705
- rmSync12(temporaryDir, { recursive: true, force: true });
16706
- }
16707
- }
16708
- function publishBlobFor(absolutePath, digests) {
16709
- return {
16710
- sourcePath: absolutePath,
16711
- sha256: planFileSha256(absolutePath, digests),
16712
- sizeBytes: statSync9(absolutePath).size
16713
- };
16714
- }
16715
- async function putBlobs(publisher, blobs) {
16716
- await putTasks(blobs.map((blob) => () => publisher.putBlob(blob)));
16717
- }
16718
- async function putTasks(tasks) {
16719
- const uploadSlots = 32;
16720
- let next = 0;
16721
- let failure;
16722
- const upload = async () => {
16723
- while (!failure && next < tasks.length) {
16724
- const task = tasks[next++];
16725
- try {
16726
- await task();
16727
- } catch (reason) {
16728
- failure ??= { reason };
16729
- }
16730
- }
16731
- };
16732
- await Promise.all(Array.from({ length: uploadSlots }, upload));
16733
- if (failure) throw failure.reason;
16734
- }
16735
- function buildPlanV2Publication(executionPlanDir, digests) {
16736
- const executionPlanPath = join37(executionPlanDir, "plan.json");
16737
- if (!existsSync24(executionPlanPath)) {
16738
- throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
16739
- }
16740
- const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
16741
- if (!isRecord3(executionPlanValue)) {
16742
- throw new PlanV2IntegrityError("execution plan.json must be an object");
16743
- }
16744
- const executionPlan = executionPlanValue;
16745
- readPlanProtocolV1(executionPlan);
16746
- const executionPlanHash = executionPlan.planHash;
16747
- if (!isSha256(executionPlanHash)) {
16748
- throw new PlanV2IntegrityError("execution plan.json.planHash must be a sha256 digest");
16749
- }
16750
- const recomputedExecutionPlanHash = recomputePlanHashFromPlanDir(executionPlanDir, digests);
16751
- if (recomputedExecutionPlanHash !== executionPlanHash) {
16752
- throw new PlanV2IntegrityError(
16753
- `execution plan content fingerprint does not match plan.json.planHash: expected ${executionPlanHash}, recomputed ${recomputedExecutionPlanHash}`
16754
- );
16755
- }
16756
- const artifacts = [];
16757
- const blobs = /* @__PURE__ */ new Map();
16758
- const dimensions = executionPlan.dimensions;
16759
- if (!isRecord3(dimensions)) {
16760
- throw new PlanV2IntegrityError("execution plan.json.dimensions must be an object");
16761
- }
16762
- validateExtractionCacheCompleteSentinels(executionPlanDir);
16763
- const videoDependencyPlan = buildVideoChunkDependencies(executionPlanDir, dimensions);
16764
- const encoder = readJsonFile(join37(executionPlanDir, "meta", "encoder.json"), "meta/encoder.json");
16765
- const chunksCarryAudio2 = isRecord3(encoder) && encoder.chunksCarryAudio === true;
16766
- for (const file of listFiles(executionPlanDir)) {
16767
- if (isExtractionCacheCompleteSentinelPath(file.path)) continue;
16768
- const targets = artifactTargets(file.path, videoDependencyPlan.dependencies, chunksCarryAudio2);
16769
- if ((file.path.startsWith("video-frames/") || file.path.startsWith("video-sources/")) && targets.chunks !== "all" && targets.chunks.length === 0 && !targets.assembler) {
16770
- continue;
16771
- }
16772
- const blob = publishBlobFor(file.absolutePath, digests);
16773
- if (!blobs.has(blob.sha256)) blobs.set(blob.sha256, blob);
16774
- artifacts.push({
16775
- path: file.path,
16776
- sha256: blob.sha256,
16777
- sizeBytes: blob.sizeBytes,
16778
- ...targets
16779
- });
16780
- }
16781
- const storedBlobs = /* @__PURE__ */ new Map();
16782
- for (const remote of readPlanRemoteFiles(executionPlanDir)) {
16783
- const { sha256, sizeBytes } = remote;
16784
- if (!blobs.has(sha256)) storedBlobs.set(sha256, { sha256, sizeBytes });
16785
- artifacts.push({
16786
- ...remote,
16787
- ...artifactTargets(remote.path, videoDependencyPlan.dependencies, chunksCarryAudio2)
16788
- });
16789
- }
16790
- const base = {
16791
- protocol: PLAN_PROTOCOL_V2,
16792
- // Retain the established manifest key byte-for-byte. It now acts as the
16793
- // wire alias for the neutral local execution-plan hash.
16794
- sourcePlanV1Hash: executionPlanHash,
16795
- chunkCount: readPositiveInteger2(executionPlan.chunkCount, "chunkCount"),
16796
- totalFrames: readPositiveInteger2(executionPlan.totalFrames, "totalFrames"),
16797
- fps: readExecutionPlanFps(dimensions),
16798
- width: readPositiveInteger2(dimensions.width, "dimensions.width"),
16799
- height: readPositiveInteger2(dimensions.height, "dimensions.height"),
16800
- format: readDistributedFormat(dimensions.format),
16801
- ffmpegVersion: readString2(executionPlan.ffmpegVersion, "ffmpegVersion"),
16802
- producerVersion: readString2(executionPlan.producerVersion, "producerVersion"),
16803
- limitations: { videoDependencyMode: videoDependencyPlan.mode },
16804
- artifacts
16805
- };
16806
- return {
16807
- manifest: {
16808
- ...base,
16809
- planHash: computeManifestHash(base)
16810
- },
16811
- blobs: [...blobs.values()],
16812
- storedBlobs: [...storedBlobs.values()]
16813
- };
16814
- }
16815
- function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
16816
- if (existsSync24(planV2Dir)) {
16817
- throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
16818
- }
16819
- const publication = buildPlanV2Publication(executionPlanDir);
16820
- if (publication.storedBlobs.length > 0) {
16821
- throw new PlanV2IntegrityError("a local plan v2 directory cannot reference remote files");
16822
- }
16823
- mkdirSync13(dirname14(planV2Dir), { recursive: true });
16824
- const tempDir = mkdtempSync6(join37(dirname14(planV2Dir), ".plan-v2-build-"));
16825
- try {
16826
- for (const blob of publication.blobs) {
16827
- writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
16828
- }
16829
- writeFileSync10(
16830
- join37(tempDir, "plan.json"),
16831
- canonicalJsonStringify(publication.manifest),
16832
- "utf-8"
16833
- );
16834
- renameSync6(tempDir, planV2Dir);
16835
- return resultFromManifest(planV2Dir, publication.manifest);
16836
- } catch (error) {
16837
- rmSync12(tempDir, { recursive: true, force: true });
16838
- throw error;
16839
- }
16840
- }
16841
- function createPlanV2FromV1(executionPlanDir, planV2Dir) {
16842
- return createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir);
16843
- }
16844
- async function publishPlanV2FromExecutionPlan(executionPlanDir, publisher) {
16845
- return publishExecutionPlan(executionPlanDir, publisher, {});
16846
- }
16847
- async function publishExecutionPlan(executionPlanDir, publisher, { digests, log = defaultLogger }) {
16848
- try {
16849
- let stageStarted = Date.now();
16850
- const publication = buildPlanV2Publication(executionPlanDir, digests);
16851
- logPlanStage(log, "publication", stageStarted);
16852
- if (publication.storedBlobs.length > 0 && !publisher.referenceStoredBlob) {
16853
- throw new PlanV2IntegrityError("this publisher cannot reference remote files");
16854
- }
16855
- stageStarted = Date.now();
16856
- await putTasks([
16857
- ...publication.blobs.map((blob) => () => publisher.putBlob(blob)),
16858
- ...publication.storedBlobs.map((blob) => () => publisher.referenceStoredBlob(blob))
16859
- ]);
16860
- logPlanStage(log, "upload", stageStarted);
16861
- stageStarted = Date.now();
16862
- await publisher.commitManifest(canonicalJsonStringify(publication.manifest));
16863
- logPlanStage(log, "manifest", stageStarted);
16864
- return publication.manifest;
16865
- } catch (error) {
16866
- try {
16867
- await publisher.abort();
16868
- } catch {
16869
- }
16870
- throw error;
16871
- }
16872
- }
16873
- async function publishPlanV2FromV1(executionPlanDir, publisher) {
16874
- return publishPlanV2FromExecutionPlan(executionPlanDir, publisher);
16875
- }
16876
- async function planV2WithPublisher(projectDir, config, publisher, options = {}) {
16877
- const stagingRoot = mkdtempSync6(join37(options.stagingParentDir ?? tmpdir4(), ".execution-plan-"));
16878
- const digests = /* @__PURE__ */ new Map();
16879
- try {
16880
- await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
16881
- executionPlanSizeLimitBytes: Number.MAX_SAFE_INTEGER,
16882
- deferVideoExtraction: true,
16883
- remoteFiles: options.remoteFiles,
16884
- digests,
16885
- // artifactTargets ships every compiled/ file to all chunks, so each is in the manifest.
16886
- whileAudioMixes: () => putBlobs(
16887
- publisher,
16888
- listFiles(join37(stagingRoot, "compiled")).map(
16889
- (file) => publishBlobFor(file.absolutePath, digests)
16890
- )
16891
- ),
16892
- chunksCarryAudio: options.chunksCarryAudio
16893
- });
16894
- return await publishExecutionPlan(stagingRoot, publisher, { digests, log: config.logger });
16895
- } catch (error) {
16896
- try {
16897
- await publisher.abort();
16898
- } catch {
16899
- }
16900
- throw error;
16901
- } finally {
16902
- rmSync12(stagingRoot, { recursive: true, force: true });
16903
- }
16904
- }
16905
- async function planV2(projectDir, config, planV2Dir) {
16906
- if (existsSync24(planV2Dir)) {
16907
- throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
16908
- }
16909
- const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
16910
- const manifest = await planV2WithPublisher(projectDir, config, publisher, {
16911
- stagingParentDir: dirname14(planV2Dir)
16912
- });
16913
- return resultFromManifest(planV2Dir, manifest);
16914
- }
16915
- function resultFromManifest(planV2Dir, manifest) {
16916
- return {
16917
- planDir: planV2Dir,
16918
- manifestPath: join37(planV2Dir, "plan.json"),
16919
- planProtocol: PLAN_PROTOCOL_V2,
16920
- planHash: manifest.planHash,
16921
- sourcePlanV1Hash: getPlanV2ExecutionPlanHash(manifest),
16922
- chunkCount: manifest.chunkCount,
16923
- totalFrames: manifest.totalFrames,
16924
- fps: manifest.fps,
16925
- width: manifest.width,
16926
- height: manifest.height,
16927
- format: manifest.format,
16928
- ffmpegVersion: manifest.ffmpegVersion,
16929
- producerVersion: manifest.producerVersion,
16930
- limitations: manifest.limitations
16931
- };
16932
- }
16933
- function getPlanV2ExecutionPlanHash(plan2) {
16934
- return plan2.sourcePlanV1Hash;
16935
- }
16936
- function readString2(value, field) {
16937
- if (typeof value !== "string" || value.length === 0) {
16938
- throw new PlanV2IntegrityError(`${field} must be a non-empty string`);
16939
- }
16940
- return value;
16941
- }
16942
- function readPositiveInteger2(value, field) {
16943
- if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
16944
- throw new PlanV2IntegrityError(`${field} must be a positive integer`);
16945
- }
16946
- return value;
16947
- }
16948
- function readNonNegativeInteger2(value, field) {
16949
- if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
16950
- throw new PlanV2IntegrityError(`${field} must be a non-negative integer`);
17205
+ indexes.add(chunk.index);
17206
+ mutable.set(path, indexes);
17207
+ }
17208
+ }
16951
17209
  }
16952
- return value;
17210
+ return new Map([...mutable].map(([path, indexes]) => [path, [...indexes].sort((a, b) => a - b)]));
16953
17211
  }
16954
- function readSupportedFps(value) {
16955
- if (!isDistributedFps(value)) {
16956
- throw new PlanV2IntegrityError(
16957
- `dimensions.fpsNum must be one of ${DISTRIBUTED_FPS.join(", ")}`
16958
- );
16959
- }
16960
- return value;
17212
+ function manifestPayload(manifest) {
17213
+ return manifest;
16961
17214
  }
16962
- function readExecutionPlanFps(dimensions) {
16963
- const fpsDen = readPositiveInteger2(dimensions.fpsDen, "dimensions.fpsDen");
16964
- if (fpsDen !== 1) {
16965
- throw new PlanV2IntegrityError("dimensions.fpsDen must be 1 for plan v2");
16966
- }
16967
- return readSupportedFps(dimensions.fpsNum);
17215
+ function computeManifestHash(manifest) {
17216
+ return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
16968
17217
  }
16969
- function readDistributedFormat(value) {
16970
- if (value !== "mp4" && value !== "mov" && value !== "webm" && value !== "png-sequence") {
16971
- throw new PlanV2IntegrityError("dimensions.format is unsupported");
17218
+ function writeBlob(sourcePath, destinationPath) {
17219
+ if (existsSync25(destinationPath)) return;
17220
+ mkdirSync15(dirname15(destinationPath), { recursive: true });
17221
+ const temporaryDir = mkdtempSync7(join39(dirname15(destinationPath), ".plan-v2-blob-"));
17222
+ const temporaryPath = join39(temporaryDir, "blob");
17223
+ try {
17224
+ copyFileSync6(sourcePath, temporaryPath);
17225
+ renameSync7(temporaryPath, destinationPath);
17226
+ } finally {
17227
+ rmSync13(temporaryDir, { recursive: true, force: true });
16972
17228
  }
16973
- return value;
16974
17229
  }
16975
- function parseArtifact(value, index) {
16976
- if (!isRecord3(value)) throw new PlanV2IntegrityError(`artifacts[${index}] must be an object`);
16977
- const path = readString2(value.path, `artifacts[${index}].path`);
16978
- assertSafeRelativePath(path);
16979
- if (!isSha256(value.sha256)) {
16980
- throw new PlanV2IntegrityError(`artifacts[${index}].sha256 must be a lowercase sha256`);
16981
- }
16982
- const sizeBytes = value.sizeBytes;
16983
- if (typeof sizeBytes !== "number" || !Number.isInteger(sizeBytes) || sizeBytes < 0) {
16984
- throw new PlanV2IntegrityError(`artifacts[${index}].sizeBytes must be a non-negative integer`);
16985
- }
16986
- let chunks;
16987
- if (value.chunks === "all") {
16988
- chunks = "all";
16989
- } else if (isNonNegativeIntegerArray(value.chunks)) {
16990
- chunks = value.chunks;
16991
- } else {
16992
- throw new PlanV2IntegrityError(`artifacts[${index}].chunks is invalid`);
16993
- }
16994
- if (typeof value.assembler !== "boolean") {
16995
- throw new PlanV2IntegrityError(`artifacts[${index}].assembler must be boolean`);
16996
- }
17230
+ function publishBlobFor(absolutePath, digests) {
16997
17231
  return {
16998
- path,
16999
- sha256: value.sha256,
17000
- sizeBytes,
17001
- chunks,
17002
- assembler: value.assembler
17003
- };
17004
- }
17005
- function parsePlanV2Manifest(value) {
17006
- if (!isRecord3(value)) throw new PlanV2IntegrityError("manifest must be an object");
17007
- if (!isRecord3(value.protocol) || value.protocol.schemaVersion !== PLAN_PROTOCOL_V2.schemaVersion || value.protocol.artifactLayout !== PLAN_PROTOCOL_V2.artifactLayout || value.protocol.hashSchema !== PLAN_PROTOCOL_V2.hashSchema) {
17008
- throw new PlanV2IntegrityError("manifest protocol is not the supported v2 descriptor");
17009
- }
17010
- if (!isSha256(value.planHash) || !isSha256(value.sourcePlanV1Hash)) {
17011
- throw new PlanV2IntegrityError("manifest hashes must be lowercase sha256 digests");
17012
- }
17013
- if (!Array.isArray(value.artifacts)) throw new PlanV2IntegrityError("artifacts must be an array");
17014
- const artifacts = value.artifacts.map(parseArtifact);
17015
- const paths = /* @__PURE__ */ new Set();
17016
- for (const artifact of artifacts) {
17017
- if (paths.has(artifact.path))
17018
- throw new PlanV2IntegrityError(`duplicate artifact path: ${artifact.path}`);
17019
- paths.add(artifact.path);
17020
- }
17021
- if (!paths.has("plan.json"))
17022
- throw new PlanV2IntegrityError("manifest must include the local execution plan.json artifact");
17023
- if (!isRecord3(value.limitations) || value.limitations.videoDependencyMode !== "exact-rendered-frames" && value.limitations.videoDependencyMode !== "full-source-pack" && value.limitations.videoDependencyMode !== "source-extract") {
17024
- throw new PlanV2IntegrityError("unsupported video dependency mode");
17025
- }
17026
- const parsed = {
17027
- protocol: PLAN_PROTOCOL_V2,
17028
- planHash: value.planHash,
17029
- sourcePlanV1Hash: value.sourcePlanV1Hash,
17030
- chunkCount: readPositiveInteger2(value.chunkCount, "chunkCount"),
17031
- totalFrames: readPositiveInteger2(value.totalFrames, "totalFrames"),
17032
- fps: readSupportedFps(value.fps),
17033
- width: readPositiveInteger2(value.width, "width"),
17034
- height: readPositiveInteger2(value.height, "height"),
17035
- format: readDistributedFormat(value.format),
17036
- ffmpegVersion: readString2(value.ffmpegVersion, "ffmpegVersion"),
17037
- producerVersion: readString2(value.producerVersion, "producerVersion"),
17038
- limitations: { videoDependencyMode: value.limitations.videoDependencyMode },
17039
- artifacts
17232
+ sourcePath: absolutePath,
17233
+ sha256: planFileSha256(absolutePath, digests),
17234
+ sizeBytes: statSync9(absolutePath).size
17040
17235
  };
17041
- const { planHash: _planHash, ...payload } = parsed;
17042
- const expectedHash = computeManifestHash(payload);
17043
- if (expectedHash !== parsed.planHash) {
17044
- throw new PlanV2IntegrityError("manifest integrity hash mismatch");
17045
- }
17046
- for (const [index, artifact] of parsed.artifacts.entries()) {
17047
- if (artifact.chunks !== "all" && artifact.chunks.some((chunkIndex) => chunkIndex >= parsed.chunkCount)) {
17048
- throw new PlanV2IntegrityError(`artifacts[${index}].chunks contains an out-of-range index`);
17049
- }
17050
- }
17051
- return parsed;
17052
17236
  }
17053
- function readPlanV2Manifest(planV2Dir) {
17054
- const manifestPath = join37(planV2Dir, "plan.json");
17055
- if (!existsSync24(manifestPath)) {
17056
- throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
17057
- }
17058
- const value = readJsonFile(manifestPath, "v2 manifest");
17059
- return parsePlanV2Manifest(value);
17237
+ async function putBlobs(publisher, blobs) {
17238
+ await putTasks(blobs.map((blob) => () => publisher.putBlob(blob)));
17060
17239
  }
17061
- function listPlanV2ArtifactsForTarget(manifest, target) {
17062
- if (target.role === "chunk") {
17063
- if (!Number.isInteger(target.chunkIndex) || target.chunkIndex < 0 || target.chunkIndex >= manifest.chunkCount) {
17064
- throw new PlanV2IntegrityError(`chunkIndex ${String(target.chunkIndex)} is out of range`);
17240
+ async function putTasks(tasks) {
17241
+ const uploadSlots = 32;
17242
+ let next = 0;
17243
+ let failure;
17244
+ const upload = async () => {
17245
+ while (!failure && next < tasks.length) {
17246
+ const task = tasks[next++];
17247
+ try {
17248
+ await task();
17249
+ } catch (reason) {
17250
+ failure ??= { reason };
17251
+ }
17065
17252
  }
17066
- return manifest.artifacts.filter(
17067
- (artifact) => artifact.chunks === "all" || artifact.chunks.some((chunkIndex) => chunkIndex === target.chunkIndex)
17068
- );
17069
- }
17070
- return manifest.artifacts.filter((artifact) => artifact.assembler);
17253
+ };
17254
+ await Promise.all(Array.from({ length: uploadSlots }, upload));
17255
+ if (failure) throw failure.reason;
17071
17256
  }
17072
- function verifyBlob(planV2Dir, artifact) {
17073
- const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
17074
- if (!existsSync24(sourcePath)) {
17075
- throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
17076
- }
17077
- const stats = statSync9(sourcePath);
17078
- if (!stats.isFile() || stats.size !== artifact.sizeBytes) {
17079
- throw new PlanV2IntegrityError(`artifact size mismatch for ${artifact.path}`);
17257
+ function buildPlanV2Publication(executionPlanDir, digests) {
17258
+ const executionPlanPath = join39(executionPlanDir, "plan.json");
17259
+ if (!existsSync25(executionPlanPath)) {
17260
+ throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
17080
17261
  }
17081
- if (sha256File(sourcePath) !== artifact.sha256) {
17082
- throw new PlanV2IntegrityError(`artifact hash mismatch for ${artifact.path}`);
17262
+ const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
17263
+ if (!isRecord3(executionPlanValue)) {
17264
+ throw new PlanV2IntegrityError("execution plan.json must be an object");
17083
17265
  }
17084
- return sourcePath;
17085
- }
17086
- function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
17087
- if (existsSync24(destinationDir)) {
17088
- throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
17266
+ const executionPlan = executionPlanValue;
17267
+ readPlanProtocolV1(executionPlan);
17268
+ const executionPlanHash = executionPlan.planHash;
17269
+ if (!isSha256(executionPlanHash)) {
17270
+ throw new PlanV2IntegrityError("execution plan.json.planHash must be a sha256 digest");
17089
17271
  }
17090
- const manifest = readPlanV2Manifest(planV2Dir);
17091
- const artifacts = listPlanV2ArtifactsForTarget(manifest, target);
17092
- const remoteSources = options.remoteSources ?? {};
17093
- assertRemoteSources(remoteSources, artifacts);
17094
- const audioArtifact = artifacts.find((artifact) => isPlanAudioArtifactPath(artifact.path));
17095
- const verified = artifacts.filter((artifact) => !Object.hasOwn(remoteSources, artifact.path)).map((artifact) => ({
17096
- artifact,
17097
- sourcePath: verifyBlob(planV2Dir, artifact)
17098
- }));
17099
- mkdirSync13(dirname14(destinationDir), { recursive: true });
17100
- const tempDir = mkdtempSync6(join37(dirname14(destinationDir), ".plan-v2-materialize-"));
17101
- try {
17102
- for (const { artifact, sourcePath } of verified) {
17103
- const destinationPath = join37(tempDir, ...artifact.path.split("/"));
17104
- mkdirSync13(dirname14(destinationPath), { recursive: true });
17105
- copyFileSync6(sourcePath, destinationPath);
17106
- }
17107
- if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
17108
- writeFileSync10(
17109
- join37(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
17110
- canonicalJsonStringify({
17111
- manifest,
17112
- target,
17113
- ...Object.keys(remoteSources).length > 0 ? { remoteSources } : {}
17114
- }),
17115
- "utf-8"
17272
+ const recomputedExecutionPlanHash = recomputePlanHashFromPlanDir(executionPlanDir, digests);
17273
+ if (recomputedExecutionPlanHash !== executionPlanHash) {
17274
+ throw new PlanV2IntegrityError(
17275
+ `execution plan content fingerprint does not match plan.json.planHash: expected ${executionPlanHash}, recomputed ${recomputedExecutionPlanHash}`
17116
17276
  );
17117
- renameSync6(tempDir, destinationDir);
17118
- } catch (error) {
17119
- rmSync12(tempDir, { recursive: true, force: true });
17120
- throw error;
17121
17277
  }
17122
- return {
17123
- planDir: destinationDir,
17124
- target,
17125
- planHash: manifest.planHash,
17126
- sourcePlanV1Hash: getPlanV2ExecutionPlanHash(manifest),
17127
- artifactCount: artifacts.length,
17128
- sizeBytes: artifacts.reduce((sum2, artifact) => sum2 + artifact.sizeBytes, 0),
17129
- // Join the artifact's OWN name, not the current constant: a plan written
17130
- // before the container change carries the legacy name, and materializing it
17131
- // under the new one would point at a file that was never written.
17132
- audioPath: target.role === "assembler" ? audioArtifact ? join37(destinationDir, audioArtifact.path) : null : null
17133
- };
17134
- }
17135
- function assertRemoteSources(remoteSources, artifacts) {
17136
- if (!isRecord3(remoteSources)) throw new PlanV2IntegrityError("remoteSources must be an object");
17137
- for (const [path, url] of Object.entries(remoteSources)) {
17138
- const artifact = artifacts.find((candidate) => candidate.path === path);
17139
- if (!artifact || !isPlanV2DeferredSourceArtifact(artifact)) {
17140
- throw new PlanV2IntegrityError(`remote source is not a selected deferred video: ${path}`);
17141
- }
17142
- if (typeof url !== "string" || !/^https?:\/\//.test(url)) {
17143
- throw new PlanV2IntegrityError(`remote source for ${path} must be an http(s) URL`);
17278
+ const artifacts = [];
17279
+ const blobs = /* @__PURE__ */ new Map();
17280
+ const dimensions = executionPlan.dimensions;
17281
+ if (!isRecord3(dimensions)) {
17282
+ throw new PlanV2IntegrityError("execution plan.json.dimensions must be an object");
17283
+ }
17284
+ validateExtractionCacheCompleteSentinels(executionPlanDir);
17285
+ const videoDependencyPlan = buildVideoChunkDependencies(executionPlanDir, dimensions);
17286
+ const encoder = readJsonFile(join39(executionPlanDir, "meta", "encoder.json"), "meta/encoder.json");
17287
+ const files = listFiles(executionPlanDir);
17288
+ const chunkAudio = !isRecord3(encoder) || encoder.chunksCarryAudio !== true ? "none" : files.some((file) => chunkAudioSliceTarget(file.path) !== null) ? "sliced" : "whole";
17289
+ for (const file of files) {
17290
+ if (isExtractionCacheCompleteSentinelPath(file.path)) continue;
17291
+ const targets = artifactTargets(file.path, videoDependencyPlan.dependencies, chunkAudio);
17292
+ if ((file.path.startsWith("video-frames/") || file.path.startsWith("video-sources/")) && targets.chunks !== "all" && targets.chunks.length === 0 && !targets.assembler) {
17293
+ continue;
17144
17294
  }
17295
+ const blob = publishBlobFor(file.absolutePath, digests);
17296
+ if (!blobs.has(blob.sha256)) blobs.set(blob.sha256, blob);
17297
+ artifacts.push({
17298
+ path: file.path,
17299
+ sha256: blob.sha256,
17300
+ sizeBytes: blob.sizeBytes,
17301
+ ...targets
17302
+ });
17145
17303
  }
17304
+ const storedBlobs = /* @__PURE__ */ new Map();
17305
+ for (const remote of readPlanRemoteFiles(executionPlanDir)) {
17306
+ const { sha256, sizeBytes } = remote;
17307
+ if (!blobs.has(sha256)) storedBlobs.set(sha256, { sha256, sizeBytes });
17308
+ artifacts.push({
17309
+ ...remote,
17310
+ ...artifactTargets(remote.path, videoDependencyPlan.dependencies, chunkAudio)
17311
+ });
17312
+ }
17313
+ const base = {
17314
+ protocol: PLAN_PROTOCOL_V2,
17315
+ // Retain the established manifest key byte-for-byte. It now acts as the
17316
+ // wire alias for the neutral local execution-plan hash.
17317
+ sourcePlanV1Hash: executionPlanHash,
17318
+ chunkCount: readPositiveInteger2(executionPlan.chunkCount, "chunkCount"),
17319
+ totalFrames: readPositiveInteger2(executionPlan.totalFrames, "totalFrames"),
17320
+ fps: readExecutionPlanFps(dimensions),
17321
+ width: readPositiveInteger2(dimensions.width, "dimensions.width"),
17322
+ height: readPositiveInteger2(dimensions.height, "dimensions.height"),
17323
+ format: readDistributedFormat(dimensions.format),
17324
+ ffmpegVersion: readString2(executionPlan.ffmpegVersion, "ffmpegVersion"),
17325
+ producerVersion: readString2(executionPlan.producerVersion, "producerVersion"),
17326
+ limitations: { videoDependencyMode: videoDependencyPlan.mode },
17327
+ artifacts
17328
+ };
17329
+ return {
17330
+ manifest: {
17331
+ ...base,
17332
+ planHash: computeManifestHash(base)
17333
+ },
17334
+ blobs: [...blobs.values()],
17335
+ storedBlobs: [...storedBlobs.values()]
17336
+ };
17146
17337
  }
17147
- function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
17148
- return readValidatedPlanV2Materialization(planDir, expectedTarget)?.manifest ?? null;
17149
- }
17150
- function readValidatedPlanV2Materialization(planDir, expectedTarget) {
17151
- const markerPath = join37(planDir, PLAN_V2_MATERIALIZATION_MARKER);
17152
- if (!existsSync24(markerPath)) return null;
17153
- const marker = readJsonFile(markerPath, "materialization marker");
17154
- if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
17155
- throw new PlanV2IntegrityError("malformed materialization marker");
17338
+ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
17339
+ if (existsSync25(planV2Dir)) {
17340
+ throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
17156
17341
  }
17157
- const target = marker.target;
17158
- if (target.role !== expectedTarget.role || expectedTarget.role === "chunk" && target.chunkIndex !== expectedTarget.chunkIndex) {
17159
- throw new PlanV2IntegrityError(
17160
- "materialization target does not match requested execution role"
17161
- );
17342
+ const publication = buildPlanV2Publication(executionPlanDir);
17343
+ if (publication.storedBlobs.length > 0) {
17344
+ throw new PlanV2IntegrityError("a local plan v2 directory cannot reference remote files");
17162
17345
  }
17163
- const manifest = parsePlanV2Manifest(marker.manifest);
17164
- const required = listPlanV2ArtifactsForTarget(manifest, expectedTarget);
17165
- const remoteSources = marker.remoteSources ?? {};
17166
- assertRemoteSources(remoteSources, required);
17167
- for (const artifact of required) {
17168
- if (Object.hasOwn(remoteSources, artifact.path)) continue;
17169
- const path = join37(planDir, ...artifact.path.split("/"));
17170
- if (!existsSync24(path) || statSync9(path).size !== artifact.sizeBytes) {
17171
- throw new PlanV2IntegrityError(
17172
- `materialized artifact missing or truncated: ${artifact.path}`
17173
- );
17174
- }
17175
- if (sha256File(path) !== artifact.sha256) {
17176
- throw new PlanV2IntegrityError(`materialized artifact hash mismatch: ${artifact.path}`);
17346
+ mkdirSync15(dirname15(planV2Dir), { recursive: true });
17347
+ const tempDir = mkdtempSync7(join39(dirname15(planV2Dir), ".plan-v2-build-"));
17348
+ try {
17349
+ for (const blob of publication.blobs) {
17350
+ writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
17177
17351
  }
17352
+ writeFileSync11(
17353
+ join39(tempDir, "plan.json"),
17354
+ canonicalJsonStringify(publication.manifest),
17355
+ "utf-8"
17356
+ );
17357
+ renameSync7(tempDir, planV2Dir);
17358
+ return resultFromManifest(planV2Dir, publication.manifest);
17359
+ } catch (error) {
17360
+ rmSync13(tempDir, { recursive: true, force: true });
17361
+ throw error;
17178
17362
  }
17179
- return { manifest, remoteSources };
17180
- }
17181
-
17182
- // ../producer/src/services/distributed/planV2Execution.ts
17183
- import { mkdtempSync as mkdtempSync9, rmSync as rmSync16 } from "fs";
17184
- import { tmpdir as tmpdir5 } from "os";
17185
- import { join as join41 } from "path";
17186
-
17187
- // ../producer/src/services/distributed/assemble.ts
17188
- import {
17189
- cpSync as cpSync3,
17190
- existsSync as existsSync25,
17191
- mkdirSync as mkdirSync15,
17192
- mkdtempSync as mkdtempSync8,
17193
- readFileSync as readFileSync12,
17194
- readdirSync as readdirSync10,
17195
- renameSync as renameSync8,
17196
- rmSync as rmSync14,
17197
- statSync as statSync10,
17198
- writeFileSync as writeFileSync11
17199
- } from "fs";
17200
- import { dirname as dirname16, join as join39 } from "path";
17201
-
17202
- // ../producer/src/services/distributed/mp4Stitch.ts
17203
- import {
17204
- createReadStream as createReadStream5,
17205
- createWriteStream,
17206
- mkdirSync as mkdirSync14,
17207
- mkdtempSync as mkdtempSync7,
17208
- renameSync as renameSync7,
17209
- rmSync as rmSync13
17210
- } from "fs";
17211
- import { open } from "fs/promises";
17212
- import { dirname as dirname15, join as join38 } from "path";
17213
- import { finished, pipeline } from "stream/promises";
17214
- function boxAt(src, off, at, limit) {
17215
- let size = src.readUInt32BE(off);
17216
- let bodyStart = at + 8;
17217
- if (size === 1) {
17218
- size = Number(src.readBigUInt64BE(off + 8));
17219
- bodyStart = at + 16;
17220
- }
17221
- if (size < 8 || at + size > limit) throw new Error(`[mp4Stitch] malformed box at ${at}`);
17222
- return { type: src.toString("latin1", off + 4, off + 8), start: at, bodyStart, end: at + size };
17223
- }
17224
- function childBoxes(buf, parent) {
17225
- const boxes = [];
17226
- for (let at = parent.bodyStart; at < parent.end; at = boxes.at(-1).end) {
17227
- boxes.push(boxAt(buf, at, at, parent.end));
17228
- }
17229
- return boxes;
17230
17363
  }
17231
- function findChild(buf, parent, type) {
17232
- return childBoxes(buf, parent).find((box2) => box2.type === type);
17364
+ function createPlanV2FromV1(executionPlanDir, planV2Dir) {
17365
+ return createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir);
17233
17366
  }
17234
- function child(buf, parent, type) {
17235
- const found = findChild(buf, parent, type);
17236
- if (!found) throw new Error(`[mp4Stitch] ${parent.type} has no ${type}`);
17237
- return found;
17367
+ async function publishPlanV2FromExecutionPlan(executionPlanDir, publisher) {
17368
+ return publishExecutionPlan(executionPlanDir, publisher, {});
17238
17369
  }
17239
- var SCAN_BYTES = 64 * 1024;
17240
- async function readMp4Layout(read) {
17241
- const found = /* @__PURE__ */ new Map();
17242
- let payload;
17243
- let buf = Buffer.alloc(0);
17244
- let bufAt = 0;
17245
- const load = async (start, end) => {
17246
- if (start >= bufAt && end <= bufAt + buf.length) return;
17247
- buf = await read(start, Math.max(end, start + SCAN_BYTES));
17248
- bufAt = start;
17249
- };
17250
- for (let at = 0; !(found.has("ftyp") && found.has("moov") && payload); ) {
17251
- await load(at, at + 16);
17252
- if (at + 8 > bufAt + buf.length) {
17253
- throw new Error(`[mp4Stitch] chunk needs ftyp, moov and mdat; found ${[...found.keys()]}`);
17370
+ async function publishExecutionPlan(executionPlanDir, publisher, { digests, log = defaultLogger }) {
17371
+ try {
17372
+ let stageStarted = Date.now();
17373
+ const publication = buildPlanV2Publication(executionPlanDir, digests);
17374
+ logPlanStage(log, "publication", stageStarted);
17375
+ if (publication.storedBlobs.length > 0 && !publisher.referenceStoredBlob) {
17376
+ throw new PlanV2IntegrityError("this publisher cannot reference remote files");
17254
17377
  }
17255
- const box2 = boxAt(buf, at - bufAt, at, Number.MAX_SAFE_INTEGER);
17256
- if (box2.type === "mdat") payload ??= box2;
17257
- if (box2.type === "ftyp" || box2.type === "moov") {
17258
- await load(at, box2.end);
17259
- found.set(box2.type, buf.subarray(at - bufAt, box2.end - bufAt));
17378
+ stageStarted = Date.now();
17379
+ await putTasks([
17380
+ ...publication.blobs.map((blob) => () => publisher.putBlob(blob)),
17381
+ ...publication.storedBlobs.map((blob) => () => publisher.referenceStoredBlob(blob))
17382
+ ]);
17383
+ logPlanStage(log, "upload", stageStarted);
17384
+ stageStarted = Date.now();
17385
+ await publisher.commitManifest(canonicalJsonStringify(publication.manifest));
17386
+ logPlanStage(log, "manifest", stageStarted);
17387
+ return publication.manifest;
17388
+ } catch (error) {
17389
+ try {
17390
+ await publisher.abort();
17391
+ } catch {
17260
17392
  }
17261
- at = box2.end;
17393
+ throw error;
17262
17394
  }
17263
- const [ftyp, moov] = [found.get("ftyp"), found.get("moov")];
17264
- if (moov.length !== moovSize(moov)) throw new Error("[mp4Stitch] moov is cut off");
17265
- return { ftyp, moov, payloadStart: payload.bodyStart, payloadEnd: payload.end };
17266
- }
17267
- var moovSize = (moov) => boxAt(moov, 0, 0, Number.MAX_SAFE_INTEGER).end;
17268
- function readTracks(moov) {
17269
- const root = boxAt(moov, 0, 0, moov.length);
17270
- return childBoxes(moov, root).filter((box2) => box2.type === "trak").map((trak) => readTrack(moov, trak));
17271
17395
  }
17272
- function rows(buf, table, columns, countAt = 4) {
17273
- const count = buf.readUInt32BE(table.bodyStart + countAt);
17274
- const first = table.bodyStart + countAt + 4;
17275
- return Array.from(
17276
- { length: count },
17277
- (_, row) => Array.from(
17278
- { length: columns },
17279
- (_2, col) => buf.readUInt32BE(first + (row * columns + col) * 4)
17280
- )
17281
- );
17396
+ async function publishPlanV2FromV1(executionPlanDir, publisher) {
17397
+ return publishPlanV2FromExecutionPlan(executionPlanDir, publisher);
17282
17398
  }
17283
- function readMediaTime(moov, trak) {
17284
- const edts = findChild(moov, trak, "edts");
17285
- if (!edts) return 0;
17286
- const elst = child(moov, edts, "elst");
17287
- const wide = moov[elst.bodyStart] === 1;
17288
- const entries = moov.readUInt32BE(elst.bodyStart + 4);
17289
- for (let i = 0, at = elst.bodyStart + 8; i < entries; i++, at += wide ? 20 : 12) {
17290
- const offset = at + (wide ? 8 : 4);
17291
- const mediaTime = wide ? Number(moov.readBigInt64BE(offset)) : moov.readInt32BE(offset);
17292
- if (mediaTime !== -1) return mediaTime;
17399
+ async function planV2WithPublisher(projectDir, config, publisher, options = {}) {
17400
+ const stagingRoot = mkdtempSync7(join39(options.stagingParentDir ?? tmpdir4(), ".execution-plan-"));
17401
+ const digests = /* @__PURE__ */ new Map();
17402
+ try {
17403
+ await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
17404
+ executionPlanSizeLimitBytes: Number.MAX_SAFE_INTEGER,
17405
+ deferVideoExtraction: true,
17406
+ remoteFiles: options.remoteFiles,
17407
+ digests,
17408
+ // artifactTargets ships every compiled/ file to all chunks, so each is in the manifest.
17409
+ whileAudioMixes: () => putBlobs(
17410
+ publisher,
17411
+ listFiles(join39(stagingRoot, "compiled")).map(
17412
+ (file) => publishBlobFor(file.absolutePath, digests)
17413
+ )
17414
+ ),
17415
+ chunksCarryAudio: options.chunksCarryAudio,
17416
+ sliceChunkAudio: true
17417
+ });
17418
+ return await publishExecutionPlan(stagingRoot, publisher, { digests, log: config.logger });
17419
+ } catch (error) {
17420
+ try {
17421
+ await publisher.abort();
17422
+ } catch {
17423
+ }
17424
+ throw error;
17425
+ } finally {
17426
+ rmSync13(stagingRoot, { recursive: true, force: true });
17293
17427
  }
17294
- return 0;
17295
- }
17296
- function readSizes(moov, stsz) {
17297
- const fixedSize = moov.readUInt32BE(stsz.bodyStart + 4);
17298
- if (!fixedSize) return rows(moov, stsz, 1, 8).map(([size]) => size);
17299
- return Array(moov.readUInt32BE(stsz.bodyStart + 8)).fill(fixedSize);
17300
- }
17301
- function readSync4(moov, stbl, sampleCount) {
17302
- const stss = findChild(moov, stbl, "stss");
17303
- if (!stss) return null;
17304
- const sync = Array(sampleCount).fill(false);
17305
- for (const [n] of rows(moov, stss, 1)) sync[n - 1] = true;
17306
- return sync;
17307
- }
17308
- function readChunkOffsets(moov, stbl) {
17309
- const stco = findChild(moov, stbl, "stco");
17310
- if (stco) return rows(moov, stco, 1).map(([offset]) => offset);
17311
- return rows(moov, child(moov, stbl, "co64"), 2).map(([hi, lo]) => hi * 2 ** 32 + lo);
17312
17428
  }
17313
- function readSamplesPerChunk(moov, stbl, chunkCount) {
17314
- const stsc = rows(moov, child(moov, stbl, "stsc"), 3);
17315
- return stsc.flatMap(([firstChunk, perChunk, description], i) => {
17316
- if (description !== 1) throw new Error("[mp4Stitch] chunk uses more than one sample entry");
17317
- const nextFirst = stsc[i + 1]?.[0] ?? chunkCount + 1;
17318
- return Array(nextFirst - firstChunk).fill(perChunk);
17429
+ async function planV2(projectDir, config, planV2Dir) {
17430
+ if (existsSync25(planV2Dir)) {
17431
+ throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
17432
+ }
17433
+ const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
17434
+ const manifest = await planV2WithPublisher(projectDir, config, publisher, {
17435
+ stagingParentDir: dirname15(planV2Dir)
17319
17436
  });
17437
+ return resultFromManifest(planV2Dir, manifest);
17320
17438
  }
17321
- function readTrack(moov, trak) {
17322
- const mdia = child(moov, trak, "mdia");
17323
- const hdlr = child(moov, mdia, "hdlr");
17324
- const stbl = child(moov, child(moov, mdia, "minf"), "stbl");
17325
- if (findChild(moov, stbl, "ctts")) throw new Error("[mp4Stitch] chunk has ctts (B-frames)");
17326
- const stts = rows(moov, child(moov, stbl, "stts"), 2);
17327
- const durations = stts.flatMap(([n, delta]) => Array(n).fill(delta));
17328
- const sizes = readSizes(moov, child(moov, stbl, "stsz"));
17329
- const chunkOffsets = readChunkOffsets(moov, stbl);
17330
- const samplesPerChunk = readSamplesPerChunk(moov, stbl, chunkOffsets.length);
17331
- if (durations.length !== sizes.length || sum(samplesPerChunk) !== sizes.length) {
17332
- throw new Error("[mp4Stitch] sample tables disagree on the sample count");
17333
- }
17334
- const stsd = child(moov, stbl, "stsd");
17439
+ function resultFromManifest(planV2Dir, manifest) {
17335
17440
  return {
17336
- trak,
17337
- handler: moov.toString("latin1", hdlr.bodyStart + 8, hdlr.bodyStart + 12),
17338
- timescale: timescaleOf(moov, child(moov, mdia, "mdhd")),
17339
- mediaTime: readMediaTime(moov, trak),
17340
- stsd: moov.subarray(stsd.start, stsd.end),
17341
- durations,
17342
- sizes,
17343
- sync: readSync4(moov, stbl, sizes.length),
17344
- samplesPerChunk,
17345
- chunkOffsets
17441
+ planDir: planV2Dir,
17442
+ manifestPath: join39(planV2Dir, "plan.json"),
17443
+ planProtocol: PLAN_PROTOCOL_V2,
17444
+ planHash: manifest.planHash,
17445
+ sourcePlanV1Hash: getPlanV2ExecutionPlanHash(manifest),
17446
+ chunkCount: manifest.chunkCount,
17447
+ totalFrames: manifest.totalFrames,
17448
+ fps: manifest.fps,
17449
+ width: manifest.width,
17450
+ height: manifest.height,
17451
+ format: manifest.format,
17452
+ ffmpegVersion: manifest.ffmpegVersion,
17453
+ producerVersion: manifest.producerVersion,
17454
+ limitations: manifest.limitations
17346
17455
  };
17347
17456
  }
17348
- function sum(values) {
17349
- return values.reduce((total, value) => total + value, 0);
17457
+ function getPlanV2ExecutionPlanHash(plan2) {
17458
+ return plan2.sourcePlanV1Hash;
17350
17459
  }
17351
- function box(type, ...body) {
17352
- const header = Buffer.alloc(8);
17353
- header.writeUInt32BE(8 + sum(body.map((part) => part.length)));
17354
- header.write(type, 4, "latin1");
17355
- return Buffer.concat([header, ...body]);
17460
+ function readString2(value, field) {
17461
+ if (typeof value !== "string" || value.length === 0) {
17462
+ throw new PlanV2IntegrityError(`${field} must be a non-empty string`);
17463
+ }
17464
+ return value;
17356
17465
  }
17357
- function fullBox(type, version, body) {
17358
- return box(type, Buffer.from([version, 0, 0, 0]), body);
17466
+ function readPositiveInteger2(value, field) {
17467
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
17468
+ throw new PlanV2IntegrityError(`${field} must be a positive integer`);
17469
+ }
17470
+ return value;
17359
17471
  }
17360
- function u32s(values) {
17361
- const buf = Buffer.alloc(values.length * 4);
17362
- values.forEach((value, i) => buf.writeUInt32BE(value, i * 4));
17363
- return buf;
17472
+ function readNonNegativeInteger2(value, field) {
17473
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
17474
+ throw new PlanV2IntegrityError(`${field} must be a non-negative integer`);
17475
+ }
17476
+ return value;
17364
17477
  }
17365
- function runs(values) {
17366
- const out = [];
17367
- for (const value of values) {
17368
- const last = out.at(-1);
17369
- if (last && last[1] === value) last[0]++;
17370
- else out.push([1, value]);
17478
+ function readSupportedFps(value) {
17479
+ if (!isDistributedFps(value)) {
17480
+ throw new PlanV2IntegrityError(
17481
+ `dimensions.fpsNum must be one of ${DISTRIBUTED_FPS.join(", ")}`
17482
+ );
17371
17483
  }
17372
- return out;
17484
+ return value;
17373
17485
  }
17374
- function rebuild(buf, parent, replace) {
17375
- const parts = childBoxes(buf, parent).map(
17376
- (box2) => replace[box2.type]?.(box2) ?? buf.subarray(box2.start, box2.end)
17377
- );
17378
- return box(parent.type, ...parts);
17486
+ function readExecutionPlanFps(dimensions) {
17487
+ const fpsDen = readPositiveInteger2(dimensions.fpsDen, "dimensions.fpsDen");
17488
+ if (fpsDen !== 1) {
17489
+ throw new PlanV2IntegrityError("dimensions.fpsDen must be 1 for plan v2");
17490
+ }
17491
+ return readSupportedFps(dimensions.fpsNum);
17379
17492
  }
17380
- var DURATION_AT = {
17381
- mvhd: [16, 24],
17382
- mdhd: [16, 24],
17383
- tkhd: [20, 28]
17384
- };
17385
- function withDuration(buf, target, duration) {
17386
- const copy = Buffer.from(buf.subarray(target.start, target.end));
17387
- const body = target.bodyStart - target.start;
17388
- const wide = copy[body] === 1;
17389
- const at = body + DURATION_AT[target.type][wide ? 1 : 0];
17390
- if (wide) copy.writeBigUInt64BE(BigInt(duration), at);
17391
- else if (duration <= 4294967295) copy.writeUInt32BE(duration, at);
17392
- else throw new Error(`[mp4Stitch] ${target.type} duration ${duration} needs a version 1 box`);
17393
- return copy;
17493
+ function readDistributedFormat(value) {
17494
+ if (value !== "mp4" && value !== "mov" && value !== "webm" && value !== "png-sequence") {
17495
+ throw new PlanV2IntegrityError("dimensions.format is unsupported");
17496
+ }
17497
+ return value;
17394
17498
  }
17395
- function timescaleOf(buf, header) {
17396
- return buf.readUInt32BE(header.bodyStart + (buf[header.bodyStart] === 1 ? 20 : 12));
17499
+ function parseArtifact(value, index) {
17500
+ if (!isRecord3(value)) throw new PlanV2IntegrityError(`artifacts[${index}] must be an object`);
17501
+ const path = readString2(value.path, `artifacts[${index}].path`);
17502
+ assertSafeRelativePath(path);
17503
+ if (!isSha256(value.sha256)) {
17504
+ throw new PlanV2IntegrityError(`artifacts[${index}].sha256 must be a lowercase sha256`);
17505
+ }
17506
+ const sizeBytes = value.sizeBytes;
17507
+ if (typeof sizeBytes !== "number" || !Number.isInteger(sizeBytes) || sizeBytes < 0) {
17508
+ throw new PlanV2IntegrityError(`artifacts[${index}].sizeBytes must be a non-negative integer`);
17509
+ }
17510
+ let chunks;
17511
+ if (value.chunks === "all") {
17512
+ chunks = "all";
17513
+ } else if (isNonNegativeIntegerArray(value.chunks)) {
17514
+ chunks = value.chunks;
17515
+ } else {
17516
+ throw new PlanV2IntegrityError(`artifacts[${index}].chunks is invalid`);
17517
+ }
17518
+ if (typeof value.assembler !== "boolean") {
17519
+ throw new PlanV2IntegrityError(`artifacts[${index}].assembler must be boolean`);
17520
+ }
17521
+ return {
17522
+ path,
17523
+ sha256: value.sha256,
17524
+ sizeBytes,
17525
+ chunks,
17526
+ assembler: value.assembler
17527
+ };
17397
17528
  }
17398
- function buildStbl(stsd, track) {
17399
- const stts = runs(track.durations);
17400
- const stsc = [];
17401
- let firstChunk = 1;
17402
- for (const [count, perChunk] of runs(track.samplesPerChunk)) {
17403
- stsc.push([firstChunk, perChunk, 1]);
17404
- firstChunk += count;
17529
+ function parsePlanV2Manifest(value) {
17530
+ if (!isRecord3(value)) throw new PlanV2IntegrityError("manifest must be an object");
17531
+ if (!isRecord3(value.protocol) || value.protocol.schemaVersion !== PLAN_PROTOCOL_V2.schemaVersion || value.protocol.artifactLayout !== PLAN_PROTOCOL_V2.artifactLayout || value.protocol.hashSchema !== PLAN_PROTOCOL_V2.hashSchema) {
17532
+ throw new PlanV2IntegrityError("manifest protocol is not the supported v2 descriptor");
17405
17533
  }
17406
- const co64 = Buffer.alloc(4 + track.chunkOffsets.length * 8);
17407
- co64.writeUInt32BE(track.chunkOffsets.length);
17408
- track.chunkOffsets.forEach((offset, i) => co64.writeBigUInt64BE(BigInt(offset), 4 + i * 8));
17409
- const syncSamples = track.sync?.flatMap((isSync, i) => isSync ? [i + 1] : []) ?? null;
17410
- return box(
17411
- "stbl",
17412
- stsd,
17413
- fullBox("stts", 0, u32s([stts.length, ...stts.flat()])),
17414
- ...syncSamples ? [fullBox("stss", 0, u32s([syncSamples.length, ...syncSamples]))] : [],
17415
- fullBox("stsc", 0, u32s([stsc.length, ...stsc.flat()])),
17416
- fullBox("stsz", 0, u32s([0, track.sizes.length, ...track.sizes])),
17417
- fullBox("co64", 0, co64)
17418
- );
17534
+ if (!isSha256(value.planHash) || !isSha256(value.sourcePlanV1Hash)) {
17535
+ throw new PlanV2IntegrityError("manifest hashes must be lowercase sha256 digests");
17536
+ }
17537
+ if (!Array.isArray(value.artifacts)) throw new PlanV2IntegrityError("artifacts must be an array");
17538
+ const artifacts = value.artifacts.map(parseArtifact);
17539
+ const paths = /* @__PURE__ */ new Set();
17540
+ for (const artifact of artifacts) {
17541
+ if (paths.has(artifact.path))
17542
+ throw new PlanV2IntegrityError(`duplicate artifact path: ${artifact.path}`);
17543
+ paths.add(artifact.path);
17544
+ }
17545
+ if (!paths.has("plan.json"))
17546
+ throw new PlanV2IntegrityError("manifest must include the local execution plan.json artifact");
17547
+ if (!isRecord3(value.limitations) || value.limitations.videoDependencyMode !== "exact-rendered-frames" && value.limitations.videoDependencyMode !== "full-source-pack" && value.limitations.videoDependencyMode !== "source-extract") {
17548
+ throw new PlanV2IntegrityError("unsupported video dependency mode");
17549
+ }
17550
+ const parsed = {
17551
+ protocol: PLAN_PROTOCOL_V2,
17552
+ planHash: value.planHash,
17553
+ sourcePlanV1Hash: value.sourcePlanV1Hash,
17554
+ chunkCount: readPositiveInteger2(value.chunkCount, "chunkCount"),
17555
+ totalFrames: readPositiveInteger2(value.totalFrames, "totalFrames"),
17556
+ fps: readSupportedFps(value.fps),
17557
+ width: readPositiveInteger2(value.width, "width"),
17558
+ height: readPositiveInteger2(value.height, "height"),
17559
+ format: readDistributedFormat(value.format),
17560
+ ffmpegVersion: readString2(value.ffmpegVersion, "ffmpegVersion"),
17561
+ producerVersion: readString2(value.producerVersion, "producerVersion"),
17562
+ limitations: { videoDependencyMode: value.limitations.videoDependencyMode },
17563
+ artifacts
17564
+ };
17565
+ const { planHash: _planHash, ...payload } = parsed;
17566
+ const expectedHash = computeManifestHash(payload);
17567
+ if (expectedHash !== parsed.planHash) {
17568
+ throw new PlanV2IntegrityError("manifest integrity hash mismatch");
17569
+ }
17570
+ for (const [index, artifact] of parsed.artifacts.entries()) {
17571
+ if (artifact.chunks !== "all" && artifact.chunks.some((chunkIndex) => chunkIndex >= parsed.chunkCount)) {
17572
+ throw new PlanV2IntegrityError(`artifacts[${index}].chunks contains an out-of-range index`);
17573
+ }
17574
+ }
17575
+ return parsed;
17419
17576
  }
17420
- var codecOf = (track) => track.stsd.toString("latin1", 20, 24);
17421
- var sameTrack = (track, plan2) => track.handler === plan2.handler && track.timescale === plan2.timescale && codecOf(track) === codecOf(plan2);
17422
- function planTracksOf(tracks, plan2, index) {
17423
- let next = 0;
17424
- const matched = plan2.map((planTrack) => {
17425
- const track = tracks[next];
17426
- if (track && sameTrack(track, planTrack)) return tracks[next++];
17427
- if (planTrack.handler === "soun") return null;
17428
- throw new Error(`[mp4Stitch] chunk ${index} tracks differ from chunk 0`);
17429
- });
17430
- if (next !== tracks.length)
17431
- throw new Error(`[mp4Stitch] chunk ${index} tracks differ from chunk 0`);
17432
- for (const track of tracks) {
17433
- if (track.handler === "vide" && track.mediaTime !== 0) {
17434
- throw new Error(`[mp4Stitch] chunk ${index} video starts at media time ${track.mediaTime}`);
17577
+ function readPlanV2Manifest(planV2Dir) {
17578
+ const manifestPath = join39(planV2Dir, "plan.json");
17579
+ if (!existsSync25(manifestPath)) {
17580
+ throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
17581
+ }
17582
+ const value = readJsonFile(manifestPath, "v2 manifest");
17583
+ return parsePlanV2Manifest(value);
17584
+ }
17585
+ function listPlanV2ArtifactsForTarget(manifest, target) {
17586
+ if (target.role === "chunk") {
17587
+ if (!Number.isInteger(target.chunkIndex) || target.chunkIndex < 0 || target.chunkIndex >= manifest.chunkCount) {
17588
+ throw new PlanV2IntegrityError(`chunkIndex ${String(target.chunkIndex)} is out of range`);
17435
17589
  }
17590
+ return manifest.artifacts.filter(
17591
+ (artifact) => artifact.chunks === "all" || artifact.chunks.some((chunkIndex) => chunkIndex === target.chunkIndex)
17592
+ );
17436
17593
  }
17437
- return matched;
17594
+ return manifest.artifacts.filter((artifact) => artifact.assembler);
17438
17595
  }
17439
- function buildStitchedHeader(layouts) {
17440
- const plan2 = readTracks(layouts[0].moov);
17441
- const chunks = layouts.map((layout, i) => planTracksOf(readTracks(layout.moov), plan2, i));
17442
- const payloadBases = [];
17443
- let payloadBytes = 0;
17444
- for (const layout of layouts) {
17445
- payloadBases.push(payloadBytes);
17446
- payloadBytes += layout.payloadEnd - layout.payloadStart;
17596
+ function verifyBlob(planV2Dir, artifact) {
17597
+ const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
17598
+ if (!existsSync25(sourcePath)) {
17599
+ throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
17600
+ }
17601
+ const stats = statSync9(sourcePath);
17602
+ if (!stats.isFile() || stats.size !== artifact.sizeBytes) {
17603
+ throw new PlanV2IntegrityError(`artifact size mismatch for ${artifact.path}`);
17604
+ }
17605
+ if (sha256File(sourcePath) !== artifact.sha256) {
17606
+ throw new PlanV2IntegrityError(`artifact hash mismatch for ${artifact.path}`);
17607
+ }
17608
+ return sourcePath;
17609
+ }
17610
+ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
17611
+ if (existsSync25(destinationDir)) {
17612
+ throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
17613
+ }
17614
+ const manifest = readPlanV2Manifest(planV2Dir);
17615
+ const artifacts = listPlanV2ArtifactsForTarget(manifest, target);
17616
+ const remoteSources = options.remoteSources ?? {};
17617
+ assertRemoteSources(remoteSources, artifacts);
17618
+ const audioArtifact = artifacts.find((artifact) => isPlanAudioArtifactPath(artifact.path));
17619
+ const verified = artifacts.filter((artifact) => !Object.hasOwn(remoteSources, artifact.path)).map((artifact) => ({
17620
+ artifact,
17621
+ sourcePath: verifyBlob(planV2Dir, artifact)
17622
+ }));
17623
+ mkdirSync15(dirname15(destinationDir), { recursive: true });
17624
+ const tempDir = mkdtempSync7(join39(dirname15(destinationDir), ".plan-v2-materialize-"));
17625
+ try {
17626
+ for (const { artifact, sourcePath } of verified) {
17627
+ const destinationPath = join39(tempDir, ...artifact.path.split("/"));
17628
+ mkdirSync15(dirname15(destinationPath), { recursive: true });
17629
+ copyFileSync6(sourcePath, destinationPath);
17630
+ }
17631
+ if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
17632
+ writeFileSync11(
17633
+ join39(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
17634
+ canonicalJsonStringify({
17635
+ manifest,
17636
+ target,
17637
+ ...Object.keys(remoteSources).length > 0 ? { remoteSources } : {}
17638
+ }),
17639
+ "utf-8"
17640
+ );
17641
+ renameSync7(tempDir, destinationDir);
17642
+ } catch (error) {
17643
+ rmSync13(tempDir, { recursive: true, force: true });
17644
+ throw error;
17447
17645
  }
17448
- const template = layouts[0].moov;
17449
- const root = boxAt(template, 0, 0, template.length);
17450
- const movieTimescale = timescaleOf(template, child(template, root, "mvhd"));
17451
- const buildMoov = (dataStart2) => {
17452
- const traks = plan2.map((track, t) => {
17453
- const parts = chunks.flatMap((tracks, chunk) => {
17454
- const part = tracks[t];
17455
- return part ? [{ ...part, chunk }] : [];
17456
- });
17457
- const merged = {
17458
- durations: parts.flatMap((part) => part.durations),
17459
- sizes: parts.flatMap((part) => part.sizes),
17460
- sync: parts.every((part) => !part.sync) ? null : parts.flatMap((part) => part.sync ?? part.sizes.map(() => true)),
17461
- samplesPerChunk: parts.flatMap((part) => part.samplesPerChunk),
17462
- chunkOffsets: parts.flatMap(
17463
- (part) => part.chunkOffsets.map((offset) => {
17464
- const i = part.chunk;
17465
- const { payloadStart, payloadEnd } = layouts[i];
17466
- if (offset < payloadStart || offset >= payloadEnd) {
17467
- throw new Error(`[mp4Stitch] chunk ${i} has samples outside its mdat`);
17468
- }
17469
- return offset - payloadStart + payloadBases[i] + dataStart2;
17470
- })
17471
- )
17472
- };
17473
- const mediaDuration = sum(merged.durations);
17474
- const shown = Math.round(
17475
- (mediaDuration - track.mediaTime) * movieTimescale / track.timescale
17476
- );
17477
- const elst = Buffer.alloc(24);
17478
- elst.writeUInt32BE(1);
17479
- elst.writeBigUInt64BE(BigInt(shown), 4);
17480
- elst.writeBigInt64BE(BigInt(track.mediaTime), 12);
17481
- elst.writeUInt32BE(65536, 20);
17482
- const edts = track.mediaTime ? box("edts", fullBox("elst", 1, elst)) : Buffer.alloc(0);
17483
- const trak = rebuild(template, track.trak, {
17484
- tkhd: (tkhd) => Buffer.concat([withDuration(template, tkhd, shown), edts]),
17485
- edts: () => Buffer.alloc(0),
17486
- mdia: (mdia) => rebuild(template, mdia, {
17487
- mdhd: (mdhd) => withDuration(template, mdhd, mediaDuration),
17488
- minf: (minf) => rebuild(template, minf, { stbl: () => buildStbl(track.stsd, merged) })
17489
- })
17490
- });
17491
- return { trak, shown };
17492
- });
17493
- let next = 0;
17494
- return rebuild(template, root, {
17495
- mvhd: (mvhd) => withDuration(template, mvhd, Math.max(...traks.map((t) => t.shown))),
17496
- trak: () => traks[next++].trak
17497
- });
17646
+ return {
17647
+ planDir: destinationDir,
17648
+ target,
17649
+ planHash: manifest.planHash,
17650
+ sourcePlanV1Hash: getPlanV2ExecutionPlanHash(manifest),
17651
+ artifactCount: artifacts.length,
17652
+ sizeBytes: artifacts.reduce((sum2, artifact) => sum2 + artifact.sizeBytes, 0),
17653
+ // Join the artifact's OWN name, not the current constant: a plan written
17654
+ // before the container change carries the legacy name, and materializing it
17655
+ // under the new one would point at a file that was never written.
17656
+ audioPath: target.role === "assembler" ? audioArtifact ? join39(destinationDir, audioArtifact.path) : null : null
17498
17657
  };
17499
- const ftyp = layouts[0].ftyp;
17500
- const dataStart = ftyp.length + buildMoov(0).length + 16;
17501
- const mdat = Buffer.alloc(16);
17502
- mdat.writeUInt32BE(1);
17503
- mdat.write("mdat", 4, "latin1");
17504
- mdat.writeBigUInt64BE(BigInt(16 + payloadBytes), 8);
17505
- return Buffer.concat([ftyp, buildMoov(dataStart), mdat]);
17506
17658
  }
17507
- function fileRange(path) {
17508
- return async (start, end) => {
17509
- const file = await open(path, "r", 384);
17510
- try {
17511
- const buf = Buffer.alloc(end - start);
17512
- const { bytesRead } = await file.read(buf, 0, buf.length, start);
17513
- return buf.subarray(0, bytesRead);
17514
- } finally {
17515
- await file.close();
17659
+ function assertRemoteSources(remoteSources, artifacts) {
17660
+ if (!isRecord3(remoteSources)) throw new PlanV2IntegrityError("remoteSources must be an object");
17661
+ for (const [path, url] of Object.entries(remoteSources)) {
17662
+ const artifact = artifacts.find((candidate) => candidate.path === path);
17663
+ if (!artifact || !isPlanV2DeferredSourceArtifact(artifact)) {
17664
+ throw new PlanV2IntegrityError(`remote source is not a selected deferred video: ${path}`);
17516
17665
  }
17517
- };
17666
+ if (typeof url !== "string" || !/^https?:\/\//.test(url)) {
17667
+ throw new PlanV2IntegrityError(`remote source for ${path} must be an http(s) URL`);
17668
+ }
17669
+ }
17518
17670
  }
17519
- async function copyPayload(path, layout, index, out) {
17520
- const { payloadStart, payloadEnd } = layout;
17521
- const payload = createReadStream5(path, { start: payloadStart, end: payloadEnd - 1 });
17522
- await pipeline(payload, out, { end: false });
17523
- if (payload.bytesRead !== payloadEnd - payloadStart) {
17524
- throw new Error(
17525
- `[mp4Stitch] chunk ${index} mdat holds ${payload.bytesRead} of ${payloadEnd - payloadStart} payload bytes`
17671
+ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
17672
+ return readValidatedPlanV2Materialization(planDir, expectedTarget)?.manifest ?? null;
17673
+ }
17674
+ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
17675
+ const markerPath = join39(planDir, PLAN_V2_MATERIALIZATION_MARKER);
17676
+ if (!existsSync25(markerPath)) return null;
17677
+ const marker = readJsonFile(markerPath, "materialization marker");
17678
+ if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
17679
+ throw new PlanV2IntegrityError("malformed materialization marker");
17680
+ }
17681
+ const target = marker.target;
17682
+ if (target.role !== expectedTarget.role || expectedTarget.role === "chunk" && target.chunkIndex !== expectedTarget.chunkIndex) {
17683
+ throw new PlanV2IntegrityError(
17684
+ "materialization target does not match requested execution role"
17526
17685
  );
17527
17686
  }
17528
- }
17529
- async function writeStitchedMp4(chunkPaths, outputPath) {
17530
- const layouts = await Promise.all(chunkPaths.map((path) => readMp4Layout(fileRange(path))));
17531
- const header = buildStitchedHeader(layouts);
17532
- mkdirSync14(dirname15(outputPath), { recursive: true });
17533
- const workDir = mkdtempSync7(`${outputPath}.stitch-`);
17534
- const out = createWriteStream(join38(workDir, "out.mp4"));
17535
- try {
17536
- out.write(header);
17537
- for (const [i, path] of chunkPaths.entries()) await copyPayload(path, layouts[i], i, out);
17538
- out.end();
17539
- await finished(out);
17540
- renameSync7(join38(workDir, "out.mp4"), outputPath);
17541
- } finally {
17542
- out.destroy();
17543
- rmSync13(workDir, { recursive: true, force: true });
17687
+ const manifest = parsePlanV2Manifest(marker.manifest);
17688
+ const required = listPlanV2ArtifactsForTarget(manifest, expectedTarget);
17689
+ const remoteSources = marker.remoteSources ?? {};
17690
+ assertRemoteSources(remoteSources, required);
17691
+ for (const artifact of required) {
17692
+ if (Object.hasOwn(remoteSources, artifact.path)) continue;
17693
+ const path = join39(planDir, ...artifact.path.split("/"));
17694
+ if (!existsSync25(path) || statSync9(path).size !== artifact.sizeBytes) {
17695
+ throw new PlanV2IntegrityError(
17696
+ `materialized artifact missing or truncated: ${artifact.path}`
17697
+ );
17698
+ }
17699
+ if (sha256File(path) !== artifact.sha256) {
17700
+ throw new PlanV2IntegrityError(`materialized artifact hash mismatch: ${artifact.path}`);
17701
+ }
17544
17702
  }
17703
+ return { manifest, remoteSources };
17545
17704
  }
17546
17705
 
17706
+ // ../producer/src/services/distributed/planV2Execution.ts
17707
+ import { mkdtempSync as mkdtempSync9, rmSync as rmSync16 } from "fs";
17708
+ import { tmpdir as tmpdir5 } from "os";
17709
+ import { join as join42 } from "path";
17710
+
17547
17711
  // ../producer/src/services/distributed/assemble.ts
17712
+ import {
17713
+ cpSync as cpSync3,
17714
+ existsSync as existsSync26,
17715
+ mkdirSync as mkdirSync16,
17716
+ mkdtempSync as mkdtempSync8,
17717
+ readFileSync as readFileSync13,
17718
+ readdirSync as readdirSync10,
17719
+ renameSync as renameSync8,
17720
+ rmSync as rmSync14,
17721
+ statSync as statSync10,
17722
+ writeFileSync as writeFileSync12
17723
+ } from "fs";
17724
+ import { dirname as dirname16, join as join40 } from "path";
17548
17725
  function cfrRateControlArgs(locked) {
17549
17726
  const preset = getEncoderPreset(locked.quality ?? "standard", "mp4");
17550
17727
  if (locked.crf == null && locked.bitrate) {
@@ -17571,8 +17748,8 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
17571
17748
  }
17572
17749
  const format = plan2.dimensions.format;
17573
17750
  const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
17574
- if (!existsSync25(dirname16(outputPath))) {
17575
- mkdirSync15(dirname16(outputPath), { recursive: true });
17751
+ if (!existsSync26(dirname16(outputPath))) {
17752
+ mkdirSync16(dirname16(outputPath), { recursive: true });
17576
17753
  }
17577
17754
  const workDir = mkdtempSync8(`${outputPath}.assemble-work-`);
17578
17755
  const fps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
@@ -17594,8 +17771,8 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
17594
17771
  };
17595
17772
  try {
17596
17773
  const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
17597
- const finalPath = join39(workDir, `output.${format}`);
17598
- const audio = audioPath !== null && existsSync25(audioPath) ? audioPath : null;
17774
+ const finalPath = join40(workDir, `output.${format}`);
17775
+ const audio = audioPath !== null && existsSync26(audioPath) ? audioPath : null;
17599
17776
  await writeJoined(ctx, videoSources, audio, finalPath);
17600
17777
  renameSync8(finalPath, outputPath);
17601
17778
  log.info("[assemble] stage timings", {
@@ -17613,7 +17790,7 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
17613
17790
  });
17614
17791
  }
17615
17792
  }
17616
- const fileSize = existsSync25(outputPath) ? statSync10(outputPath).size : 0;
17793
+ const fileSize = existsSync26(outputPath) ? statSync10(outputPath).size : 0;
17617
17794
  return {
17618
17795
  outputPath,
17619
17796
  durationMs: Date.now() - start,
@@ -17622,25 +17799,25 @@ async function assemble(planDir, chunkPaths, audioPath, outputPath, options) {
17622
17799
  };
17623
17800
  }
17624
17801
  function readAssemblePlan(planDir, chunkPaths) {
17625
- const planJsonPath = join39(planDir, "plan.json");
17626
- const chunksJsonPath = join39(planDir, "meta", "chunks.json");
17627
- if (!existsSync25(planJsonPath)) {
17802
+ const planJsonPath = join40(planDir, "plan.json");
17803
+ const chunksJsonPath = join40(planDir, "meta", "chunks.json");
17804
+ if (!existsSync26(planJsonPath)) {
17628
17805
  throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
17629
17806
  }
17630
- const plan2 = JSON.parse(readFileSync12(planJsonPath, "utf-8"));
17807
+ const plan2 = JSON.parse(readFileSync13(planJsonPath, "utf-8"));
17631
17808
  readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
17632
17809
  validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
17633
- if (!existsSync25(chunksJsonPath)) {
17810
+ if (!existsSync26(chunksJsonPath)) {
17634
17811
  throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
17635
17812
  }
17636
- const chunks = JSON.parse(readFileSync12(chunksJsonPath, "utf-8"));
17813
+ const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
17637
17814
  if (chunkPaths.length !== chunks.length) {
17638
17815
  throw new Error(
17639
17816
  `[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
17640
17817
  );
17641
17818
  }
17642
17819
  for (const path of chunkPaths) {
17643
- if (!existsSync25(path)) {
17820
+ if (!existsSync26(path)) {
17644
17821
  throw new Error(`[assemble] chunk path does not exist: ${path}`);
17645
17822
  }
17646
17823
  }
@@ -17650,7 +17827,7 @@ function videoInputArgs(ctx, videoSources) {
17650
17827
  return videoSources.length === 1 ? ["-i", videoSources[0]] : concatDemuxerInput(videoSources, ctx.workDir, ctx.fpsArg);
17651
17828
  }
17652
17829
  async function reencodeCfr(ctx, chunkPaths, encoder) {
17653
- const cfrOutputPath = join39(ctx.workDir, `cfr.${ctx.format}`);
17830
+ const cfrOutputPath = join40(ctx.workDir, `cfr.${ctx.format}`);
17654
17831
  const cfrArgs = [
17655
17832
  ...videoInputArgs(ctx, chunkPaths),
17656
17833
  "-c:v",
@@ -17688,7 +17865,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
17688
17865
  );
17689
17866
  return;
17690
17867
  }
17691
- const paddedAudioPath = join39(ctx.workDir, "audio-padded.m4a");
17868
+ const paddedAudioPath = join40(ctx.workDir, "audio-padded.m4a");
17692
17869
  const padTrimResult = await ctx.timed(
17693
17870
  "audioMs",
17694
17871
  () => padOrTrimAudioToVideoFrameCount({
@@ -17728,9 +17905,9 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
17728
17905
  }
17729
17906
  }
17730
17907
  function concatDemuxerInput(chunkPaths, workDir, fpsArg) {
17731
- const listPath = join39(workDir, "concat-list.txt");
17908
+ const listPath = join40(workDir, "concat-list.txt");
17732
17909
  const body = chunkPaths.map((path) => `file '${path.replace(/'/g, "'\\''")}'`).join("\n");
17733
- writeFileSync11(listPath, `${body}
17910
+ writeFileSync12(listPath, `${body}
17734
17911
  `, "utf-8");
17735
17912
  return ["-r", fpsArg, "-f", "concat", "-safe", "0", "-i", listPath];
17736
17913
  }
@@ -17761,9 +17938,9 @@ function chunksAreStitchedMp4(planDir, cfr) {
17761
17938
  return true;
17762
17939
  }
17763
17940
  function chunksCarryAudio(planDir) {
17764
- const encoderJsonPath = join39(planDir, "meta", "encoder.json");
17765
- if (!existsSync25(encoderJsonPath)) return false;
17766
- return JSON.parse(readFileSync12(encoderJsonPath, "utf-8")).chunksCarryAudio === true;
17941
+ const encoderJsonPath = join40(planDir, "meta", "encoder.json");
17942
+ if (!existsSync26(encoderJsonPath)) return false;
17943
+ return JSON.parse(readFileSync13(encoderJsonPath, "utf-8")).chunksCarryAudio === true;
17767
17944
  }
17768
17945
  function readCfrEncoder(planDir, format) {
17769
17946
  if (format !== "mp4") {
@@ -17771,11 +17948,11 @@ function readCfrEncoder(planDir, format) {
17771
17948
  `[assemble] cfr=true is only supported for format="mp4" (got "${format}"). Stream-copy paths for webm and mov already produce exact avg_frame_rate; cfr re-encode is not needed.`
17772
17949
  );
17773
17950
  }
17774
- const encoderJsonPath = join39(planDir, "meta", "encoder.json");
17775
- if (!existsSync25(encoderJsonPath)) {
17951
+ const encoderJsonPath = join40(planDir, "meta", "encoder.json");
17952
+ if (!existsSync26(encoderJsonPath)) {
17776
17953
  throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
17777
17954
  }
17778
- const encoderJson = JSON.parse(readFileSync12(encoderJsonPath, "utf-8"));
17955
+ const encoderJson = JSON.parse(readFileSync13(encoderJsonPath, "utf-8"));
17779
17956
  if (encoderJson.encoder === "libx265-software") {
17780
17957
  throw new Error(
17781
17958
  `[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".`
@@ -17784,8 +17961,8 @@ function readCfrEncoder(planDir, format) {
17784
17961
  return encoderJson;
17785
17962
  }
17786
17963
  function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
17787
- if (existsSync25(outputPath)) rmSync14(outputPath, { recursive: true, force: true });
17788
- mkdirSync15(outputPath, { recursive: true });
17964
+ if (existsSync26(outputPath)) rmSync14(outputPath, { recursive: true, force: true });
17965
+ mkdirSync16(outputPath, { recursive: true });
17789
17966
  let globalIdx = 0;
17790
17967
  for (const chunkDir of chunkPaths) {
17791
17968
  if (!statSync10(chunkDir).isDirectory()) {
@@ -17798,8 +17975,8 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
17798
17975
  throw new Error(`[assemble] png-sequence chunk has no frames: ${chunkDir}`);
17799
17976
  }
17800
17977
  for (const frame of frames) {
17801
- const dst = join39(outputPath, formatExportFrameName(globalIdx, "png"));
17802
- cpSync3(join39(chunkDir, frame), dst);
17978
+ const dst = join40(outputPath, formatExportFrameName(globalIdx, "png"));
17979
+ cpSync3(join40(chunkDir, frame), dst);
17803
17980
  globalIdx += 1;
17804
17981
  }
17805
17982
  }
@@ -17808,14 +17985,14 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
17808
17985
  `[assemble] png-sequence frame count mismatch: merged ${globalIdx} frames vs plan.totalFrames=${totalFrames}. Using on-disk count.`
17809
17986
  );
17810
17987
  }
17811
- if (audioPath !== null && existsSync25(audioPath)) {
17812
- const sidecar = join39(outputPath, MIXED_AUDIO_FILENAME);
17988
+ if (audioPath !== null && existsSync26(audioPath)) {
17989
+ const sidecar = join40(outputPath, MIXED_AUDIO_FILENAME);
17813
17990
  cpSync3(audioPath, sidecar);
17814
17991
  }
17815
17992
  let fileSize = 0;
17816
17993
  for (const name of readdirSync10(outputPath)) {
17817
17994
  try {
17818
- fileSize += statSync10(join39(outputPath, name)).size;
17995
+ fileSize += statSync10(join40(outputPath, name)).size;
17819
17996
  } catch {
17820
17997
  }
17821
17998
  }
@@ -17829,105 +18006,8 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
17829
18006
 
17830
18007
  // ../producer/src/services/distributed/renderChunk.ts
17831
18008
  import { randomBytes } from "crypto";
17832
- import { existsSync as existsSync26, mkdirSync as mkdirSync16, readFileSync as readFileSync13, readdirSync as readdirSync11, rmSync as rmSync15, writeFileSync as writeFileSync12 } from "fs";
17833
- import { join as join40 } from "path";
17834
-
17835
- // ../producer/src/services/distributed/chunkAudio.ts
17836
- function audioCutForChunk(grid, chunks, index, fps) {
17837
- const firstPacketAt = (frame) => Math.min(
17838
- grid.packetCount,
17839
- Math.max(
17840
- 0,
17841
- Math.ceil(
17842
- (frame * fps.den * grid.sampleRate - grid.firstStart * fps.num) / (grid.packetSamples * fps.num)
17843
- )
17844
- )
17845
- );
17846
- const first = index === 0 ? 0 : firstPacketAt(chunks[index].startFrame);
17847
- const end = index === chunks.length - 1 ? grid.packetCount : firstPacketAt(chunks[index + 1].startFrame);
17848
- if (end <= first) return null;
17849
- const cutSeconds = (packet) => (grid.firstStart + packet * grid.packetSamples - grid.packetSamples / 2) / grid.sampleRate;
17850
- const seekSeconds = index === 0 ? null : cutSeconds(first);
17851
- const durationSeconds = end === grid.packetCount ? null : cutSeconds(end) - (seekSeconds ?? 0);
17852
- return { seekSeconds, durationSeconds, firstPacket: first, packetCount: end - first };
17853
- }
17854
- async function probeAacPacketGrid(audioPath) {
17855
- const probe = await runFfprobeJson([
17856
- "-v",
17857
- "error",
17858
- "-select_streams",
17859
- "a:0",
17860
- "-show_entries",
17861
- "stream=sample_rate,time_base,nb_frames:packet=pts,duration",
17862
- "-read_intervals",
17863
- "%+#1",
17864
- "-of",
17865
- "json",
17866
- "--",
17867
- audioPath
17868
- ]);
17869
- const stream = probe.streams?.[0] ?? {};
17870
- const packet = probe.packets?.[0] ?? {};
17871
- const sampleRate = Number(stream.sample_rate);
17872
- const [tbNum = NaN, tbDen = NaN] = String(stream.time_base).split("/").map(Number);
17873
- const toSamples = (ticks) => Number(ticks) * sampleRate * tbNum / tbDen;
17874
- const grid = {
17875
- sampleRate,
17876
- firstStart: toSamples(packet.pts),
17877
- packetSamples: toSamples(packet.duration),
17878
- packetCount: Number(stream.nb_frames)
17879
- };
17880
- const counts = [grid.sampleRate, grid.packetSamples, grid.packetCount];
17881
- if (!Number.isFinite(grid.firstStart) || !counts.every((n) => Number.isFinite(n) && n > 0)) {
17882
- throw new Error(`[chunkAudio] could not read the AAC packet grid: ${JSON.stringify(probe)}`);
17883
- }
17884
- return grid;
17885
- }
17886
- function chunkMuxArgs(input, cut) {
17887
- if (!cut) return ["-i", input.videoPath, "-map", "0:v:0"];
17888
- const seek = cut.seekSeconds === null ? [] : ["-ss", cut.seekSeconds.toFixed(6)];
17889
- const length = cut.durationSeconds === null ? [] : ["-t", cut.durationSeconds.toFixed(6)];
17890
- const exact = seek.length ? ["-copypriorss", "0"] : [];
17891
- return [
17892
- ...["-i", input.videoPath, ...seek, ...length, "-i", input.audioPath],
17893
- ...["-map", "0:v:0", "-map", "1:a:0", ...exact]
17894
- ];
17895
- }
17896
- async function muxChunkWithAudio(input) {
17897
- const grid = input.audioPath ? await probeAacPacketGrid(input.audioPath) : null;
17898
- const cut = grid ? audioCutForChunk(grid, input.chunks, input.chunkIndex, input.fps) : null;
17899
- const args = [...chunkMuxArgs(input, cut), "-c", "copy"];
17900
- appendRenderProvenanceArgs(args, input.outputPath);
17901
- args.push("-y", input.outputPath);
17902
- const result = await runFfmpeg(args, { signal: input.signal, timeout: input.timeoutMs });
17903
- if (!result.success) {
17904
- throw new Error(
17905
- `[chunkAudio] chunk mux failed: ${describeFfmpegFailure(result, input.timeoutMs)}`
17906
- );
17907
- }
17908
- const slice = input.chunks[input.chunkIndex];
17909
- const expected = { video: slice.endFrame - slice.startFrame };
17910
- if (cut) expected.audio = cut.packetCount;
17911
- const probe = await runFfprobeJson([
17912
- "-v",
17913
- "error",
17914
- "-count_packets",
17915
- "-show_entries",
17916
- "stream=codec_type,nb_read_packets",
17917
- "-of",
17918
- "json",
17919
- "--",
17920
- input.outputPath
17921
- ]);
17922
- for (const [type, count] of Object.entries(expected)) {
17923
- const packets = Number(probe.streams?.find((s) => s.codec_type === type)?.nb_read_packets ?? 0);
17924
- if (packets !== count) {
17925
- throw new Error(`[chunkAudio] ${type} packets in chunk ${packets}, expected ${count}`);
17926
- }
17927
- }
17928
- }
17929
-
17930
- // ../producer/src/services/distributed/renderChunk.ts
18009
+ import { existsSync as existsSync27, mkdirSync as mkdirSync17, readFileSync as readFileSync14, readdirSync as readdirSync11, rmSync as rmSync15, writeFileSync as writeFileSync13 } from "fs";
18010
+ import { join as join41 } from "path";
17931
18011
  var LEGACY_DISTRIBUTED_VP9_CPU_USED = 2;
17932
18012
  function validatePlanVideosForChunk(value) {
17933
18013
  try {
@@ -17992,10 +18072,10 @@ async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBe
17992
18072
  return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
17993
18073
  }
17994
18074
  function hashChunkOutput(outputPath, kind) {
17995
- if (kind === "file") return sha256Hex(readFileSync13(outputPath));
18075
+ if (kind === "file") return sha256Hex(readFileSync14(outputPath));
17996
18076
  const entries = readdirSync11(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
17997
18077
  const lines = entries.map(
17998
- (name) => `${name}\0${sha256Hex(readFileSync13(join40(outputPath, name)))}`
18078
+ (name) => `${name}\0${sha256Hex(readFileSync14(join41(outputPath, name)))}`
17999
18079
  );
18000
18080
  return sha256Hex(lines.join("\0"));
18001
18081
  }
@@ -18012,16 +18092,16 @@ function resolveLockedVp9CpuUsed(lockedEncoder) {
18012
18092
  async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18013
18093
  const start = Date.now();
18014
18094
  const log = defaultLogger;
18015
- const planJsonPath = join40(planDir, "plan.json");
18016
- const encoderJsonPath = join40(planDir, "meta", "encoder.json");
18017
- const chunksJsonPath = join40(planDir, "meta", "chunks.json");
18018
- if (!existsSync26(planJsonPath)) {
18095
+ const planJsonPath = join41(planDir, "plan.json");
18096
+ const encoderJsonPath = join41(planDir, "meta", "encoder.json");
18097
+ const chunksJsonPath = join41(planDir, "meta", "chunks.json");
18098
+ if (!existsSync27(planJsonPath)) {
18019
18099
  throw new RenderChunkValidationError(
18020
18100
  MISSING_PLAN_ARTIFACT,
18021
18101
  `[renderChunk] planDir is missing required artifact: ${planJsonPath}`
18022
18102
  );
18023
18103
  }
18024
- const plan2 = JSON.parse(readFileSync13(planJsonPath, "utf-8"));
18104
+ const plan2 = JSON.parse(readFileSync14(planJsonPath, "utf-8"));
18025
18105
  readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
18026
18106
  const planHashStarted = Date.now();
18027
18107
  const v2Materialization = readValidatedPlanV2Materialization(planDir, {
@@ -18031,20 +18111,20 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18031
18111
  const v2Manifest = v2Materialization?.manifest ?? null;
18032
18112
  const remoteSources = v2Materialization?.remoteSources ?? {};
18033
18113
  for (const required of [encoderJsonPath, chunksJsonPath]) {
18034
- if (!existsSync26(required)) {
18114
+ if (!existsSync27(required)) {
18035
18115
  throw new RenderChunkValidationError(
18036
18116
  MISSING_PLAN_ARTIFACT,
18037
18117
  `[renderChunk] planDir is missing required artifact: ${required}`
18038
18118
  );
18039
18119
  }
18040
18120
  }
18041
- const encoder = JSON.parse(readFileSync13(encoderJsonPath, "utf-8"));
18042
- const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
18043
- const videosJsonPath = join40(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18121
+ const encoder = JSON.parse(readFileSync14(encoderJsonPath, "utf-8"));
18122
+ const chunks = JSON.parse(readFileSync14(chunksJsonPath, "utf-8"));
18123
+ const videosJsonPath = join41(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
18044
18124
  let planVideos = null;
18045
- if (existsSync26(videosJsonPath)) {
18125
+ if (existsSync27(videosJsonPath)) {
18046
18126
  try {
18047
- planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync13(videosJsonPath, "utf-8")));
18127
+ planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync14(videosJsonPath, "utf-8")));
18048
18128
  } catch (err) {
18049
18129
  if (err instanceof RenderChunkValidationError) throw err;
18050
18130
  throw new RenderChunkValidationError(
@@ -18070,8 +18150,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18070
18150
  `[renderChunk] chunk ${chunkIndex} has non-positive frame count: ${framesInChunk}`
18071
18151
  );
18072
18152
  }
18073
- const compiledDir = join40(planDir, "compiled");
18074
- if (!existsSync26(compiledDir)) {
18153
+ const compiledDir = join41(planDir, "compiled");
18154
+ if (!existsSync27(compiledDir)) {
18075
18155
  throw new RenderChunkValidationError(
18076
18156
  MISSING_PLAN_ARTIFACT,
18077
18157
  `[renderChunk] planDir missing compiled/ directory: ${compiledDir}`
@@ -18127,9 +18207,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18127
18207
  forceScreenshotExplicitlyOptedOut: !encoder.forceScreenshot
18128
18208
  };
18129
18209
  const workDir = `${outputChunkPath}.work.${process.pid}.${randomBytes(4).toString("hex")}`;
18130
- mkdirSync16(workDir, { recursive: true });
18131
- const framesDir = join40(workDir, "captured-frames");
18132
- mkdirSync16(framesDir, { recursive: true });
18210
+ mkdirSync17(workDir, { recursive: true });
18211
+ const framesDir = join41(workDir, "captured-frames");
18212
+ mkdirSync17(framesDir, { recursive: true });
18133
18213
  const videoFramesDir = `${workDir}.video-frames`;
18134
18214
  const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
18135
18215
  planDir,
@@ -18175,7 +18255,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18175
18255
  dimensions: plan2.dimensions,
18176
18256
  encoder,
18177
18257
  videoCount: planVideos?.videos.length ?? 0,
18178
- html: readFileSync13(join40(compiledDir, "index.html"), "utf-8"),
18258
+ html: readFileSync14(join41(compiledDir, "index.html"), "utf-8"),
18179
18259
  // The plan ships a deferred source only to chunks that show it; don't wait on the rest.
18180
18260
  skipReadinessVideoIds: deferredVideoFrames.filter((video) => video.framePaths.size === 0 || remoteVideoIds.has(video.videoId)).map((video) => video.videoId),
18181
18261
  videoMetadataHints: collectVideoMetadataHints(remoteVideos)
@@ -18309,7 +18389,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18309
18389
  },
18310
18390
  resetForScreenshotRetry: () => {
18311
18391
  rmSync15(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
18312
- mkdirSync16(framesDir, { recursive: true });
18392
+ mkdirSync17(framesDir, { recursive: true });
18313
18393
  capturePerfs.length = 0;
18314
18394
  captureAttempts.length = 0;
18315
18395
  job.framesRendered = 0;
@@ -18340,10 +18420,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18340
18420
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
18341
18421
  const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
18342
18422
  if (isPngSequence) {
18343
- if (!existsSync26(outputChunkPath)) mkdirSync16(outputChunkPath, { recursive: true });
18423
+ if (!existsSync27(outputChunkPath)) mkdirSync17(outputChunkPath, { recursive: true });
18344
18424
  } else {
18345
- const outDir = join40(outputChunkPath, "..");
18346
- if (!existsSync26(outDir)) mkdirSync16(outDir, { recursive: true });
18425
+ const outDir = join41(outputChunkPath, "..");
18426
+ if (!existsSync27(outDir)) mkdirSync17(outDir, { recursive: true });
18347
18427
  }
18348
18428
  const encodeStarted = Date.now();
18349
18429
  await runEncodeStage({
@@ -18386,9 +18466,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18386
18466
  gopSize: framesInChunk
18387
18467
  });
18388
18468
  if (encoder.chunksCarryAudio) {
18469
+ const audioScratchPath = `${outputChunkPath}.audio.m4a`;
18389
18470
  await muxChunkWithAudio({
18390
18471
  videoPath: videoOnlyPath,
18391
- audioPath: resolvePlanAudioPath(planDir),
18472
+ audioPath: chunkAudioSource(planDir, chunkIndex, audioScratchPath),
18392
18473
  outputPath: outputChunkPath,
18393
18474
  chunks,
18394
18475
  chunkIndex,
@@ -18396,6 +18477,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18396
18477
  timeoutMs: cfg.ffmpegProcessTimeout
18397
18478
  });
18398
18479
  rmSync15(videoOnlyPath, { force: true });
18480
+ rmSync15(audioScratchPath, { force: true });
18399
18481
  }
18400
18482
  encodeStageMs = Date.now() - encodeStarted;
18401
18483
  } finally {
@@ -18434,7 +18516,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18434
18516
  producerVersion: plan2.producerVersion,
18435
18517
  ffmpegVersion
18436
18518
  };
18437
- writeFileSync12(perfPath, `${JSON.stringify(perfPayload, null, 2)}
18519
+ writeFileSync13(perfPath, `${JSON.stringify(perfPayload, null, 2)}
18438
18520
  `, "utf-8");
18439
18521
  try {
18440
18522
  rmSync15(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
@@ -18466,8 +18548,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
18466
18548
 
18467
18549
  // ../producer/src/services/distributed/planV2Execution.ts
18468
18550
  async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath) {
18469
- const workRoot = mkdtempSync9(join41(tmpdir5(), "hf-plan-v2-chunk-"));
18470
- const materializedPlanDir = join41(workRoot, "plan");
18551
+ const workRoot = mkdtempSync9(join42(tmpdir5(), "hf-plan-v2-chunk-"));
18552
+ const materializedPlanDir = join42(workRoot, "plan");
18471
18553
  try {
18472
18554
  materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
18473
18555
  return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath);
@@ -18476,8 +18558,8 @@ async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath) {
18476
18558
  }
18477
18559
  }
18478
18560
  async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
18479
- const workRoot = mkdtempSync9(join41(tmpdir5(), "hf-plan-v2-assembler-"));
18480
- const materializedPlanDir = join41(workRoot, "plan");
18561
+ const workRoot = mkdtempSync9(join42(tmpdir5(), "hf-plan-v2-assembler-"));
18562
+ const materializedPlanDir = join42(workRoot, "plan");
18481
18563
  try {
18482
18564
  const materialized = materializePlanV2Target(
18483
18565
  planV2Dir,
@@ -18497,9 +18579,9 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
18497
18579
  }
18498
18580
 
18499
18581
  // ../producer/src/services/distributed/projectHash.ts
18500
- import { readdirSync as readdirSync12, readFileSync as readFileSync14, realpathSync as realpathSync2, statSync as statSync11 } from "fs";
18582
+ import { readdirSync as readdirSync12, readFileSync as readFileSync15, realpathSync as realpathSync2, statSync as statSync11 } from "fs";
18501
18583
  import { createHash as createHash7 } from "crypto";
18502
- import { join as join42 } from "path";
18584
+ import { join as join43 } from "path";
18503
18585
 
18504
18586
  export {
18505
18587
  createConsoleLogger,