cluaupp 1.5.0 → 1.5.1

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 (323) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +10 -5
  3. package/api/CHANGELOG.md +3 -0
  4. package/api/chat.js +74 -0
  5. package/api/fixtures/roblox/README.md +6 -0
  6. package/api/fixtures/roblox/aux/sample.d.luau +6 -0
  7. package/api/fixtures/roblox/mini/Mini-API-Dump.json +72 -0
  8. package/api/generated/bench-latest.json +39 -0
  9. package/api/generated/capability-profile.json +76 -0
  10. package/api/generated/lsp-index.json +1 -0
  11. package/api/generated/luau/classes.d.luau +11221 -0
  12. package/api/generated/luau/datatypes.d.luau +327 -0
  13. package/api/generated/luau/enums.d.luau +4918 -0
  14. package/api/generated/luau/globals.d.luau +12 -0
  15. package/api/manifests/roblox-target.manifest.json +22 -0
  16. package/api/normalized/roblox-target.profile.json +237461 -0
  17. package/api/overrides/classes.json +8 -0
  18. package/api/overrides/compatibility.json +9 -0
  19. package/api/overrides/datatypes.json +1579 -0
  20. package/api/overrides/enums.json +8 -0
  21. package/api/overrides/globals.json +8 -0
  22. package/api/overrides/type-mappings.json +9 -0
  23. package/api/roblox-api.lock.json +9 -0
  24. package/data/Mini-API-Dump.json +1 -0
  25. package/editors/vscode/README.md +12 -10
  26. package/editors/vscode/extension.js +82 -0
  27. package/editors/vscode/icons/axiom.svg +4 -0
  28. package/editors/vscode/icons/bloom.svg +5 -0
  29. package/editors/vscode/icons/helm.svg +4 -0
  30. package/editors/vscode/icons/hive.svg +5 -0
  31. package/editors/vscode/icons/mint.svg +4 -0
  32. package/editors/vscode/icons/shift.svg +6 -0
  33. package/editors/vscode/language-configuration-schema.json +24 -0
  34. package/editors/vscode/package.json +172 -12
  35. package/editors/vscode/snippets/axiom.json +12 -0
  36. package/editors/vscode/snippets/bloom.json +12 -0
  37. package/editors/vscode/snippets/helm.json +12 -0
  38. package/editors/vscode/snippets/hive.json +12 -0
  39. package/editors/vscode/snippets/mint.json +12 -0
  40. package/editors/vscode/snippets/shift.json +17 -0
  41. package/editors/vscode/syntaxes/axiom.tmLanguage.json +5 -0
  42. package/editors/vscode/syntaxes/bloom.tmLanguage.json +5 -0
  43. package/editors/vscode/syntaxes/helm.tmLanguage.json +5 -0
  44. package/editors/vscode/syntaxes/hive.tmLanguage.json +5 -0
  45. package/editors/vscode/syntaxes/mint.tmLanguage.json +5 -0
  46. package/editors/vscode/syntaxes/schema.tmLanguage.json +89 -0
  47. package/editors/vscode/syntaxes/shift.tmLanguage.json +5 -0
  48. package/examples/game/.cluaupp/compile-cache/ReplicatedStorage__Shared__Constants__Datas__TemplateData.clh.json +1 -0
  49. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Boot__DataBoot.server.clpp.json +1 -0
  50. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Configuration__ServerConfig.clp.json +1 -0
  51. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Handlers__Guard.server.clpp.json +1 -0
  52. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Handlers__PlayerHandler.server.clpp.json +1 -0
  53. package/examples/game/.cluaupp/compile-cache/StarterPlayer__StarterCharacterScripts__Character__CharacterReady.client.clpp.json +1 -0
  54. package/examples/game/.cluaupp/compile-cache/StarterPlayer__StarterPlayerScripts__Controllers__DataController.client.clpp.json +1 -0
  55. package/examples/game/.cluaupp/compile-cache/index.json +12 -0
  56. package/examples/game/.cluaupp/datamodel-profile.json +94 -0
  57. package/examples/game/.vscode/settings.json +12 -1
  58. package/examples/game/include/clpp/datatypes.clh +337 -373
  59. package/examples/game/include/clpp/generated/datamodel.clh +25 -0
  60. package/examples/game/include/clpp/generated/enums.clh +1 -1
  61. package/examples/game/include/clpp/generated/instances.clh +41135 -41064
  62. package/examples/game/include/clpp/libs/axiom.clh +1 -1
  63. package/examples/game/include/clpp/libs/math.clh +14 -0
  64. package/examples/game/include/clpp/libs/spring.clh +17 -0
  65. package/examples/game/include/clpp/libs/ward.clh +1 -0
  66. package/examples/game/include/clpp/roblox.clh +3 -4
  67. package/examples/game/libs/ArrayIndexer/init.luau +209 -0
  68. package/examples/game/libs/Axiom/init.luau +608 -0
  69. package/examples/game/libs/Bloom/init.luau +433 -0
  70. package/examples/game/libs/Coil/init.luau +215 -0
  71. package/examples/game/libs/Crest/init.luau +426 -0
  72. package/examples/game/libs/Echo/init.luau +417 -0
  73. package/examples/game/libs/Ember/init.luau +365 -0
  74. package/examples/game/libs/Flare/init.luau +717 -0
  75. package/examples/game/libs/Gleam/init.luau +729 -0
  76. package/examples/game/libs/Guide/init.luau +215 -0
  77. package/examples/game/libs/Helm/init.luau +377 -0
  78. package/examples/game/libs/Hive/init.luau +241 -0
  79. package/examples/game/libs/Keep/Client.luau +114 -0
  80. package/examples/game/libs/Keep/Data.luau +883 -0
  81. package/examples/game/libs/Keep/Engine.luau +211 -0
  82. package/examples/game/libs/Keep/Enum.luau +6 -0
  83. package/examples/game/libs/Keep/Enums.luau +10 -0
  84. package/examples/game/libs/Keep/Path.luau +236 -0
  85. package/examples/game/libs/Keep/Replicator.luau +298 -0
  86. package/examples/game/libs/Keep/Server.luau +251 -0
  87. package/examples/game/libs/Keep/Store.luau +652 -0
  88. package/examples/game/libs/Keep/Trade.luau +455 -0
  89. package/examples/game/libs/Keep/init.luau +205 -0
  90. package/examples/game/libs/Lens/init.luau +609 -0
  91. package/examples/game/libs/Mint/init.luau +279 -0
  92. package/examples/game/libs/Net/init.luau +156 -0
  93. package/examples/game/libs/Net/src/codec.luau +224 -0
  94. package/examples/game/libs/Occlude/init.luau +117 -0
  95. package/examples/game/libs/Pin/init.luau +309 -0
  96. package/examples/game/libs/Promise/LICENSE +21 -0
  97. package/examples/game/libs/Promise/_impl/LICENSE +21 -0
  98. package/examples/game/libs/Promise/_impl/init.lua +1991 -0
  99. package/examples/game/libs/Promise/init.lua +1990 -0
  100. package/examples/game/libs/Promise/init.luau +13 -0
  101. package/examples/game/libs/Promise/types.luau +46 -0
  102. package/examples/game/libs/Roster/init.luau +686 -0
  103. package/examples/game/libs/SOURCES.md +31 -0
  104. package/examples/game/libs/Shift/init.luau +238 -0
  105. package/examples/game/libs/Spark/init.luau +487 -0
  106. package/examples/game/libs/Stage/init.luau +178 -0
  107. package/examples/game/libs/Sweep/init.luau +259 -0
  108. package/examples/game/libs/Trace/init.luau +213 -0
  109. package/examples/game/libs/Ward/init.luau +211 -0
  110. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +29 -0
  111. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +13 -0
  112. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +83 -0
  113. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +13 -0
  114. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +18 -0
  115. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +13 -0
  116. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +14 -0
  117. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +13 -0
  118. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +15 -0
  119. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +13 -0
  120. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +50 -0
  121. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +13 -0
  122. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +12 -0
  123. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +13 -0
  124. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +16 -0
  125. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +13 -0
  126. package/examples/game/src/ReplicatedStorage/Shared/Net/Net.clh +26 -0
  127. package/examples/game/src/ServerScriptService/Boot/DataBoot.server.clpp +1 -1
  128. package/examples/game/src/ServerScriptService/Handlers/PlayerHandler.server.clpp +1 -1
  129. package/examples/game/stylua.toml +9 -0
  130. package/generated/api/build-profile.js +115 -0
  131. package/generated/api/cache.js +44 -0
  132. package/generated/api/coverage.js +32 -0
  133. package/generated/api/diff.js +125 -0
  134. package/generated/api/generators/clpp-headers.js +353 -0
  135. package/generated/api/generators/docs-reference.js +7 -0
  136. package/generated/api/generators/lsp-index.js +81 -0
  137. package/generated/api/generators/luau-defs.js +94 -0
  138. package/generated/api/ide.js +76 -0
  139. package/generated/api/identifier.js +47 -0
  140. package/generated/api/index.js +324 -0
  141. package/generated/api/loader.js +57 -0
  142. package/generated/api/merge.js +88 -0
  143. package/generated/api/model.js +5 -0
  144. package/generated/api/normalize.js +231 -0
  145. package/generated/api/overrides-validate.js +58 -0
  146. package/generated/api/perf.js +12 -0
  147. package/generated/api/registry.js +136 -0
  148. package/generated/api/release.js +44 -0
  149. package/generated/api/resolver.js +250 -0
  150. package/generated/api/sources/aux-types-diff.js +75 -0
  151. package/generated/api/sources/client-tracker.js +11 -0
  152. package/generated/api/sources/creator-docs.js +42 -0
  153. package/generated/api/sources/luau-types.js +34 -0
  154. package/generated/api/sources/mini-dump.js +17 -0
  155. package/generated/api/sources/studio-api.js +8 -0
  156. package/generated/api/sources/studio-apiv2.js +11 -0
  157. package/generated/api/sources/studio-full-api.js +8 -0
  158. package/generated/api/studio-smoke.js +80 -0
  159. package/generated/api/type-parse.js +49 -0
  160. package/generated/api/understander.js +51 -0
  161. package/generated/api/validate.js +135 -0
  162. package/generated/api-cli.js +421 -0
  163. package/generated/cli.js +16 -5
  164. package/generated/clpp/contract.js +3 -3
  165. package/generated/clpp/format-luau.js +236 -0
  166. package/generated/clpp/index.js +13 -1
  167. package/generated/clpp/modules.js +227 -0
  168. package/generated/clpp/postprocess.js +86 -5
  169. package/generated/clpp/rewrite-janitor.js +145 -0
  170. package/generated/clpp/runner.js +14 -2
  171. package/generated/compile.js +82 -0
  172. package/generated/flare/parse.js +12 -1
  173. package/generated/intellisense.js +57 -1
  174. package/generated/native/index.js +217 -16
  175. package/generated/target/authority-check.js +136 -0
  176. package/generated/target/authority-graph.js +74 -0
  177. package/generated/target/build-cache.js +112 -0
  178. package/generated/target/capabilities.js +89 -0
  179. package/generated/target/capability-profile.js +30 -0
  180. package/generated/target/component-contracts.js +60 -0
  181. package/generated/target/context-check.js +160 -0
  182. package/generated/target/datamodel-check.js +170 -0
  183. package/generated/target/datamodel-generate.js +74 -0
  184. package/generated/target/datamodel.js +233 -0
  185. package/generated/target/diagnostics.js +63 -0
  186. package/generated/target/doctor.js +152 -0
  187. package/generated/target/flare-version.js +58 -0
  188. package/generated/target/get-service.js +69 -0
  189. package/generated/target/lifetime-check.js +47 -0
  190. package/generated/target/luau-stamp.js +39 -0
  191. package/generated/target/optimizer-advise.js +88 -0
  192. package/generated/target/optimizer-apply.js +44 -0
  193. package/generated/target/optimizer-soa.js +254 -0
  194. package/generated/target/parallel-check.js +136 -0
  195. package/generated/target/pgo.js +148 -0
  196. package/generated/target/project-docs.js +69 -0
  197. package/generated/target/project-graph.js +71 -0
  198. package/generated/target/roblox.js +63 -0
  199. package/generated/target/security-check.js +41 -0
  200. package/generated/target/source-map.js +25 -0
  201. package/generated/target/studio-bridge.js +112 -0
  202. package/generated/utils/project.js +178 -8
  203. package/generated/utils/rojo-mapper.js +11 -3
  204. package/include/clpp/datatypes.clh +337 -373
  205. package/include/clpp/generated/enums.clh +1 -1
  206. package/include/clpp/generated/instances.clh +41135 -41064
  207. package/include/clpp/libs/axiom.clh +1 -1
  208. package/include/clpp/libs/hive.clh +1 -0
  209. package/include/clpp/libs/keep.clh +2 -0
  210. package/include/clpp/libs/math.clh +14 -0
  211. package/include/clpp/libs/spring.clh +17 -0
  212. package/include/clpp/libs/ward.clh +1 -0
  213. package/include/clpp/roblox.clh +3 -4
  214. package/package.json +10 -3
  215. package/plugins/CluauppNav/Plugin.luau +207 -0
  216. package/plugins/CluauppNav/ProfileLogger.luau +27 -0
  217. package/plugins/CluauppNav/README.md +24 -0
  218. package/runtime/Axiom/init.luau +10 -7
  219. package/runtime/Coil/init.luau +8 -2
  220. package/runtime/Crest/init.luau +1 -0
  221. package/runtime/Flare/init.luau +2 -1
  222. package/runtime/Gleam/init.luau +15 -4
  223. package/runtime/Hive/init.luau +36 -14
  224. package/runtime/Pin/init.luau +5 -2
  225. package/runtime/Roster/init.luau +2 -0
  226. package/runtime/Spark/init.luau +3 -0
  227. package/runtime/Sweep/init.luau +2 -0
  228. package/runtime/Trace/init.luau +3 -0
  229. package/runtime/Ward/init.luau +38 -5
  230. package/scripts/postinstall-editor.js +17 -0
  231. package/skills/README.md +13 -0
  232. package/skills/clpp-hive-flare/SKILL.md +79 -0
  233. package/skills/clpp-language/SKILL.md +123 -0
  234. package/skills/clpp-language/compiler.md +89 -0
  235. package/skills/clpp-style/SKILL.md +95 -0
  236. package/src/api/build-profile.ts +138 -0
  237. package/src/api/cache.ts +45 -0
  238. package/src/api/coverage.ts +46 -0
  239. package/src/api/diff.ts +143 -0
  240. package/src/api/generators/clpp-headers.ts +377 -0
  241. package/src/api/generators/docs-reference.ts +11 -0
  242. package/src/api/generators/lsp-index.ts +106 -0
  243. package/src/api/generators/luau-defs.ts +98 -0
  244. package/src/api/ide.ts +87 -0
  245. package/src/api/identifier.ts +47 -0
  246. package/src/api/index.ts +269 -0
  247. package/src/api/loader.ts +60 -0
  248. package/src/api/merge.ts +126 -0
  249. package/src/api/model.ts +283 -0
  250. package/src/api/normalize.ts +257 -0
  251. package/src/api/overrides-validate.ts +58 -0
  252. package/src/api/perf.ts +15 -0
  253. package/src/api/registry.ts +162 -0
  254. package/src/api/release.ts +47 -0
  255. package/src/api/resolver.ts +255 -0
  256. package/src/api/sources/aux-types-diff.ts +81 -0
  257. package/src/api/sources/client-tracker.ts +10 -0
  258. package/src/api/sources/creator-docs.ts +37 -0
  259. package/src/api/sources/luau-types.ts +30 -0
  260. package/src/api/sources/mini-dump.ts +50 -0
  261. package/src/api/sources/studio-api.ts +7 -0
  262. package/src/api/sources/studio-apiv2.ts +10 -0
  263. package/src/api/sources/studio-full-api.ts +7 -0
  264. package/src/api/studio-smoke.ts +82 -0
  265. package/src/api/type-parse.ts +48 -0
  266. package/src/api/understander.ts +72 -0
  267. package/src/api/validate.ts +160 -0
  268. package/src/api-cli.ts +493 -0
  269. package/src/api.generated.js +37 -22
  270. package/src/cli.ts +21 -6
  271. package/src/clpp/contract.ts +34 -3
  272. package/src/clpp/format-luau.ts +262 -0
  273. package/src/clpp/index.ts +13 -2
  274. package/src/clpp/modules.ts +252 -0
  275. package/src/clpp/postprocess.ts +100 -5
  276. package/src/clpp/rewrite-janitor.ts +156 -0
  277. package/src/clpp/runner.ts +14 -2
  278. package/src/compile.ts +87 -0
  279. package/src/flare/parse.ts +14 -1
  280. package/src/flare/types.ts +2 -0
  281. package/src/intellisense.ts +58 -1
  282. package/src/native/index.ts +225 -16
  283. package/src/target/authority-check.ts +162 -0
  284. package/src/target/authority-graph.ts +80 -0
  285. package/src/target/build-cache.ts +129 -0
  286. package/src/target/capabilities.ts +115 -0
  287. package/src/target/capability-profile.ts +25 -0
  288. package/src/target/component-contracts.ts +64 -0
  289. package/src/target/context-check.ts +177 -0
  290. package/src/target/datamodel-check.ts +188 -0
  291. package/src/target/datamodel-generate.ts +78 -0
  292. package/src/target/datamodel.ts +261 -0
  293. package/src/target/diagnostics.ts +67 -0
  294. package/src/target/doctor.ts +171 -0
  295. package/src/target/flare-version.ts +61 -0
  296. package/src/target/get-service.ts +85 -0
  297. package/src/target/lifetime-check.ts +45 -0
  298. package/src/target/luau-stamp.ts +33 -0
  299. package/src/target/optimizer-advise.ts +93 -0
  300. package/src/target/optimizer-apply.ts +63 -0
  301. package/src/target/optimizer-soa.ts +270 -0
  302. package/src/target/parallel-check.ts +135 -0
  303. package/src/target/pgo.ts +157 -0
  304. package/src/target/project-docs.ts +68 -0
  305. package/src/target/project-graph.ts +75 -0
  306. package/src/target/roblox.ts +78 -0
  307. package/src/target/security-check.ts +40 -0
  308. package/src/target/source-map.ts +36 -0
  309. package/src/target/studio-bridge.ts +140 -0
  310. package/src/types.ts +14 -0
  311. package/src/utils/project.ts +183 -9
  312. package/src/utils/rojo-mapper.ts +12 -3
  313. package/stylua.toml +9 -0
  314. package/templates/game/.vscode/settings.json +7 -1
  315. package/templates/game/include/clpp/datatypes.clh +337 -373
  316. package/templates/game/include/clpp/generated/enums.clh +1 -1
  317. package/templates/game/include/clpp/generated/instances.clh +41135 -41064
  318. package/templates/game/include/clpp/libs/axiom.clh +1 -1
  319. package/templates/game/include/clpp/libs/ward.clh +1 -0
  320. package/templates/game/include/clpp/roblox.clh +3 -4
  321. package/templates/game/src/ServerScriptService/Boot/DataBoot.server.clpp +1 -1
  322. package/templates/game/src/ServerScriptService/Handlers/PlayerHandler.server.clpp +1 -1
  323. package/templates/game/stylua.toml +9 -0
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * Cluaupp Optimizer advisor (v1) — suggestions only; does not rewrite Luau yet.
4
+ * Philosophy: write idiomatic CL++; compiler advises best representation.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.adviseOptimizer = adviseOptimizer;
8
+ exports.adviceAsDiagnostics = adviceAsDiagnostics;
9
+ const diagnostics_js_1 = require("./diagnostics.js");
10
+ function adviseOptimizer(source, fileName) {
11
+ const clean = (0, diagnostics_js_1.stripCommentsKeepStrings)(source);
12
+ const advice = [];
13
+ const structRe = /struct\s+([A-Za-z_]\w*)\s*\{([^}]*)\}/g;
14
+ let m;
15
+ while ((m = structRe.exec(clean))) {
16
+ const name = m[1];
17
+ const body = m[2];
18
+ const fields = body.split(";").map((s) => s.trim()).filter(Boolean);
19
+ if (fields.length < 2)
20
+ continue;
21
+ const numeric = fields.filter((f) => /^(float|double|int|i32|f32|f64|bool)\b/.test(f) || /\bVector3\b/.test(f));
22
+ const ratio = numeric.length / fields.length;
23
+ if (ratio >= 0.75 && fields.length >= 3) {
24
+ advice.push({
25
+ kind: "storage",
26
+ target: name,
27
+ message: `Hot numeric struct ${name} (${numeric.length}/${fields.length} fields) — candidate for SoA / dense storage (Optimizer Level 1–3)`,
28
+ severity: "info",
29
+ });
30
+ }
31
+ const bools = fields.filter((f) => /^bool\b/.test(f));
32
+ if (bools.length >= 4) {
33
+ advice.push({
34
+ kind: "layout",
35
+ target: name,
36
+ message: `${name} has ${bools.length} bool fields — candidate for internal bitset packing`,
37
+ severity: "info",
38
+ });
39
+ }
40
+ }
41
+ const fnRe = /(?:void|int|float|double|bool|auto)\s+([A-Za-z_]\w*)\s*\([^)]*\)\s*\{/g;
42
+ while ((m = fnRe.exec(clean))) {
43
+ const name = m[1];
44
+ if (name === "init")
45
+ continue;
46
+ const start = m.index + m[0].length;
47
+ let depth = 1;
48
+ let i = start;
49
+ for (; i < clean.length && depth > 0; i++) {
50
+ if (clean[i] === "{")
51
+ depth++;
52
+ else if (clean[i] === "}")
53
+ depth--;
54
+ }
55
+ const body = clean.slice(start, i);
56
+ const apiHits = (body.match(/GetService|WaitForChild|Instance|FindFirstChild|FireServer|FireClient/g) || []).length;
57
+ const numericHits = (body.match(/\b(sin|cos|sqrt|Vector3|CFrame|math::)\b|[+\-*/]=|[+\-*/]/g) || []).length;
58
+ if (numericHits >= 6 && apiHits <= 1 && body.length > 40) {
59
+ advice.push({
60
+ kind: "native",
61
+ target: name,
62
+ message: `${name} looks numeric-hot / low Instance API — good @native / --!native candidate (CLUAU-PERF-001)`,
63
+ severity: "info",
64
+ });
65
+ }
66
+ if (apiHits === 0 && /for\s*\(/.test(body) && !/shared|static/.test(body)) {
67
+ advice.push({
68
+ kind: "parallel",
69
+ target: name,
70
+ message: `${name} may be parallelizable (independent loop, no shared Instance API) — review ThreadSafety`,
71
+ severity: "info",
72
+ });
73
+ }
74
+ }
75
+ void fileName;
76
+ void diagnostics_js_1.lineColAt;
77
+ return advice;
78
+ }
79
+ function adviceAsDiagnostics(advice, fileName) {
80
+ return advice.map((a) => ({
81
+ code: a.kind === "native" ? "CLUAU_PERF001" : a.kind === "parallel" ? "CLUAU_PAR_ADVISE" : "CLUAU_OPT001",
82
+ message: a.message,
83
+ line: 1,
84
+ column: 1,
85
+ severity: a.severity === "warning" ? "warning" : "info",
86
+ file: fileName,
87
+ }));
88
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ /**
3
+ * Apply Optimizer transforms to CL++ source:
4
+ * - `#pragma native` on numeric-hot functions
5
+ * - `#pragma layout soa` + SoA Luau modules when --layout
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.applyOptimizerHints = applyOptimizerHints;
9
+ const optimizer_advise_js_1 = require("./optimizer-advise.js");
10
+ const pgo_js_1 = require("./pgo.js");
11
+ const optimizer_soa_js_1 = require("./optimizer-soa.js");
12
+ function applyOptimizerHints(source, fileName, profile = null, opts = {}) {
13
+ const advice = (0, pgo_js_1.rankAdvice)((0, optimizer_advise_js_1.adviseOptimizer)(source, fileName), profile);
14
+ const nativeTargets = new Set(advice.filter((a) => a.kind === "native").map((a) => a.target));
15
+ let out = source;
16
+ const applied = [];
17
+ for (const name of nativeTargets) {
18
+ const re = new RegExp(`((?:^|\\n)(?:\\s*))((?:void|int|float|double|bool|auto)\\s+${name}\\s*\\()`, "m");
19
+ const m = out.match(re);
20
+ if (!m)
21
+ continue;
22
+ const before = out.slice(0, m.index + m[1].length);
23
+ if (/#pragma\s+native\s*$/m.test(before.slice(-80)) ||
24
+ /#pragma\s+native/.test(out.slice(Math.max(0, m.index - 40), m.index))) {
25
+ continue;
26
+ }
27
+ out = out.replace(re, `$1#pragma native\n$1$2`);
28
+ applied.push(`#pragma native → ${name}`);
29
+ }
30
+ let soaPlans;
31
+ if (opts.layout) {
32
+ const layout = (0, optimizer_soa_js_1.applyLayoutPragmas)(out, fileName, profile);
33
+ out = layout.source;
34
+ applied.push(...layout.applied);
35
+ soaPlans = layout.plans;
36
+ if (opts.projectRoot && layout.plans.length) {
37
+ const files = (0, optimizer_soa_js_1.writeSoaArtifacts)(opts.projectRoot, layout.plans, { useBuffer: opts.useBuffer });
38
+ for (const f of files) {
39
+ applied.push(`SoA emit → ${f.replace(/\\/g, "/")}`);
40
+ }
41
+ }
42
+ }
43
+ return { source: out, changed: applied.length > 0 || out !== source, applied, soaPlans };
44
+ }
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ /**
3
+ * Opt-in SoA / dense storage codegen.
4
+ * Triggered by `#pragma layout soa StructName` (or optimize --apply --layout).
5
+ * Emits a Luau ModuleScript companion — does not silently rewrite gameplay structs.
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.parseSoaFields = parseSoaFields;
12
+ exports.findStructBody = findStructBody;
13
+ exports.listLayoutTargets = listLayoutTargets;
14
+ exports.planSoa = planSoa;
15
+ exports.generateSoaLuau = generateSoaLuau;
16
+ exports.applyLayoutPragmas = applyLayoutPragmas;
17
+ exports.writeSoaArtifacts = writeSoaArtifacts;
18
+ exports.collectSoaPlansFromProject = collectSoaPlansFromProject;
19
+ const node_fs_1 = __importDefault(require("node:fs"));
20
+ const node_path_1 = __importDefault(require("node:path"));
21
+ const optimizer_advise_js_1 = require("./optimizer-advise.js");
22
+ const pgo_js_1 = require("./pgo.js");
23
+ const FIELD_RE = /^(?:float|f32|double|f64)\s+([A-Za-z_]\w*)|^(?:int|i32)\s+([A-Za-z_]\w*)|^bool\s+([A-Za-z_]\w*)|^Vector3\s+([A-Za-z_]\w*)/;
24
+ function parseSoaFields(structBody) {
25
+ const fields = [];
26
+ for (const piece of structBody.split(";")) {
27
+ const line = piece.trim().replace(/=.*/, "").trim();
28
+ if (!line)
29
+ continue;
30
+ const m = line.match(FIELD_RE);
31
+ if (!m)
32
+ continue;
33
+ if (m[1])
34
+ fields.push({ name: m[1], kind: "f32" });
35
+ else if (m[2])
36
+ fields.push({ name: m[2], kind: "i32" });
37
+ else if (m[3])
38
+ fields.push({ name: m[3], kind: "bool" });
39
+ else if (m[4])
40
+ fields.push({ name: m[4], kind: "Vector3" });
41
+ }
42
+ return fields;
43
+ }
44
+ function findStructBody(source, structName) {
45
+ const re = new RegExp(`struct\\s+${structName}\\s*\\{([^}]*)\\}`);
46
+ const m = source.match(re);
47
+ return m ? m[1] : null;
48
+ }
49
+ function listLayoutTargets(source) {
50
+ const names = new Set();
51
+ const re = /#pragma\s+layout\s+soa\s+([A-Za-z_]\w*)/g;
52
+ let m;
53
+ while ((m = re.exec(source))) {
54
+ names.add(m[1]);
55
+ }
56
+ return [...names];
57
+ }
58
+ function planSoa(source, structName) {
59
+ const body = findStructBody(source, structName);
60
+ if (!body)
61
+ return null;
62
+ const fields = parseSoaFields(body);
63
+ if (fields.length < 2)
64
+ return null;
65
+ return { structName, fields };
66
+ }
67
+ function defaultFor(kind) {
68
+ if (kind === "f32")
69
+ return "0";
70
+ if (kind === "i32")
71
+ return "0";
72
+ if (kind === "bool")
73
+ return "false";
74
+ return "Vector3.zero";
75
+ }
76
+ /** Generate a standalone Luau SoA module (table-columns; buffer pack optional for floats). */
77
+ function generateSoaLuau(plan, useBuffer = false) {
78
+ const { structName, fields } = plan;
79
+ const cols = fields
80
+ .map((f) => {
81
+ if (useBuffer && (f.kind === "f32" || f.kind === "i32")) {
82
+ return `\t\t${f.name} = buffer.create(0), -- grown on ensure`;
83
+ }
84
+ return `\t\t${f.name} = table.create(capacity),`;
85
+ })
86
+ .join("\n");
87
+ const pushLines = fields
88
+ .map((f) => {
89
+ if (useBuffer && f.kind === "f32") {
90
+ return `\tbuffer.writef32(store.${f.name}, (i - 1) * 4, row.${f.name} or 0)`;
91
+ }
92
+ if (useBuffer && f.kind === "i32") {
93
+ return `\tbuffer.writei32(store.${f.name}, (i - 1) * 4, row.${f.name} or 0)`;
94
+ }
95
+ return `\tstore.${f.name}[i] = row.${f.name}`;
96
+ })
97
+ .join("\n");
98
+ const getLines = fields
99
+ .map((f) => {
100
+ if (useBuffer && f.kind === "f32") {
101
+ return `\t\t${f.name} = buffer.readf32(store.${f.name}, (i - 1) * 4),`;
102
+ }
103
+ if (useBuffer && f.kind === "i32") {
104
+ return `\t\t${f.name} = buffer.readi32(store.${f.name}, (i - 1) * 4),`;
105
+ }
106
+ return `\t\t${f.name} = store.${f.name}[i],`;
107
+ })
108
+ .join("\n");
109
+ const ensureBuf = useBuffer
110
+ ? fields
111
+ .filter((f) => f.kind === "f32" || f.kind === "i32")
112
+ .map((f) => `
113
+ local need = capacity * 4
114
+ if buffer.len(store.${f.name}) < need then
115
+ local nb = buffer.create(need)
116
+ buffer.copy(nb, 0, store.${f.name})
117
+ store.${f.name} = nb
118
+ end`)
119
+ .join("\n")
120
+ : "";
121
+ return `--!native
122
+ --!optimize 2
123
+ --!strict
124
+ -- cluaupp generated SoA for ${structName} — do not edit by hand.
125
+ -- Use via: local ${structName}Soa = require(...${structName}Soa)
126
+
127
+ export type ${structName}Row = {
128
+ ${fields.map((f) => `\t${f.name}: ${f.kind === "f32" ? "number" : f.kind === "i32" ? "number" : f.kind === "bool" ? "boolean" : "Vector3"},`).join("\n")}
129
+ }
130
+
131
+ export type ${structName}Store = {
132
+ len: number,
133
+ capacity: number,
134
+ ${fields.map((f) => `\t${f.name}: ${useBuffer && (f.kind === "f32" || f.kind === "i32") ? "buffer" : "{}"},`).join("\n")}
135
+ }
136
+
137
+ local ${structName}Soa = {}
138
+
139
+ function ${structName}Soa.new(capacity: number): ${structName}Store
140
+ capacity = math.max(1, capacity or 64)
141
+ local store = {
142
+ len = 0,
143
+ capacity = capacity,
144
+ ${cols}
145
+ }
146
+ return store
147
+ end
148
+
149
+ function ${structName}Soa.ensure(store: ${structName}Store, capacity: number)
150
+ if capacity <= store.capacity then
151
+ return
152
+ end
153
+ store.capacity = capacity
154
+ ${ensureBuf}
155
+ ${useBuffer ? "" : fields.map((f) => `\t-- table.create columns grow on index assign`).join("\n")}
156
+ end
157
+
158
+ function ${structName}Soa.push(store: ${structName}Store, row: ${structName}Row): number
159
+ local i = store.len + 1
160
+ ${structName}Soa.ensure(store, i)
161
+ store.len = i
162
+ ${pushLines}
163
+ return i
164
+ end
165
+
166
+ function ${structName}Soa.get(store: ${structName}Store, i: number): ${structName}Row
167
+ return {
168
+ ${getLines}
169
+ }
170
+ end
171
+
172
+ function ${structName}Soa.set(store: ${structName}Store, i: number, row: ${structName}Row)
173
+ ${pushLines}
174
+ end
175
+
176
+ return ${structName}Soa
177
+ `;
178
+ }
179
+ /** Insert `#pragma layout soa Name` before advised storage structs. */
180
+ function applyLayoutPragmas(source, fileName, profile = null) {
181
+ const advice = (0, pgo_js_1.rankAdvice)((0, optimizer_advise_js_1.adviseOptimizer)(source, fileName), profile);
182
+ const targets = new Set(advice.filter((a) => a.kind === "storage").map((a) => a.target));
183
+ const applied = [];
184
+ const plans = [];
185
+ let out = source;
186
+ for (const name of targets) {
187
+ if (new RegExp(`#pragma\\s+layout\\s+soa\\s+${name}\\b`).test(out)) {
188
+ const plan = planSoa(out, name);
189
+ if (plan)
190
+ plans.push(plan);
191
+ continue;
192
+ }
193
+ const re = new RegExp(`((?:^|\\n)(?:\\s*))(struct\\s+${name}\\s*\\{)`, "m");
194
+ if (!re.test(out))
195
+ continue;
196
+ out = out.replace(re, `$1#pragma layout soa ${name}\n$1$2`);
197
+ applied.push(`#pragma layout soa → ${name}`);
198
+ const plan = planSoa(out, name);
199
+ if (plan)
200
+ plans.push(plan);
201
+ }
202
+ for (const name of listLayoutTargets(out)) {
203
+ if (plans.some((p) => p.structName === name))
204
+ continue;
205
+ const plan = planSoa(out, name);
206
+ if (plan)
207
+ plans.push(plan);
208
+ }
209
+ return { source: out, changed: applied.length > 0, applied, plans };
210
+ }
211
+ function writeSoaArtifacts(projectRoot, plans, opts = {}) {
212
+ if (!plans.length)
213
+ return [];
214
+ const outDir = node_path_1.default.join(projectRoot, ".cluaupp", "generated", "soa");
215
+ node_fs_1.default.mkdirSync(outDir, { recursive: true });
216
+ const written = [];
217
+ for (const plan of plans) {
218
+ const file = node_path_1.default.join(outDir, `${plan.structName}Soa.luau`);
219
+ node_fs_1.default.writeFileSync(file, generateSoaLuau(plan, opts.useBuffer === true), "utf8");
220
+ written.push(file);
221
+ }
222
+ return written;
223
+ }
224
+ /** Collect SoA plans from all sources that already have layout pragmas. */
225
+ function collectSoaPlansFromProject(projectRoot, rootDir = "src") {
226
+ const srcDir = node_path_1.default.join(projectRoot, rootDir);
227
+ if (!node_fs_1.default.existsSync(srcDir))
228
+ return [];
229
+ const plans = [];
230
+ const seen = new Set();
231
+ const walk = (dir) => {
232
+ for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
233
+ const full = node_path_1.default.join(dir, entry.name);
234
+ if (entry.isDirectory()) {
235
+ walk(full);
236
+ continue;
237
+ }
238
+ if (!/\.(clpp|clp|clh)$/i.test(entry.name))
239
+ continue;
240
+ const source = node_fs_1.default.readFileSync(full, "utf8");
241
+ for (const name of listLayoutTargets(source)) {
242
+ if (seen.has(name))
243
+ continue;
244
+ const plan = planSoa(source, name);
245
+ if (plan) {
246
+ seen.add(name);
247
+ plans.push(plan);
248
+ }
249
+ }
250
+ }
251
+ };
252
+ walk(srcDir);
253
+ return plans;
254
+ }
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Parallel Luau / ThreadSafety diagnostics (CLUAU-PAR*).
4
+ * Flags Unsafe mutations inside parallel { } / desynchronize regions.
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.checkParallelSafety = checkParallelSafety;
11
+ exports.assertParallelSafe = assertParallelSafe;
12
+ const node_fs_1 = __importDefault(require("node:fs"));
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const package_info_js_1 = require("../package-info.js");
15
+ const diagnostics_js_1 = require("./diagnostics.js");
16
+ let cachedUnsafeMembers = null;
17
+ function loadUnsafeMemberNames() {
18
+ if (cachedUnsafeMembers) {
19
+ return cachedUnsafeMembers;
20
+ }
21
+ const set = new Set(["Parent", "Name"]); // Parent writes always unsafe in parallel
22
+ const profilePath = node_path_1.default.join(package_info_js_1.projectRoot, "api", "normalized", "roblox-target.profile.json");
23
+ if (node_fs_1.default.existsSync(profilePath)) {
24
+ try {
25
+ const profile = JSON.parse(node_fs_1.default.readFileSync(profilePath, "utf8"));
26
+ for (const cls of Object.values(profile.classes || {})) {
27
+ for (const prop of Object.values(cls.properties || {})) {
28
+ if (prop.threadSafety === "Unsafe") {
29
+ set.add(prop.name);
30
+ }
31
+ }
32
+ for (const method of Object.values(cls.methods || {})) {
33
+ if (method.threadSafety === "Unsafe") {
34
+ set.add(method.name);
35
+ }
36
+ }
37
+ }
38
+ }
39
+ catch {
40
+ /* fallback set only */
41
+ }
42
+ }
43
+ cachedUnsafeMembers = set;
44
+ return set;
45
+ }
46
+ function extractParallelRegions(source) {
47
+ const regions = [];
48
+ const reParallel = /\bparallel\s*\{/g;
49
+ let m;
50
+ while ((m = reParallel.exec(source))) {
51
+ const open = m.index + m[0].length - 1;
52
+ let depth = 0;
53
+ for (let i = open; i < source.length; i++) {
54
+ if (source[i] === "{")
55
+ depth++;
56
+ else if (source[i] === "}") {
57
+ depth--;
58
+ if (depth === 0) {
59
+ regions.push({ start: m.index, body: source.slice(open + 1, i) });
60
+ break;
61
+ }
62
+ }
63
+ }
64
+ }
65
+ const reDesync = /task\s*::\s*desynchronize\s*\(\s*\)/g;
66
+ while ((m = reDesync.exec(source))) {
67
+ const rest = source.slice(m.index);
68
+ const sync = rest.search(/task\s*::\s*synchronize\s*\(\s*\)/);
69
+ const body = sync >= 0 ? rest.slice(0, sync) : rest.slice(0, Math.min(rest.length, 800));
70
+ regions.push({ start: m.index, body });
71
+ }
72
+ if (/\bConnectParallel\b/.test(source)) {
73
+ regions.push({ start: source.indexOf("ConnectParallel"), body: source });
74
+ }
75
+ return regions;
76
+ }
77
+ function checkParallelSafety(source, fileName = "input.clpp") {
78
+ const clean = (0, diagnostics_js_1.stripCommentsKeepStrings)(source);
79
+ const unsafe = loadUnsafeMemberNames();
80
+ const out = [];
81
+ const regions = extractParallelRegions(clean);
82
+ if (!regions.length) {
83
+ return out;
84
+ }
85
+ for (const region of regions) {
86
+ const parentAssign = /\.Parent\s*=/g;
87
+ let pm;
88
+ while ((pm = parentAssign.exec(region.body))) {
89
+ const { line, column } = (0, diagnostics_js_1.lineColAt)(clean, region.start + pm.index);
90
+ out.push({
91
+ code: "CLUAU_PAR001",
92
+ message: "Unsafe Instance write `.Parent =` inside parallel / desynchronized region",
93
+ line,
94
+ column,
95
+ severity: "error",
96
+ file: fileName,
97
+ });
98
+ }
99
+ const memberWrite = /\.([A-Za-z_]\w*)\s*=/g;
100
+ while ((pm = memberWrite.exec(region.body))) {
101
+ const name = pm[1];
102
+ if (name === "Parent")
103
+ continue;
104
+ if (!unsafe.has(name))
105
+ continue;
106
+ const { line, column } = (0, diagnostics_js_1.lineColAt)(clean, region.start + pm.index);
107
+ out.push({
108
+ code: "CLUAU_PAR002",
109
+ message: `API member \`.${name}\` is ThreadSafety=Unsafe inside parallel region`,
110
+ line,
111
+ column,
112
+ severity: "error",
113
+ file: fileName,
114
+ });
115
+ }
116
+ const destroy = /\.Destroy\s*\(/g;
117
+ while ((pm = destroy.exec(region.body))) {
118
+ const { line, column } = (0, diagnostics_js_1.lineColAt)(clean, region.start + pm.index);
119
+ out.push({
120
+ code: "CLUAU_PAR003",
121
+ message: "`Destroy()` is Unsafe inside parallel region",
122
+ line,
123
+ column,
124
+ severity: "error",
125
+ file: fileName,
126
+ });
127
+ }
128
+ }
129
+ return out;
130
+ }
131
+ function assertParallelSafe(source, fileName) {
132
+ const errors = checkParallelSafety(source, fileName).filter((d) => d.severity === "error");
133
+ if (!errors.length)
134
+ return;
135
+ throw new Error(`Parallel safety:\n${errors.map((e) => ` ${e.line}:${e.column} ${e.code} ${e.message}`).join("\n")}`);
136
+ }
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ /**
3
+ * Profile-Guided Optimization (PGO) — format, load, Studio ingest.
4
+ * cluaupp.profile.json weights optimizer advice toward hot symbols.
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.PGO_SCHEMA_VERSION = void 0;
11
+ exports.defaultProfilePath = defaultProfilePath;
12
+ exports.loadProfile = loadProfile;
13
+ exports.writeProfileTemplate = writeProfileTemplate;
14
+ exports.ingestProfile = ingestProfile;
15
+ exports.rankAdvice = rankAdvice;
16
+ const node_fs_1 = __importDefault(require("node:fs"));
17
+ const node_path_1 = __importDefault(require("node:path"));
18
+ exports.PGO_SCHEMA_VERSION = 1;
19
+ function defaultProfilePath(projectRoot) {
20
+ return node_path_1.default.join(projectRoot, "cluaupp.profile.json");
21
+ }
22
+ function loadProfile(file) {
23
+ if (!node_fs_1.default.existsSync(file)) {
24
+ return null;
25
+ }
26
+ try {
27
+ const raw = JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
28
+ if (!raw || typeof raw.hot !== "object") {
29
+ return null;
30
+ }
31
+ return { schemaVersion: exports.PGO_SCHEMA_VERSION, hot: raw.hot, structs: raw.structs, generatedAt: raw.generatedAt };
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ /** Write an empty / template profile for games to fill from Studio logging. */
38
+ function writeProfileTemplate(projectRoot) {
39
+ const file = defaultProfilePath(projectRoot);
40
+ const template = {
41
+ schemaVersion: exports.PGO_SCHEMA_VERSION,
42
+ generatedAt: new Date().toISOString(),
43
+ hot: {
44
+ CalculateDamage: 0,
45
+ EnemyUpdate: 0,
46
+ },
47
+ structs: {
48
+ EnemyState: 0,
49
+ },
50
+ };
51
+ node_fs_1.default.writeFileSync(file, JSON.stringify(template, null, "\t") + "\n", "utf8");
52
+ return file;
53
+ }
54
+ function parseEvents(raw) {
55
+ const trimmed = raw.trim();
56
+ if (!trimmed)
57
+ return [];
58
+ if (trimmed.startsWith("[")) {
59
+ const arr = JSON.parse(trimmed);
60
+ if (!Array.isArray(arr))
61
+ return [];
62
+ return arr.filter((e) => e && typeof e.symbol === "string");
63
+ }
64
+ if (trimmed.startsWith("{") && !trimmed.includes("\n")) {
65
+ const one = JSON.parse(trimmed);
66
+ if (typeof one.symbol === "string") {
67
+ return [one];
68
+ }
69
+ if (one.hot && typeof one.hot === "object") {
70
+ return Object.entries(one.hot).map(([symbol, count]) => ({
71
+ symbol,
72
+ kind: "hot",
73
+ count: Number(count) || 0,
74
+ }));
75
+ }
76
+ }
77
+ const events = [];
78
+ for (const line of trimmed.split(/\r?\n/)) {
79
+ const t = line.trim();
80
+ if (!t || t.startsWith("#"))
81
+ continue;
82
+ try {
83
+ const e = JSON.parse(t);
84
+ if (e && typeof e.symbol === "string")
85
+ events.push(e);
86
+ }
87
+ catch {
88
+ /* skip bad line */
89
+ }
90
+ }
91
+ return events;
92
+ }
93
+ /** Merge Studio/playtest events into cluaupp.profile.json (additive counts). */
94
+ function ingestProfile(projectRoot, eventsPath) {
95
+ const eventsFile = node_path_1.default.resolve(eventsPath);
96
+ if (!node_fs_1.default.existsSync(eventsFile)) {
97
+ throw new Error(`cluaupp profile ingest: missing ${eventsFile}`);
98
+ }
99
+ const events = parseEvents(node_fs_1.default.readFileSync(eventsFile, "utf8"));
100
+ const profilePath = defaultProfilePath(projectRoot);
101
+ const existing = loadProfile(profilePath) || {
102
+ schemaVersion: exports.PGO_SCHEMA_VERSION,
103
+ hot: {},
104
+ structs: {},
105
+ };
106
+ const hot = { ...(existing.hot || {}) };
107
+ const structs = { ...(existing.structs || {}) };
108
+ let merged = 0;
109
+ for (const e of events) {
110
+ const n = typeof e.count === "number" && Number.isFinite(e.count) ? e.count : 1;
111
+ if (e.kind === "struct") {
112
+ structs[e.symbol] = (structs[e.symbol] || 0) + n;
113
+ }
114
+ else {
115
+ hot[e.symbol] = (hot[e.symbol] || 0) + n;
116
+ }
117
+ merged++;
118
+ }
119
+ const profile = {
120
+ schemaVersion: exports.PGO_SCHEMA_VERSION,
121
+ generatedAt: new Date().toISOString(),
122
+ hot,
123
+ structs,
124
+ };
125
+ node_fs_1.default.writeFileSync(profilePath, JSON.stringify(profile, null, "\t") + "\n", "utf8");
126
+ return { profilePath, profile, merged };
127
+ }
128
+ function rankAdvice(advice, profile) {
129
+ if (!profile) {
130
+ return advice;
131
+ }
132
+ return [...advice]
133
+ .sort((a, b) => {
134
+ const wa = profile.hot[a.target] || profile.structs?.[a.target] || 0;
135
+ const wb = profile.hot[b.target] || profile.structs?.[b.target] || 0;
136
+ return wb - wa;
137
+ })
138
+ .map((a) => {
139
+ const w = profile.hot[a.target] || profile.structs?.[a.target];
140
+ if (!w)
141
+ return a;
142
+ return {
143
+ ...a,
144
+ message: `${a.message} [PGO weight=${w}]`,
145
+ severity: w > 1000 ? "warning" : a.severity,
146
+ };
147
+ });
148
+ }