@volter/editor-core 0.5.57

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.
Files changed (719) hide show
  1. package/BUNDLED_NOTICES +1527 -0
  2. package/LICENSE +686 -0
  3. package/LICENSE-APACHE +202 -0
  4. package/NOTICE +18 -0
  5. package/README.md +24 -0
  6. package/dist/build/chunk-JS4D4XZ3.js +175 -0
  7. package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
  8. package/dist/build/vite-plugin-product-contributions.js +100 -0
  9. package/dist/build/vite-plugin-product-contributions.js.map +7 -0
  10. package/dist/build/vite-plugin-shared-react.js +25 -0
  11. package/dist/build/vite-plugin-shared-react.js.map +7 -0
  12. package/dist/build/vite-plugin-shared-three.js +86 -0
  13. package/dist/build/vite-plugin-shared-three.js.map +7 -0
  14. package/dist/server/chunk-KMJC7AGO.js +98 -0
  15. package/dist/server/chunk-KMJC7AGO.js.map +7 -0
  16. package/dist/server/chunk-PSILUMGS.js +46 -0
  17. package/dist/server/chunk-PSILUMGS.js.map +7 -0
  18. package/dist/server/frame-proxy.js +255 -0
  19. package/dist/server/frame-proxy.js.map +7 -0
  20. package/dist/server/open-browser.js +53 -0
  21. package/dist/server/open-browser.js.map +7 -0
  22. package/dist/server/process-shutdown.js +127 -0
  23. package/dist/server/process-shutdown.js.map +7 -0
  24. package/dist/server/session-registry.js +141 -0
  25. package/dist/server/session-registry.js.map +7 -0
  26. package/dist/server/spawn-opener.js +11 -0
  27. package/dist/server/spawn-opener.js.map +7 -0
  28. package/dist/server/worktree-identity.js +11 -0
  29. package/dist/server/worktree-identity.js.map +7 -0
  30. package/dist-server/packaged.mjs +88816 -0
  31. package/package.json +118 -0
  32. package/scripts/build-plugins.mjs +14 -0
  33. package/scripts/build-server.mjs +26 -0
  34. package/scripts/build-session.mjs +9 -0
  35. package/server/account-credentials.ts +60 -0
  36. package/server/account-service.ts +1434 -0
  37. package/server/adapter-region-includes.ts +47 -0
  38. package/server/asset-catalog-v2.ts +175 -0
  39. package/server/asset-history-snapshots.ts +196 -0
  40. package/server/asset-ledger-store.ts +195 -0
  41. package/server/asset-library-routes.ts +1595 -0
  42. package/server/blender-wasm-artifact.ts +430 -0
  43. package/server/boot-timings.ts +106 -0
  44. package/server/canonical-path.ts +50 -0
  45. package/server/checkout-workspace-preflight.ts +349 -0
  46. package/server/cloud-asset-catalog.ts +158 -0
  47. package/server/coding-inference-launch.ts +237 -0
  48. package/server/collaboration-account-client.ts +202 -0
  49. package/server/collaboration-attribution.ts +122 -0
  50. package/server/collaboration-session.ts +934 -0
  51. package/server/comfyui-bridge.ts +248 -0
  52. package/server/console-ledger.ts +499 -0
  53. package/server/creation-site-transform.ts +304 -0
  54. package/server/creation-site-write.ts +648 -0
  55. package/server/data-file-serialize.ts +158 -0
  56. package/server/editor-brand-html.ts +72 -0
  57. package/server/editor-control-socket.ts +366 -0
  58. package/server/editor-server-options.ts +190 -0
  59. package/server/editor-server.ts +1483 -0
  60. package/server/editor-sse.ts +352 -0
  61. package/server/engine-provenance.ts +169 -0
  62. package/server/engine-source-restart.ts +117 -0
  63. package/server/frame-bridge.ts +115 -0
  64. package/server/frame-proxy.ts +490 -0
  65. package/server/frame-workbench.ts +259 -0
  66. package/server/frontend-handoff.ts +229 -0
  67. package/server/game-globals-shadow.ts +113 -0
  68. package/server/gameplay-session-retention.ts +149 -0
  69. package/server/gameplay-sessions.ts +123 -0
  70. package/server/generation-jobs.ts +201 -0
  71. package/server/generation-reconciler.ts +137 -0
  72. package/server/generative-execution-context.ts +59 -0
  73. package/server/git-workflow.ts +612 -0
  74. package/server/harness-chat-caller.ts +36 -0
  75. package/server/harness-chat-service.ts +1681 -0
  76. package/server/idle-shutdown.ts +170 -0
  77. package/server/js-profiling-policy.ts +52 -0
  78. package/server/launcher-settings.ts +84 -0
  79. package/server/local-asset-catalog.ts +410 -0
  80. package/server/managed-account-defaults.ts +22 -0
  81. package/server/mock-control-plane.ts +540 -0
  82. package/server/model-import-conversion.ts +345 -0
  83. package/server/native-credential-store.ts +162 -0
  84. package/server/open-browser.ts +118 -0
  85. package/server/packaged.ts +1192 -0
  86. package/server/play-stall.ts +126 -0
  87. package/server/process-shutdown.ts +218 -0
  88. package/server/product-presets.ts +55 -0
  89. package/server/project-build-artifact.ts +95 -0
  90. package/server/project-components.ts +100 -0
  91. package/server/project-dependency-invalidation.ts +114 -0
  92. package/server/project-file-scan.ts +235 -0
  93. package/server/project-hmr-files.ts +215 -0
  94. package/server/project-install-roots.ts +210 -0
  95. package/server/project-kinds.ts +71 -0
  96. package/server/project-mcp-servers.ts +82 -0
  97. package/server/project-module-freshness.ts +109 -0
  98. package/server/project-module-instance.ts +375 -0
  99. package/server/project-optimize-deps-entries.ts +827 -0
  100. package/server/project-output-writer.ts +679 -0
  101. package/server/project-package-origin.ts +63 -0
  102. package/server/project-root-surface.ts +831 -0
  103. package/server/project-scratch-path.ts +47 -0
  104. package/server/project-script-hmr.ts +619 -0
  105. package/server/project-serving-plugins.ts +236 -0
  106. package/server/project-tools.ts +653 -0
  107. package/server/project-validation.ts +250 -0
  108. package/server/project-verbs.ts +127 -0
  109. package/server/project-view.ts +52 -0
  110. package/server/project-watch.ts +1134 -0
  111. package/server/project-work-coordinator.ts +288 -0
  112. package/server/provider-credentials.ts +331 -0
  113. package/server/recent-projects-store.ts +98 -0
  114. package/server/redact-secrets.ts +71 -0
  115. package/server/repository-presence.ts +267 -0
  116. package/server/routes/account.ts +357 -0
  117. package/server/routes/agents.ts +60 -0
  118. package/server/routes/assets.ts +154 -0
  119. package/server/routes/build.ts +13 -0
  120. package/server/routes/collaboration.ts +595 -0
  121. package/server/routes/configurations.ts +419 -0
  122. package/server/routes/context.ts +264 -0
  123. package/server/routes/control-plane.ts +1811 -0
  124. package/server/routes/logs.ts +272 -0
  125. package/server/routes/project-identity.ts +302 -0
  126. package/server/routes/project-open.ts +429 -0
  127. package/server/routes/project-source.ts +686 -0
  128. package/server/routes/project-state.ts +383 -0
  129. package/server/routes/relay.ts +1029 -0
  130. package/server/routes/route-helpers.ts +29 -0
  131. package/server/routes/served-modules.ts +138 -0
  132. package/server/routes/session-tabs.ts +196 -0
  133. package/server/routes/settings.ts +121 -0
  134. package/server/routes/share-control.ts +486 -0
  135. package/server/routes/themes.ts +116 -0
  136. package/server/routes/tools.ts +230 -0
  137. package/server/routes/worktrees.ts +595 -0
  138. package/server/scoped-game-css.ts +461 -0
  139. package/server/server-utils.ts +1194 -0
  140. package/server/session-product.ts +112 -0
  141. package/server/session-registry.ts +230 -0
  142. package/server/share-claims.ts +138 -0
  143. package/server/share-host.ts +526 -0
  144. package/server/share-session-gateway.ts +1362 -0
  145. package/server/share-tunnel.ts +123 -0
  146. package/server/spawn-opener.ts +74 -0
  147. package/server/support/play/log-format.ts +76 -0
  148. package/server/support/play/session-record.ts +199 -0
  149. package/server/support/project/build-discipline.ts +718 -0
  150. package/server/support/project/inspection-node.ts +119 -0
  151. package/server/support/project/log-naming.ts +54 -0
  152. package/server/support/project/provenance.ts +290 -0
  153. package/server/support/project/run-name.ts +18 -0
  154. package/server/support/project/session-journal.ts +830 -0
  155. package/server/support/project/shared.ts +327 -0
  156. package/server/tab-bootstrap.ts +62 -0
  157. package/server/tab-heartbeat.ts +477 -0
  158. package/server/tab-lifecycle.ts +550 -0
  159. package/server/tab-presence.ts +1508 -0
  160. package/server/team-agent-mirror.ts +95 -0
  161. package/server/twin-auth.ts +138 -0
  162. package/server/vendored-lock-recorder.ts +968 -0
  163. package/server/worktree-identity.ts +121 -0
  164. package/server/worktree-management.ts +398 -0
  165. package/server/worktree-retention.d.mts +13 -0
  166. package/server/worktree-retention.mjs +205 -0
  167. package/src/EditorContext.tsx +169 -0
  168. package/src/account.ts +333 -0
  169. package/src/action-registry.ts +532 -0
  170. package/src/active-product.ts +74 -0
  171. package/src/active-project.ts +155 -0
  172. package/src/adapter-editor-config.ts +25 -0
  173. package/src/adapter-observation.ts +49 -0
  174. package/src/animation/stage-transport.ts +379 -0
  175. package/src/animation/three-clips-subject.ts +172 -0
  176. package/src/api/asset-library-wire.ts +45 -0
  177. package/src/api/assets.ts +365 -0
  178. package/src/api/base.ts +10 -0
  179. package/src/api/build.ts +99 -0
  180. package/src/api/git-wire.ts +56 -0
  181. package/src/api/logs.ts +92 -0
  182. package/src/api/project-identity.ts +74 -0
  183. package/src/api/project-open.ts +355 -0
  184. package/src/api/project-source.ts +162 -0
  185. package/src/api/project-state.ts +107 -0
  186. package/src/api/relay.ts +268 -0
  187. package/src/api/settings.ts +36 -0
  188. package/src/api/themes.ts +45 -0
  189. package/src/api/worktrees.ts +257 -0
  190. package/src/asset-compare-core.ts +171 -0
  191. package/src/asset-compare.ts +294 -0
  192. package/src/asset-editor-context.tsx +100 -0
  193. package/src/asset-events.ts +97 -0
  194. package/src/asset-inspector-actions.ts +87 -0
  195. package/src/asset-preview-framing.ts +357 -0
  196. package/src/asset-preview.ts +2802 -0
  197. package/src/asset-selection-viewer-registry.ts +113 -0
  198. package/src/asset-selection.ts +133 -0
  199. package/src/asset-workflow/asset-capabilities.ts +344 -0
  200. package/src/asset-workflow/asset-import-jobs.ts +106 -0
  201. package/src/asset-workflow/asset-ledger-backend.ts +126 -0
  202. package/src/asset-workflow/asset-ledger.ts +156 -0
  203. package/src/asset-workflow/asset-materialization-report.ts +140 -0
  204. package/src/asset-workflow/asset-pack-manifest.ts +320 -0
  205. package/src/asset-workflow/asset-types.ts +142 -0
  206. package/src/asset-workflow/asset-workflow-quality.ts +61 -0
  207. package/src/asset-workflow/audio-preview-player.ts +193 -0
  208. package/src/asset-workflow/audio-waveform.ts +22 -0
  209. package/src/asset-workflow/cloud-asset-client.ts +263 -0
  210. package/src/asset-workflow/folder-preview.ts +345 -0
  211. package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
  212. package/src/asset-workflow/image-view-scale.ts +32 -0
  213. package/src/asset-workflow/import-contract.ts +124 -0
  214. package/src/asset-workflow/ledger-write-lock.ts +244 -0
  215. package/src/asset-workflow/model-inspection.ts +818 -0
  216. package/src/asset-workflow/pixi-spritesheet.ts +197 -0
  217. package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
  218. package/src/asset-workflow/project-asset-commands.ts +23 -0
  219. package/src/asset-workflow/project-asset-health.ts +139 -0
  220. package/src/asset-workflow/project-asset-operations.ts +154 -0
  221. package/src/asset-workflow/project-asset-roots.ts +68 -0
  222. package/src/asset-workflow/project-content.ts +288 -0
  223. package/src/asset-workflow/project-source-index.ts +550 -0
  224. package/src/asset-workflow/thumbnail-system.ts +256 -0
  225. package/src/authoring/active-adapter.ts +199 -0
  226. package/src/authoring/active-systems.ts +422 -0
  227. package/src/authoring/adapter-key.ts +18 -0
  228. package/src/authoring/authoring-asset-url.ts +27 -0
  229. package/src/authoring/bootstrap-state.ts +49 -0
  230. package/src/authoring/boundary-authoring-adapter.ts +184 -0
  231. package/src/authoring/breakpoint-state.ts +43 -0
  232. package/src/authoring/canvas-scene-guides.ts +76 -0
  233. package/src/authoring/component-instance-root.ts +171 -0
  234. package/src/authoring/composite-authoring-adapter.ts +2110 -0
  235. package/src/authoring/consumer-actions.ts +520 -0
  236. package/src/authoring/css-numeric-style.ts +97 -0
  237. package/src/authoring/design-time-layers.ts +849 -0
  238. package/src/authoring/design-time-mount-registry.ts +235 -0
  239. package/src/authoring/design-time-settle.ts +343 -0
  240. package/src/authoring/edit-mode-authoring.ts +619 -0
  241. package/src/authoring/eyedropper-session.ts +60 -0
  242. package/src/authoring/instance-source-menu-register.ts +13 -0
  243. package/src/authoring/instance-source-menu.ts +135 -0
  244. package/src/authoring/layered-pick.ts +183 -0
  245. package/src/authoring/live-gesture-lock.ts +50 -0
  246. package/src/authoring/live-object-transform.ts +62 -0
  247. package/src/authoring/mount-failure-report.ts +154 -0
  248. package/src/authoring/mounted-root-subjects.ts +144 -0
  249. package/src/authoring/no-authoring-adapter.ts +55 -0
  250. package/src/authoring/null-inspection-subjects.tsx +75 -0
  251. package/src/authoring/object3d-document-persistence.ts +106 -0
  252. package/src/authoring/object3d-document-session-registry.ts +120 -0
  253. package/src/authoring/object3d-document-session.ts +1326 -0
  254. package/src/authoring/object3d-gesture-controller.ts +113 -0
  255. package/src/authoring/panel-authoring.ts +121 -0
  256. package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
  257. package/src/authoring/prefab-instance-section-model.ts +55 -0
  258. package/src/authoring/project-authoring-session.ts +105 -0
  259. package/src/authoring/provenance.ts +99 -0
  260. package/src/authoring/quarks-particle-systems.ts +19 -0
  261. package/src/authoring/react-canvas-navigation.ts +255 -0
  262. package/src/authoring/react-design-canvas-style.ts +20 -0
  263. package/src/authoring/react-story-board.ts +937 -0
  264. package/src/authoring/selection-scope.ts +195 -0
  265. package/src/authoring/shell-document-ops.ts +169 -0
  266. package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
  267. package/src/authoring/shell-viewport-policy.ts +48 -0
  268. package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
  269. package/src/authoring/stories-scope.ts +35 -0
  270. package/src/authoring/story-board-chrome-fit.ts +107 -0
  271. package/src/authoring/story-board-presentation.ts +111 -0
  272. package/src/authoring/three-projection-core.ts +226 -0
  273. package/src/authoring/viewport-pick-context.ts +40 -0
  274. package/src/authoring/viewport-raycast.ts +240 -0
  275. package/src/authoring/viewport-tool-context.ts +73 -0
  276. package/src/authoring/world-canvas-viewport-state.ts +35 -0
  277. package/src/authoring/world-hidden-viewport.ts +152 -0
  278. package/src/authoring/world-pan-state.ts +198 -0
  279. package/src/authoring/world-session-state.ts +101 -0
  280. package/src/authoring/write-pipe.ts +173 -0
  281. package/src/availability-tick.ts +66 -0
  282. package/src/bitmap-label.ts +120 -0
  283. package/src/blender-tab-metrics.ts +161 -0
  284. package/src/board-open-actions.ts +20 -0
  285. package/src/boot-routing.ts +391 -0
  286. package/src/build-identity.ts +15 -0
  287. package/src/bytes-codec.ts +62 -0
  288. package/src/camera-authoring.ts +175 -0
  289. package/src/cancellation-reason.ts +58 -0
  290. package/src/canvas-preview-frames.ts +482 -0
  291. package/src/capture-camera-pose.ts +77 -0
  292. package/src/chrome-registry.ts +159 -0
  293. package/src/chrome-slot-registry.ts +91 -0
  294. package/src/collaboration-client.ts +264 -0
  295. package/src/collaboration-presence.ts +41 -0
  296. package/src/command-dispatch.ts +19 -0
  297. package/src/command-listener.ts +2743 -0
  298. package/src/command-registry.ts +70 -0
  299. package/src/component-board-registry.ts +205 -0
  300. package/src/component-states-registry.ts +200 -0
  301. package/src/components/AgentPresentationNotice.tsx +75 -0
  302. package/src/components/AlignToolbar.tsx +204 -0
  303. package/src/components/AppRoot.tsx +394 -0
  304. package/src/components/ApplicationMenus.tsx +415 -0
  305. package/src/components/AssetBrowser.tsx +2843 -0
  306. package/src/components/AssetEditorShell.tsx +216 -0
  307. package/src/components/AssetInspectorToolSection.tsx +124 -0
  308. package/src/components/BoardRulers.tsx +354 -0
  309. package/src/components/CameraInfo.tsx +81 -0
  310. package/src/components/CanvasSceneViewport.tsx +556 -0
  311. package/src/components/CapabilityCoverageSection.tsx +185 -0
  312. package/src/components/CenterDocuments.tsx +22 -0
  313. package/src/components/ChromeSlot.tsx +20 -0
  314. package/src/components/CodeView.tsx +470 -0
  315. package/src/components/CollaborationHeaderControl.css +74 -0
  316. package/src/components/CompactInspectorCard.tsx +190 -0
  317. package/src/components/CompactInspectorShell.tsx +39 -0
  318. package/src/components/ConsolePanel.css +55 -0
  319. package/src/components/ConsolePanel.tsx +270 -0
  320. package/src/components/DefaultEditorLayout.tsx +106 -0
  321. package/src/components/DocumentHeaderStrip.tsx +96 -0
  322. package/src/components/DocumentShelfRail.tsx +45 -0
  323. package/src/components/DocumentThumbnail.tsx +345 -0
  324. package/src/components/EditorLeaseGuard.tsx +372 -0
  325. package/src/components/ErrorBanner.tsx +123 -0
  326. package/src/components/FolderPreviewTile.tsx +159 -0
  327. package/src/components/GameHierarchy.tsx +3842 -0
  328. package/src/components/GameplaySessionTimeline.tsx +295 -0
  329. package/src/components/InspectionProjection.tsx +968 -0
  330. package/src/components/Inspector.tsx +233 -0
  331. package/src/components/InspectorCanvasPreview.tsx +35 -0
  332. package/src/components/InspectorFieldsSection.tsx +290 -0
  333. package/src/components/InspectorObjectPreview.tsx +57 -0
  334. package/src/components/InspectorStoriesSection.tsx +92 -0
  335. package/src/components/InspectorToolSection.tsx +96 -0
  336. package/src/components/InspectorTransformSection.tsx +245 -0
  337. package/src/components/LightExplorerPanel.tsx +433 -0
  338. package/src/components/MediaProperties.tsx +145 -0
  339. package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
  340. package/src/components/Object3DDocumentToolbar.css +100 -0
  341. package/src/components/Object3DDocumentToolbar.tsx +454 -0
  342. package/src/components/Object3DDocumentViewport.tsx +58 -0
  343. package/src/components/ProjectHeader.css +617 -0
  344. package/src/components/ProjectHeader.tsx +328 -0
  345. package/src/components/ProjectLayout.tsx +249 -0
  346. package/src/components/ReactCanvasControls.tsx +284 -0
  347. package/src/components/RootSelectionOverlay.tsx +3162 -0
  348. package/src/components/RootTextEditor.tsx +79 -0
  349. package/src/components/SaveStatus.tsx +70 -0
  350. package/src/components/StageHost.tsx +1954 -0
  351. package/src/components/StageOverlays.tsx +21 -0
  352. package/src/components/StartupErrorScreen.tsx +76 -0
  353. package/src/components/StartupLoadingScreen.tsx +65 -0
  354. package/src/components/StatsOverlay.tsx +78 -0
  355. package/src/components/SurfaceStateOverlay.tsx +24 -0
  356. package/src/components/ToolContributionSurfaces.tsx +65 -0
  357. package/src/components/ToolHost.tsx +370 -0
  358. package/src/components/ToolObject3DPreview.tsx +39 -0
  359. package/src/components/Toolbar.tsx +595 -0
  360. package/src/components/TransientHint.tsx +44 -0
  361. package/src/components/TransportStrip.tsx +174 -0
  362. package/src/components/VersionControlSection.tsx +470 -0
  363. package/src/components/VgaiLogo.css +83 -0
  364. package/src/components/VgaiLogo.tsx +35 -0
  365. package/src/components/ViewportControlsHint.tsx +60 -0
  366. package/src/components/ViewportFurniture.tsx +401 -0
  367. package/src/components/ViewportOverlay.tsx +145 -0
  368. package/src/components/ViewportOverlaysMenu.tsx +153 -0
  369. package/src/components/ViewportShadingMenu.tsx +300 -0
  370. package/src/components/ViewportViewMenu.tsx +101 -0
  371. package/src/components/WorkspaceDocumentSurface.tsx +204 -0
  372. package/src/components/WorkspaceUtilitySurface.tsx +22 -0
  373. package/src/components/WorktreeSwitcher.css +239 -0
  374. package/src/components/WorktreeSwitcher.tsx +890 -0
  375. package/src/components/account-documents.tsx +1162 -0
  376. package/src/components/asset-documents.tsx +812 -0
  377. package/src/components/asset-editor-persistence.ts +216 -0
  378. package/src/components/asset-selection-section.tsx +535 -0
  379. package/src/components/asset-thumbnails.tsx +312 -0
  380. package/src/components/asset-viewers/AudioViewer.tsx +201 -0
  381. package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
  382. package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
  383. package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
  384. package/src/components/asset-viewers/ImageViewer.tsx +300 -0
  385. package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
  386. package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
  387. package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
  388. package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
  389. package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
  390. package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
  391. package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
  392. package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
  393. package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
  394. package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
  395. package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
  396. package/src/components/asset-viewers/VideoViewer.tsx +101 -0
  397. package/src/components/asset-viewers/shader-source.ts +144 -0
  398. package/src/components/asset-workflow.css +731 -0
  399. package/src/components/board-guides.ts +150 -0
  400. package/src/components/compact-inspector.css +539 -0
  401. package/src/components/core-utilities.tsx +90 -0
  402. package/src/components/editor-notifications.css +40 -0
  403. package/src/components/engine-workspace.css +274 -0
  404. package/src/components/inspector-preview-section.tsx +238 -0
  405. package/src/components/inspector-property-grouping.ts +64 -0
  406. package/src/components/inspector-revert-label.ts +20 -0
  407. package/src/components/inspector-selection.ts +42 -0
  408. package/src/components/inspector-stories-gating.ts +171 -0
  409. package/src/components/inspector-transform-subject.ts +11 -0
  410. package/src/components/inspector-transform.ts +75 -0
  411. package/src/components/kind-documents.tsx +500 -0
  412. package/src/components/palette-action-publisher.tsx +140 -0
  413. package/src/components/primitives/DraftColorInput.tsx +74 -0
  414. package/src/components/product-shell.css +1829 -0
  415. package/src/components/project-tool-documents.tsx +410 -0
  416. package/src/components/scene-documents.tsx +223 -0
  417. package/src/components/stage-keyboard.tsx +37 -0
  418. package/src/components/stage-overlay-set.tsx +107 -0
  419. package/src/components/stage-presence-markers.ts +478 -0
  420. package/src/components/standard-viewport-dressing.ts +417 -0
  421. package/src/components/status-contributions.tsx +403 -0
  422. package/src/components/tool-documents.tsx +309 -0
  423. package/src/components/tool-schema-form.tsx +262 -0
  424. package/src/components/use-after-paint.ts +41 -0
  425. package/src/components/use-project-image-assets.ts +86 -0
  426. package/src/components/viewport-header-controls.css +90 -0
  427. package/src/components/viewport-surface-status.tsx +55 -0
  428. package/src/components/workspace-history.ts +32 -0
  429. package/src/components/workspace-static-panel-registry.tsx +152 -0
  430. package/src/components/workspace-surfaces.css +621 -0
  431. package/src/components/world-documents.tsx +586 -0
  432. package/src/components/world-overlay-gestures.ts +1694 -0
  433. package/src/composite-screenshot.ts +1641 -0
  434. package/src/console-sync.ts +131 -0
  435. package/src/constraint-helper.ts +338 -0
  436. package/src/content-entry-source-registry.ts +184 -0
  437. package/src/coverage/authoring-seam-evidence.ts +300 -0
  438. package/src/coverage/canvas-reveal.ts +192 -0
  439. package/src/coverage/design-time-surfaces.ts +101 -0
  440. package/src/coverage/live-seam-evidence.ts +11 -0
  441. package/src/coverage/ontology-invariants.ts +466 -0
  442. package/src/coverage/session-vitals.ts +501 -0
  443. package/src/coverage/system-seam-evidence.ts +72 -0
  444. package/src/crash-null-boundary.ts +36 -0
  445. package/src/creation-site-edit.ts +1479 -0
  446. package/src/creation-site-registry.ts +160 -0
  447. package/src/delegate-harness-registry.ts +143 -0
  448. package/src/document-context-registry.ts +177 -0
  449. package/src/document-open-registry.ts +200 -0
  450. package/src/document-preview-source.ts +20 -0
  451. package/src/document-renderer-session.ts +138 -0
  452. package/src/editor-api.ts +46 -0
  453. package/src/editor-chrome-capture.ts +139 -0
  454. package/src/editor-commands.ts +164 -0
  455. package/src/editor-console.ts +563 -0
  456. package/src/editor-current-view.ts +82 -0
  457. package/src/editor-document-probe.ts +881 -0
  458. package/src/editor-git-client.ts +115 -0
  459. package/src/editor-host-door.ts +462 -0
  460. package/src/editor-hotkeys.ts +842 -0
  461. package/src/editor-lease-view.ts +39 -0
  462. package/src/editor-lease.ts +415 -0
  463. package/src/editor-mode.ts +19 -0
  464. package/src/editor-notifications.ts +140 -0
  465. package/src/editor-presence.ts +563 -0
  466. package/src/editor-presentation-activity.ts +50 -0
  467. package/src/editor-presentation-notice.ts +42 -0
  468. package/src/editor-runtime.tsx +145 -0
  469. package/src/editor-server-response.ts +86 -0
  470. package/src/editor-session-attribution.ts +75 -0
  471. package/src/editor-session-mode.ts +54 -0
  472. package/src/editor-shell-store.ts +1304 -0
  473. package/src/editor-state-facets.ts +74 -0
  474. package/src/editor-styles.css +33 -0
  475. package/src/editor-view-presentation.ts +810 -0
  476. package/src/editor-viewport.ts +5302 -0
  477. package/src/entity-lod.ts +31 -0
  478. package/src/entity-object.ts +91 -0
  479. package/src/files/file-provider.ts +62 -0
  480. package/src/files/project-files.ts +264 -0
  481. package/src/finders/index.ts +136 -0
  482. package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
  483. package/src/frame/bridge.tsx +1408 -0
  484. package/src/frame/product.ts +77 -0
  485. package/src/gameplay-dom-recording.ts +319 -0
  486. package/src/gameplay-export-state.ts +14 -0
  487. package/src/gameplay-replay.ts +417 -0
  488. package/src/gameplay-session-time.ts +9 -0
  489. package/src/gameplay-sessions.ts +204 -0
  490. package/src/harness-chat-types.ts +253 -0
  491. package/src/hierarchy-component-marks.ts +298 -0
  492. package/src/hierarchy-drop.ts +91 -0
  493. package/src/hierarchy-expansion-state.ts +80 -0
  494. package/src/hierarchy-header-slot.ts +52 -0
  495. package/src/hierarchy-internals.ts +197 -0
  496. package/src/hierarchy-kind-icon.ts +217 -0
  497. package/src/hierarchy-mark-reader.ts +73 -0
  498. package/src/hierarchy-menu-registry.ts +67 -0
  499. package/src/hierarchy-node-rows.ts +307 -0
  500. package/src/hierarchy-panel-view.ts +280 -0
  501. package/src/hierarchy-projection.ts +76 -0
  502. package/src/hierarchy-row-cache.ts +243 -0
  503. package/src/hierarchy-row-model.ts +308 -0
  504. package/src/hierarchy-rows.ts +11 -0
  505. package/src/hierarchy-walk.ts +86 -0
  506. package/src/history/editor-session.ts +25 -0
  507. package/src/history/history-commands.ts +147 -0
  508. package/src/history/history-delegate.ts +163 -0
  509. package/src/history/history-limit-notices.ts +43 -0
  510. package/src/history/history-service.ts +1173 -0
  511. package/src/history/persistence-coordinator.ts +35 -0
  512. package/src/history/project-file-history.ts +386 -0
  513. package/src/history/project-root-history-backends.ts +139 -0
  514. package/src/history/resource-registry.ts +209 -0
  515. package/src/history/snapshot-store.ts +103 -0
  516. package/src/history/source-history-backend.ts +546 -0
  517. package/src/history/types.ts +124 -0
  518. package/src/hmr-registration-group.ts +67 -0
  519. package/src/hmr-stable-react-context.ts +23 -0
  520. package/src/hotkeys.ts +188 -0
  521. package/src/inference-diagnostics.ts +69 -0
  522. package/src/initial-project.ts +80 -0
  523. package/src/inspection/active-subject.ts +578 -0
  524. package/src/inspection/active-surface.ts +142 -0
  525. package/src/inspection/compose.ts +1064 -0
  526. package/src/inspection/display.ts +170 -0
  527. package/src/inspection/document-subject.ts +109 -0
  528. package/src/inspection/game-subject.ts +85 -0
  529. package/src/inspection/model.ts +542 -0
  530. package/src/inspection/null-subject.ts +115 -0
  531. package/src/inspection/serialize.ts +357 -0
  532. package/src/inspection/use-active-inspection.ts +180 -0
  533. package/src/inspector-presentation.ts +201 -0
  534. package/src/inspector-section-registry.ts +221 -0
  535. package/src/instance-source-actions.ts +163 -0
  536. package/src/instanced-presentation.ts +164 -0
  537. package/src/js-heap.ts +71 -0
  538. package/src/key-actions.ts +91 -0
  539. package/src/keymap-presets.ts +401 -0
  540. package/src/layout-policy.ts +31 -0
  541. package/src/learn-links.ts +73 -0
  542. package/src/light-explorer-model.ts +134 -0
  543. package/src/live-canvas-frame.ts +55 -0
  544. package/src/live-document.ts +279 -0
  545. package/src/live-module-source.ts +230 -0
  546. package/src/live-session-registry.ts +220 -0
  547. package/src/live-transition.ts +633 -0
  548. package/src/manifest-project.ts +107 -0
  549. package/src/model-thumbnail.ts +523 -0
  550. package/src/native-selection-style.ts +202 -0
  551. package/src/object3d-document-write-policy.ts +137 -0
  552. package/src/packaged-runtime.ts +108 -0
  553. package/src/palettes/maya.palette.json +57 -0
  554. package/src/palettes/substance.palette.json +57 -0
  555. package/src/pasteboard-module.ts +81 -0
  556. package/src/performance-profiler.ts +367 -0
  557. package/src/performance-sources.ts +69 -0
  558. package/src/play-boot-phase.ts +145 -0
  559. package/src/presentation-surface.ts +248 -0
  560. package/src/project-adapter.ts +1143 -0
  561. package/src/project-asset-refresh.ts +26 -0
  562. package/src/project-declaration-refresh.ts +31 -0
  563. package/src/project-local-state.ts +118 -0
  564. package/src/project-manager.ts +243 -0
  565. package/src/project-module-changes.ts +163 -0
  566. package/src/project-module-split.ts +266 -0
  567. package/src/project-provenance.ts +115 -0
  568. package/src/project-ready.ts +42 -0
  569. package/src/project-session-reset.ts +67 -0
  570. package/src/project-shape.ts +68 -0
  571. package/src/project-tool-discovery.ts +66 -0
  572. package/src/project-tools.ts +107 -0
  573. package/src/project-work-types.ts +149 -0
  574. package/src/projection/three.ts +898 -0
  575. package/src/projection/types.ts +44 -0
  576. package/src/readiness.ts +113 -0
  577. package/src/reflection-probe-helper.ts +142 -0
  578. package/src/reported-play-state.ts +90 -0
  579. package/src/resolve-relative-specifier.ts +33 -0
  580. package/src/scene-document-plan.ts +315 -0
  581. package/src/scene-framing.ts +315 -0
  582. package/src/scene-live-open.ts +210 -0
  583. package/src/scene-view-fog.ts +89 -0
  584. package/src/scoped-game-css.ts +152 -0
  585. package/src/session-orphan-record.ts +193 -0
  586. package/src/session-tombstone.ts +126 -0
  587. package/src/settings/settings-provider.ts +82 -0
  588. package/src/settings-store.ts +345 -0
  589. package/src/shared-view-restore.ts +42 -0
  590. package/src/shell-store-door.ts +45 -0
  591. package/src/source-conflict.ts +122 -0
  592. package/src/spatial-handle-visuals.ts +332 -0
  593. package/src/stage-context.ts +341 -0
  594. package/src/stage-store-registry.ts +63 -0
  595. package/src/stale-chunk-recovery.ts +34 -0
  596. package/src/state-report-deferral.ts +73 -0
  597. package/src/storage/host-files-storage.ts +97 -0
  598. package/src/storage/http-storage.ts +174 -0
  599. package/src/storage/index.ts +75 -0
  600. package/src/storage/mem-storage.ts +149 -0
  601. package/src/storage/path-lock.ts +44 -0
  602. package/src/storage/paths.ts +26 -0
  603. package/src/storage/types.ts +116 -0
  604. package/src/stories/StoryComponentThumbnail.tsx +184 -0
  605. package/src/stories/StoryPreviewMount.tsx +306 -0
  606. package/src/stories/component-content-source.tsx +106 -0
  607. package/src/stories/component-states-source.ts +78 -0
  608. package/src/stories/compose-project-stories.ts +255 -0
  609. package/src/stories/pixi-story-model.ts +30 -0
  610. package/src/stories/prefabs-finder.ts +54 -0
  611. package/src/stories/prefabs-from-stories.ts +182 -0
  612. package/src/stories/project-story-discovery.ts +78 -0
  613. package/src/stories/project-story-regions.ts +24 -0
  614. package/src/stories/story-actions.ts +54 -0
  615. package/src/stories/story-capture-command.ts +124 -0
  616. package/src/stories/story-capture.ts +584 -0
  617. package/src/stories/story-declared-medium.ts +126 -0
  618. package/src/stories/story-discovery.ts +176 -0
  619. package/src/stories/story-document-openers.ts +36 -0
  620. package/src/stories/story-dom-runtime.ts +78 -0
  621. package/src/stories/story-grouping.ts +111 -0
  622. package/src/stories/story-lane.ts +88 -0
  623. package/src/stories/story-mount-turn.ts +27 -0
  624. package/src/stories/story-opener.ts +260 -0
  625. package/src/stories/story-pixi-preview.ts +408 -0
  626. package/src/stories/story-presentation.ts +215 -0
  627. package/src/stories/story-registry.ts +517 -0
  628. package/src/stories/story-three-preview.ts +807 -0
  629. package/src/stories/three-story-model.ts +96 -0
  630. package/src/story-three-preview-runtime.ts +56 -0
  631. package/src/surface-keyboard.ts +112 -0
  632. package/src/surface-state.ts +124 -0
  633. package/src/tab-bootstrap.js +628 -0
  634. package/src/tab-census.ts +198 -0
  635. package/src/tab-lifecycle-client.ts +196 -0
  636. package/src/theme-library.ts +844 -0
  637. package/src/theme-preference.ts +378 -0
  638. package/src/theme.css +3437 -0
  639. package/src/three-viewport/bone-selection-highlight.ts +119 -0
  640. package/src/three-viewport/camera-fit.ts +41 -0
  641. package/src/three-viewport/interactive-renderer.ts +132 -0
  642. package/src/three-viewport/selection-brackets.ts +310 -0
  643. package/src/three-viewport/selection-outline.ts +183 -0
  644. package/src/three-viewport/skeleton-helper.ts +61 -0
  645. package/src/three-viewport/source-color.ts +167 -0
  646. package/src/three-viewport/studio-environment.ts +96 -0
  647. package/src/three-viewport-presentation.ts +23 -0
  648. package/src/tool-contribution-play.ts +74 -0
  649. package/src/tool-loader.ts +1798 -0
  650. package/src/transform-mode-request.ts +57 -0
  651. package/src/transient-hint.ts +78 -0
  652. package/src/trigger-volume-helper.ts +116 -0
  653. package/src/ui-source/adapter-region-includes.ts +241 -0
  654. package/src/ui-source/ensure-import.ts +132 -0
  655. package/src/ui-source/file-region-resolver.ts +302 -0
  656. package/src/ui-source/inspect.ts +775 -0
  657. package/src/ui-source/oid-transform.ts +1845 -0
  658. package/src/ui-source/plan-csf-story.ts +196 -0
  659. package/src/ui-source/plan-extract-component.ts +421 -0
  660. package/src/ui-source/plan-fork-component.ts +621 -0
  661. package/src/ui-source/plan-named-style.ts +95 -0
  662. package/src/ui-source/plan-source-edit.ts +353 -0
  663. package/src/ui-source/r3f-contract-resolver.ts +327 -0
  664. package/src/ui-source/r3f-diagnostic-index.ts +165 -0
  665. package/src/ui-source/r3f-environment-binding.ts +133 -0
  666. package/src/ui-source/r3f-joint-binding.ts +251 -0
  667. package/src/ui-source/r3f-lod-binding.ts +91 -0
  668. package/src/ui-source/r3f-particle-binding.ts +235 -0
  669. package/src/ui-source/r3f-physics-binding.ts +483 -0
  670. package/src/ui-source/r3f-project-contracts.ts +444 -0
  671. package/src/ui-source/relative-import-specifier.ts +34 -0
  672. package/src/ui-source/reparent-guard.ts +544 -0
  673. package/src/ui-source/source-edit-request.ts +97 -0
  674. package/src/ui-source/source-write-backend.ts +618 -0
  675. package/src/ui-source/syntactic-prop-specs.ts +296 -0
  676. package/src/ui-source/tier-source-write-backend.ts +279 -0
  677. package/src/ui-source/ts-ast.ts +171 -0
  678. package/src/ui-source/utility-class-support.ts +169 -0
  679. package/src/ui-source/write-component-default.ts +133 -0
  680. package/src/ui-source/writer.ts +1989 -0
  681. package/src/viewport-activation-timings.ts +840 -0
  682. package/src/viewport-authoring-policy.ts +186 -0
  683. package/src/viewport-controls-hint.ts +57 -0
  684. package/src/viewport-door.ts +237 -0
  685. package/src/viewport-shading-boundary.ts +12 -0
  686. package/src/vite-error-surface.ts +141 -0
  687. package/src/wait-until.ts +37 -0
  688. package/src/workspace-areas.ts +156 -0
  689. package/src/workspace-aux-commands.ts +11 -0
  690. package/src/workspace-available-documents.ts +127 -0
  691. package/src/workspace-core-utilities.ts +31 -0
  692. package/src/workspace-document-ids.ts +59 -0
  693. package/src/workspace-document-registry.ts +612 -0
  694. package/src/workspace-document-restore.ts +145 -0
  695. package/src/workspace-host-commands.ts +141 -0
  696. package/src/workspace-persistence-gate.ts +40 -0
  697. package/src/workspace-play-utilities.ts +44 -0
  698. package/src/workspace-presets.ts +425 -0
  699. package/src/workspace-regions.ts +294 -0
  700. package/src/workspace-state-persistence.ts +536 -0
  701. package/src/workspace-static-panels.ts +73 -0
  702. package/src/workspace-status-registry.ts +121 -0
  703. package/src/workspace-style.ts +239 -0
  704. package/src/workspace-utility-commands.ts +74 -0
  705. package/src/workspace-utility-registry.ts +263 -0
  706. package/src/workspace-viewport-rect.ts +97 -0
  707. package/src/world-adoption.ts +115 -0
  708. package/src/world-document-routing.ts +104 -0
  709. package/vite-plugin-creation-site-write.ts +184 -0
  710. package/vite-plugin-creation-site.ts +80 -0
  711. package/vite-plugin-game-static.ts +303 -0
  712. package/vite-plugin-module-doorways.ts +336 -0
  713. package/vite-plugin-product-contributions.ts +197 -0
  714. package/vite-plugin-project-game-static.ts +125 -0
  715. package/vite-plugin-project-jsx-js.ts +65 -0
  716. package/vite-plugin-project-root-absolute-assets.ts +359 -0
  717. package/vite-plugin-shared-react.ts +420 -0
  718. package/vite-plugin-shared-three.ts +203 -0
  719. package/vite-plugin-ui-oid.ts +2106 -0
@@ -0,0 +1,1192 @@
1
+ /**
2
+ * Packaged editor server — the entry a `@vgai/editor` npm package resolves from a
3
+ * PROJECT's own `node_modules`, with NO monorepo checkout on disk (Phase B; see
4
+ * the phase plan's D-FORK decision + §0/§3 "the real work" for `@vgai/editor`).
5
+ *
6
+ * Run with:
7
+ * VGAI_PROJECT=<projectDir> node dist-server/packaged.mjs
8
+ *
9
+ * ## Why this file exists (and isn't just `dev.ts`)
10
+ *
11
+ * `dev.ts` boots ONE Vite instance rooted at the monorepo checkout, which
12
+ * does double duty: it builds the editor's OWN React app AND resolves the
13
+ * OPENED PROJECT's module graph (bare imports like `three`/`zod`/`react`
14
+ * resolve only because that Vite's `root` is the repo root, whose hoisted
15
+ * `node_modules` has everything). That is exactly what a registry install
16
+ * does NOT have: there is no checkout, so there is no repo root to serve
17
+ * either graph from.
18
+ *
19
+ * This file is the second code path: it serves the editor's OWN
20
+ * app as a PREBUILT static SPA (`../dist/`, produced by the workspace-local
21
+ * `npm run build -w @vgai/editor`) — no dev-mode transform of the editor's
22
+ * own source is needed, since a project author isn't hot-reloading the
23
+ * editor's own UI — and boots a SEPARATE, PROJECT-ROOTED Vite instance
24
+ * (`root: <project>`, `configFile: false`, middleware mode, HMR + watch
25
+ * ENABLED) whose job is ONLY to resolve/transform the opened PROJECT's own
26
+ * source. Because that Vite instance's `root` is the project directory,
27
+ * Vite's own resolver naturally walks the PROJECT's OWN `node_modules` for
28
+ * every bare specifier (`three`, `zod`, `react`, ...) —
29
+ * for free, with zero hand-written import rewriting. This is the same
30
+ * pattern `packages/editor/scripts/run-conformance.ts` uses to run the
31
+ * adapter conformance kit against an arbitrary external folder, adapted for
32
+ * live editing (HMR/watch on) instead of a one-shot SSR run.
33
+ *
34
+ * The project's runtime packages — `@vgai/project`, `@vgai/threejs-runtime`
35
+ * and `@vgai/game-runtime` — resolve to the PROJECT's own installed copies,
36
+ * not copies baked into this package's tarball. A project pinning
37
+ * `@vgai/game-runtime@0.3.0` must be served ITS 0.3.0 source, so each is
38
+ * resolved via `createRequire`, rooted at the project directory — Node's own
39
+ * resolution algorithm, not a hardcoded path (see
40
+ * `resolveInstalledPackageSrcDir` in `server-utils.ts`).
41
+ *
42
+ * ## React-world OID authoring parity (DEBT-4 follow-on)
43
+ *
44
+ * `dev.ts` reaches THREE extra plugins via its `configFile:` (repo-root
45
+ * `vite.config.ts`): `uiOidPlugin()` (stamps `data-oid` on every JSX element
46
+ * in project-scoped `.tsx` files + serves the `/__ui-source/*` read/write
47
+ * endpoints the hierarchy/inspector's source-write path needs),
48
+ * `creationSiteWritePlugin()` (the `/__ingest-source/*` ownership +
49
+ * read/plan/apply routes an INGEST root's edits are written through — absent
50
+ * here until 2026-08-20, which made ingest-lane source writes unreachable
51
+ * under a registry install no matter what the client asked) and
52
+ * `reactRootProviderPlugin()` (resolves the synthetic
53
+ * `/__vgai-game-provider` module `binding-resolver.ts`'s
54
+ * `loadProjectWorldProvider` imports — WITHOUT it, every `kind: 'dom'`
55
+ * world 404s on mount, not just "authors without OID"). All are carried
56
+ * over here, imported straight from this package's own `vite-plugin-ui-oid`
57
+ * / `vite-plugin-creation-site-write` / `vite-plugin-react-world-provider`
58
+ * (same files, no copy) and registered
59
+ * with NO extra scoping beyond their own built-in `defaultProjectScopeInclude`
60
+ * (already project-scoped — see that file's doc comment: excludes
61
+ * `node_modules`, vendored trees, and the vgai tooling/engine source, which
62
+ * this Vite instance never serves anyway since its `root` IS the project).
63
+ *
64
+ * `@vitejs/plugin-react` (`react()`) is DELIBERATELY NOT added here — this
65
+ * is the one piece of `dev.ts` parity that is a genuine, structural
66
+ * incompatibility, not a missing plugin call:
67
+ *
68
+ * 1. **Fast Refresh's preamble has nowhere to run.** `@vitejs/plugin-react`
69
+ * only skips its fast-refresh code-injection when `config.isProduction ||
70
+ * config.command === 'build' || config.server.hmr === false` — none of
71
+ * which hold here (this instance is `middlewareMode` `serve` with `hmr`
72
+ * ON, by design, for live editing). With fast-refresh injection ON, every
73
+ * transformed `.tsx` module gets a trailer that THROWS
74
+ * (`"@vitejs/plugin-react can't detect preamble. Something is wrong."`)
75
+ * unless `window.$RefreshReg$`/`$RefreshSig$` were set by the preamble
76
+ * script `transformIndexHtml` injects into an HTML page THAT INSTANCE
77
+ * SERVES. This instance is `appType: 'custom'` (no HTML serving at all —
78
+ * the editor's own shell is a PREBUILT static `dist/`, sent by `express.
79
+ * static` below with zero Vite involvement, so no `transformIndexHtml`
80
+ * hook ever runs for it either). Registering `react()` as-is would not
81
+ * degrade gracefully to "no Fast Refresh" — it would make every react-
82
+ * world/component module throw on first load, a regression versus
83
+ * today's baseline (plain esbuild JSX, which works fine for basic JSX —
84
+ * proven by the default template). The only way to silence just the
85
+ * fast-refresh half (short of a bespoke preamble-bootstrap module the
86
+ * browser would need to import before any project entry, itself a new
87
+ * client-side subsystem) is `server.hmr: false` on the WHOLE instance,
88
+ * which would also kill the `vgai-script-hmr` plugin below's WebSocket
89
+ * delivery — not an acceptable trade for this slice.
90
+ * 2. **JSX compilation itself doesn't need it.** Vite's default esbuild JSX
91
+ * transform (`automatic` runtime, no config here) already compiles
92
+ * `.tsx`/`.jsx` correctly without `@vitejs/plugin-react` — proven by the
93
+ * default (threejs-only) template authoring fine today, and unaffected
94
+ * by this file's plugin list either way.
95
+ *
96
+ * So: OID stamping (source-write authoring) and the `WorldProvider` identity
97
+ * fix are real, working parity gained by this change. React Fast Refresh
98
+ * for a project's own components remains NOT at `dev.ts` parity under the
99
+ * packaged runtime — a documented, structural gap, not a hidden one.
100
+ *
101
+ * ## ONE React for the editor's tree — the shell's (`vite-plugin-shared-react.ts`)
102
+ *
103
+ * The deeper structural gap this file's two-graph design opens: the editor's
104
+ * own `react`/`react-dom` are STATICALLY bundled into its prebuilt SPA
105
+ * (`dist/`, built at `npm run build -w @vgai/editor` time), while this
106
+ * project-rooted Vite instance resolves bare `react` from the PROJECT's own
107
+ * `node_modules` — two React module instances on one page, reading different
108
+ * internal singletons. `dev.ts` has no such split (ONE Vite + repo-root
109
+ * `vite.config.ts`'s `resolve.dedupe: ['react', 'react-dom', …]`), which is
110
+ * why nothing in a checkout ever reproduced any of it.
111
+ *
112
+ * It surfaced twice, both confirmed live against a tarball-installed,
113
+ * checkout-absent packaged editor:
114
+ *
115
+ * 1. a `dom` root's `useWorldState` threw `TypeError: Cannot read properties
116
+ * of null (reading 'useContext')` at mount, because the editor's bundled
117
+ * `createRoot` reconciled a component whose hooks came from the project's
118
+ * react; and
119
+ * 2. every project TOOL CONTRIBUTION — the four dev-tools inspector facets
120
+ * (`src/contributions/game-*.inspector.tsx`), the builder inspectors, the World
121
+ * Labs asset inspector, the data-tables document — REGISTERED and then
122
+ * rendered as a crash box: `Invalid hook call … more than one copy of
123
+ * React in the same app`. Those render INSIDE the editor's own tree, so
124
+ * there was no isolated subtree to hide in.
125
+ *
126
+ * The fix, for (2): the editor build publishes its OWN React (plus `react-dom`,
127
+ * `react-dom/client`, `react/jsx-runtime`, `react/jsx-dev-runtime`, and the one
128
+ * third-party package that calls React hooks in the editor's tree) as entry
129
+ * chunks, and `sharedReactPlugin` below resolves those specifiers — for
130
+ * the EDITOR TREE only — to those chunks' URLs, the same URLs the shell's
131
+ * bundle already imports, so the browser's module map hands both graphs one
132
+ * instance. `@volter/editor-sdk/widgets` moves out of `optimizeDeps.include` and into
133
+ * `exclude` for the same reason: a prebundled chunk's React is decided at
134
+ * prebundle time, where a source-level redirect cannot reach it.
135
+ *
136
+ * The seam stops at the GAME, and that boundary is measured, not assumed: a
137
+ * first attempt redirected the whole graph and broke Play mode
138
+ * (`@react-three/fiber`'s prebundled DEVELOPMENT `react-reconciler` against the
139
+ * shell's PRODUCTION React → `Cannot read properties of undefined (reading
140
+ * 'push')` in `updateContainer`, then `onCreated did not fire within 10s`). So
141
+ * the game keeps its own development React — which is exactly (1)'s fix, and
142
+ * why the react-world and story MODULE DOORWAYS below still route the
143
+ * editor's own mount/story imports through THIS graph. Read
144
+ * `../vite-plugin-shared-react.ts` for the full rule and for why the entry
145
+ * bodies are generated rather than written.
146
+ *
147
+ * ## Scoped dependency discovery, not disabled discovery (C3 follow-on)
148
+ *
149
+ * `optimizeDeps` below used to carry `noDiscovery: true` (see git history for
150
+ * the exact prior comment): with the boot-time crawl DEFAULT-targeting the
151
+ * project's own `index.html`, and a scaffolded project's `src/main.ts`
152
+ * bootstrap containing a `?net=p2p-host`-gated `await import('../server/
153
+ * rooms')` that esbuild's scanner statically follows regardless of the
154
+ * runtime guard around it, that crawl reached real Colyseus SERVER code
155
+ * (`colyseus` -> `@colyseus/core` -> `@pm2/io`, a package with no browser-
156
+ * resolvable entry) and crashed the esbuild prebundle step outright. Turning
157
+ * discovery off entirely sidestepped that crash — at the cost of any CJS-
158
+ * only dependency NOT in the hardcoded `include` list below being served raw
159
+ * to the browser and failing (this is exactly what broke `examples/
160
+ * r3f-first-party`: `@react-three/fiber`'s CJS transitive deps, e.g.
161
+ * `use-sync-external-store/shim/with-selector.js`, have no place to be
162
+ * hardcoded — this server serves ARBITRARY projects, most without R3F
163
+ * installed at all, and Vite errors on `include`-ing a not-installed dep).
164
+ *
165
+ * The fix (C3, `phase-b/c3-r3f-packaged`) is not "add R3F to the allowlist"
166
+ * — that just moves the whack-a-mole to the next ecosystem a project
167
+ * happens to pull in. It SCOPES the crawl instead of disabling it:
168
+ * `optimizeDeps.entries` is set (see `projectOptimizeDepsEntries` /
169
+ * `computeProjectOptimizeDepsEntries` in `project-optimize-deps-entries.ts`)
170
+ * to the OPENED PROJECT's own `vgai.project.json`-declared world `entry` files
171
+ * — the exact per-world mount code the editor's binding-resolver dynamically
172
+ * imports for Play mode, never the project's `src/main.ts` bootstrap (a
173
+ * standalone-build concern this Vite instance never serves — the editor's
174
+ * own prebuilt SPA answers `/`). A world entry's own import graph reaches
175
+ * real client packages (react, R3F, a legitimate client `@colyseus/sdk`
176
+ * import for real multiplayer — `examples/third-person-arena/src/
177
+ * network.ts`) but never `server/`, so the `@pm2/io` crash class cannot
178
+ * occur no matter what a project's room code imports, while ANY CJS-needing-
179
+ * prebundle client dependency a world reaches gets auto-discovered and
180
+ * prebundled — no per-package allowlist, adapts to whatever the opened
181
+ * project actually has installed. `project-optimize-deps-entries.ts`'s doc
182
+ * comment has the empirical proof (direct `vite.optimizeDeps` runs, not just
183
+ * theory): scoped to the template's world entry, prebundling succeeds
184
+ * clean; scoped to `index.html` (the old default), it reproduces the exact
185
+ * `@pm2/io` crash; scoped to `examples/r3f-first-party`'s world entries, `@react-
186
+ * three/fiber`/`@react-three/drei` are discovered and prebundled with no
187
+ * hardcoding; scoped to `examples/third-person-arena`'s world entry (a real
188
+ * colyseus multiplayer project), `@colyseus/sdk` prebundles clean too — the
189
+ * client SDK's own published build is self-contained and never reaches
190
+ * `@colyseus/core`'s server-only `Stats.mjs`. See the C3 build report for
191
+ * the real-browser proof (checkout absent, tarball-installed): R3F's
192
+ * `examples/r3f-first-party` "net" world mounts with no console error, AND the
193
+ * pre-existing react-world (C1) / colyseus-bearing-project (this section)
194
+ * cases still work — no regression.
195
+ *
196
+ * ## Known scope trims (v1 — flagged, not silently dropped)
197
+ *
198
+ * - Project creation resolves the shared scaffold from this package's npm
199
+ * installation root, so the in-editor and terminal journeys use the same
200
+ * packaged template, examples, and registry dependency mode.
201
+ */
202
+
203
+ import { createRequire } from 'node:module';
204
+ import path from 'node:path';
205
+ import { fileURLToPath } from 'node:url';
206
+ import type { ProductIdentity } from '@volter/editor-sdk/session/product-locator';
207
+ import express from 'express';
208
+ import {
209
+ createServer as createViteServer,
210
+ type InlineConfig,
211
+ optimizeDeps,
212
+ resolveConfig,
213
+ } from 'vite';
214
+ import {
215
+ DEFAULT_EDITOR_PORT,
216
+ editorHmrPort,
217
+ editorOrigin,
218
+ } from '@volter/editor-project/manifest/editor-port';
219
+ import { servesIngestSourceRoutes } from '../vite-plugin-creation-site-write';
220
+ import { PACKAGED_MODULE_DOORWAYS } from '../vite-plugin-module-doorways';
221
+ import { readSharedReactManifest, sharedReactUrls } from '../vite-plugin-shared-react';
222
+ import { readSharedThreeManifest, sharedThreeUrl } from '../vite-plugin-shared-three';
223
+ import { servesUiSourceRoutes } from '../vite-plugin-ui-oid';
224
+ import { canonicalProjectRoot } from './canonical-path';
225
+ import { createEditorServer } from './editor-server';
226
+ import { clientCount } from './editor-sse';
227
+ import {
228
+ builtFrameBridgeModule,
229
+ FRAME_BRIDGE_PACKAGED_PATH,
230
+ readBuiltProductEntry,
231
+ } from './frame-bridge';
232
+ import {
233
+ type FrameWorkbench,
234
+ frameLaunchFromEnv,
235
+ frameWorkbenchUrl,
236
+ startFrameWorkbench,
237
+ } from './frame-workbench';
238
+ import {
239
+ createIdleShutdown,
240
+ formatIdleWindow,
241
+ IDLE_SHUTDOWN_MINUTES_ENV,
242
+ resolveIdleShutdownMs,
243
+ } from './idle-shutdown';
244
+ import { jsProfilingPolicy } from './js-profiling-policy';
245
+ import { configureManagedAccountDefaults } from './managed-account-defaults';
246
+ import { closeHttpServer, createProcessShutdown } from './process-shutdown';
247
+ import { projectServingRoots } from './project-install-roots';
248
+ import { freshProjectModuleLoader } from './project-module-freshness';
249
+ import {
250
+ computeEditorRuntimeIncludes,
251
+ computeRuntimeSourceCrawlEntries,
252
+ computePackageContributionCrawlEntries,
253
+ computeProjectOptimizeDepsEntries,
254
+ computeUnresolvableRuntimeImports,
255
+ RUNTIME_PACKAGE_NAMES,
256
+ SERVER_ONLY_PREBUNDLE_EXCLUDE,
257
+ viteOptimizeDepsEntries,
258
+ } from './project-optimize-deps-entries';
259
+ import { isProjectScratchPath } from './project-scratch-path';
260
+ import { createProjectServingPlugins } from './project-serving-plugins';
261
+ import {
262
+ friendlyListenError,
263
+ resolveBindHost,
264
+ resolveInstalledPackageSrcDir,
265
+ } from './server-utils';
266
+ import { resolveProductForProject, sessionProduct } from './session-product';
267
+ import { registerSession, unregisterSession } from './session-registry';
268
+
269
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
270
+ // This file lives directly under the `@vgai/editor` package root either as
271
+ // `packages/editor/server/packaged.ts` (source, run via tsx) or as the
272
+ // esbuild-bundled `packages/editor/dist-server/packaged.mjs` (`server/` and
273
+ // `dist-server/` are BOTH direct children of the package root) — so
274
+ // `resolve(__dirname, '..')` lands on the package root in either shape.
275
+ const editorPackageRoot = path.resolve(__dirname, '..');
276
+ const checkoutRoot = path.resolve(editorPackageRoot, '..', '..');
277
+ const PORT =
278
+ Number(process.env['PORT']) || Number(process.env['VGAI_EDITOR_PORT']) || DEFAULT_EDITOR_PORT;
279
+ const HMR_PORT = Number(process.env['VGAI_HMR_PORT']) || editorHmrPort(PORT);
280
+ const HMR_CLIENT_PORT = Number(process.env['VGAI_HMR_CLIENT_PORT']) || HMR_PORT;
281
+ // S3: bind to loopback by default; opt-in to a wider interface via env.
282
+ const HOST = resolveBindHost();
283
+ // Same rule as dev.ts: every URL this process prints or opens names the host it
284
+ // actually bound, never `localhost` (see `editorOrigin`).
285
+ const EDITOR_ORIGIN = editorOrigin(PORT, HOST);
286
+
287
+ if (!process.env['VGAI_PROJECT']) {
288
+ console.error(
289
+ '\n \x1b[31mVGAI_PROJECT is required — the packaged editor has no monorepo checkout to fall ' +
290
+ 'back to. Set VGAI_PROJECT=<absolute path to your vgai project>.\x1b[0m\n',
291
+ );
292
+ process.exit(1);
293
+ }
294
+ // Canonicalized (symlink-resolved) — see canonical-path.ts's doc comment
295
+ // (dev.ts's identical fix: a react world's `WorldProvider` colocation breaks
296
+ // if this path and Vite's own resolver disagree on a symlinked segment).
297
+ const projectPath = canonicalProjectRoot(process.env['VGAI_PROJECT']);
298
+
299
+ // THE PRODUCT'S BUILD is what this host serves — `npm run build -w
300
+ // @vgai/game-editor` / `-w @vgai/model-editor`, each into its own package's
301
+ // `dist/` (`packages/editor/vite-product-build.ts`). The kit has no browser
302
+ // build of its own any more: `frame/bridge.tsx` is a module the product's entry
303
+ // imports, and the ENTRY is what a build has (ARCHITECTURE-CORE §The target
304
+ // shape, rule 4: "each product's build is its own entry").
305
+ //
306
+ // `null` when this session serves no product — the served-modules door says so
307
+ // by name rather than answering with an empty directory.
308
+ const resolvedProduct = sessionProduct(projectPath);
309
+ if (resolvedProduct === null) {
310
+ // The same refusal the CLI prints before it ever spawns this process. Said
311
+ // again here because this host can also be started by hand, and a packaged
312
+ // session with no product has no bundle to serve at all — there is nothing to
313
+ // degrade to.
314
+ try {
315
+ resolveProductForProject(projectPath);
316
+ } catch (error) {
317
+ console.error(`\n \x1b[31m${error instanceof Error ? error.message : String(error)}\x1b[0m\n`);
318
+ }
319
+ process.exit(1);
320
+ }
321
+ // Re-bound with the non-null type rather than leaning on the narrowing above:
322
+ // every reader below is inside a closure, and a narrowing is not a type.
323
+ const sessionProductIdentity: ProductIdentity = resolvedProduct;
324
+ const distPath = path.join(sessionProductIdentity.dir, 'dist');
325
+
326
+ // WHAT THIS SESSION WAS TOLD TO FRAME — the workbench directory `vgai edit`
327
+ // resolved and the two reserved ports. `null` when the launch named none.
328
+ const frameLaunch = frameLaunchFromEnv();
329
+ /** What the frame is running, once it is. Read by `/__editor/state`. */
330
+ let frameWorkbench: FrameWorkbench | null = null;
331
+
332
+ // Where this project's modules REALLY live — the project plus the install
333
+ // roots Node itself would resolve its dependencies from
334
+ // (`project-install-roots.ts`). This replaces a boot-time
335
+ // `realpath(<project>/node_modules)` guess that was correct only for a
336
+ // standalone flat install: a workspace-member project (every `examples/<id>`,
337
+ // any user monorepo game) has no `node_modules` of its own — npm hoists it
338
+ // ABOVE the project — and its sibling packages resolve through symlinks that
339
+ // land outside the project entirely, so Vite 403'd the world's own engine
340
+ // imports and served `?inline` CSS raw instead of transforming it. See that
341
+ // module's header for the measured failures.
342
+ const projectFsRoots = projectServingRoots(projectPath);
343
+
344
+ // D-X2: the project-static route must follow the CURRENTLY open project, not
345
+ // a boot-time snapshot — mirrors dev.ts's `currentProjectRoot` box, updated
346
+ // in the SAME `onProjectOpened` callback that extends Vite's fs.allow/
347
+ // watcher/game-globals shadow for a newly opened project.
348
+ let currentProjectRoot: string = projectPath;
349
+ const projectRoots = new Set<string>([projectPath]);
350
+
351
+ // C3: the OPENED project's own manifest-declared world entry files — scopes
352
+ // `optimizeDeps.entries` below to exactly the client code a world can mount,
353
+ // never `index.html`/the project's own bootstrap `main.ts` (which is how the
354
+ // crawl used to reach `server/` room code and crash on `@pm2/io`). Boot-time
355
+ // snapshot only, same trim as `runtimeSources`/`resolve.alias` below — see
356
+ // `project-optimize-deps-entries.ts`'s doc comment for the full rationale
357
+ // and the empirical proof this avoids the colyseus-scanner crash.
358
+ const projectOptimizeDepsEntries = computeProjectOptimizeDepsEntries(projectPath);
359
+
360
+ /** Whether `packageName` resolves from the PROJECT's own node_modules —
361
+ * Node's resolution rooted at the project, same discipline as
362
+ * `resolveInstalledPackageSrcDir` (server-utils.ts), for deps that are only
363
+ * safe to `optimizeDeps.include` when actually installed. */
364
+ function projectHasPackage(fromDir: string, packageName: string): boolean {
365
+ try {
366
+ createRequire(path.join(fromDir, 'package.json')).resolve(packageName);
367
+ return true;
368
+ } catch {
369
+ return false;
370
+ }
371
+ }
372
+
373
+ async function main(): Promise<void> {
374
+ configureManagedAccountDefaults();
375
+ const app = express();
376
+ // Cross-origin isolation, and the Blender WORKER is why it stays. Removing
377
+ // these in the belief that nothing wanted a SharedArrayBuffer broke every
378
+ // module worker on the page (measured 2026-09-14): a cross-origin-isolated
379
+ // document may only create a dedicated worker whose SCRIPT is itself
380
+ // delivered with a COEP header, and without it Chrome refuses with an
381
+ // ErrorEvent carrying no message, no file and no error — a three-line worker
382
+ // failed exactly like Blender's. Blender runs in a module worker, so this
383
+ // middleware is load-bearing. `same-origin` costs the editor nothing — every
384
+ // window it opens is `noopener` already — and `credentialless` embedding
385
+ // keeps cross-origin assets loading (without credentials) where
386
+ // `require-corp` would demand a resource policy on each. Set first, ahead of
387
+ // every route and of Vite's own responses.
388
+ app.use((_req, res, next) => {
389
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
390
+ res.setHeader('Cross-Origin-Embedder-Policy', 'credentialless');
391
+ next();
392
+ });
393
+
394
+ // Idle self-shutdown — same contract, same wiring, same reasons as dev.ts
395
+ // (read idle-shutdown.ts's header for the ownership story). Armed ahead of
396
+ // every route so `noteActivity` sees each request; the forward reference is
397
+ // resolved to this host's own shutdown function below.
398
+ let onIdleShutdown: (why: string) => void = () => {};
399
+ const idleShutdown = createIdleShutdown({
400
+ idleMs: resolveIdleShutdownMs(),
401
+ hasConnectedClients: () => clientCount() > 0,
402
+ onIdle: (why) => onIdleShutdown(why),
403
+ });
404
+ app.use((_req, _res, next) => {
405
+ idleShutdown.noteActivity();
406
+ next();
407
+ });
408
+
409
+ // Same contract as dev.ts: grant the editor page the JS Self-Profiling API
410
+ // its own `visible-tab-fps-floor` invariant tells the reader to use, ahead of
411
+ // every route so it lands on whichever of this host's three paths
412
+ // (`express.static`, the SPA fallback, Vite) writes the document response.
413
+ // See `js-profiling-policy.ts`.
414
+ app.use(jsProfilingPolicy());
415
+
416
+ // The project's own installed runtime packages — resolved from the PROJECT's
417
+ // node_modules (Node's own resolution algorithm), never copies baked into
418
+ // this package's own tarball (see this file's header doc comment). A package
419
+ // that does not resolve simply contributes no alias — e.g. a project that
420
+ // hasn't run `npm install` yet — so a bad install reports a resolution
421
+ // failure without silently using the editor package's own copy.
422
+ const runtimeSources = new Map<string, string>();
423
+ for (const name of RUNTIME_PACKAGE_NAMES) {
424
+ const dir = resolveInstalledPackageSrcDir(projectPath, name);
425
+ if (dir) runtimeSources.set(name, dir);
426
+ }
427
+ const unresolvedRuntimePackages = RUNTIME_PACKAGE_NAMES.filter(
428
+ (name) => !runtimeSources.has(name),
429
+ );
430
+ if (unresolvedRuntimePackages.length > 0) {
431
+ console.warn(
432
+ `\n \x1b[33mWarning:\x1b[0m ${unresolvedRuntimePackages.join(', ')} not resolvable from ` +
433
+ `${projectPath} — run npm install in the project first. ` +
434
+ 'Their imports will fail to resolve until then.\n',
435
+ );
436
+ }
437
+
438
+ // The editor's OWN browser-side modules, folded into the boot pass. The
439
+ // four `/__vgai-*-runtime` plugins registered above synthesize modules that
440
+ // exist only in memory, so NO crawl entry can reach them, yet they are what
441
+ // the editor imports at the first Play mount / story preview — every
442
+ // specifier they carry was otherwise discovered mid-session, re-optimized,
443
+ // and hard-reloaded the controlling tab. See
444
+ // `project-optimize-deps-entries.ts`'s "EDITOR's own browser-side modules"
445
+ // section for the measurement and for why the three parts differ:
446
+ // `include` for the packages (gated on the project actually having them),
447
+ // crawl `entries` for the engine subpaths (which `exclude` below deliberately
448
+ // keeps source-served, and which the scanner therefore refuses to follow by
449
+ // specifier), and `exclude` for engine imports this project cannot resolve
450
+ // (without which the added crawl would throw and kill the boot).
451
+ const editorRuntimeIncludes = computeEditorRuntimeIncludes(projectPath);
452
+ const runtimeSourceCrawlEntries = computeRuntimeSourceCrawlEntries(runtimeSources);
453
+ const unresolvableRuntimeImports = computeUnresolvableRuntimeImports(runtimeSources, projectPath);
454
+ // The skew packages the project declares (`@volter/editor-blender`, `@vgai/game`): their
455
+ // contributions are walked at boot the way the engine's source is, so the
456
+ // dependencies they reach are prebundled instead of served raw — see
457
+ // `computePackageContributionCrawlEntries`.
458
+ const packageContributionCrawl = computePackageContributionCrawlEntries(projectPath);
459
+
460
+ // ONE React for everything that renders inside the EDITOR's own tree — the
461
+ // shell's. The shell is a prebuilt bundle with React inlined; this Vite
462
+ // instance serves the project's source and would otherwise resolve `react`
463
+ // to the PROJECT's node_modules, so a tool contribution called hooks from a
464
+ // different React than the one reconciling it ("Invalid hook call"). The
465
+ // build publishes the shell's own React as entry chunks; `sharedReactPlugin`
466
+ // below points the editor tree's React specifiers at those chunks' URLs. The
467
+ // GAME keeps the project's own React — see that plugin's doc comment for the
468
+ // measured reason that boundary exists.
469
+ const sharedReact = readSharedReactManifest(distPath);
470
+ if (!sharedReact) {
471
+ console.warn(
472
+ `\n \x1b[33mWarning:\x1b[0m ${distPath} carries no usable vgai-shared-react.json — this ` +
473
+ 'editor build predates the shared-React entry chunks, or predates one of ' +
474
+ 'them (the manifest must name every published specifier). Project tool contributions, ' +
475
+ 'Asset Lab documents and asset inspectors will crash with "Invalid hook call" ' +
476
+ 'because the page has two React copies. Rebuild the editor ' +
477
+ `(npm run build -w ${sessionProductIdentity.name}).\n`,
478
+ );
479
+ }
480
+ const sharedReactSpecifierUrls = sharedReact ? sharedReactUrls(sharedReact) : null;
481
+
482
+ // ONE three for the whole page — the shell's. The shell inlines three (the
483
+ // viewport renderer, gizmos, ~100 modules) and evaluates it at boot; this
484
+ // project-rooted Vite would otherwise prebundle the PROJECT's own three into
485
+ // `.vite/deps`, a SECOND instance whose module body trips three's
486
+ // `window.__THREE__` "Multiple instances" guard and keeps `vgai console` off
487
+ // exit-0. The build publishes the shell's three as a chunk;
488
+ // `sharedThreePlugin` (below, via project-serving-plugins) redirects the
489
+ // project graph's `three` onto its URL, and `optimizeDeps.exclude` below
490
+ // keeps three from being prebundled so Fiber's own `import 'three'` is
491
+ // externalized and follows the same redirect. See
492
+ // `../vite-plugin-shared-three.ts`.
493
+ const sharedThree = readSharedThreeManifest(distPath);
494
+ if (!sharedThree) {
495
+ console.warn(
496
+ `\n \x1b[33mWarning:\x1b[0m ${distPath} carries no usable vgai-shared-three.json — this ` +
497
+ 'editor build predates the shared-three entry chunk. The page will load three.js twice ' +
498
+ "(the shell's inlined copy and the project's prebundled copy), and three's own " +
499
+ 'duplicate-instance guard will warn, blocking a clean console. Rebuild the editor ' +
500
+ `(npm run build -w ${sessionProductIdentity.name}).\n`,
501
+ );
502
+ }
503
+ const sharedThreeSpecifierUrl = sharedThree ? sharedThreeUrl(sharedThree) : null;
504
+
505
+ // 1. Boot a Vite instance rooted at the PROJECT, not this editor package
506
+ // or a monorepo checkout — its own node_modules resolves every bare
507
+ // specifier for free (the run-conformance.ts pattern, live-editing not
508
+ // one-shot: HMR + watch stay ON, unlike that script's headless SSR run).
509
+ // Annotated rather than inferred: an unannotated object literal widens
510
+ // `configFile: false` to `boolean` and `appType: 'custom'` to `string`,
511
+ // neither of which `resolveConfig` accepts — so the literal typechecks on
512
+ // its own and only fails at the call site far below.
513
+ const viteInlineConfig: InlineConfig = {
514
+ configFile: false,
515
+ root: projectPath,
516
+ appType: 'custom', // no HTML serving/SPA-fallback — this instance only serves project modules
517
+ // Same reasoning as `dev.ts`: a transform failure in the project's own
518
+ // source must not erase this terminal's scrollback (Vite's error
519
+ // middleware logs with `{ clear: true }`), because the `✖ Invalid project
520
+ // file` verdict printed for the same edit is the record that outlives it.
521
+ clearScreen: false,
522
+ // Registry-standalone SDK loadability (WORK.md, measured by the donut
523
+ // build 2026-08-28): a NON-checkout-linked project installs @vgai/* from
524
+ // the registry as PUBLISHED TS SOURCE under node_modules. The project-
525
+ // module loader is `vite.ssrLoadModule`, and Vite externalizes
526
+ // node_modules in SSR — handing the .ts files to Node, whose type
527
+ // stripping refuses anything under node_modules. So a standalone project
528
+ // could not run its own tool contributions (`project.bake.*`,
529
+ // `vgai screenshot <module>`) until a checkout link existed. noExternal
530
+ // keeps every @vgai package inside Vite's own SSR transform, where TS
531
+ // source is ordinary input; a checkout link resolves outside
532
+ // node_modules and never hit the wall, which is why only registry
533
+ // installs were broken.
534
+ ssr: { noExternal: [/^@volter\//] },
535
+ // This Vite instance transforms not only project TSX (which uiOidPlugin
536
+ // already sends through automatic JSX) but also the installed engine's
537
+ // React bridge. Vite/esbuild otherwise compiles that dependency source to
538
+ // `React.createElement` even though the canonical bridge uses named React
539
+ // imports and intentionally has no global `React` binding. Keep Fast
540
+ // Refresh out (the custom app has no preamble host), but make the JSX
541
+ // runtime explicit for every TSX module served through this graph.
542
+ esbuild: { jsx: 'automatic' },
543
+ // ONE ordered list with `dev.ts` (`project-serving-plugins.ts`) — the two
544
+ // hand-maintained lists drifted apart three times, each time as a silent
545
+ // capability hole under a registry install only. `packagedOnly` is what
546
+ // this host genuinely adds: the shared-React redirect, the two source-write
547
+ // route plugins `dev.ts` instead reaches through its repo-root
548
+ // `configFile:`, and the synthetic module doorways that bridge the shell's
549
+ // prebuilt bundle to this separate, project-rooted graph.
550
+ plugins: createProjectServingPlugins({
551
+ projectRoots: () => projectRoots,
552
+ // A THUNK, like every other project-scoped plugin: `onProjectOpened`
553
+ // below moves `currentProjectRoot` when a session switches project, and a
554
+ // boot-time snapshot would keep resolving an INGEST root's
555
+ // out-of-manifest sources, the reported `resourcePath` of a write, and
556
+ // the shared-session path gate against the project this process started
557
+ // on.
558
+ currentProjectRoot: () => currentProjectRoot,
559
+ packagedOnly: {
560
+ // Omitted (`null`) only when this dist predates the shared-React
561
+ // chunks, which the boot warning above names.
562
+ sharedReactUrls: sharedReactSpecifierUrls,
563
+ // Omitted (`null`) only when this dist predates the shared-three chunk
564
+ // (the boot warning above names it).
565
+ sharedThreeUrl: sharedThreeSpecifierUrl,
566
+ editorPackageRoot,
567
+ checkoutRoot,
568
+ doorways: PACKAGED_MODULE_DOORWAYS,
569
+ },
570
+ // Always registered here — a project is ALWAYS open in packaged mode,
571
+ // unlike dev.ts's conditional-on-boot-time-projectPath gate. No
572
+ // `watchDir`: this instance's Vite root IS the project.
573
+ scriptHmr: { projectRoot: () => currentProjectRoot },
574
+ }),
575
+ optimizeDeps: {
576
+ // C3 (phase-b/c3-r3f-packaged) — SCOPED auto-discovery, not disabled
577
+ // discovery. This used to be `noDiscovery: true` (see git history for
578
+ // the pre-C3 version of this comment): that flag disabled Vite's
579
+ // AUTO-prebundling entirely — both the boot-time crawl AND the lazy,
580
+ // on-request discovery of new bare specifiers — because the DEFAULT
581
+ // crawl target (the project's own `index.html`) follows everything
582
+ // dynamically reachable from it, including the project's `server/`
583
+ // room code (a scaffolded project's `src/main.ts` dynamically imports
584
+ // `../server/rooms` behind a `?net=p2p-host` URL-param branch — a
585
+ // branch esbuild's scanner still statically follows), whose colyseus
586
+ // server transitive deps (`@colyseus/core` -> `@pm2/io`) have no
587
+ // browser-resolvable entry and crash the esbuild prebundle step
588
+ // (`Failed to resolve entry for package "@pm2/io"`).
589
+ //
590
+ // That worked for the react-only CJS fix (C1) because the fix could be
591
+ // fully hardcoded: react/react-dom are ALWAYS present (the editor
592
+ // itself depends on them). It does NOT work for R3F (or any other
593
+ // ecosystem a project's own `package.json` happens to pull in): this
594
+ // server can't hardcode `@react-three/fiber` into `include` for every
595
+ // possible project — most projects don't have it installed, and Vite
596
+ // errors on `include`-ing a dep that isn't there.
597
+ //
598
+ // The actual fix: scope the CRAWL, don't disable it. `entries` below
599
+ // points the crawl at the OPENED PROJECT's own manifest-declared world
600
+ // entry files (`computeProjectOptimizeDepsEntries` /
601
+ // `project-optimize-deps-entries.ts`) instead of `index.html` — the
602
+ // exact per-world mount code the editor's binding-resolver dynamically
603
+ // imports for Play mode (NEVER the project's own `src/main.ts`
604
+ // bootstrap, which is a standalone-build concern this Vite instance
605
+ // never serves anyway — the editor's own prebuilt SPA answers `/`).
606
+ // A world entry's import graph can reach real client packages
607
+ // (`react`, `@react-three/fiber`, `@react-three/drei`, a legitimate
608
+ // client `@colyseus/sdk` import for real multiplayer) but never
609
+ // `server/` — see `project-optimize-deps-entries.ts`'s doc comment for
610
+ // the empirical proof (a direct `vite.optimizeDeps` run) that this
611
+ // scoping avoids the `@pm2/io` crash for a colyseus-bearing project
612
+ // while a `@react-three/fiber`-bearing world's CJS transitive deps
613
+ // (`use-sync-external-store` and friends) get discovered and
614
+ // prebundled automatically — no allowlist, adapts to whatever the
615
+ // opened project actually has installed.
616
+ //
617
+ // `include` below is now a MINIMUM baseline, not the only mechanism:
618
+ // React's JSX-runtime specifiers are INJECTED by the JSX transform
619
+ // (not written as literal source imports), so even an entries-scoped
620
+ // crawl does not reliably discover them on its own — the repo-root
621
+ // `vite.config.ts` / template `vite.config.ts` `optimizeDeps.include`
622
+ // carry the identical comment/list for the same reason. Kept
623
+ // unconditional (not project-conditional) since react/react-dom are
624
+ // always present (the editor's own react-world mount runtime depends
625
+ // on them regardless of what a given project uses).
626
+ //
627
+ // `react-dom` (bare, NOT just `/client`) was added for C1
628
+ // (`phase-b/c1-react-mount`): `vite-plugin-module-doorways.ts`'s
629
+ // synthetic `/__vgai-react-world-runtime` module re-exports `flushSync`
630
+ // from bare `react-dom` (the same peer `react-dom/client`'s `createRoot`
631
+ // comes from — `design-time-layers.ts`'s react-world layer mount needs
632
+ // it from that SAME installed package). Confirmed live at the time:
633
+ // a real-browser Playwright run against a tarball-installed,
634
+ // checkout-absent packaged editor threw exactly `SyntaxError: The
635
+ // requested module '/node_modules/react-dom/index.js' does not
636
+ // provide an export named 'flushSync'` at Play-mode mount before this
637
+ // entry was added.
638
+ include: [
639
+ 'react',
640
+ 'react-dom',
641
+ 'react-dom/client',
642
+ 'react/jsx-runtime',
643
+ 'react/jsx-dev-runtime',
644
+ // ^ the GAME's React, and it stays prebundled: the game mounts into
645
+ // its own isolated tree with its own development React and reconciler
646
+ // (see the `sharedReactPlugin` registration above for why the editor
647
+ // shell's production React must not reach it).
648
+ //
649
+ // A scaffold's project-authored client modules are loaded lazily by
650
+ // the prebuilt editor. They are outside the manifest-world entry
651
+ // crawl above, so a cold install otherwise discovers what they import
652
+ // in waves and Vite hard-reloads the controlling tab each time.
653
+ // The editor's own widget kit is NOT here — it renders in the editor's
654
+ // own React tree, so it is `exclude`d below and source-served, which is
655
+ // the only way `sharedReactPlugin`'s scope reaches its React imports.
656
+ // Its dependency closure is deliberately shallow (react plus the widget
657
+ // files — see `src/widgets/index.ts`'s header), so source-serving it
658
+ // costs a round of module requests, not a discovery wave. The ENGINE
659
+ // subpaths these modules import are handled a third way — as crawl
660
+ // `entries` (see `LAZY_ENGINE_CRAWL_SUBPATHS`), because `include`-ing an
661
+ // engine module prebundles a second copy of everything it reaches,
662
+ // which is exactly the split WorldProvider identity the `exclude`
663
+ // below exists to prevent.
664
+ //
665
+ // Project tool contributions load this after the shell is visible;
666
+ // discovering it then would invalidate the already-loaded React graph.
667
+ 'zod',
668
+ // The story runtime (`/__vgai-story-runtime`) re-exports
669
+ // `@storybook/react`, which a project's CSF files import ONLY as
670
+ // types (erased) — so the entries crawl never discovers it, and the
671
+ // first story preview/capture of a session would otherwise trigger a
672
+ // second optimization wave and hard-reload the controlling tab
673
+ // mid-command. Conditional because Vite errors on `include`-ing a
674
+ // package that isn't installed, and `@storybook/react` is a
675
+ // devDependency a project may legitimately not have (it then has no
676
+ // usable stories, and the story lane reports the missing package by
677
+ // name — story-dom-runtime.ts).
678
+ ...(projectHasPackage(projectPath, '@storybook/react') ? ['@storybook/react'] : []),
679
+ // The editor's own synthesized runtime modules' packages (`three`,
680
+ // `@react-three/fiber`, … — whatever those module bodies actually
681
+ // import, read from the bodies themselves). Same installed-only gate
682
+ // as the two conditionals above, for the same reason.
683
+ //
684
+ // `three` is dropped from this list when the shared-three redirect is
685
+ // active (`sharedThreeSpecifierUrl`): it moves to `exclude` below so
686
+ // Fiber's own `import 'three'` is externalized onto the shell's one
687
+ // copy, and a specifier cannot be both `include` and `exclude`. The
688
+ // three ADDONS stay included — they prebundle fine, and their internal
689
+ // `import 'three'` is the externalized one. When no shared-three chunk
690
+ // exists (old dist), three stays included/prebundled as before.
691
+ ...(sharedThreeSpecifierUrl
692
+ ? editorRuntimeIncludes.filter((specifier) => specifier !== 'three')
693
+ : editorRuntimeIncludes),
694
+ ],
695
+ // Installed engine entry points include TSX. Dependency optimization
696
+ // is a separate esbuild pipeline from Vite's top-level `esbuild`
697
+ // option, so it needs the same automatic runtime explicitly.
698
+ // `loader['.js'] = 'jsx'` is the SCANNER half of the JSX-in-`.js`
699
+ // acceptance (`projectJsxInJsPlugin` in the plugin list above is the
700
+ // transform half, and `project-validation.ts`'s `sourceLoader` the
701
+ // validate half — the plugin's header names all three and why fixing
702
+ // one alone leaves the others red). `dev.ts` has carried it since the
703
+ // transform landed; this host had neither, so a CRA-era ingested game
704
+ // under a registry install died at its first `.js` file.
705
+ esbuildOptions: { jsx: 'automatic', loader: { '.js': 'jsx' } },
706
+ // Spelled root-relative wherever the file is under the project, because
707
+ // Vite reads the whole list as ONE decision: one entry that parses as a
708
+ // glob pattern (a project at `~/my (game)`) sends every entry through
709
+ // `glob()`, whose `**/node_modules/**` ignore silently drops the
710
+ // engine-source crawl entries below. See `viteOptimizeDepsEntries`.
711
+ entries: viteOptimizeDepsEntries(projectPath, [
712
+ ...projectOptimizeDepsEntries,
713
+ ...runtimeSourceCrawlEntries,
714
+ ...packageContributionCrawl.entries,
715
+ ]),
716
+ // C3 hardening: belt to the entries-scoping suspenders. Entries-scoping
717
+ // ALREADY keeps the crawl out of `server/` for a well-formed project
718
+ // (so the colyseus server stack is never reached), but a project that
719
+ // VIOLATES D2 by value-importing `server/` room code FROM a world entry
720
+ // would drag `@colyseus/core` -> `@pm2/io` into the crawl and crash the
721
+ // esbuild prebundle step — which, unhandled in the async optimizer,
722
+ // hard-kills the whole packaged process (the only authoring surface).
723
+ // Excluding these SERVER-ONLY names stops esbuild crawling into them, so
724
+ // that pathological world degrades to a benign in-browser import failure
725
+ // instead of process death. Does NOT include `@colyseus/sdk` — the
726
+ // legitimate CLIENT SDK a real multiplayer project imports (D2, dynamic-
727
+ // imported) must still prebundle; it is a separate package and its path
728
+ // is untouched. See `project-optimize-deps-entries.ts`'s
729
+ // `SERVER_ONLY_PREBUNDLE_EXCLUDE` doc comment for the live proof.
730
+ exclude: [
731
+ ...SERVER_ONLY_PREBUNDLE_EXCLUDE,
732
+ // ONE three for the whole page. Excluding `three` externalizes it out
733
+ // of every prebundled chunk (notably `@react-three/fiber`'s), so those
734
+ // chunks' `import 'three'` is resolved at serve time by
735
+ // `sharedThreePlugin` and lands on the shell's single published copy
736
+ // instead of a second `.vite/deps/three.js`. Only when the shell
737
+ // actually published that chunk — otherwise three must stay prebundled
738
+ // (there is nothing to redirect to). See `../vite-plugin-shared-three.ts`.
739
+ ...(sharedThreeSpecifierUrl ? ['three'] : []),
740
+ // The editor's own widget kit, the API's fourth face. A PREBUNDLED
741
+ // package bakes its React import at prebundle time, where
742
+ // `sharedReactPlugin`'s source-level scope can never reach it — and
743
+ // this kit renders inside the EDITOR's React tree, so a prebundled
744
+ // copy is a second React and the crash box that plugin exists to
745
+ // remove. Source-served, its `react` import resolves through the
746
+ // plugin like any other editor-tree module. (It is listed with the
747
+ // SDK's other doors below for the same reason; the name is here
748
+ // because the kit is the one a PROJECT's own contributions import.)
749
+ '@volter/editor-sdk/widgets',
750
+ // The Blender engine spawns its worker as
751
+ // `new Worker(new URL('./worker.ts', import.meta.url))`. Prebundled,
752
+ // that URL is rewritten against a `.vite/deps` chunk that has no
753
+ // worker beside it, and the request 404s — measured 2026-09-20 from a
754
+ // REGISTRY install (invisible from a checkout, where the package is a
755
+ // symlink Vite serves as source): every Model document died with
756
+ // "Blender worker failed: the worker script did not load". Served as
757
+ // source, the URL resolves to the package's own file.
758
+ //
759
+ // THIS LIST IS THE KIT'S, AND A PRODUCT ADDS NOTHING TO IT (measured
760
+ // 2026-09-21, WORK.md step 3 P1). This instance is rooted at the
761
+ // PROJECT and prebundles what the PROJECT's graph reaches: a package
762
+ // the project DECLARES, whose contributions are served `/@fs/` from its
763
+ // own install — which is how `@volter/editor-blender` gets here, and why the
764
+ // name below is a fact about a project's dependency rather than about
765
+ // any composition. A package the PRODUCT composes is in the product's
766
+ // built bundle, which this instance never transforms, so no
767
+ // product-declared dependency needs an exclusion and there is no
768
+ // `vgai.product.optimizeDepsExclude` to declare one with.
769
+ '@volter/blender-engine',
770
+ '@volter/blender-engine/browser',
771
+ '@volter/editor-sdk/layouts',
772
+ '@volter/editor-sdk/layout-arrangements',
773
+ // The SDK's OTHER doors, same rule: each holds module state or calls
774
+ // React hooks, and a prebundled copy would carry the PROJECT's React
775
+ // into the editor's tree — measured 2026-09-17 as "Cannot read
776
+ // properties of null (reading 'useSyncExternalStore')" from a package
777
+ // contribution's status item, whose `@volter/editor-sdk/host` had been
778
+ // prebundled with `.vite/deps/react.js` inside it.
779
+ '@volter/editor-sdk/host',
780
+ '@volter/editor-sdk/contributions',
781
+ '@volter/editor-sdk/commands',
782
+ '@volter/editor-sdk/looks',
783
+ // Engine imports this project cannot resolve — the guard that lets the
784
+ // engine-source crawl entries above be safe. An unresolvable bare
785
+ // import makes Vite's discovery THROW ("imported but could not be
786
+ // resolved"), which rejects the boot-time `optimizeDeps()` barrier
787
+ // below and kills the process; excluding the name externalizes it in
788
+ // the scanner instead, degrading one optional engine feature in the
789
+ // browser rather than the whole editor. See
790
+ // `computeUnresolvableRuntimeImports`.
791
+ ...unresolvableRuntimeImports,
792
+ ...packageContributionCrawl.unresolvable,
793
+ // The project's runtime-package source is the document-side module
794
+ // graph. Keep every subpath source-served so an arbitrary project
795
+ // import cannot create a second prebundled WorldProvider identity.
796
+ // React/Fiber themselves remain prebundled and deduped above.
797
+ //
798
+ // The scanner matches the RAW specifier before any alias runs, so a
799
+ // package left out here is prebundlable — which is how eight runtime
800
+ // modules (`world3d-react`, `runtime/mount-game`, …) once sat in
801
+ // `.vite/deps` beside their source-served twins, each chunk carrying
802
+ // its own bundled `react/world-state` `GameContext`.
803
+ ...RUNTIME_PACKAGE_NAMES,
804
+ ],
805
+ },
806
+ resolve: {
807
+ // `configFile: false` deliberately ignores the project's Vite config,
808
+ // so reconstruct the template's renderer identity contract here. Fiber,
809
+ // Drei, the installed engine and the world must share these instances.
810
+ // The runtime packages are in the list for the same
811
+ // WorldProvider/`useGame` singleton the checkout-rooted `dev.ts` path
812
+ // gets from repo-root `vite.config.ts` (see that file's dedupe comment).
813
+ // The alias below already points each specifier at the project's own
814
+ // installed source when it resolves; dedupe is the remaining collapse
815
+ // for a nested copy that alias resolution still walks into.
816
+ dedupe: [
817
+ '@volter/editor-sdk',
818
+ ...RUNTIME_PACKAGE_NAMES,
819
+ 'react',
820
+ 'react-dom',
821
+ 'three',
822
+ '@react-three/fiber',
823
+ 'pixi.js',
824
+ '@pixi/react',
825
+ ],
826
+ alias: [
827
+ // The editor's own deep specifiers a project contribution still
828
+ // names — `@editor/game-module-access`, and the R3F analyzer a
829
+ // project's `check-idioms` loads (the scaffolder writes the matching
830
+ // tsconfig path). The widget kit is NOT among them any more: it is
831
+ // `@volter/editor-sdk/widgets`, an ordinary package export. In checkout
832
+ // mode the root Vite config supplies this alias; this project-rooted
833
+ // packaged Vite instance has no configFile, so it must reconstruct
834
+ // the runtime half from its own package root.
835
+ { find: /^@editor\//, replacement: `${path.join(editorPackageRoot, 'src')}/` },
836
+ // Each runtime package resolves to THIS project's installed copy.
837
+ // Keeping one source root per package is what preserves React/Fiber
838
+ // singleton identity in Play mode.
839
+ ...[...runtimeSources].map(([name, src]) => ({
840
+ find: new RegExp(`^${name.replace('/', '\\/')}(?=\\/|$)`),
841
+ replacement: src,
842
+ })),
843
+ ],
844
+ },
845
+ server: {
846
+ middlewareMode: true,
847
+ // `overlay: false` for the same product reason as `dev.ts`'s instance:
848
+ // this server's project modules load into the SAME page as the packaged
849
+ // editor shell, so Vite's full-screen error overlay would cover the
850
+ // editor whenever the game fails to compile. `overlay` is the CLIENT
851
+ // overlay only — HMR itself (and therefore `vgai-script-hmr`'s WebSocket
852
+ // delivery, which the header note above says `hmr: false` would kill)
853
+ // stays fully on.
854
+ hmr: { port: HMR_PORT, clientPort: HMR_CLIENT_PORT, overlay: false },
855
+ // OFF because the shared-React doorway makes it structurally broken in
856
+ // THIS instance — see `vite-plugin-shared-react.ts`. Every editor-tree
857
+ // module's `react`/`react/jsx-runtime` import is resolved to the prebuilt
858
+ // shell's own chunk URL (`/assets/vgai-shared-*.js`), which the BROWSER
859
+ // fetches from `express.static(dist)` below and this project-rooted Vite
860
+ // deliberately cannot resolve. Import analysis then hands each of those
861
+ // URLs to `warmupRequest`, which resolves them against the project root,
862
+ // fails, and logs a formatted `Pre-transform error` — measured on a fresh
863
+ // scaffold: 64 of them from ONE `src/contributions/*.inspector.tsx` request,
864
+ // because nothing caches a failed warmup and each import site retries it.
865
+ //
866
+ // The plugin already opts out of Vite's dependency SCAN for exactly this
867
+ // reason (`options.scan` — the scanner recorded the URL as a file to
868
+ // prebundle and died on ENOENT). Pre-transform is the same class of
869
+ // server-side pass following a browser-only redirect, but Vite exposes no
870
+ // per-request opt-out, and the alternative — teaching this Vite to LOAD
871
+ // those URLs from `dist/` — is worse than the noise: each doorway chunk
872
+ // imports the shell's own `./index-*.js`, so satisfying one warmup drags
873
+ // the entire prebuilt SPA into the project's dev module graph, which is
874
+ // the exact separation this file's two-graph design exists to keep.
875
+ //
876
+ // What is given up is latency-only: known static imports are transformed
877
+ // when the browser asks instead of just before. The editor reaches the
878
+ // project through DYNAMIC imports (tool contributions, world entries,
879
+ // stories), which pre-transform skips anyway, and this instance declares
880
+ // no `server.warmup` list (which the same flag would have disabled).
881
+ preTransformRequests: false,
882
+ // Watch source; never watch output — the same list as `dev.ts`'s
883
+ // instance, for the same product reason: a build (`npm run build`,
884
+ // `vercel build`) rewriting output HTML inside the project root reads
885
+ // as a source change and full-page-reloads the editor tab out from
886
+ // under whatever it was doing.
887
+ watch: {
888
+ ignored: [
889
+ // Scratch HTML/tsconfig writes must not reload the active editor.
890
+ (filename: string) => isProjectScratchPath(filename, projectRoots),
891
+ '**/.claude/worktrees/**',
892
+ '**/.vercel/**',
893
+ '**/dist/**',
894
+ '**/dist-ssr/**',
895
+ '**/dist-wip/**',
896
+ '**/logs/**',
897
+ ],
898
+ },
899
+ fs: {
900
+ allow: [
901
+ ...projectFsRoots,
902
+ // The `@editor/*` deep specifiers above are source-served, so this
903
+ // package's own `src/` must be readable even when it does not sit
904
+ // under the project's own resolved `node_modules`.
905
+ editorPackageRoot,
906
+ ],
907
+ },
908
+ },
909
+ };
910
+
911
+ // This is a custom, prebuilt SPA host: unlike Vite's normal HTML-serving
912
+ // path, nothing asks the project-rooted middleware server for an HTML entry
913
+ // before the editor begins importing its virtual runtime modules. Finish the
914
+ // entries-scoped optimizer pass first so every project module sees one
915
+ // browser hash from the first request. Without this barrier, the synthetic
916
+ // React/R3F runtime can load against the provisional hash while the world
917
+ // entry loads against the scan's committed hash, producing two React/Fiber
918
+ // graphs and an invalid-hook-call failure on a clean packaged install.
919
+ const resolvedViteConfig = await resolveConfig(viteInlineConfig, 'serve');
920
+ await optimizeDeps(resolvedViteConfig);
921
+ const vite = await createViteServer(resolvedViteConfig);
922
+
923
+ // 2. Mount editor API routes (/__editor/*). `engineRoot` identifies the
924
+ // running packaged editor for runtime capability checks.
925
+ const editorRouter = createEditorServer({
926
+ engineRoot: editorPackageRoot,
927
+ projectPath,
928
+ collaborationPort: PORT,
929
+ // This host DOES serve the `/__ui-source/*` recorder (`uiOidPlugin()` is
930
+ // registered above), and saying so is what makes design-time edits write
931
+ // the game's own TSX under a registry install. Read off the resolved
932
+ // plugin list rather than hardcoded `true`, so removing the plugin can
933
+ // never leave a flag claiming a route that is gone.
934
+ sourceWriteRoutes: servesUiSourceRoutes(vite.config.plugins),
935
+ // And the ingest lane's route, off the same resolved list — this host
936
+ // registers `creationSiteWritePlugin()` above, and saying so is what lets an
937
+ // ingest edit be written into the game's own source under a registry
938
+ // install instead of reporting the live-only floor forever.
939
+ ingestSourceWriteRoutes: servesIngestSourceRoutes(vite.config.plugins),
940
+ // Tab bijection — same session contract as dev.ts: one blessed browser
941
+ // tab, self-healed and closed with the session; VGAI_NO_OPEN (the CLI's
942
+ // --no-open) disables the whole loop for headless sessions.
943
+ tabBijection: {
944
+ enabled: !process.env['VGAI_NO_OPEN'],
945
+ // Same contract as dev.ts: under the frame this session's ONE tab is the
946
+ // workbench page, and the url is arithmetic over the reserved proxy port.
947
+ editorUrl: frameLaunch ? frameWorkbenchUrl(frameLaunch, projectPath) : `${EDITOR_ORIGIN}/`,
948
+ },
949
+ // The session's children are the session's to report (`vgai status`).
950
+ workbench: () => frameWorkbench?.identity ?? null,
951
+ // The serving door (routes/served-modules.ts), same contract as dev.ts:
952
+ // where the Code-OSS frame finds the editor. Registered on BOTH bundler
953
+ // hosts deliberately — a capability that exists on one and not the other is
954
+ // the drift `project-serving-plugins.ts`'s header was written about. Here
955
+ // the answer is the PRODUCTION BUILD's frame entry (WORK.md §U3), reached
956
+ // through the wrapper below rather than directly, because the built entry's
957
+ // stylesheet has no HTML of ours to be injected into.
958
+ frameBridgeUrl: () => {
959
+ readBuiltProductEntry(distPath, sessionProductIdentity);
960
+ return FRAME_BRIDGE_PACKAGED_PATH;
961
+ },
962
+ // The session's children are its to report: which product it is serving,
963
+ // beside which workbench it is running.
964
+ product: () => ({
965
+ id: sessionProductIdentity.name,
966
+ dir: sessionProductIdentity.dir,
967
+ version: sessionProductIdentity.version,
968
+ }),
969
+ loadProjectModule: freshProjectModuleLoader(vite, () => projectPath),
970
+ // Same contract as dev.ts: a dependency installed under a live session
971
+ // (`vgai add <capability>`) is invisible until the SSR module graph, which
972
+ // caches even a REJECTED module load, is dropped.
973
+ invalidateProjectModules() {
974
+ vite.environments.ssr.moduleGraph.invalidateAll();
975
+ console.log('[vgai-editor] project dependencies changed; reloaded project modules.');
976
+ },
977
+ onProjectOpened(newProjectPath: string) {
978
+ const allowed = vite.config.server.fs.allow;
979
+ // The newly opened project's OWN install roots, not just its folder —
980
+ // a project switched into at runtime has exactly the hoisting/symlink
981
+ // shapes the boot-time set above exists for.
982
+ for (const root of projectServingRoots(newProjectPath)) {
983
+ if (!allowed.includes(root)) allowed.push(root);
984
+ }
985
+ projectRoots.add(newProjectPath);
986
+ vite.watcher.add(newProjectPath);
987
+ currentProjectRoot = newProjectPath;
988
+ // Session parity with dev.ts (D12): keep the registry pointing
989
+ // at the CURRENT project so `vgai edit`/`sessions`/cwd-resolution see
990
+ // switches, not boot state — same call, same shape.
991
+ registerSession({
992
+ project: newProjectPath,
993
+ port: PORT,
994
+ pid: process.pid,
995
+ startedAt: sessionStartedAt,
996
+ });
997
+ },
998
+ });
999
+ app.use(editorRouter);
1000
+
1001
+ // 3. The project-rooted Vite instance — handles `/@fs/`, `/@id/`, and any
1002
+ // request matching a real file under the project root (module
1003
+ // transform + bare-specifier resolution through the PROJECT's own
1004
+ // node_modules). `appType: 'custom'` means it calls `next()` instead of
1005
+ // synthesizing an HTML/404 response for anything else, so requests for
1006
+ // the editor's OWN app (`/`, `/assets/*`) fall through to step 4.
1007
+ app.use(vite.middlewares);
1008
+
1009
+ // 3b. The Code-OSS frame's entry into this build (routes/served-modules.ts
1010
+ // answers this url). Generated per request rather than emitted at build
1011
+ // time, because both halves it needs — the hashed chunk and its
1012
+ // stylesheets — are facts about the `dist/` this process is serving.
1013
+ app.get(FRAME_BRIDGE_PACKAGED_PATH, (_req, res) => {
1014
+ let body: string;
1015
+ try {
1016
+ body = builtFrameBridgeModule(readBuiltProductEntry(distPath, sessionProductIdentity));
1017
+ } catch (error) {
1018
+ // The door already reports this sentence as a refusal; a direct fetch of
1019
+ // this url gets it too rather than an empty 200 that imports nothing.
1020
+ res
1021
+ .status(503)
1022
+ .type('text/plain')
1023
+ .send(error instanceof Error ? error.message : String(error));
1024
+ return;
1025
+ }
1026
+ res.type('text/javascript').set('Cache-Control', 'no-store').send(body);
1027
+ });
1028
+
1029
+ // 4. Serve the PRODUCT's prebuilt chunks (static — no dev-mode transform of
1030
+ // editor or product source; see this file's header doc comment). The
1031
+ // module served at step 3b re-exports one of these by its hashed name.
1032
+ app.use(express.static(distPath));
1033
+
1034
+ // 5. Start Express
1035
+ const sessionStartedAt = new Date().toISOString();
1036
+ const server = app.listen(PORT, HOST, () => {
1037
+ void (async () => {
1038
+ console.log(
1039
+ `\n \x1b[32mEditor server\x1b[0m (packaged) running at \x1b[36m${EDITOR_ORIGIN}\x1b[0m`,
1040
+ );
1041
+ console.log(` \x1b[33mProject\x1b[0m: ${projectPath}`);
1042
+ console.log(` \x1b[33mEditor\x1b[0m: \x1b[36m${EDITOR_ORIGIN}/\x1b[0m\n`);
1043
+ if (idleShutdown.armed) {
1044
+ console.log(
1045
+ ` Idle shutdown after ${formatIdleWindow(resolveIdleShutdownMs())} with no connected ` +
1046
+ `editor tab and no request (${IDLE_SHUTDOWN_MINUTES_ENV}=0 disables)\n`,
1047
+ );
1048
+ }
1049
+ // D12: announce this session so `vgai edit`/`sessions`/`play`
1050
+ // (cwd-resolution) see the packaged runtime exactly like dev.ts's.
1051
+ registerSession({
1052
+ project: projectPath,
1053
+ port: PORT,
1054
+ pid: process.pid,
1055
+ startedAt: sessionStartedAt,
1056
+ });
1057
+ // The workbench, last and for the same reason dev.ts starts it last: it
1058
+ // is the slowest thing this boot does, and a frame that cannot start is a
1059
+ // `vgai edit` that has failed (frame-workbench.ts states the ownership).
1060
+ if (frameLaunch) {
1061
+ // THE AGENT'S RUNTIME COMES FIRST — same reason as dev.ts: an extension
1062
+ // host inherits its server's environment and that environment is fixed
1063
+ // at spawn (frame-workbench.ts's `env`, frontend-handoff.ts's header).
1064
+ const handoff = await editorRouter.frontendHandoff();
1065
+ if (handoff.refusal) console.log(` \x1b[33mAgent\x1b[0m: ${handoff.refusal}`);
1066
+ try {
1067
+ frameWorkbench = await startFrameWorkbench({
1068
+ launch: frameLaunch,
1069
+ projectRoot: projectPath,
1070
+ sessionPort: PORT,
1071
+ env: handoff.env,
1072
+ log: (line) => console.log(` ${line}`),
1073
+ });
1074
+ console.log(` \x1b[33mWorkbench\x1b[0m: \x1b[36m${frameWorkbench.url}\x1b[0m\n`);
1075
+ } catch (error) {
1076
+ // biome-ignore lint/suspicious/noConsole: the frame's refusal IS the product here — this terminal is the only surface that exists yet.
1077
+ console.error(
1078
+ `\n \x1b[31m${error instanceof Error ? error.message : String(error)}\x1b[0m\n`,
1079
+ );
1080
+ process.exit(1);
1081
+ }
1082
+ }
1083
+ })();
1084
+ });
1085
+
1086
+ // The tab's duplex control socket rides this same HTTP server — an
1087
+ // `upgrade` for `/__editor/events`, routed by pathname so every other
1088
+ // upgrade (Vite's HMR socket) passes through untouched.
1089
+ editorRouter.attachControlSocket(server);
1090
+
1091
+ server.on('error', (err: NodeJS.ErrnoException) => {
1092
+ console.error(`\n \x1b[31m${friendlyListenError(err, PORT, HOST, 'PORT')}\x1b[0m\n`);
1093
+ process.exit(1);
1094
+ });
1095
+
1096
+ // Self-healing registration — same rationale and cadence as dev.ts: the
1097
+ // registry's unlocked read-modify-write can lose this entry to a concurrent
1098
+ // writer's interleaved write, leaving a healthy server invisible to
1099
+ // `vgai edit`/`sessions`/`close`; re-asserting once a minute heals it.
1100
+ const reregisterTimer = setInterval(() => {
1101
+ registerSession({
1102
+ project: currentProjectRoot,
1103
+ port: PORT,
1104
+ pid: process.pid,
1105
+ startedAt: sessionStartedAt,
1106
+ });
1107
+ }, 60_000);
1108
+ reregisterTimer.unref();
1109
+
1110
+ // Best-effort, and nothing but the HTTP server waits on it — see dev.ts's
1111
+ // note at its own `tabNotice` for the measurement (a `vgai close` under load
1112
+ // died inside this notify, the shutdown list never ran, and the Code-OSS
1113
+ // server outlived the session on its reserved port).
1114
+ let tabNotice: Promise<void> | null = null;
1115
+ const shutdown = createProcessShutdown({
1116
+ journal: (record) => editorRouter.journalShutdownTask(record),
1117
+ tasks: [
1118
+ { name: 'tab-close notice', run: () => tabNotice ?? undefined },
1119
+ {
1120
+ name: 'http server',
1121
+ run: async () => {
1122
+ // The `tab-close` write needs a live SSE socket, and closing the
1123
+ // server takes those sockets down: this ORDER is the only dependency
1124
+ // on the notice anywhere in the list.
1125
+ await tabNotice;
1126
+ await closeHttpServer(server);
1127
+ },
1128
+ },
1129
+ // THE ONE TEARDOWN for the REH and the proxy (frame-workbench.ts's
1130
+ // ownership note) — the path `vgai close`, SIGTERM and the idle timer
1131
+ // all take. The detached CHILD waits on nothing; the proxy is the tab's
1132
+ // route and waits with the HTTP server.
1133
+ { name: 'Code-OSS server', run: () => frameWorkbench?.stopServer() },
1134
+ {
1135
+ name: 'one-origin proxy',
1136
+ run: async () => {
1137
+ await tabNotice;
1138
+ await frameWorkbench?.closeProxy();
1139
+ },
1140
+ },
1141
+ {
1142
+ name: 'editor router',
1143
+ run: async () => {
1144
+ // The other half of the notice's dependency: `close()` takes the
1145
+ // control plane down and the `tab-close` push rides it. See dev.ts's
1146
+ // `tabNotice` for the measurement (acked ~26ms ordered, unacked at
1147
+ // the full 2s budget when it races).
1148
+ await tabNotice;
1149
+ await editorRouter.close();
1150
+ },
1151
+ },
1152
+ { name: 'vite', run: () => vite.close() },
1153
+ ],
1154
+ });
1155
+ let shutdownStarted = false;
1156
+ const unregisterAndShutdown = (signal: string) => {
1157
+ // A repeat signal is not a new instruction — see dev.ts's note at its own
1158
+ // handler registration for the measurement behind this latch.
1159
+ if (shutdownStarted) return;
1160
+ shutdownStarted = true;
1161
+ idleShutdown.stop();
1162
+ clearInterval(reregisterTimer);
1163
+ unregisterSession(process.pid);
1164
+ // Tab bijection: session-end notice to the blessed tab.
1165
+ // No-op after /__editor/tab/expect-restart.
1166
+ tabNotice = editorRouter.notifyTabSessionEnded().catch((error) => {
1167
+ console.error(
1168
+ `[vgai-editor] tab-close notice failed: ${error instanceof Error ? error.message : error}`,
1169
+ );
1170
+ });
1171
+ void shutdown(signal);
1172
+ };
1173
+ // The idle timer's ONE exit: the same function a signal takes, plus the
1174
+ // honest line naming why the session ended (dev.ts prints this from its
1175
+ // shared `lastGasp`; this host has no such line, so the idle path says it).
1176
+ onIdleShutdown = (why) => {
1177
+ console.error(`[vgai-editor] shutting down (${why}) — pid ${process.pid}, port ${PORT}`);
1178
+ unregisterAndShutdown(why);
1179
+ };
1180
+ process.on('SIGINT', () => unregisterAndShutdown('SIGINT'));
1181
+ process.on('SIGTERM', () => unregisterAndShutdown('SIGTERM'));
1182
+ process.once('exit', () => {
1183
+ idleShutdown.stop();
1184
+ clearInterval(reregisterTimer);
1185
+ unregisterSession(process.pid);
1186
+ });
1187
+ }
1188
+
1189
+ main().catch((err) => {
1190
+ console.error('Failed to start packaged editor server:', err);
1191
+ process.exit(1);
1192
+ });