@vgai/editor 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (519) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +35 -0
  3. package/dist/a/b/c/test-nested.json +1 -0
  4. package/dist/assets/CCDIKSolver-Dz-ysxZF.js +1 -0
  5. package/dist/assets/SelectionBox-BXsv_6aH.js +1 -0
  6. package/dist/assets/browser-project-scripts-DOru4L1x.js +2 -0
  7. package/dist/assets/browserAll-CEkWYlFk.js +1 -0
  8. package/dist/assets/collision-world-DniMB0qq.glb +0 -0
  9. package/dist/assets/create-runtime-DYIWr3va.js +1 -0
  10. package/dist/assets/data-asset-Bb9sIM7i.js +5 -0
  11. package/dist/assets/esbuild-BHljloGq.wasm +0 -0
  12. package/dist/assets/fake-three-BMTQ9rl_.js +1 -0
  13. package/dist/assets/game-8SDZPPgH.js +1 -0
  14. package/dist/assets/game-BI111o5f.js +10 -0
  15. package/dist/assets/game-CevorxyV.js +1 -0
  16. package/dist/assets/game-Di1H0gFj.js +1 -0
  17. package/dist/assets/game-Di7XlywD.js +1 -0
  18. package/dist/assets/game-DjnCaw8R.js +1 -0
  19. package/dist/assets/game-Q9A0aXSV.js +1 -0
  20. package/dist/assets/game-pt_1lQLv.js +1 -0
  21. package/dist/assets/game-source-CvidN7AB.js +1 -0
  22. package/dist/assets/game-xsOho_wg.js +83 -0
  23. package/dist/assets/index-Bp5cecD6.js +12 -0
  24. package/dist/assets/index-CPVV6pjw.js +1 -0
  25. package/dist/assets/index-CpOnYyeD.js +213 -0
  26. package/dist/assets/index-Cs8xwv3e.js +1 -0
  27. package/dist/assets/index-DL7RcQ0n.js +27 -0
  28. package/dist/assets/index-Dr8H_6Rl.js +1 -0
  29. package/dist/assets/index-_bCaipUk.js +13650 -0
  30. package/dist/assets/ingest-siblings-Dz8eEqrV.js +1 -0
  31. package/dist/assets/mount-manifest-Bhk0ohsl.js +1 -0
  32. package/dist/assets/recast-navigation.wasm-compat-CvP4UGK6.js +1 -0
  33. package/dist/assets/scene-query-BivAHhJu.js +1 -0
  34. package/dist/assets/stats.module--VATS4Kh.js +1 -0
  35. package/dist/assets/webworkerAll-BLrH0F6F.js +1 -0
  36. package/dist/assets/xstate-animation-binding-1NGLmw6y.js +1 -0
  37. package/dist/assets/xstate.esm-BgUD33nl.js +3 -0
  38. package/dist/index.html +37 -0
  39. package/dist/ingest/racing-game/UPSTREAM.md +68 -0
  40. package/dist/ingest/racing-game/a/Inter-Black.woff +0 -0
  41. package/dist/ingest/racing-game/a/Inter-Black.woff2 +0 -0
  42. package/dist/ingest/racing-game/a/Inter-BlackItalic.woff +0 -0
  43. package/dist/ingest/racing-game/a/Inter-BlackItalic.woff2 +0 -0
  44. package/dist/ingest/racing-game/a/Inter-Bold.woff +0 -0
  45. package/dist/ingest/racing-game/a/Inter-Bold.woff2 +0 -0
  46. package/dist/ingest/racing-game/a/Inter-BoldItalic.woff +0 -0
  47. package/dist/ingest/racing-game/a/Inter-BoldItalic.woff2 +0 -0
  48. package/dist/ingest/racing-game/a/Inter-ExtraBold.woff +0 -0
  49. package/dist/ingest/racing-game/a/Inter-ExtraBold.woff2 +0 -0
  50. package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff +0 -0
  51. package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff2 +0 -0
  52. package/dist/ingest/racing-game/a/Inter-ExtraLight.woff +0 -0
  53. package/dist/ingest/racing-game/a/Inter-ExtraLight.woff2 +0 -0
  54. package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff +0 -0
  55. package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff2 +0 -0
  56. package/dist/ingest/racing-game/a/Inter-Italic.woff +0 -0
  57. package/dist/ingest/racing-game/a/Inter-Italic.woff2 +0 -0
  58. package/dist/ingest/racing-game/a/Inter-Light.woff +0 -0
  59. package/dist/ingest/racing-game/a/Inter-Light.woff2 +0 -0
  60. package/dist/ingest/racing-game/a/Inter-LightItalic.woff +0 -0
  61. package/dist/ingest/racing-game/a/Inter-LightItalic.woff2 +0 -0
  62. package/dist/ingest/racing-game/a/Inter-Medium.woff +0 -0
  63. package/dist/ingest/racing-game/a/Inter-Medium.woff2 +0 -0
  64. package/dist/ingest/racing-game/a/Inter-MediumItalic.woff +0 -0
  65. package/dist/ingest/racing-game/a/Inter-MediumItalic.woff2 +0 -0
  66. package/dist/ingest/racing-game/a/Inter-Regular.woff +0 -0
  67. package/dist/ingest/racing-game/a/Inter-Regular.woff2 +0 -0
  68. package/dist/ingest/racing-game/a/Inter-SemiBold.woff +0 -0
  69. package/dist/ingest/racing-game/a/Inter-SemiBold.woff2 +0 -0
  70. package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff +0 -0
  71. package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff2 +0 -0
  72. package/dist/ingest/racing-game/a/Inter-Thin.woff +0 -0
  73. package/dist/ingest/racing-game/a/Inter-Thin.woff2 +0 -0
  74. package/dist/ingest/racing-game/a/Inter-ThinItalic.woff +0 -0
  75. package/dist/ingest/racing-game/a/Inter-ThinItalic.woff2 +0 -0
  76. package/dist/ingest/racing-game/a/Inter-italic.var.woff2 +0 -0
  77. package/dist/ingest/racing-game/a/Inter-roman.var.woff2 +0 -0
  78. package/dist/ingest/racing-game/a/Inter.var.woff2 +0 -0
  79. package/dist/ingest/racing-game/a/index.css +1 -0
  80. package/dist/ingest/racing-game/images/bronze.png +0 -0
  81. package/dist/ingest/racing-game/images/gold.png +0 -0
  82. package/dist/ingest/racing-game/images/silver.png +0 -0
  83. package/dist/ingest/racing-game/index.html +15 -0
  84. package/dist/ingest/racing-game/models/chassis-draco.glb +0 -0
  85. package/dist/ingest/racing-game/models/track-draco.glb +0 -0
  86. package/dist/ingest/racing-game/models/wheel-draco.glb +0 -0
  87. package/dist/ingest/racing-game/rg-entry.js +1 -0
  88. package/dist/ingest/racing-game/sounds/accelerate.mp3 +0 -0
  89. package/dist/ingest/racing-game/sounds/boost.mp3 +0 -0
  90. package/dist/ingest/racing-game/sounds/crash.mp3 +0 -0
  91. package/dist/ingest/racing-game/sounds/engine.mp3 +0 -0
  92. package/dist/ingest/racing-game/sounds/honk.mp3 +0 -0
  93. package/dist/ingest/racing-game/sounds/tire-brake.mp3 +0 -0
  94. package/dist/ingest/racing-game/sounds/train.mp3 +0 -0
  95. package/dist/ingest/racing-game/sounds/water.mp3 +0 -0
  96. package/dist/ingest/racing-game/textures/cursor.svg +3 -0
  97. package/dist/ingest/racing-game/textures/dikhololo_night_1k.hdr +0 -0
  98. package/dist/ingest/racing-game/textures/heightmap_1024.png +0 -0
  99. package/dist/ingest/racing-game/textures/mask.svg +11 -0
  100. package/dist/ingest/racing-game/textures/no_sound.png +0 -0
  101. package/dist/ingest/racing-game/three-r139.module.js +50617 -0
  102. package/dist/ingest/racing-game/vendor.js +179 -0
  103. package/dist/ingest/racing-game/vgai.game.json +20 -0
  104. package/dist/ingest/simcity/UPSTREAM.md +65 -0
  105. package/dist/ingest/simcity/favicon.ico +0 -0
  106. package/dist/ingest/simcity/fonts/Bebas-Regular.otf +0 -0
  107. package/dist/ingest/simcity/fonts/Digitalt.otf +0 -0
  108. package/dist/ingest/simcity/fonts/Stanberry.ttf +0 -0
  109. package/dist/ingest/simcity/icons/bulldozer-color.png +0 -0
  110. package/dist/ingest/simcity/icons/bulldozer.png +0 -0
  111. package/dist/ingest/simcity/icons/calendar.png +0 -0
  112. package/dist/ingest/simcity/icons/factory-color.png +0 -0
  113. package/dist/ingest/simcity/icons/factory.png +0 -0
  114. package/dist/ingest/simcity/icons/house-color.png +0 -0
  115. package/dist/ingest/simcity/icons/house.png +0 -0
  116. package/dist/ingest/simcity/icons/job.png +0 -0
  117. package/dist/ingest/simcity/icons/office.png +0 -0
  118. package/dist/ingest/simcity/icons/pause-color.png +0 -0
  119. package/dist/ingest/simcity/icons/pause.png +0 -0
  120. package/dist/ingest/simcity/icons/person.png +0 -0
  121. package/dist/ingest/simcity/icons/play-color.png +0 -0
  122. package/dist/ingest/simcity/icons/play.png +0 -0
  123. package/dist/ingest/simcity/icons/power-color.png +0 -0
  124. package/dist/ingest/simcity/icons/power-line-color.png +0 -0
  125. package/dist/ingest/simcity/icons/power.png +0 -0
  126. package/dist/ingest/simcity/icons/road-color.png +0 -0
  127. package/dist/ingest/simcity/icons/road.png +0 -0
  128. package/dist/ingest/simcity/icons/select-color.png +0 -0
  129. package/dist/ingest/simcity/icons/select.png +0 -0
  130. package/dist/ingest/simcity/icons/store-color.png +0 -0
  131. package/dist/ingest/simcity/index.html +94 -0
  132. package/dist/ingest/simcity/index.js +58 -0
  133. package/dist/ingest/simcity/main.css +233 -0
  134. package/dist/ingest/simcity/models/armored-truck.glb +0 -0
  135. package/dist/ingest/simcity/models/building-block-4floor-corner.glb +0 -0
  136. package/dist/ingest/simcity/models/building-block-4floor-short.glb +0 -0
  137. package/dist/ingest/simcity/models/building-block-5floor.glb +0 -0
  138. package/dist/ingest/simcity/models/building-burger-joint.glb +0 -0
  139. package/dist/ingest/simcity/models/building-cafe.glb +0 -0
  140. package/dist/ingest/simcity/models/building-casino.glb +0 -0
  141. package/dist/ingest/simcity/models/building-cinema.glb +0 -0
  142. package/dist/ingest/simcity/models/building-house-block-big.glb +0 -0
  143. package/dist/ingest/simcity/models/building-house-family-large.glb +0 -0
  144. package/dist/ingest/simcity/models/building-house-family-small.glb +0 -0
  145. package/dist/ingest/simcity/models/building-office-balcony.glb +0 -0
  146. package/dist/ingest/simcity/models/building-office-big.glb +0 -0
  147. package/dist/ingest/simcity/models/building-office-pyramid.glb +0 -0
  148. package/dist/ingest/simcity/models/building-office-tall.glb +0 -0
  149. package/dist/ingest/simcity/models/building-office.glb +0 -0
  150. package/dist/ingest/simcity/models/building-restaurant.glb +0 -0
  151. package/dist/ingest/simcity/models/building-skyscraper.glb +0 -0
  152. package/dist/ingest/simcity/models/car-ambulance-pickup.glb +0 -0
  153. package/dist/ingest/simcity/models/car-baywatch.glb +0 -0
  154. package/dist/ingest/simcity/models/car-hippie-van.glb +0 -0
  155. package/dist/ingest/simcity/models/car-passenger-race.glb +0 -0
  156. package/dist/ingest/simcity/models/car-passenger.glb +0 -0
  157. package/dist/ingest/simcity/models/car-police.glb +0 -0
  158. package/dist/ingest/simcity/models/car-taxi.glb +0 -0
  159. package/dist/ingest/simcity/models/car-tow-truck.glb +0 -0
  160. package/dist/ingest/simcity/models/car-truck-dump.glb +0 -0
  161. package/dist/ingest/simcity/models/car-veteran.glb +0 -0
  162. package/dist/ingest/simcity/models/construction-small.glb +0 -0
  163. package/dist/ingest/simcity/models/data-center.glb +0 -0
  164. package/dist/ingest/simcity/models/industry-factory-old.glb +0 -0
  165. package/dist/ingest/simcity/models/industry-factory.glb +0 -0
  166. package/dist/ingest/simcity/models/industry-refinery.glb +0 -0
  167. package/dist/ingest/simcity/models/industry-warehouse.glb +0 -0
  168. package/dist/ingest/simcity/models/power_line_pole_modified.glb +0 -0
  169. package/dist/ingest/simcity/models/tile-plain_grass.glb +0 -0
  170. package/dist/ingest/simcity/models/tile-road-curve.glb +0 -0
  171. package/dist/ingest/simcity/models/tile-road-end.glb +0 -0
  172. package/dist/ingest/simcity/models/tile-road-intersection-t.glb +0 -0
  173. package/dist/ingest/simcity/models/tile-road-intersection.glb +0 -0
  174. package/dist/ingest/simcity/models/tile-road-straight.glb +0 -0
  175. package/dist/ingest/simcity/models/truck.glb +0 -0
  176. package/dist/ingest/simcity/statusIcons/no-power.png +0 -0
  177. package/dist/ingest/simcity/statusIcons/no-road-access.png +0 -0
  178. package/dist/ingest/simcity/textures/base.png +0 -0
  179. package/dist/ingest/simcity/textures/grid.png +0 -0
  180. package/dist/ingest/simcity/textures/specular.png +0 -0
  181. package/dist/ingest/simcity/three-r155.module.js +51466 -0
  182. package/dist/ingest/simcity/vgai.game.json +20 -0
  183. package/dist/ingest/tanks/UPSTREAM.md +69 -0
  184. package/dist/ingest/tanks/index.html +214 -0
  185. package/dist/ingest/tanks/models/pixelTank.fbx +0 -0
  186. package/dist/ingest/tanks/tanks-entry.js +92 -0
  187. package/dist/ingest/tanks/three-r170.module.js +54571 -0
  188. package/dist/ingest/tanks/vgai.game.json +20 -0
  189. package/dist/inputmaps/default.inputmap.json +41 -0
  190. package/dist/jsm/libs/draco/gltf/draco_decoder.js +33 -0
  191. package/dist/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
  192. package/dist/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
  193. package/dist/scenes/untitled.vscn.json +29 -0
  194. package/dist/textures/waternormals.jpg +0 -0
  195. package/dist-server/packaged.mjs +18155 -0
  196. package/package.json +36 -0
  197. package/server/asset-library-routes.ts +833 -0
  198. package/server/canonical-path.ts +50 -0
  199. package/server/cloud-asset-catalog.ts +101 -0
  200. package/server/dev.ts +350 -0
  201. package/server/editor-server.ts +1537 -0
  202. package/server/editor-sse.ts +73 -0
  203. package/server/game-globals-shadow.ts +58 -0
  204. package/server/local-asset-catalog.ts +163 -0
  205. package/server/packaged.ts +571 -0
  206. package/server/prod.ts +75 -0
  207. package/server/project-optimize-deps-entries.ts +149 -0
  208. package/server/project-view.ts +65 -0
  209. package/server/server-utils.ts +430 -0
  210. package/server/session-registry.ts +93 -0
  211. package/src/EditorContext.tsx +87 -0
  212. package/src/achieved-tier.ts +126 -0
  213. package/src/action-registry.ts +268 -0
  214. package/src/adapter-resolver.ts +1858 -0
  215. package/src/asset-events.ts +100 -0
  216. package/src/authoring/active-adapter.ts +98 -0
  217. package/src/authoring/active-systems.ts +50 -0
  218. package/src/authoring/boundary-authoring-adapter.ts +178 -0
  219. package/src/authoring/composite-authoring-adapter.ts +891 -0
  220. package/src/authoring/design-time-layers.ts +776 -0
  221. package/src/authoring/edit-mode-authoring.ts +643 -0
  222. package/src/authoring/embed-only-adapter.ts +57 -0
  223. package/src/authoring/ephemeral-persistence.ts +26 -0
  224. package/src/authoring/eyedropper-session.ts +69 -0
  225. package/src/authoring/first-party-hierarchy-menu.ts +74 -0
  226. package/src/authoring/first-party-inspector/AddSectionMenu.tsx +118 -0
  227. package/src/authoring/first-party-inspector/EnvironmentSection.tsx +550 -0
  228. package/src/authoring/first-party-inspector/HeaderSection.tsx +329 -0
  229. package/src/authoring/first-party-inspector/is-first-party-selection.ts +44 -0
  230. package/src/authoring/first-party-inspector/register.tsx +108 -0
  231. package/src/authoring/ingest-authoring-adapter.ts +475 -0
  232. package/src/authoring/ingest-capture.ts +94 -0
  233. package/src/authoring/ingest-game-adapter.ts +291 -0
  234. package/src/authoring/ingest-iframe-adapter.ts +38 -0
  235. package/src/authoring/ingest-iframe-reachable-adapter.ts +401 -0
  236. package/src/authoring/ingest-overlay.ts +193 -0
  237. package/src/authoring/layered-pick.ts +87 -0
  238. package/src/authoring/mount-failure-report.ts +112 -0
  239. package/src/authoring/overlay-report.ts +52 -0
  240. package/src/authoring/project-scenes.ts +111 -0
  241. package/src/authoring/react-dom-authoring-adapter.ts +795 -0
  242. package/src/authoring/react-inspector-section.tsx +1041 -0
  243. package/src/authoring/react-world-authoring-adapter.ts +2400 -0
  244. package/src/authoring/root-activation.ts +61 -0
  245. package/src/authoring/scene-ui-layer.ts +485 -0
  246. package/src/authoring/story-overlay.ts +48 -0
  247. package/src/authoring/ui-authoring-adapter.ts +325 -0
  248. package/src/authoring/ui-hierarchy-menu.ts +40 -0
  249. package/src/authoring/ui-inspector-sections.tsx +156 -0
  250. package/src/authoring/undo-timeline.ts +145 -0
  251. package/src/authoring/vgai-scene-authoring-adapter.ts +746 -0
  252. package/src/authoring/viewport-pick-context.ts +31 -0
  253. package/src/authoring/viewport-raycast.ts +80 -0
  254. package/src/authoring/world-overlay.ts +76 -0
  255. package/src/authoring/world-pan-state.ts +115 -0
  256. package/src/authoring/world-session-state.ts +102 -0
  257. package/src/authoring/world2d-authoring-adapter.ts +313 -0
  258. package/src/browser-external-watch.ts +98 -0
  259. package/src/browser-play-entry.ts +112 -0
  260. package/src/browser-project-scripts.ts +112 -0
  261. package/src/browser-transpile.ts +246 -0
  262. package/src/browser-watch-reload.ts +100 -0
  263. package/src/command-listener.ts +326 -0
  264. package/src/components/AlignToolbar.tsx +189 -0
  265. package/src/components/AppRoot.tsx +73 -0
  266. package/src/components/AssetBrowser.tsx +502 -0
  267. package/src/components/BottomPanel.tsx +326 -0
  268. package/src/components/BuildPanel.tsx +455 -0
  269. package/src/components/CameraInfo.tsx +56 -0
  270. package/src/components/CommandPalette.tsx +265 -0
  271. package/src/components/ConsolePanel.tsx +272 -0
  272. package/src/components/DataPanel.tsx +1268 -0
  273. package/src/components/DefaultEditorLayout.tsx +509 -0
  274. package/src/components/FolderTree.tsx +179 -0
  275. package/src/components/GameHierarchy.tsx +1349 -0
  276. package/src/components/Inspector.tsx +671 -0
  277. package/src/components/InspectorToolSection.tsx +74 -0
  278. package/src/components/MountFailureBanner.tsx +110 -0
  279. package/src/components/NetworkSection.tsx +109 -0
  280. package/src/components/OnlineAssetBrowser.tsx +448 -0
  281. package/src/components/OverlayReportBanner.tsx +122 -0
  282. package/src/components/PlayBar.tsx +356 -0
  283. package/src/components/PrefabSaveDialog.tsx +201 -0
  284. package/src/components/ProjectHeader.tsx +174 -0
  285. package/src/components/ProjectScreen.tsx +1004 -0
  286. package/src/components/RightPanel.tsx +166 -0
  287. package/src/components/RootActivateDialog.tsx +105 -0
  288. package/src/components/SaveDialog.tsx +197 -0
  289. package/src/components/SaveStatus.tsx +69 -0
  290. package/src/components/StatsOverlay.tsx +35 -0
  291. package/src/components/ToolHost.tsx +80 -0
  292. package/src/components/Toolbar.tsx +307 -0
  293. package/src/components/ViewportOverlay.tsx +234 -0
  294. package/src/components/ViewportPanel.tsx +369 -0
  295. package/src/components/WorldContextMenu.tsx +257 -0
  296. package/src/components/WorldSelectionOverlay.tsx +2003 -0
  297. package/src/components/WorldTextEditor.tsx +86 -0
  298. package/src/components/asset-viewers/AssetViewer.tsx +38 -0
  299. package/src/components/asset-viewers/AudioViewer.tsx +149 -0
  300. package/src/components/asset-viewers/GenericJsonViewer.tsx +77 -0
  301. package/src/components/asset-viewers/ImageViewer.tsx +65 -0
  302. package/src/components/asset-viewers/InputMapViewer.tsx +206 -0
  303. package/src/components/asset-viewers/ModelViewer.tsx +180 -0
  304. package/src/components/asset-viewers/OnlineAssetDetail.tsx +236 -0
  305. package/src/components/data-csv.ts +301 -0
  306. package/src/components/entity-name-commit.ts +18 -0
  307. package/src/components/inspector-property-grouping.ts +65 -0
  308. package/src/components/inspector-widgets/AlignmentGrid.tsx +188 -0
  309. package/src/components/inspector-widgets/BorderEditor.tsx +305 -0
  310. package/src/components/inspector-widgets/ColorPicker.tsx +548 -0
  311. package/src/components/inspector-widgets/FilterEditor.tsx +108 -0
  312. package/src/components/inspector-widgets/FontPicker.tsx +196 -0
  313. package/src/components/inspector-widgets/GradientEditor.tsx +277 -0
  314. package/src/components/inspector-widgets/ScrubbableInput.tsx +152 -0
  315. package/src/components/inspector-widgets/ShadowEditor.tsx +332 -0
  316. package/src/components/inspector-widgets/color-utils.ts +202 -0
  317. package/src/components/inspector-widgets/gallery-main.tsx +167 -0
  318. package/src/components/inspector-widgets/index.ts +22 -0
  319. package/src/components/inspector-widgets/shared.tsx +135 -0
  320. package/src/components/inspectors/AnimationSection.tsx +143 -0
  321. package/src/components/inspectors/AudioSection.tsx +215 -0
  322. package/src/components/inspectors/BoneAttachmentSection.tsx +82 -0
  323. package/src/components/inspectors/CameraSection.tsx +105 -0
  324. package/src/components/inspectors/ComponentsSection.tsx +358 -0
  325. package/src/components/inspectors/JointsSection.tsx +255 -0
  326. package/src/components/inspectors/LightSection.tsx +116 -0
  327. package/src/components/inspectors/MaterialSection.tsx +1189 -0
  328. package/src/components/inspectors/MeshSection.tsx +99 -0
  329. package/src/components/inspectors/NavigationSection.tsx +58 -0
  330. package/src/components/inspectors/ParticleSection.tsx +681 -0
  331. package/src/components/inspectors/PhysicsSection.tsx +326 -0
  332. package/src/components/inspectors/PostProcessingSection.tsx +1290 -0
  333. package/src/components/inspectors/RenderingSection.tsx +109 -0
  334. package/src/components/inspectors/ShadowSection.tsx +84 -0
  335. package/src/components/inspectors/SplineSection.tsx +191 -0
  336. package/src/components/inspectors/TransformSection.tsx +191 -0
  337. package/src/components/inspectors/json-schema-fields.ts +281 -0
  338. package/src/components/inspectors/multi-edit-utils.ts +49 -0
  339. package/src/components/inspectors/zod-fields.ts +184 -0
  340. package/src/components/primitives/ColorInput.tsx +109 -0
  341. package/src/components/primitives/JsonInput.tsx +98 -0
  342. package/src/components/primitives/NumberInput.tsx +164 -0
  343. package/src/components/primitives/Panel.tsx +68 -0
  344. package/src/components/primitives/SectionHeader.tsx +45 -0
  345. package/src/components/primitives/Tooltip.tsx +82 -0
  346. package/src/components/primitives/Vec3Input.tsx +62 -0
  347. package/src/components/world-overlay-gestures.ts +1438 -0
  348. package/src/doctor/data-checks.ts +285 -0
  349. package/src/doctor/report.ts +386 -0
  350. package/src/editor-api.ts +700 -0
  351. package/src/editor-console.ts +194 -0
  352. package/src/editor-hotkeys.ts +441 -0
  353. package/src/editor-layers.ts +2 -0
  354. package/src/editor-mode.ts +28 -0
  355. package/src/editor-store.ts +2225 -0
  356. package/src/editor-viewport.ts +1924 -0
  357. package/src/entity-factory.ts +221 -0
  358. package/src/game-globals-prelude.ts +27 -0
  359. package/src/gated-globals.ts +131 -0
  360. package/src/generated/setup-by-example.generated.ts +53 -0
  361. package/src/gizmo-registry.ts +608 -0
  362. package/src/handle-system.ts +221 -0
  363. package/src/hierarchy-menu-registry.ts +70 -0
  364. package/src/hierarchy-rows.ts +73 -0
  365. package/src/hosted-example.ts +160 -0
  366. package/src/hotkeys.ts +142 -0
  367. package/src/ingest/discovery-react.ts +132 -0
  368. package/src/ingest/discovery.ts +205 -0
  369. package/src/ingest/discovery2d.ts +204 -0
  370. package/src/ingest/esm-entry-resolution.ts +102 -0
  371. package/src/ingest/games/bloom-composer/game.js +99 -0
  372. package/src/ingest/games/bloom-composer/vgai.game.json +21 -0
  373. package/src/ingest/games/embed-bundle/embed.html +10 -0
  374. package/src/ingest/games/embed-bundle/vgai.game.json +21 -0
  375. package/src/ingest/games/games-fps/assets/collision-world.glb +0 -0
  376. package/src/ingest/games/games-fps/game.js +479 -0
  377. package/src/ingest/games/games-fps/vgai.game.json +24 -0
  378. package/src/ingest/games/iframe-reachable/game-source.js +58 -0
  379. package/src/ingest/games/iframe-reachable/vgai.game.json +21 -0
  380. package/src/ingest/games/interactive-cubes/game.js +139 -0
  381. package/src/ingest/games/interactive-cubes/vgai.game.json +21 -0
  382. package/src/ingest/games/mismatched-three/fake-three.js +38 -0
  383. package/src/ingest/games/mismatched-three/game.js +10 -0
  384. package/src/ingest/games/mismatched-three/vgai.game.json +22 -0
  385. package/src/ingest/games/own-three-bundled/game.js +43 -0
  386. package/src/ingest/games/own-three-bundled/vgai.game.json +21 -0
  387. package/src/ingest/games/pointerlock-maze/game.js +288 -0
  388. package/src/ingest/games/pointerlock-maze/vgai.game.json +22 -0
  389. package/src/ingest/games-2d/_fixtures/first-party-scene.scn2d.json +124 -0
  390. package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +25 -0
  391. package/src/ingest/games-2d/bunnymark/game.ts +88 -0
  392. package/src/ingest/games-2d/bunnymark/vgai.game.json +21 -0
  393. package/src/ingest/games-2d/flappy/game.ts +148 -0
  394. package/src/ingest/games-2d/flappy/vgai.game.json +21 -0
  395. package/src/ingest/games-2d/flappy-pixi/vgai.game.json +25 -0
  396. package/src/ingest/games-2d/puzzling-potions/vgai.game.json +25 -0
  397. package/src/ingest/games-2d/tilemap-camera/game.ts +115 -0
  398. package/src/ingest/games-2d/tilemap-camera/vgai.game.json +21 -0
  399. package/src/ingest/games-react/react-rpg/entry.tsx +185 -0
  400. package/src/ingest/games-react/react-rpg/shims.d.ts +26 -0
  401. package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +129 -0
  402. package/src/ingest/games-react/react-rpg/vgai.game.json +21 -0
  403. package/src/ingest/host-namespace-registry.ts +196 -0
  404. package/src/ingest/types.ts +127 -0
  405. package/src/ingest-hierarchy-rows.ts +207 -0
  406. package/src/ingest-mode.ts +2092 -0
  407. package/src/ingest-siblings.ts +466 -0
  408. package/src/initial-scene.ts +194 -0
  409. package/src/inspector-section-registry.ts +117 -0
  410. package/src/interactive-edit-scope.ts +34 -0
  411. package/src/main.tsx +23 -0
  412. package/src/manifest-project.ts +43 -0
  413. package/src/model-thumbnail.ts +156 -0
  414. package/src/module-mode.ts +513 -0
  415. package/src/navmesh-handler.ts +94 -0
  416. package/src/packaged-runtime.ts +49 -0
  417. package/src/play-mode.ts +1148 -0
  418. package/src/playground/main.ts +206 -0
  419. package/src/project-manager.ts +173 -0
  420. package/src/scene-diff.ts +112 -0
  421. package/src/scene-serializer.ts +144 -0
  422. package/src/scene-sync.ts +587 -0
  423. package/src/scene-ui-mode/active-scene-ui.ts +58 -0
  424. package/src/scene-ui-mode/scene-ui-palette-panel.tsx +152 -0
  425. package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +29 -0
  426. package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +74 -0
  427. package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +49 -0
  428. package/src/scene-ui-mode/ui-prefab-io.ts +76 -0
  429. package/src/script-loader.ts +143 -0
  430. package/src/storage/fsa-storage.ts +55 -0
  431. package/src/storage/handle-storage.ts +150 -0
  432. package/src/storage/handle-store.ts +116 -0
  433. package/src/storage/http-storage.ts +137 -0
  434. package/src/storage/index.ts +147 -0
  435. package/src/storage/mem-storage.ts +135 -0
  436. package/src/storage/opfs-storage.ts +25 -0
  437. package/src/storage/paths.ts +26 -0
  438. package/src/storage/seed.ts +106 -0
  439. package/src/storage/types.ts +110 -0
  440. package/src/tool-loader.ts +291 -0
  441. package/src/ui-editor/inspector.tsx +563 -0
  442. package/src/ui-editor/node-ops.ts +266 -0
  443. package/src/ui-editor/overlay.tsx +380 -0
  444. package/src/ui-editor/palette.tsx +114 -0
  445. package/src/ui-editor/react-store.ts +78 -0
  446. package/src/ui-editor/timeline-panel.tsx +443 -0
  447. package/src/ui-editor/ui-edit-store.ts +552 -0
  448. package/src/ui-editor/ui-prefab.ts +95 -0
  449. package/src/ui-source/inspect.ts +544 -0
  450. package/src/ui-source/oid-transform.ts +133 -0
  451. package/src/ui-source/source-write-backend.ts +203 -0
  452. package/src/ui-source/tailwind-map.ts +174 -0
  453. package/src/ui-source/writer.ts +1175 -0
  454. package/src/widgets/index.ts +73 -0
  455. package/template/.claude/skills/editor/SKILL.md +252 -0
  456. package/template/CLAUDE.md +512 -0
  457. package/template/LICENSE +30 -0
  458. package/template/emit-schemas.ts +42 -0
  459. package/template/index.html +17 -0
  460. package/template/package.json +53 -0
  461. package/template/project.json +9 -0
  462. package/template/public/audio/ambient-arena.wav +0 -0
  463. package/template/public/audio/ambient-music.wav +0 -0
  464. package/template/public/audio/ambient-nature.wav +0 -0
  465. package/template/public/audio/ambient-wind.wav +0 -0
  466. package/template/public/inputmaps/default.inputmap.json +41 -0
  467. package/template/public/jsm/libs/draco/gltf/draco_decoder.js +33 -0
  468. package/template/public/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
  469. package/template/public/jsm/libs/draco/gltf/draco_encoder.js +33 -0
  470. package/template/public/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
  471. package/template/public/scenes/default.vscn.json +43 -0
  472. package/template/public/scenes/untitled.vscn.json +29 -0
  473. package/template/public/textures/waternormals.jpg +0 -0
  474. package/template/server/colyseus-setup.ts +85 -0
  475. package/template/server/main.ts +34 -0
  476. package/template/server/rooms/arena-room.ts +420 -0
  477. package/template/server/rooms/game-room.ts +162 -0
  478. package/template/server/rooms.ts +15 -0
  479. package/template/server/tsconfig.json +19 -0
  480. package/template/src/data/README.md +40 -0
  481. package/template/src/data/assets.ts +31 -0
  482. package/template/src/data/tuning.data.json +5 -0
  483. package/template/src/data/tuning.schema.json +21 -0
  484. package/template/src/data/tuning.schema.ts +34 -0
  485. package/template/src/data/tuning.ts +24 -0
  486. package/template/src/main.ts +157 -0
  487. package/template/src/scripts/components/data-spinner.ts +45 -0
  488. package/template/src/scripts/components/scene-camera.ts +40 -0
  489. package/template/src/scripts/main.ts +94 -0
  490. package/template/src/scripts/registry.ts +46 -0
  491. package/template/src/tools/README.md +14 -0
  492. package/template/src/tools/example.tool.tsx +91 -0
  493. package/template/src/tools/spin.tool.tsx +101 -0
  494. package/template/src/ui/Button.tsx +27 -0
  495. package/template/src/ui/DefaultHud.tsx +40 -0
  496. package/template/src/ui/KeyHint.tsx +25 -0
  497. package/template/src/ui/Panel.tsx +52 -0
  498. package/template/src/ui/RebindPanel.tsx +129 -0
  499. package/template/src/ui/Slider.tsx +28 -0
  500. package/template/src/ui/StatusDot.tsx +22 -0
  501. package/template/src/ui/Toggle.tsx +19 -0
  502. package/template/src/ui/game-state.tsx +37 -0
  503. package/template/src/ui/index.ts +7 -0
  504. package/template/src/ui/mount-default-hud.tsx +19 -0
  505. package/template/src/ui/mount-ui.ts +15 -0
  506. package/template/tsconfig.json +36 -0
  507. package/template/validate-asset-content.ts +78 -0
  508. package/template/validate-manifest.ts +55 -0
  509. package/template/validate-scenes.ts +211 -0
  510. package/template/vgai.game.json +21 -0
  511. package/template/vite.config.ts +128 -0
  512. package/vite-plugin-example-assets.ts +60 -0
  513. package/vite-plugin-game-globals.ts +46 -0
  514. package/vite-plugin-game-static.ts +71 -0
  515. package/vite-plugin-project-game-static.ts +84 -0
  516. package/vite-plugin-react-game-static.ts +98 -0
  517. package/vite-plugin-react-world-provider.ts +81 -0
  518. package/vite-plugin-react-world-runtime.ts +76 -0
  519. package/vite-plugin-ui-oid.ts +561 -0
@@ -0,0 +1,571 @@
1
+ /**
2
+ * Packaged editor server — the entry a `@vgai/editor` npm package resolves
3
+ * from a PROJECT's own `node_modules`, with NO monorepo checkout on disk
4
+ * (Phase B, docs/FRONTEND-FIRST-ROADMAP.md; see the phase plan's D-FORK
5
+ * decision + §0/§3 "the real work" for `@vgai/editor`).
6
+ *
7
+ * Run with:
8
+ * VGAI_PROJECT=<projectDir> node dist-server/packaged.mjs
9
+ *
10
+ * ## Why this file exists (and isn't just `prod.ts`)
11
+ *
12
+ * `dev.ts` boots ONE Vite instance rooted at the monorepo checkout, which
13
+ * does double duty: it builds the editor's OWN React app AND resolves the
14
+ * OPENED PROJECT's module graph (bare imports like `three`/`zod`/`react`
15
+ * resolve only because that Vite's `root` is the repo root, whose hoisted
16
+ * `node_modules` has everything). `prod.ts` (the Electron packaging path)
17
+ * has no Vite at all — its `serveScripts` fallback in `editor-server.ts` is
18
+ * a hand-rolled `/@fs` route that only rewrites `@engine/` imports; it has
19
+ * NO handler for bare npm specifiers or `/node_modules/*` at all, so it
20
+ * cannot serve a real project's own module graph (see the phase plan's §0).
21
+ *
22
+ * This file is the missing second code path: it serves the editor's OWN
23
+ * app as a PREBUILT static SPA (`../dist/`, produced by the workspace-local
24
+ * `npm run build -w @vgai/editor`) — no dev-mode transform of the editor's
25
+ * own source is needed, since a project author isn't hot-reloading the
26
+ * editor's own UI — and boots a SEPARATE, PROJECT-ROOTED Vite instance
27
+ * (`root: <project>`, `configFile: false`, middleware mode, HMR + watch
28
+ * ENABLED) whose job is ONLY to resolve/transform the opened PROJECT's own
29
+ * source. Because that Vite instance's `root` is the project directory,
30
+ * Vite's own resolver naturally walks the PROJECT's OWN `node_modules` for
31
+ * every bare specifier (`three`, `zod`, `react`, `@vgai/scene-ui`, ...) —
32
+ * for free, with zero hand-written import rewriting. This is the same
33
+ * pattern `packages/editor/scripts/run-conformance.ts` uses to run the
34
+ * adapter conformance kit against an arbitrary external folder, adapted for
35
+ * live editing (HMR/watch on) instead of a one-shot SSR run.
36
+ *
37
+ * The `@engine/` alias project source still uses (pending the `@engine/*` →
38
+ * `@vgai/engine/*` migration the phase plan defers to a follow-up cleanup
39
+ * slice) is reconstructed here too — pointed at the PROJECT's own installed
40
+ * `@vgai/engine`, not a copy baked into this package's own tarball. A
41
+ * project pinning `@vgai/engine@0.3.0` must be served ITS 0.3.0 source, so
42
+ * `@vgai/engine`/`@vgai/scene-ui` are resolved via `createRequire`, rooted
43
+ * at the project directory — Node's own resolution algorithm, not a
44
+ * hardcoded path (see `resolveInstalledPackageSrcDir` in `server-utils.ts`,
45
+ * shared with `editor-server.ts`'s equivalent fix for `prod.ts`'s route).
46
+ *
47
+ * ## React-world OID authoring parity (DEBT-4 follow-on)
48
+ *
49
+ * `dev.ts` reaches TWO extra plugins via its `configFile:` (repo-root
50
+ * `vite.config.ts`): `uiOidPlugin()` (stamps `data-oid` on every JSX element
51
+ * in project-scoped `.tsx` files + serves the `/__ui-source/*` read/write
52
+ * endpoints the hierarchy/inspector's source-write path needs) and
53
+ * `reactWorldProviderPlugin()` (resolves the synthetic
54
+ * `/__vgai-game-provider` module `adapter-resolver.ts`'s
55
+ * `loadProjectGameProvider` imports — WITHOUT it, every `kind: 'react'`
56
+ * world 404s on mount, not just "authors without OID"). Both are carried
57
+ * over here, imported straight from this package's own `vite-plugin-ui-oid`
58
+ * / `vite-plugin-react-world-provider` (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 `GameProvider` 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
+ * **A second, deeper gap this change surfaced — FIXED by the C1 follow-on**
102
+ * (`phase-b/c1-react-mount`): `adapter-resolver.ts` used to mount every react
103
+ * world with THIS PACKAGE'S OWN bundled `react`/`react-dom` (`import {
104
+ * createRoot } from 'react-dom/client'` / `createElement`, statically baked
105
+ * into the prebuilt `dist/` at `npm run build -w @vgai/editor` time), while
106
+ * the entry component's own hooks ran against WHATEVER `react` the
107
+ * PROJECT'S OWN `node_modules` resolves (this Vite instance's `root` is the
108
+ * project, so bare `react` imports resolve there, by design — a project pins
109
+ * its own react version, independent of the editor). `dev.ts` avoids this
110
+ * exact failure mode via repo-root `vite.config.ts`'s `resolve.dedupe:
111
+ * ['react', 'react-dom', ...]`, which works ONLY because dev.ts's single
112
+ * Vite instance resolves BOTH the editor's own source and the opened
113
+ * project's source through the SAME `node_modules` — there is no equivalent
114
+ * collapsing point here: the editor's own react is baked into a separate,
115
+ * already-built production bundle, physically disconnected from this
116
+ * instance's live resolution of the project's react. Two separate `react`
117
+ * module instances meant the mounting `react-dom`'s hook dispatcher and the
118
+ * entry component's own `useState`/`useContext` calls read DIFFERENT
119
+ * internal singleton objects — the classic "duplicate React copies" failure.
120
+ * **Confirmed live, not theoretical, at the time this was still open**: a
121
+ * real-browser Playwright run against a tarball-installed, checkout-absent
122
+ * packaged editor (`vgai.game.json`'s `kind: 'react'` "hud" world,
123
+ * `useGameState`) threw exactly this at mount: `TypeError: Cannot read
124
+ * properties of null (reading 'useContext')` inside `useGame()`
125
+ * (`src/ui/game-state.tsx`), called from the project's `Hud` component,
126
+ * rendered by the editor's OWN bundled `react_stack_bottom_frame`
127
+ * (`assets/index-*.js`).
128
+ *
129
+ * The fix (C1): a react world's mount does NOT need to share react with the
130
+ * editor's own UI at all — it is an isolated `createRoot` subtree that only
131
+ * needs to be internally consistent with itself. So
132
+ * `adapter-resolver.ts`'s `resolveReactWorldMountRuntime()` now resolves
133
+ * `createElement`/`createRoot`/the engine-`GameProvider` fallback from the
134
+ * PROJECT's own graph under the packaged runtime, via this file's newly
135
+ * registered `reactWorldRuntimePlugin()` (a synthetic virtual module,
136
+ * `/__vgai-react-world-runtime`, the same "literal static re-export" trick
137
+ * `reactWorldProviderPlugin()` already uses for `GameProvider` — see
138
+ * `vite-plugin-react-world-runtime.ts`'s doc comment) — converging the mount
139
+ * call and the entry component's hooks on ONE react instance, the project's
140
+ * own. Gated on `isPackagedRuntime()` (`src/packaged-runtime.ts`, itself
141
+ * reading a `packaged` flag `/__editor/project` now reports) so `dev.ts`/
142
+ * Electron's existing static-import path — already correct via
143
+ * `resolve.dedupe` — stays byte-unchanged. See `adapter-resolver.ts`'s
144
+ * `resolveReactWorldMountRuntime` doc comment for the full mechanism, and
145
+ * the C1 build report for the real-browser proof (checkout absent, tarball-
146
+ * installed, no console error, DOM rendered).
147
+ *
148
+ * ## Scoped dependency discovery, not disabled discovery (C3 follow-on)
149
+ *
150
+ * `optimizeDeps` below used to carry `noDiscovery: true` (see git history for
151
+ * the exact prior comment): with the boot-time crawl DEFAULT-targeting the
152
+ * project's own `index.html`, and a scaffolded project's `src/main.ts`
153
+ * bootstrap containing a `?net=p2p-host`-gated `await import('../server/
154
+ * rooms')` that esbuild's scanner statically follows regardless of the
155
+ * runtime guard around it, that crawl reached real Colyseus SERVER code
156
+ * (`colyseus` -> `@colyseus/core` -> `@pm2/io`, a package with no browser-
157
+ * resolvable entry) and crashed the esbuild prebundle step outright. Turning
158
+ * discovery off entirely sidestepped that crash — at the cost of any CJS-
159
+ * only dependency NOT in the hardcoded `include` list below being served raw
160
+ * to the browser and failing (this is exactly what broke `examples/
161
+ * r3f-probe`: `@react-three/fiber`'s CJS transitive deps, e.g.
162
+ * `use-sync-external-store/shim/with-selector.js`, have no place to be
163
+ * hardcoded — this server serves ARBITRARY projects, most without R3F
164
+ * installed at all, and Vite errors on `include`-ing a not-installed dep).
165
+ *
166
+ * The fix (C3, `phase-b/c3-r3f-packaged`) is not "add R3F to the allowlist"
167
+ * — that just moves the whack-a-mole to the next ecosystem a project
168
+ * happens to pull in. It SCOPES the crawl instead of disabling it:
169
+ * `optimizeDeps.entries` is set (see `projectOptimizeDepsEntries` /
170
+ * `computeProjectOptimizeDepsEntries` in `project-optimize-deps-entries.ts`)
171
+ * to the OPENED PROJECT's own `vgai.game.json`-declared world `entry` files
172
+ * — the exact per-world mount code the editor's adapter-resolver dynamically
173
+ * imports for Play mode, never the project's `src/main.ts` bootstrap (a
174
+ * standalone-build concern this Vite instance never serves — the editor's
175
+ * own prebuilt SPA answers `/`). A world entry's own import graph reaches
176
+ * real client packages (react, R3F, a legitimate client `@colyseus/sdk`
177
+ * import for real multiplayer — `examples/third-person-arena/src/
178
+ * network.ts`) but never `server/`, so the `@pm2/io` crash class cannot
179
+ * occur no matter what a project's room code imports, while ANY CJS-needing-
180
+ * prebundle client dependency a world reaches gets auto-discovered and
181
+ * prebundled — no per-package allowlist, adapts to whatever the opened
182
+ * project actually has installed. `project-optimize-deps-entries.ts`'s doc
183
+ * comment has the empirical proof (direct `vite.optimizeDeps` runs, not just
184
+ * theory): scoped to the template's world entry, prebundling succeeds
185
+ * clean; scoped to `index.html` (the old default), it reproduces the exact
186
+ * `@pm2/io` crash; scoped to `examples/r3f-probe`'s world entries, `@react-
187
+ * three/fiber`/`@react-three/drei` are discovered and prebundled with no
188
+ * hardcoding; scoped to `examples/third-person-arena`'s world entry (a real
189
+ * colyseus multiplayer project), `@colyseus/sdk` prebundles clean too — the
190
+ * client SDK's own published build is self-contained and never reaches
191
+ * `@colyseus/core`'s server-only `Stats.mjs`. See the C3 build report for
192
+ * the real-browser proof (checkout absent, tarball-installed): R3F's
193
+ * `examples/r3f-probe` "net" world mounts with no console error, AND the
194
+ * pre-existing react-world (C1) / colyseus-bearing-project (this section)
195
+ * cases still work — no regression.
196
+ *
197
+ * ## Known scope trims (v1 — flagged, not silently dropped)
198
+ *
199
+ * - "New Project" scaffolding from WITHIN a packaged (no-checkout) editor
200
+ * fails loudly instead of scaffolding — see `editor-server.ts`'s
201
+ * `/__editor/create-project` handler (`packagedNoMonorepoTemplateError`).
202
+ * `scaffoldProject`'s `monoRoot` assumes a monorepo checkout layout
203
+ * (`<monoRoot>/packages/editor/template`), which a `node_modules`-
204
+ * installed `@vgai/editor` does not have (only a bare `template/` at its
205
+ * own package root). The phase plan (§6 landmine 5) already recommends
206
+ * relocating `template/` into `create-vgai-project` as the real fix —
207
+ * deferred there, not attempted here; `vgai create <name>` from a terminal
208
+ * is the supported path today.
209
+ */
210
+
211
+ import { realpathSync } from 'node:fs';
212
+ import path from 'node:path';
213
+ import { fileURLToPath } from 'node:url';
214
+ import express from 'express';
215
+ import { createServer as createViteServer } from 'vite';
216
+ import { projectGameStaticPlugin } from '../vite-plugin-project-game-static';
217
+ import { reactWorldProviderPlugin } from '../vite-plugin-react-world-provider';
218
+ import { reactWorldRuntimePlugin } from '../vite-plugin-react-world-runtime';
219
+ import { uiOidPlugin } from '../vite-plugin-ui-oid';
220
+ import { canonicalProjectRoot } from './canonical-path';
221
+ import { createEditorServer } from './editor-server';
222
+ import { shouldShadowGameGlobals } from './game-globals-shadow';
223
+ import {
224
+ SERVER_ONLY_PREBUNDLE_EXCLUDE,
225
+ computeProjectOptimizeDepsEntries,
226
+ } from './project-optimize-deps-entries';
227
+ import {
228
+ friendlyListenError,
229
+ resolveBindHost,
230
+ resolveInstalledPackageSrcDir,
231
+ } from './server-utils';
232
+ import { registerSession, unregisterSession } from './session-registry';
233
+
234
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
235
+ // This file lives directly under the `@vgai/editor` package root either as
236
+ // `packages/editor/server/packaged.ts` (source, run via tsx) or as the
237
+ // esbuild-bundled `packages/editor/dist-server/packaged.mjs` (`server/` and
238
+ // `dist-server/` are BOTH direct children of the package root) — so
239
+ // `resolve(__dirname, '..')` lands on the package root in either shape.
240
+ const editorPackageRoot = path.resolve(__dirname, '..');
241
+ // The workspace-local `npm run build -w @vgai/editor` (root `vite.config.ts`,
242
+ // `--outDir packages/editor/dist`) output — the editor's own prebuilt SPA.
243
+ const distPath = path.resolve(editorPackageRoot, 'dist');
244
+
245
+ const PORT = Number(process.env['PORT']) || Number(process.env['VGAI_EDITOR_PORT']) || 5173;
246
+ const HMR_PORT = Number(process.env['VGAI_HMR_PORT']) || (PORT === 5173 ? 24678 : PORT + 100);
247
+ // S3: bind to loopback by default; opt-in to a wider interface via env.
248
+ const HOST = resolveBindHost();
249
+
250
+ if (!process.env['VGAI_PROJECT']) {
251
+ console.error(
252
+ '\n \x1b[31mVGAI_PROJECT is required — the packaged editor has no monorepo checkout to fall ' +
253
+ 'back to. Set VGAI_PROJECT=<absolute path to your vgai project>.\x1b[0m\n',
254
+ );
255
+ process.exit(1);
256
+ }
257
+ // Canonicalized (symlink-resolved) — see canonical-path.ts's doc comment
258
+ // (dev.ts's identical fix: a react world's `GameProvider` colocation breaks
259
+ // if this path and Vite's own resolver disagree on a symlinked segment).
260
+ const projectPath = canonicalProjectRoot(process.env['VGAI_PROJECT']);
261
+
262
+ // Worktree / hoisted-`node_modules` support — same rationale as dev.ts:
263
+ // `node_modules` is very often a symlink, and Vite's `server.fs.allow`
264
+ // matches on the symlink-RESOLVED path.
265
+ const projectNodeModulesRealPath = (() => {
266
+ try {
267
+ const real = realpathSync(path.join(projectPath, 'node_modules'));
268
+ return real.startsWith(projectPath) ? null : real;
269
+ } catch {
270
+ return null;
271
+ }
272
+ })();
273
+
274
+ // D-X2: the project-static route must follow the CURRENTLY open project, not
275
+ // a boot-time snapshot — mirrors dev.ts's `currentProjectRoot` box, updated
276
+ // in the SAME `onProjectOpened` callback that extends Vite's fs.allow/
277
+ // watcher/game-globals shadow for a newly opened project.
278
+ let currentProjectRoot: string = projectPath;
279
+ const projectRoots = new Set<string>([projectPath]);
280
+
281
+ // C3: the OPENED project's own manifest-declared world entry files — scopes
282
+ // `optimizeDeps.entries` below to exactly the client code a world can mount,
283
+ // never `index.html`/the project's own bootstrap `main.ts` (which is how the
284
+ // crawl used to reach `server/` room code and crash on `@pm2/io`). Boot-time
285
+ // snapshot only, same trim as `engineSrc`/`resolve.alias` below — see
286
+ // `project-optimize-deps-entries.ts`'s doc comment for the full rationale
287
+ // and the empirical proof this avoids the colyseus-scanner crash.
288
+ const projectOptimizeDepsEntries = computeProjectOptimizeDepsEntries(projectPath);
289
+
290
+ // Kept byte-identical to dev.ts's copy (see its own doc comment) — the
291
+ // lexical shadow prepended to every project module so `window`/`document`
292
+ // resolve to the editor's gated proxies instead of the real, shared globals.
293
+ const GAME_GLOBALS_PRELUDE =
294
+ 'const window=(globalThis.__vgaiGameWindow||globalThis.window),' +
295
+ 'document=(globalThis.__vgaiGameDocument||globalThis.document);\n';
296
+
297
+ async function main(): Promise<void> {
298
+ const app = express();
299
+
300
+ // The project's own installed `@vgai/engine` — resolved from the PROJECT's
301
+ // node_modules (Node's own resolution algorithm), never a copy baked into
302
+ // this package's own tarball (see this file's header doc comment). Falls
303
+ // back to nothing (alias omitted) when unresolvable — e.g. a project that
304
+ // hasn't `npm install`ed yet, or predates Phase B — so a bad/missing
305
+ // install degrades to "no @engine alias" rather than blocking boot.
306
+ const engineSrc = resolveInstalledPackageSrcDir(projectPath, '@vgai/engine');
307
+ if (!engineSrc) {
308
+ console.warn(
309
+ '\n \x1b[33mWarning:\x1b[0m no @vgai/engine resolvable from ' +
310
+ `${projectPath} — run npm install in the project first. ` +
311
+ '`@engine/*` imports will fail to resolve until then.\n',
312
+ );
313
+ }
314
+
315
+ // 1. Boot a Vite instance rooted at the PROJECT, not this editor package
316
+ // or a monorepo checkout — its own node_modules resolves every bare
317
+ // specifier for free (the run-conformance.ts pattern, live-editing not
318
+ // one-shot: HMR + watch stay ON, unlike that script's headless SSR run).
319
+ const vite = await createViteServer({
320
+ configFile: false,
321
+ root: projectPath,
322
+ appType: 'custom', // no HTML serving/SPA-fallback — this instance only serves project modules
323
+ plugins: [
324
+ // React-world OID authoring parity (see this file's header doc comment,
325
+ // "React-world OID authoring parity"): live `data-oid` instrumentation +
326
+ // the `/__ui-source/*` read/write endpoints the hierarchy/inspector's
327
+ // source-write path needs. Uses its own built-in `defaultProjectScopeInclude`
328
+ // (already project-scoped) unmodified — no extra scoping needed since this
329
+ // instance's `root` IS the project already.
330
+ uiOidPlugin(),
331
+ // Resolves the synthetic `/__vgai-game-provider` module
332
+ // `adapter-resolver.ts`'s `loadProjectGameProvider` imports for every
333
+ // `kind: 'react'` world's mount — WITHOUT this, a react world 404s at
334
+ // mount time in packaged mode (this is a correctness fix, not just an
335
+ // authoring nicety). See vite-plugin-react-world-provider.ts's doc comment.
336
+ reactWorldProviderPlugin(),
337
+ // Resolves the synthetic `/__vgai-react-world-runtime` module
338
+ // `adapter-resolver.ts`'s `resolveReactWorldMountRuntime` imports —
339
+ // the dual-React-instance fix (this file's header doc comment,
340
+ // "React-world OID authoring parity" section, final paragraph): a
341
+ // react world's `createElement`/`createRoot`/engine-`GameProvider`
342
+ // fallback must come from THIS project-rooted Vite instance's own
343
+ // react, not the editor's separately-bundled static SPA copy. See
344
+ // vite-plugin-react-world-runtime.ts's doc comment.
345
+ reactWorldRuntimePlugin(),
346
+ // vgai-game-globals — byte-identical rationale to dev.ts's copy.
347
+ {
348
+ name: 'vgai-game-globals',
349
+ transform(code: string, id: string) {
350
+ const file = id.split('?')[0]!;
351
+ if (!shouldShadowGameGlobals(file, projectRoots)) return null;
352
+ return { code: GAME_GLOBALS_PRELUDE + code, map: null };
353
+ },
354
+ },
355
+ // Project-rooted verbatim static serving (D-X2) — same plugin dev.ts uses.
356
+ projectGameStaticPlugin(() => currentProjectRoot),
357
+ // Intercept project script changes and send a custom HMR event instead
358
+ // of triggering a full page reload — byte-identical to dev.ts's copy
359
+ // (always registered here since a project is ALWAYS open in packaged
360
+ // mode, unlike dev.ts's conditional-on-boot-time-projectPath gate).
361
+ {
362
+ name: 'vgai-script-hmr',
363
+ handleHotUpdate({
364
+ file,
365
+ server,
366
+ }: {
367
+ file: string;
368
+ server: { ws: { send: (msg: unknown) => void } };
369
+ }) {
370
+ if (/\.(ts|tsx|js|jsx)$/.test(file)) {
371
+ server.ws.send({ type: 'custom', event: 'vgai:script-update', data: { file } });
372
+ }
373
+ // Swallow all project file changes — the editor handles scene/asset
374
+ // updates via its own SSE channel (mirrors dev.ts's `return []`).
375
+ return [];
376
+ },
377
+ },
378
+ ],
379
+ optimizeDeps: {
380
+ // C3 (phase-b/c3-r3f-packaged) — SCOPED auto-discovery, not disabled
381
+ // discovery. This used to be `noDiscovery: true` (see git history for
382
+ // the pre-C3 version of this comment): that flag disabled Vite's
383
+ // AUTO-prebundling entirely — both the boot-time crawl AND the lazy,
384
+ // on-request discovery of new bare specifiers — because the DEFAULT
385
+ // crawl target (the project's own `index.html`) follows everything
386
+ // dynamically reachable from it, including the project's `server/`
387
+ // room code (a scaffolded project's `src/main.ts` dynamically imports
388
+ // `../server/rooms` behind a `?net=p2p-host` URL-param branch — a
389
+ // branch esbuild's scanner still statically follows), whose colyseus
390
+ // server transitive deps (`@colyseus/core` -> `@pm2/io`) have no
391
+ // browser-resolvable entry and crash the esbuild prebundle step
392
+ // (`Failed to resolve entry for package "@pm2/io"`).
393
+ //
394
+ // That worked for the react-only CJS fix (C1) because the fix could be
395
+ // fully hardcoded: react/react-dom are ALWAYS present (the editor
396
+ // itself depends on them). It does NOT work for R3F (or any other
397
+ // ecosystem a project's own `package.json` happens to pull in): this
398
+ // server can't hardcode `@react-three/fiber` into `include` for every
399
+ // possible project — most projects don't have it installed, and Vite
400
+ // errors on `include`-ing a dep that isn't there.
401
+ //
402
+ // The actual fix: scope the CRAWL, don't disable it. `entries` below
403
+ // points the crawl at the OPENED PROJECT's own manifest-declared world
404
+ // entry files (`computeProjectOptimizeDepsEntries` /
405
+ // `project-optimize-deps-entries.ts`) instead of `index.html` — the
406
+ // exact per-world mount code the editor's adapter-resolver dynamically
407
+ // imports for Play mode (NEVER the project's own `src/main.ts`
408
+ // bootstrap, which is a standalone-build concern this Vite instance
409
+ // never serves anyway — the editor's own prebuilt SPA answers `/`).
410
+ // A world entry's import graph can reach real client packages
411
+ // (`react`, `@react-three/fiber`, `@react-three/drei`, a legitimate
412
+ // client `@colyseus/sdk` import for real multiplayer) but never
413
+ // `server/` — see `project-optimize-deps-entries.ts`'s doc comment for
414
+ // the empirical proof (a direct `vite.optimizeDeps` run) that this
415
+ // scoping avoids the `@pm2/io` crash for a colyseus-bearing project
416
+ // while a `@react-three/fiber`-bearing world's CJS transitive deps
417
+ // (`use-sync-external-store` and friends) get discovered and
418
+ // prebundled automatically — no allowlist, adapts to whatever the
419
+ // opened project actually has installed.
420
+ //
421
+ // `include` below is now a MINIMUM baseline, not the only mechanism:
422
+ // React's JSX-runtime specifiers are INJECTED by the JSX transform
423
+ // (not written as literal source imports), so even an entries-scoped
424
+ // crawl does not reliably discover them on its own — the repo-root
425
+ // `vite.config.ts` / template `vite.config.ts` `optimizeDeps.include`
426
+ // carry the identical comment/list for the same reason. Kept
427
+ // unconditional (not project-conditional) since react/react-dom are
428
+ // always present (the editor's own react-world mount runtime depends
429
+ // on them regardless of what a given project uses).
430
+ //
431
+ // `react-dom` (bare, NOT just `/client`) was added for C1
432
+ // (`phase-b/c1-react-mount`): `vite-plugin-react-world-runtime.ts`'s
433
+ // synthetic `/__vgai-react-world-runtime` module re-exports `flushSync`
434
+ // from bare `react-dom` (the same peer `react-dom/client`'s `createRoot`
435
+ // comes from — `design-time-layers.ts`'s react-world layer mount needs
436
+ // it from that SAME installed package). Confirmed live at the time:
437
+ // a real-browser Playwright run against a tarball-installed,
438
+ // checkout-absent packaged editor threw exactly `SyntaxError: The
439
+ // requested module '/node_modules/react-dom/index.js' does not
440
+ // provide an export named 'flushSync'` at Play-mode mount before this
441
+ // entry was added.
442
+ include: [
443
+ 'react',
444
+ 'react-dom',
445
+ 'react-dom/client',
446
+ 'react/jsx-runtime',
447
+ 'react/jsx-dev-runtime',
448
+ ],
449
+ entries: projectOptimizeDepsEntries,
450
+ // C3 hardening: belt to the entries-scoping suspenders. Entries-scoping
451
+ // ALREADY keeps the crawl out of `server/` for a well-formed project
452
+ // (so the colyseus server stack is never reached), but a project that
453
+ // VIOLATES D2 by value-importing `server/` room code FROM a world entry
454
+ // would drag `@colyseus/core` -> `@pm2/io` into the crawl and crash the
455
+ // esbuild prebundle step — which, unhandled in the async optimizer,
456
+ // hard-kills the whole packaged process (the only authoring surface).
457
+ // Excluding these SERVER-ONLY names stops esbuild crawling into them, so
458
+ // that pathological world degrades to a benign in-browser import failure
459
+ // instead of process death. Does NOT include `@colyseus/sdk` — the
460
+ // legitimate CLIENT SDK a real multiplayer project imports (D2, dynamic-
461
+ // imported) must still prebundle; it is a separate package and its path
462
+ // is untouched. See `project-optimize-deps-entries.ts`'s
463
+ // `SERVER_ONLY_PREBUNDLE_EXCLUDE` doc comment for the live proof.
464
+ exclude: [...SERVER_ONLY_PREBUNDLE_EXCLUDE],
465
+ },
466
+ resolve: {
467
+ alias: [
468
+ // The `@engine/*` alias project source still uses (pending the
469
+ // `@engine/*` → `@vgai/engine/*` migration deferred to a follow-up
470
+ // slice) — pointed at THIS project's own installed engine version,
471
+ // never a checkout or this editor package's own copy. Omitted
472
+ // entirely when unresolvable (see the warning above) rather than
473
+ // pointing at a wrong/nonexistent directory.
474
+ ...(engineSrc ? [{ find: /^@engine\//, replacement: `${engineSrc}/` }] : []),
475
+ ],
476
+ },
477
+ server: {
478
+ middlewareMode: true,
479
+ hmr: { port: HMR_PORT },
480
+ watch: {},
481
+ fs: {
482
+ allow: [projectPath, ...(projectNodeModulesRealPath ? [projectNodeModulesRealPath] : [])],
483
+ },
484
+ },
485
+ });
486
+
487
+ // 2. Mount editor API routes (/__editor/*). `engineRoot` has no monorepo
488
+ // meaning in packaged mode (no checkout exists) — it's used by
489
+ // `createEditorServer` only as (a) the "no project open" sentinel
490
+ // default for `projectRoot`, which is never hit here since
491
+ // `projectPath` is always set, and (b) `create-project`'s `monoRoot`
492
+ // (New Project scaffolding from the in-editor UI — a pre-existing gap
493
+ // in packaged mode, see this file's header doc comment). Pointing it at
494
+ // this editor package's own root is the closest available analog and
495
+ // keeps both uses well-defined rather than pointing at nothing.
496
+ const editorRouter = createEditorServer({
497
+ engineRoot: editorPackageRoot,
498
+ projectPath,
499
+ onProjectOpened(newProjectPath: string) {
500
+ const allowed = vite.config.server.fs.allow;
501
+ if (!allowed.includes(newProjectPath)) {
502
+ allowed.push(newProjectPath);
503
+ }
504
+ vite.watcher.add(newProjectPath);
505
+ projectRoots.add(newProjectPath);
506
+ currentProjectRoot = newProjectPath;
507
+ // Session parity with dev.ts (D12/Phase B): keep the registry pointing
508
+ // at the CURRENT project so `vgai edit`/`sessions`/cwd-resolution see
509
+ // switches, not boot state — same call, same shape.
510
+ registerSession({
511
+ project: newProjectPath,
512
+ port: PORT,
513
+ pid: process.pid,
514
+ startedAt: sessionStartedAt,
515
+ });
516
+ },
517
+ });
518
+ app.use(editorRouter);
519
+
520
+ // 3. The project-rooted Vite instance — handles `/@fs/`, `/@id/`, and any
521
+ // request matching a real file under the project root (module
522
+ // transform + bare-specifier resolution through the PROJECT's own
523
+ // node_modules). `appType: 'custom'` means it calls `next()` instead of
524
+ // synthesizing an HTML/404 response for anything else, so requests for
525
+ // the editor's OWN app (`/`, `/assets/*`) fall through to step 4.
526
+ app.use(vite.middlewares);
527
+
528
+ // 4. Serve the editor's own PREBUILT SPA (static — no dev-mode transform
529
+ // of the editor's own source; see this file's header doc comment).
530
+ app.use(express.static(distPath));
531
+ app.use((_req, res) => {
532
+ res.sendFile(path.join(distPath, 'index.html'));
533
+ });
534
+
535
+ // 5. Start Express
536
+ const sessionStartedAt = new Date().toISOString();
537
+ const server = app.listen(PORT, HOST, () => {
538
+ console.log(
539
+ `\n \x1b[32mEditor server\x1b[0m (packaged) running at \x1b[36mhttp://${HOST}:${PORT}\x1b[0m`,
540
+ );
541
+ console.log(` \x1b[33mProject\x1b[0m: ${projectPath}`);
542
+ console.log(` \x1b[33mEditor\x1b[0m: \x1b[36mhttp://${HOST}:${PORT}/\x1b[0m\n`);
543
+ // D12/Phase B: announce this session so `vgai edit`/`sessions`/`play`
544
+ // (cwd-resolution) see the packaged runtime exactly like dev.ts's.
545
+ registerSession({
546
+ project: projectPath,
547
+ port: PORT,
548
+ pid: process.pid,
549
+ startedAt: sessionStartedAt,
550
+ });
551
+ });
552
+
553
+ server.on('error', (err: NodeJS.ErrnoException) => {
554
+ console.error(`\n \x1b[31m${friendlyListenError(err, PORT, HOST)}\x1b[0m\n`);
555
+ process.exit(1);
556
+ });
557
+
558
+ const shutdown = () => {
559
+ unregisterSession(process.pid);
560
+ vite.close();
561
+ server.close();
562
+ };
563
+ process.once('SIGINT', shutdown);
564
+ process.once('SIGTERM', shutdown);
565
+ process.once('exit', () => unregisterSession(process.pid));
566
+ }
567
+
568
+ main().catch((err) => {
569
+ console.error('Failed to start packaged editor server:', err);
570
+ process.exit(1);
571
+ });
package/server/prod.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Editor production server — Express serves built frontend + API.
3
+ *
4
+ * Used by Electron or standalone production deployment.
5
+ * Run with:
6
+ * npx tsx packages/editor/server/prod.ts
7
+ *
8
+ * Supports VGAI_PROJECT env var for initial project path.
9
+ */
10
+
11
+ import path from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import express from 'express';
14
+ import { canonicalProjectRoot } from './canonical-path';
15
+ import { createEditorServer } from './editor-server';
16
+ import { friendlyListenError, resolveBindHost } from './server-utils';
17
+
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+ const PORT = Number(process.env['PORT']) || 5173;
20
+ // S3: bind to loopback by default; opt-in to a wider interface via env.
21
+ const HOST = resolveBindHost();
22
+ const engineRoot = process.env['VGAI_ENGINE_ROOT']
23
+ ? path.resolve(process.env['VGAI_ENGINE_ROOT'])
24
+ : path.resolve(__dirname, '../../..');
25
+ // Canonicalized — see canonical-path.ts's doc comment (dev.ts's sibling fix).
26
+ const projectPath = process.env['VGAI_PROJECT']
27
+ ? canonicalProjectRoot(process.env['VGAI_PROJECT'])
28
+ : undefined;
29
+
30
+ async function main(): Promise<void> {
31
+ const app = express();
32
+
33
+ // 1. Mount editor API routes
34
+ const editorRouter = createEditorServer({ engineRoot, projectPath, serveScripts: true });
35
+ app.use(editorRouter);
36
+
37
+ // 2. Serve built frontend from dist/
38
+ const distPath = path.resolve(engineRoot, 'dist');
39
+ app.use(express.static(distPath));
40
+
41
+ // Fallback: serve index.html for unmatched routes (SPA behavior)
42
+ app.use((_req, res) => {
43
+ res.sendFile(path.join(distPath, 'index.html'));
44
+ });
45
+
46
+ // 3. Start Express
47
+ const server = app.listen(PORT, HOST, () => {
48
+ const addr = server.address();
49
+ const actualPort = typeof addr === 'object' && addr ? addr.port : PORT;
50
+ console.log(`\n \x1b[32mProduction server\x1b[0m running at \x1b[36mhttp://${HOST}:${actualPort}\x1b[0m`);
51
+ if (projectPath) {
52
+ console.log(` \x1b[33mProject\x1b[0m: ${projectPath}`);
53
+ }
54
+ console.log();
55
+ // Machine-readable signal for Electron to detect server readiness
56
+ console.log(`EDITOR_SERVER_READY:${actualPort}`);
57
+ });
58
+
59
+ // SC5: friendly message instead of an opaque crash on port conflict.
60
+ server.on('error', (err: NodeJS.ErrnoException) => {
61
+ console.error(`\n \x1b[31m${friendlyListenError(err, PORT, HOST)}\x1b[0m\n`);
62
+ process.exit(1);
63
+ });
64
+
65
+ const shutdown = () => {
66
+ server.close();
67
+ };
68
+ process.once('SIGINT', shutdown);
69
+ process.once('SIGTERM', shutdown);
70
+ }
71
+
72
+ main().catch((err) => {
73
+ console.error('Failed to start production server:', err);
74
+ process.exit(1);
75
+ });