sdl-mcp 0.12.2 → 0.12.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 (276) hide show
  1. package/README.md +10 -0
  2. package/config/sdlmcp.config.example.json +53 -8
  3. package/config/sdlmcp.config.schema.json +530 -106
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/benchmark/external-manifest.d.ts +162 -0
  6. package/dist/benchmark/external-manifest.d.ts.map +1 -0
  7. package/dist/benchmark/external-manifest.js +336 -0
  8. package/dist/benchmark/external-manifest.js.map +1 -0
  9. package/dist/benchmark/external-runner.d.ts +100 -0
  10. package/dist/benchmark/external-runner.d.ts.map +1 -0
  11. package/dist/benchmark/external-runner.js +1171 -0
  12. package/dist/benchmark/external-runner.js.map +1 -0
  13. package/dist/benchmark/output-file.d.ts +3 -0
  14. package/dist/benchmark/output-file.d.ts.map +1 -0
  15. package/dist/benchmark/output-file.js +8 -0
  16. package/dist/benchmark/output-file.js.map +1 -0
  17. package/dist/cli/argParsing.d.ts.map +1 -1
  18. package/dist/cli/argParsing.js +6 -0
  19. package/dist/cli/argParsing.js.map +1 -1
  20. package/dist/cli/commands/benchmark.d.ts.map +1 -1
  21. package/dist/cli/commands/benchmark.js +3 -2
  22. package/dist/cli/commands/benchmark.js.map +1 -1
  23. package/dist/cli/commands/index.d.ts +1 -0
  24. package/dist/cli/commands/index.d.ts.map +1 -1
  25. package/dist/cli/commands/index.js +13 -7
  26. package/dist/cli/commands/index.js.map +1 -1
  27. package/dist/cli/commands/init.d.ts +5 -1
  28. package/dist/cli/commands/init.d.ts.map +1 -1
  29. package/dist/cli/commands/init.js +73 -3
  30. package/dist/cli/commands/init.js.map +1 -1
  31. package/dist/cli/commands/serve.d.ts.map +1 -1
  32. package/dist/cli/commands/serve.js +2 -0
  33. package/dist/cli/commands/serve.js.map +1 -1
  34. package/dist/cli/index.js +2 -0
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/transport/http.d.ts +1 -35
  37. package/dist/cli/transport/http.d.ts.map +1 -1
  38. package/dist/cli/transport/http.js +151 -175
  39. package/dist/cli/transport/http.js.map +1 -1
  40. package/dist/cli/types.d.ts +1 -0
  41. package/dist/cli/types.d.ts.map +1 -1
  42. package/dist/config/constants.d.ts +30 -0
  43. package/dist/config/constants.d.ts.map +1 -1
  44. package/dist/config/constants.js +30 -0
  45. package/dist/config/constants.js.map +1 -1
  46. package/dist/config/types.d.ts +53 -1
  47. package/dist/config/types.d.ts.map +1 -1
  48. package/dist/config/types.js +62 -14
  49. package/dist/config/types.js.map +1 -1
  50. package/dist/db/ladybug-batching.d.ts +1 -0
  51. package/dist/db/ladybug-batching.d.ts.map +1 -1
  52. package/dist/db/ladybug-batching.js +1 -0
  53. package/dist/db/ladybug-batching.js.map +1 -1
  54. package/dist/db/ladybug-core.d.ts +3 -0
  55. package/dist/db/ladybug-core.d.ts.map +1 -1
  56. package/dist/db/ladybug-core.js +72 -2
  57. package/dist/db/ladybug-core.js.map +1 -1
  58. package/dist/db/ladybug-embeddings.d.ts +6 -1
  59. package/dist/db/ladybug-embeddings.d.ts.map +1 -1
  60. package/dist/db/ladybug-embeddings.js +6 -1
  61. package/dist/db/ladybug-embeddings.js.map +1 -1
  62. package/dist/db/ladybug-file-summaries.d.ts.map +1 -1
  63. package/dist/db/ladybug-file-summaries.js +23 -0
  64. package/dist/db/ladybug-file-summaries.js.map +1 -1
  65. package/dist/db/ladybug-queries.js +1 -1
  66. package/dist/db/ladybug-queries.js.map +1 -1
  67. package/dist/db/migrations/index.d.ts.map +1 -1
  68. package/dist/db/migrations/index.js +2 -0
  69. package/dist/db/migrations/index.js.map +1 -1
  70. package/dist/db/migrations/m007-copy-embeddings-to-symbol.d.ts +5 -9
  71. package/dist/db/migrations/m007-copy-embeddings-to-symbol.d.ts.map +1 -1
  72. package/dist/db/migrations/m007-copy-embeddings-to-symbol.js +24 -126
  73. package/dist/db/migrations/m007-copy-embeddings-to-symbol.js.map +1 -1
  74. package/dist/db/migrations/m021-remediate-symbol-embeddings.d.ts +5 -0
  75. package/dist/db/migrations/m021-remediate-symbol-embeddings.d.ts.map +1 -0
  76. package/dist/db/migrations/m021-remediate-symbol-embeddings.js +7 -0
  77. package/dist/db/migrations/m021-remediate-symbol-embeddings.js.map +1 -0
  78. package/dist/db/migrations/symbol-embedding-remediation.d.ts +57 -0
  79. package/dist/db/migrations/symbol-embedding-remediation.d.ts.map +1 -0
  80. package/dist/db/migrations/symbol-embedding-remediation.js +335 -0
  81. package/dist/db/migrations/symbol-embedding-remediation.js.map +1 -0
  82. package/dist/graph/layout/force-layout.d.ts +4 -0
  83. package/dist/graph/layout/force-layout.d.ts.map +1 -0
  84. package/dist/graph/layout/force-layout.js +127 -0
  85. package/dist/graph/layout/force-layout.js.map +1 -0
  86. package/dist/graph/layout/layout-service.d.ts +37 -0
  87. package/dist/graph/layout/layout-service.d.ts.map +1 -0
  88. package/dist/graph/layout/layout-service.js +172 -0
  89. package/dist/graph/layout/layout-service.js.map +1 -0
  90. package/dist/graph/layout/native-engine.d.ts +9 -0
  91. package/dist/graph/layout/native-engine.d.ts.map +1 -0
  92. package/dist/graph/layout/native-engine.js +59 -0
  93. package/dist/graph/layout/native-engine.js.map +1 -0
  94. package/dist/graph/layout/prng.d.ts +3 -0
  95. package/dist/graph/layout/prng.d.ts.map +1 -0
  96. package/dist/graph/layout/prng.js +19 -0
  97. package/dist/graph/layout/prng.js.map +1 -0
  98. package/dist/graph/layout/serializer.d.ts +3 -0
  99. package/dist/graph/layout/serializer.d.ts.map +1 -0
  100. package/dist/graph/layout/serializer.js +20 -0
  101. package/dist/graph/layout/serializer.js.map +1 -0
  102. package/dist/graph/layout/types.d.ts +30 -0
  103. package/dist/graph/layout/types.d.ts.map +1 -0
  104. package/dist/graph/layout/types.js +2 -0
  105. package/dist/graph/layout/types.js.map +1 -0
  106. package/dist/indexer/embeddings.d.ts +7 -0
  107. package/dist/indexer/embeddings.d.ts.map +1 -1
  108. package/dist/indexer/embeddings.js +44 -4
  109. package/dist/indexer/embeddings.js.map +1 -1
  110. package/dist/indexer/file-summary-embeddings.d.ts +12 -0
  111. package/dist/indexer/file-summary-embeddings.d.ts.map +1 -1
  112. package/dist/indexer/file-summary-embeddings.js +83 -21
  113. package/dist/indexer/file-summary-embeddings.js.map +1 -1
  114. package/dist/indexer/fileWalker.d.ts.map +1 -1
  115. package/dist/indexer/fileWalker.js +2 -1
  116. package/dist/indexer/fileWalker.js.map +1 -1
  117. package/dist/indexer/metrics-updater.d.ts.map +1 -1
  118. package/dist/indexer/metrics-updater.js +27 -4
  119. package/dist/indexer/metrics-updater.js.map +1 -1
  120. package/dist/indexer/watcher.d.ts.map +1 -1
  121. package/dist/indexer/watcher.js +2 -1
  122. package/dist/indexer/watcher.js.map +1 -1
  123. package/dist/mcp/context-response-projection.d.ts.map +1 -1
  124. package/dist/mcp/context-response-projection.js +49 -13
  125. package/dist/mcp/context-response-projection.js.map +1 -1
  126. package/dist/mcp/tools/search-edit/planner.d.ts.map +1 -1
  127. package/dist/mcp/tools/search-edit/planner.js +10 -3
  128. package/dist/mcp/tools/search-edit/planner.js.map +1 -1
  129. package/dist/observability/event-tap.d.ts +2 -1
  130. package/dist/observability/event-tap.d.ts.map +1 -1
  131. package/dist/observability/event-tap.js.map +1 -1
  132. package/dist/observability/service.d.ts +11 -4
  133. package/dist/observability/service.d.ts.map +1 -1
  134. package/dist/observability/service.js +83 -13
  135. package/dist/observability/service.js.map +1 -1
  136. package/dist/observability/types.d.ts +48 -2
  137. package/dist/observability/types.d.ts.map +1 -1
  138. package/dist/ui/.tsbuildinfo +1 -0
  139. package/dist/ui/vendor/fflate.js +2692 -0
  140. package/dist/ui/vendor/jsm/controls/ArcballControls.js +3543 -0
  141. package/dist/ui/vendor/jsm/controls/DragControls.js +452 -0
  142. package/dist/ui/vendor/jsm/controls/FirstPersonControls.js +488 -0
  143. package/dist/ui/vendor/jsm/controls/FlyControls.js +384 -0
  144. package/dist/ui/vendor/jsm/controls/MapControls.js +116 -0
  145. package/dist/ui/vendor/jsm/controls/OrbitControls.js +1963 -0
  146. package/dist/ui/vendor/jsm/controls/PointerLockControls.js +264 -0
  147. package/dist/ui/vendor/jsm/controls/TrackballControls.js +1001 -0
  148. package/dist/ui/vendor/jsm/controls/TransformControls.js +2003 -0
  149. package/dist/ui/vendor/jsm/loaders/3DMLoader.js +1836 -0
  150. package/dist/ui/vendor/jsm/loaders/3MFLoader.js +1621 -0
  151. package/dist/ui/vendor/jsm/loaders/AMFLoader.js +541 -0
  152. package/dist/ui/vendor/jsm/loaders/BVHLoader.js +484 -0
  153. package/dist/ui/vendor/jsm/loaders/ColladaLoader.js +153 -0
  154. package/dist/ui/vendor/jsm/loaders/DDSLoader.js +385 -0
  155. package/dist/ui/vendor/jsm/loaders/DRACOLoader.js +772 -0
  156. package/dist/ui/vendor/jsm/loaders/EXRLoader.js +3395 -0
  157. package/dist/ui/vendor/jsm/loaders/FBXLoader.js +4580 -0
  158. package/dist/ui/vendor/jsm/loaders/FontLoader.js +260 -0
  159. package/dist/ui/vendor/jsm/loaders/GCodeLoader.js +318 -0
  160. package/dist/ui/vendor/jsm/loaders/GLTFLoader.js +4860 -0
  161. package/dist/ui/vendor/jsm/loaders/HDRCubeTextureLoader.js +164 -0
  162. package/dist/ui/vendor/jsm/loaders/HDRLoader.js +463 -0
  163. package/dist/ui/vendor/jsm/loaders/IESLoader.js +379 -0
  164. package/dist/ui/vendor/jsm/loaders/KMZLoader.js +163 -0
  165. package/dist/ui/vendor/jsm/loaders/KTX2Loader.js +1276 -0
  166. package/dist/ui/vendor/jsm/loaders/KTXLoader.js +197 -0
  167. package/dist/ui/vendor/jsm/loaders/LDrawLoader.js +2510 -0
  168. package/dist/ui/vendor/jsm/loaders/LUT3dlLoader.js +205 -0
  169. package/dist/ui/vendor/jsm/loaders/LUTCubeLoader.js +190 -0
  170. package/dist/ui/vendor/jsm/loaders/LUTImageLoader.js +190 -0
  171. package/dist/ui/vendor/jsm/loaders/LWOLoader.js +1051 -0
  172. package/dist/ui/vendor/jsm/loaders/LottieLoader.js +130 -0
  173. package/dist/ui/vendor/jsm/loaders/MD2Loader.js +435 -0
  174. package/dist/ui/vendor/jsm/loaders/MDDLoader.js +147 -0
  175. package/dist/ui/vendor/jsm/loaders/MTLLoader.js +593 -0
  176. package/dist/ui/vendor/jsm/loaders/MaterialXLoader.js +1126 -0
  177. package/dist/ui/vendor/jsm/loaders/NRRDLoader.js +718 -0
  178. package/dist/ui/vendor/jsm/loaders/OBJLoader.js +955 -0
  179. package/dist/ui/vendor/jsm/loaders/PCDLoader.js +620 -0
  180. package/dist/ui/vendor/jsm/loaders/PDBLoader.js +272 -0
  181. package/dist/ui/vendor/jsm/loaders/PLYLoader.js +968 -0
  182. package/dist/ui/vendor/jsm/loaders/PVRLoader.js +270 -0
  183. package/dist/ui/vendor/jsm/loaders/RGBELoader.js +18 -0
  184. package/dist/ui/vendor/jsm/loaders/STLLoader.js +421 -0
  185. package/dist/ui/vendor/jsm/loaders/SVGLoader.js +3319 -0
  186. package/dist/ui/vendor/jsm/loaders/TDSLoader.js +1142 -0
  187. package/dist/ui/vendor/jsm/loaders/TGALoader.js +538 -0
  188. package/dist/ui/vendor/jsm/loaders/TIFFLoader.js +59 -0
  189. package/dist/ui/vendor/jsm/loaders/TTFLoader.js +261 -0
  190. package/dist/ui/vendor/jsm/loaders/USDLoader.js +303 -0
  191. package/dist/ui/vendor/jsm/loaders/USDZLoader.js +16 -0
  192. package/dist/ui/vendor/jsm/loaders/UltraHDRLoader.js +755 -0
  193. package/dist/ui/vendor/jsm/loaders/VOXLoader.js +919 -0
  194. package/dist/ui/vendor/jsm/loaders/VRMLLoader.js +3646 -0
  195. package/dist/ui/vendor/jsm/loaders/VTKLoader.js +1293 -0
  196. package/dist/ui/vendor/jsm/loaders/XYZLoader.js +143 -0
  197. package/dist/ui/vendor/jsm/loaders/collada/ColladaComposer.js +3044 -0
  198. package/dist/ui/vendor/jsm/loaders/collada/ColladaParser.js +1977 -0
  199. package/dist/ui/vendor/jsm/loaders/lwo/IFFParser.js +1217 -0
  200. package/dist/ui/vendor/jsm/loaders/lwo/LWO2Parser.js +414 -0
  201. package/dist/ui/vendor/jsm/loaders/lwo/LWO3Parser.js +373 -0
  202. package/dist/ui/vendor/jsm/loaders/usd/USDAParser.js +901 -0
  203. package/dist/ui/vendor/jsm/loaders/usd/USDCParser.js +1878 -0
  204. package/dist/ui/vendor/jsm/loaders/usd/USDComposer.js +4627 -0
  205. package/dist/ui/vendor/jsm/utils/BufferGeometryUtils.js +1501 -0
  206. package/dist/ui/vendor/jsm/utils/CameraUtils.js +82 -0
  207. package/dist/ui/vendor/jsm/utils/ColorUtils.js +76 -0
  208. package/dist/ui/vendor/jsm/utils/GeometryCompressionUtils.js +547 -0
  209. package/dist/ui/vendor/jsm/utils/GeometryUtils.js +226 -0
  210. package/dist/ui/vendor/jsm/utils/LDrawUtils.js +211 -0
  211. package/dist/ui/vendor/jsm/utils/SceneOptimizer.js +458 -0
  212. package/dist/ui/vendor/jsm/utils/SceneUtils.js +363 -0
  213. package/dist/ui/vendor/jsm/utils/ShadowMapViewer.js +244 -0
  214. package/dist/ui/vendor/jsm/utils/ShadowMapViewerGPU.js +233 -0
  215. package/dist/ui/vendor/jsm/utils/SkeletonUtils.js +496 -0
  216. package/dist/ui/vendor/jsm/utils/SortUtils.js +175 -0
  217. package/dist/ui/vendor/jsm/utils/UVsDebug.js +173 -0
  218. package/dist/ui/vendor/jsm/utils/WebGLTextureUtils.js +115 -0
  219. package/dist/ui/vendor/jsm/utils/WebGPUTextureUtils.js +81 -0
  220. package/dist/ui/vendor/jsm/utils/WorkerPool.js +167 -0
  221. package/dist/ui/vendor/three.core.min.js +6 -0
  222. package/dist/ui/vendor/three.module.min.js +6 -0
  223. package/dist/ui/viewer/ambient.js +39 -0
  224. package/dist/ui/viewer/api.js +93 -0
  225. package/dist/ui/viewer/chrome.js +36 -0
  226. package/dist/ui/viewer/edges.js +33 -0
  227. package/dist/ui/viewer/index.html +32 -0
  228. package/dist/ui/viewer/inspector.js +23 -0
  229. package/dist/ui/viewer/lenses/activity.js +24 -0
  230. package/dist/ui/viewer/lenses/community.js +5 -0
  231. package/dist/ui/viewer/lenses/edges.js +4 -0
  232. package/dist/ui/viewer/lenses/impact.js +6 -0
  233. package/dist/ui/viewer/lenses/search.js +69 -0
  234. package/dist/ui/viewer/lod.js +100 -0
  235. package/dist/ui/viewer/main.js +92 -0
  236. package/dist/ui/viewer/picking.js +45 -0
  237. package/dist/ui/viewer/scene.js +85 -0
  238. package/dist/ui/viewer/skins/default-skin.js +15 -0
  239. package/dist/ui/viewer/skins/loader.js +44 -0
  240. package/dist/ui/viewer/skins/manifest-schema.js +30 -0
  241. package/dist/ui/viewer/state.js +44 -0
  242. package/dist/ui/viewer/theme.js +23 -0
  243. package/dist/ui/viewer/universe.js +151 -0
  244. package/dist/ui/viewer/viewer.css +85 -0
  245. package/dist/util/safeRegex.d.ts.map +1 -1
  246. package/dist/util/safeRegex.js +221 -68
  247. package/dist/util/safeRegex.js.map +1 -1
  248. package/dist/viewer/legacy-graph.d.ts +40 -0
  249. package/dist/viewer/legacy-graph.d.ts.map +1 -0
  250. package/dist/viewer/legacy-graph.js +152 -0
  251. package/dist/viewer/legacy-graph.js.map +1 -0
  252. package/dist/viewer/routes.d.ts +10 -0
  253. package/dist/viewer/routes.d.ts.map +1 -0
  254. package/dist/viewer/routes.js +221 -0
  255. package/dist/viewer/routes.js.map +1 -0
  256. package/dist/viewer/service.d.ts +44 -0
  257. package/dist/viewer/service.d.ts.map +1 -0
  258. package/dist/viewer/service.js +160 -0
  259. package/dist/viewer/service.js.map +1 -0
  260. package/dist/viewer/types.d.ts +63 -0
  261. package/dist/viewer/types.d.ts.map +1 -0
  262. package/dist/viewer/types.js +2 -0
  263. package/dist/viewer/types.js.map +1 -0
  264. package/dist/viewer/viewer-config.d.ts +12 -0
  265. package/dist/viewer/viewer-config.d.ts.map +1 -0
  266. package/dist/viewer/viewer-config.js +36 -0
  267. package/dist/viewer/viewer-config.js.map +1 -0
  268. package/package.json +18 -9
  269. package/templates/SDL.md +19 -0
  270. package/templates/claude-code.json +8 -43
  271. package/dist/ui/graph.css +0 -108
  272. package/dist/ui/graph.d.ts +0 -2
  273. package/dist/ui/graph.d.ts.map +0 -1
  274. package/dist/ui/graph.html +0 -52
  275. package/dist/ui/graph.js +0 -259
  276. package/dist/ui/graph.js.map +0 -1
@@ -0,0 +1,901 @@
1
+ // Pre-compiled regex patterns for performance
2
+ const DEF_MATCH_REGEX = /^def\s+(?:(\w+)\s+)?"?([^"]+)"?$/;
3
+ const VARIANT_STRING_REGEX = /^string\s+(\w+)$/;
4
+ const ATTR_MATCH_REGEX = /^(?:uniform\s+)?(\w+(?:\[\])?)\s+(.+)$/;
5
+
6
+ // Spec types (must match USDCParser/USDComposer)
7
+ const SpecType = {
8
+ Attribute: 1,
9
+ Prim: 6,
10
+ Relationship: 8
11
+ };
12
+
13
+ class USDAParser {
14
+
15
+ parseText( text ) {
16
+
17
+ // Preprocess: strip comments and normalize multiline values
18
+ text = this._preprocess( text );
19
+
20
+ const root = {};
21
+
22
+ const lines = text.split( '\n' );
23
+
24
+ let string = null;
25
+ let target = root;
26
+
27
+ const stack = [ root ];
28
+
29
+ for ( const line of lines ) {
30
+
31
+ if ( line.includes( '=' ) ) {
32
+
33
+ // Find the first '=' that's not inside quotes
34
+ const eqIdx = this._findAssignmentOperator( line );
35
+
36
+ if ( eqIdx === - 1 ) {
37
+
38
+ string = line.trim();
39
+ continue;
40
+
41
+ }
42
+
43
+ const lhs = line.slice( 0, eqIdx ).trim();
44
+ const rhs = line.slice( eqIdx + 1 ).trim();
45
+
46
+ if ( rhs.endsWith( '{' ) ) {
47
+
48
+ const group = {};
49
+ stack.push( group );
50
+
51
+ target[ lhs ] = group;
52
+ target = group;
53
+
54
+ } else if ( rhs.endsWith( '(' ) ) {
55
+
56
+ // see #28631
57
+
58
+ const values = rhs.slice( 0, - 1 );
59
+ target[ lhs ] = values;
60
+
61
+ const meta = {};
62
+ stack.push( meta );
63
+
64
+ target = meta;
65
+
66
+ } else {
67
+
68
+ target[ lhs ] = rhs;
69
+
70
+ }
71
+
72
+ } else if ( line.includes( ':' ) && ! line.includes( '=' ) ) {
73
+
74
+ // Handle dictionary entries like "0: [(...)...]" for timeSamples
75
+ const colonIdx = line.indexOf( ':' );
76
+ const key = line.slice( 0, colonIdx ).trim();
77
+ const value = line.slice( colonIdx + 1 ).trim();
78
+
79
+ // Only process if key looks like a number (timeSamples frame)
80
+ if ( /^[\d.]+$/.test( key ) ) {
81
+
82
+ target[ key ] = value;
83
+
84
+ }
85
+
86
+ } else if ( line.endsWith( '{' ) ) {
87
+
88
+ string = line.slice( 0, - 1 ).trim() || string;
89
+
90
+ const group = target[ string ] || {};
91
+ stack.push( group );
92
+
93
+ target[ string ] = group;
94
+ target = group;
95
+
96
+ } else if ( line.endsWith( '}' ) ) {
97
+
98
+ stack.pop();
99
+
100
+ if ( stack.length === 0 ) continue;
101
+
102
+ target = stack[ stack.length - 1 ];
103
+
104
+ } else if ( line.endsWith( '(' ) ) {
105
+
106
+ const meta = {};
107
+ stack.push( meta );
108
+
109
+ string = line.split( '(' )[ 0 ].trim() || string;
110
+
111
+ target[ string ] = meta;
112
+ target = meta;
113
+
114
+ } else if ( line.endsWith( ')' ) ) {
115
+
116
+ stack.pop();
117
+
118
+ target = stack[ stack.length - 1 ];
119
+
120
+ } else if ( line.trim() ) {
121
+
122
+ string = line.trim();
123
+
124
+ }
125
+
126
+ }
127
+
128
+ return root;
129
+
130
+ }
131
+
132
+ _preprocess( text ) {
133
+
134
+ // Remove block comments /* ... */
135
+ text = this._stripBlockComments( text );
136
+
137
+ // Collapse triple-quoted strings into single lines
138
+ text = this._collapseTripleQuotedStrings( text );
139
+
140
+ // Remove line comments # ... (but preserve #usda header)
141
+ // Only remove # comments that aren't at the start of a line or after whitespace
142
+ const lines = text.split( '\n' );
143
+ const processed = [];
144
+
145
+ let inMultilineValue = false;
146
+ let bracketDepth = 0;
147
+ let parenDepth = 0;
148
+ let accumulated = '';
149
+
150
+ for ( let i = 0; i < lines.length; i ++ ) {
151
+
152
+ let line = lines[ i ];
153
+
154
+ // Strip inline comments (but not inside strings)
155
+ line = this._stripInlineComment( line );
156
+
157
+ // Track bracket/paren depth for multiline values
158
+ const trimmed = line.trim();
159
+
160
+ if ( inMultilineValue ) {
161
+
162
+ // Continue accumulating multiline value
163
+ accumulated += ' ' + trimmed;
164
+
165
+ // Update depths
166
+ for ( const ch of trimmed ) {
167
+
168
+ if ( ch === '[' ) bracketDepth ++;
169
+ else if ( ch === ']' ) bracketDepth --;
170
+ else if ( ch === '(' && bracketDepth > 0 ) parenDepth ++;
171
+ else if ( ch === ')' && bracketDepth > 0 ) parenDepth --;
172
+
173
+ }
174
+
175
+ // Check if multiline value is complete
176
+ if ( bracketDepth === 0 && parenDepth === 0 ) {
177
+
178
+ processed.push( accumulated );
179
+ accumulated = '';
180
+ inMultilineValue = false;
181
+
182
+ }
183
+
184
+ } else {
185
+
186
+ // Check if this line starts a multiline array value
187
+ // Look for patterns like "attr = [" or "attr = @path@[" without closing ]
188
+ if ( trimmed.includes( '=' ) ) {
189
+
190
+ const eqIdx = this._findAssignmentOperator( trimmed );
191
+
192
+ if ( eqIdx !== - 1 ) {
193
+
194
+ const rhs = trimmed.slice( eqIdx + 1 ).trim();
195
+
196
+ // Count brackets in the value part
197
+ let openBrackets = 0;
198
+ let closeBrackets = 0;
199
+
200
+ for ( const ch of rhs ) {
201
+
202
+ if ( ch === '[' ) openBrackets ++;
203
+ else if ( ch === ']' ) closeBrackets ++;
204
+
205
+ }
206
+
207
+ if ( openBrackets > closeBrackets ) {
208
+
209
+ // Multiline array detected
210
+ inMultilineValue = true;
211
+ bracketDepth = openBrackets - closeBrackets;
212
+ parenDepth = 0;
213
+ accumulated = trimmed;
214
+ continue;
215
+
216
+ }
217
+
218
+ }
219
+
220
+ }
221
+
222
+ processed.push( trimmed );
223
+
224
+ }
225
+
226
+ }
227
+
228
+ return processed.join( '\n' );
229
+
230
+ }
231
+
232
+ _stripBlockComments( text ) {
233
+
234
+ // Iteratively remove /* ... */ comments without regex backtracking
235
+ let result = '';
236
+ let i = 0;
237
+
238
+ while ( i < text.length ) {
239
+
240
+ // Check for block comment start
241
+ if ( text[ i ] === '/' && i + 1 < text.length && text[ i + 1 ] === '*' ) {
242
+
243
+ // Find the closing */
244
+ let j = i + 2;
245
+
246
+ while ( j < text.length ) {
247
+
248
+ if ( text[ j ] === '*' && j + 1 < text.length && text[ j + 1 ] === '/' ) {
249
+
250
+ // Found closing, skip past it
251
+ j += 2;
252
+ break;
253
+
254
+ }
255
+
256
+ j ++;
257
+
258
+ }
259
+
260
+ // Move past the comment (or to end if unclosed)
261
+ i = j;
262
+
263
+ } else {
264
+
265
+ result += text[ i ];
266
+ i ++;
267
+
268
+ }
269
+
270
+ }
271
+
272
+ return result;
273
+
274
+ }
275
+
276
+ _collapseTripleQuotedStrings( text ) {
277
+
278
+ let result = '';
279
+ let i = 0;
280
+
281
+ while ( i < text.length ) {
282
+
283
+ if ( i + 2 < text.length ) {
284
+
285
+ const triple = text.slice( i, i + 3 );
286
+
287
+ if ( triple === '\'\'\'' || triple === '"""' ) {
288
+
289
+ const quoteChar = triple;
290
+ result += quoteChar;
291
+ i += 3;
292
+
293
+ while ( i < text.length ) {
294
+
295
+ if ( i + 2 < text.length && text.slice( i, i + 3 ) === quoteChar ) {
296
+
297
+ result += quoteChar;
298
+ i += 3;
299
+ break;
300
+
301
+ } else {
302
+
303
+ if ( text[ i ] === '\n' ) {
304
+
305
+ result += '\\n';
306
+
307
+ } else if ( text[ i ] !== '\r' ) {
308
+
309
+ result += text[ i ];
310
+
311
+ }
312
+
313
+ i ++;
314
+
315
+ }
316
+
317
+ }
318
+
319
+ continue;
320
+
321
+ }
322
+
323
+ }
324
+
325
+ result += text[ i ];
326
+ i ++;
327
+
328
+ }
329
+
330
+ return result;
331
+
332
+ }
333
+
334
+ _stripInlineComment( line ) {
335
+
336
+ // Don't strip if line starts with #usda
337
+ if ( line.trim().startsWith( '#usda' ) ) return line;
338
+
339
+ // Find # that's not inside a string
340
+ let inString = false;
341
+ let stringChar = null;
342
+ let escaped = false;
343
+
344
+ for ( let i = 0; i < line.length; i ++ ) {
345
+
346
+ const ch = line[ i ];
347
+
348
+ if ( escaped ) {
349
+
350
+ escaped = false;
351
+ continue;
352
+
353
+ }
354
+
355
+ if ( ch === '\\' ) {
356
+
357
+ escaped = true;
358
+ continue;
359
+
360
+ }
361
+
362
+ if ( ! inString && ( ch === '"' || ch === '\'' ) ) {
363
+
364
+ inString = true;
365
+ stringChar = ch;
366
+
367
+ } else if ( inString && ch === stringChar ) {
368
+
369
+ inString = false;
370
+ stringChar = null;
371
+
372
+ } else if ( ! inString && ch === '#' ) {
373
+
374
+ // Found comment start outside of string
375
+ return line.slice( 0, i ).trimEnd();
376
+
377
+ }
378
+
379
+ }
380
+
381
+ return line;
382
+
383
+ }
384
+
385
+ _findAssignmentOperator( line ) {
386
+
387
+ // Find the first '=' that's not inside quotes
388
+ let inString = false;
389
+ let stringChar = null;
390
+ let escaped = false;
391
+
392
+ for ( let i = 0; i < line.length; i ++ ) {
393
+
394
+ const ch = line[ i ];
395
+
396
+ if ( escaped ) {
397
+
398
+ escaped = false;
399
+ continue;
400
+
401
+ }
402
+
403
+ if ( ch === '\\' ) {
404
+
405
+ escaped = true;
406
+ continue;
407
+
408
+ }
409
+
410
+ if ( ! inString && ( ch === '"' || ch === '\'' ) ) {
411
+
412
+ inString = true;
413
+ stringChar = ch;
414
+
415
+ } else if ( inString && ch === stringChar ) {
416
+
417
+ inString = false;
418
+ stringChar = null;
419
+
420
+ } else if ( ! inString && ch === '=' ) {
421
+
422
+ return i;
423
+
424
+ }
425
+
426
+ }
427
+
428
+ return - 1;
429
+
430
+ }
431
+
432
+ /**
433
+ * Parse USDA text and return raw spec data in specsByPath format.
434
+ * Used by USDComposer for unified scene composition.
435
+ */
436
+ parseData( text ) {
437
+
438
+ const root = this.parseText( text );
439
+ const specsByPath = {};
440
+
441
+ // Parse root metadata
442
+ const rootFields = {};
443
+ if ( '#usda 1.0' in root ) {
444
+
445
+ const header = root[ '#usda 1.0' ];
446
+
447
+ if ( header.upAxis ) {
448
+
449
+ rootFields.upAxis = header.upAxis.replace( /"/g, '' );
450
+
451
+ }
452
+
453
+ if ( header.defaultPrim ) {
454
+
455
+ rootFields.defaultPrim = header.defaultPrim.replace( /"/g, '' );
456
+
457
+ }
458
+
459
+ if ( header.metersPerUnit !== undefined ) {
460
+
461
+ rootFields.metersPerUnit = parseFloat( header.metersPerUnit );
462
+
463
+ }
464
+
465
+ if ( header.framesPerSecond !== undefined ) {
466
+
467
+ rootFields.framesPerSecond = parseFloat( header.framesPerSecond );
468
+
469
+ }
470
+
471
+ if ( header.timeCodesPerSecond !== undefined ) {
472
+
473
+ rootFields.timeCodesPerSecond = parseFloat( header.timeCodesPerSecond );
474
+
475
+ }
476
+
477
+ }
478
+
479
+ specsByPath[ '/' ] = { specType: SpecType.Prim, fields: rootFields };
480
+
481
+ // Walk the tree and build specsByPath
482
+ const walkTree = ( data, parentPath ) => {
483
+
484
+ const primChildren = [];
485
+
486
+ for ( const key in data ) {
487
+
488
+ // Skip metadata
489
+ if ( key === '#usda 1.0' ) continue;
490
+ if ( key === 'variants' ) continue;
491
+
492
+ // Check for primitive definitions
493
+ // Matches both 'def TypeName "name"' and 'def "name"' (no type)
494
+ const defMatch = key.match( DEF_MATCH_REGEX );
495
+ if ( defMatch ) {
496
+
497
+ const typeName = defMatch[ 1 ] || '';
498
+ const name = defMatch[ 2 ];
499
+ const path = parentPath === '/' ? '/' + name : parentPath + '/' + name;
500
+
501
+ primChildren.push( name );
502
+
503
+ const primFields = { typeName };
504
+ const primData = data[ key ];
505
+
506
+ // Extract attributes and relationships from this prim
507
+ this._extractPrimData( primData, path, primFields, specsByPath, SpecType );
508
+
509
+ specsByPath[ path ] = { specType: SpecType.Prim, fields: primFields };
510
+
511
+ // Recurse into children
512
+ walkTree( primData, path );
513
+
514
+ }
515
+
516
+ }
517
+
518
+ // Add primChildren to parent spec
519
+ if ( primChildren.length > 0 && specsByPath[ parentPath ] ) {
520
+
521
+ specsByPath[ parentPath ].fields.primChildren = primChildren;
522
+
523
+ }
524
+
525
+ };
526
+
527
+ walkTree( root, '/' );
528
+
529
+ // Fallback: infer elementSize for primvars:skel:jointIndices/jointWeights
530
+ // when not explicitly declared in the USDA text
531
+ this._inferSkelElementSize( specsByPath );
532
+
533
+ return { specsByPath };
534
+
535
+ }
536
+
537
+ _inferSkelElementSize( specsByPath ) {
538
+
539
+ // For each mesh prim with primvars:skel:jointIndices/jointWeights but no
540
+ // elementSize, infer it from the data: elementSize = array.length / numVertices.
541
+ for ( const path in specsByPath ) {
542
+
543
+ const spec = specsByPath[ path ];
544
+ if ( spec.specType !== SpecType.Prim || spec.fields.typeName !== 'Mesh' ) continue;
545
+
546
+ const pointsSpec = specsByPath[ path + '.points' ];
547
+ if ( ! pointsSpec || ! pointsSpec.fields.default ) continue;
548
+
549
+ const numVertices = pointsSpec.fields.default.length / 3;
550
+ if ( numVertices === 0 ) continue;
551
+
552
+ this._inferElementSize( specsByPath[ path + '.primvars:skel:jointIndices' ], numVertices );
553
+ this._inferElementSize( specsByPath[ path + '.primvars:skel:jointWeights' ], numVertices );
554
+
555
+ }
556
+
557
+ }
558
+
559
+ _inferElementSize( attrSpec, numVertices ) {
560
+
561
+ if ( ! attrSpec || attrSpec.fields.elementSize !== undefined || ! attrSpec.fields.default ) return;
562
+
563
+ const len = attrSpec.fields.default.length;
564
+ if ( len > 0 && len % numVertices === 0 ) attrSpec.fields.elementSize = len / numVertices;
565
+
566
+ }
567
+
568
+ _extractPrimData( data, path, primFields, specsByPath, SpecType ) {
569
+
570
+ if ( ! data || typeof data !== 'object' ) return;
571
+
572
+ for ( const key in data ) {
573
+
574
+ // Skip nested defs (handled by walkTree)
575
+ if ( key.startsWith( 'def ' ) ) continue;
576
+
577
+ if ( key === 'prepend references' ) {
578
+
579
+ primFields.references = [ data[ key ] ];
580
+ continue;
581
+
582
+ }
583
+
584
+ if ( key === 'payload' ) {
585
+
586
+ primFields.payload = data[ key ];
587
+ continue;
588
+
589
+ }
590
+
591
+ if ( key === 'variants' ) {
592
+
593
+ const variantSelection = {};
594
+ const variants = data[ key ];
595
+
596
+ for ( const vKey in variants ) {
597
+
598
+ const match = vKey.match( VARIANT_STRING_REGEX );
599
+ if ( match ) {
600
+
601
+ const variantSetName = match[ 1 ];
602
+ const variantValue = variants[ vKey ].replace( /"/g, '' );
603
+ variantSelection[ variantSetName ] = variantValue;
604
+
605
+ }
606
+
607
+ }
608
+
609
+ if ( Object.keys( variantSelection ).length > 0 ) {
610
+
611
+ primFields.variantSelection = variantSelection;
612
+
613
+ }
614
+
615
+ continue;
616
+
617
+ }
618
+
619
+ if ( key.startsWith( 'rel ' ) ) {
620
+
621
+ const relName = key.slice( 4 );
622
+ const relPath = path + '.' + relName;
623
+ const target = data[ key ].replace( /[<>]/g, '' );
624
+ specsByPath[ relPath ] = {
625
+ specType: SpecType.Relationship,
626
+ fields: { targetPaths: [ target ] }
627
+ };
628
+ continue;
629
+
630
+ }
631
+
632
+ // Handle xformOpOrder
633
+ if ( key.includes( 'xformOpOrder' ) ) {
634
+
635
+ const ops = data[ key ]
636
+ .replace( /[\[\]]/g, '' )
637
+ .split( ',' )
638
+ .map( s => s.trim().replace( /"/g, '' ) );
639
+ primFields.xformOpOrder = ops;
640
+ continue;
641
+
642
+ }
643
+
644
+ // Handle typed attributes
645
+ // Format: [qualifier] type attrName (e.g., "uniform token[] joints", "float3 position")
646
+ const attrMatch = key.match( ATTR_MATCH_REGEX );
647
+ if ( attrMatch ) {
648
+
649
+ const valueType = attrMatch[ 1 ];
650
+ const attrName = attrMatch[ 2 ];
651
+ const rawValue = data[ key ];
652
+
653
+ // Handle connection attributes (e.g., "inputs:normal.connect = </path>")
654
+ if ( attrName.endsWith( '.connect' ) ) {
655
+
656
+ const baseAttrName = attrName.slice( 0, - 8 ); // Remove '.connect'
657
+ const attrPath = path + '.' + baseAttrName;
658
+
659
+ // Parse connection path - extract from <path> format
660
+ let connPath = String( rawValue ).trim();
661
+ if ( connPath.startsWith( '<' ) ) connPath = connPath.slice( 1 );
662
+ if ( connPath.endsWith( '>' ) ) connPath = connPath.slice( 0, - 1 );
663
+
664
+ // Get or create the attribute spec
665
+ if ( ! specsByPath[ attrPath ] ) {
666
+
667
+ specsByPath[ attrPath ] = {
668
+ specType: SpecType.Attribute,
669
+ fields: { typeName: valueType }
670
+ };
671
+
672
+ }
673
+
674
+ specsByPath[ attrPath ].fields.connectionPaths = [ connPath ];
675
+ continue;
676
+
677
+ }
678
+
679
+ // Handle timeSamples attributes specially
680
+ if ( attrName.endsWith( '.timeSamples' ) && typeof rawValue === 'object' ) {
681
+
682
+ const baseAttrName = attrName.slice( 0, - 12 ); // Remove '.timeSamples'
683
+ const attrPath = path + '.' + baseAttrName;
684
+
685
+ // Parse timeSamples dictionary into times and values arrays
686
+ const times = [];
687
+ const values = [];
688
+
689
+ for ( const frameKey in rawValue ) {
690
+
691
+ const frame = parseFloat( frameKey );
692
+ if ( isNaN( frame ) ) continue;
693
+
694
+ times.push( frame );
695
+ values.push( this._parseAttributeValue( valueType, rawValue[ frameKey ] ) );
696
+
697
+ }
698
+
699
+ // Sort by time
700
+ const sorted = times.map( ( t, i ) => ( { t, v: values[ i ] } ) ).sort( ( a, b ) => a.t - b.t );
701
+
702
+ specsByPath[ attrPath ] = {
703
+ specType: SpecType.Attribute,
704
+ fields: {
705
+ timeSamples: { times: sorted.map( s => s.t ), values: sorted.map( s => s.v ) },
706
+ typeName: valueType
707
+ }
708
+ };
709
+
710
+ } else {
711
+
712
+ // Parse value based on type
713
+ const parsedValue = this._parseAttributeValue( valueType, rawValue );
714
+
715
+ // Store as attribute spec, preserving any existing fields
716
+ // (e.g. connectionPaths set by an earlier `.connect` form)
717
+ const attrPath = path + '.' + attrName;
718
+
719
+ if ( specsByPath[ attrPath ] ) {
720
+
721
+ specsByPath[ attrPath ].fields.default = parsedValue;
722
+ specsByPath[ attrPath ].fields.typeName = valueType;
723
+
724
+ } else {
725
+
726
+ specsByPath[ attrPath ] = {
727
+ specType: SpecType.Attribute,
728
+ fields: { default: parsedValue, typeName: valueType }
729
+ };
730
+
731
+ }
732
+
733
+ }
734
+
735
+ }
736
+
737
+ }
738
+
739
+ }
740
+
741
+ _parseAttributeValue( valueType, rawValue ) {
742
+
743
+ if ( rawValue === undefined || rawValue === null ) return undefined;
744
+
745
+ const str = String( rawValue ).trim();
746
+
747
+ // Array types
748
+ if ( valueType.endsWith( '[]' ) ) {
749
+
750
+ let result;
751
+
752
+ // Parse JSON-like arrays
753
+ try {
754
+
755
+ // Handle arrays with parentheses like [(1,2,3), (4,5,6)]
756
+ // Remove trailing comma (valid in USDA but not JSON)
757
+ let cleaned = str.replace( /\(/g, '[' ).replace( /\)/g, ']' );
758
+ if ( cleaned.endsWith( ',' ) ) cleaned = cleaned.slice( 0, - 1 );
759
+ const parsed = JSON.parse( cleaned );
760
+
761
+ // Flatten nested arrays for types like point3f[]
762
+ result = Array.isArray( parsed ) && Array.isArray( parsed[ 0 ] ) ? parsed.flat() : parsed;
763
+
764
+ } catch ( e ) {
765
+
766
+ // Try simple array parsing
767
+ const cleaned = str.replace( /[\[\]]/g, '' );
768
+ result = cleaned.split( ',' ).map( s => {
769
+
770
+ const trimmed = s.trim();
771
+ const num = parseFloat( trimmed );
772
+ return isNaN( num ) ? trimmed.replace( /"/g, '' ) : num;
773
+
774
+ } );
775
+
776
+ }
777
+
778
+ //reorder (w, x, y, z) to (x, y, z, w)
779
+ if ( valueType.startsWith( 'quat' ) ) {
780
+
781
+ for ( let i = 0; i < result.length; i += 4 ) {
782
+
783
+ const w = result[ i ];
784
+ result[ i ] = result[ i + 1 ];
785
+ result[ i + 1 ] = result[ i + 2 ];
786
+ result[ i + 2 ] = result[ i + 3 ];
787
+ result[ i + 3 ] = w;
788
+
789
+ }
790
+
791
+ }
792
+
793
+ return result;
794
+
795
+ }
796
+
797
+ // Vector types (double3, float3, point3f, etc.)
798
+ if ( valueType.includes( '3' ) || valueType.includes( '2' ) || valueType.includes( '4' ) ) {
799
+
800
+ // Parse (x, y, z) format
801
+ const cleaned = str.replace( /[()]/g, '' );
802
+ const values = cleaned.split( ',' ).map( s => parseFloat( s.trim() ) );
803
+ return values;
804
+
805
+ }
806
+
807
+ // Quaternion types (quatf, quatd, quath)
808
+ // Text format is (w, x, y, z), convert to (x, y, z, w)
809
+ if ( valueType.startsWith( 'quat' ) ) {
810
+
811
+ const cleaned = str.replace( /[()]/g, '' );
812
+ const values = cleaned.split( ',' ).map( s => parseFloat( s.trim() ) );
813
+ return [ values[ 1 ], values[ 2 ], values[ 3 ], values[ 0 ] ];
814
+
815
+ }
816
+
817
+ // Matrix types
818
+ if ( valueType.includes( 'matrix' ) ) {
819
+
820
+ const cleaned = str.replace( /[()]/g, '' );
821
+ const values = cleaned.split( ',' ).map( s => parseFloat( s.trim() ) );
822
+ return values;
823
+
824
+ }
825
+
826
+ // Scalar numeric types
827
+ if ( valueType === 'float' || valueType === 'double' || valueType === 'int' ) {
828
+
829
+ return parseFloat( str );
830
+
831
+ }
832
+
833
+ // String/token types
834
+ if ( valueType === 'string' || valueType === 'token' ) {
835
+
836
+ return this._parseString( str );
837
+
838
+ }
839
+
840
+ // Asset path
841
+ if ( valueType === 'asset' ) {
842
+
843
+ return str.replace( /@/g, '' ).replace( /"/g, '' );
844
+
845
+ }
846
+
847
+ // Default: return as string with quotes removed
848
+ return this._parseString( str );
849
+
850
+ }
851
+
852
+ _parseString( str ) {
853
+
854
+ // Remove surrounding quotes
855
+ if ( ( str.startsWith( '"' ) && str.endsWith( '"' ) ) ||
856
+ ( str.startsWith( '\'' ) && str.endsWith( '\'' ) ) ) {
857
+
858
+ str = str.slice( 1, - 1 );
859
+
860
+ }
861
+
862
+ // Handle escape sequences
863
+ let result = '';
864
+ let i = 0;
865
+
866
+ while ( i < str.length ) {
867
+
868
+ if ( str[ i ] === '\\' && i + 1 < str.length ) {
869
+
870
+ const next = str[ i + 1 ];
871
+
872
+ switch ( next ) {
873
+
874
+ case 'n': result += '\n'; break;
875
+ case 't': result += '\t'; break;
876
+ case 'r': result += '\r'; break;
877
+ case '\\': result += '\\'; break;
878
+ case '"': result += '"'; break;
879
+ case '\'': result += '\''; break;
880
+ default: result += next; break;
881
+
882
+ }
883
+
884
+ i += 2;
885
+
886
+ } else {
887
+
888
+ result += str[ i ];
889
+ i ++;
890
+
891
+ }
892
+
893
+ }
894
+
895
+ return result;
896
+
897
+ }
898
+
899
+ }
900
+
901
+ export { USDAParser };