@volter/editor-blender 0.1.50 → 0.1.51

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.
@@ -549,13 +549,29 @@ function blenderRoutesPlugin(services) {
549
549
  json(res, { error: "That path leaves the project root." }, 403);
550
550
  return;
551
551
  }
552
+ let size;
552
553
  try {
553
- const bytes = await readFile2(absolute);
554
- res.setHeader("content-type", "application/octet-stream");
555
- res.end(bytes);
554
+ const info = await stat2(absolute);
555
+ if (!info.isFile()) throw new Error("not a file");
556
+ size = info.size;
556
557
  } catch {
557
558
  json(res, { error: "Not found." }, 404);
559
+ return;
558
560
  }
561
+ res.setHeader("content-type", "application/octet-stream");
562
+ res.setHeader("content-length", String(size));
563
+ await new Promise((resolve) => {
564
+ const stream = createReadStream2(absolute);
565
+ stream.once("error", () => {
566
+ res.destroy();
567
+ resolve();
568
+ });
569
+ res.once("close", () => {
570
+ stream.destroy();
571
+ resolve();
572
+ });
573
+ stream.pipe(res);
574
+ });
559
575
  }
560
576
  },
561
577
  // ---- A SHIPPED OUTPUT: bytes Blender produced for the game to ship (`public/`), recorded
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@volter/editor-blender",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "AGPL-3.0-only AND GPL-3.0-or-later",
5
- "version": "0.1.50",
5
+ "version": "0.1.51",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
@@ -70,14 +70,14 @@
70
70
  },
71
71
  "dependencies": {
72
72
  "@modelcontextprotocol/sdk": "1.30.0",
73
- "@volter/blender-engine": "0.1.48",
74
- "@volter/editor-threejs": "0.5.110",
73
+ "@volter/blender-engine": "0.1.49",
74
+ "@volter/editor-threejs": "0.5.111",
75
75
  "zod": "^4.3.6"
76
76
  },
77
77
  "peerDependencies": {
78
- "@volter/editor-live": "0.5.110",
79
- "@volter/editor-project": "0.5.110",
80
- "@volter/editor-sdk": "0.5.110",
78
+ "@volter/editor-live": "0.5.111",
79
+ "@volter/editor-project": "0.5.111",
80
+ "@volter/editor-sdk": "0.5.111",
81
81
  "react": "^19.0.0",
82
82
  "react-dom": "^19.0.0",
83
83
  "three": "^0.180.0",
@@ -318,13 +318,33 @@ export function blenderRoutesPlugin(services: ProjectServingServices): Plugin {
318
318
  json(res, { error: 'That path leaves the project root.' }, 403);
319
319
  return;
320
320
  }
321
+ // STREAMED, never read whole: on a tab-served project this server runs in the page, and a
322
+ // 307 MB document read into one buffer and ended as one body was copied along the page's
323
+ // own response path. MEASURED 2026-09-29 on the hosted Stoneguard open: the page's
324
+ // isolate held 1,009 MB of ArrayBuffers while the document crossed, against 19 MB before.
325
+ let size: number;
321
326
  try {
322
- const bytes = await readFile(absolute);
323
- res.setHeader('content-type', 'application/octet-stream');
324
- res.end(bytes);
327
+ const info = await stat(absolute);
328
+ if (!info.isFile()) throw new Error('not a file');
329
+ size = info.size;
325
330
  } catch {
326
331
  json(res, { error: 'Not found.' }, 404);
332
+ return;
327
333
  }
334
+ res.setHeader('content-type', 'application/octet-stream');
335
+ res.setHeader('content-length', String(size));
336
+ await new Promise<void>((resolve) => {
337
+ const stream = createReadStream(absolute);
338
+ stream.once('error', () => {
339
+ res.destroy();
340
+ resolve();
341
+ });
342
+ res.once('close', () => {
343
+ stream.destroy();
344
+ resolve();
345
+ });
346
+ stream.pipe(res);
347
+ });
328
348
  },
329
349
  },
330
350
  // ---- A SHIPPED OUTPUT: bytes Blender produced for the game to ship (`public/`), recorded