@fps-games/vfx 0.1.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/README.md +41 -0
  3. package/dist/effects/coin-burst/index.js +6 -2
  4. package/dist/effects/comet-trail-flame/index.js +3 -1
  5. package/dist/effects/debris-smoke/index.js +1 -0
  6. package/dist/effects/electric-arc/index.js +1 -0
  7. package/dist/effects/energy-shield/index.js +1 -0
  8. package/dist/effects/explosion/index.js +3 -2
  9. package/dist/effects/hit-flash/index.js +12 -4
  10. package/dist/effects/laser-beam/index.js +14 -2
  11. package/dist/effects/lumber_vehicle_upgrade_aura/index.js +1 -0
  12. package/dist/effects/monster-hit/index.js +2 -1
  13. package/dist/effects/player-upgrade/index.js +5 -3
  14. package/dist/effects/pulse-laser-volley/index.js +14 -4
  15. package/dist/effects/thruster-flame/index.js +3 -1
  16. package/dist/effects/treasure-attention/index.d.ts +54 -0
  17. package/dist/effects/treasure-attention/index.js +535 -0
  18. package/dist/effects/ufo-thread-flame/index.js +1 -0
  19. package/dist/effects/vehicle-upgrade/index.js +4 -1
  20. package/dist/effects/weapon-trail/index.js +8 -3
  21. package/dist/effects/wood_crusher_sawdust/index.d.ts +4 -0
  22. package/dist/effects/wood_cutter_sawdust/index.d.ts +4 -0
  23. package/dist/index.js +85 -17
  24. package/dist/types.d.ts +80 -0
  25. package/index.json +19 -1
  26. package/package.json +9 -4
  27. package/packages/effects/treasure-attention/vfx-params.json +49 -0
  28. package/tooling/vfx-cli.mjs +8 -1
  29. package/docs/AUTHORING_EFFECTS.md +0 -297
  30. package/docs/VFX_LIBRARY_ARCHITECTURE_chatgpt.md +0 -1580
  31. package/docs/VFX_LIBRARY_EXECUTION_PLAN.md +0 -229
  32. package/docs/VFX_LIBRARY_PLAN.md +0 -546
  33. package/docs/vfx-ab/RESULTS.md +0 -35
  34. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/after.png +0 -0
  35. package/docs/vfx-ab/lumber-order/vehicle-exhaust-smoke/before.png +0 -0
  36. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/after.png +0 -0
  37. package/docs/vfx-ab/lumber-truck/vehicle-exhaust-smoke/before.png +0 -0
  38. package/docs/vfx-integration_chatgpt.md +0 -462
  39. package/docs/weapon-trail-visualization.html +0 -241
  40. package/packages/EffectPackageService.ts +0 -240
  41. package/packages/SceneVfxService.ts +0 -96
  42. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/runtime/simpleMeshEffect.v1.js +0 -333
  43. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.frag.glsl +0 -25
  44. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/shaders/mesh-effect-preview.vert.glsl +0 -11
  45. package/packages/build.mesh.yellow_build_upgrade_indicator/assets/textures/effect_jianbian.png +0 -0
  46. package/packages/build.mesh.yellow_build_upgrade_indicator/effect.json +0 -927
  47. package/packages/build.mesh.yellow_build_upgrade_indicator/media/contact-sheet.png +0 -0
  48. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.gif +0 -0
  49. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.mp4 +0 -0
  50. package/packages/build.mesh.yellow_build_upgrade_indicator/media/preview.webm +0 -0
  51. package/packages/build.mesh.yellow_build_upgrade_indicator/media/thumbnail.png +0 -0
  52. package/packages/build.mesh.yellow_build_upgrade_indicator/metadata/description.json +0 -197
  53. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-effect.original.json +0 -895
  54. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/03-normalized-vfx-to-babylon-report.json +0 -241
  55. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-overrides.json +0 -542
  56. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/04-preview-tuning-report.json +0 -69
  57. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/asset-map.json +0 -125
  58. package/packages/build.mesh.yellow_build_upgrade_indicator/provenance/lineage.json +0 -44
  59. package/packages/build.particle.golden_upgrade_starburst/assets/runtime/particleEmitter.v1.js +0 -210
  60. package/packages/build.particle.golden_upgrade_starburst/assets/textures/effect_guangdian.png +0 -0
  61. package/packages/build.particle.golden_upgrade_starburst/effect.json +0 -880
  62. package/packages/build.particle.golden_upgrade_starburst/media/contact-sheet.png +0 -0
  63. package/packages/build.particle.golden_upgrade_starburst/media/preview.gif +0 -0
  64. package/packages/build.particle.golden_upgrade_starburst/media/preview.mp4 +0 -0
  65. package/packages/build.particle.golden_upgrade_starburst/media/preview.webm +0 -0
  66. package/packages/build.particle.golden_upgrade_starburst/media/thumbnail.png +0 -0
  67. package/packages/build.particle.golden_upgrade_starburst/metadata/description.json +0 -161
  68. package/packages/build.particle.golden_upgrade_starburst/provenance/03-effect.original.json +0 -762
  69. package/packages/build.particle.golden_upgrade_starburst/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  70. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-overrides.json +0 -436
  71. package/packages/build.particle.golden_upgrade_starburst/provenance/04-preview-tuning-report.json +0 -40
  72. package/packages/build.particle.golden_upgrade_starburst/provenance/asset-map.json +0 -115
  73. package/packages/build.particle.golden_upgrade_starburst/provenance/lineage.json +0 -40
  74. package/packages/build.particle.orange_upgrade_transform_sparks/assets/runtime/particleEmitter.v1.js +0 -210
  75. package/packages/build.particle.orange_upgrade_transform_sparks/assets/textures/b5b27ab1-e740-4398-b407-848fc2b2c897.png +0 -0
  76. package/packages/build.particle.orange_upgrade_transform_sparks/effect.json +0 -887
  77. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.gif +0 -0
  78. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.mp4 +0 -0
  79. package/packages/build.particle.orange_upgrade_transform_sparks/media/preview.webm +0 -0
  80. package/packages/build.particle.orange_upgrade_transform_sparks/media/thumbnail.png +0 -0
  81. package/packages/build.particle.orange_upgrade_transform_sparks/metadata/description.json +0 -179
  82. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-effect.original.json +0 -859
  83. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/03-normalized-vfx-to-babylon-report.json +0 -260
  84. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-overrides.json +0 -478
  85. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/04-preview-tuning-report.json +0 -40
  86. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/asset-map.json +0 -114
  87. package/packages/build.particle.orange_upgrade_transform_sparks/provenance/lineage.json +0 -41
  88. package/packages/build.particle.wood_chip_dust_burst/assets/runtime/particleEmitter.v1.js +0 -210
  89. package/packages/build.particle.wood_chip_dust_burst/assets/textures/effect_yanwu4.png +0 -0
  90. package/packages/build.particle.wood_chip_dust_burst/assets/textures/suipian3.png +0 -0
  91. package/packages/build.particle.wood_chip_dust_burst/effect.json +0 -1400
  92. package/packages/build.particle.wood_chip_dust_burst/media/contact-sheet.png +0 -0
  93. package/packages/build.particle.wood_chip_dust_burst/media/preview.gif +0 -0
  94. package/packages/build.particle.wood_chip_dust_burst/media/preview.mp4 +0 -0
  95. package/packages/build.particle.wood_chip_dust_burst/media/preview.webm +0 -0
  96. package/packages/build.particle.wood_chip_dust_burst/media/thumbnail.png +0 -0
  97. package/packages/build.particle.wood_chip_dust_burst/metadata/description.json +0 -173
  98. package/packages/build.particle.wood_chip_dust_burst/provenance/03-effect.original.json +0 -1277
  99. package/packages/build.particle.wood_chip_dust_burst/provenance/03-normalized-vfx-to-babylon-report.json +0 -329
  100. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-overrides.json +0 -749
  101. package/packages/build.particle.wood_chip_dust_burst/provenance/04-preview-tuning-report.json +0 -60
  102. package/packages/build.particle.wood_chip_dust_burst/provenance/asset-map.json +0 -146
  103. package/packages/build.particle.wood_chip_dust_burst/provenance/lineage.json +0 -69
  104. package/packages/effects/building-upgrade/effect.json +0 -6
  105. package/packages/effects/building-upgrade/index.ts +0 -387
  106. package/packages/effects/character-entrance/effect.json +0 -6
  107. package/packages/effects/character-entrance/index.ts +0 -308
  108. package/packages/effects/coin-burst/index.ts +0 -43
  109. package/packages/effects/comet-trail-flame/assets/textures/trail_5x4.webp +0 -0
  110. package/packages/effects/comet-trail-flame/index.ts +0 -106
  111. package/packages/effects/conveyor-upgrade/effect.json +0 -6
  112. package/packages/effects/conveyor-upgrade/index.ts +0 -401
  113. package/packages/effects/debris-smoke/assets/models/rock_debris.glb +0 -0
  114. package/packages/effects/debris-smoke/assets/textures/smoke22.webp +0 -0
  115. package/packages/effects/debris-smoke/index.ts +0 -101
  116. package/packages/effects/door_open_gold_flakes/assets/runtime/goldFlakes.v1.js +0 -397
  117. package/packages/effects/door_open_gold_flakes/door_open_gold_flakes.json +0 -498
  118. package/packages/effects/door_open_gold_flakes/index.ts +0 -11
  119. package/packages/effects/electric-arc/assets/textures/spark_4x4.webp +0 -0
  120. package/packages/effects/electric-arc/index.ts +0 -62
  121. package/packages/effects/energy-shield/effect.json +0 -7
  122. package/packages/effects/energy-shield/index.ts +0 -431
  123. package/packages/effects/explosion/assets/textures/baozha29.webp +0 -0
  124. package/packages/effects/explosion/assets/textures/glow01.webp +0 -0
  125. package/packages/effects/explosion/assets/textures/lizi01.webp +0 -0
  126. package/packages/effects/explosion/assets/textures/xulie_lizibaozha02_5x5.webp +0 -0
  127. package/packages/effects/explosion/index.ts +0 -114
  128. package/packages/effects/ground-tile-upgrade/effect.json +0 -6
  129. package/packages/effects/ground-tile-upgrade/index.ts +0 -410
  130. package/packages/effects/hit-flash/index.ts +0 -69
  131. package/packages/effects/laser-beam/assets/textures/laser_glow.webp +0 -0
  132. package/packages/effects/laser-beam/assets/textures/laser_noise.webp +0 -0
  133. package/packages/effects/laser-beam/index.ts +0 -6
  134. package/packages/effects/laser-beam-factory.ts +0 -135
  135. package/packages/effects/lumber-cart-suction/effect.json +0 -6
  136. package/packages/effects/lumber-cart-suction/index.ts +0 -399
  137. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb +0 -0
  138. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/.active +0 -1
  139. package/packages/effects/lumber-debris-scatter/assets/models/lumber_prop_model.glb.versions/1781589063890_upload.glb +0 -0
  140. package/packages/effects/lumber-debris-scatter/assets/runtime/solidParticleScatter.v1.js +0 -236
  141. package/packages/effects/lumber-debris-scatter/effect.json +0 -228
  142. package/packages/effects/lumber-debris-scatter/index.ts +0 -780
  143. package/packages/effects/lumber_vehicle_gear_spin/assets/runtime/gearSpin.v1.js +0 -202
  144. package/packages/effects/lumber_vehicle_gear_spin/index.ts +0 -11
  145. package/packages/effects/lumber_vehicle_gear_spin/lumber_vehicle_gear_spin.json +0 -338
  146. package/packages/effects/lumber_vehicle_upgrade_aura/assets/runtime/upgradeAura.v1.js +0 -353
  147. package/packages/effects/lumber_vehicle_upgrade_aura/index.ts +0 -11
  148. package/packages/effects/lumber_vehicle_upgrade_aura/lumber_vehicle_upgrade_aura.json +0 -365
  149. package/packages/effects/lumber_vehicle_wood_chips/assets/runtime/solidParticleScatter.v1.js +0 -310
  150. package/packages/effects/lumber_vehicle_wood_chips/index.ts +0 -11
  151. package/packages/effects/lumber_vehicle_wood_chips/lumber_vehicle_wood_chips.json +0 -335
  152. package/packages/effects/monster-hit/index.ts +0 -128
  153. package/packages/effects/player-upgrade/effect.json +0 -6
  154. package/packages/effects/player-upgrade/index.ts +0 -635
  155. package/packages/effects/pulse-laser-volley/assets/textures/bolt_guangshu.webp +0 -0
  156. package/packages/effects/pulse-laser-volley/index.ts +0 -204
  157. package/packages/effects/resource-collect-sparkles/effect.json +0 -6
  158. package/packages/effects/resource-collect-sparkles/index.ts +0 -254
  159. package/packages/effects/run-footstep-dust/assets/runtime/runFootstepDust.v1.js +0 -215
  160. package/packages/effects/run-footstep-dust/effect.json +0 -183
  161. package/packages/effects/run-footstep-dust/index.ts +0 -377
  162. package/packages/effects/space-shared.ts +0 -120
  163. package/packages/effects/thruster-flame/index.ts +0 -305
  164. package/packages/effects/tree-chop-dust/assets/textures/tree_chop_dust_atlas.png +0 -0
  165. package/packages/effects/tree-chop-dust/effect.json +0 -8
  166. package/packages/effects/tree-chop-dust/index.ts +0 -202
  167. package/packages/effects/tree-leaf-chop-burst/assets/runtime/particleEmitter.v1.js +0 -303
  168. package/packages/effects/tree-leaf-chop-burst/assets/textures/tree_leaf_chop_burst_leaf_atlas.png +0 -0
  169. package/packages/effects/tree-leaf-chop-burst/effect.json +0 -498
  170. package/packages/effects/tree-leaf-chop-burst/index.ts +0 -294
  171. package/packages/effects/tree_chop_dust_burst/assets/runtime/particleEmitter.v1.js +0 -318
  172. package/packages/effects/tree_chop_dust_burst/assets/textures/tree_chop_dust_burst_dust.png +0 -0
  173. package/packages/effects/tree_chop_dust_burst/index.ts +0 -13
  174. package/packages/effects/tree_chop_dust_burst/tree_chop_dust_burst.json +0 -412
  175. package/packages/effects/ufo-thread-flame/assets/textures/flame_flow.webp +0 -0
  176. package/packages/effects/ufo-thread-flame/assets/textures/flame_spike.webp +0 -0
  177. package/packages/effects/ufo-thread-flame/index.ts +0 -152
  178. package/packages/effects/vehicle-exhaust-smoke/assets/textures/vehicle_exhaust_smoke_atlas.png +0 -0
  179. package/packages/effects/vehicle-exhaust-smoke/effect.json +0 -8
  180. package/packages/effects/vehicle-exhaust-smoke/index.ts +0 -298
  181. package/packages/effects/vehicle-upgrade/effect.json +0 -6
  182. package/packages/effects/vehicle-upgrade/index.ts +0 -401
  183. package/packages/effects/weapon-trail/assets/textures/trail_wedge.png +0 -0
  184. package/packages/effects/weapon-trail/index.ts +0 -313
  185. package/packages/effects/wood-crusher-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  186. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  187. package/packages/effects/wood-crusher-sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  188. package/packages/effects/wood-crusher-sawdust/index.ts +0 -43
  189. package/packages/effects/wood-crusher-sawdust/wood_crusher_sawdust.json +0 -592
  190. package/packages/effects/wood-cutter-sawdust/assets/runtime/particleEmitter.v1.js +0 -312
  191. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  192. package/packages/effects/wood-cutter-sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  193. package/packages/effects/wood-cutter-sawdust/index.ts +0 -43
  194. package/packages/effects/wood-cutter-sawdust/wood_cutter_sawdust.json +0 -592
  195. package/packages/effects/wood-sawdust-effect-factory.ts +0 -194
  196. package/packages/effects/wood_crusher_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  197. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_dust.png +0 -0
  198. package/packages/effects/wood_crusher_sawdust/assets/textures/wood_crusher_sawdust_fiber.png +0 -0
  199. package/packages/effects/wood_crusher_sawdust/index.ts +0 -29
  200. package/packages/effects/wood_crusher_sawdust/wood_crusher_sawdust.json +0 -592
  201. package/packages/effects/wood_cutter_sawdust/assets/runtime/particleEmitter.v1.js +0 -318
  202. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_chip.png +0 -0
  203. package/packages/effects/wood_cutter_sawdust/assets/textures/wood_cutter_sawdust_dust.png +0 -0
  204. package/packages/effects/wood_cutter_sawdust/index.ts +0 -29
  205. package/packages/effects/wood_cutter_sawdust/wood_cutter_sawdust.json +0 -592
  206. package/packages/effects/wood_peel_material_test/assets/runtime/woodPeelMaterialTest.v1.js +0 -196
  207. package/packages/effects/wood_peel_material_test/index.ts +0 -11
  208. package/packages/effects/wood_peel_material_test/wood_peel_material_test.json +0 -136
  209. package/packages/env.particle.forest_edge_fog/assets/runtime/particleEmitter.v1.js +0 -210
  210. package/packages/env.particle.forest_edge_fog/assets/textures/f12a23c4-b924-4322-a260-3d982428f1e8.png +0 -0
  211. package/packages/env.particle.forest_edge_fog/assets/textures/yuan.png +0 -0
  212. package/packages/env.particle.forest_edge_fog/effect.json +0 -854
  213. package/packages/env.particle.forest_edge_fog/media/preview.gif +0 -0
  214. package/packages/env.particle.forest_edge_fog/media/preview.mp4 +0 -0
  215. package/packages/env.particle.forest_edge_fog/media/preview.webm +0 -0
  216. package/packages/env.particle.forest_edge_fog/media/thumbnail.png +0 -0
  217. package/packages/env.particle.forest_edge_fog/metadata/description.json +0 -148
  218. package/packages/env.particle.forest_edge_fog/provenance/03-effect.original.json +0 -827
  219. package/packages/env.particle.forest_edge_fog/provenance/03-normalized-vfx-to-babylon-report.json +0 -196
  220. package/packages/env.particle.forest_edge_fog/provenance/04-preview-overrides.json +0 -432
  221. package/packages/env.particle.forest_edge_fog/provenance/04-preview-tuning-report.json +0 -28
  222. package/packages/env.particle.forest_edge_fog/provenance/asset-map.json +0 -124
  223. package/packages/env.particle.forest_edge_fog/provenance/lineage.json +0 -41
  224. package/packages/generation.ts +0 -14
  225. package/packages/index.ts +0 -14
  226. package/packages/legacy-particle-runtime.ts +0 -113
  227. package/packages/manifestRuntimePackage.ts +0 -519
  228. package/packages/registry.ts +0 -85
  229. package/packages/rendering.ts +0 -13
  230. package/packages/resource.particle.viscous_oil_suction/assets/runtime/top_disk_suction/particleEmitter.v1.js +0 -424
  231. package/packages/resource.particle.viscous_oil_suction/assets/textures/Glow ad.png +0 -0
  232. package/packages/resource.particle.viscous_oil_suction/effect.json +0 -1254
  233. package/packages/resource.particle.viscous_oil_suction/media/preview.gif +0 -0
  234. package/packages/resource.particle.viscous_oil_suction/media/preview.mp4 +0 -0
  235. package/packages/resource.particle.viscous_oil_suction/media/preview.webm +0 -0
  236. package/packages/resource.particle.viscous_oil_suction/media/thumbnail.png +0 -0
  237. package/packages/resource.particle.viscous_oil_suction/metadata/description.json +0 -122
  238. package/packages/resource.particle.viscous_oil_suction/provenance/03-effect.original.json +0 -1051
  239. package/packages/resource.particle.viscous_oil_suction/provenance/03-normalized-vfx-to-babylon-report.json +0 -200
  240. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-overrides.json +0 -581
  241. package/packages/resource.particle.viscous_oil_suction/provenance/04-preview-tuning-report.json +0 -28
  242. package/packages/resource.particle.viscous_oil_suction/provenance/asset-map.json +0 -111
  243. package/packages/resource.particle.viscous_oil_suction/provenance/lineage.json +0 -45
  244. package/packages/types.ts +0 -107
  245. package/tooling/check-dist-runtime.mjs +0 -166
  246. package/tooling/generate-index.mjs +0 -55
  247. package/tooling/test-guardrails.mjs +0 -157
  248. package/tooling/validate-boundary.mjs +0 -92
  249. package/tooling/validate-effects.mjs +0 -304
  250. package/tooling/validate.mjs +0 -53
@@ -1,111 +0,0 @@
1
- {
2
- "schemaVersion": "vfx-asset-map/1.0",
3
- "generatedAt": "2026-05-21T13:04:29.644Z",
4
- "sourcePackage": "vfx-intermediate/cocos-babylon-packages/gas_town_oil_suction",
5
- "policy": {
6
- "runtimeAssetsRoot": "assets/",
7
- "sourceAssetsRoot": "source/",
8
- "note": "assets/ contains files loaded by Babylon runtime. source/cocos/assets/ preserves the original Cocos asset tree for traceability and reconversion."
9
- },
10
- "entries": [
11
- {
12
- "id": "glow_ad",
13
- "kind": "texture",
14
- "group": "textures",
15
- "role": "babylon-runtime",
16
- "runtimeUse": true,
17
- "packagePath": "assets/textures/Glow ad.png",
18
- "sourcePackagePath": "assets/textures/Glow ad.png",
19
- "sourceArchivePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/Glow ad.png",
20
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/Glow ad.png",
21
- "sourceId": "dd44b0a9-0998-4c24-ba2e-d6ac9d1bebb7@6c48a",
22
- "usedBy": [],
23
- "originalProjectPath": "assets/ArtResources/Effect/GasTown_OilSuction/Glow ad.png"
24
- },
25
- {
26
- "id": "asset",
27
- "kind": "material",
28
- "group": "materials",
29
- "role": "source-evidence",
30
- "runtimeUse": false,
31
- "packagePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/石油.mtl",
32
- "sourcePackagePath": "assets/materials/石油.mtl",
33
- "sourceArchivePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/石油.mtl",
34
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/石油.mtl",
35
- "sourceId": "4aabf2b0-1d20-4e13-9384-ae47eb8522aa",
36
- "usedBy": [],
37
- "originalProjectPath": "assets/ArtResources/Effect/GasTown_OilSuction/石油.mtl"
38
- },
39
- {
40
- "id": "particle_emitter_oil_suction_v1",
41
- "kind": "script",
42
- "group": "scripts",
43
- "role": "babylon-runtime",
44
- "runtimeUse": true,
45
- "packagePath": "assets/runtime/gas_town_oil_suction/particleEmitter.v1.js",
46
- "sourcePackagePath": "assets/runtime/gas_town_oil_suction/particleEmitter.v1.js",
47
- "sourceArchivePath": null,
48
- "sourcePath": null,
49
- "sourceId": null,
50
- "usedBy": []
51
- },
52
- {
53
- "id": "02-normalized-vfx",
54
- "kind": "raw",
55
- "group": "raw",
56
- "role": "source-evidence",
57
- "runtimeUse": false,
58
- "packagePath": "source/02-normalized-vfx.json",
59
- "sourcePackagePath": "source/02-normalized-vfx.json",
60
- "sourceArchivePath": null,
61
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/02-normalized-vfx.json",
62
- "sourceId": null,
63
- "usedBy": []
64
- },
65
- {
66
- "id": "01-source-dump",
67
- "kind": "raw",
68
- "group": "raw",
69
- "role": "source-evidence",
70
- "runtimeUse": false,
71
- "packagePath": "source/01-source-dump.json",
72
- "sourcePackagePath": "source/01-source-dump.json",
73
- "sourceArchivePath": null,
74
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/01-source-dump.json",
75
- "sourceId": null,
76
- "usedBy": []
77
- },
78
- {
79
- "id": "asset",
80
- "kind": "raw",
81
- "group": "raw",
82
- "role": "source-evidence",
83
- "runtimeUse": false,
84
- "packagePath": "source/raw/吸石油.prefab",
85
- "sourcePackagePath": "source/raw/吸石油.prefab",
86
- "sourceArchivePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab",
87
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab",
88
- "sourceId": "29954e7e-d137-4c8b-a730-4f0320cbece8",
89
- "usedBy": [],
90
- "originalProjectPath": "assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab"
91
- },
92
- {
93
- "id": "asset.meta",
94
- "kind": "raw",
95
- "group": "raw",
96
- "role": "source-evidence",
97
- "runtimeUse": false,
98
- "packagePath": "source/raw/吸石油.prefab.meta",
99
- "sourcePackagePath": "source/raw/吸石油.prefab.meta",
100
- "sourceArchivePath": "source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab.meta",
101
- "sourcePath": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source/cocos/assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab.meta",
102
- "sourceId": "29954e7e-d137-4c8b-a730-4f0320cbece8",
103
- "usedBy": [],
104
- "originalProjectPath": "assets/ArtResources/Effect/GasTown_OilSuction/吸石油.prefab.meta"
105
- }
106
- ],
107
- "gcsPrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction",
108
- "sourceArchivePrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source",
109
- "fullProvenancePrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/provenance",
110
- "canonicalSource": "gcs"
111
- }
@@ -1,45 +0,0 @@
1
- {
2
- "schemaVersion": "vfx-library-lineage/1.0",
3
- "savedAt": "2026-05-21T13:04:29.665Z",
4
- "sourcePackage": "vfx-intermediate/cocos-babylon-packages/gas_town_oil_suction",
5
- "sourceEffect": "03-effect.json",
6
- "libraryPackage": "packages/resource.particle.viscous_oil_suction",
7
- "effectId": "resource.particle.viscous_oil_suction",
8
- "media": {
9
- "previewVideo": "media/preview.webm",
10
- "previewWebm": "media/preview.webm",
11
- "previewMp4": "media/preview.mp4",
12
- "previewGif": "media/preview.gif",
13
- "thumbnail": "media/thumbnail.png",
14
- "gcsUri": null
15
- },
16
- "recording": {
17
- "provider": "browser-mediarecorder",
18
- "recordedAt": "2026-05-21T13:04:30.575Z",
19
- "mimeType": "video/webm;codecs=vp9",
20
- "durationSec": 10,
21
- "fps": 30,
22
- "media": {
23
- "previewVideo": "media/preview.webm",
24
- "previewWebm": "media/preview.webm",
25
- "previewMp4": "media/preview.mp4",
26
- "previewGif": "media/preview.gif",
27
- "thumbnail": "media/thumbnail.png",
28
- "gcsUri": null
29
- }
30
- },
31
- "description": {
32
- "provider": "litellm",
33
- "status": "described",
34
- "model": "gemini-3.1-pro-preview",
35
- "generatedAt": "2026-05-21T13:30:34.263Z"
36
- },
37
- "gcsPrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction",
38
- "sourceArchivePrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/source",
39
- "fullProvenancePrefix": "gs://fps-vfx-library/packages/resource.particle.viscous_oil_suction/provenance",
40
- "previousIds": [
41
- "world.particle.gas_town_oil_suction",
42
- "world.particle.viscous_oil_suction"
43
- ],
44
- "renamedAt": "2026-05-21T13:39:08.485Z"
45
- }
package/packages/types.ts DELETED
@@ -1,107 +0,0 @@
1
- import type { Scene } from '@babylonjs/core/scene';
2
- import type { Vector3 } from '@babylonjs/core/Maths/math.vector';
3
- import type { TransformNode } from '@babylonjs/core/Meshes/transformNode';
4
-
5
- export type VfxDebugAnchor = 'playerRight' | 'player';
6
-
7
- export type VfxParamValue = number | string | VfxColorValue;
8
- export type VfxParamValues = Record<string, VfxParamValue>;
9
-
10
- export interface VfxColorValue {
11
- r: number;
12
- g: number;
13
- b: number;
14
- }
15
-
16
- export type VfxParamKind =
17
- | 'number'
18
- | 'color'
19
- | 'enum'
20
- | 'opacity'
21
- | 'lifetime'
22
- | 'scale';
23
-
24
- export interface VfxParamDefinitionBase {
25
- key: string;
26
- label: string;
27
- kind: VfxParamKind;
28
- description?: string;
29
- }
30
-
31
- export interface VfxNumberParamDefinition extends VfxParamDefinitionBase {
32
- kind: 'number' | 'opacity' | 'lifetime' | 'scale';
33
- min?: number;
34
- max?: number;
35
- step?: number;
36
- }
37
-
38
- export interface VfxColorParamDefinition extends VfxParamDefinitionBase {
39
- kind: 'color';
40
- }
41
-
42
- export interface VfxEnumParamOption {
43
- value: string;
44
- label: string;
45
- }
46
-
47
- export interface VfxEnumParamDefinition extends VfxParamDefinitionBase {
48
- kind: 'enum';
49
- options: VfxEnumParamOption[];
50
- }
51
-
52
- export type VfxParamDefinition = VfxNumberParamDefinition | VfxColorParamDefinition | VfxEnumParamDefinition;
53
-
54
- export interface VfxEffectPackageContext<PParams extends VfxParamValues = VfxParamValues> {
55
- scene: Scene;
56
- root: TransformNode;
57
- position: Vector3;
58
- rotationY: number;
59
- renderingGroupId?: number;
60
- params: PParams;
61
- }
62
-
63
- export interface VfxEffectHandle {
64
- root: TransformNode;
65
- ready?: Promise<void>;
66
- dispose(): void;
67
- }
68
-
69
- export interface VfxEffectManifest {
70
- id: string;
71
- nameZh: string;
72
- assets?: string[];
73
- source?: string;
74
- }
75
-
76
- export interface VfxSpawnParamKeys {
77
- offsetX?: string;
78
- offsetY?: string;
79
- offsetZ?: string;
80
- }
81
-
82
- export interface VfxSpawnTransform {
83
- position?: Vector3;
84
- rotationY?: number;
85
- renderingGroupId?: number;
86
- }
87
-
88
- export interface VfxParamsFile {
89
- schemaVersion: string;
90
- effectId?: string;
91
- updatedAt?: string | null;
92
- params?: Partial<VfxParamValues>;
93
- }
94
-
95
- export interface VfxEffectPackage<PParams extends VfxParamValues = VfxParamValues> {
96
- id: string;
97
- nameZh: string;
98
- manifest?: VfxEffectManifest;
99
- defaultParams: PParams;
100
- debugAnchor?: VfxDebugAnchor;
101
- spawnParams?: VfxSpawnParamKeys;
102
- savedParams?: Partial<VfxParamValues>;
103
- debugParams: VfxParamDefinition[];
104
- create(context: VfxEffectPackageContext<PParams>): VfxEffectHandle;
105
- }
106
-
107
- export type VfxDebugOverrides = Record<string, Partial<VfxParamValues>>;
@@ -1,166 +0,0 @@
1
- #!/usr/bin/env node
2
- // 新增特效 guardrail · Layer 2(构建产物禁运行时扫描,见 issue #3,最强一层)。
3
- // `pnpm build` 之后扫 dist/,镜像消费方 Playable bundle 策略(lumber-order
4
- // scripts/check-playable-asset-bundle.mjs 的 forbiddenTokens),把"会坑消费方"的重运行时
5
- // 提前到库侧拦住 —— 即便它是被某个特效"间接"拉进来的(顺着 dist 的 chunk import 图归因到特效)。
6
- //
7
- // 用法:
8
- // node tooling/check-dist-runtime.mjs # 扫 dist/
9
- // node tooling/check-dist-runtime.mjs --dist <dir> # 换扫描目录(回归 fixture 用)
10
- // 退出码:命中 → 1;干净 → 0。
11
- import { readdirSync, statSync, readFileSync, existsSync } from 'node:fs';
12
- import { resolve, dirname, join, relative } from 'node:path';
13
- import { fileURLToPath } from 'node:url';
14
-
15
- const LIB_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
16
- const argv = process.argv.slice(2);
17
- const distArg = argv.indexOf('--dist') >= 0 ? argv[argv.indexOf('--dist') + 1] : undefined;
18
- const DIST = resolve(distArg ?? join(LIB_ROOT, 'dist'));
19
-
20
- // 精确 token —— 类名/扩展名/MIME,绝不用宽松子串(如 `basis` 会误报 RingBasis 变量)。
21
- // 与 lumber-order check-playable-asset-bundle.mjs 的 forbiddenTokens 对齐 + 补 draco/ktx2。
22
- const SUBSTRING_TOKENS = [
23
- 'EXT_meshopt_compression', 'MeshoptDecoder', 'meshopt_decoder',
24
- 'KHR_draco_mesh_compression', 'DracoCompression', 'DracoDecoder', 'draco_decoder', 'draco_wasm_wrapper',
25
- 'KHR_texture_basisu', 'KhronosTextureContainer2', '_KTX2Decoder', 'MSCTranscoder', 'basis_transcoder',
26
- 'application/x-glb-brotli', 'brotli-dec-wasm',
27
- 'application/wasm', 'data:application/wasm',
28
- 'WebAssembly.Module', 'WebAssembly.instantiate',
29
- ];
30
- // `.wasm` 资源 URL:要求路径分隔符(/),只拦真资源路径如 "./decoder.wasm"/"chunks/x.wasm",
31
- // 不误报纯文件名字符串(错误消息 "physics.wasm" / debug 标签 "ringBurst.wasm" 无 /)。
32
- const WASM_URL_RE = /['"][^'"]*\/[^'"/]*\.wasm(\?[^'"]*)?['"]/;
33
-
34
- // 与 Layer 1 一致:扫描前剥注释(避免 dist 里残留的解释性注释/banner 误报)。
35
- function stripComments(src) {
36
- return src.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(?<!:)\/\/[^\n]*/g, '');
37
- }
38
-
39
- function tokensIn(rawContent) {
40
- const content = stripComments(rawContent);
41
- const hits = [];
42
- for (const t of SUBSTRING_TOKENS) if (content.includes(t)) hits.push(t);
43
- const m = content.match(WASM_URL_RE);
44
- if (m) hits.push(m[0]);
45
- return hits;
46
- }
47
-
48
- // dist 模块文件:.js + .mjs/.cjs(防止重运行时藏在非 .js chunk 里绕过)。
49
- const DIST_MODULE_EXT = /\.(js|mjs|cjs)$/;
50
- function jsFilesIn(dir, out = []) {
51
- if (!existsSync(dir)) return out;
52
- for (const e of readdirSync(dir)) {
53
- const p = join(dir, e);
54
- const s = statSync(p);
55
- if (s.isDirectory()) jsFilesIn(p, out);
56
- else if (DIST_MODULE_EXT.test(e)) out.push(p);
57
- }
58
- return out;
59
- }
60
-
61
- // 解析一个 dist 模块的相对 import(构 chunk 依赖图;裸包如 @babylonjs/* 不进图)。
62
- function relImports(file) {
63
- const src = readFileSync(file, 'utf8');
64
- const specs = new Set();
65
- let m;
66
- const re = /\bfrom\s*['"]([^'"]+)['"]/g;
67
- while ((m = re.exec(src))) specs.add(m[1]);
68
- const side = /\bimport\s*['"]([^'"]+)['"]/g;
69
- while ((m = side.exec(src))) specs.add(m[1]);
70
- const dyn = /\bimport\(\s*['"]([^'"]+)['"]\s*\)/g;
71
- while ((m = dyn.exec(src))) specs.add(m[1]);
72
- const resolved = [];
73
- for (const spec of specs) {
74
- if (!spec.startsWith('.')) continue;
75
- const target = resolve(dirname(file), spec);
76
- const cands = [target, target + '.js', target + '.mjs', target + '.cjs',
77
- join(target, 'index.js'), join(target, 'index.mjs'), join(target, 'index.cjs')];
78
- for (const cand of cands) {
79
- if (existsSync(cand) && statSync(cand).isFile()) { resolved.push(cand); break; }
80
- }
81
- }
82
- return resolved;
83
- }
84
-
85
- // dist/<dir>/index.{js,mjs,cjs} 取第一个存在的入口。
86
- function indexEntry(dir) {
87
- for (const ext of ['js', 'mjs', 'cjs']) {
88
- const p = join(dir, `index.${ext}`);
89
- if (existsSync(p)) return p;
90
- }
91
- return null;
92
- }
93
-
94
- function reachableFrom(entry, importGraph) {
95
- const seen = new Set();
96
- const stack = [entry];
97
- while (stack.length) {
98
- const f = stack.pop();
99
- if (seen.has(f)) continue;
100
- seen.add(f);
101
- for (const dep of importGraph.get(f) ?? []) if (!seen.has(dep)) stack.push(dep);
102
- }
103
- return seen;
104
- }
105
-
106
- // ── 主流程 ────────────────────────────────────────────────────────────────────
107
- if (!existsSync(DIST)) {
108
- console.error(`✗ dist 不存在:${DIST}(先 \`pnpm build\`)。`);
109
- process.exit(1);
110
- }
111
-
112
- const allJs = jsFilesIn(DIST);
113
- const importGraph = new Map(allJs.map((f) => [f, relImports(f)]));
114
- const tokenByFile = new Map();
115
- for (const f of allJs) {
116
- const hits = tokensIn(readFileSync(f, 'utf8'));
117
- if (hits.length) tokenByFile.set(f, [...new Set(hits)]);
118
- }
119
-
120
- // 入口 = dist/index.{js,mjs,cjs}(runtime 核心)+ 每个 dist/effects/<id>/index.{js,mjs,cjs}。
121
- const entries = [];
122
- const rootIndex = indexEntry(DIST);
123
- if (rootIndex) entries.push({ label: 'runtime (index)', entry: rootIndex });
124
- const effectsDist = join(DIST, 'effects');
125
- if (existsSync(effectsDist)) {
126
- for (const id of readdirSync(effectsDist)) {
127
- const idx = indexEntry(join(effectsDist, id));
128
- if (idx) entries.push({ label: id, entry: idx });
129
- }
130
- }
131
-
132
- // 归因:每个入口 → 它可达的、命中 token 的文件。
133
- const findings = []; // {label, file, tokens}
134
- const attributedFiles = new Set();
135
- for (const { label, entry } of entries) {
136
- const reach = reachableFrom(entry, importGraph);
137
- for (const f of reach) {
138
- if (tokenByFile.has(f)) {
139
- attributedFiles.add(f);
140
- findings.push({ label, file: f, tokens: tokenByFile.get(f) });
141
- }
142
- }
143
- }
144
- // 没被任何入口可达但仍命中的文件(孤儿,理论上不该有)。
145
- for (const [f, tokens] of tokenByFile) {
146
- if (!attributedFiles.has(f)) findings.push({ label: '(未归因/孤儿 chunk)', file: f, tokens });
147
- }
148
-
149
- if (findings.length === 0) {
150
- console.log(`✅ Layer 2 dist 禁运行时扫描通过:${allJs.length} 个 js,0 命中(无 meshopt/draco/ktx2/brotli/wasm 运行时)。`);
151
- process.exit(0);
152
- }
153
-
154
- console.log(`❌ Layer 2 dist 禁运行时扫描:发现 ${findings.length} 处命中。\n`);
155
- // 按特效聚合展示
156
- const byLabel = {};
157
- for (const f of findings) (byLabel[f.label] ??= []).push(f);
158
- for (const [label, items] of Object.entries(byLabel)) {
159
- console.log(` 特效/入口:${label}`);
160
- for (const it of items) {
161
- console.log(` ✗ ${relative(DIST, it.file)} ← ${it.tokens.join(', ')}`);
162
- }
163
- }
164
- console.log('\n修复:定位拉入该运行时的特效(多半是全量 glTF loader / 含 meshopt|draco|ktx2 的 GLB)。');
165
- console.log(' 改用核心 loader、把 GLB 预解压成 plain、或用 ?url 内联的轻量资源。详见 Layer 1 提示与 #2 v0.1.1。');
166
- process.exit(1);
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env node
2
- // 生成顶层 index.json:扫 packages/effects/<id>/,汇总 id/nameZh/kind/category/tags/media。
3
- // 富检索字段(category/tags/media)优先取 description.json,避免与 manifest 双源。
4
- import { readdirSync, statSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
5
- import { resolve, dirname, join } from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
-
8
- const LIB_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
9
- const EFFECTS_DIR = join(LIB_ROOT, 'packages', 'effects');
10
-
11
- function readJsonSafe(p) {
12
- try { return JSON.parse(readFileSync(p, 'utf8')); } catch { return null; }
13
- }
14
-
15
- const effects = [];
16
- for (const entry of readdirSync(EFFECTS_DIR)) {
17
- const dir = join(EFFECTS_DIR, entry);
18
- if (!statSync(dir).isDirectory()) continue;
19
- const indexPath = join(dir, 'index.ts');
20
- if (!existsSync(indexPath)) continue;
21
-
22
- const effectJson = readJsonSafe(join(dir, 'effect.json')) ?? {};
23
- const desc = readJsonSafe(join(dir, 'description.json')) ?? {};
24
- const indexSrc = readFileSync(indexPath, 'utf8');
25
- const kind = /createManifestRuntimeEffectPackage|createDataDrivenEffectPackage/.test(indexSrc) ? 'data' : 'code';
26
-
27
- const mediaDir = join(dir, 'media');
28
- const media = {};
29
- if (existsSync(mediaDir)) {
30
- for (const f of readdirSync(mediaDir)) {
31
- if (/thumbnail/.test(f)) media.thumbnail = `packages/effects/${entry}/media/${f}`;
32
- else if (/preview\.(mp4|webm|gif)$/.test(f)) media.preview = `packages/effects/${entry}/media/${f}`;
33
- }
34
- }
35
-
36
- effects.push({
37
- id: effectJson.id || entry,
38
- dir: entry,
39
- nameZh: effectJson.nameZh || desc.searchTitleZh || entry,
40
- kind,
41
- category: desc.semanticTags?.[0] || (effectJson.id?.split('.')[0]) || null,
42
- tags: desc.searchKeywordsEn || desc.visualTags || effectJson.search?.tags || [],
43
- media,
44
- path: `packages/effects/${entry}`,
45
- });
46
- }
47
- effects.sort((a, b) => a.id.localeCompare(b.id));
48
-
49
- const out = {
50
- schemaVersion: 'vfx-index/1.0',
51
- count: effects.length,
52
- effects,
53
- };
54
- writeFileSync(join(LIB_ROOT, 'index.json'), JSON.stringify(out, null, 2) + '\n');
55
- console.log(`✅ index.json 生成:${effects.length} 个特效。`);
@@ -1,157 +0,0 @@
1
- #!/usr/bin/env node
2
- // guardrail 回归用例(issue #3 验收最后一条):
3
- // "故意造一个引全量 glTF 的特效 → 被 Layer 1 + Layer 2 双重拦下"。
4
- // 全程 hermetic(写临时目录,不碰 packages/effects 与真实 dist),跑完即清理。
5
- // Layer 1:对一批坏 fixture 跑 validate-effects,断言每个都被拒(exit≠0)+ 对干净 fixture 断言放行。
6
- // Layer 2:造一个含 meshopt token 的假 dist chunk(= 全量 glTF loader 会拉进来的东西),
7
- // 断言 check-dist-runtime 命中并归因到引它的特效;且 `basis` 变量名不被误报。
8
- // 用法:node tooling/test-guardrails.mjs 退出码:全过 0,任一断言失败 1。
9
- import { spawnSync } from 'node:child_process';
10
- import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
11
- import { tmpdir } from 'node:os';
12
- import { resolve, dirname, join } from 'node:path';
13
- import { fileURLToPath } from 'node:url';
14
-
15
- const LIB_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
16
- const tmp = mkdtempSync(join(tmpdir(), 'vfx-guardrail-'));
17
- let failures = 0;
18
-
19
- function runValidateEffects(effectsDir) {
20
- return spawnSync('node', ['tooling/validate-effects.mjs', '--effects-dir', effectsDir], { cwd: LIB_ROOT, encoding: 'utf8' });
21
- }
22
- function runCheckDist(distDir) {
23
- return spawnSync('node', ['tooling/check-dist-runtime.mjs', '--dist', distDir], { cwd: LIB_ROOT, encoding: 'utf8' });
24
- }
25
- function assert(cond, msg, detail = '') {
26
- if (cond) { console.log(` ✅ ${msg}`); }
27
- else { console.log(` ❌ ${msg}${detail ? `\n ${detail.replace(/\n/g, '\n ')}` : ''}`); failures += 1; }
28
- }
29
- function write(p, content) { mkdirSync(dirname(p), { recursive: true }); writeFileSync(p, content); }
30
-
31
- try {
32
- // ── Layer 1 ────────────────────────────────────────────────────────────────
33
- console.log('Layer 1(validate-effects)回归:');
34
- const fxRoot = join(tmp, 'effects');
35
-
36
- // 坏 1:引全量 glTF barrel(meshopt 事故的根因)
37
- write(join(fxRoot, 'full-gltf', 'index.ts'),
38
- `import '@babylonjs/loaders/glTF';\nexport const e = { id:'full-gltf', nameZh:'x', defaultParams:{}, debugParams:[], create(){ return { root:{}, dispose(){} }; } };\n`);
39
- // 坏 2:new URL 资源 + 缺资源
40
- write(join(fxRoot, 'new-url', 'index.ts'),
41
- `const u = new URL('./assets/a.bin', import.meta.url);\nimport tex from './assets/missing.png?url';\nexport const e = { id:'new-url', nameZh:'x', defaultParams:{}, debugParams:[], create(){ return { root:{}, dispose(){} }; } };\n`);
42
- // 坏 3:直接引解码器类(间接 token)
43
- write(join(fxRoot, 'decoder-token', 'index.ts'),
44
- `import { MeshoptDecoder } from '@babylonjs/core/Meshes/Compression/meshoptCompression';\nexport const e = { id:'decoder-token', nameZh:'x', defaultParams:{}, debugParams:[], create(){ MeshoptDecoder.UseInstances = true; return { root:{}, dispose(){} }; } };\n`);
45
- // 坏 4:不是合法 package
46
- write(join(fxRoot, 'not-a-package', 'index.ts'), `export const notAPackage = { hello: 'world' };\n`);
47
- // 干净:核心 loader + ?url 存在 + 合法契约 + 注释里提 meshopt 不应误报
48
- write(join(fxRoot, 'clean', 'assets', 'm.glb'), 'GLB');
49
- write(join(fxRoot, 'clean', 'index.ts'),
50
- `// 用核心 loader,避免 EXT_meshopt_compression / MeshoptDecoder 被拉进包\nimport '@babylonjs/loaders/glTF/2.0/glTFLoader';\nimport url from './assets/m.glb?url';\nexport const e = { id:'clean', nameZh:'干净', defaultParams:{ a:1 }, debugParams:[{key:'a',label:'a',kind:'number'}], create(){ return { root:{}, dispose(){} }; } };\n`);
51
- write(join(fxRoot, 'clean', 'vfx-params.json'), `{ "schemaVersion":"vfx-params/1.0","effectId":"clean","params":{} }`);
52
-
53
- // 一次扫整根:坏的应被拒
54
- const allRes = runValidateEffects(fxRoot);
55
- assert(allRes.status !== 0, '坏 fixture 集合被 Layer 1 拒绝(exit≠0)', allRes.stdout);
56
- for (const [bad, needle] of [
57
- ['full-gltf', '@babylonjs/loaders/glTF'],
58
- ['new-url', 'new URL'],
59
- ['decoder-token', 'MeshoptDecoder'],
60
- ['not-a-package', '未检测到合法 VfxEffectPackage'],
61
- ]) {
62
- assert(allRes.stdout.includes(bad) && allRes.stdout.includes(needle), `命中坏 fixture「${bad}」并给出原因`, allRes.stdout);
63
- }
64
- // 规避向量回归(对抗式审查发现并已堵住的绕过手法,每个都必须仍被拦):
65
- const evasion = join(tmp, 'evasion');
66
- // 把全量 barrel + MeshoptDecoder 藏在 helper .js 里(非 .ts sibling)
67
- write(join(evasion, 'sibling-js', 'index.ts'), `import './heavy.js';\nexport const e={id:'x',nameZh:'x',defaultParams:{},debugParams:[],create(){return{root:{},dispose(){}};}};\n`);
68
- write(join(evasion, 'sibling-js', 'heavy.js'), `import '@babylonjs/loaders/glTF';\nexport const z = MeshoptDecoder;\n`);
69
- // 无空格副作用导入
70
- write(join(evasion, 'nospace', 'index.ts'), `import'@babylonjs/loaders/glTF';\nexport const e={id:'x',nameZh:'x',defaultParams:{},debugParams:[],create(){return{root:{},dispose(){}};}};\n`);
71
- // 模板字符串动态 import
72
- write(join(evasion, 'tmpl-dyn', 'index.ts'), "export const e={id:'x',nameZh:'x',defaultParams:{},debugParams:[],async create(){await import(`@babylonjs/loaders/glTF`);return{root:{},dispose(){}};}};\n");
73
- // 字符串拼接动态 import
74
- write(join(evasion, 'concat', 'index.ts'), `export const e={id:'x',nameZh:'x',defaultParams:{},debugParams:[],async create(){await import('@babylonjs/loaders'+'/glTF');return{root:{},dispose(){}};}};\n`);
75
- // 模板字符串 new URL
76
- write(join(evasion, 'tmpl-url', 'index.ts'), "const u=new URL(`./a.glb`,import.meta.url);\nexport const e={id:'x',nameZh:'x',defaultParams:{},debugParams:[],create(){return{root:{},dispose(){}};}};\n");
77
- const evRes = runValidateEffects(evasion);
78
- assert(evRes.status !== 0, '规避向量集合被 Layer 1 拒绝(exit≠0)', evRes.stdout);
79
- for (const id of ['sibling-js', 'nospace', 'tmpl-dyn', 'concat', 'tmpl-url']) {
80
- assert(evRes.stdout.includes(id), `规避向量「${id}」被拦下`, evRes.stdout);
81
- }
82
-
83
- // 契约误报回归:箭头 create / 泛型 create / re-export barrel 都应放行(0 error)
84
- const contractOk = join(tmp, 'contract-ok');
85
- write(join(contractOk, 'arrow', 'index.ts'), `export const e={id:'x',nameZh:'测',defaultParams:{a:1},debugParams:[{key:'a',label:'a',kind:'number'}],create:(ctx)=>({root:ctx.root,dispose(){}})};\n`);
86
- write(join(contractOk, 'arrow', 'vfx-params.json'), `{"schemaVersion":"vfx-params/1.0","effectId":"arrow","params":{}}`);
87
- write(join(contractOk, 'barrel', 'index.ts'), `export * from './impl';\n`);
88
- write(join(contractOk, 'barrel', 'impl.ts'), `export const e={id:'x',nameZh:'测',defaultParams:{},debugParams:[],create(){return{root:{},dispose(){}};}};\n`);
89
- write(join(contractOk, 'barrel', 'vfx-params.json'), `{"schemaVersion":"vfx-params/1.0","effectId":"barrel","params":{}}`);
90
- const contractRes = runValidateEffects(contractOk);
91
- assert(contractRes.status === 0, '箭头 create / re-export barrel 合法写法被放行(无契约误报)', contractRes.stdout);
92
-
93
- // 面板↔参数 key 一致性:debugParams 旋钮的 key 不在 defaultParams → 拦;对齐 → 放行
94
- const keyBad = join(tmp, 'key-bad');
95
- write(join(keyBad, 'orphan-knob', 'index.ts'),
96
- `const DEFAULT_PARAMS = { lifetime: 1, scale: 2 };\nexport const e = { id:'orphan-knob', nameZh:'测', defaultParams: DEFAULT_PARAMS, debugParams: [ {key:'lifetime',label:'a',kind:'lifetime'}, {key:'scale',label:'b',kind:'scale'}, {key:'partcileCount',label:'拼错',kind:'number'} ], create(){ return { root:{}, dispose(){} }; } };\n`);
97
- write(join(keyBad, 'orphan-knob', 'vfx-params.json'), `{"schemaVersion":"vfx-params/1.0","effectId":"orphan-knob","params":{}}`);
98
- const keyBadRes = runValidateEffects(keyBad);
99
- assert(keyBadRes.status !== 0 && /partcileCount/.test(keyBadRes.stdout), 'debugParams 旋钮 key 无对应 defaultParams 被拦(面板↔参数一致性)', keyBadRes.stdout);
100
-
101
- const keyOk = join(tmp, 'key-ok');
102
- write(join(keyOk, 'aligned', 'index.ts'),
103
- `const DEFAULT_PARAMS = { lifetime: 1, count: 8 };\nexport const e = { id:'aligned', nameZh:'测', defaultParams: DEFAULT_PARAMS, debugParams: [ {key:'lifetime',label:'a',kind:'lifetime'}, {key:'count',label:'b',kind:'number'} ], create(){ return { root:{}, dispose(){} }; } };\n`);
104
- write(join(keyOk, 'aligned', 'vfx-params.json'), `{"schemaVersion":"vfx-params/1.0","effectId":"aligned","params":{}}`);
105
- const keyOkRes = runValidateEffects(keyOk);
106
- assert(keyOkRes.status === 0, 'debugParams.key ⊆ defaultParams.key 时放行', keyOkRes.stdout);
107
-
108
- // 干净 fixture 单独扫应放行
109
- const cleanRoot = join(tmp, 'effects-clean');
110
- write(join(cleanRoot, 'clean', 'assets', 'm.glb'), 'GLB');
111
- write(join(cleanRoot, 'clean', 'index.ts'),
112
- `// 注释提到 EXT_meshopt_compression / MeshoptDecoder 不应误报\nimport '@babylonjs/loaders/glTF/2.0/glTFLoader';\nimport url from './assets/m.glb?url';\nexport const e = { id:'clean', nameZh:'干净', defaultParams:{ a:1 }, debugParams:[{key:'a',label:'a',kind:'number'}], create(){ return { root:{}, dispose(){} }; } };\n`);
113
- write(join(cleanRoot, 'clean', 'vfx-params.json'), `{ "schemaVersion":"vfx-params/1.0","effectId":"clean","params":{} }`);
114
- const cleanRes = runValidateEffects(cleanRoot);
115
- assert(cleanRes.status === 0, '干净 fixture 被放行(注释里的 meshopt 字样不误报)', cleanRes.stdout);
116
-
117
- // ── Layer 2 ────────────────────────────────────────────────────────────────
118
- console.log('\nLayer 2(check-dist-runtime)回归:');
119
- const fakeDist = join(tmp, 'dist');
120
- // 共享 heavy chunk = 全量 glTF loader 会拉进来的 meshopt 运行时
121
- write(join(fakeDist, 'chunks', 'heavy-aaa.js'),
122
- `export const EXT = "EXT_meshopt_compression";\nexport function setup(){ MeshoptDecoder.UseInstances = true; }\n`);
123
- // light chunk:含 `basis` 变量名(必须不被误报)
124
- write(join(fakeDist, 'chunks', 'light-bbb.js'), `export const ringBasis = "a normal basis variable";\n`);
125
- write(join(fakeDist, 'index.js'), `import { ringBasis } from "./chunks/light-bbb.js";\nconsole.log(ringBasis);\n`);
126
- write(join(fakeDist, 'effects', 'bad-effect', 'index.js'), `import { setup } from "../../chunks/heavy-aaa.js";\nsetup();\n`);
127
- write(join(fakeDist, 'effects', 'clean-effect', 'index.js'), `import { ringBasis } from "../../chunks/light-bbb.js";\nconsole.log(ringBasis);\n`);
128
- const distRes = runCheckDist(fakeDist);
129
- assert(distRes.status !== 0, '含 meshopt 的假 dist 被 Layer 2 拦下(exit≠0)', distRes.stdout);
130
- assert(distRes.stdout.includes('bad-effect') && /meshopt/i.test(distRes.stdout), '命中并归因到引入它的特效「bad-effect」', distRes.stdout);
131
- assert(!distRes.stdout.includes('clean-effect'), '只引 light chunk 的「clean-effect」不被误报', distRes.stdout);
132
- assert(!/✗.*basis['"]?\s*$/m.test(distRes.stdout) && !distRes.stdout.includes('light-bbb'), '`basis` 变量名不触发误报', distRes.stdout);
133
-
134
- // Layer 2 鲁棒性回归:meshopt 藏 .mjs chunk 仍被拦
135
- const distMjs = join(tmp, 'dist-mjs');
136
- write(join(distMjs, 'chunks', 'heavy.mjs'), `export const x="EXT_meshopt_compression"; MeshoptDecoder.foo();\n`);
137
- write(join(distMjs, 'effects', 'evil', 'index.js'), `import '../../chunks/heavy.mjs';\n`);
138
- const mjsRes = runCheckDist(distMjs);
139
- assert(mjsRes.status !== 0 && mjsRes.stdout.includes('evil'), 'meshopt 藏 .mjs chunk 仍被 Layer 2 拦下并归因', mjsRes.stdout);
140
-
141
- // Layer 2 假阳性回归:.wasm 错误消息(无 /)+ 注释里的 token 都不应误报
142
- const distFp = join(tmp, 'dist-fp');
143
- write(join(distFp, 'effects', 'a', 'index.js'), `export function c(){ throw new Error("dropped optional physics.wasm"); }\n`);
144
- write(join(distFp, 'effects', 'b', 'index.js'), `/** intentionally avoids MeshoptDecoder and DracoCompression */\nexport const e=1;\n`);
145
- const fpRes = runCheckDist(distFp);
146
- assert(fpRes.status === 0, '.wasm 文件名错误消息 + 注释里的禁 token 不触发 Layer 2 误报', fpRes.stdout);
147
- } finally {
148
- rmSync(tmp, { recursive: true, force: true });
149
- }
150
-
151
- console.log('');
152
- if (failures === 0) {
153
- console.log('✅ guardrail 回归全部通过(全量 glTF 特效被 Layer 1 + Layer 2 双重拦下;无误报)。');
154
- process.exit(0);
155
- }
156
- console.log(`❌ guardrail 回归:${failures} 条断言失败。`);
157
- process.exit(1);