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
package/src/cli.ts CHANGED
@@ -9,9 +9,10 @@ import { clppInstalls, clppManifest, clppVersion, hasClpp, resolveClppBinary } f
9
9
  import { installEditorSupport, syncEditorSupport } from "./intellisense.js";
10
10
  import { RojoMapper } from "./utils/rojo-mapper.js";
11
11
  import { ProcessOrchestrator } from "./utils/process-orchestrator.js";
12
- import { build as buildProject, init, loadConfig, watch } from "./utils/project.js";
12
+ import { build as buildProject, buildAsync, init, loadConfig, watch } from "./utils/project.js";
13
13
  import { transpileSource } from "./transpile.js";
14
14
  import type { BuildOptions, BuildResult } from "./types.js";
15
+ import { registerApiCommands, registerTargetCommands, registerPlatformCommands } from "./api-cli.js";
15
16
 
16
17
  const program = new Command();
17
18
 
@@ -45,8 +46,11 @@ program
45
46
  .option("-o, --output <path>", "Luau file or directory")
46
47
  .option("-r, --rojo <path>", "Path to Rojo default.project.json", "./default.project.json")
47
48
  .option("--strict", "Emit --!strict")
48
- .option("--format", "Run StyLua on output")
49
+ .option("--format", "Also run StyLua if it is on PATH")
49
50
  .option("--analyze", "Run luau-analyze on output")
51
+ .option("--frozen", "Hermetic: fail if API lock / Flare versions / component contracts drift")
52
+ .option("--no-incremental", "Disable compile cache under .cluaupp/compile-cache")
53
+ .option("-j, --jobs <n>", "Parallel transpile workers (default: CPU-bounded; env CLUAUPP_JOBS)", (v: string) => Number(v))
50
54
  .action(async (folder: string, options: {
51
55
  input?: string;
52
56
  output?: string;
@@ -54,18 +58,25 @@ program
54
58
  strict?: boolean;
55
59
  format?: boolean;
56
60
  analyze?: boolean;
61
+ frozen?: boolean;
62
+ incremental?: boolean;
63
+ jobs?: number;
57
64
  }) => {
58
65
  if (options.input) {
59
66
  await buildInput(options);
60
67
  return;
61
68
  }
62
69
  try {
63
- buildProject(path.resolve(process.cwd(), folder), {
70
+ const opts = {
64
71
  format: options.format === true,
65
72
  analyze: options.analyze === true,
66
73
  rojo: options.rojo,
67
74
  strict: options.strict === true,
68
- });
75
+ frozen: options.frozen === true,
76
+ incremental: options.incremental !== false,
77
+ jobs: Number.isFinite(options.jobs) && (options.jobs as number) > 0 ? options.jobs : undefined,
78
+ };
79
+ await buildAsync(path.resolve(process.cwd(), folder), opts);
69
80
  } catch (err) {
70
81
  console.error(err instanceof Error ? err.message : err);
71
82
  process.exit(1);
@@ -77,7 +88,7 @@ program
77
88
  .description("rebuild on save")
78
89
  .argument("[folder]", "project folder", ".")
79
90
  .option("-r, --rojo <path>", "Path to Rojo default.project.json", "./default.project.json")
80
- .option("--format", "Run StyLua on output")
91
+ .option("--format", "Also run StyLua if it is on PATH")
81
92
  .action((folder: string, options: { rojo?: string; format?: boolean }) => {
82
93
  try {
83
94
  watch(path.resolve(process.cwd(), folder), {
@@ -101,7 +112,7 @@ program
101
112
  program
102
113
  .command("intellisense")
103
114
  .alias("intelisense")
104
- .description("Install Flare IntelliSense and point CL++ at `clpp setup`")
115
+ .description("Install schema IntelliSense (.flare .hive .mint .bloom .helm .shift .axiom) + sync .vscode; CL++ via clpp install")
105
116
  .argument("[folder]", "project folder", ".")
106
117
  .action(async (folder: string) => {
107
118
  try {
@@ -138,6 +149,10 @@ program
138
149
  }
139
150
  });
140
151
 
152
+ registerApiCommands(program);
153
+ registerTargetCommands(program);
154
+ registerPlatformCommands(program);
155
+
141
156
  async function buildInput(options: {
142
157
  input?: string;
143
158
  output?: string;
@@ -1,10 +1,22 @@
1
- /** Stable CL++ ↔ Cluaupp contract (CL++ 0.3.2 / 0.3.3). Cluaupp invokes the `clpp` binary. */
1
+ /** Stable CL++ ↔ Cluaupp contract (CL++ 0.8.x). Cluaupp invokes the `clpp` binary. */
2
2
 
3
3
  export interface CompileRequest {
4
4
  source: string;
5
5
  fileName: string;
6
6
  strict?: boolean;
7
7
  cwd?: string;
8
+ /** false = skip high-level opts (fair baseline). Default true when omitted. */
9
+ optimize?: boolean;
10
+ /** Path to RobloxTargetProfile JSON (Cluaupp target — not embedded in CLPP). */
11
+ targetProfilePath?: string;
12
+ /** Directory holding cached target artifacts. */
13
+ targetCacheDir?: string;
14
+ /** Run context from file tags (.server / .client / .plugin). */
15
+ runContext?: "Server" | "Client" | "Plugin" | "Shared";
16
+ /** Path to capability-profile.json for Context Safety rules. */
17
+ capabilityProfilePath?: string;
18
+ /** Path to Typed DataModel profile JSON (Rojo graph). */
19
+ datamodelProfilePath?: string;
8
20
  }
9
21
 
10
22
  export interface CompileDiagnostic {
@@ -16,6 +28,14 @@ export interface CompileDiagnostic {
16
28
  severity: string;
17
29
  }
18
30
 
31
+ export interface ModuleRequireEntry {
32
+ name: string;
33
+ from_file?: string;
34
+ to_file?: string;
35
+ fromFile?: string;
36
+ toFile?: string;
37
+ }
38
+
19
39
  export interface CompileArtifact {
20
40
  ok: boolean;
21
41
  luau: string;
@@ -28,6 +48,17 @@ export interface CompileArtifact {
28
48
  libraries: string[];
29
49
  error?: string;
30
50
  diagnostics?: CompileDiagnostic[];
51
+ sourceMap?: Array<{ luauLine: number; clppLine: number; file: string }>;
52
+ /** Named `import` / quoted include requires from CL++ (RFC 0003). */
53
+ requires?: ModuleRequireEntry[];
54
+ /** Selective @native candidates (RFC 0011). Not auto-applied as blanket. */
55
+ nativeHints?: string[];
56
+ /** Monomorphized generic symbols (`name__Type`). */
57
+ specialized?: string[];
58
+ /** Dense / SoA / buffer layout candidates. */
59
+ layoutHints?: string[];
60
+ /** False when compiled with --no-opt / optimize:false. */
61
+ optimized?: boolean;
31
62
  }
32
63
 
33
64
  export interface LanguageManifest {
@@ -45,7 +76,7 @@ export interface LanguageManifest {
45
76
  io: Array<{ clpp: string; luau: string }>;
46
77
  }
47
78
 
48
- export const MIN_CLPP_VERSION = "0.3.2";
79
+ export const MIN_CLPP_VERSION = "0.8.0";
49
80
 
50
81
  export const CLPP_INSTALL_HINT =
51
- "Install CL++ 0.3.2 or newer (clpp-setup.exe or `clpp setup`) and put `clpp` on PATH. 0.3.3 is the same JSON contract. Instances are class names (`Player player`, not `Player*`). Override with CLPP or CLPP_PATH.";
82
+ "Install CL++ 0.8.0+ (`clpp` from https://github.com/KartzRbx/CLPP/releases). Put it on PATH. Prefer `import { Name } from \"./x.clh\"` for language modules; angle `#include <clpp/…>` stays for host prelude. Override with CLPP or CLPP_PATH.";
@@ -0,0 +1,262 @@
1
+ // Roblox Luau style for Cluaupp emit: tabs, indent blocks, leave inner spacing alone.
2
+
3
+ const TAB = "\t";
4
+
5
+ type Carry = {
6
+ long: "none" | "string" | "comment";
7
+ eq: number;
8
+ };
9
+
10
+ function longClose(eq: number): string {
11
+ return `]${"=".repeat(eq)}]`;
12
+ }
13
+
14
+ function longOpenEq(text: string, index: number): number | null {
15
+ if (text[index] !== "[") {
16
+ return null;
17
+ }
18
+ let i = index + 1;
19
+ let eq = 0;
20
+ while (text[i] === "=") {
21
+ eq += 1;
22
+ i += 1;
23
+ }
24
+ if (text[i] === "[") {
25
+ return eq;
26
+ }
27
+ return null;
28
+ }
29
+
30
+ function skipLong(text: string, start: number, eq: number): { next: number; closed: boolean } {
31
+ const close = longClose(eq);
32
+ const found = text.indexOf(close, start);
33
+ if (found === -1) {
34
+ return { next: text.length, closed: false };
35
+ }
36
+ return { next: found + close.length, closed: true };
37
+ }
38
+
39
+ function firstToken(text: string): string {
40
+ const match = text.match(/^[A-Za-z_][A-Za-z0-9_]*|^\}/);
41
+ return match ? match[0] : "";
42
+ }
43
+
44
+ function identAt(text: string, from: number): string {
45
+ let j = from;
46
+ while (j < text.length && /[A-Za-z0-9_]/.test(text[j] || "")) {
47
+ j += 1;
48
+ }
49
+ return text.slice(from, j);
50
+ }
51
+
52
+ function scanDelta(text: string, carry: Carry): { delta: number; carry: Carry } {
53
+ let i = 0;
54
+ let delta = 0;
55
+ let long = carry.long;
56
+ let eq = carry.eq;
57
+
58
+ const consumeLong = (kind: "string" | "comment", depth: number, from: number): boolean => {
59
+ const skipped = skipLong(text, from, depth);
60
+ if (!skipped.closed) {
61
+ long = kind;
62
+ eq = depth;
63
+ i = text.length;
64
+ return false;
65
+ }
66
+ long = "none";
67
+ eq = 0;
68
+ i = skipped.next;
69
+ return true;
70
+ };
71
+
72
+ if (long === "string") {
73
+ consumeLong("string", eq, 0);
74
+ } else if (long === "comment") {
75
+ consumeLong("comment", eq, 0);
76
+ }
77
+
78
+ while (i < text.length) {
79
+ const ch = text[i] || "";
80
+ if (ch === "-" && text[i + 1] === "-") {
81
+ const commentEq = longOpenEq(text, i + 2);
82
+ if (commentEq !== null) {
83
+ const openEnd = i + 4 + commentEq;
84
+ if (!consumeLong("comment", commentEq, openEnd)) {
85
+ return { delta, carry: { long, eq } };
86
+ }
87
+ continue;
88
+ }
89
+ break;
90
+ }
91
+ if (ch === "'" || ch === '"') {
92
+ const quote = ch;
93
+ i += 1;
94
+ while (i < text.length) {
95
+ if (text[i] === "\\") {
96
+ i += 2;
97
+ continue;
98
+ }
99
+ if (text[i] === quote) {
100
+ i += 1;
101
+ break;
102
+ }
103
+ i += 1;
104
+ }
105
+ continue;
106
+ }
107
+ const strEq = longOpenEq(text, i);
108
+ if (strEq !== null) {
109
+ const openEnd = i + 2 + strEq;
110
+ if (!consumeLong("string", strEq, openEnd)) {
111
+ return { delta, carry: { long, eq } };
112
+ }
113
+ continue;
114
+ }
115
+ if (ch === "{") {
116
+ delta += 1;
117
+ i += 1;
118
+ continue;
119
+ }
120
+ if (ch === "}") {
121
+ delta -= 1;
122
+ i += 1;
123
+ continue;
124
+ }
125
+ if (/[A-Za-z_]/.test(ch)) {
126
+ const word = identAt(text, i);
127
+ i += word.length;
128
+ if (word === "function" || word === "then" || word === "do" || word === "repeat") {
129
+ delta += 1;
130
+ } else if (word === "end" || word === "until") {
131
+ delta -= 1;
132
+ }
133
+ continue;
134
+ }
135
+ i += 1;
136
+ }
137
+ return { delta, carry: { long, eq } };
138
+ }
139
+
140
+ export function formatLuauRoblox(source: string): string {
141
+ const lines = String(source).split(/\r?\n/);
142
+ const out: string[] = [];
143
+ let indent = 0;
144
+ let carry: Carry = { long: "none", eq: 0 };
145
+
146
+ for (const raw of lines) {
147
+ if (raw.trim() === "") {
148
+ out.push("");
149
+ continue;
150
+ }
151
+
152
+ if (carry.long !== "none") {
153
+ out.push(raw.trimEnd());
154
+ carry = scanDelta(raw, carry).carry;
155
+ continue;
156
+ }
157
+
158
+ const trimmed = raw.trim();
159
+ const lead = firstToken(trimmed);
160
+ const closer = lead === "end" || lead === "until" || lead === "else" || lead === "elseif" || lead === "}";
161
+ if (closer) {
162
+ indent = Math.max(0, indent - 1);
163
+ }
164
+
165
+ out.push(`${TAB.repeat(indent)}${trimmed}`);
166
+
167
+ const rest = closer ? trimmed.slice(lead.length) : trimmed;
168
+ const scanned = scanDelta(rest, carry);
169
+ indent = Math.max(0, indent + scanned.delta);
170
+ if (lead === "else") {
171
+ indent += 1;
172
+ }
173
+ carry = scanned.carry;
174
+ }
175
+
176
+ return `${collapseBlanks(compactTableFields(spaceScopes(out))).join("\n").replace(/[ \t]+$/gm, "").replace(/\n+$/, "")}\n`;
177
+ }
178
+
179
+ function lineIndent(line: string): number {
180
+ const match = line.match(/^\t*/);
181
+ return match ? match[0].length : 0;
182
+ }
183
+
184
+ function isComment(line: string): boolean {
185
+ return line.trimStart().startsWith("--");
186
+ }
187
+
188
+ function spaceScopes(lines: string[]): string[] {
189
+ const out: string[] = [];
190
+ for (let i = 0; i < lines.length; i += 1) {
191
+ const line = lines[i] || "";
192
+ const next = lines[i + 1];
193
+ out.push(line);
194
+ if (next === undefined || line.trim() === "" || next.trim() === "") {
195
+ continue;
196
+ }
197
+ if (lineIndent(line) !== lineIndent(next)) {
198
+ continue;
199
+ }
200
+
201
+ const lead = firstToken(line.trim());
202
+ const nextLead = firstToken(next.trim());
203
+ const nextCloses = nextLead === "else" || nextLead === "elseif" || nextLead === "end" || nextLead === "until" || nextLead === "}";
204
+ if ((lead === "end" || lead === "until" || lead === "}") && !nextCloses) {
205
+ out.push("");
206
+ continue;
207
+ }
208
+
209
+ const nextOpens = nextLead === "if" || nextLead === "for" || nextLead === "while" || nextLead === "repeat";
210
+ const prevOpens = lead === "if" || lead === "for" || lead === "while" || lead === "repeat" || lead === "else" || lead === "elseif";
211
+ if (nextOpens && !prevOpens && lead !== "end" && lead !== "until" && lead !== "}" && !isComment(line)) {
212
+ out.push("");
213
+ }
214
+ }
215
+ return out;
216
+ }
217
+
218
+ function collapseBlanks(lines: string[]): string[] {
219
+ const out: string[] = [];
220
+ for (const line of lines) {
221
+ if (line.trim() === "" && out.length > 0 && (out[out.length - 1] || "").trim() === "") {
222
+ continue;
223
+ }
224
+ out.push(line);
225
+ }
226
+ return out;
227
+ }
228
+
229
+ function tableName(line: string): string | null {
230
+ const trimmed = line.trim();
231
+ const field = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)\.[A-Za-z_][A-Za-z0-9_]*\s*=/);
232
+ if (field) {
233
+ return field[1];
234
+ }
235
+ const ctor = trimmed.match(/^(?:local|const)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\{\s*\}\s*$/);
236
+ return ctor ? ctor[1] : null;
237
+ }
238
+
239
+ function compactTableFields(lines: string[]): string[] {
240
+ const out: string[] = [];
241
+ for (let i = 0; i < lines.length; i += 1) {
242
+ const line = lines[i] || "";
243
+ if (line.trim() !== "") {
244
+ out.push(line);
245
+ continue;
246
+ }
247
+
248
+ const prev = out[out.length - 1];
249
+ let nextIndex = i + 1;
250
+ while (nextIndex < lines.length && (lines[nextIndex] || "").trim() === "") {
251
+ nextIndex += 1;
252
+ }
253
+ const next = lines[nextIndex];
254
+ const prevTable = prev ? tableName(prev) : null;
255
+ const nextTable = next !== undefined ? tableName(next) : null;
256
+ if (prevTable && nextTable && prevTable === nextTable) {
257
+ continue;
258
+ }
259
+ out.push(line);
260
+ }
261
+ return out;
262
+ }
package/src/clpp/index.ts CHANGED
@@ -1,7 +1,7 @@
1
- export type { CompileArtifact, CompileDiagnostic, CompileRequest, LanguageManifest } from "./contract.js";
1
+ export type { CompileArtifact, CompileDiagnostic, CompileRequest, LanguageManifest, ModuleRequireEntry } from "./contract.js";
2
2
  export { CLPP_INSTALL_HINT, MIN_CLPP_VERSION } from "./contract.js";
3
3
  export { compileViaClpp, compileViaClppAsync, clppManifest, clppVersion, hasClpp, resolveClppBinary } from "./runner.js";
4
- export { clppLuauToGame, shouldSkipInit } from "./postprocess.js";
4
+ export { clppLuauToGame, formatLuauRoblox, rewriteClppEmit, shouldSkipInit } from "./postprocess.js";
5
5
  export {
6
6
  SOURCE_EXTS,
7
7
  collectSources,
@@ -16,3 +16,14 @@ export {
16
16
  siblingHeader,
17
17
  toLuauPath,
18
18
  } from "./paths.js";
19
+ export {
20
+ applyNativeHintsToLuau,
21
+ collectLanguageModulePaths,
22
+ collectModuleRefs,
23
+ ensureGameServices,
24
+ fingerprintSourceWithDeps,
25
+ planLayoutFromHints,
26
+ resolveLanguageDeps,
27
+ resolveModuleFile,
28
+ rewriteGameRootedRequires,
29
+ } from "./modules.js";
@@ -0,0 +1,252 @@
1
+ /**
2
+ * CL++ 0.8 module surface for the Cluaupp host.
3
+ * Canonical: `import { Name } from "./path.clh"` — `#include` remains legacy / angle prelude.
4
+ */
5
+
6
+ import fs from "node:fs";
7
+ import path from "node:path";
8
+
9
+ export type ModuleRefKind = "import" | "include";
10
+
11
+ export interface NamedImportBinding {
12
+ name: string;
13
+ alias: string;
14
+ }
15
+
16
+ export interface ModuleRef {
17
+ kind: ModuleRefKind;
18
+ /** Raw path text from source (quoted or angle body). */
19
+ modulePath: string;
20
+ /** Angle `#include <…>` (host prelude) — not a language module. */
21
+ angled: boolean;
22
+ bindings: NamedImportBinding[];
23
+ index: number;
24
+ }
25
+
26
+ const IMPORT_RE =
27
+ /\bimport\s*\{([^}]*)\}\s*from\s*"([^"]+)"\s*;?/g;
28
+ const INCLUDE_RE = /#\s*include\s*(?:<([^>]+)>|"([^"]+)")/g;
29
+
30
+ function parseBindings(inner: string): NamedImportBinding[] {
31
+ const out: NamedImportBinding[] = [];
32
+ for (const part of inner.split(",")) {
33
+ const piece = part.trim();
34
+ if (!piece) continue;
35
+ const asMatch = /^([A-Za-z_]\w*)\s+as\s+([A-Za-z_]\w*)$/.exec(piece);
36
+ if (asMatch) {
37
+ out.push({ name: asMatch[1], alias: asMatch[2] });
38
+ continue;
39
+ }
40
+ const ident = /^([A-Za-z_]\w*)$/.exec(piece);
41
+ if (ident) {
42
+ out.push({ name: ident[1], alias: ident[1] });
43
+ }
44
+ }
45
+ return out;
46
+ }
47
+
48
+ /** Collect every `import { … } from` and quoted/angle `#include` in source order. */
49
+ export function collectModuleRefs(source: string): ModuleRef[] {
50
+ const refs: ModuleRef[] = [];
51
+ const text = String(source);
52
+
53
+ IMPORT_RE.lastIndex = 0;
54
+ let m: RegExpExecArray | null;
55
+ while ((m = IMPORT_RE.exec(text))) {
56
+ refs.push({
57
+ kind: "import",
58
+ modulePath: m[2].replace(/\\/g, "/"),
59
+ angled: false,
60
+ bindings: parseBindings(m[1]),
61
+ index: m.index,
62
+ });
63
+ }
64
+
65
+ INCLUDE_RE.lastIndex = 0;
66
+ while ((m = INCLUDE_RE.exec(text))) {
67
+ const angled = Boolean(m[1]);
68
+ const modulePath = (m[1] || m[2] || "").replace(/\\/g, "/");
69
+ if (!modulePath) continue;
70
+ refs.push({
71
+ kind: "include",
72
+ modulePath,
73
+ angled,
74
+ bindings: [],
75
+ index: m.index,
76
+ });
77
+ }
78
+
79
+ refs.sort((a, b) => a.index - b.index);
80
+ return refs;
81
+ }
82
+
83
+ /** Language modules only — named imports + quoted includes (not angle `<clpp/…>`). */
84
+ export function collectLanguageModulePaths(source: string): string[] {
85
+ const out: string[] = [];
86
+ const seen = new Set<string>();
87
+ for (const ref of collectModuleRefs(source)) {
88
+ if (ref.angled) continue;
89
+ if (seen.has(ref.modulePath)) continue;
90
+ seen.add(ref.modulePath);
91
+ out.push(ref.modulePath);
92
+ }
93
+ return out;
94
+ }
95
+
96
+ export function isAnglePlatformInclude(modulePath: string): boolean {
97
+ const p = modulePath.replace(/\\/g, "/").toLowerCase();
98
+ return (
99
+ p.startsWith("clpp/") ||
100
+ p.startsWith("cluaupp/") ||
101
+ p.startsWith("cluau/") ||
102
+ p === "roblox.clh" ||
103
+ p.endsWith("/roblox.clh")
104
+ );
105
+ }
106
+
107
+ /** Resolve a module path relative to the importing file (and optional src root). */
108
+ export function resolveModuleFile(
109
+ modulePath: string,
110
+ fromFile: string,
111
+ srcDir?: string,
112
+ ): string | null {
113
+ const raw = modulePath.replace(/[<>'"]/g, "").trim();
114
+ if (!raw || isAnglePlatformInclude(raw)) {
115
+ return null;
116
+ }
117
+ const bases: string[] = [path.dirname(fromFile)];
118
+ if (srcDir) {
119
+ bases.push(srcDir);
120
+ }
121
+ for (const base of bases) {
122
+ const candidate = path.resolve(base, raw);
123
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
124
+ return candidate;
125
+ }
126
+ // Allow omitting extension when the stem matches a source file.
127
+ for (const ext of [".clh", ".clp", ".clpp"]) {
128
+ const withExt = candidate.endsWith(ext) ? candidate : candidate + ext;
129
+ if (fs.existsSync(withExt) && fs.statSync(withExt).isFile()) {
130
+ return withExt;
131
+ }
132
+ }
133
+ }
134
+ return null;
135
+ }
136
+
137
+ /** Absolute paths of language deps (one hop). */
138
+ export function resolveLanguageDeps(source: string, fromFile: string, srcDir?: string): string[] {
139
+ const out: string[] = [];
140
+ const seen = new Set<string>();
141
+ for (const mod of collectLanguageModulePaths(source)) {
142
+ const resolved = resolveModuleFile(mod, fromFile, srcDir);
143
+ if (!resolved) continue;
144
+ const key = path.resolve(resolved);
145
+ if (seen.has(key)) continue;
146
+ seen.add(key);
147
+ out.push(key);
148
+ }
149
+ return out;
150
+ }
151
+
152
+ /**
153
+ * Fingerprint source + one-hop language deps so import/include edits invalidate incremental cache.
154
+ */
155
+ export function fingerprintSourceWithDeps(
156
+ source: string,
157
+ fromFile: string,
158
+ configKey: string,
159
+ srcDir: string | undefined,
160
+ hashText: (text: string) => string,
161
+ ): string {
162
+ const parts = [configKey, source];
163
+ for (const dep of resolveLanguageDeps(source, fromFile, srcDir)) {
164
+ try {
165
+ parts.push(`\n//dep:${dep.replace(/\\/g, "/")}\n`);
166
+ parts.push(fs.readFileSync(dep, "utf8"));
167
+ } catch {
168
+ parts.push(`\n//dep-missing:${dep.replace(/\\/g, "/")}\n`);
169
+ }
170
+ }
171
+ return hashText(parts.join(""));
172
+ }
173
+
174
+ /** Rewrite CL++ `script.Parent…Service…` walks to game-rooted requires. */
175
+ export function rewriteGameRootedRequires(luau: string): string {
176
+ let next = String(luau);
177
+ const services = [
178
+ "ReplicatedStorage",
179
+ "ReplicatedFirst",
180
+ "ServerScriptService",
181
+ "ServerStorage",
182
+ "StarterPlayer",
183
+ "StarterGui",
184
+ "Workspace",
185
+ ];
186
+ for (const svc of services) {
187
+ const re = new RegExp(`script(?:\\.Parent)+\\.${svc}\\.`, "g");
188
+ next = next.replace(re, `${svc}.`);
189
+ }
190
+ return next;
191
+ }
192
+
193
+ /** Ensure GetService lines exist for any game-rooted require used. */
194
+ export function ensureGameServices(luau: string): string {
195
+ let next = String(luau);
196
+ const needed: string[] = [];
197
+ for (const svc of ["ReplicatedStorage", "ReplicatedFirst", "ServerScriptService", "ServerStorage", "StarterPlayer"]) {
198
+ if (new RegExp(`require\\(${svc}\\.`).test(next) && !new RegExp(`GetService\\(\\s*"${svc}"\\s*\\)`).test(next)) {
199
+ needed.push(svc);
200
+ }
201
+ }
202
+ if (needed.length === 0) {
203
+ return next;
204
+ }
205
+ const block = needed.map((svc) => `const ${svc} = game:GetService("${svc}")`).join("\n");
206
+ const match = next.match(/^(?:--[^\n]*\n)+/);
207
+ if (!match) {
208
+ return `${block}\n${next}`;
209
+ }
210
+ const insertAt = match[0].length;
211
+ const rest = next.slice(insertAt).replace(/^\n*/, "\n");
212
+ return `${next.slice(0, insertAt)}\n${block}\n${rest}`;
213
+ }
214
+
215
+ /** Selective `@native` from CompileArtifact.nativeHints (never blanket). */
216
+ export function applyNativeHintsToLuau(luau: string, hints: string[] | undefined): string {
217
+ const set = new Set((hints || []).filter(Boolean));
218
+ if (set.size === 0) {
219
+ return luau;
220
+ }
221
+ const lines = luau.split(/\r?\n/);
222
+ const out: string[] = [];
223
+ for (let i = 0; i < lines.length; i++) {
224
+ const line = lines[i];
225
+ const m = line.match(/^(\s*)(local\s+function|function)\s+([A-Za-z_][\w.:]*)/);
226
+ if (m) {
227
+ const short = m[3].includes(":") ? m[3].split(":").pop()! : m[3].split(".").pop()!;
228
+ if ((set.has(m[3]) || set.has(short)) && (i === 0 || !lines[i - 1].includes("@native"))) {
229
+ out.push(`${m[1]}@native`);
230
+ }
231
+ }
232
+ out.push(line);
233
+ }
234
+ return out.join("\n");
235
+ }
236
+
237
+ export function planLayoutFromHints(layoutHints: string[] | undefined): { soa: string[]; buffer: string[] } {
238
+ const soa: string[] = [];
239
+ const buffer: string[] = [];
240
+ for (const h of layoutHints || []) {
241
+ if (h.startsWith("SoA:") || h.startsWith("SoACandidate:")) {
242
+ soa.push(h.replace(/^SoA(Candidate)?:/, ""));
243
+ } else if (
244
+ h.startsWith("BufferSpecialize:") ||
245
+ h.startsWith("BufferCandidate:") ||
246
+ h.startsWith("DenseNumeric:")
247
+ ) {
248
+ buffer.push(h.split(":")[1] ?? h);
249
+ }
250
+ }
251
+ return { soa, buffer };
252
+ }