@cueframe/kernel 0.2.2 → 0.2.3
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/_members/animate/index.d.ts +8 -2
- package/dist/_members/animate/layout/FlexLayout.d.ts +69 -0
- package/dist/_members/animate/layout/fit.d.ts +44 -0
- package/dist/_members/animate/layout/index.d.ts +3 -0
- package/dist/_members/animate/layout/solveFlex.d.ts +70 -0
- package/dist/_members/animate/media/MediaTileScene.d.ts +65 -0
- package/dist/_members/animate/media/recipes.d.ts +61 -0
- package/dist/_members/animate/scene/FocusPass.d.ts +76 -0
- package/dist/_members/animate/scene/UnlitPlane.d.ts +45 -0
- package/dist/_members/animate/scene/__oracle__/cssProjection.d.ts +24 -0
- package/dist/_members/animate/scene/assets.d.ts +21 -0
- package/dist/_members/animate/scene/backdrop.d.ts +7 -0
- package/dist/_members/animate/scene/coordinates.d.ts +58 -0
- package/dist/_members/animate/scene/deviceOpacity.d.ts +18 -0
- package/dist/_members/animate/scene/errors.d.ts +14 -0
- package/dist/_members/animate/scene/finishing.d.ts +69 -0
- package/dist/_members/animate/scene/finishingMath.d.ts +60 -0
- package/dist/_members/animate/scene/finishingPass.d.ts +35 -0
- package/dist/_members/animate/scene/focusBlur.d.ts +56 -0
- package/dist/_members/animate/scene/focusResources.d.ts +190 -0
- package/dist/_members/animate/scene/focusShaders.d.ts +68 -0
- package/dist/_members/animate/scene/fontReadiness.d.ts +27 -0
- package/dist/_members/animate/scene/grainNoise.d.ts +118 -0
- package/dist/_members/animate/scene/index.d.ts +19 -0
- package/dist/_members/animate/scene/internal.d.ts +29 -0
- package/dist/_members/animate/scene/planeFit.d.ts +46 -0
- package/dist/_members/animate/scene/preparedWorldText.d.ts +25 -0
- package/dist/_members/animate/scene/rasterCache.d.ts +30 -0
- package/dist/_members/animate/scene/rasterReadiness.d.ts +33 -0
- package/dist/_members/animate/scene/readiness.d.ts +69 -0
- package/dist/_members/animate/scene/redraw.d.ts +10 -0
- package/dist/_members/animate/scene/render.d.ts +9 -0
- package/dist/_members/animate/scene/scaling.d.ts +13 -0
- package/dist/_members/animate/scene/text.d.ts +40 -0
- package/dist/_members/animate/scene/textLayout.d.ts +78 -0
- package/dist/_members/animate/scene/textureImageSize.d.ts +4 -0
- package/dist/_members/animate/scene/useSceneImageTexture.d.ts +21 -0
- package/dist/_members/animate/scene/useWorldTextTexture.d.ts +21 -0
- package/dist/_members/animate/scene/v2/MeasuredPlaneGeometry.d.ts +7 -0
- package/dist/_members/animate/scene/v2/PhotographicStudioRig.d.ts +11 -0
- package/dist/_members/animate/scene/v2/PreparedSceneShotV1.d.ts +12 -0
- package/dist/_members/animate/scene/v2/SceneShotV2.d.ts +59 -0
- package/dist/_members/animate/scene/v2/builders.d.ts +170 -0
- package/dist/_members/animate/scene/v2/finishes.d.ts +152 -0
- package/dist/_members/animate/scene/v2/lighting.d.ts +119 -0
- package/dist/_members/animate/scene/v2/observePmremTargets.d.ts +4 -0
- package/dist/_members/animate/scene/v2/preparationFacts.d.ts +32 -0
- package/dist/_members/animate/scene/v2/preparedSurface.d.ts +17 -0
- package/dist/_members/animate/scene/v2/projectedResolution.d.ts +2 -0
- package/dist/_members/animate/scene/v2/renderTargetObserver.d.ts +5 -0
- package/dist/_members/animate/scene/v2/runtimeAllocations.d.ts +55 -0
- package/dist/_members/animate/scene/v2/shadow.d.ts +23 -0
- package/dist/_members/animate/scene/v2/slabGeometry.d.ts +134 -0
- package/dist/_members/animate/scene/v2/slots.d.ts +73 -0
- package/dist/_members/animate/scene/v2/videoBinding.d.ts +39 -0
- package/dist/_members/animate/scene/worldBounds.d.ts +19 -0
- package/dist/_members/animate/scene/worldTextRaster.d.ts +87 -0
- package/dist/_members/animate/scene/worldTextTexture.d.ts +32 -0
- package/dist/_members/animate/stage/PerspectiveStage.d.ts +73 -0
- package/dist/_members/animate/stage/camera.d.ts +143 -0
- package/dist/_members/animate/stage/dof.d.ts +9 -0
- package/dist/_members/animate/stage/index.d.ts +6 -0
- package/dist/_members/animate/stage/presets.d.ts +17 -0
- package/dist/_members/animate/three/DeviceMesh.d.ts +256 -0
- package/dist/_members/animate/three/SceneCameraRig.d.ts +12 -0
- package/dist/_members/animate/three/StageCameraRig.d.ts +7 -0
- package/dist/_members/animate/three/StudioRig.d.ts +228 -0
- package/dist/_members/animate/three/ThreeCanvas.d.ts +22 -0
- package/dist/_members/animate/three/defaultScreen.d.ts +23 -0
- package/dist/_members/animate/three/index.d.ts +7 -1
- package/dist/_members/animate/three/textureLifecycle.d.ts +29 -0
- package/dist/_members/animate/three/toneMapping.d.ts +113 -0
- package/dist/_members/animate/three/useImageTexture.d.ts +17 -0
- package/dist/_members/authoring/index.d.ts +2 -0
- package/dist/_members/authoring/scene/index.d.ts +6 -0
- package/dist/_members/authoring/scene/materialize.d.ts +47 -0
- package/dist/_members/authoring/scene/validate.d.ts +80 -0
- package/dist/_members/authoring/scene/wrapper.d.ts +41 -0
- package/dist/_members/component-runtime/allowedImports.d.ts +3 -0
- package/dist/_members/component-runtime/allowedImports.d.ts.map +1 -0
- package/dist/_members/component-runtime/bake.d.ts +8 -0
- package/dist/_members/component-runtime/bake.d.ts.map +1 -1
- package/dist/_members/component-runtime/browserFirewall.d.ts +7 -1
- package/dist/_members/component-runtime/browserFirewall.d.ts.map +1 -1
- package/dist/_members/component-runtime/browserOptions.d.ts +59 -3
- package/dist/_members/component-runtime/browserOptions.d.ts.map +1 -1
- package/dist/_members/component-runtime/canonicalIdentity.d.ts +14 -0
- package/dist/_members/component-runtime/canonicalIdentity.d.ts.map +1 -1
- package/dist/_members/component-runtime/contracts.d.ts +341 -1
- package/dist/_members/component-runtime/contracts.d.ts.map +1 -1
- package/dist/_members/component-runtime/fixtures/imageTileDriftWall.d.ts +5 -0
- package/dist/_members/component-runtime/fixtures/imageTileDriftWall.d.ts.map +1 -0
- package/dist/_members/component-runtime/fixtures/mediaTileMixed.d.ts +5 -0
- package/dist/_members/component-runtime/fixtures/mediaTileMixed.d.ts.map +1 -0
- package/dist/_members/component-runtime/fixtures/mediaTileVideo.d.ts +8 -0
- package/dist/_members/component-runtime/fixtures/mediaTileVideo.d.ts.map +1 -0
- package/dist/_members/component-runtime/importPolicy.d.ts +3 -2
- package/dist/_members/component-runtime/importPolicy.d.ts.map +1 -1
- package/dist/_members/component-runtime/index.d.ts +3 -0
- package/dist/_members/component-runtime/index.d.ts.map +1 -1
- package/dist/_members/component-runtime/livePreviewProxy.d.ts +7 -0
- package/dist/_members/component-runtime/livePreviewProxy.d.ts.map +1 -0
- package/dist/_members/component-runtime/localRoot.d.ts +37 -0
- package/dist/_members/component-runtime/localRoot.d.ts.map +1 -0
- package/dist/_members/component-runtime/mediaResolution.d.ts +25 -0
- package/dist/_members/component-runtime/mediaResolution.d.ts.map +1 -0
- package/dist/_members/component-runtime/render.d.ts +151 -13
- package/dist/_members/component-runtime/render.d.ts.map +1 -1
- package/dist/_members/component-runtime/renderDiagnostics.d.ts +14 -0
- package/dist/_members/component-runtime/renderDiagnostics.d.ts.map +1 -0
- package/dist/_members/component-runtime/sceneAdmission.d.ts +47 -0
- package/dist/_members/component-runtime/sceneAdmission.d.ts.map +1 -0
- package/dist/_members/component-runtime/sourceWorkspace.d.ts +8 -0
- package/dist/_members/component-runtime/sourceWorkspace.d.ts.map +1 -1
- package/dist/_members/component-runtime/webglProbe.d.ts +191 -0
- package/dist/_members/component-runtime/webglProbe.d.ts.map +1 -0
- package/dist/_members/composition/fonts/bundledFaces.d.ts +150 -0
- package/dist/_members/composition/fonts/captionFaces.d.ts +268 -0
- package/dist/_members/composition/fonts.d.ts +11 -4
- package/dist/_members/composition/graphics-plan/schemas.d.ts +4 -4
- package/dist/_members/composition/ops/types.d.ts +1 -1
- package/dist/_members/composition/primitive-catalog/types.d.ts +1 -0
- package/dist/_members/composition/projection/behind-readiness.d.ts +3 -0
- package/dist/_members/composition/projection/brandKit.d.ts +6 -0
- package/dist/_members/composition/projection/composition-floor.d.ts +2 -1
- package/dist/_members/composition/scene/animation.d.ts +25 -0
- package/dist/_members/composition/scene/budget.d.ts +29 -0
- package/dist/_members/composition/scene/budgetPolicy.d.mts +13 -0
- package/dist/_members/composition/scene/clipRules.d.ts +119 -0
- package/dist/_members/composition/scene/constraints.d.ts +61 -0
- package/dist/_members/composition/scene/device.d.ts +21 -0
- package/dist/_members/composition/scene/examples.d.ts +11 -0
- package/dist/_members/composition/scene/index.d.ts +11 -0
- package/dist/_members/composition/scene/photographicRig.d.ts +242 -0
- package/dist/_members/composition/scene/preparedRender.d.ts +239 -0
- package/dist/_members/composition/scene/resolution.d.ts +178 -0
- package/dist/_members/composition/scene/schema.d.ts +351 -0
- package/dist/_members/composition/scene/videoSchedule.d.ts +43 -0
- package/dist/_members/composition/validators/brandKit.d.ts +1 -0
- package/dist/_members/composition/validators/render.d.ts +2 -0
- package/dist/_members/composition-ops/index.d.ts +45 -0
- package/dist/_members/composition-ops/opRecorder.d.ts +1 -1
- package/dist/_members/core/render/compositionRender.d.ts +41 -9
- package/dist/_members/core/render/index.d.ts +9 -2
- package/dist/_members/core/render/sceneCaptureIdentity.d.ts +14 -0
- package/dist/_members/core/render/sceneShotPlan.d.ts +142 -0
- package/dist/_members/core/render/sceneShotPreparation.d.ts +57 -0
- package/dist/_members/core/render/slabDescriptor.d.ts +21 -0
- package/dist/_members/core/render/trackProjection.d.ts +33 -0
- package/dist/_members/core/render/types.d.ts +142 -0
- package/dist/_members/errors/registry.d.ts +6 -0
- package/dist/_members/mcp/adapter.d.ts +5 -0
- package/dist/_members/mcp/generated/sceneContract.d.ts +155 -0
- package/dist/_members/mcp/sceneShot.d.ts +38 -0
- package/dist/_members/mcp/sceneShotExample.d.ts +22 -0
- package/dist/_members/media-model/api.d.ts +1 -0
- package/dist/_members/media-model/media-kind.d.ts +1 -1
- package/dist/_members/media-model/testing/mediaStoreContract.d.ts +1 -1
- package/dist/_members/primitives/effects/index.d.ts +1 -0
- package/dist/_members/primitives/effects/stage-camera/config.d.ts +50 -0
- package/dist/_members/primitives/effects/stage-camera/index.d.ts +31 -0
- package/dist/_members/primitives/layouts/flex-layout/config.d.ts +73 -0
- package/dist/_members/primitives/layouts/flex-layout/index.d.ts +4 -0
- package/dist/_members/primitives/layouts/index.d.ts +1 -0
- package/dist/_members/primitives/layouts/picture-in-picture/config.d.ts +1 -1
- package/dist/_members/primitives/lib/frameScale.d.ts +4 -0
- package/dist/_members/primitives/lib/index.d.ts +1 -0
- package/dist/_members/primitives/lib/palette.d.ts +30 -30
- package/dist/_members/primitives/lib/text/contrast.d.ts +2 -1
- package/dist/_members/primitives/lib/text/spec.d.ts +0 -8
- package/dist/_members/primitives/lib/types.d.ts +2 -0
- package/dist/_members/primitives/scenes/device-showcase/config.d.ts +41 -0
- package/dist/_members/primitives/scenes/device-showcase/index.d.ts +36 -0
- package/dist/_members/primitives/scenes/hero-device-assemble/config.d.ts +1 -0
- package/dist/_members/primitives/scenes/hero-device-assemble/index.d.ts +2 -1
- package/dist/_members/primitives/scenes/index.d.ts +1 -0
- package/dist/_members/primitives/ui-blocks/badge-pop/config.d.ts +1 -1
- package/dist/_members/primitives/ui-blocks/device-mockup-zoom/config.d.ts +1 -0
- package/dist/_members/primitives/ui-blocks/device-mockup-zoom/index.d.ts +2 -1
- package/dist/_members/primitives/ui-blocks/terminal-simulator/config.d.ts +1 -1
- package/dist/_members/primitives/ui-blocks/toast-notification/config.d.ts +1 -1
- package/dist/_members/render-harness/cardAssetAdmission.d.ts.map +1 -1
- package/dist/_members/render-harness/cardRender.d.ts.map +1 -1
- package/dist/_members/render-harness/css-tree-subpaths.d.ts +11 -0
- package/dist/_members/render-harness/extractSegments.d.ts +1 -0
- package/dist/_members/render-harness/extractSegments.d.ts.map +1 -1
- package/dist/_members/render-harness/imageHeader.d.ts +14 -0
- package/dist/_members/render-harness/imageHeader.d.ts.map +1 -0
- package/dist/_members/render-harness/index.d.ts +6 -0
- package/dist/_members/render-harness/index.d.ts.map +1 -1
- package/dist/_members/render-harness/materializeSources.d.ts +46 -2
- package/dist/_members/render-harness/materializeSources.d.ts.map +1 -1
- package/dist/_members/render-harness/nodeScenePreparation.d.ts +97 -0
- package/dist/_members/render-harness/nodeScenePreparation.d.ts.map +1 -0
- package/dist/_members/render-harness/remotion-runtime.d.ts +16 -2
- package/dist/_members/render-harness/renderInputAssets.d.ts +1 -1
- package/dist/_members/render-harness/renderInputAssets.d.ts.map +1 -1
- package/dist/_members/render-harness/resolveAssets.d.ts +2 -2
- package/dist/_members/render-harness/resolveAssets.d.ts.map +1 -1
- package/dist/_members/render-harness/sceneShotAssets.d.ts +17 -0
- package/dist/_members/render-harness/sceneShotAssets.d.ts.map +1 -0
- package/dist/animate/preparation-facts.d.ts +1 -0
- package/dist/animate/preparation-facts.js +22 -0
- package/dist/animate.js +336 -1005
- package/dist/authoring/build-tools.js +7 -6
- package/dist/authoring/context.js +5 -4
- package/dist/authoring/prompts.js +1 -1
- package/dist/authoring/scene.d.ts +1 -0
- package/dist/authoring/scene.js +50 -0
- package/dist/authoring/tools.js +6 -5
- package/dist/authoring.js +49 -29
- package/dist/chunk-2L7PYYJK.js +23 -0
- package/dist/{chunk-ZN6P3KOJ.js → chunk-32TH76H3.js} +2 -2
- package/dist/chunk-5DJ3IEJR.js +4424 -0
- package/dist/chunk-6BVVCRRD.js +26488 -0
- package/dist/{chunk-DQKSCHFC.js → chunk-6EWRIAZA.js} +187 -66
- package/dist/{chunk-YQ7FFW3G.js → chunk-6VWICHOZ.js} +26 -5
- package/dist/{chunk-RETQFTGY.js → chunk-A74FZCPY.js} +42 -5
- package/dist/{chunk-C5SEPOWD.js → chunk-AS5EXBNS.js} +1792 -707
- package/dist/{chunk-U5LBYS3N.js → chunk-B3GSTQLR.js} +339 -272
- package/dist/chunk-BVA3537Z.js +63 -0
- package/dist/{chunk-DHKQ47Q6.js → chunk-DAARYRNT.js} +6 -1
- package/dist/chunk-DQLYWNLC.js +2063 -0
- package/dist/chunk-E6WHXIDF.js +3619 -0
- package/dist/{chunk-HXKTZPRH.js → chunk-E7GIZEPS.js} +1 -1
- package/dist/{chunk-M2TLRXEO.js → chunk-EXY4JJPH.js} +1 -1
- package/dist/{chunk-GEGRJKKO.js → chunk-F2DJL7P7.js} +1 -1
- package/dist/{chunk-AWT3P5ZN.js → chunk-FVDFPJHT.js} +1 -1
- package/dist/{chunk-EMGFZ3PR.js → chunk-FWSJLHSV.js} +5 -1
- package/dist/{chunk-TMIBBD5E.js → chunk-I5QWBM3M.js} +280 -113
- package/dist/{chunk-IBMJ33EL.js → chunk-I5RX2QZT.js} +1 -1
- package/dist/{chunk-4FOOBSEE.js → chunk-J3DIUG24.js} +1 -0
- package/dist/{chunk-AD5HWBCE.js → chunk-JX53CVJV.js} +14 -5
- package/dist/{chunk-LNBVTDFR.js → chunk-L2AHS3AW.js} +1 -1
- package/dist/{chunk-BDMTUEEZ.js → chunk-LT4PWSR2.js} +2 -2
- package/dist/chunk-MH3UPKWR.js +3415 -0
- package/dist/{chunk-RFXR7ATW.js → chunk-NUKDEOK6.js} +13 -1
- package/dist/{chunk-IJ7PVJTK.js → chunk-ODUWCQI3.js} +3 -2
- package/dist/chunk-QAGGJHAF.js +16 -0
- package/dist/chunk-QS36DMFG.js +332 -0
- package/dist/{chunk-G5BFI32U.js → chunk-RAXHCRVV.js} +1 -1
- package/dist/{chunk-BIBYZJL3.js → chunk-RL5SO653.js} +3 -3
- package/dist/{chunk-ZSZABOE5.js → chunk-S5H6GH6M.js} +1 -1
- package/dist/{chunk-EEXCDFY5.js → chunk-SD25VQDE.js} +1 -1
- package/dist/{chunk-7REW2MW3.js → chunk-SX523NDS.js} +10 -10
- package/dist/chunk-TARKDVP6.js +430 -0
- package/dist/{chunk-IHFREGFA.js → chunk-TCHPRGC5.js} +18 -2
- package/dist/{chunk-ZCAQHNPI.js → chunk-UKGNHZS4.js} +1 -1
- package/dist/{chunk-WQSGVRFU.js → chunk-UY77TMKD.js} +31 -44
- package/dist/chunk-VKATKWWR.js +1462 -0
- package/dist/{chunk-CX2IIQ4R.js → chunk-W2245F6G.js} +29 -29
- package/dist/{chunk-XHUYWVYK.js → chunk-WBNFSG6T.js} +1 -1
- package/dist/chunk-WIHXXF3X.js +221 -0
- package/dist/{chunk-XVBOSPZI.js → chunk-X74EEFXV.js} +6 -6
- package/dist/{chunk-7KEUL3FI.js → chunk-XF2QMKRG.js} +9 -1
- package/dist/{chunk-HFC3MKXA.js → chunk-Y4HPN4BC.js} +1 -0
- package/dist/{chunk-D43J23QX.js → chunk-YFSBFCXO.js} +1018 -42
- package/dist/{chunk-6ID2J64H.js → chunk-YHC3RXS7.js} +162 -25
- package/dist/{chunk-KKEY5MIW.js → chunk-ZS3A3QUM.js} +16 -16
- package/dist/{chunk-LQUWKBST.js → chunk-ZVG446DJ.js} +139 -10
- package/dist/component-runtime/browserOptions.js +14 -3
- package/dist/component-runtime/contracts.js +35 -3
- package/dist/component-runtime/identity/browser.js +1 -1
- package/dist/component-runtime/identity.js +2 -2
- package/dist/component-runtime/importPolicy.js +2 -2
- package/dist/component-runtime/mediaResolution.d.ts +1 -0
- package/dist/component-runtime/mediaResolution.js +11 -0
- package/dist/component-runtime/render.js +38 -5
- package/dist/component-runtime.js +118 -14
- package/dist/compose.js +26 -20
- package/dist/composition/fonts.js +64 -67
- package/dist/composition/graphics-plan.js +3 -2
- package/dist/composition/ops.js +4 -3
- package/dist/composition/primitive-catalog.js +1 -1
- package/dist/composition/projection.js +10 -3
- package/dist/composition/scene/budget-policy.d.ts +1 -0
- package/dist/composition/scene/budget-policy.js +17 -0
- package/dist/composition/scene/schema.json +2757 -0
- package/dist/composition/scene.d.ts +1 -0
- package/dist/composition/scene.js +275 -0
- package/dist/composition/validators.js +3 -2
- package/dist/composition-ops.js +103 -13
- package/dist/composition.js +12 -5
- package/dist/{context-4GSDBFT3.js → context-IUPQ54V4.js} +5 -4
- package/dist/core/render.js +54 -3
- package/dist/core/select.js +2 -2
- package/dist/core.js +6 -3
- package/dist/director/ai-sdk.js +3 -3
- package/dist/director/domain.js +2 -2
- package/dist/director.js +3 -3
- package/dist/errors.js +1 -1
- package/dist/mcp/adapter.js +4 -4
- package/dist/mcp/core.js +1 -1
- package/dist/mcp/curation.js +1 -1
- package/dist/mcp/server.js +4 -4
- package/dist/mcp.js +4 -4
- package/dist/media-model/media-kind.js +2 -0
- package/dist/media-model/testing.js +16 -11
- package/dist/media-model.js +1 -1
- package/dist/nle-export.js +26 -20
- package/dist/primitives/backgrounds.js +2 -2
- package/dist/primitives/captions.js +21 -11
- package/dist/primitives/effects.js +14 -4
- package/dist/primitives/graphics.json +3 -0
- package/dist/primitives/hero-text.js +4 -3
- package/dist/primitives/layouts.js +9 -2
- package/dist/primitives/lib/frameScale.d.ts +1 -0
- package/dist/primitives/lib/frameScale.js +11 -0
- package/dist/primitives/lib/index.js +18 -10
- package/dist/primitives/lib/palette.js +1 -1
- package/dist/primitives/lib/text/contrast.js +2 -1
- package/dist/primitives/lib/text/typeface.js +1 -1
- package/dist/primitives/lib.js +18 -10
- package/dist/primitives/params-required.json +3 -0
- package/dist/primitives/scenes.js +12 -3
- package/dist/primitives/text.js +5 -5
- package/dist/primitives/transitions.js +2 -2
- package/dist/primitives/ui-blocks.js +6 -5
- package/dist/primitives/version-map.json +5 -2
- package/dist/primitives.js +20 -13
- package/dist/render-harness/cardAssetAdmission.js +2 -2
- package/dist/render-harness/renderInputAssets.js +2 -2
- package/dist/render-harness.js +874 -124
- package/package.json +49 -15
- package/dist/_members/animate/three/drei.d.ts +0 -6
- package/dist/_members/component-runtime/fixtures/webgpuTiledRefraction.d.ts +0 -9
- package/dist/_members/component-runtime/fixtures/webgpuTiledRefraction.d.ts.map +0 -1
- package/dist/chunk-5OTQ7M43.js +0 -718
- package/dist/chunk-CGHTMZNC.js +0 -6
- package/dist/chunk-KVMVYRXX.js +0 -9527
- package/dist/chunk-WYQX4QIK.js +0 -544
- /package/dist/{chunk-H5LMJKZU.js → chunk-JVDUSOYG.js} +0 -0
|
@@ -0,0 +1,2063 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cacheDir,
|
|
3
|
+
componentDir,
|
|
4
|
+
findWorkspace
|
|
5
|
+
} from "./chunk-WB2VFDQZ.js";
|
|
6
|
+
import {
|
|
7
|
+
sha256Hex,
|
|
8
|
+
sourceVersionId
|
|
9
|
+
} from "./chunk-EXY4JJPH.js";
|
|
10
|
+
import {
|
|
11
|
+
assertComponentImportsAllowed
|
|
12
|
+
} from "./chunk-A74FZCPY.js";
|
|
13
|
+
import {
|
|
14
|
+
assertComponentBrowserHardware,
|
|
15
|
+
componentBrowserBackend,
|
|
16
|
+
componentBrowserOptions,
|
|
17
|
+
readGpuAttestation
|
|
18
|
+
} from "./chunk-WIHXXF3X.js";
|
|
19
|
+
import {
|
|
20
|
+
ComponentCapturePageRasterAttestationV1,
|
|
21
|
+
ComponentGraphicsApiV2,
|
|
22
|
+
ComponentRuntimeError,
|
|
23
|
+
ComponentRuntimeErrorCode,
|
|
24
|
+
ComponentSourceV2,
|
|
25
|
+
ComponentStillPngContainerFactsV1,
|
|
26
|
+
ComponentStillSessionAttestationV1,
|
|
27
|
+
ComponentStillSessionHostAttestationV1
|
|
28
|
+
} from "./chunk-ZVG446DJ.js";
|
|
29
|
+
import {
|
|
30
|
+
parseSceneShotSpecV2
|
|
31
|
+
} from "./chunk-MH3UPKWR.js";
|
|
32
|
+
|
|
33
|
+
// ../execution/component-runtime/src/sceneAdmission.ts
|
|
34
|
+
import { ZodError } from "zod";
|
|
35
|
+
var SUPPORTED_SCENE_SHOT_VERSIONS = Object.freeze([2]);
|
|
36
|
+
var SCENE_SHOT_PARAM = "scene";
|
|
37
|
+
var SCENE_ADMISSION_STAGE = "admission";
|
|
38
|
+
function subject(context) {
|
|
39
|
+
const component = context.componentId ? `component '${context.componentId}'` : "component";
|
|
40
|
+
return context.clipId ? `clip '${context.clipId}' (${component})` : component;
|
|
41
|
+
}
|
|
42
|
+
function locator(context, path) {
|
|
43
|
+
return {
|
|
44
|
+
stage: SCENE_ADMISSION_STAGE,
|
|
45
|
+
...context.clipId ? { clipId: context.clipId } : {},
|
|
46
|
+
...context.componentId ? { componentId: context.componentId } : {},
|
|
47
|
+
path
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function sceneShotIssuePath(path) {
|
|
51
|
+
return [SCENE_SHOT_PARAM, ...path.map(String)].join(".");
|
|
52
|
+
}
|
|
53
|
+
function assertSceneShotCapabilitySupported(manifest, context = {}) {
|
|
54
|
+
if (!manifest || typeof manifest !== "object") return null;
|
|
55
|
+
const record = manifest;
|
|
56
|
+
if (record.sceneShot === void 0) return null;
|
|
57
|
+
const declaration = record.sceneShot;
|
|
58
|
+
const declared = declaration && typeof declaration === "object" && !Array.isArray(declaration) ? declaration.version : void 0;
|
|
59
|
+
if (typeof declared !== "number") {
|
|
60
|
+
throw new ComponentRuntimeError(
|
|
61
|
+
"unsupported_runtime_version",
|
|
62
|
+
`Scene-shot admission refused for ${subject(context)}: manifest declares sceneShot but manifest.sceneShot.version is not a number (received ${JSON.stringify(declaration) ?? String(declaration)}). Declare sceneShot as { version: ${SUPPORTED_SCENE_SHOT_VERSIONS.join(" | ")} } or remove it; a declaration this runtime cannot read is never treated as an ordinary component.`,
|
|
63
|
+
{ locator: locator(context, "manifest.sceneShot.version") }
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (!SUPPORTED_SCENE_SHOT_VERSIONS.includes(declared)) {
|
|
67
|
+
throw new ComponentRuntimeError(
|
|
68
|
+
"unsupported_runtime_version",
|
|
69
|
+
`Scene-shot admission refused for ${subject(context)}: manifest.sceneShot.version ${declared} is not supported by this runtime (supported: ${SUPPORTED_SCENE_SHOT_VERSIONS.join(", ")}). Re-author the source with a supported sceneShot.version or upgrade the runtime.`,
|
|
70
|
+
{ locator: locator(context, "manifest.sceneShot.version") }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const api = record.graphics && typeof record.graphics === "object" ? record.graphics.api : void 0;
|
|
74
|
+
if (api !== "webgl") {
|
|
75
|
+
throw new ComponentRuntimeError(
|
|
76
|
+
"invalid_component_source",
|
|
77
|
+
`Scene-shot admission refused for ${subject(context)}: manifest declares sceneShot but manifest.graphics.api is ${JSON.stringify(api)}. Set manifest.graphics.api to "webgl"; a scene shot renders through WebGL and is never captured as ${typeof api === "string" ? api : "another api"}.`,
|
|
78
|
+
{ locator: locator(context, "manifest.graphics.api") }
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return { version: declared };
|
|
82
|
+
}
|
|
83
|
+
function assertSceneShotParamsAdmitted(manifest, params, context = {}) {
|
|
84
|
+
if (!assertSceneShotCapabilitySupported(manifest, context)) return null;
|
|
85
|
+
try {
|
|
86
|
+
return parseSceneShotSpecV2(params[SCENE_SHOT_PARAM]);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (!(error instanceof ZodError)) throw error;
|
|
89
|
+
const [first, ...rest] = error.issues;
|
|
90
|
+
const path = sceneShotIssuePath(first?.path ?? []);
|
|
91
|
+
const detail = first?.message ?? "invalid value";
|
|
92
|
+
const remaining = rest.length > 0 ? ` (${rest.length} more issue${rest.length === 1 ? "" : "s"})` : "";
|
|
93
|
+
throw new ComponentRuntimeError(
|
|
94
|
+
"component_scene_invalid",
|
|
95
|
+
`Scene-shot admission refused for ${subject(context)}: params.scene is invalid at ${path}: ${detail}${remaining}. Correct the clip's scene spec at that path.`,
|
|
96
|
+
{ cause: error, locator: locator(context, path) }
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ../execution/component-runtime/src/sourceWorkspace.ts
|
|
102
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
103
|
+
import { join } from "path";
|
|
104
|
+
var PINNED_SOURCE_FILENAME = "component-source-v2.json";
|
|
105
|
+
function materializeComponentSource(workspaceRoot, input) {
|
|
106
|
+
assertSceneShotCapabilitySupported(input?.manifest, { componentId: input?.componentId });
|
|
107
|
+
const source = ComponentSourceV2.parse(input);
|
|
108
|
+
assertComponentImportsAllowed(source.tsxSource, source.manifest.graphics.api);
|
|
109
|
+
mkdirSync(workspaceRoot, { recursive: true });
|
|
110
|
+
writeFileSync(join(workspaceRoot, "cueframe.json"), '{"componentsDir":"cueframe/components"}\n', {
|
|
111
|
+
flag: "wx"
|
|
112
|
+
});
|
|
113
|
+
const dir = join(workspaceRoot, "cueframe", "components", source.componentId);
|
|
114
|
+
mkdirSync(dir, { recursive: true });
|
|
115
|
+
writeFileSync(join(dir, "index.tsx"), source.tsxSource, { flag: "wx" });
|
|
116
|
+
writeFileSync(join(dir, PINNED_SOURCE_FILENAME), `${JSON.stringify(source)}
|
|
117
|
+
`, { flag: "wx" });
|
|
118
|
+
const workspace = findWorkspace(workspaceRoot);
|
|
119
|
+
if (!workspace) throw new Error("Materialized component workspace could not be opened.");
|
|
120
|
+
return {
|
|
121
|
+
workspace,
|
|
122
|
+
source,
|
|
123
|
+
sourceVersionId: sourceVersionId(source),
|
|
124
|
+
componentDir: dir
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function readMaterializedComponentSource(ws, id) {
|
|
128
|
+
const path = join(componentDir(ws, id), PINNED_SOURCE_FILENAME);
|
|
129
|
+
if (!existsSync(path)) return null;
|
|
130
|
+
return ComponentSourceV2.parse(JSON.parse(readFileSync(path, "utf8")));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ../execution/component-runtime/src/browserFirewall.ts
|
|
134
|
+
function isLoopbackHost(hostname) {
|
|
135
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
136
|
+
}
|
|
137
|
+
function decideComponentRequest(url, resourceType, documentOrigin, allowedAssetOrigins) {
|
|
138
|
+
let parsed;
|
|
139
|
+
try {
|
|
140
|
+
parsed = new URL(url);
|
|
141
|
+
} catch {
|
|
142
|
+
return { allow: false, documentOrigin };
|
|
143
|
+
}
|
|
144
|
+
if (parsed.protocol === "data:" || parsed.protocol === "blob:" || parsed.protocol === "about:") {
|
|
145
|
+
return { allow: true, documentOrigin };
|
|
146
|
+
}
|
|
147
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
148
|
+
return { allow: false, documentOrigin };
|
|
149
|
+
}
|
|
150
|
+
if (documentOrigin === null && resourceType === "Document" && isLoopbackHost(parsed.hostname)) {
|
|
151
|
+
return { allow: true, documentOrigin: parsed.origin };
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
allow: parsed.origin === documentOrigin || allowedAssetOrigins.has(parsed.origin),
|
|
155
|
+
documentOrigin
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
var PAGE_LOCKDOWN = `
|
|
159
|
+
Object.defineProperties(globalThis, {
|
|
160
|
+
open: { value: undefined },
|
|
161
|
+
Worker: { value: undefined },
|
|
162
|
+
SharedWorker: { value: undefined },
|
|
163
|
+
RTCPeerConnection: { value: undefined },
|
|
164
|
+
webkitRTCPeerConnection: { value: undefined },
|
|
165
|
+
WebSocket: { value: undefined },
|
|
166
|
+
EventSource: { value: undefined }
|
|
167
|
+
});
|
|
168
|
+
`;
|
|
169
|
+
var COMPONENT_PAGE_LOCKDOWN = PAGE_LOCKDOWN;
|
|
170
|
+
async function installComponentRequestFirewall(browser, allowedAssetOrigins, onDiagnostic) {
|
|
171
|
+
let closing = false;
|
|
172
|
+
let fatal = null;
|
|
173
|
+
let closePromise = null;
|
|
174
|
+
const denied = [];
|
|
175
|
+
const closeBrowser = browser.close.bind(browser);
|
|
176
|
+
const close = () => {
|
|
177
|
+
closing = true;
|
|
178
|
+
closePromise ??= closeBrowser({ silent: true });
|
|
179
|
+
return closePromise;
|
|
180
|
+
};
|
|
181
|
+
browser.close = async (options) => {
|
|
182
|
+
closing = true;
|
|
183
|
+
closePromise ??= closeBrowser(options);
|
|
184
|
+
return await closePromise;
|
|
185
|
+
};
|
|
186
|
+
const configureSession = async (client) => {
|
|
187
|
+
let documentOrigin = null;
|
|
188
|
+
client.on("Fetch.requestPaused", (event) => {
|
|
189
|
+
const decision = decideComponentRequest(
|
|
190
|
+
event.request.url,
|
|
191
|
+
event.resourceType,
|
|
192
|
+
documentOrigin,
|
|
193
|
+
allowedAssetOrigins
|
|
194
|
+
);
|
|
195
|
+
documentOrigin = decision.documentOrigin;
|
|
196
|
+
if (!decision.allow) denied.push(event.request.url);
|
|
197
|
+
const action = decision.allow ? client.send("Fetch.continueRequest", { requestId: event.requestId }) : client.send("Fetch.failRequest", { requestId: event.requestId, errorReason: "BlockedByClient" });
|
|
198
|
+
void action.catch(() => {
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
client.on("Network.loadingFailed", (event) => {
|
|
202
|
+
onDiagnostic?.({ type: "request-failed", message: String(event.errorText ?? "request failed") });
|
|
203
|
+
});
|
|
204
|
+
client.on("Runtime.exceptionThrown", (event) => {
|
|
205
|
+
onDiagnostic?.({ type: "page-error", message: String(event.exceptionDetails?.text ?? "page exception") });
|
|
206
|
+
});
|
|
207
|
+
await client.send("Network.enable");
|
|
208
|
+
await client.send("Runtime.enable");
|
|
209
|
+
await client.send("Fetch.enable", { patterns: [{ urlPattern: "*" }] });
|
|
210
|
+
await client.send("Page.addScriptToEvaluateOnNewDocument", { source: PAGE_LOCKDOWN });
|
|
211
|
+
await client.send("Runtime.runIfWaitingForDebugger");
|
|
212
|
+
};
|
|
213
|
+
browser.connection.on("sessionattached", (client) => {
|
|
214
|
+
void configureSession(client).catch((error) => {
|
|
215
|
+
fatal ??= error;
|
|
216
|
+
if (!closing) void close().catch(() => {
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
await browser.connection.send("Target.setAutoAttach", {
|
|
221
|
+
autoAttach: true,
|
|
222
|
+
waitForDebuggerOnStart: true,
|
|
223
|
+
flatten: true,
|
|
224
|
+
filter: [{ type: "page", exclude: false }, { exclude: true }]
|
|
225
|
+
});
|
|
226
|
+
return {
|
|
227
|
+
deniedRequests: () => denied,
|
|
228
|
+
fatalError: () => fatal,
|
|
229
|
+
close
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ../execution/component-runtime/src/renderDiagnostics.ts
|
|
234
|
+
var GRAPHICS_ATTESTATION_PREFIX = "__cueframe_graphics_v2__:";
|
|
235
|
+
var CAPTURE_PAGE_RASTER_ATTESTATION_PREFIX = "__cueframe_capture_raster_v1__:";
|
|
236
|
+
var BROWSER_RUNTIME_ERROR_PREFIX = "__cueframe_component_error_v2__:";
|
|
237
|
+
function componentRuntimeError(error, fallbackCode, retryable = false) {
|
|
238
|
+
if (error instanceof ComponentRuntimeError) return error;
|
|
239
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
240
|
+
const marker = message.lastIndexOf(BROWSER_RUNTIME_ERROR_PREFIX);
|
|
241
|
+
if (marker >= 0) {
|
|
242
|
+
const payload = message.slice(marker + BROWSER_RUNTIME_ERROR_PREFIX.length);
|
|
243
|
+
const separator = payload.indexOf(":");
|
|
244
|
+
const code = ComponentRuntimeErrorCode.safeParse(separator >= 0 ? payload.slice(0, separator) : payload);
|
|
245
|
+
if (code.success) {
|
|
246
|
+
return new ComponentRuntimeError(
|
|
247
|
+
code.data,
|
|
248
|
+
separator >= 0 ? payload.slice(separator + 1) : message,
|
|
249
|
+
{ retryable: code.data === "component_device_lost" || code.data === "component_deadline_exceeded", cause: error }
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return new ComponentRuntimeError(fallbackCode, message, { retryable, cause: error });
|
|
254
|
+
}
|
|
255
|
+
function parseGraphicsAttestationLog(text) {
|
|
256
|
+
if (!text.startsWith(GRAPHICS_ATTESTATION_PREFIX)) return null;
|
|
257
|
+
try {
|
|
258
|
+
const parsed = JSON.parse(text.slice(GRAPHICS_ATTESTATION_PREFIX.length));
|
|
259
|
+
if (!ComponentGraphicsApiV2.safeParse(parsed.api).success || parsed.acceleration !== "hardware" && parsed.acceleration !== "software" || parsed.deviceLost !== false) return null;
|
|
260
|
+
return parsed;
|
|
261
|
+
} catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function parseCapturePageRasterAttestationLog(text) {
|
|
266
|
+
if (!text.startsWith(CAPTURE_PAGE_RASTER_ATTESTATION_PREFIX)) return null;
|
|
267
|
+
try {
|
|
268
|
+
return ComponentCapturePageRasterAttestationV1.parse(
|
|
269
|
+
JSON.parse(text.slice(CAPTURE_PAGE_RASTER_ATTESTATION_PREFIX.length))
|
|
270
|
+
);
|
|
271
|
+
} catch {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function graphicsCollector(required) {
|
|
276
|
+
let attestation = null;
|
|
277
|
+
return {
|
|
278
|
+
onBrowserLog(log) {
|
|
279
|
+
const parsed = parseGraphicsAttestationLog(log.text);
|
|
280
|
+
if (!parsed) return;
|
|
281
|
+
if (parsed.api !== required.api) {
|
|
282
|
+
throw new ComponentRuntimeError(
|
|
283
|
+
"component_graphics_mismatch",
|
|
284
|
+
`Renderer reported ${parsed.api} for a ${required.api} component.`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
if (attestation && JSON.stringify(attestation) !== JSON.stringify(parsed)) {
|
|
288
|
+
throw new ComponentRuntimeError(
|
|
289
|
+
"component_graphics_mismatch",
|
|
290
|
+
"Renderer workers reported inconsistent graphics adapters."
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
attestation = parsed;
|
|
294
|
+
},
|
|
295
|
+
receipt() {
|
|
296
|
+
if (!attestation) {
|
|
297
|
+
throw new ComponentRuntimeError(
|
|
298
|
+
"component_graphics_unavailable",
|
|
299
|
+
`Renderer produced no trusted ${required.api} attestation.`
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return attestation;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ../execution/component-runtime/src/localRoot.ts
|
|
308
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
309
|
+
import { join as join2, relative } from "path";
|
|
310
|
+
var LOCAL_ROOT_FILENAME = "Root.local.tsx";
|
|
311
|
+
var LOCAL_ENTRY_FILENAME = "entry.local.tsx";
|
|
312
|
+
var LOCAL_COMPOSITION_ID = "CueFrameLocalStill";
|
|
313
|
+
function createLivePresentationRouter(callbacks) {
|
|
314
|
+
let pending = null;
|
|
315
|
+
let committedFrame = null;
|
|
316
|
+
let presentedFrame = null;
|
|
317
|
+
let presentedScene = null;
|
|
318
|
+
const matches = (transport, actual) => transport.playing ? actual >= transport.frame : actual === transport.frame;
|
|
319
|
+
const sameRequest = (left, right) => left.sequence === right.sequence && left.frame === right.frame && left.playing === right.playing;
|
|
320
|
+
const route = () => {
|
|
321
|
+
const transport = pending;
|
|
322
|
+
if (transport === null || committedFrame === null || !matches(transport, committedFrame)) return;
|
|
323
|
+
const committed = committedFrame;
|
|
324
|
+
const scene = callbacks.mediaTileScene();
|
|
325
|
+
if (scene !== null) {
|
|
326
|
+
if (presentedScene === scene && presentedFrame !== null && matches(transport, presentedFrame)) {
|
|
327
|
+
pending = null;
|
|
328
|
+
callbacks.acknowledge(transport, presentedFrame);
|
|
329
|
+
}
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
callbacks.requestFrame(() => {
|
|
333
|
+
if (pending === null || !sameRequest(pending, transport) || callbacks.mediaTileScene() !== null || committedFrame === null || !matches(transport, committedFrame)) return;
|
|
334
|
+
const acknowledged = pending;
|
|
335
|
+
pending = null;
|
|
336
|
+
callbacks.acknowledge(acknowledged, committed);
|
|
337
|
+
});
|
|
338
|
+
};
|
|
339
|
+
return {
|
|
340
|
+
request(transport) {
|
|
341
|
+
pending = transport;
|
|
342
|
+
route();
|
|
343
|
+
},
|
|
344
|
+
committed(frame) {
|
|
345
|
+
committedFrame = frame;
|
|
346
|
+
route();
|
|
347
|
+
},
|
|
348
|
+
tilePresented(frame, scene) {
|
|
349
|
+
presentedFrame = frame;
|
|
350
|
+
presentedScene = scene;
|
|
351
|
+
route();
|
|
352
|
+
},
|
|
353
|
+
dispose() {
|
|
354
|
+
pending = null;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function toImportSpecifier(fromDir, toFile) {
|
|
359
|
+
const rel = relative(fromDir, toFile).split("\\").join("/");
|
|
360
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
361
|
+
}
|
|
362
|
+
function generateLocalRoot(ws, opts) {
|
|
363
|
+
const dir = cacheDir(ws);
|
|
364
|
+
mkdirSync2(dir, { recursive: true });
|
|
365
|
+
const componentFile = join2(componentDir(ws, opts.id), "index");
|
|
366
|
+
const componentSpecifier = toImportSpecifier(dir, componentFile);
|
|
367
|
+
const root = `// GENERATED by @cueframe/component-runtime \u2014 DO NOT EDIT.
|
|
368
|
+
import React from 'react';
|
|
369
|
+
import { useEffect, useLayoutEffect, useState } from 'react';
|
|
370
|
+
import { Composition, cancelRender, continueRender, delayRender, staticFile, useCurrentFrame } from 'remotion';
|
|
371
|
+
|
|
372
|
+
const importAuthoredComponent = async () => {
|
|
373
|
+
return import(${JSON.stringify(componentSpecifier)});
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const hostFontFaces = ${JSON.stringify(opts.fontFaces ?? [])};
|
|
377
|
+
const hostFontCss = hostFontFaces.map((face) => {
|
|
378
|
+
const url = face.url.startsWith('/') ? staticFile(face.url) : face.url;
|
|
379
|
+
return '@font-face{font-family:' + JSON.stringify(face.family) + ';font-weight:' + face.weight +
|
|
380
|
+
';font-style:' + (face.style || 'normal') + ';src:url(' + JSON.stringify(url) + ');font-display:block;}';
|
|
381
|
+
}).join('');
|
|
382
|
+
|
|
383
|
+
const GuardedAuthoredComponent: React.FC<{
|
|
384
|
+
Component: React.ComponentType<any>;
|
|
385
|
+
componentProps: Record<string, unknown>;
|
|
386
|
+
rootFrame: number;
|
|
387
|
+
captureReceipt?: {sessionId: string; requestId: number};
|
|
388
|
+
}> = ({Component, componentProps, rootFrame, captureReceipt}) => {
|
|
389
|
+
useEffect(() => {
|
|
390
|
+
window.dispatchEvent(new CustomEvent('cueframe-authored-component-mounted'));
|
|
391
|
+
}, []);
|
|
392
|
+
useLayoutEffect(() => {
|
|
393
|
+
window.dispatchEvent(new CustomEvent('cueframe-root-frame-committed', {
|
|
394
|
+
detail: {frame: rootFrame},
|
|
395
|
+
}));
|
|
396
|
+
if (captureReceipt) {
|
|
397
|
+
console.info(${JSON.stringify(CAPTURE_PAGE_RASTER_ATTESTATION_PREFIX)} + JSON.stringify({
|
|
398
|
+
...captureReceipt,
|
|
399
|
+
frame: rootFrame,
|
|
400
|
+
deviceScaleFactor: window.devicePixelRatio,
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
}, [captureReceipt, rootFrame]);
|
|
404
|
+
return <div data-cueframe-authored-root data-cueframe-root-frame={rootFrame} style={{display:'contents'}}>
|
|
405
|
+
<Component {...componentProps} />
|
|
406
|
+
</div>;
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
type CueFrameComponentInput = {
|
|
410
|
+
params: Record<string, unknown>;
|
|
411
|
+
assets: Record<string, unknown>;
|
|
412
|
+
seed: string;
|
|
413
|
+
graphics: {
|
|
414
|
+
api: 'canvas2d' | 'webgl' | 'webgl2' | 'webgpu';
|
|
415
|
+
required: true;
|
|
416
|
+
accelerationPreference: 'hardware-preferred' | 'software-allowed';
|
|
417
|
+
};
|
|
418
|
+
__cueframeCaptureReceipt?: {sessionId: string; requestId: number};
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
export const CueFrameLocalComponent: React.FC<CueFrameComponentInput> = (input) => {
|
|
422
|
+
const frame = useCurrentFrame();
|
|
423
|
+
const [Component, setComponent] = useState<React.ComponentType<any> | null>(null);
|
|
424
|
+
const [loadError, setLoadError] = useState<Error | null>(null);
|
|
425
|
+
const [waitHandle] = useState(() => delayRender('CueFrame component admission'));
|
|
426
|
+
|
|
427
|
+
// The authored module is lazy-loaded for both graphics APIs. Release the
|
|
428
|
+
// Canvas2D gate only after React mounts it, so its own asset-loading effects
|
|
429
|
+
// have registered their render gates before a screenshot can be taken.
|
|
430
|
+
useEffect(() => {
|
|
431
|
+
if (Component && input.graphics.api === 'canvas2d') continueRender(waitHandle);
|
|
432
|
+
}, [Component, waitHandle]);
|
|
433
|
+
|
|
434
|
+
useEffect(() => {
|
|
435
|
+
let disposed = false;
|
|
436
|
+
let admitted = false;
|
|
437
|
+
let admissionTimer: ReturnType<typeof setTimeout> | undefined;
|
|
438
|
+
let gpuPrototype: { requestAdapter: (options?: unknown) => Promise<any> } | undefined;
|
|
439
|
+
let originalRequestAdapter: ((options?: unknown) => Promise<any>) | undefined;
|
|
440
|
+
const canvasPrototype = HTMLCanvasElement.prototype as unknown as {
|
|
441
|
+
getContext: (kind: string, ...args: unknown[]) => unknown;
|
|
442
|
+
};
|
|
443
|
+
const originalGetContext = canvasPrototype.getContext;
|
|
444
|
+
|
|
445
|
+
void (async () => {
|
|
446
|
+
try {
|
|
447
|
+
if (input.graphics.api === 'canvas2d') {
|
|
448
|
+
const loaded = await importAuthoredComponent();
|
|
449
|
+
if (disposed) return;
|
|
450
|
+
setComponent(() => loaded.default);
|
|
451
|
+
console.info(${JSON.stringify(GRAPHICS_ATTESTATION_PREFIX)} + JSON.stringify({
|
|
452
|
+
api: 'canvas2d', acceleration: 'software', deviceLost: false,
|
|
453
|
+
}));
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (input.graphics.api === 'webgl2' || input.graphics.api === 'webgl') {
|
|
458
|
+
// A webgl (scene-shot) component draws through a GL context of either
|
|
459
|
+
// version; a webgl2 component must create exactly WebGL2. Either way the
|
|
460
|
+
// attestation names the required api, never a substitute.
|
|
461
|
+
const requiredApi = input.graphics.api;
|
|
462
|
+
const acceptedKinds: readonly string[] = requiredApi === 'webgl2' ? ['webgl2'] : ['webgl', 'webgl2', 'experimental-webgl'];
|
|
463
|
+
canvasPrototype.getContext = function (kind: string, ...args: unknown[]) {
|
|
464
|
+
if (kind === '2d') return originalGetContext.call(this, kind, ...args);
|
|
465
|
+
if (!acceptedKinds.includes(kind)) {
|
|
466
|
+
throw new Error('A ' + requiredApi + '-required component may only create a ' + acceptedKinds.join('/') + ' canvas context.');
|
|
467
|
+
}
|
|
468
|
+
const context = originalGetContext.call(this, kind, ...args) as WebGLRenderingContext | WebGL2RenderingContext | null;
|
|
469
|
+
if (!context || admitted) return context;
|
|
470
|
+
admitted = true;
|
|
471
|
+
const rendererInfo = context.getExtension('WEBGL_debug_renderer_info');
|
|
472
|
+
const adapterName = rendererInfo
|
|
473
|
+
? String(context.getParameter(rendererInfo.UNMASKED_RENDERER_WEBGL) || '')
|
|
474
|
+
: String(context.getParameter(context.RENDERER) || '');
|
|
475
|
+
const acceleration = /swiftshader|software|llvmpipe|lavapipe/i.test(adapterName)
|
|
476
|
+
? 'software'
|
|
477
|
+
: 'hardware';
|
|
478
|
+
console.info(${JSON.stringify(GRAPHICS_ATTESTATION_PREFIX)} + JSON.stringify({
|
|
479
|
+
api: requiredApi, acceleration, adapterName: adapterName || undefined, deviceLost: false,
|
|
480
|
+
}));
|
|
481
|
+
this.addEventListener('webglcontextlost', () => {
|
|
482
|
+
if (!disposed) cancelRender(new Error(${JSON.stringify(BROWSER_RUNTIME_ERROR_PREFIX)} + 'component_device_lost:' + requiredApi + ' context was lost during component rendering.'));
|
|
483
|
+
}, {once: true});
|
|
484
|
+
if (admissionTimer) clearTimeout(admissionTimer);
|
|
485
|
+
continueRender(waitHandle as number);
|
|
486
|
+
return context;
|
|
487
|
+
};
|
|
488
|
+
const loaded = await importAuthoredComponent();
|
|
489
|
+
if (disposed) return;
|
|
490
|
+
setComponent(() => loaded.default);
|
|
491
|
+
admissionTimer = setTimeout(() => {
|
|
492
|
+
if (!disposed && !admitted) cancelRender(new Error(${JSON.stringify(BROWSER_RUNTIME_ERROR_PREFIX)} + 'component_graphics_mismatch:The component did not create a ' + requiredApi + ' canvas surface.'));
|
|
493
|
+
}, 10_000);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const gpu = (navigator as unknown as { gpu?: { requestAdapter: (options?: unknown) => Promise<unknown> } }).gpu;
|
|
498
|
+
if (!gpu) throw new Error(${JSON.stringify(BROWSER_RUNTIME_ERROR_PREFIX)} + 'component_graphics_unavailable:The staged browser does not expose WebGPU.');
|
|
499
|
+
let adapterInfo: { description?: string; architecture?: string; device?: string; vendor?: string; isFallbackAdapter?: boolean } = {};
|
|
500
|
+
gpuPrototype = Object.getPrototypeOf(gpu) as { requestAdapter: (options?: unknown) => Promise<any> };
|
|
501
|
+
originalRequestAdapter = gpuPrototype.requestAdapter;
|
|
502
|
+
gpuPrototype.requestAdapter = async function (options?: unknown) {
|
|
503
|
+
const adapter = await originalRequestAdapter!.call(this, options);
|
|
504
|
+
if (adapter?.info) adapterInfo = adapter.info;
|
|
505
|
+
return adapter;
|
|
506
|
+
};
|
|
507
|
+
canvasPrototype.getContext = function (kind: string, ...args: unknown[]) {
|
|
508
|
+
if (kind === '2d') return originalGetContext.call(this, kind, ...args);
|
|
509
|
+
if (kind !== 'webgpu') {
|
|
510
|
+
throw new Error('A WebGPU-required component may only create a WebGPU canvas context.');
|
|
511
|
+
}
|
|
512
|
+
const context = originalGetContext.call(this, kind, ...args) as null | { configure: (configuration: any) => void };
|
|
513
|
+
if (!context) return context;
|
|
514
|
+
const contextPrototype = Object.getPrototypeOf(context) as { configure: (configuration: any) => void; __cueframePatched?: boolean };
|
|
515
|
+
if (!contextPrototype.__cueframePatched) {
|
|
516
|
+
const originalConfigure = contextPrototype.configure;
|
|
517
|
+
Object.defineProperty(contextPrototype, '__cueframePatched', { value: true });
|
|
518
|
+
contextPrototype.configure = function (configuration: any) {
|
|
519
|
+
const result = originalConfigure.call(this, configuration);
|
|
520
|
+
if (!disposed && !admitted) {
|
|
521
|
+
admitted = true;
|
|
522
|
+
const adapterName = [adapterInfo.vendor, adapterInfo.architecture, adapterInfo.device, adapterInfo.description]
|
|
523
|
+
.filter(Boolean)
|
|
524
|
+
.join(' ');
|
|
525
|
+
const softwarePattern = /swiftshader|software|fallback/i;
|
|
526
|
+
const acceleration = adapterInfo.isFallbackAdapter === true || softwarePattern.test(adapterName)
|
|
527
|
+
? 'software'
|
|
528
|
+
: 'hardware';
|
|
529
|
+
console.info(${JSON.stringify(GRAPHICS_ATTESTATION_PREFIX)} + JSON.stringify({
|
|
530
|
+
api: 'webgpu', acceleration, adapterName: adapterName || undefined, deviceLost: false,
|
|
531
|
+
}));
|
|
532
|
+
const device = configuration?.device as { lost?: Promise<unknown> } | undefined;
|
|
533
|
+
void device?.lost?.then(() => {
|
|
534
|
+
if (!disposed) cancelRender(new Error(${JSON.stringify(BROWSER_RUNTIME_ERROR_PREFIX)} + 'component_device_lost:WebGPU device was lost during component rendering.'));
|
|
535
|
+
});
|
|
536
|
+
if (admissionTimer) clearTimeout(admissionTimer);
|
|
537
|
+
continueRender(waitHandle as number);
|
|
538
|
+
}
|
|
539
|
+
return result;
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
return context;
|
|
543
|
+
};
|
|
544
|
+
Object.defineProperty(globalThis, 'OffscreenCanvas', { value: undefined, configurable: true });
|
|
545
|
+
const loaded = await importAuthoredComponent();
|
|
546
|
+
if (disposed) return;
|
|
547
|
+
setComponent(() => loaded.default);
|
|
548
|
+
admissionTimer = setTimeout(() => {
|
|
549
|
+
if (!disposed && !admitted) cancelRender(new Error(${JSON.stringify(BROWSER_RUNTIME_ERROR_PREFIX)} + 'component_graphics_mismatch:The component did not configure a WebGPU canvas surface.'));
|
|
550
|
+
}, 10_000);
|
|
551
|
+
} catch (error) {
|
|
552
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
553
|
+
setLoadError(failure);
|
|
554
|
+
cancelRender(failure);
|
|
555
|
+
}
|
|
556
|
+
})();
|
|
557
|
+
return () => {
|
|
558
|
+
disposed = true;
|
|
559
|
+
if (admissionTimer) clearTimeout(admissionTimer);
|
|
560
|
+
canvasPrototype.getContext = originalGetContext;
|
|
561
|
+
if (gpuPrototype && originalRequestAdapter) gpuPrototype.requestAdapter = originalRequestAdapter;
|
|
562
|
+
};
|
|
563
|
+
}, []);
|
|
564
|
+
|
|
565
|
+
// Commit host-owned face declarations before the authored module import effect
|
|
566
|
+
// runs. Authored modules may inspect document.fonts during initialization.
|
|
567
|
+
if (loadError) throw loadError;
|
|
568
|
+
if (!Component) return hostFontCss ? <style data-cueframe-host-fonts>{hostFontCss}</style> : null;
|
|
569
|
+
return <><style data-cueframe-host-fonts>{hostFontCss}</style><GuardedAuthoredComponent
|
|
570
|
+
Component={Component}
|
|
571
|
+
rootFrame={frame}
|
|
572
|
+
componentProps={{
|
|
573
|
+
durationInFrames: ${opts.durationInFrames},
|
|
574
|
+
params: input.params,
|
|
575
|
+
assets: input.assets,
|
|
576
|
+
render: {
|
|
577
|
+
frame,
|
|
578
|
+
fps: ${opts.fps},
|
|
579
|
+
width: ${opts.width},
|
|
580
|
+
height: ${opts.height},
|
|
581
|
+
seed: input.seed,
|
|
582
|
+
colorSpace: 'srgb',
|
|
583
|
+
},
|
|
584
|
+
}}
|
|
585
|
+
captureReceipt={input.__cueframeCaptureReceipt}
|
|
586
|
+
/></>;
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
export const RemotionRoot: React.FC = () => (
|
|
590
|
+
<Composition
|
|
591
|
+
id=${JSON.stringify(LOCAL_COMPOSITION_ID)}
|
|
592
|
+
component={CueFrameLocalComponent}
|
|
593
|
+
durationInFrames={${opts.durationInFrames}}
|
|
594
|
+
fps={${opts.fps}}
|
|
595
|
+
width={${opts.width}}
|
|
596
|
+
height={${opts.height}}
|
|
597
|
+
defaultProps={{}}
|
|
598
|
+
/>
|
|
599
|
+
);
|
|
600
|
+
`;
|
|
601
|
+
writeFileSync2(join2(dir, LOCAL_ROOT_FILENAME), root);
|
|
602
|
+
const entry = `// GENERATED by @cueframe/component-runtime \u2014 DO NOT EDIT.
|
|
603
|
+
import React from 'react';
|
|
604
|
+
import { createRoot } from 'react-dom/client';
|
|
605
|
+
import { Player } from '@remotion/player';
|
|
606
|
+
import { registerRoot } from 'remotion';
|
|
607
|
+
import { CueFrameLocalComponent, RemotionRoot } from './${LOCAL_ROOT_FILENAME}';
|
|
608
|
+
|
|
609
|
+
const live = new URLSearchParams(window.location.search).has('__cueframe_live');
|
|
610
|
+
if (!live) {
|
|
611
|
+
registerRoot(RemotionRoot);
|
|
612
|
+
} else {
|
|
613
|
+
const revision = ${JSON.stringify(opts.revision ?? "")};
|
|
614
|
+
const instanceId = new URLSearchParams(window.location.search).get('__cueframe_instance') || '';
|
|
615
|
+
const inputProps = ${JSON.stringify(opts.liveInputProps ?? {})};
|
|
616
|
+
document.documentElement.style.width = document.body.style.width = '100%';
|
|
617
|
+
document.documentElement.style.height = document.body.style.height = '100%';
|
|
618
|
+
document.body.style.margin = '0';
|
|
619
|
+
document.body.style.overflow = 'hidden';
|
|
620
|
+
document.body.style.background = 'transparent';
|
|
621
|
+
const host = document.createElement('div');
|
|
622
|
+
host.style.width = host.style.height = '100%';
|
|
623
|
+
document.body.appendChild(host);
|
|
624
|
+
let player = null;
|
|
625
|
+
let updatePlaybackRate = null;
|
|
626
|
+
let pendingTransport = null;
|
|
627
|
+
let appliedTransportEpoch = null;
|
|
628
|
+
let appliedSeekEpoch = null;
|
|
629
|
+
let authoredMounted = false;
|
|
630
|
+
let ready = false;
|
|
631
|
+
const announceReady = () => {
|
|
632
|
+
if (ready || !player || !updatePlaybackRate || !authoredMounted) return;
|
|
633
|
+
ready = true;
|
|
634
|
+
window.parent.postMessage({protocol:'cueframe-live-component-v1',type:'ready',revision,instanceId}, '*');
|
|
635
|
+
if (pendingTransport !== null) applyTransport(pendingTransport);
|
|
636
|
+
};
|
|
637
|
+
const acknowledge = ({instanceId, sequence, frame}, presentedFrame = frame) => {
|
|
638
|
+
window.parent.postMessage({
|
|
639
|
+
protocol:'cueframe-live-component-v1',type:'frame-applied',revision,
|
|
640
|
+
instanceId,sequence,frame,presentedFrame,
|
|
641
|
+
}, '*');
|
|
642
|
+
};
|
|
643
|
+
const createLivePresentationRouter = ${createLivePresentationRouter.toString()};
|
|
644
|
+
const presentation = createLivePresentationRouter({
|
|
645
|
+
mediaTileScene: () => document.querySelector('[data-cueframe-media-tile-scene]')
|
|
646
|
+
?.getAttribute('data-cueframe-media-tile-scene') || null,
|
|
647
|
+
requestFrame: (callback) => {
|
|
648
|
+
// WKWebView can suspend requestAnimationFrame inside a paused nested
|
|
649
|
+
// player even after it has painted the committed React tree. Keep the
|
|
650
|
+
// paint-aligned receipt when available, with a task fallback so a
|
|
651
|
+
// visibly presented paused frame cannot wait forever for an animation
|
|
652
|
+
// clock that is intentionally idle.
|
|
653
|
+
let delivered = false;
|
|
654
|
+
const afterPresentation = () => {
|
|
655
|
+
if (delivered) return;
|
|
656
|
+
delivered = true;
|
|
657
|
+
callback();
|
|
658
|
+
};
|
|
659
|
+
requestAnimationFrame(afterPresentation);
|
|
660
|
+
setTimeout(afterPresentation, 50);
|
|
661
|
+
},
|
|
662
|
+
acknowledge,
|
|
663
|
+
});
|
|
664
|
+
window.addEventListener('pagehide', () => presentation.dispose(), {once:true});
|
|
665
|
+
const applyTransport = (transport) => {
|
|
666
|
+
const {sequence, seekEpoch, frame, playing, playbackRate} = transport;
|
|
667
|
+
if (!player || !updatePlaybackRate || !authoredMounted) {
|
|
668
|
+
pendingTransport = transport;
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
pendingTransport = null;
|
|
672
|
+
document.documentElement.dataset.cueframeSeekEpoch = String(seekEpoch);
|
|
673
|
+
document.documentElement.dataset.cueframePlaying = playing ? 'true' : 'false';
|
|
674
|
+
updatePlaybackRate(playbackRate);
|
|
675
|
+
const drift = Math.abs(player.getCurrentFrame() - frame);
|
|
676
|
+
const newEpoch = sequence !== appliedTransportEpoch;
|
|
677
|
+
const explicitSeek = seekEpoch !== appliedSeekEpoch;
|
|
678
|
+
if (!playing || explicitSeek || (newEpoch && drift > 2)) player.seekTo(frame);
|
|
679
|
+
appliedTransportEpoch = sequence;
|
|
680
|
+
appliedSeekEpoch = seekEpoch;
|
|
681
|
+
if (playing) player.play(); else player.pause();
|
|
682
|
+
presentation.request(transport);
|
|
683
|
+
};
|
|
684
|
+
window.addEventListener('cueframe-authored-component-mounted', () => {
|
|
685
|
+
authoredMounted = true;
|
|
686
|
+
window.parent.postMessage({
|
|
687
|
+
protocol:'cueframe-live-component-v1',type:'lifecycle',revision,instanceId,
|
|
688
|
+
phase:'authored-mounted',
|
|
689
|
+
}, '*');
|
|
690
|
+
announceReady();
|
|
691
|
+
if (pendingTransport !== null) applyTransport(pendingTransport);
|
|
692
|
+
});
|
|
693
|
+
window.addEventListener('cueframe-root-frame-committed', (event) => {
|
|
694
|
+
const frame = event instanceof CustomEvent && Number.isSafeInteger(event.detail?.frame)
|
|
695
|
+
? event.detail.frame
|
|
696
|
+
: null;
|
|
697
|
+
if (frame === null) return;
|
|
698
|
+
presentation.committed(frame);
|
|
699
|
+
});
|
|
700
|
+
window.addEventListener('cueframe-media-tile-presented', (event) => {
|
|
701
|
+
const frame = event instanceof CustomEvent && Number.isSafeInteger(event.detail?.frame)
|
|
702
|
+
? event.detail.frame
|
|
703
|
+
: null;
|
|
704
|
+
const scene = event instanceof CustomEvent && typeof event.detail?.sceneId === 'string'
|
|
705
|
+
? event.detail.sceneId
|
|
706
|
+
: null;
|
|
707
|
+
if (frame === null || scene === null) return;
|
|
708
|
+
presentation.tilePresented(frame, scene);
|
|
709
|
+
});
|
|
710
|
+
const LiveErrorFallback = ({error}) => {
|
|
711
|
+
const detail = error instanceof Error
|
|
712
|
+
? (error.stack?.includes(error.message) ? error.stack : [error.message, error.stack].filter(Boolean).join('\\n'))
|
|
713
|
+
: String(error);
|
|
714
|
+
console.error('CueFrame live component failed', error);
|
|
715
|
+
window.parent.postMessage({protocol:'cueframe-live-component-v1',type:'error',revision,instanceId,detail}, '*');
|
|
716
|
+
return <pre data-cueframe-live-error style={{color:'#fff',whiteSpace:'pre-wrap'}}>{detail}</pre>;
|
|
717
|
+
};
|
|
718
|
+
window.addEventListener('message', (event) => {
|
|
719
|
+
const message = event.data;
|
|
720
|
+
if (!message || message.protocol !== 'cueframe-live-component-v1' || message.revision !== revision) return;
|
|
721
|
+
if (message.type === 'transport' && message.instanceId === instanceId &&
|
|
722
|
+
Number.isSafeInteger(message.sequence) && Number.isSafeInteger(message.seekEpoch) &&
|
|
723
|
+
Number.isSafeInteger(message.frame) &&
|
|
724
|
+
typeof message.playing === 'boolean' && Number.isFinite(message.playbackRate)) {
|
|
725
|
+
applyTransport(message);
|
|
726
|
+
} else if (message.type === 'diagnostics-request' && message.instanceId === instanceId &&
|
|
727
|
+
typeof message.requestId === 'string') {
|
|
728
|
+
window.parent.postMessage({
|
|
729
|
+
protocol:'cueframe-live-component-v1',type:'root-diagnostics',revision,instanceId,
|
|
730
|
+
requestId:message.requestId,
|
|
731
|
+
state:{
|
|
732
|
+
authoredMounted,
|
|
733
|
+
playerReady:Boolean(player && updatePlaybackRate),
|
|
734
|
+
currentFrame:player?.getCurrentFrame?.() ?? null,
|
|
735
|
+
sceneCount:document.querySelectorAll('[data-cueframe-media-tile-scene]').length,
|
|
736
|
+
hasPendingTransport:pendingTransport !== null,
|
|
737
|
+
videos:Array.from(document.querySelectorAll('video')).map((video) => ({
|
|
738
|
+
currentTime:video.currentTime,
|
|
739
|
+
duration:video.duration,
|
|
740
|
+
paused:video.paused,
|
|
741
|
+
readyState:video.readyState,
|
|
742
|
+
networkState:video.networkState,
|
|
743
|
+
seeking:video.seeking,
|
|
744
|
+
})),
|
|
745
|
+
},
|
|
746
|
+
}, '*');
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
const LivePlayer = () => {
|
|
750
|
+
const [playbackRate, setPlaybackRate] = React.useState(1);
|
|
751
|
+
React.useEffect(() => {
|
|
752
|
+
updatePlaybackRate = setPlaybackRate;
|
|
753
|
+
announceReady();
|
|
754
|
+
return () => { updatePlaybackRate = null; };
|
|
755
|
+
}, []);
|
|
756
|
+
return <Player
|
|
757
|
+
ref={(value) => {
|
|
758
|
+
player = value;
|
|
759
|
+
if (value) announceReady();
|
|
760
|
+
}}
|
|
761
|
+
component={CueFrameLocalComponent}
|
|
762
|
+
inputProps={inputProps}
|
|
763
|
+
durationInFrames={${opts.durationInFrames}}
|
|
764
|
+
fps={${opts.fps}}
|
|
765
|
+
compositionWidth={${opts.width}}
|
|
766
|
+
compositionHeight={${opts.height}}
|
|
767
|
+
controls={false}
|
|
768
|
+
errorFallback={LiveErrorFallback}
|
|
769
|
+
clickToPlay={false}
|
|
770
|
+
playbackRate={playbackRate}
|
|
771
|
+
acknowledgeRemotionLicense
|
|
772
|
+
style={{width:'100%',height:'100%',background:'transparent'}}
|
|
773
|
+
/>;
|
|
774
|
+
};
|
|
775
|
+
createRoot(host).render(<LivePlayer />);
|
|
776
|
+
}
|
|
777
|
+
`;
|
|
778
|
+
const entryPath = join2(dir, LOCAL_ENTRY_FILENAME);
|
|
779
|
+
writeFileSync2(entryPath, entry);
|
|
780
|
+
return entryPath;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// ../execution/component-runtime/src/render.ts
|
|
784
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
785
|
+
import { readFile as readFile2, unlink } from "fs/promises";
|
|
786
|
+
import { randomUUID } from "crypto";
|
|
787
|
+
import { dirname, isAbsolute, join as join4, relative as relative2, resolve, sep as sep2 } from "path";
|
|
788
|
+
import { fileURLToPath } from "url";
|
|
789
|
+
|
|
790
|
+
// ../execution/component-runtime/src/determinism.ts
|
|
791
|
+
function createDeterministicRuntimeScope(target) {
|
|
792
|
+
const failure = (name) => {
|
|
793
|
+
throw new Error(`Ambient nondeterministic API is not allowed: ${name}. Use render.frame, render.fps, and render.seed.`);
|
|
794
|
+
};
|
|
795
|
+
const deniedFunction = (name) => function deniedAmbientApi() {
|
|
796
|
+
return failure(name);
|
|
797
|
+
};
|
|
798
|
+
const guardedFacade = (value, label, blockedKeys, denyCall = false) => {
|
|
799
|
+
const blocked = new Set(blockedKeys);
|
|
800
|
+
const deniedByKey = /* @__PURE__ */ new Map();
|
|
801
|
+
const denied = (key) => {
|
|
802
|
+
let fn = deniedByKey.get(key);
|
|
803
|
+
if (!fn) {
|
|
804
|
+
fn = deniedFunction(`${label}.${String(key)}`);
|
|
805
|
+
deniedByKey.set(key, fn);
|
|
806
|
+
}
|
|
807
|
+
return fn;
|
|
808
|
+
};
|
|
809
|
+
return new Proxy(value, {
|
|
810
|
+
get(original, key, receiver) {
|
|
811
|
+
return blocked.has(key) ? denied(key) : Reflect.get(original, key, receiver);
|
|
812
|
+
},
|
|
813
|
+
getOwnPropertyDescriptor(original, key) {
|
|
814
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(original, key);
|
|
815
|
+
if (!blocked.has(key) || descriptor?.configurable === false) return descriptor;
|
|
816
|
+
return {
|
|
817
|
+
configurable: true,
|
|
818
|
+
enumerable: descriptor?.enumerable ?? false,
|
|
819
|
+
writable: false,
|
|
820
|
+
value: denied(key)
|
|
821
|
+
};
|
|
822
|
+
},
|
|
823
|
+
set(original, key, next, receiver) {
|
|
824
|
+
if (blocked.has(key)) return failure(`${label}.${String(key)} mutation`);
|
|
825
|
+
return Reflect.set(original, key, next, receiver);
|
|
826
|
+
},
|
|
827
|
+
defineProperty(original, key, descriptor) {
|
|
828
|
+
if (blocked.has(key)) return failure(`${label}.${String(key)} mutation`);
|
|
829
|
+
return Reflect.defineProperty(original, key, descriptor);
|
|
830
|
+
},
|
|
831
|
+
deleteProperty(original, key) {
|
|
832
|
+
if (blocked.has(key)) return failure(`${label}.${String(key)} mutation`);
|
|
833
|
+
return Reflect.deleteProperty(original, key);
|
|
834
|
+
},
|
|
835
|
+
apply(original, thisArg, args) {
|
|
836
|
+
if (denyCall) return failure(label);
|
|
837
|
+
return Reflect.apply(original, thisArg, args);
|
|
838
|
+
},
|
|
839
|
+
construct(original, args, newTarget) {
|
|
840
|
+
if (denyCall) return failure(label);
|
|
841
|
+
return Reflect.construct(original, args, newTarget);
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
};
|
|
845
|
+
const overrides = /* @__PURE__ */ new Map();
|
|
846
|
+
const math = target.Math;
|
|
847
|
+
if (math && (typeof math === "object" || typeof math === "function")) {
|
|
848
|
+
overrides.set("Math", guardedFacade(math, "Math", ["random"]));
|
|
849
|
+
}
|
|
850
|
+
const date = target.Date;
|
|
851
|
+
if (typeof date === "function") {
|
|
852
|
+
overrides.set("Date", guardedFacade(date, "Date", ["now"], true));
|
|
853
|
+
}
|
|
854
|
+
const performance2 = target.performance;
|
|
855
|
+
if (performance2 && (typeof performance2 === "object" || typeof performance2 === "function")) {
|
|
856
|
+
overrides.set("performance", guardedFacade(performance2, "performance", ["now", "timeOrigin"]));
|
|
857
|
+
}
|
|
858
|
+
const crypto = target.crypto;
|
|
859
|
+
if (crypto && (typeof crypto === "object" || typeof crypto === "function")) {
|
|
860
|
+
overrides.set("crypto", guardedFacade(crypto, "crypto", ["getRandomValues", "randomUUID"]));
|
|
861
|
+
}
|
|
862
|
+
for (const name of ["requestAnimationFrame", "setTimeout", "setInterval", "Worker", "SharedWorker"]) {
|
|
863
|
+
if (name in target) overrides.set(name, deniedFunction(name));
|
|
864
|
+
}
|
|
865
|
+
let scope;
|
|
866
|
+
scope = new Proxy(target, {
|
|
867
|
+
get(original, key) {
|
|
868
|
+
if (key === "globalThis" || key === "window" || key === "self") return scope;
|
|
869
|
+
return overrides.has(key) ? overrides.get(key) : Reflect.get(original, key, original);
|
|
870
|
+
},
|
|
871
|
+
getOwnPropertyDescriptor(original, key) {
|
|
872
|
+
if (key === "globalThis" || key === "window" || key === "self" || overrides.has(key)) {
|
|
873
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(original, key);
|
|
874
|
+
if (descriptor?.configurable === false) return descriptor;
|
|
875
|
+
return {
|
|
876
|
+
configurable: true,
|
|
877
|
+
enumerable: descriptor?.enumerable ?? true,
|
|
878
|
+
writable: false,
|
|
879
|
+
value: key === "globalThis" || key === "window" || key === "self" ? scope : overrides.get(key)
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
return Reflect.getOwnPropertyDescriptor(original, key);
|
|
883
|
+
},
|
|
884
|
+
set(original, key, value, receiver) {
|
|
885
|
+
if (overrides.has(key) || key === "globalThis" || key === "window" || key === "self") {
|
|
886
|
+
return failure(`${String(key)} mutation`);
|
|
887
|
+
}
|
|
888
|
+
return Reflect.set(original, key, value, receiver);
|
|
889
|
+
},
|
|
890
|
+
defineProperty(original, key, descriptor) {
|
|
891
|
+
if (overrides.has(key) || key === "globalThis" || key === "window" || key === "self") {
|
|
892
|
+
return failure(`${String(key)} mutation`);
|
|
893
|
+
}
|
|
894
|
+
return Reflect.defineProperty(original, key, descriptor);
|
|
895
|
+
},
|
|
896
|
+
deleteProperty(original, key) {
|
|
897
|
+
if (overrides.has(key) || key === "globalThis" || key === "window" || key === "self") {
|
|
898
|
+
return failure(`${String(key)} mutation`);
|
|
899
|
+
}
|
|
900
|
+
return Reflect.deleteProperty(original, key);
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
return scope;
|
|
904
|
+
}
|
|
905
|
+
function deterministicRuntimeScopeSource() {
|
|
906
|
+
return createDeterministicRuntimeScope.toString();
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// ../execution/component-runtime/src/livePreviewProxy.ts
|
|
910
|
+
import { createServer } from "http";
|
|
911
|
+
import { randomBytes } from "crypto";
|
|
912
|
+
import { readFile, realpath } from "fs/promises";
|
|
913
|
+
import { extname, join as join3, sep } from "path";
|
|
914
|
+
function contentSecurityPolicy(assetOrigins, scriptNonce, resourceOrigin) {
|
|
915
|
+
const assets = [...assetOrigins].join(" ");
|
|
916
|
+
return [
|
|
917
|
+
"default-src 'none'",
|
|
918
|
+
`script-src 'self' ${resourceOrigin} 'unsafe-eval' 'nonce-${scriptNonce}'`,
|
|
919
|
+
`style-src 'self' ${resourceOrigin} 'unsafe-inline'`,
|
|
920
|
+
`img-src 'self' ${resourceOrigin} data: blob:${assets ? ` ${assets}` : ""}`,
|
|
921
|
+
`media-src 'self' ${resourceOrigin} data: blob:${assets ? ` ${assets}` : ""}`,
|
|
922
|
+
`font-src 'self' ${resourceOrigin} data:${assets ? ` ${assets}` : ""}`,
|
|
923
|
+
`connect-src 'self' ${resourceOrigin}${assets ? ` ${assets}` : ""}`,
|
|
924
|
+
`worker-src 'self' ${resourceOrigin} blob:`,
|
|
925
|
+
// Studio has a tauri: origin on macOS. CSP's wildcard source does not
|
|
926
|
+
// match custom schemes, so the trusted desktop parent must be explicit.
|
|
927
|
+
"frame-ancestors tauri: http://tauri.localhost"
|
|
928
|
+
].join("; ");
|
|
929
|
+
}
|
|
930
|
+
async function startLivePreviewProxy(upstream, assetOrigins) {
|
|
931
|
+
const upstreamUrl = URL.canParse(upstream) ? new URL(upstream) : null;
|
|
932
|
+
if (upstreamUrl && (upstreamUrl.protocol !== "http:" || !["127.0.0.1", "localhost"].includes(upstreamUrl.hostname))) {
|
|
933
|
+
throw new Error("Live component preview requires a loopback bundle origin.");
|
|
934
|
+
}
|
|
935
|
+
const bundleRoot = upstreamUrl ? null : await realpath(upstream);
|
|
936
|
+
const scriptNonce = randomBytes(18).toString("base64");
|
|
937
|
+
const server = createServer(async (request, response) => {
|
|
938
|
+
try {
|
|
939
|
+
let body;
|
|
940
|
+
let contentType = null;
|
|
941
|
+
if (upstreamUrl) {
|
|
942
|
+
const target = new URL(request.url ?? "/", upstreamUrl.origin);
|
|
943
|
+
const fetched = await fetch(target, { method: request.method });
|
|
944
|
+
response.statusCode = fetched.status;
|
|
945
|
+
contentType = fetched.headers.get("content-type");
|
|
946
|
+
for (const [name, value] of fetched.headers) {
|
|
947
|
+
if (!["content-security-policy", "content-length", "transfer-encoding"].includes(name.toLowerCase())) {
|
|
948
|
+
response.setHeader(name, value);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
body = new Uint8Array(await fetched.arrayBuffer());
|
|
952
|
+
} else {
|
|
953
|
+
const pathname = decodeURIComponent(new URL(request.url ?? "/", "http://localhost").pathname);
|
|
954
|
+
const requested = pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "");
|
|
955
|
+
const resolved = await realpath(join3(bundleRoot, requested));
|
|
956
|
+
if (resolved !== bundleRoot && !resolved.startsWith(bundleRoot + sep)) throw new Error("Invalid bundle path.");
|
|
957
|
+
body = await readFile(resolved);
|
|
958
|
+
const mime = (/* @__PURE__ */ new Map([
|
|
959
|
+
[".html", "text/html; charset=utf-8"],
|
|
960
|
+
[".js", "text/javascript; charset=utf-8"],
|
|
961
|
+
[".css", "text/css; charset=utf-8"],
|
|
962
|
+
[".json", "application/json"],
|
|
963
|
+
[".png", "image/png"],
|
|
964
|
+
[".svg", "image/svg+xml"],
|
|
965
|
+
[".woff2", "font/woff2"]
|
|
966
|
+
])).get(extname(resolved));
|
|
967
|
+
if (mime) {
|
|
968
|
+
contentType = mime;
|
|
969
|
+
response.setHeader("Content-Type", mime);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
if (contentType?.toLowerCase().startsWith("text/html")) {
|
|
973
|
+
body = Buffer.from(Buffer.from(body).toString("utf8").replaceAll("<script>", `<script nonce="${scriptNonce}">`));
|
|
974
|
+
}
|
|
975
|
+
const address2 = server.address();
|
|
976
|
+
if (!address2 || typeof address2 === "string") throw new Error("Live component preview has no loopback address.");
|
|
977
|
+
const resourceOrigin = `http://localhost:${address2.port}`;
|
|
978
|
+
response.setHeader("Content-Security-Policy", contentSecurityPolicy(assetOrigins, scriptNonce, resourceOrigin));
|
|
979
|
+
response.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
|
980
|
+
response.end(Buffer.from(body));
|
|
981
|
+
} catch (error) {
|
|
982
|
+
response.statusCode = 502;
|
|
983
|
+
response.end(error instanceof Error ? error.message : String(error));
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
await new Promise((resolve2, reject) => {
|
|
987
|
+
server.once("error", reject);
|
|
988
|
+
server.listen(0, "127.0.0.1", () => {
|
|
989
|
+
server.off("error", reject);
|
|
990
|
+
resolve2();
|
|
991
|
+
});
|
|
992
|
+
});
|
|
993
|
+
const address = server.address();
|
|
994
|
+
if (!address || typeof address === "string") {
|
|
995
|
+
server.close();
|
|
996
|
+
throw new Error("Live component preview did not bind a loopback port.");
|
|
997
|
+
}
|
|
998
|
+
const liveUrl = new URL(upstreamUrl ? upstreamUrl.pathname + upstreamUrl.search : "/", `http://localhost:${address.port}`);
|
|
999
|
+
liveUrl.searchParams.set("__cueframe_live", "1");
|
|
1000
|
+
let closePromise;
|
|
1001
|
+
return {
|
|
1002
|
+
url: liveUrl.href,
|
|
1003
|
+
close: () => closePromise ??= new Promise((resolve2, reject) => {
|
|
1004
|
+
server.close((error) => error ? reject(error) : resolve2());
|
|
1005
|
+
})
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// ../execution/component-runtime/src/render.ts
|
|
1010
|
+
function shouldIsolateWebGpuStillBrowser(platform, graphicsApi) {
|
|
1011
|
+
return platform === "darwin" && graphicsApi === "webgpu";
|
|
1012
|
+
}
|
|
1013
|
+
var COMPONENT_BROWSER_FIREWALL_TIMEOUT_MS = 1e4;
|
|
1014
|
+
var COMPONENT_BROWSER_CLEANUP_TIMEOUT_MS = 5e3;
|
|
1015
|
+
var COMPONENT_BROWSER_FIREWALL_ATTEMPTS = 2;
|
|
1016
|
+
var ComponentBrowserFirewallTimeout = class extends Error {
|
|
1017
|
+
};
|
|
1018
|
+
function withComponentBrowserFirewallTimeout(promise) {
|
|
1019
|
+
return new Promise((resolve2, reject) => {
|
|
1020
|
+
const timer = setTimeout(
|
|
1021
|
+
() => reject(
|
|
1022
|
+
new ComponentBrowserFirewallTimeout(
|
|
1023
|
+
`Component browser firewall setup exceeded ${COMPONENT_BROWSER_FIREWALL_TIMEOUT_MS / 1e3}s.`
|
|
1024
|
+
)
|
|
1025
|
+
),
|
|
1026
|
+
COMPONENT_BROWSER_FIREWALL_TIMEOUT_MS
|
|
1027
|
+
);
|
|
1028
|
+
promise.then(
|
|
1029
|
+
(value) => {
|
|
1030
|
+
clearTimeout(timer);
|
|
1031
|
+
resolve2(value);
|
|
1032
|
+
},
|
|
1033
|
+
(error) => {
|
|
1034
|
+
clearTimeout(timer);
|
|
1035
|
+
reject(error);
|
|
1036
|
+
}
|
|
1037
|
+
);
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
function withComponentBrowserCleanupTimeout(promise) {
|
|
1041
|
+
return new Promise((resolve2, reject) => {
|
|
1042
|
+
const timer = setTimeout(
|
|
1043
|
+
() => reject(new Error(
|
|
1044
|
+
`Component browser cleanup exceeded ${COMPONENT_BROWSER_CLEANUP_TIMEOUT_MS / 1e3}s.`
|
|
1045
|
+
)),
|
|
1046
|
+
COMPONENT_BROWSER_CLEANUP_TIMEOUT_MS
|
|
1047
|
+
);
|
|
1048
|
+
promise.then(
|
|
1049
|
+
() => {
|
|
1050
|
+
clearTimeout(timer);
|
|
1051
|
+
resolve2();
|
|
1052
|
+
},
|
|
1053
|
+
(error) => {
|
|
1054
|
+
clearTimeout(timer);
|
|
1055
|
+
reject(error);
|
|
1056
|
+
}
|
|
1057
|
+
);
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
var DeterministicAuthoredModulePlugin = class {
|
|
1061
|
+
#authoredComponentRoot;
|
|
1062
|
+
#scopeSource;
|
|
1063
|
+
constructor(authoredComponentRoot) {
|
|
1064
|
+
this.#authoredComponentRoot = realpathSync(authoredComponentRoot);
|
|
1065
|
+
this.#scopeSource = deterministicRuntimeScopeSource();
|
|
1066
|
+
}
|
|
1067
|
+
apply(compiler) {
|
|
1068
|
+
const pluginName = "CueFrameDeterministicAuthoredModulePlugin";
|
|
1069
|
+
compiler.hooks.compilation.tap(pluginName, (compilation) => {
|
|
1070
|
+
const hooks = compiler.webpack.javascript.JavascriptModulesPlugin.getCompilationHooks(compilation);
|
|
1071
|
+
const { ConcatSource } = compiler.webpack.sources;
|
|
1072
|
+
hooks.renderModuleContent.tap(
|
|
1073
|
+
pluginName,
|
|
1074
|
+
(source, module) => {
|
|
1075
|
+
if (!module.resource) return source;
|
|
1076
|
+
const resource = realpathSync(module.resource.split("?")[0]);
|
|
1077
|
+
const relativeResource = relative2(this.#authoredComponentRoot, resource);
|
|
1078
|
+
const belongsToAuthoredComponent = relativeResource === "" || relativeResource !== ".." && !relativeResource.startsWith(`..${sep2}`) && !isAbsolute(relativeResource);
|
|
1079
|
+
if (!belongsToAuthoredComponent) return source;
|
|
1080
|
+
return new ConcatSource(
|
|
1081
|
+
"const __cueframeHostGlobal = globalThis;\n{\nconst {globalThis, window, self, Math, Date, performance, crypto, requestAnimationFrame, setTimeout, setInterval, Worker, SharedWorker} = (",
|
|
1082
|
+
this.#scopeSource,
|
|
1083
|
+
")(__cueframeHostGlobal);\n{\n",
|
|
1084
|
+
source,
|
|
1085
|
+
"\n}\n}\n"
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
);
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
function buildLocalWebpackOverride(renderEntryRoot, authoredComponentRoot) {
|
|
1093
|
+
const animateEntry = join4(resolve(renderEntryRoot, ".."), "animate", "src", "index.ts");
|
|
1094
|
+
return (config) => ({
|
|
1095
|
+
...config,
|
|
1096
|
+
resolve: {
|
|
1097
|
+
...config.resolve,
|
|
1098
|
+
modules: [
|
|
1099
|
+
...config.resolve?.modules ?? ["node_modules"],
|
|
1100
|
+
join4(renderEntryRoot, "node_modules"),
|
|
1101
|
+
join4(resolve(renderEntryRoot, ".."), "animate", "node_modules"),
|
|
1102
|
+
// Private workspace packages are copied here for the installed preview runtime. Animate
|
|
1103
|
+
// imports their public subpaths directly, so webpack must search the staged tree rather
|
|
1104
|
+
// than accidentally relying on this checkout's root node_modules.
|
|
1105
|
+
join4(resolve(renderEntryRoot, ".."), "workspace", "node_modules"),
|
|
1106
|
+
join4(resolve(renderEntryRoot, "..", ".."), "node_modules"),
|
|
1107
|
+
join4(resolve(renderEntryRoot, "..", "..", ".."), "node_modules")
|
|
1108
|
+
],
|
|
1109
|
+
alias: {
|
|
1110
|
+
"@cueframe/animate$": animateEntry
|
|
1111
|
+
}
|
|
1112
|
+
},
|
|
1113
|
+
plugins: [
|
|
1114
|
+
...config.plugins ?? [],
|
|
1115
|
+
new DeterministicAuthoredModulePlugin(authoredComponentRoot)
|
|
1116
|
+
]
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
async function importFromRenderEntry(renderEntryRoot, spec) {
|
|
1120
|
+
const { createRequire } = await import("module");
|
|
1121
|
+
const { pathToFileURL } = await import("url");
|
|
1122
|
+
const requireFromRenderEntry = createRequire(join4(renderEntryRoot, "package.json"));
|
|
1123
|
+
return await import(pathToFileURL(requireFromRenderEntry.resolve(spec)).href);
|
|
1124
|
+
}
|
|
1125
|
+
var LOCAL_BROWSER_VERSION_PROBE_MS = 3e4;
|
|
1126
|
+
var stagedBrowserModule = null;
|
|
1127
|
+
async function verifyLocalStagedBrowser(renderEntryRoot) {
|
|
1128
|
+
const { pathToFileURL } = await import("url");
|
|
1129
|
+
stagedBrowserModule ??= await import(pathToFileURL(join4(renderEntryRoot, "scripts", "stagedBrowser.mjs")).href);
|
|
1130
|
+
return stagedBrowserModule.verifyStagedBrowser(process.env.CUEFRAME_BROWSER_DIR ?? null, {
|
|
1131
|
+
timeoutMs: LOCAL_BROWSER_VERSION_PROBE_MS
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
var laneWindowModule = null;
|
|
1135
|
+
async function installLocalLaneWindowSizer(renderEntryRoot, browser) {
|
|
1136
|
+
const { pathToFileURL } = await import("url");
|
|
1137
|
+
laneWindowModule ??= await import(pathToFileURL(join4(renderEntryRoot, "scripts", "laneWindow.mjs")).href);
|
|
1138
|
+
let refusal = null;
|
|
1139
|
+
const size = await laneWindowModule.installLaneWindowSizer(
|
|
1140
|
+
browser,
|
|
1141
|
+
(error) => {
|
|
1142
|
+
refusal ??= error;
|
|
1143
|
+
},
|
|
1144
|
+
LOCAL_BROWSER_VERSION_PROBE_MS
|
|
1145
|
+
);
|
|
1146
|
+
return async (width, height) => {
|
|
1147
|
+
if (refusal) throw refusal;
|
|
1148
|
+
await size(width, height);
|
|
1149
|
+
if (refusal) throw refusal;
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
async function sizeLocalLaneWindows(renderEntryRoot, browser, width, height) {
|
|
1153
|
+
const size = await installLocalLaneWindowSizer(renderEntryRoot, browser);
|
|
1154
|
+
await size(width, height);
|
|
1155
|
+
}
|
|
1156
|
+
function stagedBrowserLocator(error) {
|
|
1157
|
+
return stagedBrowserModule?.stagedBrowserLocator(error) ?? null;
|
|
1158
|
+
}
|
|
1159
|
+
function resolveRenderEntryRoot(override) {
|
|
1160
|
+
if (override) return override;
|
|
1161
|
+
if (process.env.RENDER_ENTRY_ROOT) return process.env.RENDER_ENTRY_ROOT;
|
|
1162
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
1163
|
+
return join4(resolve(here, "..", ".."), "render-entry");
|
|
1164
|
+
}
|
|
1165
|
+
function localSourceRevision(ws, id) {
|
|
1166
|
+
return sha256Hex(readFileSync2(join4(componentDir(ws, id), "index.tsx")));
|
|
1167
|
+
}
|
|
1168
|
+
var COMPONENT_STILL_RUNTIME_IDENTITY = "@cueframe/component-runtime/still-session-v1";
|
|
1169
|
+
var REMOTION_DISABLED_RASTER_FEATURES = "AudioServiceOutOfProcess,IsolateOrigins,site-per-process,Translate,BackForwardCache,AvoidUnnecessaryBeforeUnloadCheckSync,IntensiveWakeUpThrottling,LocalNetworkAccessChecks,BlockInsecurePrivateNetworkRequests,PrivateNetworkAccessSendPreflights,PrivateNetworkAccessRespectPreflightResults";
|
|
1170
|
+
function glLaunchArguments(backend) {
|
|
1171
|
+
if (backend === "swangle") return ["--use-gl=angle", "--use-angle=swiftshader"];
|
|
1172
|
+
if (backend === "angle-egl") return ["--use-gl=angle", "--use-angle=gl-egl"];
|
|
1173
|
+
if (backend === "vulkan") return [
|
|
1174
|
+
"--use-angle=vulkan",
|
|
1175
|
+
"--use-vulkan=native",
|
|
1176
|
+
"--disable-vulkan-fallback-to-gl-for-testing",
|
|
1177
|
+
"--disable-vulkan-surface",
|
|
1178
|
+
"--ignore-gpu-blocklist",
|
|
1179
|
+
"--enable-gpu"
|
|
1180
|
+
];
|
|
1181
|
+
return [`--use-gl=${backend}`];
|
|
1182
|
+
}
|
|
1183
|
+
function componentStillRasterAffectingFlags(graphics) {
|
|
1184
|
+
const browserBackend = componentBrowserBackend(graphics).chromiumOptions;
|
|
1185
|
+
const backend = browserBackend.gl;
|
|
1186
|
+
const features = backend === "vulkan" ? "NetworkService,NetworkServiceInProcess,Vulkan,UseSkiaRenderer" : backend === "angle-egl" ? "NetworkService,NetworkServiceInProcess,VaapiVideoDecoder" : "NetworkService,NetworkServiceInProcess";
|
|
1187
|
+
const args = [
|
|
1188
|
+
`--enable-features=${features}`,
|
|
1189
|
+
`--disable-features=${REMOTION_DISABLED_RASTER_FEATURES}`,
|
|
1190
|
+
"--force-color-profile=srgb",
|
|
1191
|
+
"--force-device-scale-factor=1",
|
|
1192
|
+
"--font-render-hinting=none",
|
|
1193
|
+
"--ignore-gpu-blocklist",
|
|
1194
|
+
"--enable-unsafe-webgpu",
|
|
1195
|
+
"--hide-scrollbars",
|
|
1196
|
+
"--force-gpu-mem-available-mb=4096",
|
|
1197
|
+
browserBackend.headless === false ? null : "--headless=new",
|
|
1198
|
+
process.platform === "linux" && backend !== "vulkan" && browserBackend.enableMultiProcessOnLinux !== true ? "--single-process" : null,
|
|
1199
|
+
...glLaunchArguments(backend)
|
|
1200
|
+
].filter((value) => value !== null);
|
|
1201
|
+
return { browserArguments: [...new Set(args)].sort() };
|
|
1202
|
+
}
|
|
1203
|
+
async function observedRasterAffectingFlags(browser, expected) {
|
|
1204
|
+
const result = await browser.connection.send("Browser.getBrowserCommandLine");
|
|
1205
|
+
const commandLine = result.value?.arguments;
|
|
1206
|
+
if (!Array.isArray(commandLine) || commandLine.some((arg) => typeof arg !== "string")) {
|
|
1207
|
+
throw new ComponentRuntimeError(
|
|
1208
|
+
"component_runtime_error",
|
|
1209
|
+
"The admitted browser did not expose its effective launch arguments."
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
const operationalPrefixes = [
|
|
1213
|
+
"--remote-debugging-port=",
|
|
1214
|
+
"--user-data-dir=",
|
|
1215
|
+
"--disk-cache-dir=",
|
|
1216
|
+
"--crash-dumps-dir=",
|
|
1217
|
+
"--log-file=",
|
|
1218
|
+
"--disk-cache-size=",
|
|
1219
|
+
"--proxy-bypass-list=",
|
|
1220
|
+
"--proxy-server="
|
|
1221
|
+
];
|
|
1222
|
+
const operationalExact = /* @__PURE__ */ new Set([
|
|
1223
|
+
"--no-first-run",
|
|
1224
|
+
"--no-default-browser-check",
|
|
1225
|
+
"--no-startup-window",
|
|
1226
|
+
"--allow-chrome-scheme-url",
|
|
1227
|
+
"--allow-pre-commit-input",
|
|
1228
|
+
"--allow-running-insecure-content",
|
|
1229
|
+
"--disable-background-media-suspend",
|
|
1230
|
+
"--disable-background-networking",
|
|
1231
|
+
"--disable-background-timer-throttling",
|
|
1232
|
+
"--disable-backgrounding-occluded-windows",
|
|
1233
|
+
"--disable-breakpad",
|
|
1234
|
+
"--disable-client-side-phishing-detection",
|
|
1235
|
+
"--disable-component-extensions-with-background-pages",
|
|
1236
|
+
"--disable-component-update",
|
|
1237
|
+
"--disable-default-apps",
|
|
1238
|
+
"--disable-dev-shm-usage",
|
|
1239
|
+
"--disable-domain-reliability",
|
|
1240
|
+
"--disable-extensions",
|
|
1241
|
+
"--disable-hang-monitor",
|
|
1242
|
+
"--disable-ipc-flooding-protection",
|
|
1243
|
+
"--disable-popup-blocking",
|
|
1244
|
+
"--disable-print-preview",
|
|
1245
|
+
"--disable-prompt-on-repost",
|
|
1246
|
+
"--disable-renderer-backgrounding",
|
|
1247
|
+
"--disable-setuid-sandbox",
|
|
1248
|
+
"--disable-site-isolation-trials",
|
|
1249
|
+
"--disable-sync",
|
|
1250
|
+
"--enable-automation",
|
|
1251
|
+
"--enable-blink-features=IdleDetection",
|
|
1252
|
+
"--export-tagged-pdf",
|
|
1253
|
+
"--flag-switches-begin",
|
|
1254
|
+
"--flag-switches-end",
|
|
1255
|
+
"--intensive-wake-up-throttling-policy=0",
|
|
1256
|
+
"--metrics-recording-only",
|
|
1257
|
+
"--mute-audio",
|
|
1258
|
+
"--no-pings",
|
|
1259
|
+
"--no-proxy-server",
|
|
1260
|
+
"--no-sandbox",
|
|
1261
|
+
"--no-zygote",
|
|
1262
|
+
"--noerrdialogs",
|
|
1263
|
+
"--password-store=basic",
|
|
1264
|
+
"--use-mock-keychain"
|
|
1265
|
+
]);
|
|
1266
|
+
const actual = commandLine.filter((argument) => argument.startsWith("--")).filter((argument) => !operationalExact.has(argument) && !operationalPrefixes.some((prefix) => argument.startsWith(prefix))).sort();
|
|
1267
|
+
const wanted = [...expected.browserArguments].sort();
|
|
1268
|
+
const videoThreads = actual.filter((argument) => /^--video-threads=[1-9]\d*$/.test(argument));
|
|
1269
|
+
const comparableActual = actual.filter((argument) => !argument.startsWith("--video-threads="));
|
|
1270
|
+
if (videoThreads.length !== 1 || JSON.stringify(comparableActual) !== JSON.stringify(wanted)) {
|
|
1271
|
+
const missing = wanted.filter((argument) => !actual.includes(argument));
|
|
1272
|
+
const unexpected = comparableActual.filter((argument) => !wanted.includes(argument));
|
|
1273
|
+
throw new ComponentRuntimeError(
|
|
1274
|
+
"component_runtime_error",
|
|
1275
|
+
`The admitted browser's raster-affecting launch arguments differ from the admitted configuration (missing: ${missing.join(", ") || "none"}; unexpected: ${unexpected.join(", ") || "none"}).`
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
return { browserArguments: actual };
|
|
1279
|
+
}
|
|
1280
|
+
function effectiveGlBackend(requested, renderer) {
|
|
1281
|
+
const observed = renderer.toLowerCase();
|
|
1282
|
+
if (observed.includes("swiftshader")) return "swiftshader";
|
|
1283
|
+
if (observed.includes("metal")) return "angle-metal";
|
|
1284
|
+
if (observed.includes("vulkan")) return "vulkan";
|
|
1285
|
+
if (observed.includes("opengl")) return requested.startsWith("angle") ? "angle-opengl" : "opengl";
|
|
1286
|
+
if (observed.includes("direct3d11") || observed.includes("d3d11")) return "angle-d3d11";
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
async function sessionBrowserVersion(browser) {
|
|
1290
|
+
const result = await browser.connection.send("Browser.getVersion");
|
|
1291
|
+
const product = result.value?.product;
|
|
1292
|
+
if (typeof product !== "string" || !product.includes("/")) {
|
|
1293
|
+
throw new ComponentRuntimeError("component_runtime_error", "The admitted browser did not report its product and version.");
|
|
1294
|
+
}
|
|
1295
|
+
const slash = product.indexOf("/");
|
|
1296
|
+
return { product: product.slice(0, slash), version: product.slice(slash + 1) };
|
|
1297
|
+
}
|
|
1298
|
+
async function componentStillDependencyIdentity(renderEntryRoot) {
|
|
1299
|
+
const { createRequire } = await import("module");
|
|
1300
|
+
const requireFromRenderEntry = createRequire(join4(renderEntryRoot, "package.json"));
|
|
1301
|
+
let packageRoot = dirname(requireFromRenderEntry.resolve("@remotion/renderer"));
|
|
1302
|
+
while (!existsSync2(join4(packageRoot, "package.json"))) {
|
|
1303
|
+
const parent = dirname(packageRoot);
|
|
1304
|
+
if (parent === packageRoot) {
|
|
1305
|
+
throw new ComponentRuntimeError(
|
|
1306
|
+
"component_runtime_error",
|
|
1307
|
+
"The staged Remotion renderer package manifest could not be located."
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
packageRoot = parent;
|
|
1311
|
+
}
|
|
1312
|
+
const manifest = JSON.parse(
|
|
1313
|
+
readFileSync2(join4(packageRoot, "package.json"), "utf8")
|
|
1314
|
+
);
|
|
1315
|
+
if (manifest.name !== "@remotion/renderer" || typeof manifest.version !== "string" || !manifest.version) {
|
|
1316
|
+
throw new ComponentRuntimeError(
|
|
1317
|
+
"component_runtime_error",
|
|
1318
|
+
"The staged Remotion renderer has no verifiable package identity."
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
return `${manifest.name}@${manifest.version}/renderStill`;
|
|
1322
|
+
}
|
|
1323
|
+
function pngContainerFacts(path, bytes) {
|
|
1324
|
+
const signature = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
1325
|
+
const isPng = bytes.length >= 24 && signature.every((value, index) => bytes[index] === value) && String.fromCharCode(...bytes.slice(12, 16)) === "IHDR";
|
|
1326
|
+
if (!isPng) {
|
|
1327
|
+
throw new ComponentRuntimeError("component_runtime_error", "Component still output is not a PNG container.");
|
|
1328
|
+
}
|
|
1329
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
1330
|
+
return ComponentStillPngContainerFactsV1.parse({
|
|
1331
|
+
handle: path,
|
|
1332
|
+
mimeType: "image/png",
|
|
1333
|
+
byteLength: bytes.byteLength,
|
|
1334
|
+
width: view.getUint32(16),
|
|
1335
|
+
height: view.getUint32(20),
|
|
1336
|
+
encodedSha256: sha256Hex(bytes)
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
async function prepareLocalBundle(opts) {
|
|
1340
|
+
const pinnedManifest = readMaterializedComponentSource(opts.ws, opts.id)?.manifest ?? null;
|
|
1341
|
+
if (pinnedManifest) assertSceneShotParamsAdmitted(pinnedManifest, opts.params, { componentId: opts.id });
|
|
1342
|
+
const renderEntryRoot = resolveRenderEntryRoot(opts.renderEntryRoot);
|
|
1343
|
+
const stagedBrowser = await verifyLocalStagedBrowser(renderEntryRoot);
|
|
1344
|
+
const browserExecutable = stagedBrowser.path;
|
|
1345
|
+
const inputProps = {
|
|
1346
|
+
params: opts.params,
|
|
1347
|
+
assets: opts.assets ?? {},
|
|
1348
|
+
seed: opts.seed ?? "cueframe",
|
|
1349
|
+
graphics: opts.graphics ?? {
|
|
1350
|
+
api: "canvas2d",
|
|
1351
|
+
required: true,
|
|
1352
|
+
accelerationPreference: "software-allowed"
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
const entryPoint = generateLocalRoot(opts.ws, {
|
|
1356
|
+
id: opts.id,
|
|
1357
|
+
width: opts.width,
|
|
1358
|
+
height: opts.height,
|
|
1359
|
+
fps: opts.fps,
|
|
1360
|
+
durationInFrames: opts.durationInFrames,
|
|
1361
|
+
revision: opts.revision,
|
|
1362
|
+
liveInputProps: inputProps,
|
|
1363
|
+
fontFaces: opts.fontFaces
|
|
1364
|
+
});
|
|
1365
|
+
const { bundle } = await importFromRenderEntry(
|
|
1366
|
+
renderEntryRoot,
|
|
1367
|
+
"@remotion/bundler"
|
|
1368
|
+
);
|
|
1369
|
+
let serveUrl;
|
|
1370
|
+
try {
|
|
1371
|
+
serveUrl = await bundle({
|
|
1372
|
+
entryPoint,
|
|
1373
|
+
rootDir: process.env.CUEFRAME_COMPONENT_BUNDLE_CACHE ?? null,
|
|
1374
|
+
// Prepared scene components load the pinned photographic environment via
|
|
1375
|
+
// Remotion staticFile(). Use the verified render-entry public tree so the
|
|
1376
|
+
// local preview and composition renderer consume the same exact bytes.
|
|
1377
|
+
publicDir: join4(renderEntryRoot, "public"),
|
|
1378
|
+
// NAMED, like the composition lane's (`render-entry/scripts/render.mjs`). Left to Remotion,
|
|
1379
|
+
// `bundle()` mkdtemps `remotion-webpack-bundle-*` into the system temp and never removes it
|
|
1380
|
+
// (@remotion/bundler `prepareOutDir`) — ~28 MB per render into a directory nothing owns, which
|
|
1381
|
+
// is how 5.9 GB of them accumulated before Stage 9 counted. Inside the component's own
|
|
1382
|
+
// workspace directory it lives and dies with the workspace the caller already tears down,
|
|
1383
|
+
// and nothing new needs cleaning up.
|
|
1384
|
+
outDir: join4(componentDir(opts.ws, opts.id), ".remotion-bundle"),
|
|
1385
|
+
webpackOverride: buildLocalWebpackOverride(renderEntryRoot, componentDir(opts.ws, opts.id))
|
|
1386
|
+
});
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
throw componentRuntimeError(error, "component_does_not_compile");
|
|
1389
|
+
}
|
|
1390
|
+
return {
|
|
1391
|
+
serveUrl,
|
|
1392
|
+
browserExecutable,
|
|
1393
|
+
stagedBrowser,
|
|
1394
|
+
inputProps,
|
|
1395
|
+
renderEntryRoot,
|
|
1396
|
+
pinnedManifest
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
async function createComponentStillSession(opts) {
|
|
1400
|
+
const width = opts.width ?? 1920;
|
|
1401
|
+
const height = opts.height ?? 1080;
|
|
1402
|
+
const fps = opts.fps ?? 30;
|
|
1403
|
+
const durationInFrames = opts.durationInFrames ?? 1;
|
|
1404
|
+
const scale = opts.scale ?? 1;
|
|
1405
|
+
for (const [field, value] of [["width", width], ["height", height], ["durationInFrames", durationInFrames]]) {
|
|
1406
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
1407
|
+
throw new ComponentRuntimeError("component_runtime_error", `${field} must be a positive safe integer.`);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
if (!Number.isFinite(fps) || fps <= 0) {
|
|
1411
|
+
throw new ComponentRuntimeError("component_runtime_error", "fps must be a finite number greater than zero.");
|
|
1412
|
+
}
|
|
1413
|
+
if (!Number.isFinite(scale) || scale <= 0) {
|
|
1414
|
+
throw new ComponentRuntimeError("component_runtime_error", "Component still scale must be a finite number greater than zero.");
|
|
1415
|
+
}
|
|
1416
|
+
const revision = opts.revision ?? localSourceRevision(opts.ws, opts.id);
|
|
1417
|
+
const allowedAssetOrigins = new Set(opts.allowedAssetOrigins ?? []);
|
|
1418
|
+
opts.onStartupPhase?.("prepare");
|
|
1419
|
+
const { serveUrl, browserExecutable, stagedBrowser, inputProps, renderEntryRoot, pinnedManifest } = await prepareLocalBundle({
|
|
1420
|
+
ws: opts.ws,
|
|
1421
|
+
id: opts.id,
|
|
1422
|
+
width,
|
|
1423
|
+
height,
|
|
1424
|
+
fps,
|
|
1425
|
+
durationInFrames,
|
|
1426
|
+
params: opts.params,
|
|
1427
|
+
assets: opts.assets,
|
|
1428
|
+
seed: opts.seed,
|
|
1429
|
+
graphics: opts.graphics,
|
|
1430
|
+
renderEntryRoot: opts.renderEntryRoot,
|
|
1431
|
+
revision,
|
|
1432
|
+
fontFaces: opts.fontFaces
|
|
1433
|
+
});
|
|
1434
|
+
opts.onStartupPhase?.("prepared");
|
|
1435
|
+
const { openBrowser, selectComposition, renderStill } = await importFromRenderEntry(
|
|
1436
|
+
renderEntryRoot,
|
|
1437
|
+
"@remotion/renderer"
|
|
1438
|
+
);
|
|
1439
|
+
const requiredGraphics = opts.graphics ?? {
|
|
1440
|
+
api: "canvas2d",
|
|
1441
|
+
required: true,
|
|
1442
|
+
accelerationPreference: "software-allowed"
|
|
1443
|
+
};
|
|
1444
|
+
const isolatesFrameBrowsers = shouldIsolateWebGpuStillBrowser(process.platform, requiredGraphics.api);
|
|
1445
|
+
const collector = graphicsCollector(requiredGraphics);
|
|
1446
|
+
const launch = {
|
|
1447
|
+
...componentBrowserOptions(requiredGraphics, browserExecutable, stagedBrowser.chromeMode),
|
|
1448
|
+
// Pin the value Remotion otherwise defaults internally so the attestation describes an
|
|
1449
|
+
// explicit launch fact rather than an assumed browser default.
|
|
1450
|
+
forceDeviceScaleFactor: 1
|
|
1451
|
+
};
|
|
1452
|
+
let browser;
|
|
1453
|
+
let firewall;
|
|
1454
|
+
let composition;
|
|
1455
|
+
let hostAttestation = null;
|
|
1456
|
+
for (let attempt = 1; attempt <= COMPONENT_BROWSER_FIREWALL_ATTEMPTS; attempt += 1) {
|
|
1457
|
+
let candidateBrowser;
|
|
1458
|
+
let candidateFirewall;
|
|
1459
|
+
try {
|
|
1460
|
+
opts.onStartupPhase?.("open-browser");
|
|
1461
|
+
candidateBrowser = await openBrowser("chrome", launch);
|
|
1462
|
+
opts.onStartupPhase?.("browser-open");
|
|
1463
|
+
await sizeLocalLaneWindows(renderEntryRoot, candidateBrowser, width, height);
|
|
1464
|
+
opts.onStartupPhase?.("firewall");
|
|
1465
|
+
candidateFirewall = await withComponentBrowserFirewallTimeout(installComponentRequestFirewall(
|
|
1466
|
+
candidateBrowser,
|
|
1467
|
+
allowedAssetOrigins,
|
|
1468
|
+
opts.onBrowserLog ? (entry) => opts.onBrowserLog?.(entry) : void 0
|
|
1469
|
+
));
|
|
1470
|
+
await assertComponentBrowserHardware(candidateBrowser, requiredGraphics);
|
|
1471
|
+
let candidateAttestation = null;
|
|
1472
|
+
if (opts.captureAttestation) {
|
|
1473
|
+
const gpu = await readGpuAttestation(candidateBrowser);
|
|
1474
|
+
if (!gpu.renderer) {
|
|
1475
|
+
throw new ComponentRuntimeError(
|
|
1476
|
+
"component_graphics_unavailable",
|
|
1477
|
+
"The admitted browser did not report its actual renderer, so capture identity cannot be established."
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
const requestedBackend = componentBrowserBackend(requiredGraphics).chromiumOptions.gl;
|
|
1481
|
+
const actualGlBackend = effectiveGlBackend(requestedBackend, gpu.renderer);
|
|
1482
|
+
if (actualGlBackend === null) {
|
|
1483
|
+
throw new ComponentRuntimeError(
|
|
1484
|
+
"component_graphics_unavailable",
|
|
1485
|
+
`The admitted browser reported an unrecognized renderer (${gpu.renderer}).`
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
const browserVersion = await sessionBrowserVersion(candidateBrowser);
|
|
1489
|
+
if (browserVersion.version !== stagedBrowser.version) {
|
|
1490
|
+
throw new ComponentRuntimeError(
|
|
1491
|
+
"component_runtime_error",
|
|
1492
|
+
`The admitted browser version ${browserVersion.version} does not match staged browser version ${stagedBrowser.version}.`
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
const rasterAffectingFlags = componentStillRasterAffectingFlags(requiredGraphics);
|
|
1496
|
+
candidateAttestation = ComponentStillSessionHostAttestationV1.parse({
|
|
1497
|
+
loadedRuntimeIdentity: COMPONENT_STILL_RUNTIME_IDENTITY,
|
|
1498
|
+
loadedDependencyIdentity: await componentStillDependencyIdentity(renderEntryRoot),
|
|
1499
|
+
observedRasterAffectingFlags: await observedRasterAffectingFlags(candidateBrowser, rasterAffectingFlags),
|
|
1500
|
+
browserProduct: browserVersion.product,
|
|
1501
|
+
browserVersion: browserVersion.version,
|
|
1502
|
+
browserExecutableSha256: stagedBrowser.sha256,
|
|
1503
|
+
headless: launch.chromiumOptions.headless !== false,
|
|
1504
|
+
os: process.platform,
|
|
1505
|
+
arch: process.arch,
|
|
1506
|
+
// Remotion's admitted browser launch fixes `--force-color-profile=srgb`; its exact package
|
|
1507
|
+
// version is sealed above as dependencyIdentity.
|
|
1508
|
+
colorProfile: "srgb",
|
|
1509
|
+
actualGlBackend,
|
|
1510
|
+
actualRenderer: gpu.renderer,
|
|
1511
|
+
gpuIdentity: gpu.gpuIdentity
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
opts.onStartupPhase?.("select-composition");
|
|
1515
|
+
const candidateComposition = await selectComposition({
|
|
1516
|
+
serveUrl,
|
|
1517
|
+
id: LOCAL_COMPOSITION_ID,
|
|
1518
|
+
inputProps,
|
|
1519
|
+
puppeteerInstance: candidateBrowser,
|
|
1520
|
+
timeoutInMilliseconds: 3e4,
|
|
1521
|
+
onBrowserLog: (entry) => {
|
|
1522
|
+
collector.onBrowserLog(entry);
|
|
1523
|
+
opts.onBrowserLog?.(entry);
|
|
1524
|
+
}
|
|
1525
|
+
});
|
|
1526
|
+
const denied = candidateFirewall.deniedRequests();
|
|
1527
|
+
if (denied.length > 0) {
|
|
1528
|
+
throw new ComponentRuntimeError("component_network_denied", `Component attempted ${denied.length} denied network request(s).`);
|
|
1529
|
+
}
|
|
1530
|
+
browser = candidateBrowser;
|
|
1531
|
+
firewall = candidateFirewall;
|
|
1532
|
+
composition = candidateComposition;
|
|
1533
|
+
hostAttestation = candidateAttestation;
|
|
1534
|
+
break;
|
|
1535
|
+
} catch (error) {
|
|
1536
|
+
try {
|
|
1537
|
+
const cleanup = candidateFirewall ? candidateFirewall.close() : candidateBrowser?.close({ silent: true });
|
|
1538
|
+
if (cleanup) await withComponentBrowserCleanupTimeout(cleanup);
|
|
1539
|
+
} catch {
|
|
1540
|
+
}
|
|
1541
|
+
if (!candidateBrowser) {
|
|
1542
|
+
throw componentRuntimeError(error, "component_resource_exhausted", true);
|
|
1543
|
+
}
|
|
1544
|
+
if (error instanceof ComponentBrowserFirewallTimeout && attempt < COMPONENT_BROWSER_FIREWALL_ATTEMPTS) continue;
|
|
1545
|
+
const retryable = error instanceof ComponentBrowserFirewallTimeout;
|
|
1546
|
+
throw componentRuntimeError(
|
|
1547
|
+
error,
|
|
1548
|
+
retryable ? "component_deadline_exceeded" : "component_runtime_error",
|
|
1549
|
+
retryable
|
|
1550
|
+
);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
opts.onStartupPhase?.("composition-ready");
|
|
1554
|
+
let liveProxy;
|
|
1555
|
+
try {
|
|
1556
|
+
opts.onStartupPhase?.("start-proxy");
|
|
1557
|
+
liveProxy = await startLivePreviewProxy(serveUrl, allowedAssetOrigins);
|
|
1558
|
+
} catch (error) {
|
|
1559
|
+
let cleanupError;
|
|
1560
|
+
await firewall.close().catch((caught) => {
|
|
1561
|
+
cleanupError = caught;
|
|
1562
|
+
});
|
|
1563
|
+
const failure = componentRuntimeError(error, "component_runtime_error");
|
|
1564
|
+
if (cleanupError !== void 0 && failure.cause === void 0) failure.cause = cleanupError;
|
|
1565
|
+
throw failure;
|
|
1566
|
+
}
|
|
1567
|
+
opts.onStartupPhase?.("ready");
|
|
1568
|
+
let state = "open";
|
|
1569
|
+
let tail = Promise.resolve();
|
|
1570
|
+
let closePromise = null;
|
|
1571
|
+
let cancelPromise = null;
|
|
1572
|
+
const sessionId = randomUUID();
|
|
1573
|
+
let requestId = 0;
|
|
1574
|
+
const ownedOutputs = /* @__PURE__ */ new Set();
|
|
1575
|
+
const enqueue = (operation) => {
|
|
1576
|
+
const result = tail.then(operation, operation);
|
|
1577
|
+
tail = result.then(() => void 0, () => void 0);
|
|
1578
|
+
return result;
|
|
1579
|
+
};
|
|
1580
|
+
const session = {
|
|
1581
|
+
revision,
|
|
1582
|
+
liveUrl: liveProxy.url,
|
|
1583
|
+
hostAttestation,
|
|
1584
|
+
renderFrame(frameOpts = {}) {
|
|
1585
|
+
if (state !== "open") {
|
|
1586
|
+
return Promise.reject(new ComponentRuntimeError("component_conflict", "Component preview session is closing or closed."));
|
|
1587
|
+
}
|
|
1588
|
+
return enqueue(async () => {
|
|
1589
|
+
const fatal = firewall.fatalError();
|
|
1590
|
+
if (fatal !== null) {
|
|
1591
|
+
state = "closed";
|
|
1592
|
+
closePromise ??= Promise.all([firewall.close(), liveProxy.close()]).then(() => void 0);
|
|
1593
|
+
throw componentRuntimeError(fatal, "component_runtime_error");
|
|
1594
|
+
}
|
|
1595
|
+
if (frameOpts.expectedRevision !== void 0 && frameOpts.expectedRevision !== revision) {
|
|
1596
|
+
throw new ComponentRuntimeError("component_conflict", "Component preview request targets a stale source revision.");
|
|
1597
|
+
}
|
|
1598
|
+
const frame = frameOpts.frame ?? 0;
|
|
1599
|
+
if (!Number.isInteger(frame) || frame < 0 || frame >= durationInFrames) {
|
|
1600
|
+
throw new ComponentRuntimeError(
|
|
1601
|
+
"component_runtime_error",
|
|
1602
|
+
`Component preview frame must be an integer from 0 through ${durationInFrames - 1}.`
|
|
1603
|
+
);
|
|
1604
|
+
}
|
|
1605
|
+
if (frameOpts.params && pinnedManifest) {
|
|
1606
|
+
assertSceneShotParamsAdmitted(pinnedManifest, frameOpts.params, { componentId: opts.id });
|
|
1607
|
+
}
|
|
1608
|
+
const captureId = requestId++;
|
|
1609
|
+
const requestInputProps = {
|
|
1610
|
+
...inputProps,
|
|
1611
|
+
params: frameOpts.params ?? opts.params,
|
|
1612
|
+
assets: frameOpts.assets ?? opts.assets ?? {},
|
|
1613
|
+
seed: frameOpts.seed ?? opts.seed ?? "cueframe",
|
|
1614
|
+
...opts.captureAttestation ? { __cueframeCaptureReceipt: { sessionId, requestId: captureId } } : {}
|
|
1615
|
+
};
|
|
1616
|
+
const deniedBefore = firewall.deniedRequests().length;
|
|
1617
|
+
const pngPath = frameOpts.outputPath ?? join4(cacheDir(opts.ws), `still-${opts.id}-${sessionId}-${captureId}.png`);
|
|
1618
|
+
const outputOwnedBySession = frameOpts.outputPath === void 0;
|
|
1619
|
+
if (outputOwnedBySession) ownedOutputs.add(pngPath);
|
|
1620
|
+
const removeOwnedOutput = async () => {
|
|
1621
|
+
if (!outputOwnedBySession) return;
|
|
1622
|
+
await unlink(pngPath).catch((error) => {
|
|
1623
|
+
if (error.code !== "ENOENT") throw error;
|
|
1624
|
+
});
|
|
1625
|
+
ownedOutputs.delete(pngPath);
|
|
1626
|
+
};
|
|
1627
|
+
const refuseAfterCleanup = async (error) => {
|
|
1628
|
+
await removeOwnedOutput().catch((cleanupError) => {
|
|
1629
|
+
if (error.cause === void 0) error.cause = cleanupError;
|
|
1630
|
+
else process.stderr.write(`[component-runtime] output cleanup failed: ${String(cleanupError)}
|
|
1631
|
+
`);
|
|
1632
|
+
});
|
|
1633
|
+
throw error;
|
|
1634
|
+
};
|
|
1635
|
+
const frameCollector = graphicsCollector(requiredGraphics);
|
|
1636
|
+
const capturePageRaster = { value: null };
|
|
1637
|
+
let frameBrowser = browser;
|
|
1638
|
+
let frameFirewall = firewall;
|
|
1639
|
+
let frameComposition = composition;
|
|
1640
|
+
let ownsFrameBrowser = false;
|
|
1641
|
+
try {
|
|
1642
|
+
if (isolatesFrameBrowsers) {
|
|
1643
|
+
for (let attempt = 1; attempt <= COMPONENT_BROWSER_FIREWALL_ATTEMPTS; attempt += 1) {
|
|
1644
|
+
let candidateBrowser;
|
|
1645
|
+
let candidateFirewall;
|
|
1646
|
+
try {
|
|
1647
|
+
candidateBrowser = await openBrowser("chrome", launch);
|
|
1648
|
+
await sizeLocalLaneWindows(renderEntryRoot, candidateBrowser, width, height);
|
|
1649
|
+
candidateFirewall = await withComponentBrowserFirewallTimeout(installComponentRequestFirewall(
|
|
1650
|
+
candidateBrowser,
|
|
1651
|
+
allowedAssetOrigins,
|
|
1652
|
+
opts.onBrowserLog ? (entry) => opts.onBrowserLog?.(entry) : void 0
|
|
1653
|
+
));
|
|
1654
|
+
await assertComponentBrowserHardware(candidateBrowser, requiredGraphics);
|
|
1655
|
+
const candidateComposition = await selectComposition({
|
|
1656
|
+
serveUrl,
|
|
1657
|
+
id: LOCAL_COMPOSITION_ID,
|
|
1658
|
+
inputProps: requestInputProps,
|
|
1659
|
+
puppeteerInstance: candidateBrowser,
|
|
1660
|
+
timeoutInMilliseconds: 3e4,
|
|
1661
|
+
onBrowserLog: frameCollector.onBrowserLog
|
|
1662
|
+
});
|
|
1663
|
+
frameBrowser = candidateBrowser;
|
|
1664
|
+
frameFirewall = candidateFirewall;
|
|
1665
|
+
frameComposition = candidateComposition;
|
|
1666
|
+
ownsFrameBrowser = true;
|
|
1667
|
+
break;
|
|
1668
|
+
} catch (error) {
|
|
1669
|
+
try {
|
|
1670
|
+
const cleanup = candidateFirewall ? candidateFirewall.close() : candidateBrowser?.close({ silent: true });
|
|
1671
|
+
if (cleanup) await withComponentBrowserCleanupTimeout(cleanup);
|
|
1672
|
+
} catch {
|
|
1673
|
+
}
|
|
1674
|
+
if (!candidateBrowser) {
|
|
1675
|
+
throw componentRuntimeError(error, "component_resource_exhausted", true);
|
|
1676
|
+
}
|
|
1677
|
+
if (error instanceof ComponentBrowserFirewallTimeout && attempt < COMPONENT_BROWSER_FIREWALL_ATTEMPTS) continue;
|
|
1678
|
+
const retryable = error instanceof ComponentBrowserFirewallTimeout;
|
|
1679
|
+
throw componentRuntimeError(
|
|
1680
|
+
error,
|
|
1681
|
+
retryable ? "component_deadline_exceeded" : "component_runtime_error",
|
|
1682
|
+
retryable
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
const frameDeniedBefore = frameFirewall.deniedRequests().length;
|
|
1688
|
+
const stillOptions = {
|
|
1689
|
+
// Remotion sends `composition.props` as the resolved props. Keep it
|
|
1690
|
+
// aligned with each request when reusing a selected composition;
|
|
1691
|
+
// otherwise later preview params can silently render the first call.
|
|
1692
|
+
composition: { ...frameComposition, props: requestInputProps },
|
|
1693
|
+
serveUrl,
|
|
1694
|
+
inputProps: requestInputProps,
|
|
1695
|
+
output: pngPath,
|
|
1696
|
+
frame,
|
|
1697
|
+
scale,
|
|
1698
|
+
puppeteerInstance: frameBrowser,
|
|
1699
|
+
timeoutInMilliseconds: 12e4,
|
|
1700
|
+
onBrowserLog(log) {
|
|
1701
|
+
frameCollector.onBrowserLog(log);
|
|
1702
|
+
const parsed = parseCapturePageRasterAttestationLog(log.text);
|
|
1703
|
+
if (!parsed) return;
|
|
1704
|
+
if (parsed.sessionId !== sessionId || parsed.requestId !== captureId || parsed.frame !== frame) {
|
|
1705
|
+
throw new ComponentRuntimeError(
|
|
1706
|
+
"component_runtime_error",
|
|
1707
|
+
"Component capture page reported raster facts for a different request or frame."
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
if (capturePageRaster.value && JSON.stringify(capturePageRaster.value) !== JSON.stringify(parsed)) {
|
|
1711
|
+
throw new ComponentRuntimeError(
|
|
1712
|
+
"component_runtime_error",
|
|
1713
|
+
"Component capture page reported inconsistent raster facts."
|
|
1714
|
+
);
|
|
1715
|
+
}
|
|
1716
|
+
capturePageRaster.value = parsed;
|
|
1717
|
+
}
|
|
1718
|
+
};
|
|
1719
|
+
await renderStill(stillOptions);
|
|
1720
|
+
const frameDenied = frameFirewall.deniedRequests().length - frameDeniedBefore;
|
|
1721
|
+
if (frameDenied > 0) {
|
|
1722
|
+
throw new ComponentRuntimeError("component_network_denied", `Component attempted ${frameDenied} denied network request(s).`);
|
|
1723
|
+
}
|
|
1724
|
+
const frameFatal = frameFirewall.fatalError();
|
|
1725
|
+
if (frameFatal !== null) throw frameFatal;
|
|
1726
|
+
} catch (error) {
|
|
1727
|
+
await removeOwnedOutput().catch((cleanupError) => {
|
|
1728
|
+
if (error instanceof Error && error.cause === void 0) error.cause = cleanupError;
|
|
1729
|
+
});
|
|
1730
|
+
throw componentRuntimeError(error, "component_runtime_error");
|
|
1731
|
+
} finally {
|
|
1732
|
+
if (ownsFrameBrowser) {
|
|
1733
|
+
await withComponentBrowserCleanupTimeout(frameFirewall.close()).catch(() => {
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
const denied = isolatesFrameBrowsers ? 0 : firewall.deniedRequests().length - deniedBefore;
|
|
1738
|
+
if (denied > 0) {
|
|
1739
|
+
return refuseAfterCleanup(new ComponentRuntimeError("component_network_denied", `Component attempted ${denied} denied network request(s).`));
|
|
1740
|
+
}
|
|
1741
|
+
const renderFatal = firewall.fatalError();
|
|
1742
|
+
if (renderFatal !== null) {
|
|
1743
|
+
state = "closed";
|
|
1744
|
+
closePromise ??= Promise.all([firewall.close(), liveProxy.close()]).then(() => void 0);
|
|
1745
|
+
return refuseAfterCleanup(componentRuntimeError(renderFatal, "component_runtime_error"));
|
|
1746
|
+
}
|
|
1747
|
+
if (opts.captureAttestation && capturePageRaster.value === null) {
|
|
1748
|
+
return refuseAfterCleanup(new ComponentRuntimeError(
|
|
1749
|
+
"component_runtime_error",
|
|
1750
|
+
"Component capture page produced no correlated raster attestation."
|
|
1751
|
+
));
|
|
1752
|
+
}
|
|
1753
|
+
if (capturePageRaster.value && capturePageRaster.value.deviceScaleFactor !== scale) {
|
|
1754
|
+
return refuseAfterCleanup(new ComponentRuntimeError(
|
|
1755
|
+
"component_runtime_error",
|
|
1756
|
+
`Component capture page used deviceScaleFactor ${capturePageRaster.value.deviceScaleFactor}; expected ${scale}.`
|
|
1757
|
+
));
|
|
1758
|
+
}
|
|
1759
|
+
return { pngPath, revision, actualGraphics: frameCollector.receipt(), capturePageRaster: capturePageRaster.value, outputOwnedBySession };
|
|
1760
|
+
});
|
|
1761
|
+
},
|
|
1762
|
+
async renderVerifiedFrame(frameOpts = {}) {
|
|
1763
|
+
const rendered = await session.renderFrame(frameOpts);
|
|
1764
|
+
const refuseVerified = async (error) => {
|
|
1765
|
+
if (rendered.outputOwnedBySession) {
|
|
1766
|
+
let removed = false;
|
|
1767
|
+
try {
|
|
1768
|
+
await unlink(rendered.pngPath);
|
|
1769
|
+
removed = true;
|
|
1770
|
+
} catch (cleanupError) {
|
|
1771
|
+
if (cleanupError?.code === "ENOENT") removed = true;
|
|
1772
|
+
else if (error.cause === void 0) error.cause = cleanupError;
|
|
1773
|
+
else process.stderr.write(`[component-runtime] output cleanup failed: ${String(cleanupError)}
|
|
1774
|
+
`);
|
|
1775
|
+
}
|
|
1776
|
+
if (removed) ownedOutputs.delete(rendered.pngPath);
|
|
1777
|
+
}
|
|
1778
|
+
throw error;
|
|
1779
|
+
};
|
|
1780
|
+
let bytes;
|
|
1781
|
+
try {
|
|
1782
|
+
bytes = await readFile2(rendered.pngPath);
|
|
1783
|
+
} catch (error) {
|
|
1784
|
+
let cleanupError;
|
|
1785
|
+
if (rendered.outputOwnedBySession) await unlink(rendered.pngPath).catch((caught) => {
|
|
1786
|
+
if (caught.code !== "ENOENT") cleanupError = caught;
|
|
1787
|
+
});
|
|
1788
|
+
if (cleanupError === void 0) ownedOutputs.delete(rendered.pngPath);
|
|
1789
|
+
const failure = new ComponentRuntimeError(
|
|
1790
|
+
"component_runtime_error",
|
|
1791
|
+
`Component still output could not be read: ${error instanceof Error ? error.message : String(error)}.`
|
|
1792
|
+
);
|
|
1793
|
+
if (cleanupError !== void 0) failure.cause = cleanupError;
|
|
1794
|
+
throw failure;
|
|
1795
|
+
}
|
|
1796
|
+
if (!session.hostAttestation || !rendered.capturePageRaster) {
|
|
1797
|
+
return refuseVerified(new ComponentRuntimeError(
|
|
1798
|
+
"component_runtime_error",
|
|
1799
|
+
"Verified component capture requires a session opened with captureAttestation."
|
|
1800
|
+
));
|
|
1801
|
+
}
|
|
1802
|
+
let png;
|
|
1803
|
+
try {
|
|
1804
|
+
png = pngContainerFacts(rendered.pngPath, bytes);
|
|
1805
|
+
} catch (error) {
|
|
1806
|
+
return refuseVerified(error instanceof Error ? error : new Error(String(error)));
|
|
1807
|
+
}
|
|
1808
|
+
return {
|
|
1809
|
+
png,
|
|
1810
|
+
revision: rendered.revision,
|
|
1811
|
+
actualGraphics: rendered.actualGraphics,
|
|
1812
|
+
attestation: ComponentStillSessionAttestationV1.parse({
|
|
1813
|
+
...session.hostAttestation,
|
|
1814
|
+
deviceScaleFactor: rendered.capturePageRaster.deviceScaleFactor
|
|
1815
|
+
})
|
|
1816
|
+
};
|
|
1817
|
+
},
|
|
1818
|
+
close() {
|
|
1819
|
+
if (closePromise) return closePromise;
|
|
1820
|
+
state = "closing";
|
|
1821
|
+
closePromise = enqueue(async () => {
|
|
1822
|
+
try {
|
|
1823
|
+
await Promise.all([
|
|
1824
|
+
firewall.close(),
|
|
1825
|
+
liveProxy.close(),
|
|
1826
|
+
...[...ownedOutputs].map((path) => unlink(path).catch((error) => {
|
|
1827
|
+
if (error.code !== "ENOENT") throw error;
|
|
1828
|
+
}))
|
|
1829
|
+
]);
|
|
1830
|
+
ownedOutputs.clear();
|
|
1831
|
+
} finally {
|
|
1832
|
+
state = "closed";
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1835
|
+
return closePromise;
|
|
1836
|
+
},
|
|
1837
|
+
cancel() {
|
|
1838
|
+
if (cancelPromise) return cancelPromise;
|
|
1839
|
+
state = "closing";
|
|
1840
|
+
const browserClose = Promise.all([firewall.close(), liveProxy.close()]);
|
|
1841
|
+
cancelPromise = (async () => {
|
|
1842
|
+
let closeError;
|
|
1843
|
+
try {
|
|
1844
|
+
await browserClose;
|
|
1845
|
+
} catch (error) {
|
|
1846
|
+
closeError = error;
|
|
1847
|
+
}
|
|
1848
|
+
await tail;
|
|
1849
|
+
const outputCleanup = await Promise.allSettled([...ownedOutputs].map((path) => unlink(path)));
|
|
1850
|
+
ownedOutputs.clear();
|
|
1851
|
+
state = "closed";
|
|
1852
|
+
const outputFailures = outputCleanup.flatMap((result) => result.status === "rejected" && result.reason?.code !== "ENOENT" ? [result.reason] : []);
|
|
1853
|
+
if (closeError !== void 0) {
|
|
1854
|
+
if (outputFailures.length > 0 && closeError instanceof Error && closeError.cause === void 0) {
|
|
1855
|
+
closeError.cause = new AggregateError(outputFailures, "Component capture output cleanup failed.");
|
|
1856
|
+
} else if (outputFailures.length > 0) {
|
|
1857
|
+
throw new AggregateError([closeError, ...outputFailures], "Component session cancellation and output cleanup failed.");
|
|
1858
|
+
}
|
|
1859
|
+
throw closeError;
|
|
1860
|
+
}
|
|
1861
|
+
if (outputFailures.length > 0) throw new AggregateError(outputFailures, "Component capture output cleanup failed.");
|
|
1862
|
+
})();
|
|
1863
|
+
closePromise ??= cancelPromise;
|
|
1864
|
+
return cancelPromise;
|
|
1865
|
+
}
|
|
1866
|
+
};
|
|
1867
|
+
return session;
|
|
1868
|
+
}
|
|
1869
|
+
async function renderComponentStill(opts) {
|
|
1870
|
+
const session = await createComponentStillSession(opts);
|
|
1871
|
+
let primaryError;
|
|
1872
|
+
try {
|
|
1873
|
+
const rendered = await session.renderFrame({
|
|
1874
|
+
frame: opts.frame,
|
|
1875
|
+
outputPath: join4(cacheDir(opts.ws), `still-${opts.id}.png`)
|
|
1876
|
+
});
|
|
1877
|
+
return { pngPath: rendered.pngPath, actualGraphics: rendered.actualGraphics };
|
|
1878
|
+
} catch (error) {
|
|
1879
|
+
primaryError = error;
|
|
1880
|
+
throw error;
|
|
1881
|
+
} finally {
|
|
1882
|
+
try {
|
|
1883
|
+
await session.close();
|
|
1884
|
+
} catch (closeError) {
|
|
1885
|
+
if (primaryError === void 0) throw closeError;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
var vp9AlphaFfmpegOverride = ({ type, args }) => {
|
|
1890
|
+
if (type !== "stitcher") return args;
|
|
1891
|
+
return [...args.slice(0, -1), "-cpu-used", "4", "-row-mt", "1", "-threads", "8", args[args.length - 1]];
|
|
1892
|
+
};
|
|
1893
|
+
function alphaRenderConfig(codec) {
|
|
1894
|
+
return codec === "prores" ? { codec: "prores", proResProfile: "4444", pixelFormat: "yuva444p10le", imageFormat: "png", muted: true } : { codec: "vp9", pixelFormat: "yuva420p", imageFormat: "png", muted: true, crf: 24, ffmpegOverride: vp9AlphaFfmpegOverride };
|
|
1895
|
+
}
|
|
1896
|
+
function alphaExt(codec) {
|
|
1897
|
+
return codec === "prores" ? "mov" : "webm";
|
|
1898
|
+
}
|
|
1899
|
+
async function renderComponentAlpha(opts) {
|
|
1900
|
+
const width = opts.width ?? 1920;
|
|
1901
|
+
const height = opts.height ?? 1080;
|
|
1902
|
+
const fps = opts.fps ?? 30;
|
|
1903
|
+
const durationInFrames = opts.durationInFrames ?? fps * 3;
|
|
1904
|
+
const codec = opts.codec ?? "prores";
|
|
1905
|
+
let stageStarted = performance.now();
|
|
1906
|
+
const timed = (stage) => {
|
|
1907
|
+
const now = performance.now();
|
|
1908
|
+
opts.onTiming?.({ stage, durationMs: now - stageStarted });
|
|
1909
|
+
stageStarted = now;
|
|
1910
|
+
};
|
|
1911
|
+
const { serveUrl, browserExecutable, stagedBrowser, inputProps, renderEntryRoot } = await prepareLocalBundle({
|
|
1912
|
+
ws: opts.ws,
|
|
1913
|
+
id: opts.id,
|
|
1914
|
+
width,
|
|
1915
|
+
height,
|
|
1916
|
+
fps,
|
|
1917
|
+
durationInFrames,
|
|
1918
|
+
params: opts.params,
|
|
1919
|
+
assets: opts.assets,
|
|
1920
|
+
seed: opts.seed,
|
|
1921
|
+
graphics: opts.graphics,
|
|
1922
|
+
renderEntryRoot: opts.renderEntryRoot
|
|
1923
|
+
});
|
|
1924
|
+
timed("bundle");
|
|
1925
|
+
const { openBrowser, selectComposition, renderMedia, RenderInternals } = await importFromRenderEntry(
|
|
1926
|
+
renderEntryRoot,
|
|
1927
|
+
"@remotion/renderer"
|
|
1928
|
+
);
|
|
1929
|
+
const requiredGraphics = opts.graphics ?? {
|
|
1930
|
+
api: "canvas2d",
|
|
1931
|
+
required: true,
|
|
1932
|
+
accelerationPreference: "software-allowed"
|
|
1933
|
+
};
|
|
1934
|
+
const collector = graphicsCollector(requiredGraphics);
|
|
1935
|
+
let browser;
|
|
1936
|
+
try {
|
|
1937
|
+
browser = await openBrowser("chrome", componentBrowserOptions(requiredGraphics, browserExecutable, stagedBrowser.chromeMode));
|
|
1938
|
+
timed("browser");
|
|
1939
|
+
} catch (error) {
|
|
1940
|
+
throw componentRuntimeError(error, "component_resource_exhausted", true);
|
|
1941
|
+
}
|
|
1942
|
+
let primaryError;
|
|
1943
|
+
try {
|
|
1944
|
+
await sizeLocalLaneWindows(renderEntryRoot, browser, width, height);
|
|
1945
|
+
const firewall = await installComponentRequestFirewall(browser, opts.allowedAssetOrigins ?? /* @__PURE__ */ new Set());
|
|
1946
|
+
const host = await assertComponentBrowserHardware(browser, requiredGraphics);
|
|
1947
|
+
const composition = await selectComposition({
|
|
1948
|
+
serveUrl,
|
|
1949
|
+
id: LOCAL_COMPOSITION_ID,
|
|
1950
|
+
inputProps,
|
|
1951
|
+
puppeteerInstance: browser,
|
|
1952
|
+
onBrowserLog: collector.onBrowserLog
|
|
1953
|
+
});
|
|
1954
|
+
timed("composition");
|
|
1955
|
+
const rangeSuffix = opts.frameRange ? `-${opts.frameRange[0]}-${opts.frameRange[1]}` : "";
|
|
1956
|
+
const videoPath = join4(cacheDir(opts.ws), `alpha-${opts.id}${rangeSuffix}.${alphaExt(codec)}`);
|
|
1957
|
+
const renderStarted = performance.now();
|
|
1958
|
+
let firstFrame = false;
|
|
1959
|
+
const reported = /* @__PURE__ */ new Set();
|
|
1960
|
+
const result = await renderMedia({
|
|
1961
|
+
composition,
|
|
1962
|
+
serveUrl,
|
|
1963
|
+
inputProps,
|
|
1964
|
+
puppeteerInstance: browser,
|
|
1965
|
+
outputLocation: videoPath,
|
|
1966
|
+
...alphaRenderConfig(codec),
|
|
1967
|
+
onBrowserLog: collector.onBrowserLog,
|
|
1968
|
+
concurrency: opts.concurrency ?? 1,
|
|
1969
|
+
// Match extraction capacity to the bounded frame pool. Remotion otherwise
|
|
1970
|
+
// defaults to two extraction threads even when four pages are waiting.
|
|
1971
|
+
offthreadVideoThreads: Math.max(2, Math.min(4, opts.concurrency ?? 1)),
|
|
1972
|
+
// Container memory discovery can expose the entire host (377 GiB on
|
|
1973
|
+
// Modal), so Remotion's host-derived cache defaults are not safe inside
|
|
1974
|
+
// the 10 GiB sandbox.
|
|
1975
|
+
// @remotion/media centralizes decoded frames in the main browser tab;
|
|
1976
|
+
// unlike OffthreadVideo, its cache does not scale with page concurrency.
|
|
1977
|
+
mediaCacheSizeInBytes: 256 * 1024 * 1024,
|
|
1978
|
+
offthreadVideoCacheSizeInBytes: 256 * 1024 * 1024 * (opts.concurrency ?? 1),
|
|
1979
|
+
onProgress: ({ renderedFrames, encodedFrames, renderedDoneIn, encodedDoneIn }) => {
|
|
1980
|
+
if (!firstFrame && renderedFrames > 0) {
|
|
1981
|
+
firstFrame = true;
|
|
1982
|
+
opts.onTiming?.({ stage: "first-frame", durationMs: performance.now() - renderStarted });
|
|
1983
|
+
}
|
|
1984
|
+
for (const [stage, durationMs] of [["frames", renderedDoneIn], ["encoding", encodedDoneIn]]) {
|
|
1985
|
+
if (durationMs != null && !reported.has(stage)) {
|
|
1986
|
+
reported.add(stage);
|
|
1987
|
+
opts.onTiming?.({ stage, durationMs });
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
opts.onProgress?.({
|
|
1991
|
+
renderedFrames,
|
|
1992
|
+
encodedFrames,
|
|
1993
|
+
totalFrames: opts.frameRange ? opts.frameRange[1] - opts.frameRange[0] + 1 : durationInFrames
|
|
1994
|
+
});
|
|
1995
|
+
},
|
|
1996
|
+
...opts.frameRange ? { frameRange: opts.frameRange } : {}
|
|
1997
|
+
});
|
|
1998
|
+
opts.onTiming?.({
|
|
1999
|
+
stage: "render",
|
|
2000
|
+
durationMs: performance.now() - renderStarted,
|
|
2001
|
+
// In pinned Remotion 4.0.429 these are completion intervals under
|
|
2002
|
+
// concurrency, not individual frame latency. Retain the native summary
|
|
2003
|
+
// alongside them so capture work can be assessed independently.
|
|
2004
|
+
slowestFrames: result.slowestFrames,
|
|
2005
|
+
rendererPerformance: RenderInternals.perf.getPerf()
|
|
2006
|
+
});
|
|
2007
|
+
const denied = firewall.deniedRequests();
|
|
2008
|
+
if (denied.length > 0) {
|
|
2009
|
+
throw new ComponentRuntimeError("component_network_denied", `Component attempted ${denied.length} denied network request(s).`);
|
|
2010
|
+
}
|
|
2011
|
+
return { videoPath, actualGraphics: collector.receipt(), host };
|
|
2012
|
+
} catch (error) {
|
|
2013
|
+
primaryError = componentRuntimeError(error, "component_runtime_error");
|
|
2014
|
+
throw primaryError;
|
|
2015
|
+
} finally {
|
|
2016
|
+
try {
|
|
2017
|
+
await browser.close({ silent: true });
|
|
2018
|
+
} catch (closeError) {
|
|
2019
|
+
if (primaryError === void 0) throw closeError;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
export {
|
|
2025
|
+
SUPPORTED_SCENE_SHOT_VERSIONS,
|
|
2026
|
+
SCENE_SHOT_PARAM,
|
|
2027
|
+
SCENE_ADMISSION_STAGE,
|
|
2028
|
+
sceneShotIssuePath,
|
|
2029
|
+
assertSceneShotCapabilitySupported,
|
|
2030
|
+
assertSceneShotParamsAdmitted,
|
|
2031
|
+
materializeComponentSource,
|
|
2032
|
+
readMaterializedComponentSource,
|
|
2033
|
+
decideComponentRequest,
|
|
2034
|
+
COMPONENT_PAGE_LOCKDOWN,
|
|
2035
|
+
installComponentRequestFirewall,
|
|
2036
|
+
GRAPHICS_ATTESTATION_PREFIX,
|
|
2037
|
+
parseGraphicsAttestationLog,
|
|
2038
|
+
LOCAL_ROOT_FILENAME,
|
|
2039
|
+
LOCAL_ENTRY_FILENAME,
|
|
2040
|
+
LOCAL_COMPOSITION_ID,
|
|
2041
|
+
generateLocalRoot,
|
|
2042
|
+
shouldIsolateWebGpuStillBrowser,
|
|
2043
|
+
COMPONENT_BROWSER_FIREWALL_TIMEOUT_MS,
|
|
2044
|
+
COMPONENT_BROWSER_CLEANUP_TIMEOUT_MS,
|
|
2045
|
+
COMPONENT_BROWSER_FIREWALL_ATTEMPTS,
|
|
2046
|
+
buildLocalWebpackOverride,
|
|
2047
|
+
importFromRenderEntry,
|
|
2048
|
+
LOCAL_BROWSER_VERSION_PROBE_MS,
|
|
2049
|
+
verifyLocalStagedBrowser,
|
|
2050
|
+
installLocalLaneWindowSizer,
|
|
2051
|
+
sizeLocalLaneWindows,
|
|
2052
|
+
stagedBrowserLocator,
|
|
2053
|
+
resolveRenderEntryRoot,
|
|
2054
|
+
COMPONENT_STILL_RUNTIME_IDENTITY,
|
|
2055
|
+
componentStillRasterAffectingFlags,
|
|
2056
|
+
componentStillDependencyIdentity,
|
|
2057
|
+
createComponentStillSession,
|
|
2058
|
+
renderComponentStill,
|
|
2059
|
+
vp9AlphaFfmpegOverride,
|
|
2060
|
+
alphaRenderConfig,
|
|
2061
|
+
alphaExt,
|
|
2062
|
+
renderComponentAlpha
|
|
2063
|
+
};
|