@hyperframes/producer 0.8.177 → 0.8.178
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/distributed.d.ts +3 -3
- package/dist/distributed.js +219 -68
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +57 -55
- package/dist/index.js +181 -53
- package/dist/public-server.js +29 -13
- package/dist/services/distributed/chunkFrames.d.ts +4 -0
- package/dist/services/distributed/plan.d.ts +8 -4
- package/dist/services/distributed/planV2.d.ts +15 -2
- package/dist/services/distributed/planV2Publisher.d.ts +1 -0
- package/dist/services/distributed/projectHash.d.ts +13 -8
- package/dist/services/htmlCompiler.d.ts +2 -0
- package/dist/services/render/stages/audioStage.d.ts +1 -0
- package/dist/services/render/stages/compileStage.d.ts +2 -0
- package/dist/services/render/stages/extractVideosStage.d.ts +1 -0
- package/dist/services/render/stages/freezePlan.d.ts +8 -0
- package/package.json +7 -7
- package/dist/services/audioExtractor.d.ts +0 -38
package/dist/distributed.d.ts
CHANGED
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
* No networking, no AWS SDK, no Temporal SDK — those live in adapter
|
|
30
30
|
* packages. This module is library code only.
|
|
31
31
|
*/
|
|
32
|
-
export { buildChunkSlices, measurePlanDirBytes, plan, rejectUnsupportedDistributedFormat, resolveChunkPlan, type DistributedRenderConfig, type PlanResult, DEFAULT_CHUNK_SIZE, DEFAULT_MAX_PARALLEL_CHUNKS, MIN_CHUNK_SIZE, PLAN_DIR_SIZE_LIMIT_BYTES, PLAN_PROJECT_DIR_SKIP_SEGMENTS, CHUNK_EXCEEDS_WORKER_TIMEOUT, ChunkExceedsWorkerTimeoutError, FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED, FormatNotSupportedInDistributedError, PLAN_TOO_LARGE, PlanTooLargeError, } from "./services/distributed/plan.js";
|
|
33
|
-
export { createPlanV2FromExecutionPlan, createPlanV2FromV1, getPlanV2ExecutionPlanHash, listPlanV2ArtifactsForTarget, materializePlanV2Target, planV2, planV2WithPublisher, publishPlanV2FromExecutionPlan, publishPlanV2FromV1, readPlanV2Manifest, validatePlanV2MaterializedTarget, PLAN_V2_INTEGRITY_UNRECOVERABLE, PLAN_V2_MATERIALIZATION_MARKER, PlanV2IntegrityError, type PlanV2Artifact, type PlanV2Limitations, type PlanV2Manifest, type PlanV2MaterializationResult, type PlanV2MaterializationTarget, type PlanV2Result, type PlanV2WithPublisherOptions, } from "./services/distributed/planV2.js";
|
|
32
|
+
export { buildChunkSlices, measurePlanDirBytes, plan, rejectUnsupportedDistributedFormat, resolveChunkPlan, type DistributedRenderConfig, type PlanResult, type RemoteProjectFile, DEFAULT_CHUNK_SIZE, DEFAULT_MAX_PARALLEL_CHUNKS, MIN_CHUNK_SIZE, PLAN_DIR_SIZE_LIMIT_BYTES, PLAN_PROJECT_DIR_SKIP_SEGMENTS, CHUNK_EXCEEDS_WORKER_TIMEOUT, ChunkExceedsWorkerTimeoutError, FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED, FormatNotSupportedInDistributedError, PLAN_TOO_LARGE, PlanTooLargeError, } from "./services/distributed/plan.js";
|
|
33
|
+
export { createPlanV2FromExecutionPlan, createPlanV2FromV1, getPlanV2ExecutionPlanHash, isPlanV2DeferredSourceArtifact, listPlanV2ArtifactsForTarget, materializePlanV2Target, planV2, planV2WithPublisher, publishPlanV2FromExecutionPlan, publishPlanV2FromV1, readPlanV2Manifest, validatePlanV2MaterializedTarget, PLAN_V2_INTEGRITY_UNRECOVERABLE, PLAN_V2_MATERIALIZATION_MARKER, PlanV2IntegrityError, type PlanV2Artifact, type PlanV2Limitations, type PlanV2Manifest, type PlanV2MaterializeOptions, type PlanV2MaterializationResult, type PlanV2MaterializationTarget, type PlanV2RemoteSources, type PlanV2Result, type PlanV2WithPublisherOptions, } from "./services/distributed/planV2.js";
|
|
34
34
|
export { LocalPlanV2ArtifactPublisher, type LocalPlanV2ArtifactPublisherOptions, type PlanV2ArtifactPublisher, type PlanV2PublishBlob, } from "./services/distributed/planV2Publisher.js";
|
|
35
35
|
export { assembleV2, renderChunkV2 } from "./services/distributed/planV2Execution.js";
|
|
36
36
|
export { applyRuntimeEnvSnapshot, readWebGlVendorInfoFromCanvas, renderChunk, type ChunkRenderer, type ChunkResult, type EffectiveChunkResult, FFMPEG_VERSION_MISMATCH, INVALID_VIDEO_METADATA, PLAN_HASH_MISMATCH, RenderChunkValidationError, } from "./services/distributed/renderChunk.js";
|
|
@@ -38,7 +38,7 @@ export { assemble, chunksAreStitchedMp4, type AssembleResult, } from "./services
|
|
|
38
38
|
export { muxChunkWithAudio } from "./services/distributed/chunkAudio.js";
|
|
39
39
|
export { buildStitchedHeader, readMp4Layout, type Mp4Layout, type ReadRange, } from "./services/distributed/mp4Stitch.js";
|
|
40
40
|
export { InvalidConfigError, type SerializableDistributedRenderConfig, validateDistributedRenderConfig, validateVariablesPayload, } from "./services/distributed/renderConfigValidation.js";
|
|
41
|
-
export { hashProjectDir } from "./services/distributed/projectHash.js";
|
|
41
|
+
export { hashProjectDir, listSiteFiles } from "./services/distributed/projectHash.js";
|
|
42
42
|
export { CURRENT_PLAN_PROTOCOL, DISTRIBUTED_RENDER_CAPABILITIES, getDistributedRenderCapabilities, PLAN_ARTIFACT_LAYOUT, PLAN_HASH_SCHEMA, PLAN_PROTOCOL_V1, PLAN_PROTOCOL_V2, PLAN_PROTOCOL_UNSUPPORTED, PLAN_SCHEMA_VERSION, PLAN_V2_ARTIFACT_LAYOUT, PLAN_V2_HASH_SCHEMA, PLAN_V2_SCHEMA_VERSION, PlanProtocolUnsupportedError, readPlanProtocol, readPlanProtocolV1, type DistributedRenderCapabilities, type PlanProtocolConsumerCapabilities, type PlanProtocolDescriptor, type PlanProtocolV1Descriptor, type PlanProtocolV2Descriptor, type SupportedPlanProtocolDescriptor, } from "./services/distributed/planProtocol.js";
|
|
43
43
|
export { PlanVideosMetadataError, type DistributedFormat } from "./services/distributed/shared.js";
|
|
44
44
|
export { isPlanAudioArtifactPath, PLAN_AUDIO_LEGACY_RELATIVE_PATH, PLAN_AUDIO_RELATIVE_PATH, resolvePlanAudioPath, } from "./services/distributed/shared.js";
|
package/dist/distributed.js
CHANGED
|
@@ -1148,6 +1148,9 @@ import {
|
|
|
1148
1148
|
extractAudioMetadata
|
|
1149
1149
|
} from "@hyperframes/engine";
|
|
1150
1150
|
|
|
1151
|
+
// src/services/render/renderDirOwner.ts
|
|
1152
|
+
import { RENDER_TEMP_DIR } from "@hyperframes/core/project-folders";
|
|
1153
|
+
|
|
1151
1154
|
// src/services/render/capturePlan.ts
|
|
1152
1155
|
function assertWorkerCount(workerCount) {
|
|
1153
1156
|
if (!Number.isInteger(workerCount) || workerCount < 1) {
|
|
@@ -2337,7 +2340,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
2337
2340
|
}
|
|
2338
2341
|
return false;
|
|
2339
2342
|
}
|
|
2340
|
-
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log) {
|
|
2343
|
+
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
|
|
2341
2344
|
let filePath = src;
|
|
2342
2345
|
if (isHttpUrl(src)) {
|
|
2343
2346
|
if (!existsSync6(downloadDir)) mkdirSync4(downloadDir, { recursive: true });
|
|
@@ -2351,8 +2354,9 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
2351
2354
|
} else {
|
|
2352
2355
|
filePath = decodedUrlPath(src);
|
|
2353
2356
|
if (!filePath.startsWith("/")) filePath = join5(baseDir, filePath);
|
|
2357
|
+
filePath = urlByAbsentLocalPath?.get(filePath) ?? filePath;
|
|
2354
2358
|
}
|
|
2355
|
-
if (!existsSync6(filePath)) {
|
|
2359
|
+
if (!isHttpUrl(filePath) && !existsSync6(filePath)) {
|
|
2356
2360
|
return { duration: null, resolvedPath: filePath };
|
|
2357
2361
|
}
|
|
2358
2362
|
const withSrcContext = (error) => {
|
|
@@ -2415,7 +2419,7 @@ function markInferredVariableMediaDurations(html, unresolvedMedia) {
|
|
|
2415
2419
|
}
|
|
2416
2420
|
return changed ? document2.toString() : html;
|
|
2417
2421
|
}
|
|
2418
|
-
async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
2422
|
+
async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocalPath) {
|
|
2419
2423
|
const { html: staticCompiled, unresolved } = compileTimingAttrs(html);
|
|
2420
2424
|
const mediaUnresolved = unresolved.filter(
|
|
2421
2425
|
(el) => (el.tagName === "video" || el.tagName === "audio") && el.src
|
|
@@ -2431,7 +2435,8 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
|
2431
2435
|
downloadDir,
|
|
2432
2436
|
el.tagName,
|
|
2433
2437
|
el.id,
|
|
2434
|
-
log
|
|
2438
|
+
log,
|
|
2439
|
+
urlByAbsentLocalPath
|
|
2435
2440
|
).then(({ duration }) => ({ id: el.id, duration }))
|
|
2436
2441
|
)
|
|
2437
2442
|
);
|
|
@@ -2473,7 +2478,7 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
|
2473
2478
|
compiledHtml = compiledHtml.replace(/(<audio\b[^>]*)\s+crossorigin(?:=["'][^"']*["'])?/gi, "$1");
|
|
2474
2479
|
return { html: compiledHtml, unresolvedCompositions };
|
|
2475
2480
|
}
|
|
2476
|
-
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set()) {
|
|
2481
|
+
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set(), urlByAbsentLocalPath) {
|
|
2477
2482
|
const subCompositions = /* @__PURE__ */ new Map();
|
|
2478
2483
|
const { document: document2 } = parseHTML4(html);
|
|
2479
2484
|
const compEls = document2.querySelectorAll("[data-composition-src]");
|
|
@@ -2499,13 +2504,16 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
2499
2504
|
const { html: compiledSub } = await compileHtmlFile(
|
|
2500
2505
|
item.rawSubHtml,
|
|
2501
2506
|
dirname4(item.filePath),
|
|
2502
|
-
downloadDir
|
|
2507
|
+
downloadDir,
|
|
2508
|
+
void 0,
|
|
2509
|
+
urlByAbsentLocalPath
|
|
2503
2510
|
);
|
|
2504
2511
|
const nested = await parseSubCompositions(
|
|
2505
2512
|
compiledSub,
|
|
2506
2513
|
projectDir,
|
|
2507
2514
|
downloadDir,
|
|
2508
|
-
item.nestedVisited
|
|
2515
|
+
item.nestedVisited,
|
|
2516
|
+
urlByAbsentLocalPath
|
|
2509
2517
|
);
|
|
2510
2518
|
return {
|
|
2511
2519
|
srcPath: item.srcPath,
|
|
@@ -3263,9 +3271,16 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
3263
3271
|
rawHtml,
|
|
3264
3272
|
projectDir,
|
|
3265
3273
|
downloadDir,
|
|
3266
|
-
options.log
|
|
3274
|
+
options.log,
|
|
3275
|
+
options.urlByAbsentLocalPath
|
|
3276
|
+
);
|
|
3277
|
+
const { subCompositions } = await parseSubCompositions(
|
|
3278
|
+
compiledHtml,
|
|
3279
|
+
projectDir,
|
|
3280
|
+
downloadDir,
|
|
3281
|
+
void 0,
|
|
3282
|
+
options.urlByAbsentLocalPath
|
|
3267
3283
|
);
|
|
3268
|
-
const { subCompositions } = await parseSubCompositions(compiledHtml, projectDir, downloadDir);
|
|
3269
3284
|
const fullHtml = ensureFullDocument(compiledHtml);
|
|
3270
3285
|
const inlinedHtml = inlineSubCompositions(
|
|
3271
3286
|
fullHtml,
|
|
@@ -3761,6 +3776,7 @@ async function runCompileStage(input) {
|
|
|
3761
3776
|
allowSystemFontCapture,
|
|
3762
3777
|
abortSignal,
|
|
3763
3778
|
variables: input.variables,
|
|
3779
|
+
urlByAbsentLocalPath: input.urlByAbsentLocalPath,
|
|
3764
3780
|
animatedGifCacheDir: cfg.extractCacheDir ? join6(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
3765
3781
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
|
|
3766
3782
|
});
|
|
@@ -4759,7 +4775,8 @@ async function runExtractVideosStage(input) {
|
|
|
4759
4775
|
abortSignal,
|
|
4760
4776
|
assertNotAborted,
|
|
4761
4777
|
materializeSymlinks,
|
|
4762
|
-
deferRangeExtraction
|
|
4778
|
+
deferRangeExtraction,
|
|
4779
|
+
urlByAbsentLocalPath
|
|
4763
4780
|
} = input;
|
|
4764
4781
|
const stage2Start = Date.now();
|
|
4765
4782
|
const extractionPolicy = resolveVideoExtractionPolicy();
|
|
@@ -4853,7 +4870,8 @@ async function runExtractVideosStage(input) {
|
|
|
4853
4870
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
4854
4871
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
4855
4872
|
deferRangeExtraction,
|
|
4856
|
-
contentKeyedDirs: [join8(compiledDir, REMOTE_MEDIA_SUBDIR)]
|
|
4873
|
+
contentKeyedDirs: [join8(compiledDir, REMOTE_MEDIA_SUBDIR)],
|
|
4874
|
+
urlByAbsentLocalPath
|
|
4857
4875
|
},
|
|
4858
4876
|
abortSignal,
|
|
4859
4877
|
cfg,
|
|
@@ -4936,6 +4954,7 @@ async function runAudioStage(input) {
|
|
|
4936
4954
|
audioGain,
|
|
4937
4955
|
audios,
|
|
4938
4956
|
masterFxChain,
|
|
4957
|
+
urlByAbsentLocalPath,
|
|
4939
4958
|
abortSignal,
|
|
4940
4959
|
assertNotAborted,
|
|
4941
4960
|
log
|
|
@@ -4955,7 +4974,7 @@ async function runAudioStage(input) {
|
|
|
4955
4974
|
audioOutputPath,
|
|
4956
4975
|
duration,
|
|
4957
4976
|
abortSignal,
|
|
4958
|
-
{ ffmpegProcessTimeout, audioGain, masterFxChain },
|
|
4977
|
+
{ ffmpegProcessTimeout, audioGain, masterFxChain, urlByAbsentLocalPath },
|
|
4959
4978
|
compiledDir
|
|
4960
4979
|
);
|
|
4961
4980
|
} catch (err) {
|
|
@@ -6833,6 +6852,8 @@ function buildChunkCaptureOptions(input) {
|
|
|
6833
6852
|
// The controller's snapshot, so the first capture sees the in-process renderer's `__hfVariables`.
|
|
6834
6853
|
variables: encoder.variables,
|
|
6835
6854
|
subTimelineWaitMemo: {},
|
|
6855
|
+
skipReadinessVideoIds: input.skipReadinessVideoIds,
|
|
6856
|
+
videoMetadataHints: input.videoMetadataHints,
|
|
6836
6857
|
...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {},
|
|
6837
6858
|
// A fixed warmup count keeps `beginFrameTimeTicks` host-independent.
|
|
6838
6859
|
lockWarmupTicks: true,
|
|
@@ -6841,6 +6862,19 @@ function buildChunkCaptureOptions(input) {
|
|
|
6841
6862
|
}
|
|
6842
6863
|
async function extractDeferredVideoFramesForChunk(input) {
|
|
6843
6864
|
const deferred = input.planVideos.extracted.filter((video) => video.deferredRange);
|
|
6865
|
+
const remoteSources = input.remoteSources ?? {};
|
|
6866
|
+
for (const path of Object.keys(remoteSources)) {
|
|
6867
|
+
if (!deferred.some((video) => video.deferredRange.sourcePath === path)) {
|
|
6868
|
+
throw new RenderChunkValidationError(
|
|
6869
|
+
MISSING_PLAN_ARTIFACT,
|
|
6870
|
+
`[renderChunk] ${path} is read by URL but no deferred video uses it`
|
|
6871
|
+
);
|
|
6872
|
+
}
|
|
6873
|
+
defaultLogger.info("[renderChunk] reading video by URL", {
|
|
6874
|
+
path,
|
|
6875
|
+
url: remoteSources[path].split("?")[0]
|
|
6876
|
+
});
|
|
6877
|
+
}
|
|
6844
6878
|
if (deferred.length === 0) return [];
|
|
6845
6879
|
const table = createFrameLookupTable2(
|
|
6846
6880
|
input.planVideos.videos,
|
|
@@ -6868,8 +6902,9 @@ async function extractDeferredVideoFramesForChunk(input) {
|
|
|
6868
6902
|
return { ...video, outputDir, framePaths: /* @__PURE__ */ new Map(), ownedByLookup: false };
|
|
6869
6903
|
}
|
|
6870
6904
|
const range = video.deferredRange;
|
|
6871
|
-
const
|
|
6872
|
-
|
|
6905
|
+
const remoteUrl = remoteSources[range.sourcePath];
|
|
6906
|
+
const sourcePath = remoteUrl ?? join16(input.planDir, ...range.sourcePath.split("/"));
|
|
6907
|
+
if (remoteUrl === void 0 && !existsSync12(sourcePath)) {
|
|
6873
6908
|
throw new RenderChunkValidationError(
|
|
6874
6909
|
MISSING_PLAN_ARTIFACT,
|
|
6875
6910
|
`[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
|
|
@@ -7209,6 +7244,16 @@ function collectPlanAssetShas(planDir, digests) {
|
|
|
7209
7244
|
}
|
|
7210
7245
|
return { compositionHtml, assets };
|
|
7211
7246
|
}
|
|
7247
|
+
var PLAN_REMOTE_FILES_RELATIVE_PATH = "meta/remote-files.json";
|
|
7248
|
+
function writePlanRemoteFiles(planDir, files) {
|
|
7249
|
+
if (files.length === 0) return;
|
|
7250
|
+
const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : 1);
|
|
7251
|
+
writeFileSync4(join18(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
|
|
7252
|
+
}
|
|
7253
|
+
function readPlanRemoteFiles(planDir) {
|
|
7254
|
+
const path = join18(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
7255
|
+
return existsSync13(path) ? JSON.parse(readFileSync8(path, "utf-8")) : [];
|
|
7256
|
+
}
|
|
7212
7257
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
7213
7258
|
const planJsonPath = join18(planDir, "plan.json");
|
|
7214
7259
|
const encoderJsonPath = join18(planDir, "meta", "encoder.json");
|
|
@@ -7794,7 +7839,7 @@ function createVideoSourceShipper(input) {
|
|
|
7794
7839
|
let result = null;
|
|
7795
7840
|
for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
|
|
7796
7841
|
const rel = pathInside(root, srcPath);
|
|
7797
|
-
if (rel !== null && existsSync14(join20(input.planDir, "compiled", rel))) {
|
|
7842
|
+
if (rel !== null && (existsSync14(join20(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
|
|
7798
7843
|
result = ["compiled", ...rel.split(sep2)].join("/");
|
|
7799
7844
|
break;
|
|
7800
7845
|
}
|
|
@@ -7872,8 +7917,17 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
7872
7917
|
logPlanStage(log, "copy", stageStarted);
|
|
7873
7918
|
const finalCompiledDir = join20(planDir, "compiled");
|
|
7874
7919
|
const needsAlpha = outputNeedsAlpha(config.format);
|
|
7920
|
+
const remoteFiles = Object.entries(options.remoteFiles ?? {});
|
|
7921
|
+
const remoteLocalPath = (rel) => join20(projectDir, ...rel.split("/"));
|
|
7922
|
+
const urlByAbsentLocalPath = new Map(
|
|
7923
|
+
remoteFiles.map(([rel, file]) => [remoteLocalPath(rel), file.url])
|
|
7924
|
+
);
|
|
7925
|
+
const localPathByUrl = new Map(
|
|
7926
|
+
remoteFiles.map(([rel, file]) => [file.url, remoteLocalPath(rel)])
|
|
7927
|
+
);
|
|
7875
7928
|
stageStarted = Date.now();
|
|
7876
7929
|
const compileResult = await runCompileStage({
|
|
7930
|
+
urlByAbsentLocalPath,
|
|
7877
7931
|
projectDir,
|
|
7878
7932
|
workDir,
|
|
7879
7933
|
htmlPath,
|
|
@@ -7961,7 +8015,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
7961
8015
|
abortSignal,
|
|
7962
8016
|
assertNotAborted,
|
|
7963
8017
|
materializeSymlinks: true,
|
|
7964
|
-
deferRangeExtraction: options.deferVideoExtraction
|
|
8018
|
+
deferRangeExtraction: options.deferVideoExtraction,
|
|
8019
|
+
urlByAbsentLocalPath
|
|
7965
8020
|
});
|
|
7966
8021
|
logPlanStage(log, "extract", stageStarted);
|
|
7967
8022
|
if (extractResult.failureToEnforce) throw extractResult.failureToEnforce;
|
|
@@ -7984,7 +8039,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
7984
8039
|
planDir,
|
|
7985
8040
|
projectDir,
|
|
7986
8041
|
workCompiledDir: compiledDir,
|
|
7987
|
-
workVideoFramesDir: stagedVideoFrames
|
|
8042
|
+
workVideoFramesDir: stagedVideoFrames,
|
|
8043
|
+
isRemote: (srcPath) => urlByAbsentLocalPath.has(srcPath)
|
|
7988
8044
|
});
|
|
7989
8045
|
const sourceRoots = [
|
|
7990
8046
|
[stagedVideoFrames, "video-frames"],
|
|
@@ -7994,17 +8050,28 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
7994
8050
|
const planVideosJson = buildPlanVideosJson({
|
|
7995
8051
|
videos: composition.videos,
|
|
7996
8052
|
compositionEnd: job.duration ?? Number.NaN,
|
|
7997
|
-
extracted: (extractResult.extractionResult?.extracted ?? []).map((ext) =>
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8053
|
+
extracted: (extractResult.extractionResult?.extracted ?? []).map((ext) => {
|
|
8054
|
+
const sourcePath = localPathByUrl.get(ext.srcPath) ?? ext.srcPath;
|
|
8055
|
+
return {
|
|
8056
|
+
videoId: ext.videoId,
|
|
8057
|
+
srcPath: stableSourcePath(sourceRoots, sourcePath),
|
|
8058
|
+
framePattern: ext.framePattern,
|
|
8059
|
+
fps: ext.fps,
|
|
8060
|
+
totalFrames: ext.totalFrames,
|
|
8061
|
+
metadata: ext.metadata,
|
|
8062
|
+
...ext.deferredRange ? { deferredRange: { ...ext.deferredRange, sourcePath: shipVideoSource(sourcePath) } } : {}
|
|
8063
|
+
};
|
|
8064
|
+
})
|
|
8006
8065
|
});
|
|
8007
8066
|
mkdirSync8(join20(planDir, "meta"), { recursive: true });
|
|
8067
|
+
writePlanRemoteFiles(
|
|
8068
|
+
planDir,
|
|
8069
|
+
remoteFiles.map(([rel, { sha256, sizeBytes }]) => ({
|
|
8070
|
+
path: `compiled/${rel}`,
|
|
8071
|
+
sha256,
|
|
8072
|
+
sizeBytes
|
|
8073
|
+
}))
|
|
8074
|
+
);
|
|
8008
8075
|
writeFileSync5(
|
|
8009
8076
|
join20(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
|
|
8010
8077
|
JSON.stringify(planVideosJson, null, 2),
|
|
@@ -8019,6 +8086,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
8019
8086
|
projectDir,
|
|
8020
8087
|
workDir,
|
|
8021
8088
|
compiledDir: finalCompiledDir,
|
|
8089
|
+
urlByAbsentLocalPath,
|
|
8022
8090
|
duration: audioSeconds,
|
|
8023
8091
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout,
|
|
8024
8092
|
audioGain: cfg.audioGain,
|
|
@@ -8428,17 +8496,20 @@ function artifactTargets(path, videoDependencies, chunksCarryAudio2) {
|
|
|
8428
8496
|
if (path === "plan.json" || path === "meta/chunks.json" || path === "meta/encoder.json") {
|
|
8429
8497
|
return { chunks: "all", assembler: true };
|
|
8430
8498
|
}
|
|
8431
|
-
if (path
|
|
8432
|
-
return { chunks: "all", assembler: false };
|
|
8433
|
-
}
|
|
8434
|
-
if (path.startsWith("video-frames/") || path.startsWith("video-sources/")) {
|
|
8499
|
+
if (path.startsWith("video-frames/") || path.startsWith("video-sources/") || path.startsWith("compiled/") && videoDependencies?.has(path)) {
|
|
8435
8500
|
return {
|
|
8436
8501
|
chunks: videoDependencies === null ? "all" : videoDependencies.get(path) ?? [],
|
|
8437
8502
|
assembler: false
|
|
8438
8503
|
};
|
|
8439
8504
|
}
|
|
8505
|
+
if (path === "meta/composition.json" || path === "meta/videos.json" || path.startsWith("compiled/")) {
|
|
8506
|
+
return { chunks: "all", assembler: false };
|
|
8507
|
+
}
|
|
8440
8508
|
return { chunks: "all", assembler: true };
|
|
8441
8509
|
}
|
|
8510
|
+
function isPlanV2DeferredSourceArtifact(artifact) {
|
|
8511
|
+
return artifact.path.startsWith("compiled/") && artifact.chunks !== "all";
|
|
8512
|
+
}
|
|
8442
8513
|
function listVideoFramePaths(executionPlanDir, videos) {
|
|
8443
8514
|
return videos.extracted.map((video) => {
|
|
8444
8515
|
const outputDir = resolveExtractedVideoOutputDir(executionPlanDir, video.videoId);
|
|
@@ -8601,14 +8672,17 @@ function publishBlobFor(absolutePath, digests) {
|
|
|
8601
8672
|
};
|
|
8602
8673
|
}
|
|
8603
8674
|
async function putBlobs(publisher, blobs) {
|
|
8675
|
+
await putTasks(blobs.map((blob) => () => publisher.putBlob(blob)));
|
|
8676
|
+
}
|
|
8677
|
+
async function putTasks(tasks) {
|
|
8604
8678
|
const uploadSlots = 32;
|
|
8605
8679
|
let next = 0;
|
|
8606
8680
|
let failure;
|
|
8607
8681
|
const upload = async () => {
|
|
8608
|
-
while (!failure && next <
|
|
8609
|
-
const
|
|
8682
|
+
while (!failure && next < tasks.length) {
|
|
8683
|
+
const task = tasks[next++];
|
|
8610
8684
|
try {
|
|
8611
|
-
await
|
|
8685
|
+
await task();
|
|
8612
8686
|
} catch (reason) {
|
|
8613
8687
|
failure ??= { reason };
|
|
8614
8688
|
}
|
|
@@ -8663,6 +8737,15 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
8663
8737
|
...targets
|
|
8664
8738
|
});
|
|
8665
8739
|
}
|
|
8740
|
+
const storedBlobs = /* @__PURE__ */ new Map();
|
|
8741
|
+
for (const remote of readPlanRemoteFiles(executionPlanDir)) {
|
|
8742
|
+
const { sha256, sizeBytes } = remote;
|
|
8743
|
+
if (!blobs.has(sha256)) storedBlobs.set(sha256, { sha256, sizeBytes });
|
|
8744
|
+
artifacts.push({
|
|
8745
|
+
...remote,
|
|
8746
|
+
...artifactTargets(remote.path, videoDependencyPlan.dependencies, chunksCarryAudio2)
|
|
8747
|
+
});
|
|
8748
|
+
}
|
|
8666
8749
|
const base = {
|
|
8667
8750
|
protocol: PLAN_PROTOCOL_V2,
|
|
8668
8751
|
// Retain the established manifest key byte-for-byte. It now acts as the
|
|
@@ -8684,7 +8767,8 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
8684
8767
|
...base,
|
|
8685
8768
|
planHash: computeManifestHash(base)
|
|
8686
8769
|
},
|
|
8687
|
-
blobs: [...blobs.values()]
|
|
8770
|
+
blobs: [...blobs.values()],
|
|
8771
|
+
storedBlobs: [...storedBlobs.values()]
|
|
8688
8772
|
};
|
|
8689
8773
|
}
|
|
8690
8774
|
function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
@@ -8692,6 +8776,9 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
8692
8776
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
8693
8777
|
}
|
|
8694
8778
|
const publication = buildPlanV2Publication(executionPlanDir);
|
|
8779
|
+
if (publication.storedBlobs.length > 0) {
|
|
8780
|
+
throw new PlanV2IntegrityError("a local plan v2 directory cannot reference remote files");
|
|
8781
|
+
}
|
|
8695
8782
|
mkdirSync10(dirname10(planV2Dir), { recursive: true });
|
|
8696
8783
|
const tempDir = mkdtempSync4(join23(dirname10(planV2Dir), ".plan-v2-build-"));
|
|
8697
8784
|
try {
|
|
@@ -8721,8 +8808,14 @@ async function publishExecutionPlan(executionPlanDir, publisher, { digests, log
|
|
|
8721
8808
|
let stageStarted = Date.now();
|
|
8722
8809
|
const publication = buildPlanV2Publication(executionPlanDir, digests);
|
|
8723
8810
|
logPlanStage(log, "publication", stageStarted);
|
|
8811
|
+
if (publication.storedBlobs.length > 0 && !publisher.referenceStoredBlob) {
|
|
8812
|
+
throw new PlanV2IntegrityError("this publisher cannot reference remote files");
|
|
8813
|
+
}
|
|
8724
8814
|
stageStarted = Date.now();
|
|
8725
|
-
await
|
|
8815
|
+
await putTasks([
|
|
8816
|
+
...publication.blobs.map((blob) => () => publisher.putBlob(blob)),
|
|
8817
|
+
...publication.storedBlobs.map((blob) => () => publisher.referenceStoredBlob(blob))
|
|
8818
|
+
]);
|
|
8726
8819
|
logPlanStage(log, "upload", stageStarted);
|
|
8727
8820
|
stageStarted = Date.now();
|
|
8728
8821
|
await publisher.commitManifest(canonicalJsonStringify(publication.manifest));
|
|
@@ -8746,6 +8839,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
8746
8839
|
await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
|
|
8747
8840
|
executionPlanSizeLimitBytes: Number.MAX_SAFE_INTEGER,
|
|
8748
8841
|
deferVideoExtraction: true,
|
|
8842
|
+
remoteFiles: options.remoteFiles,
|
|
8749
8843
|
digests,
|
|
8750
8844
|
// artifactTargets ships every compiled/ file to all chunks, so each is in the manifest.
|
|
8751
8845
|
whileAudioMixes: () => putBlobs(
|
|
@@ -8948,14 +9042,16 @@ function verifyBlob(planV2Dir, artifact) {
|
|
|
8948
9042
|
}
|
|
8949
9043
|
return sourcePath;
|
|
8950
9044
|
}
|
|
8951
|
-
function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
9045
|
+
function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
|
|
8952
9046
|
if (existsSync16(destinationDir)) {
|
|
8953
9047
|
throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
|
|
8954
9048
|
}
|
|
8955
9049
|
const manifest = readPlanV2Manifest(planV2Dir);
|
|
8956
9050
|
const artifacts = listPlanV2ArtifactsForTarget(manifest, target);
|
|
9051
|
+
const remoteSources = options.remoteSources ?? {};
|
|
9052
|
+
assertRemoteSources(remoteSources, artifacts);
|
|
8957
9053
|
const audioArtifact = artifacts.find((artifact) => isPlanAudioArtifactPath(artifact.path));
|
|
8958
|
-
const verified = artifacts.map((artifact) => ({
|
|
9054
|
+
const verified = artifacts.filter((artifact) => !Object.hasOwn(remoteSources, artifact.path)).map((artifact) => ({
|
|
8959
9055
|
artifact,
|
|
8960
9056
|
sourcePath: verifyBlob(planV2Dir, artifact)
|
|
8961
9057
|
}));
|
|
@@ -8970,7 +9066,11 @@ function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
|
8970
9066
|
if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
|
|
8971
9067
|
writeFileSync7(
|
|
8972
9068
|
join23(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
|
|
8973
|
-
canonicalJsonStringify({
|
|
9069
|
+
canonicalJsonStringify({
|
|
9070
|
+
manifest,
|
|
9071
|
+
target,
|
|
9072
|
+
...Object.keys(remoteSources).length > 0 ? { remoteSources } : {}
|
|
9073
|
+
}),
|
|
8974
9074
|
"utf-8"
|
|
8975
9075
|
);
|
|
8976
9076
|
renameSync5(tempDir, destinationDir);
|
|
@@ -8991,7 +9091,22 @@ function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
|
8991
9091
|
audioPath: target.role === "assembler" ? audioArtifact ? join23(destinationDir, audioArtifact.path) : null : null
|
|
8992
9092
|
};
|
|
8993
9093
|
}
|
|
9094
|
+
function assertRemoteSources(remoteSources, artifacts) {
|
|
9095
|
+
if (!isRecord3(remoteSources)) throw new PlanV2IntegrityError("remoteSources must be an object");
|
|
9096
|
+
for (const [path, url] of Object.entries(remoteSources)) {
|
|
9097
|
+
const artifact = artifacts.find((candidate) => candidate.path === path);
|
|
9098
|
+
if (!artifact || !isPlanV2DeferredSourceArtifact(artifact)) {
|
|
9099
|
+
throw new PlanV2IntegrityError(`remote source is not a selected deferred video: ${path}`);
|
|
9100
|
+
}
|
|
9101
|
+
if (typeof url !== "string" || !/^https?:\/\//.test(url)) {
|
|
9102
|
+
throw new PlanV2IntegrityError(`remote source for ${path} must be an http(s) URL`);
|
|
9103
|
+
}
|
|
9104
|
+
}
|
|
9105
|
+
}
|
|
8994
9106
|
function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
9107
|
+
return readValidatedPlanV2Materialization(planDir, expectedTarget)?.manifest ?? null;
|
|
9108
|
+
}
|
|
9109
|
+
function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
8995
9110
|
const markerPath = join23(planDir, PLAN_V2_MATERIALIZATION_MARKER);
|
|
8996
9111
|
if (!existsSync16(markerPath)) return null;
|
|
8997
9112
|
const marker = readJsonFile(markerPath, "materialization marker");
|
|
@@ -9006,7 +9121,10 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
9006
9121
|
}
|
|
9007
9122
|
const manifest = parsePlanV2Manifest(marker.manifest);
|
|
9008
9123
|
const required = listPlanV2ArtifactsForTarget(manifest, expectedTarget);
|
|
9124
|
+
const remoteSources = marker.remoteSources ?? {};
|
|
9125
|
+
assertRemoteSources(remoteSources, required);
|
|
9009
9126
|
for (const artifact of required) {
|
|
9127
|
+
if (Object.hasOwn(remoteSources, artifact.path)) continue;
|
|
9010
9128
|
const path = join23(planDir, ...artifact.path.split("/"));
|
|
9011
9129
|
if (!existsSync16(path) || statSync6(path).size !== artifact.sizeBytes) {
|
|
9012
9130
|
throw new PlanV2IntegrityError(
|
|
@@ -9017,7 +9135,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
9017
9135
|
throw new PlanV2IntegrityError(`materialized artifact hash mismatch: ${artifact.path}`);
|
|
9018
9136
|
}
|
|
9019
9137
|
}
|
|
9020
|
-
return manifest;
|
|
9138
|
+
return { manifest, remoteSources };
|
|
9021
9139
|
}
|
|
9022
9140
|
|
|
9023
9141
|
// src/services/distributed/planV2Execution.ts
|
|
@@ -9892,10 +10010,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
9892
10010
|
const plan2 = JSON.parse(readFileSync11(planJsonPath, "utf-8"));
|
|
9893
10011
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
|
|
9894
10012
|
const planHashStarted = Date.now();
|
|
9895
|
-
const
|
|
10013
|
+
const v2Materialization = readValidatedPlanV2Materialization(planDir, {
|
|
9896
10014
|
role: "chunk",
|
|
9897
10015
|
chunkIndex
|
|
9898
10016
|
});
|
|
10017
|
+
const v2Manifest = v2Materialization?.manifest ?? null;
|
|
10018
|
+
const remoteSources = v2Materialization?.remoteSources ?? {};
|
|
9899
10019
|
for (const required of [encoderJsonPath, chunksJsonPath]) {
|
|
9900
10020
|
if (!existsSync18(required)) {
|
|
9901
10021
|
throw new RenderChunkValidationError(
|
|
@@ -9997,6 +10117,24 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
9997
10117
|
const framesDir = join26(workDir, "captured-frames");
|
|
9998
10118
|
mkdirSync13(framesDir, { recursive: true });
|
|
9999
10119
|
const videoFramesDir = `${workDir}.video-frames`;
|
|
10120
|
+
const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
|
|
10121
|
+
planDir,
|
|
10122
|
+
planVideos,
|
|
10123
|
+
frames: Array.from(
|
|
10124
|
+
{ length: slice.endFrame - slice.startFrame },
|
|
10125
|
+
(_, i) => slice.startFrame + i
|
|
10126
|
+
),
|
|
10127
|
+
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
10128
|
+
outputRoot: videoFramesDir,
|
|
10129
|
+
cfg,
|
|
10130
|
+
remoteSources
|
|
10131
|
+
}) : [];
|
|
10132
|
+
const remoteVideoIds = new Set(
|
|
10133
|
+
planVideos?.extracted.filter(
|
|
10134
|
+
(video) => video.deferredRange && Object.hasOwn(remoteSources, video.deferredRange.sourcePath)
|
|
10135
|
+
).map((video) => video.videoId)
|
|
10136
|
+
);
|
|
10137
|
+
const remoteVideos = deferredVideoFrames.filter((video) => remoteVideoIds.has(video.videoId));
|
|
10000
10138
|
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? createFrameLookupTable5(
|
|
10001
10139
|
planVideos.videos,
|
|
10002
10140
|
[
|
|
@@ -10005,17 +10143,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10005
10143
|
planVideos.extracted.filter((video) => !video.deferredRange),
|
|
10006
10144
|
v2Manifest === null ? "dense-v1" : "sparse-v2"
|
|
10007
10145
|
),
|
|
10008
|
-
...
|
|
10009
|
-
planDir,
|
|
10010
|
-
planVideos,
|
|
10011
|
-
frames: Array.from(
|
|
10012
|
-
{ length: slice.endFrame - slice.startFrame },
|
|
10013
|
-
(_, i) => slice.startFrame + i
|
|
10014
|
-
),
|
|
10015
|
-
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
10016
|
-
outputRoot: videoFramesDir,
|
|
10017
|
-
cfg
|
|
10018
|
-
})
|
|
10146
|
+
...deferredVideoFrames
|
|
10019
10147
|
],
|
|
10020
10148
|
resolveRenderFpsConfig(job.config.fps).value
|
|
10021
10149
|
) : null;
|
|
@@ -10033,7 +10161,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
10033
10161
|
dimensions: plan2.dimensions,
|
|
10034
10162
|
encoder,
|
|
10035
10163
|
videoCount: planVideos?.videos.length ?? 0,
|
|
10036
|
-
html: readFileSync11(join26(compiledDir, "index.html"), "utf-8")
|
|
10164
|
+
html: readFileSync11(join26(compiledDir, "index.html"), "utf-8"),
|
|
10165
|
+
// The plan ships a deferred source only to chunks that show it; don't wait on the rest.
|
|
10166
|
+
skipReadinessVideoIds: deferredVideoFrames.filter((video) => video.framePaths.size === 0 || remoteVideoIds.has(video.videoId)).map((video) => video.videoId),
|
|
10167
|
+
videoMetadataHints: collectVideoMetadataHints(remoteVideos)
|
|
10037
10168
|
});
|
|
10038
10169
|
const chunkWorkerCount = calculateOptimalWorkers2(framesInChunk, void 0, cfg);
|
|
10039
10170
|
let session = null;
|
|
@@ -10594,28 +10725,46 @@ function validateIntDimension(field, value) {
|
|
|
10594
10725
|
}
|
|
10595
10726
|
|
|
10596
10727
|
// src/services/distributed/projectHash.ts
|
|
10597
|
-
import { readdirSync as readdirSync10, readFileSync as readFileSync12 } from "node:fs";
|
|
10728
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync12, realpathSync as realpathSync2, statSync as statSync8 } from "node:fs";
|
|
10598
10729
|
import { createHash as createHash6 } from "node:crypto";
|
|
10599
|
-
import { join as join28
|
|
10600
|
-
|
|
10601
|
-
|
|
10730
|
+
import { join as join28 } from "node:path";
|
|
10731
|
+
var SITE_ID_SCHEME = "site-v2\0";
|
|
10732
|
+
function listSiteFiles(projectDir) {
|
|
10602
10733
|
const files = [];
|
|
10603
|
-
|
|
10604
|
-
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
const
|
|
10609
|
-
|
|
10610
|
-
|
|
10734
|
+
const ancestors = /* @__PURE__ */ new Set([realpathSync2(projectDir)]);
|
|
10735
|
+
function walk(dir, rel) {
|
|
10736
|
+
for (const name of readdirSync10(dir).sort()) {
|
|
10737
|
+
if (rel === "" && PLAN_PROJECT_DIR_SKIP_SEGMENTS.has(name)) continue;
|
|
10738
|
+
const full = join28(dir, name);
|
|
10739
|
+
const relPath = rel === "" ? name : `${rel}/${name}`;
|
|
10740
|
+
let stat;
|
|
10741
|
+
try {
|
|
10742
|
+
stat = statSync8(full);
|
|
10743
|
+
} catch (err) {
|
|
10744
|
+
throw new Error(`[listSiteFiles] cannot read ${relPath} (dangling symlink?)`, {
|
|
10745
|
+
cause: err
|
|
10746
|
+
});
|
|
10747
|
+
}
|
|
10748
|
+
if (stat.isFile()) {
|
|
10749
|
+
files.push(relPath);
|
|
10750
|
+
} else if (stat.isDirectory()) {
|
|
10751
|
+
const real = realpathSync2(full);
|
|
10752
|
+
if (ancestors.has(real)) continue;
|
|
10753
|
+
ancestors.add(real);
|
|
10754
|
+
walk(full, relPath);
|
|
10755
|
+
ancestors.delete(real);
|
|
10756
|
+
}
|
|
10611
10757
|
}
|
|
10612
10758
|
}
|
|
10613
|
-
walk(projectDir,
|
|
10614
|
-
|
|
10615
|
-
|
|
10759
|
+
walk(projectDir, "");
|
|
10760
|
+
return files;
|
|
10761
|
+
}
|
|
10762
|
+
function hashProjectDir(projectDir, files = listSiteFiles(projectDir)) {
|
|
10763
|
+
const hash = createHash6("sha256").update(SITE_ID_SCHEME);
|
|
10764
|
+
for (const rel of files) {
|
|
10616
10765
|
hash.update(rel);
|
|
10617
10766
|
hash.update("\0");
|
|
10618
|
-
hash.update(readFileSync12(
|
|
10767
|
+
hash.update(readFileSync12(join28(projectDir, rel)));
|
|
10619
10768
|
}
|
|
10620
10769
|
return hash.digest("hex").slice(0, 16);
|
|
10621
10770
|
}
|
|
@@ -10670,7 +10819,9 @@ export {
|
|
|
10670
10819
|
getPlanV2ExecutionPlanHash,
|
|
10671
10820
|
hashProjectDir,
|
|
10672
10821
|
isPlanAudioArtifactPath,
|
|
10822
|
+
isPlanV2DeferredSourceArtifact,
|
|
10673
10823
|
listPlanV2ArtifactsForTarget,
|
|
10824
|
+
listSiteFiles,
|
|
10674
10825
|
materializePlanV2Target,
|
|
10675
10826
|
measurePlanDirBytes,
|
|
10676
10827
|
muxChunkWithAudio,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1.0",
|
|
3
3
|
"buildId": "dev",
|
|
4
|
-
"sha256": "
|
|
4
|
+
"sha256": "5da92c0e03e3d47df672f23707742908357e94fb7b39ed0f518d4c37400f433d",
|
|
5
5
|
"artifacts": {
|
|
6
6
|
"iife": "hyperframe.runtime.iife.js",
|
|
7
7
|
"esm": "hyperframe.runtime.mjs"
|