@hyperframes/gcp-cloud-run 0.8.231 → 0.9.0

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/README.md CHANGED
@@ -30,8 +30,8 @@ GCS bucket ←→ Cloud Run service (plan / renderChunk / assemble)
30
30
  Cloud Workflows (Plan → parallel RenderChunk → Assemble)
31
31
  ```
32
32
 
33
- - **Plan** downloads the project tarball and publishes either a legacy v1
34
- planDir tarball or a v2 manifest plus content-addressed artifacts.
33
+ - **Plan** downloads the project tarball and publishes a Plan v2 manifest plus
34
+ content-addressed artifacts.
35
35
  - **RenderChunk** runs in a parallel `for` loop in the workflow, fanned out
36
36
  up to the plan's chunk count. Each invocation renders one chunk and uploads
37
37
  it.
@@ -43,11 +43,11 @@ The workflow accumulates each step's small result body and returns
43
43
  `{ Plan, Chunks, Assemble }` so `getRenderProgress` can read frame totals and
44
44
  per-step durations on success.
45
45
 
46
- ### Plan transport selection
46
+ ### Plan transport
47
47
 
48
- Plan v2 is the default for new renders. When `planProtocol` is omitted,
49
- `renderToCloudRun` sends an explicit `PlanProtocol: "v2"` so the SDK and
50
- the deployed workflow agree:
48
+ Plan v2 is the only supported distributed plan transport.
49
+ `renderToCloudRun` sends `PlanProtocol: "v2"` without requiring a protocol
50
+ option:
51
51
 
52
52
  ```ts
53
53
  await renderToCloudRun({
@@ -56,22 +56,17 @@ await renderToCloudRun({
56
56
  ```
57
57
 
58
58
  V2 uses separate manifest and content-addressed artifact locators throughout
59
- the workflow. Unknown protocols and integrity failures fail closed; a render
60
- never mixes v1 and v2 artifacts.
61
-
62
- The monolithic v1 transport remains available as deprecated compatibility by
63
- passing `planProtocol: "v1"` explicitly.
59
+ the workflow. Unknown protocols and integrity failures fail closed.
64
60
 
65
61
  #### Upgrade order
66
62
 
67
63
  Redeploy the Cloud Run image and Cloud Workflows definition from the same new
68
64
  package version before upgrading an application that calls
69
65
  `renderToCloudRun`. Pause new renders and drain active workflow executions
70
- during the infrastructure update. Older workflows can default omission to v1
71
- or lack the v2 branch, while the new SDK sends explicit v2. If infrastructure
72
- cannot be redeployed first, keep the previous SDK version or pass
73
- `planProtocol: "v1"` explicitly until the Terraform/workflow redeploy is
74
- complete.
66
+ during the infrastructure update. Older workflows may lack the Plan v2-only
67
+ request and locator contract. If infrastructure cannot be redeployed first,
68
+ keep the application on its previous package version until the workflow
69
+ redeploy is complete.
75
70
 
76
71
  ## Chrome runtime
77
72
 
package/dist/events.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * dispatches on the `Action` field of the JSON request body. Each action
7
7
  * maps 1:1 onto one of the three OSS distributed primitives:
8
8
  *
9
- * "plan" → `plan(projectDir, config, planDir)` (Activity A)
9
+ * "plan" → `planV2WithPublisher(...)` (Activity A)
10
10
  * "renderChunk" → `renderChunk(planDir, chunkIndex, output)` (Activity B)
11
11
  * "assemble" → `assemble(planDir, chunkPaths, audio, out)` (Activity C)
12
12
  *
@@ -25,8 +25,6 @@ import type { DistributedFormat, SerializableDistributedRenderConfig } from "@hy
25
25
  export type { SerializableDistributedRenderConfig } from "@hyperframes/producer/distributed";
26
26
  /** Discriminator for the three roles the one Cloud Run image fulfills. */
27
27
  export type CloudRunAction = "plan" | "renderChunk" | "assemble";
28
- /** Transport protocol selected for one complete distributed render. */
29
- export type CloudRunPlanProtocol = "v1" | "v2";
30
28
  /**
31
29
  * Top-level shape of any request body the handler may receive.
32
30
  *
@@ -41,7 +39,7 @@ export type CloudRunEvent = PlanEvent | RenderChunkEvent | AssembleEvent | {
41
39
  Input: CloudRunEvent;
42
40
  };
43
41
  /** Activity A: produce a planDir, upload to GCS. */
44
- interface PlanEventBase {
42
+ export interface PlanEvent {
45
43
  Action: "plan";
46
44
  /** GCS URI pointing at a `tar -czf`-archived project directory (`gs://bucket/key.tar.gz`). */
47
45
  ProjectGcsUri: string;
@@ -49,22 +47,13 @@ interface PlanEventBase {
49
47
  PlanOutputGcsPrefix: string;
50
48
  /** `DistributedRenderConfig` minus runtime-only fields (logger, abortSignal). */
51
49
  Config: SerializableDistributedRenderConfig;
52
- }
53
- /**
54
- * Legacy plan transport. Callers must select it explicitly.
55
- *
56
- * @deprecated Use {@link PlanV2Event} for new integrations.
57
- */
58
- export interface PlanV1Event extends PlanEventBase {
59
- PlanProtocol: "v1";
60
- }
61
- /** Default content-addressed v2 plan transport. */
62
- export interface PlanV2Event extends PlanEventBase {
50
+ /** Optional for direct handler callers; the SDK and orchestrator send it explicitly. */
63
51
  PlanProtocol?: "v2";
64
52
  }
65
- export type PlanEvent = PlanV1Event | PlanV2Event;
53
+ /** Compatibility alias for the only supported plan event. */
54
+ export type PlanV2Event = PlanEvent;
66
55
  /** Activity B: fetch planDir, render one chunk, upload result. */
67
- interface RenderChunkEventBase {
56
+ export interface RenderChunkEvent {
68
57
  Action: "renderChunk";
69
58
  /**
70
59
  * `PlanResult.planHash` from the Plan invocation. The handler verifies
@@ -80,32 +69,14 @@ interface RenderChunkEventBase {
80
69
  ChunkOutputGcsPrefix: string;
81
70
  /** Output container format from the plan's encoder.json; drives file vs frame-dir handling. */
82
71
  Format: DistributedFormat;
83
- }
84
- /**
85
- * Legacy chunk event. Callers must select it explicitly.
86
- *
87
- * @deprecated Use {@link RenderChunkV2Event} for new integrations.
88
- */
89
- export interface RenderChunkV1Event extends RenderChunkEventBase {
90
- PlanProtocol: "v1";
91
- /** GCS URI of the v1 plan tar produced by a PlanEvent invocation. */
92
- PlanGcsUri: string;
93
- PlanV2ManifestGcsUri?: never;
94
- PlanV2ArtifactGcsPrefix?: never;
95
- }
96
- /**
97
- * V2 chunk event. It intentionally cannot carry `PlanGcsUri`: the manifest
98
- * describes the exact content-addressed artifacts needed by this chunk.
99
- */
100
- export interface RenderChunkV2Event extends RenderChunkEventBase {
101
72
  PlanProtocol?: "v2";
102
73
  PlanV2ManifestGcsUri: string;
103
74
  PlanV2ArtifactGcsPrefix: string;
104
- PlanGcsUri?: never;
105
75
  }
106
- export type RenderChunkEvent = RenderChunkV1Event | RenderChunkV2Event;
76
+ /** Compatibility alias for the only supported chunk event. */
77
+ export type RenderChunkV2Event = RenderChunkEvent;
107
78
  /** Activity C: fetch planDir + all chunks + audio, assemble, upload final. */
108
- interface AssembleEventBase {
79
+ export interface AssembleEvent {
109
80
  Action: "assemble";
110
81
  /** GCS URIs of every chunk, ordered by chunk index. Length must equal `chunkCount`. */
111
82
  ChunkGcsUris: string[];
@@ -123,34 +94,17 @@ interface AssembleEventBase {
123
94
  * unset preserves current `-c copy` behavior.
124
95
  */
125
96
  Cfr?: boolean;
126
- }
127
- /**
128
- * Legacy assemble event. Callers must select it explicitly.
129
- *
130
- * @deprecated Use {@link AssembleV2Event} for new integrations.
131
- */
132
- export interface AssembleV1Event extends AssembleEventBase {
133
- PlanProtocol: "v1";
134
- /** GCS URI of the v1 plan tar produced by a PlanEvent invocation. */
135
- PlanGcsUri: string;
136
- /** Legacy standalone audio locator; `null` when audio is embedded in the v1 plan tar. */
137
- AudioGcsUri: string | null;
138
- PlanV2ManifestGcsUri?: never;
139
- PlanV2ArtifactGcsPrefix?: never;
140
- }
141
- /** V2 assemble event, scoped to manifest-declared assembler artifacts. */
142
- export interface AssembleV2Event extends AssembleEventBase {
143
97
  PlanProtocol?: "v2";
144
98
  PlanV2ManifestGcsUri: string;
145
99
  PlanV2ArtifactGcsPrefix: string;
146
100
  PlanHash: string;
147
- PlanGcsUri?: never;
148
101
  /** V2 audio is a manifest artifact materialized only for the assembler. */
149
102
  AudioGcsUri: null;
150
103
  }
151
- export type AssembleEvent = AssembleV1Event | AssembleV2Event;
104
+ /** Compatibility alias for the only supported assemble event. */
105
+ export type AssembleV2Event = AssembleEvent;
152
106
  /** Result of a `plan` invocation. Carries enough to size the Map(N) state. */
153
- interface PlanResultBodyBase {
107
+ export interface PlanResultBody {
154
108
  Action: "plan";
155
109
  PlanHash: string;
156
110
  ChunkCount: number;
@@ -160,31 +114,16 @@ interface PlanResultBodyBase {
160
114
  Height: number;
161
115
  Format: DistributedFormat;
162
116
  HasAudio: boolean;
163
- AudioGcsUri: string | null;
164
117
  FfmpegVersion: string;
165
118
  ProducerVersion: string;
166
119
  DurationMs: number;
167
- }
168
- /**
169
- * Existing v1 result. Kept unchanged for wire compatibility.
170
- *
171
- * @deprecated New integrations should consume {@link PlanV2ResultBody}.
172
- */
173
- export interface PlanV1ResultBody extends PlanResultBodyBase {
174
- PlanGcsUri: string;
175
- PlanProtocol?: never;
176
- PlanV2ManifestGcsUri?: never;
177
- PlanV2ArtifactGcsPrefix?: never;
178
- }
179
- /** V2 result. The two v2 locators are never aliases for `PlanGcsUri`. */
180
- export interface PlanV2ResultBody extends PlanResultBodyBase {
181
120
  PlanProtocol: "v2";
182
121
  PlanV2ManifestGcsUri: string;
183
122
  PlanV2ArtifactGcsPrefix: string;
184
- PlanGcsUri?: never;
185
123
  AudioGcsUri: null;
186
124
  }
187
- export type PlanResultBody = PlanV1ResultBody | PlanV2ResultBody;
125
+ /** Compatibility alias for the only supported plan result. */
126
+ export type PlanV2ResultBody = PlanResultBody;
188
127
  /** Result of a `renderChunk` invocation. Sized ≤200 bytes. */
189
128
  export interface RenderChunkResultBody {
190
129
  Action: "renderChunk";
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  * separately.
19
19
  */
20
20
  export { createApp, dispatch, type HandlerDeps, startServer, unwrapEvent } from "./server.js";
21
- export { type AssembleEvent, type AssembleV1Event, type AssembleV2Event, type AssembleResultBody, type CloudRunAction, type CloudRunEvent, type CloudRunPlanProtocol, type CloudRunResult, type PlanEvent, type PlanResultBody, type PlanV1Event, type PlanV1ResultBody, type PlanV2Event, type PlanV2ResultBody, type RenderChunkEvent, type RenderChunkResultBody, type RenderChunkV1Event, type RenderChunkV2Event, type SerializableDistributedRenderConfig, } from "./events.js";
21
+ export { type AssembleEvent, type AssembleV2Event, type AssembleResultBody, type CloudRunAction, type CloudRunEvent, type CloudRunResult, type PlanEvent, type PlanResultBody, type PlanV2Event, type PlanV2ResultBody, type RenderChunkEvent, type RenderChunkResultBody, type RenderChunkV2Event, type SerializableDistributedRenderConfig, } from "./events.js";
22
22
  export { ChromeBinaryUnavailableError, resolveChromeExecutablePath } from "./chromium.js";
23
23
  export { downloadGcsObjectToFile, downloadGcsObjectToFileVerified, formatGcsUri, type GcsLocation, parseGcsUri, sha256File, tarDirectory, untarDirectory, uploadContentAddressedFileToGcs, uploadFileToGcs, } from "./gcsTransport.js";
24
24
  export { GcsPlanV2ArtifactPublisher, type GcsPlanV2ArtifactPublisherOptions, } from "./gcsPlanV2Publisher.js";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/server.ts
2
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync2, readFileSync, rmSync as rmSync3, statSync as statSync3 } from "node:fs";
2
+ import { mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "node:fs";
3
3
  import { tmpdir as tmpdir2 } from "node:os";
4
4
  import { basename, extname, join as join2 } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -10,9 +10,7 @@ import {
10
10
  assemble,
11
11
  listPlanV2ArtifactsForTarget,
12
12
  materializePlanV2Target,
13
- plan,
14
13
  isPlanAudioArtifactPath,
15
- PLAN_AUDIO_RELATIVE_PATH,
16
14
  resolvePlanAudioPath,
17
15
  planV2WithPublisher,
18
16
  readPlanV2Manifest,
@@ -383,27 +381,26 @@ async function dispatch(event, deps) {
383
381
  function validatePlanProtocolShape(event) {
384
382
  const raw = event;
385
383
  const protocol = raw.PlanProtocol;
386
- if (protocol !== void 0 && protocol !== "v1" && protocol !== "v2") {
384
+ if (protocol !== void 0 && protocol !== "v2") {
387
385
  const error = new Error(
388
- `[handler] unsupported PlanProtocol ${JSON.stringify(protocol)}; expected "v1", "v2", or absent`
386
+ `[handler] unsupported PlanProtocol ${JSON.stringify(protocol)}; expected "v2" or absent`
389
387
  );
390
388
  error.name = "PLAN_PROTOCOL_UNSUPPORTED";
391
389
  throw error;
392
390
  }
393
391
  if (event.Action === "plan") return;
394
- const effectiveProtocol = protocol ?? "v2";
395
392
  const hasV1Locator = typeof raw.PlanGcsUri === "string";
396
393
  const hasV2Manifest = typeof raw.PlanV2ManifestGcsUri === "string";
397
394
  const hasV2Prefix = typeof raw.PlanV2ArtifactGcsPrefix === "string";
398
- const valid = effectiveProtocol === "v2" ? !hasV1Locator && hasV2Manifest && hasV2Prefix : hasV1Locator && !hasV2Manifest && !hasV2Prefix;
395
+ const valid = !hasV1Locator && hasV2Manifest && hasV2Prefix;
399
396
  if (!valid) {
400
397
  const error = new Error(
401
- `[handler] ${effectiveProtocol} ${event.Action} event has mixed or missing plan locators`
398
+ `[handler] v2 ${event.Action} event has mixed or missing plan locators`
402
399
  );
403
400
  error.name = "PLAN_PROTOCOL_UNSUPPORTED";
404
401
  throw error;
405
402
  }
406
- if (effectiveProtocol === "v2" && event.Action === "assemble" && event.AudioGcsUri !== null) {
403
+ if (event.Action === "assemble" && event.AudioGcsUri !== null) {
407
404
  const error = new Error("[handler] v2 assemble audio must be materialized from the manifest");
408
405
  error.name = "PLAN_PROTOCOL_UNSUPPORTED";
409
406
  throw error;
@@ -412,7 +409,7 @@ function validatePlanProtocolShape(event) {
412
409
  function normalizeTerminalErrorName(error) {
413
410
  if (!error || typeof error !== "object") return;
414
411
  const candidate = error;
415
- if (candidate.code === "PLAN_PROTOCOL_UNSUPPORTED" || candidate.code === "PLAN_TOO_LARGE" || candidate.code === "PLAN_V2_INTEGRITY_UNRECOVERABLE" || candidate.code === "FONT_FETCH_FAILED" || candidate.code === "FONT_FETCH_UNAVAILABLE" || candidate.code === "VIDEO_SOURCE_UNRENDERABLE" || candidate.code === "VIDEO_EXTRACTION_FAILED" || candidate.code === "INVALID_VIDEO_METADATA" || candidate.code === "CHUNK_EXCEEDS_WORKER_TIMEOUT") {
412
+ if (candidate.code === "PLAN_PROTOCOL_UNSUPPORTED" || candidate.code === "PLAN_V2_INTEGRITY_UNRECOVERABLE" || candidate.code === "FONT_FETCH_FAILED" || candidate.code === "FONT_FETCH_UNAVAILABLE" || candidate.code === "VIDEO_SOURCE_UNRENDERABLE" || candidate.code === "VIDEO_EXTRACTION_FAILED" || candidate.code === "INVALID_VIDEO_METADATA" || candidate.code === "CHUNK_EXCEEDS_WORKER_TIMEOUT") {
416
413
  candidate.name = candidate.code;
417
414
  }
418
415
  }
@@ -459,14 +456,14 @@ function summarizeEvent(event) {
459
456
  case "renderChunk":
460
457
  return {
461
458
  planProtocol: event.PlanProtocol ?? "v2",
462
- ...event.PlanProtocol !== "v1" ? { planV2ManifestGcsUri: event.PlanV2ManifestGcsUri } : { planGcsUri: event.PlanGcsUri },
459
+ planV2ManifestGcsUri: event.PlanV2ManifestGcsUri,
463
460
  chunkIndex: event.ChunkIndex,
464
461
  format: event.Format
465
462
  };
466
463
  case "assemble":
467
464
  return {
468
465
  planProtocol: event.PlanProtocol ?? "v2",
469
- ...event.PlanProtocol !== "v1" ? { planV2ManifestGcsUri: event.PlanV2ManifestGcsUri } : { planGcsUri: event.PlanGcsUri },
466
+ planV2ManifestGcsUri: event.PlanV2ManifestGcsUri,
470
467
  chunkCount: event.ChunkGcsUris.length,
471
468
  hasAudio: event.AudioGcsUri !== null,
472
469
  outputGcsUri: event.OutputGcsUri,
@@ -480,51 +477,6 @@ function primeChrome(deps) {
480
477
  process.env.PRODUCER_HEADLESS_SHELL_PATH = resolveChromeExecutablePath();
481
478
  }
482
479
  async function handlePlan(event, deps) {
483
- if (event.PlanProtocol !== "v1") {
484
- return handlePlanV2(event, deps);
485
- }
486
- const started = Date.now();
487
- const storage = deps?.storage ?? getStorage();
488
- const primitive = deps?.primitives?.plan ?? plan;
489
- primeChrome(deps);
490
- const work = mkdtempSync2(join2(deps?.tmpRoot ?? tmpdir2(), "hf-cr-plan-"));
491
- const projectArchive = join2(work, "project.tar.gz");
492
- const projectDir = join2(work, "project");
493
- const planDir = join2(work, "plan");
494
- try {
495
- await downloadGcsObjectToFile(storage, event.ProjectGcsUri, projectArchive);
496
- await untarDirectory(projectArchive, projectDir);
497
- const config = {
498
- ...event.Config
499
- };
500
- const result = await primitive(projectDir, config, planDir);
501
- const planTar = join2(work, "plan.tar.gz");
502
- await tarDirectory(planDir, planTar);
503
- const planTarUri = `${trimTrailingSlash2(event.PlanOutputGcsPrefix)}/plan.tar.gz`;
504
- const audioPath = join2(planDir, PLAN_AUDIO_RELATIVE_PATH);
505
- const hasAudio = existsSync3(audioPath) && statSync3(audioPath).size > 0;
506
- await uploadFileToGcs(storage, planTar, planTarUri, "application/gzip");
507
- return {
508
- Action: "plan",
509
- PlanGcsUri: planTarUri,
510
- PlanHash: result.planHash,
511
- ChunkCount: result.chunkCount,
512
- TotalFrames: result.totalFrames,
513
- Fps: result.fps,
514
- Width: result.width,
515
- Height: result.height,
516
- Format: result.format,
517
- HasAudio: hasAudio,
518
- AudioGcsUri: null,
519
- FfmpegVersion: result.ffmpegVersion,
520
- ProducerVersion: result.producerVersion,
521
- DurationMs: Date.now() - started
522
- };
523
- } finally {
524
- cleanupDir(work);
525
- }
526
- }
527
- async function handlePlanV2(event, deps) {
528
480
  const started = Date.now();
529
481
  const storage = deps?.storage ?? getStorage();
530
482
  const primitive = deps?.primitives?.planV2WithPublisher ?? planV2WithPublisher;
@@ -566,45 +518,6 @@ async function handlePlanV2(event, deps) {
566
518
  }
567
519
  }
568
520
  async function handleRenderChunk(event, deps) {
569
- if (event.PlanProtocol !== "v1") {
570
- return handleRenderChunkV2(event, deps);
571
- }
572
- const started = Date.now();
573
- const storage = deps?.storage ?? getStorage();
574
- const primitive = deps?.primitives?.renderChunk ?? renderChunk;
575
- primeChrome(deps);
576
- const work = mkdtempSync2(join2(deps?.tmpRoot ?? tmpdir2(), "hf-cr-chunk-"));
577
- const planTar = join2(work, "plan.tar.gz");
578
- const planDir = join2(work, "plan");
579
- try {
580
- await downloadGcsObjectToFile(storage, event.PlanGcsUri, planTar);
581
- await untarDirectory(planTar, planDir);
582
- verifyPlanHash(planDir, event.PlanHash);
583
- const chunkOutputBase = join2(
584
- work,
585
- event.Format === "png-sequence" ? `chunk-${pad(event.ChunkIndex)}` : `chunk-${pad(event.ChunkIndex)}${formatExtension(event.Format)}`
586
- );
587
- const result = await primitive(planDir, event.ChunkIndex, chunkOutputBase);
588
- const chunkUri = await uploadChunkOutput(
589
- storage,
590
- result,
591
- event.ChunkOutputGcsPrefix,
592
- event.ChunkIndex
593
- );
594
- return {
595
- Action: "renderChunk",
596
- ChunkGcsUri: chunkUri,
597
- ChunkIndex: event.ChunkIndex,
598
- Sha256: result.sha256,
599
- FramesEncoded: result.framesEncoded,
600
- CaptureMode: result.captureMode,
601
- DurationMs: Date.now() - started
602
- };
603
- } finally {
604
- cleanupDir(work);
605
- }
606
- }
607
- async function handleRenderChunkV2(event, deps) {
608
521
  const started = Date.now();
609
522
  const storage = deps?.storage ?? getStorage();
610
523
  const primitive = deps?.primitives?.renderChunk ?? renderChunk;
@@ -656,50 +569,6 @@ async function uploadChunkOutput(storage, result, prefix, chunkIndex) {
656
569
  return uri;
657
570
  }
658
571
  async function handleAssemble(event, deps) {
659
- if (event.PlanProtocol !== "v1") {
660
- return handleAssembleV2(event, deps);
661
- }
662
- const started = Date.now();
663
- const storage = deps?.storage ?? getStorage();
664
- const primitive = deps?.primitives?.assemble ?? assemble;
665
- const work = mkdtempSync2(join2(deps?.tmpRoot ?? tmpdir2(), "hf-cr-assemble-"));
666
- const planTar = join2(work, "plan.tar.gz");
667
- const planDir = join2(work, "plan");
668
- try {
669
- await downloadGcsObjectToFile(storage, event.PlanGcsUri, planTar);
670
- await untarDirectory(planTar, planDir);
671
- const chunkPaths = await downloadChunkObjects(storage, event.ChunkGcsUris, work, event.Format);
672
- let audioPath = null;
673
- const planAudio = resolvePlanAudioPath(planDir) ?? join2(planDir, PLAN_AUDIO_RELATIVE_PATH);
674
- if (existsSync3(planAudio) && statSync3(planAudio).size > 0) {
675
- audioPath = planAudio;
676
- } else if (event.AudioGcsUri) {
677
- audioPath = planAudio;
678
- await downloadGcsObjectToFile(storage, event.AudioGcsUri, audioPath);
679
- }
680
- const finalOutput = event.Format === "png-sequence" ? join2(work, "output-frames") : join2(work, `output${formatExtension(event.Format)}`);
681
- const result = await primitive(planDir, chunkPaths, audioPath, finalOutput, {
682
- cfr: event.Cfr === true
683
- });
684
- if (event.Format === "png-sequence") {
685
- const tarball = `${finalOutput}.tar.gz`;
686
- await tarDirectory(finalOutput, tarball);
687
- await uploadFileToGcs(storage, tarball, event.OutputGcsUri, "application/gzip");
688
- } else {
689
- await uploadFileToGcs(storage, finalOutput, event.OutputGcsUri);
690
- }
691
- return {
692
- Action: "assemble",
693
- OutputGcsUri: event.OutputGcsUri,
694
- FramesEncoded: result.framesEncoded,
695
- FileSize: result.fileSize,
696
- DurationMs: Date.now() - started
697
- };
698
- } finally {
699
- cleanupDir(work);
700
- }
701
- }
702
- async function handleAssembleV2(event, deps) {
703
572
  const started = Date.now();
704
573
  const storage = deps?.storage ?? getStorage();
705
574
  const primitive = deps?.primitives?.assemble ?? assemble;
@@ -818,10 +687,15 @@ function getEventGcsUris(event) {
818
687
  case "plan":
819
688
  return [event.ProjectGcsUri, event.PlanOutputGcsPrefix];
820
689
  case "renderChunk":
821
- return event.PlanProtocol !== "v1" ? [event.PlanV2ManifestGcsUri, event.PlanV2ArtifactGcsPrefix, event.ChunkOutputGcsPrefix] : [event.PlanGcsUri, event.ChunkOutputGcsPrefix];
690
+ return [
691
+ event.PlanV2ManifestGcsUri,
692
+ event.PlanV2ArtifactGcsPrefix,
693
+ event.ChunkOutputGcsPrefix
694
+ ];
822
695
  case "assemble":
823
696
  return [
824
- ...event.PlanProtocol !== "v1" ? [event.PlanV2ManifestGcsUri, event.PlanV2ArtifactGcsPrefix] : [event.PlanGcsUri],
697
+ event.PlanV2ManifestGcsUri,
698
+ event.PlanV2ArtifactGcsPrefix,
825
699
  ...event.ChunkGcsUris,
826
700
  event.OutputGcsUri,
827
701
  event.AudioGcsUri
@@ -866,26 +740,6 @@ function cleanupDir(dir) {
866
740
  } catch {
867
741
  }
868
742
  }
869
- function verifyPlanHash(planDir, expected) {
870
- const planJsonPath = join2(planDir, "plan.json");
871
- let parsed;
872
- try {
873
- parsed = JSON.parse(readFileSync(planJsonPath, "utf-8"));
874
- } catch (err) {
875
- const msg = err instanceof Error ? err.message : String(err);
876
- const error = new Error(`PLAN_HASH_MISMATCH: failed to read ${planJsonPath}: ${msg}`);
877
- error.name = "PLAN_HASH_MISMATCH";
878
- throw error;
879
- }
880
- const actual = parsed.planHash;
881
- if (typeof actual !== "string" || actual !== expected) {
882
- const error = new Error(
883
- `PLAN_HASH_MISMATCH: event PlanHash=${expected} did not match plan.json planHash=${String(actual)}`
884
- );
885
- error.name = "PLAN_HASH_MISMATCH";
886
- throw error;
887
- }
888
- }
889
743
  var NON_RETRYABLE_ERROR_NAMES = /* @__PURE__ */ new Set([
890
744
  // Handler-boundary guards.
891
745
  "GCS_URI_NOT_ALLOWED",
@@ -903,13 +757,11 @@ var NON_RETRYABLE_ERROR_NAMES = /* @__PURE__ */ new Set([
903
757
  // non-retryable list.
904
758
  "FormatNotSupportedInDistributedError",
905
759
  "NotMediaPayloadError",
906
- "PlanTooLargeError",
907
760
  "PlanProtocolUnsupportedError",
908
761
  "PlanV2IntegrityError",
909
762
  "RenderChunkValidationError",
910
763
  "FFMPEG_VERSION_MISMATCH",
911
764
  "FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED",
912
- "PLAN_TOO_LARGE",
913
765
  "BROWSER_GPU_NOT_SOFTWARE",
914
766
  "FONT_FETCH_FAILED",
915
767
  "ChromeBinaryUnavailableError"
@@ -948,13 +800,13 @@ if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
948
800
  }
949
801
 
950
802
  // src/sdk/deploySite.ts
951
- import { mkdtempSync as mkdtempSync3, rmSync as rmSync4, statSync as statSync4 } from "node:fs";
803
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync4, statSync as statSync3 } from "node:fs";
952
804
  import { tmpdir as tmpdir3 } from "node:os";
953
805
  import { join as join3 } from "node:path";
954
806
  import { Storage as Storage2 } from "@google-cloud/storage";
955
807
  import { hashProjectDir, listSiteFiles } from "@hyperframes/producer/distributed";
956
808
  async function deploySite(opts) {
957
- if (!statSync4(opts.projectDir).isDirectory()) {
809
+ if (!statSync3(opts.projectDir).isDirectory()) {
958
810
  throw new Error(`[deploySite] projectDir is not a directory: ${opts.projectDir}`);
959
811
  }
960
812
  const files = listSiteFiles(opts.projectDir);
@@ -978,7 +830,7 @@ async function deploySite(opts) {
978
830
  try {
979
831
  const tarball = join3(workdir, "project.tar.gz");
980
832
  await tarDirectory(opts.projectDir, tarball, files);
981
- const size = statSync4(tarball).size;
833
+ const size = statSync3(tarball).size;
982
834
  await uploadFileToGcs(storage, tarball, projectGcsUri, "application/gzip");
983
835
  return {
984
836
  siteId,
@@ -1042,7 +894,17 @@ function validateWorkflowsInputSize(input) {
1042
894
  }
1043
895
 
1044
896
  // src/sdk/renderToCloudRun.ts
897
+ function rejectUnsupportedLegacyPlanProtocol(opts) {
898
+ const planProtocol = Reflect.get(opts, "planProtocol");
899
+ if (planProtocol === void 0 || planProtocol === "v2") return;
900
+ const error = new Error(
901
+ `[renderToCloudRun] planProtocol is no longer configurable; expected "v2" or absent, got ${JSON.stringify(planProtocol)}`
902
+ );
903
+ error.name = "PLAN_PROTOCOL_UNSUPPORTED";
904
+ throw error;
905
+ }
1045
906
  async function renderToCloudRun(opts) {
907
+ rejectUnsupportedLegacyPlanProtocol(opts);
1046
908
  validateDistributedRenderConfig(opts.config);
1047
909
  if (!opts.bucketName) throw new Error("[renderToCloudRun] bucketName is required");
1048
910
  if (!opts.projectId) throw new Error("[renderToCloudRun] projectId is required");
@@ -1077,7 +939,7 @@ async function renderToCloudRun(opts) {
1077
939
  OutputGcsUri: outputGcsUri,
1078
940
  ServiceUrl: opts.serviceUrl,
1079
941
  Config: opts.config,
1080
- PlanProtocol: opts.planProtocol ?? "v2"
942
+ PlanProtocol: "v2"
1081
943
  };
1082
944
  validateWorkflowsInputSize(argument);
1083
945
  const executions = opts.executions ?? await defaultExecutionsClient();
package/dist/sdk/index.js CHANGED
@@ -161,7 +161,17 @@ function validateWorkflowsInputSize(input) {
161
161
  }
162
162
 
163
163
  // src/sdk/renderToCloudRun.ts
164
+ function rejectUnsupportedLegacyPlanProtocol(opts) {
165
+ const planProtocol = Reflect.get(opts, "planProtocol");
166
+ if (planProtocol === void 0 || planProtocol === "v2") return;
167
+ const error = new Error(
168
+ `[renderToCloudRun] planProtocol is no longer configurable; expected "v2" or absent, got ${JSON.stringify(planProtocol)}`
169
+ );
170
+ error.name = "PLAN_PROTOCOL_UNSUPPORTED";
171
+ throw error;
172
+ }
164
173
  async function renderToCloudRun(opts) {
174
+ rejectUnsupportedLegacyPlanProtocol(opts);
165
175
  validateDistributedRenderConfig(opts.config);
166
176
  if (!opts.bucketName) throw new Error("[renderToCloudRun] bucketName is required");
167
177
  if (!opts.projectId) throw new Error("[renderToCloudRun] projectId is required");
@@ -196,7 +206,7 @@ async function renderToCloudRun(opts) {
196
206
  OutputGcsUri: outputGcsUri,
197
207
  ServiceUrl: opts.serviceUrl,
198
208
  Config: opts.config,
199
- PlanProtocol: opts.planProtocol ?? "v2"
209
+ PlanProtocol: "v2"
200
210
  };
201
211
  validateWorkflowsInputSize(argument);
202
212
  const executions = opts.executions ?? await defaultExecutionsClient();
@@ -24,7 +24,7 @@
24
24
  * separately for polling.
25
25
  */
26
26
  import type { Storage } from "@google-cloud/storage";
27
- import type { CloudRunPlanProtocol, SerializableDistributedRenderConfig } from "../events.js";
27
+ import type { SerializableDistributedRenderConfig } from "../events.js";
28
28
  import { type SiteHandle } from "./deploySite.js";
29
29
  /**
30
30
  * Minimal surface of `@google-cloud/workflows`' `ExecutionsClient` that
@@ -50,11 +50,6 @@ export interface RenderToCloudRunOptions {
50
50
  siteHandle?: SiteHandle;
51
51
  /** Validated `SerializableDistributedRenderConfig` (no logger / abortSignal). */
52
52
  config: SerializableDistributedRenderConfig;
53
- /**
54
- * Distributed plan transport. Defaults to `"v2"`. Select `"v1"`
55
- * explicitly only for deprecated compatibility with the monolithic plan.
56
- */
57
- planProtocol?: CloudRunPlanProtocol;
58
53
  /** GCS bucket from the Terraform output (`render_bucket_name`). */
59
54
  bucketName: string;
60
55
  /** GCP project id hosting the workflow. */
package/dist/server.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import { Storage } from "@google-cloud/storage";
19
19
  import { Hono } from "hono";
20
- import { assemble, type ChunkRenderer, plan, planV2WithPublisher } from "@hyperframes/producer/distributed";
20
+ import { assemble, type ChunkRenderer, planV2WithPublisher } from "@hyperframes/producer/distributed";
21
21
  import type { AssembleEvent, CloudRunEvent, CloudRunResult, PlanEvent, RenderChunkEvent } from "./events.js";
22
22
  /**
23
23
  * Optional injection points used by the handler's unit tests. Production
@@ -27,7 +27,6 @@ import type { AssembleEvent, CloudRunEvent, CloudRunResult, PlanEvent, RenderChu
27
27
  export interface HandlerDeps {
28
28
  storage?: Storage;
29
29
  primitives?: {
30
- plan: typeof plan;
31
30
  planV2WithPublisher?: typeof planV2WithPublisher;
32
31
  renderChunk: ChunkRenderer;
33
32
  assemble: typeof assemble;