@buildcores/render-client 1.0.7 → 1.0.8

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/api.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { RenderBuildRequest, AvailablePartsResponse } from "./types";
1
+ import { RenderBuildRequest, AvailablePartsResponse, ApiConfig } from "./types";
2
2
  declare const API_BASE_URL = "https://www.renderapi.buildcores.com";
3
3
  export declare const API_ENDPOINTS: {
4
4
  readonly RENDER_BUILD_EXPERIMENTAL: "/render-build-experimental";
5
+ readonly RENDER_BUILD: "/render-build";
5
6
  readonly AVAILABLE_PARTS: "/available-parts";
6
7
  };
7
8
  export interface RenderBuildResponse {
@@ -18,6 +19,21 @@ export interface RenderBuildResponse {
18
19
  format?: string;
19
20
  };
20
21
  }
22
+ export interface RenderJobCreateResponse {
23
+ job_id: string;
24
+ status: "queued" | "processing" | "completed" | "error";
25
+ }
26
+ export interface RenderJobStatusResponse {
27
+ job_id: string;
28
+ status: "queued" | "processing" | "completed" | "error";
29
+ url?: string | null;
30
+ error?: string | null;
31
+ end_time?: string | null;
32
+ }
33
+ export interface RenderBuildAsyncResponse {
34
+ /** Final URL to the rendered MP4 (or sprite) asset */
35
+ videoUrl: string;
36
+ }
21
37
  export interface RenderSpriteResponse {
22
38
  /**
23
39
  * The rendered sprite sheet as a Blob (when format is "sprite")
@@ -49,13 +65,15 @@ export interface RenderAPIService {
49
65
  */
50
66
  getAvailableParts(config: ApiConfig): Promise<AvailablePartsResponse>;
51
67
  }
52
- export interface ApiConfig {
53
- environment?: "staging" | "prod";
54
- authToken?: string;
55
- }
56
68
  export declare const buildApiUrl: (endpoint: string, config: ApiConfig) => string;
57
69
  export declare const buildHeaders: (config: ApiConfig) => Record<string, string>;
58
70
  export declare const renderBuildExperimental: (request: RenderBuildRequest, config: ApiConfig) => Promise<RenderBuildResponse>;
71
+ export declare const createRenderBuildJob: (request: RenderBuildRequest, config: ApiConfig) => Promise<RenderJobCreateResponse>;
72
+ export declare const getRenderBuildStatus: (jobId: string, config: ApiConfig) => Promise<RenderJobStatusResponse>;
73
+ export declare const renderBuild: (request: RenderBuildRequest, config: ApiConfig, options?: {
74
+ pollIntervalMs?: number;
75
+ timeoutMs?: number;
76
+ }) => Promise<RenderBuildAsyncResponse>;
59
77
  export declare const renderSpriteExperimental: (request: RenderBuildRequest, config: ApiConfig) => Promise<RenderSpriteResponse>;
60
78
  export declare const getAvailableParts: (config: ApiConfig) => Promise<AvailablePartsResponse>;
61
79
  export { API_BASE_URL };
@@ -9,4 +9,12 @@ export interface UseBuildRenderReturn {
9
9
  isRenderingBuild: boolean;
10
10
  renderError: string | null;
11
11
  }
12
- export declare const useBuildRender: (parts: RenderBuildRequest, apiConfig: ApiConfig, onLoadStart?: () => void) => UseBuildRenderReturn;
12
+ export interface UseBuildRenderOptions {
13
+ /**
14
+ * Choose which backend flow to use
15
+ * - 'async' (default): uses /render-build and polls /render-build/{jobId}
16
+ * - 'experimental': uses /render-build-experimental and returns Blob
17
+ */
18
+ mode?: "async" | "experimental";
19
+ }
20
+ export declare const useBuildRender: (parts: RenderBuildRequest, apiConfig: ApiConfig, onLoadStart?: () => void, options?: UseBuildRenderOptions) => UseBuildRenderReturn;
package/dist/index.d.ts CHANGED
@@ -79,7 +79,7 @@ interface BuildRenderVideoProps {
79
79
  * />
80
80
  * ```
81
81
  */
82
- apiConfig: ApiConfig$1;
82
+ apiConfig: ApiConfig;
83
83
  /**
84
84
  * Optional mouse sensitivity for dragging (default: 0.005).
85
85
  *
@@ -188,7 +188,7 @@ interface BuildRenderProps {
188
188
  * />
189
189
  * ```
190
190
  */
191
- apiConfig: ApiConfig$1;
191
+ apiConfig: ApiConfig;
192
192
  /**
193
193
  * Optional mouse sensitivity for dragging (default: 0.05).
194
194
  *
@@ -211,7 +211,7 @@ interface BuildRenderProps {
211
211
  /**
212
212
  * API configuration for environment and authentication
213
213
  */
214
- interface ApiConfig$1 {
214
+ interface ApiConfig {
215
215
  /**
216
216
  * Environment to use for API requests
217
217
  * - 'staging': Development/testing environment
@@ -455,7 +455,15 @@ interface UseBuildRenderReturn {
455
455
  isRenderingBuild: boolean;
456
456
  renderError: string | null;
457
457
  }
458
- declare const useBuildRender: (parts: RenderBuildRequest, apiConfig: ApiConfig$1, onLoadStart?: () => void) => UseBuildRenderReturn;
458
+ interface UseBuildRenderOptions {
459
+ /**
460
+ * Choose which backend flow to use
461
+ * - 'async' (default): uses /render-build and polls /render-build/{jobId}
462
+ * - 'experimental': uses /render-build-experimental and returns Blob
463
+ */
464
+ mode?: "async" | "experimental";
465
+ }
466
+ declare const useBuildRender: (parts: RenderBuildRequest, apiConfig: ApiConfig, onLoadStart?: () => void, options?: UseBuildRenderOptions) => UseBuildRenderReturn;
459
467
 
460
468
  interface UseSpriteRenderReturn {
461
469
  spriteSrc: string | null;
@@ -467,7 +475,7 @@ interface UseSpriteRenderReturn {
467
475
  totalFrames: number;
468
476
  } | null;
469
477
  }
470
- declare const useSpriteRender: (parts: RenderBuildRequest, apiConfig: ApiConfig$1, onLoadStart?: () => void) => UseSpriteRenderReturn;
478
+ declare const useSpriteRender: (parts: RenderBuildRequest, apiConfig: ApiConfig, onLoadStart?: () => void) => UseSpriteRenderReturn;
471
479
 
472
480
  interface DragIconProps {
473
481
  width?: number;
@@ -494,6 +502,7 @@ declare const InstructionTooltip: React__default.FC<InstructionTooltipProps>;
494
502
  declare const API_BASE_URL = "https://www.renderapi.buildcores.com";
495
503
  declare const API_ENDPOINTS: {
496
504
  readonly RENDER_BUILD_EXPERIMENTAL: "/render-build-experimental";
505
+ readonly RENDER_BUILD: "/render-build";
497
506
  readonly AVAILABLE_PARTS: "/available-parts";
498
507
  };
499
508
  interface RenderBuildResponse {
@@ -541,10 +550,6 @@ interface RenderAPIService {
541
550
  */
542
551
  getAvailableParts(config: ApiConfig): Promise<AvailablePartsResponse>;
543
552
  }
544
- interface ApiConfig {
545
- environment?: "staging" | "prod";
546
- authToken?: string;
547
- }
548
553
  declare const buildApiUrl: (endpoint: string, config: ApiConfig) => string;
549
554
  declare const buildHeaders: (config: ApiConfig) => Record<string, string>;
550
555
  declare const renderBuildExperimental: (request: RenderBuildRequest, config: ApiConfig) => Promise<RenderBuildResponse>;
@@ -552,4 +557,4 @@ declare const renderSpriteExperimental: (request: RenderBuildRequest, config: Ap
552
557
  declare const getAvailableParts: (config: ApiConfig) => Promise<AvailablePartsResponse>;
553
558
 
554
559
  export { API_BASE_URL, API_ENDPOINTS, BuildRender, BuildRenderVideo, DragIcon, InstructionTooltip, LoadingErrorOverlay, PartCategory, arePartsEqual, buildApiUrl, buildHeaders, calculateCircularFrame, calculateCircularTime, getAvailableParts, renderBuildExperimental, renderSpriteExperimental, useBouncePatternProgress, useBuildRender, useSpriteRender, useSpriteScrubbing, useVideoScrubbing };
555
- export type { ApiConfig$1 as ApiConfig, AvailablePartsResponse, BuildRenderProps, BuildRenderVideoProps, PartDetails, RenderAPIService, RenderBuildRequest, RenderBuildResponse, RenderSpriteResponse, UseBuildRenderReturn, UseSpriteRenderReturn };
560
+ export type { ApiConfig, AvailablePartsResponse, BuildRenderProps, BuildRenderVideoProps, PartDetails, RenderAPIService, RenderBuildRequest, RenderBuildResponse, RenderSpriteResponse, UseBuildRenderOptions, UseBuildRenderReturn, UseSpriteRenderReturn };
package/dist/index.esm.js CHANGED
@@ -324,6 +324,7 @@ const API_BASE_URL = "https://www.renderapi.buildcores.com";
324
324
  // API Endpoints
325
325
  const API_ENDPOINTS = {
326
326
  RENDER_BUILD_EXPERIMENTAL: "/render-build-experimental",
327
+ RENDER_BUILD: "/render-build",
327
328
  AVAILABLE_PARTS: "/available-parts",
328
329
  };
329
330
  // API URL helpers
@@ -346,6 +347,7 @@ const buildHeaders = (config) => {
346
347
  }
347
348
  return headers;
348
349
  };
350
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
349
351
  // API Implementation
350
352
  const renderBuildExperimental = async (request, config) => {
351
353
  const requestWithFormat = {
@@ -369,6 +371,65 @@ const renderBuildExperimental = async (request, config) => {
369
371
  },
370
372
  };
371
373
  };
374
+ // New async endpoints implementation
375
+ const createRenderBuildJob = async (request, config) => {
376
+ const body = {
377
+ parts: request.parts,
378
+ // If provided, forward format; default handled server-side but we keep explicit default
379
+ ...(request.format ? { format: request.format } : {}),
380
+ };
381
+ const response = await fetch(buildApiUrl(API_ENDPOINTS.RENDER_BUILD, config), {
382
+ method: "POST",
383
+ headers: buildHeaders(config),
384
+ body: JSON.stringify(body),
385
+ });
386
+ if (!response.ok) {
387
+ throw new Error(`Create render job failed: ${response.status} ${response.statusText}`);
388
+ }
389
+ const data = (await response.json());
390
+ if (!data?.job_id) {
391
+ throw new Error("Create render job failed: missing job_id in response");
392
+ }
393
+ return data;
394
+ };
395
+ const getRenderBuildStatus = async (jobId, config) => {
396
+ const url = buildApiUrl(`${API_ENDPOINTS.RENDER_BUILD}/${encodeURIComponent(jobId)}`, config);
397
+ const response = await fetch(url, {
398
+ method: "GET",
399
+ headers: buildHeaders(config),
400
+ });
401
+ if (response.status === 404) {
402
+ throw new Error("Render job not found");
403
+ }
404
+ if (!response.ok) {
405
+ throw new Error(`Get render job status failed: ${response.status} ${response.statusText}`);
406
+ }
407
+ return (await response.json());
408
+ };
409
+ const renderBuild = async (request, config, options) => {
410
+ const pollIntervalMs = 1500;
411
+ const timeoutMs = 120000; // 2 minutes default
412
+ const { job_id } = await createRenderBuildJob(request, config);
413
+ const start = Date.now();
414
+ // Poll until completed or error or timeout
415
+ for (;;) {
416
+ const status = await getRenderBuildStatus(job_id, config);
417
+ if (status.status === "completed") {
418
+ const finalUrl = status.url ?? undefined;
419
+ if (!finalUrl) {
420
+ throw new Error("Render job completed but no URL returned");
421
+ }
422
+ return { videoUrl: finalUrl };
423
+ }
424
+ if (status.status === "error") {
425
+ throw new Error(status.error || "Render job failed");
426
+ }
427
+ if (Date.now() - start > timeoutMs) {
428
+ throw new Error("Timed out waiting for render job to complete");
429
+ }
430
+ await sleep(pollIntervalMs);
431
+ }
432
+ };
372
433
  const renderSpriteExperimental = async (request, config) => {
373
434
  const requestWithFormat = {
374
435
  ...request,
@@ -473,7 +534,7 @@ const arePartsEqual = (parts1, parts2) => {
473
534
  }
474
535
  return true;
475
536
  };
476
- const useBuildRender = (parts, apiConfig, onLoadStart) => {
537
+ const useBuildRender = (parts, apiConfig, onLoadStart, options) => {
477
538
  const [videoSrc, setVideoSrc] = useState(null);
478
539
  const [isRenderingBuild, setIsRenderingBuild] = useState(false);
479
540
  const [renderError, setRenderError] = useState(null);
@@ -483,15 +544,27 @@ const useBuildRender = (parts, apiConfig, onLoadStart) => {
483
544
  setIsRenderingBuild(true);
484
545
  setRenderError(null);
485
546
  onLoadStart?.();
486
- const response = await renderBuildExperimental(currentParts, apiConfig);
487
- const objectUrl = URL.createObjectURL(response.video);
488
- // Clean up previous video URL before setting new one
489
- setVideoSrc((prevSrc) => {
490
- if (prevSrc) {
491
- URL.revokeObjectURL(prevSrc);
492
- }
493
- return objectUrl;
494
- });
547
+ const mode = options?.mode ?? "async";
548
+ if (mode === "experimental") {
549
+ const response = await renderBuildExperimental(currentParts, apiConfig);
550
+ const objectUrl = URL.createObjectURL(response.video);
551
+ setVideoSrc((prevSrc) => {
552
+ if (prevSrc && prevSrc.startsWith("blob:")) {
553
+ URL.revokeObjectURL(prevSrc);
554
+ }
555
+ return objectUrl;
556
+ });
557
+ }
558
+ else {
559
+ const { videoUrl } = await renderBuild(currentParts, apiConfig);
560
+ // Clean up previous object URL (if any) before setting new one
561
+ setVideoSrc((prevSrc) => {
562
+ if (prevSrc && prevSrc.startsWith("blob:")) {
563
+ URL.revokeObjectURL(prevSrc);
564
+ }
565
+ return videoUrl;
566
+ });
567
+ }
495
568
  }
496
569
  catch (error) {
497
570
  setRenderError(error instanceof Error ? error.message : "Failed to render build");
@@ -499,7 +572,7 @@ const useBuildRender = (parts, apiConfig, onLoadStart) => {
499
572
  finally {
500
573
  setIsRenderingBuild(false);
501
574
  }
502
- }, [apiConfig, onLoadStart]);
575
+ }, [apiConfig, onLoadStart, options?.mode]);
503
576
  // Effect to call API when parts content changes (using custom equality check)
504
577
  useEffect(() => {
505
578
  const shouldFetch = previousPartsRef.current === null ||
@@ -512,7 +585,7 @@ const useBuildRender = (parts, apiConfig, onLoadStart) => {
512
585
  // Cleanup effect for component unmount
513
586
  useEffect(() => {
514
587
  return () => {
515
- if (videoSrc) {
588
+ if (videoSrc && videoSrc.startsWith("blob:")) {
516
589
  URL.revokeObjectURL(videoSrc);
517
590
  }
518
591
  };