@deepblend/dsh-blender-host 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  /** Fixed asset preview scenes, compiled in batch mode outside project revisions. */
2
+ import { assertRuntimeSceneFeatures } from './runtime-scene-features.js'
2
3
  import { copyFileSync, existsSync, writeFileSync } from 'node:fs'
3
4
  import { join } from 'node:path'
4
5
  import { randomUUID } from 'node:crypto'
@@ -100,6 +101,8 @@ export async function renderAssetPreview({ runtime, asset, directory, maxMeshPol
100
101
  'The isolated preview scene is invalid.', { detail: { errors: checked.errors } })
101
102
  const compiled = compileSceneSpec(scene).spec
102
103
  writeFileSync(sceneSpecPath, JSON.stringify(compiled))
104
+ const featureCheck = assertRuntimeSceneFeatures(runtime, compiled, { signal })
105
+ if (featureCheck) await featureCheck
103
106
  const result = await runtime.compileScene({ sceneSpecPath, profile: 'preview', projectRoot: directory,
104
107
  jobId: `asset-compile-${randomUUID()}`, session: false, signal,
105
108
  onWorkingDirectory: info => { copyFileSync(join(info.directory, 'result.blend'), checkpointPath) } })
@@ -1,4 +1,5 @@
1
1
  /** Isolated inspection renders. Successful tasks are published with one directory rename. */
2
+ import { assertRuntimeSceneFeatures } from './runtime-scene-features.js'
2
3
  import { createReadStream, copyFileSync, existsSync, mkdirSync, readdirSync, renameSync, writeFileSync } from 'node:fs'
3
4
  import { dirname, join } from 'node:path'
4
5
  import { randomUUID } from 'node:crypto'
@@ -186,6 +187,8 @@ export async function renderDiagnostic(host, request) {
186
187
  }
187
188
  const sceneSpecPath = join(compileRoot, 'scene-spec.json'), checkpointPath = join(compileRoot, 'derived.blend')
188
189
  writeJsonAtomic(sceneSpecPath, scene)
190
+ const featureCheck = assertRuntimeSceneFeatures(runtime, scene, { signal })
191
+ if (featureCheck) await featureCheck
189
192
  const compiled = await runtime.compileScene({ sceneSpecPath, projectRoot: compileRoot, profile: 'preview',
190
193
  jobId: `${jobId}-compile`, session: false, signal,
191
194
  onWorkingDirectory: info => copyFileSync(join(info.directory, 'result.blend'), checkpointPath) })
package/lib/index.js CHANGED
@@ -25,6 +25,7 @@
25
25
  * Plane: Host composition
26
26
  */
27
27
 
28
+ import { assertRuntimeSceneFeatures } from './runtime-scene-features.js'
28
29
  import { Service } from '@deepseek-ai/cordis'
29
30
  import z from '@deepseek-ai/schemastery'
30
31
 
@@ -1187,6 +1188,8 @@ export default class BlenderStudio extends Service {
1187
1188
  */
1188
1189
  async compileRevisionForRender(input) {
1189
1190
  const { projectId, revision, spec } = input
1191
+ const featureCheck = assertRuntimeSceneFeatures(this.runtime, spec, { signal: input.signal })
1192
+ if (featureCheck) await featureCheck
1190
1193
  const scratch = resolveInside(
1191
1194
  this.store.workspaceRoot,
1192
1195
  join(this.store.workspaceRoot, 'tmp', `render-${revision}-${Date.now().toString(36)}`),
@@ -41,6 +41,7 @@
41
41
  * Plane: Host composition
42
42
  */
43
43
 
44
+ import { assertRuntimeSceneFeatures } from './runtime-scene-features.js'
44
45
  import { existsSync, mkdirSync, readdirSync, renameSync, statSync } from 'node:fs'
45
46
  import { randomUUID } from 'node:crypto'
46
47
  import { join } from 'node:path'
@@ -570,6 +571,8 @@ export class RevisionTransaction {
570
571
  const checkpointStaging = join(staging, 'scene.blend')
571
572
 
572
573
  try {
574
+ const featureCheck = assertRuntimeSceneFeatures(this.runtime, plan.spec, { signal: plan.signal })
575
+ if (featureCheck) await featureCheck
573
576
  const run = await this.runtime.compileScene({
574
577
  sceneSpecPath: specPath,
575
578
  profile: plan.spec.renderProfiles?.preview === undefined ? undefined : 'preview',
@@ -0,0 +1,21 @@
1
+ import { BlenderError, BlenderErrorCode, requiredSceneFeatures } from '@deepblend/dsh-blender-contracts'
2
+
3
+ /** Old runtimes must not silently compile an axis-sized box as their scalar default. */
4
+ export function assertRuntimeSceneFeatures(runtime, spec, { signal } = {}) {
5
+ const required = requiredSceneFeatures(spec)
6
+ if (required.length === 0) return
7
+ return (async () => {
8
+ signal?.throwIfAborted()
9
+ const capabilities = typeof runtime?.getCapabilities === 'function'
10
+ ? await runtime.getCapabilities({ signal }) : null
11
+ signal?.throwIfAborted()
12
+ if (capabilities?.installed === false) {
13
+ throw new BlenderError(BlenderErrorCode.NOT_FOUND, 'Blender is unavailable for this scene.')
14
+ }
15
+ const supported = Array.isArray(capabilities?.sceneFeatures) ? capabilities.sceneFeatures : []
16
+ const missing = required.filter(feature => !supported.includes(feature))
17
+ if (missing.length) throw new BlenderError(BlenderErrorCode.SCENE_FEATURE_UNSUPPORTED,
18
+ `The runtime does not support ${missing.join(', ')}. Update the runtime before compiling this scene.`,
19
+ { detail: { required, supported, missing } })
20
+ })()
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepblend/dsh-blender-host",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "DeepBlend Host service: the blenderStudio business facade over the BlenderRuntime seam.",
6
6
  "main": "lib/index.js",
@@ -20,7 +20,7 @@
20
20
  "./package.json": "./package.json"
21
21
  },
22
22
  "dependencies": {
23
- "@deepblend/dsh-blender-contracts": "0.4.0",
23
+ "@deepblend/dsh-blender-contracts": "0.5.0",
24
24
  "sharp": "0.35.5"
25
25
  },
26
26
  "license": "MIT",