@devaloop/devalang 0.0.1-alpha.16-hotfix.1 → 0.0.1-alpha.17

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 (235) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.devalang +6 -10
  3. package/.github/workflows/ci.yml +19 -8
  4. package/Cargo.toml +18 -2
  5. package/README.md +80 -33
  6. package/docs/CHANGELOG.md +56 -0
  7. package/docs/ROADMAP.md +6 -3
  8. package/examples/index.deva +52 -35
  9. package/out-tsc/bin/index.d.ts +2 -0
  10. package/out-tsc/core/functions/index.d.ts +37 -0
  11. package/out-tsc/core/functions/index.js +76 -0
  12. package/out-tsc/core/index.d.ts +6 -0
  13. package/out-tsc/core/index.js +22 -0
  14. package/out-tsc/core/types/index.d.ts +4 -0
  15. package/out-tsc/core/types/index.js +20 -0
  16. package/out-tsc/core/types/plugin.d.ts +18 -0
  17. package/out-tsc/core/types/plugin.js +2 -0
  18. package/out-tsc/core/types/result.d.ts +27 -0
  19. package/out-tsc/core/types/result.js +2 -0
  20. package/out-tsc/core/types/statement.d.ts +106 -0
  21. package/out-tsc/core/types/statement.js +2 -0
  22. package/out-tsc/core/types/value.d.ts +43 -0
  23. package/out-tsc/core/types/value.js +2 -0
  24. package/out-tsc/index.d.ts +7 -0
  25. package/out-tsc/index.js +41 -2
  26. package/out-tsc/pkg/devalang_core.d.ts +7 -0
  27. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +33 -0
  28. package/out-tsc/scripts/copy-wasm-dts.d.ts +1 -0
  29. package/out-tsc/scripts/copy-wasm-dts.js +73 -0
  30. package/out-tsc/scripts/postinstall.d.ts +1 -0
  31. package/out-tsc/scripts/postinstall.js +33 -23
  32. package/out-tsc/scripts/version/bump.d.ts +1 -0
  33. package/out-tsc/scripts/version/fetch.d.ts +1 -0
  34. package/out-tsc/scripts/version/index.d.ts +1 -0
  35. package/out-tsc/scripts/version/sync.d.ts +1 -0
  36. package/package.json +16 -4
  37. package/project-version.json +3 -3
  38. package/rust/cli/bank/api.rs +122 -0
  39. package/rust/cli/bank/commands.rs +275 -0
  40. package/rust/cli/bank/mod.rs +29 -0
  41. package/rust/cli/build/commands.rs +97 -0
  42. package/rust/cli/build/mod.rs +2 -0
  43. package/rust/cli/build/process.rs +146 -0
  44. package/rust/cli/{check.rs → check/mod.rs} +18 -31
  45. package/rust/cli/discover/commands.rs +253 -0
  46. package/rust/cli/discover/config.rs +111 -0
  47. package/rust/cli/discover/fs.rs +19 -0
  48. package/rust/cli/discover/install.rs +103 -0
  49. package/rust/cli/discover/metadata.rs +48 -0
  50. package/rust/cli/discover/mod.rs +5 -0
  51. package/rust/cli/{init.rs → init/commands.rs} +88 -87
  52. package/rust/cli/init/mod.rs +1 -0
  53. package/rust/{installer → cli/install}/addon.rs +5 -9
  54. package/rust/cli/install/bank.rs +53 -0
  55. package/rust/cli/{install.rs → install/commands.rs} +9 -9
  56. package/rust/{installer → cli/install}/mod.rs +2 -3
  57. package/rust/cli/install/plugin.rs +61 -0
  58. package/rust/cli/{login.rs → login/commands.rs} +8 -11
  59. package/rust/cli/login/mod.rs +1 -0
  60. package/rust/cli/mod.rs +2 -3
  61. package/rust/cli/{driver.rs → parser.rs} +19 -2
  62. package/rust/cli/play/commands.rs +324 -0
  63. package/rust/cli/play/io.rs +17 -0
  64. package/rust/cli/play/mod.rs +5 -0
  65. package/rust/cli/play/process.rs +150 -0
  66. package/rust/cli/play/realtime.rs +91 -0
  67. package/rust/cli/play/utils.rs +23 -0
  68. package/rust/cli/telemetry/commands.rs +22 -0
  69. package/rust/cli/telemetry/event_creator.rs +80 -0
  70. package/rust/cli/telemetry/mod.rs +3 -0
  71. package/rust/cli/telemetry/send.rs +51 -0
  72. package/rust/cli/{template.rs → template/commands.rs} +1 -1
  73. package/rust/cli/template/mod.rs +1 -0
  74. package/rust/cli/{update.rs → update/commands.rs} +6 -6
  75. package/rust/cli/update/mod.rs +1 -0
  76. package/rust/config/driver.rs +57 -72
  77. package/rust/config/mod.rs +1 -2
  78. package/rust/config/ops.rs +26 -0
  79. package/rust/config/settings.rs +60 -50
  80. package/rust/core/audio/engine/helpers.rs +146 -0
  81. package/rust/core/audio/engine/mod.rs +7 -0
  82. package/rust/core/audio/engine/sample.rs +298 -0
  83. package/rust/core/audio/engine/synth.rs +310 -0
  84. package/rust/core/audio/evaluator.rs +15 -12
  85. package/rust/core/audio/interpreter/arrow_call.rs +99 -24
  86. package/rust/core/audio/interpreter/call.rs +81 -60
  87. package/rust/core/audio/interpreter/condition.rs +3 -2
  88. package/rust/core/audio/interpreter/driver.rs +206 -151
  89. package/rust/core/audio/interpreter/let_.rs +1 -1
  90. package/rust/core/audio/interpreter/load.rs +2 -1
  91. package/rust/core/audio/interpreter/loop_.rs +7 -6
  92. package/rust/core/audio/interpreter/sleep.rs +2 -1
  93. package/rust/core/audio/interpreter/spawn.rs +45 -57
  94. package/rust/core/audio/interpreter/tempo.rs +31 -10
  95. package/rust/core/audio/interpreter/trigger.rs +2 -2
  96. package/rust/core/audio/loader/trigger.rs +4 -7
  97. package/rust/core/audio/player.rs +6 -0
  98. package/rust/core/audio/renderer.rs +5 -7
  99. package/rust/core/audio/special/env.rs +3 -1
  100. package/rust/core/audio/special/math.rs +4 -4
  101. package/rust/core/audio/special/modulator.rs +2 -2
  102. package/rust/core/builder/mod.rs +9 -3
  103. package/rust/core/debugger/lexer.rs +1 -1
  104. package/rust/core/debugger/mod.rs +6 -0
  105. package/rust/core/debugger/module.rs +4 -4
  106. package/rust/core/debugger/preprocessor.rs +1 -1
  107. package/rust/core/debugger/store.rs +2 -2
  108. package/rust/core/error/mod.rs +189 -0
  109. package/rust/core/lexer/handler/arrow.rs +1 -1
  110. package/rust/core/lexer/handler/at.rs +1 -1
  111. package/rust/core/lexer/handler/brace.rs +2 -2
  112. package/rust/core/lexer/handler/colon.rs +1 -1
  113. package/rust/core/lexer/handler/comment.rs +1 -1
  114. package/rust/core/lexer/handler/dot.rs +1 -1
  115. package/rust/core/lexer/handler/driver.rs +1 -1
  116. package/rust/core/lexer/handler/identifier.rs +1 -1
  117. package/rust/core/lexer/handler/mod.rs +1 -2
  118. package/rust/core/lexer/handler/number.rs +1 -1
  119. package/rust/core/lexer/handler/operator.rs +1 -1
  120. package/rust/core/lexer/handler/parenthesis.rs +2 -2
  121. package/rust/core/lexer/handler/slash.rs +1 -1
  122. package/rust/core/lexer/handler/string.rs +1 -1
  123. package/rust/core/lexer/mod.rs +22 -12
  124. package/rust/core/lexer/token.rs +90 -97
  125. package/rust/core/mod.rs +0 -1
  126. package/rust/core/parser/driver.rs +66 -13
  127. package/rust/core/parser/handler/arrow_call.rs +28 -8
  128. package/rust/core/parser/handler/at.rs +55 -21
  129. package/rust/core/parser/handler/bank.rs +14 -4
  130. package/rust/core/parser/handler/condition.rs +6 -3
  131. package/rust/core/parser/handler/dot.rs +2 -1
  132. package/rust/core/parser/handler/identifier/automate.rs +13 -16
  133. package/rust/core/parser/handler/identifier/call.rs +4 -4
  134. package/rust/core/parser/handler/identifier/emit.rs +9 -5
  135. package/rust/core/parser/handler/identifier/function.rs +20 -7
  136. package/rust/core/parser/handler/identifier/group.rs +11 -7
  137. package/rust/core/parser/handler/identifier/let_.rs +24 -9
  138. package/rust/core/parser/handler/identifier/mod.rs +6 -5
  139. package/rust/core/parser/handler/identifier/on.rs +16 -7
  140. package/rust/core/parser/handler/identifier/print.rs +6 -9
  141. package/rust/core/parser/handler/identifier/sleep.rs +12 -5
  142. package/rust/core/parser/handler/identifier/spawn.rs +4 -4
  143. package/rust/core/parser/handler/identifier/synth.rs +79 -9
  144. package/rust/core/parser/handler/loop_.rs +39 -14
  145. package/rust/core/parser/handler/tempo.rs +9 -5
  146. package/rust/core/parser/mod.rs +0 -1
  147. package/rust/core/parser/statement.rs +6 -137
  148. package/rust/core/plugin/loader.rs +41 -27
  149. package/rust/core/plugin/runner.rs +68 -17
  150. package/rust/core/preprocessor/loader.rs +155 -33
  151. package/rust/core/preprocessor/processor.rs +2 -2
  152. package/rust/core/preprocessor/resolver/bank.rs +6 -8
  153. package/rust/core/preprocessor/resolver/call.rs +20 -24
  154. package/rust/core/preprocessor/resolver/condition.rs +6 -8
  155. package/rust/core/preprocessor/resolver/driver.rs +14 -16
  156. package/rust/core/preprocessor/resolver/function.rs +6 -6
  157. package/rust/core/preprocessor/resolver/group.rs +6 -8
  158. package/rust/core/preprocessor/resolver/loop_.rs +8 -10
  159. package/rust/core/preprocessor/resolver/spawn.rs +19 -23
  160. package/rust/core/preprocessor/resolver/synth.rs +6 -8
  161. package/rust/core/preprocessor/resolver/tempo.rs +6 -8
  162. package/rust/core/preprocessor/resolver/trigger.rs +22 -19
  163. package/rust/core/preprocessor/resolver/value.rs +99 -4
  164. package/rust/core/store/export.rs +28 -28
  165. package/rust/core/store/function.rs +6 -0
  166. package/rust/core/store/global.rs +7 -1
  167. package/rust/core/store/import.rs +28 -28
  168. package/rust/core/store/variable.rs +1 -1
  169. package/rust/core/utils/mod.rs +0 -1
  170. package/rust/lib.rs +102 -9
  171. package/rust/main.rs +156 -45
  172. package/rust/types/Cargo.toml +8 -0
  173. package/rust/types/src/addons.rs +55 -0
  174. package/rust/types/src/ast.rs +198 -0
  175. package/rust/types/src/config.rs +74 -0
  176. package/rust/types/src/lib.rs +12 -0
  177. package/rust/types/src/telemetry.rs +85 -0
  178. package/rust/utils/Cargo.toml +23 -0
  179. package/rust/utils/{error.rs → src/error.rs} +186 -200
  180. package/rust/utils/src/file.rs +94 -0
  181. package/rust/utils/src/first_usage.rs +97 -0
  182. package/rust/utils/{mod.rs → src/lib.rs} +1 -1
  183. package/rust/utils/{logger.rs → src/logger.rs} +17 -12
  184. package/rust/utils/src/path.rs +88 -0
  185. package/rust/utils/src/signature.rs +41 -0
  186. package/rust/utils/{spinner.rs → src/spinner.rs} +3 -5
  187. package/rust/utils/src/version.rs +27 -0
  188. package/rust/utils/{watcher.rs → src/watcher.rs} +13 -1
  189. package/rust/web/api.rs +5 -0
  190. package/rust/web/cdn.rs +34 -0
  191. package/templates/minimal/README.md +98 -54
  192. package/templates/welcome/README.md +98 -54
  193. package/templates/welcome/src/index.deva +56 -8
  194. package/templates/welcome/src/variables.deva +2 -4
  195. package/tests/rust/TODO.md +0 -0
  196. package/tests/typescript/index.spec.ts +136 -0
  197. package/tests/typescript/playhead.spec.ts +36 -0
  198. package/tests/typescript/render_e2e.spec.ts +77 -0
  199. package/tsconfig.json +1 -1
  200. package/typescript/core/functions/index.ts +83 -0
  201. package/typescript/core/index.ts +6 -0
  202. package/typescript/core/types/index.ts +4 -0
  203. package/typescript/core/types/plugin.ts +19 -0
  204. package/typescript/core/types/result.ts +29 -0
  205. package/typescript/core/types/statement.ts +47 -0
  206. package/typescript/core/types/value.ts +29 -0
  207. package/typescript/index.ts +7 -2
  208. package/typescript/pkg/devalang_core.d.ts +4 -0
  209. package/typescript/scripts/copy-wasm-dts.ts +41 -0
  210. package/typescript/scripts/postinstall.ts +45 -32
  211. package/rust/cli/bank.rs +0 -462
  212. package/rust/cli/build.rs +0 -252
  213. package/rust/cli/generator.rs +0 -1
  214. package/rust/cli/play.rs +0 -1123
  215. package/rust/cli/telemetry.rs +0 -19
  216. package/rust/common/api.rs +0 -5
  217. package/rust/common/cdn.rs +0 -5
  218. package/rust/config/loader.rs +0 -165
  219. package/rust/config/stats.rs +0 -257
  220. package/rust/core/audio/engine.rs +0 -696
  221. package/rust/core/shared/bank.rs +0 -21
  222. package/rust/core/shared/duration.rs +0 -9
  223. package/rust/core/shared/mod.rs +0 -3
  224. package/rust/core/shared/value.rs +0 -35
  225. package/rust/core/utils/validation.rs +0 -35
  226. package/rust/installer/bank.rs +0 -62
  227. package/rust/installer/plugin.rs +0 -54
  228. package/rust/installer/utils.rs +0 -56
  229. package/rust/utils/file.rs +0 -38
  230. package/rust/utils/first_usage.rs +0 -76
  231. package/rust/utils/signature.rs +0 -19
  232. package/rust/utils/telemetry.rs +0 -292
  233. package/rust/utils/version.rs +0 -15
  234. /package/rust/{common → web}/mod.rs +0 -0
  235. /package/rust/{common → web}/sso.rs +0 -0
@@ -1,19 +1,10 @@
1
+ use devalang_types::{PluginExport, PluginInfo as SharedPluginInfo};
2
+ use devalang_utils::path as path_utils;
1
3
  use serde::Deserialize;
2
- use std::path::Path;
3
4
  use toml::Value as TomlValue;
4
5
 
5
6
  #[derive(Debug, Deserialize, Clone)]
6
- pub struct PluginInfo {
7
- pub name: String,
8
- pub version: Option<String>,
9
- pub description: Option<String>,
10
- pub author: Option<String>,
11
- #[serde(skip)]
12
- pub exports: Vec<ExportEntry>,
13
- }
14
-
15
- #[derive(Debug, Deserialize, Clone)]
16
- pub struct ExportEntry {
7
+ struct LocalExportEntry {
17
8
  pub name: String,
18
9
  #[serde(rename = "type")]
19
10
  pub kind: String,
@@ -22,24 +13,29 @@ pub struct ExportEntry {
22
13
  }
23
14
 
24
15
  #[derive(Debug, Deserialize, Clone)]
25
- pub struct PluginFile {
26
- pub plugin: PluginInfo,
16
+ struct LocalPluginFile {
17
+ pub plugin: LocalPluginInfo,
27
18
  #[serde(default)]
28
- pub export: Vec<ExportEntry>,
19
+ pub export: Vec<LocalExportEntry>,
29
20
  }
30
21
 
31
- /// Load a plugin from local .deva directory given author and name
32
- pub fn load_plugin(author: &str, name: &str) -> Result<(PluginInfo, Vec<u8>), String> {
33
- // Align with other loaders (banks) that use relative ./.deva paths
34
- let root = Path::new("./.deva");
35
- // Preferred layout: ./.deva/plugin/<author>.<name>/
36
- let plugin_dir_preferred = root.join("plugin").join(format!("{}.{}", author, name));
22
+ #[derive(Debug, Deserialize, Clone)]
23
+ struct LocalPluginInfo {
24
+ pub name: String,
25
+ pub version: Option<String>,
26
+ pub description: Option<String>,
27
+ pub author: Option<String>,
28
+ }
29
+
30
+ pub fn load_plugin(author: &str, name: &str) -> Result<(SharedPluginInfo, Vec<u8>), String> {
31
+ let root = path_utils::get_deva_dir()?;
32
+ let plugin_dir_preferred = root.join("plugins").join(format!("{}.{}", author, name));
37
33
  let toml_path_preferred = plugin_dir_preferred.join("plugin.toml");
38
34
  let wasm_path_preferred_bg = plugin_dir_preferred.join(format!("{}_bg.wasm", name));
39
35
  let wasm_path_preferred_plain = plugin_dir_preferred.join(format!("{}.wasm", name));
40
36
 
41
37
  // Legacy layout (fallback): ./.deva/plugin/<author>/<name>/
42
- let plugin_dir_fallback = root.join("plugin").join(author).join(name);
38
+ let plugin_dir_fallback = root.join("plugins").join(author).join(name);
43
39
  let toml_path_fallback = plugin_dir_fallback.join("plugin.toml");
44
40
  let wasm_path_fallback_bg = plugin_dir_fallback.join(format!("{}_bg.wasm", name));
45
41
  let wasm_path_fallback_plain = plugin_dir_fallback.join(format!("{}.wasm", name));
@@ -74,20 +70,38 @@ pub fn load_plugin(author: &str, name: &str) -> Result<(PluginInfo, Vec<u8>), St
74
70
 
75
71
  let toml_content = std::fs::read_to_string(&toml_path)
76
72
  .map_err(|e| format!("Failed to read '{}': {}", toml_path.display(), e))?;
77
- let plugin_file: PluginFile = toml::from_str(&toml_content)
73
+ let plugin_file: LocalPluginFile = toml::from_str(&toml_content)
78
74
  .map_err(|e| format!("Failed to parse '{}': {}", toml_path.display(), e))?;
79
75
 
80
76
  let wasm_bytes = std::fs::read(&wasm_path)
81
77
  .map_err(|e| format!("Failed to read '{}': {}", wasm_path.display(), e))?;
82
78
 
83
- let mut info = plugin_file.plugin.clone();
84
- info.exports = plugin_file.export.clone();
79
+ // Map local parsed plugin info to shared PluginInfo
80
+ let mut exports: Vec<PluginExport> = Vec::new();
81
+ for e in plugin_file.export.iter() {
82
+ exports.push(PluginExport {
83
+ name: e.name.clone(),
84
+ kind: e.kind.clone(),
85
+ default: e.default.clone(),
86
+ });
87
+ }
88
+
89
+ let info = SharedPluginInfo {
90
+ author: plugin_file
91
+ .plugin
92
+ .author
93
+ .unwrap_or_else(|| author.to_string()),
94
+ name: plugin_file.plugin.name.clone(),
95
+ version: plugin_file.plugin.version.clone(),
96
+ description: plugin_file.plugin.description.clone(),
97
+ exports,
98
+ };
85
99
 
86
100
  Ok((info, wasm_bytes))
87
101
  }
88
102
 
89
103
  /// Load a plugin from dot notation: "author.name"
90
- pub fn load_plugin_from_dot(dot: &str) -> Result<(PluginInfo, Vec<u8>), String> {
104
+ pub fn load_plugin_from_dot(dot: &str) -> Result<(SharedPluginInfo, Vec<u8>), String> {
91
105
  let mut parts = dot.split('.');
92
106
  let author = parts
93
107
  .next()
@@ -101,7 +115,7 @@ pub fn load_plugin_from_dot(dot: &str) -> Result<(PluginInfo, Vec<u8>), String>
101
115
  load_plugin(author, name)
102
116
  }
103
117
 
104
- pub fn load_plugin_from_uri(uri: &str) -> Result<(PluginInfo, Vec<u8>), String> {
118
+ pub fn load_plugin_from_uri(uri: &str) -> Result<(SharedPluginInfo, Vec<u8>), String> {
105
119
  if !uri.starts_with("devalang://plugin/") {
106
120
  return Err("Invalid plugin URI".into());
107
121
  }
@@ -1,10 +1,24 @@
1
1
  use std::collections::HashMap;
2
+
3
+ #[cfg(not(target_arch = "wasm32"))]
2
4
  use wasmtime::{Engine, Instance, Linker, Module, Store, TypedFunc};
3
5
 
6
+ #[cfg(not(target_arch = "wasm32"))]
7
+ type RenderFunc = TypedFunc<(i32, i32, f32, f32, i32, i32, i32), ()>;
8
+
9
+ #[cfg(not(target_arch = "wasm32"))]
4
10
  pub struct WasmPluginRunner {
5
11
  engine: Engine,
6
12
  }
7
13
 
14
+ #[cfg(not(target_arch = "wasm32"))]
15
+ impl Default for WasmPluginRunner {
16
+ fn default() -> Self {
17
+ Self::new()
18
+ }
19
+ }
20
+
21
+ #[cfg(not(target_arch = "wasm32"))]
8
22
  impl WasmPluginRunner {
9
23
  pub fn new() -> Self {
10
24
  let engine = Engine::default();
@@ -18,38 +32,32 @@ impl WasmPluginRunner {
18
32
  let mut store = Store::new(&self.engine, ());
19
33
  let linker = Linker::new(&self.engine);
20
34
 
21
- // Instantiate
22
35
  let instance = linker
23
36
  .instantiate(&mut store, &module)
24
37
  .map_err(|e| format!("Failed to instantiate wasm: {e}"))?;
25
38
 
26
- // Get exports
27
39
  let memory = instance
28
40
  .get_memory(&mut store, "memory")
29
41
  .ok_or_else(|| "WASM memory export not found".to_string())?;
30
42
 
31
- // wasm-bindgen usually exports a function taking (ptr: i32, len: i32) to represent &mut [f32]
32
43
  let func = instance
33
44
  .get_typed_func::<(i32, i32), ()>(&mut store, "process")
34
45
  .map_err(|_| "Exported function `process(i32,i32)` not found".to_string())?;
35
46
 
36
- // Copy host buffer into wasm memory
37
- let byte_len = (buffer.len() * std::mem::size_of::<f32>()) as i32;
47
+ let byte_len = std::mem::size_of_val(buffer) as i32;
38
48
  let ptr = Self::alloc_temp(&mut store, &instance, &memory, byte_len as usize)? as i32;
39
49
  let mem_slice = memory
40
50
  .data_mut(&mut store)
41
51
  .get_mut(ptr as usize..(ptr as usize) + (byte_len as usize))
42
52
  .ok_or_else(|| "Failed to get memory slice".to_string())?;
43
- // Safety: same alignment/layout
53
+
44
54
  let src_bytes =
45
55
  unsafe { std::slice::from_raw_parts(buffer.as_ptr() as *const u8, byte_len as usize) };
46
56
  mem_slice.copy_from_slice(src_bytes);
47
57
 
48
- // Call process
49
58
  func.call(&mut store, (ptr, buffer.len() as i32))
50
59
  .map_err(|e| format!("Error calling `process`: {e}"))?;
51
60
 
52
- // Copy back
53
61
  let mem_slice_after = memory
54
62
  .data(&store)
55
63
  .get(ptr as usize..(ptr as usize) + (byte_len as usize))
@@ -62,8 +70,6 @@ impl WasmPluginRunner {
62
70
  Ok(())
63
71
  }
64
72
 
65
- /// Render a note by invoking either `render_note_<name>` or a generic `render_note(ptr,len,freq,amp,duration_ms,sample_rate,channels)`.
66
- /// The buffer is interleaved stereo if channels=2. The buffer is modified in place.
67
73
  pub fn render_note_in_place(
68
74
  &self,
69
75
  wasm_bytes: &[u8],
@@ -90,7 +96,7 @@ impl WasmPluginRunner {
90
96
  .ok_or_else(|| "WASM memory export not found".to_string())?;
91
97
 
92
98
  // Try specific function first
93
- let mut func_opt: Option<TypedFunc<(i32, i32, f32, f32, i32, i32, i32), ()>> = None;
99
+ let mut func_opt: Option<RenderFunc> = None;
94
100
  if let Some(name) = synth_name {
95
101
  let specific = format!("render_note_{}", name);
96
102
  if let Ok(f) = instance
@@ -113,7 +119,7 @@ impl WasmPluginRunner {
113
119
  func_opt.ok_or_else(|| "Exported function `render_note` not found".to_string())?;
114
120
 
115
121
  // Copy host buffer into wasm memory
116
- let byte_len = (buffer.len() * std::mem::size_of::<f32>()) as i32;
122
+ let byte_len = std::mem::size_of_val(buffer) as i32;
117
123
  let ptr = Self::alloc_temp(&mut store, &instance, &memory, byte_len as usize)? as i32;
118
124
  let mem_slice = memory
119
125
  .data_mut(&mut store)
@@ -207,7 +213,7 @@ impl WasmPluginRunner {
207
213
  }
208
214
 
209
215
  // Try specific or generic render function
210
- let mut func_opt: Option<TypedFunc<(i32, i32, f32, f32, i32, i32, i32), ()>> = None;
216
+ let mut func_opt: Option<RenderFunc> = None;
211
217
  if let Some(name) = synth_name {
212
218
  let specific = format!("render_note_{}", name);
213
219
  if let Ok(f) = instance
@@ -228,7 +234,7 @@ impl WasmPluginRunner {
228
234
  func_opt.ok_or_else(|| "Exported function `render_note` not found".to_string())?;
229
235
 
230
236
  // Copy host buffer into wasm memory
231
- let byte_len = (buffer.len() * std::mem::size_of::<f32>()) as i32;
237
+ let byte_len = std::mem::size_of_val(buffer) as i32;
232
238
  let ptr = Self::alloc_temp(&mut store, &instance, &memory, byte_len as usize)? as i32;
233
239
  let mem_slice = memory
234
240
  .data_mut(&mut store)
@@ -283,10 +289,10 @@ impl WasmPluginRunner {
283
289
  // Fallback: grow memory and use end of memory as scratch space
284
290
  let current_len = memory.data_size(&mut *store);
285
291
  let need = size;
286
- let pages_needed = ((current_len + need + 0xffff) / 0x10000) as u64; // 64KiB pages
292
+ let pages_needed = (current_len + need).div_ceil(0x10000) as u64; // 64KiB pages
287
293
  let current_pages = memory.size(&mut *store);
288
- if pages_needed > (current_pages as u64) {
289
- let to_grow = pages_needed - (current_pages as u64);
294
+ if pages_needed > current_pages {
295
+ let to_grow = pages_needed - current_pages;
290
296
  memory
291
297
  .grow(&mut *store, to_grow)
292
298
  .map_err(|e| format!("memory.grow failed: {e}"))?;
@@ -294,3 +300,48 @@ impl WasmPluginRunner {
294
300
  Ok(current_len)
295
301
  }
296
302
  }
303
+
304
+ // Provide a minimal stub for wasm32 target so the crate compiles there.
305
+ #[cfg(target_arch = "wasm32")]
306
+ pub struct WasmPluginRunner;
307
+
308
+ #[cfg(target_arch = "wasm32")]
309
+ impl WasmPluginRunner {
310
+ pub fn new() -> Self {
311
+ WasmPluginRunner
312
+ }
313
+
314
+ pub fn process_in_place(&self, _wasm_bytes: &[u8], _buffer: &mut [f32]) -> Result<(), String> {
315
+ Err("Wasm plugin execution is not available in wasm builds".to_string())
316
+ }
317
+
318
+ pub fn render_note_in_place(
319
+ &self,
320
+ _wasm_bytes: &[u8],
321
+ _buffer: &mut [f32],
322
+ _synth_name: Option<&str>,
323
+ _freq: f32,
324
+ _amp: f32,
325
+ _duration_ms: i32,
326
+ _sample_rate: i32,
327
+ _channels: i32,
328
+ ) -> Result<(), String> {
329
+ Err("Wasm plugin rendering is not available in wasm builds".to_string())
330
+ }
331
+
332
+ pub fn render_note_with_params_in_place(
333
+ &self,
334
+ _wasm_bytes: &[u8],
335
+ _buffer: &mut [f32],
336
+ _synth_name: Option<&str>,
337
+ _freq: f32,
338
+ _amp: f32,
339
+ _duration_ms: i32,
340
+ _sample_rate: i32,
341
+ _channels: i32,
342
+ _params_num: &HashMap<String, f32>,
343
+ _params_str: Option<&HashMap<String, String>>,
344
+ ) -> Result<(), String> {
345
+ Err("Wasm plugin rendering is not available in wasm builds".to_string())
346
+ }
347
+ }
@@ -1,24 +1,25 @@
1
+ #[cfg(feature = "cli")]
1
2
  use crate::core::preprocessor::resolver::driver::{
2
3
  resolve_all_modules, resolve_and_flatten_all_modules,
3
4
  };
5
+ #[cfg(feature = "cli")]
4
6
  use crate::core::utils::path::resolve_relative_path;
5
- use crate::{
6
- config::loader::load_config,
7
- core::{
8
- error::ErrorHandler,
9
- lexer::{Lexer, token::Token},
10
- parser::{
11
- driver::Parser,
12
- statement::{Statement, StatementKind},
13
- },
14
- plugin::loader::load_plugin,
15
- preprocessor::{module::Module, processor::process_modules},
16
- shared::{bank::BankFile, value::Value},
17
- store::global::GlobalStore,
18
- utils::path::normalize_path,
7
+ #[cfg_attr(not(feature = "cli"), allow(unused_imports))]
8
+ use crate::core::{
9
+ error::ErrorHandler,
10
+ lexer::{Lexer, token::Token},
11
+ parser::{
12
+ driver::Parser,
13
+ statement::{Statement, StatementKind},
19
14
  },
20
- utils::logger::Logger,
15
+ plugin::loader::load_plugin,
16
+ preprocessor::{module::Module, processor::process_modules},
17
+ store::global::GlobalStore,
18
+ utils::path::normalize_path,
21
19
  };
20
+ use devalang_types::{BankFile, Value};
21
+ #[cfg(feature = "cli")]
22
+ use devalang_utils::logger::{LogLevel, Logger};
22
23
  use std::{collections::HashMap, path::Path};
23
24
 
24
25
  pub struct ModuleLoader {
@@ -38,7 +39,7 @@ impl ModuleLoader {
38
39
  Self {
39
40
  entry: entry.to_string(),
40
41
  output: output.to_string(),
41
- base_dir: base_dir,
42
+ base_dir,
42
43
  }
43
44
  }
44
45
 
@@ -50,9 +51,12 @@ impl ModuleLoader {
50
51
  ) -> Self {
51
52
  let normalized_entry_path = normalize_path(entry_path);
52
53
 
53
- let mut module = Module::new(&entry_path);
54
+ let mut module = Module::new(entry_path);
54
55
  module.content = content.to_string();
55
56
 
57
+ // Insert a module stub containing the provided content into the
58
+ // global store. This is used by the WASM APIs and tests which
59
+ // operate on in-memory sources instead of files on disk.
56
60
  global_store.insert_module(normalized_entry_path.to_string(), module);
57
61
 
58
62
  Self {
@@ -145,6 +149,17 @@ impl ModuleLoader {
145
149
  return Err(format!("Failed to inject bank triggers: {}", e));
146
150
  }
147
151
 
152
+ // Insert the updated module into the global store before processing so
153
+ // process_modules can operate on it and populate variable_table, imports,
154
+ // and other derived structures.
155
+ global_store
156
+ .modules
157
+ .insert(self.entry.clone(), updated_module.clone());
158
+
159
+ // Process modules to populate module.variable_table, import/export tables,
160
+ // and other derived structures so runtime execution can resolve groups/synths.
161
+ process_modules(self, global_store);
162
+
148
163
  for (plugin_name, alias) in self.extract_plugin_uses(&updated_module.statements) {
149
164
  self.load_plugin_and_register(&mut updated_module, &plugin_name, &alias, global_store);
150
165
  }
@@ -153,10 +168,32 @@ impl ModuleLoader {
153
168
  let mut error_handler = ErrorHandler::new();
154
169
  error_handler.detect_from_statements(&mut parser, &updated_module.statements);
155
170
 
156
- // Final step : insert the updated module back into the global store
157
- global_store
158
- .modules
159
- .insert(self.entry.clone(), updated_module);
171
+ // Final step : also expose module-level variables and functions into the global store
172
+ // so runtime evaluation (render_audio) can find group/synth definitions.
173
+ // Use the module instance that was actually processed by `process_modules`
174
+ // (it lives in `global_store.modules`) because `updated_module` is a local
175
+ // clone and won't contain the mutations applied by `process_modules`.
176
+ if let Some(stored_module) = global_store.modules.get(&self.entry) {
177
+ global_store
178
+ .variables
179
+ .variables
180
+ .extend(stored_module.variable_table.variables.clone());
181
+ global_store
182
+ .functions
183
+ .functions
184
+ .extend(stored_module.function_table.functions.clone());
185
+ } else {
186
+ // Fallback to the local updated_module if for any reason the module
187
+ // wasn't inserted into the store (defensive programming).
188
+ global_store
189
+ .variables
190
+ .variables
191
+ .extend(updated_module.variable_table.variables.clone());
192
+ global_store
193
+ .functions
194
+ .functions
195
+ .extend(updated_module.function_table.functions.clone());
196
+ }
160
197
 
161
198
  Ok(())
162
199
  }
@@ -193,7 +230,14 @@ impl ModuleLoader {
193
230
  }
194
231
 
195
232
  let lexer = Lexer::new();
196
- let tokens = lexer.lex_tokens(&path);
233
+ let tokens = match lexer.lex_tokens(&path) {
234
+ Ok(t) => t,
235
+ Err(e) => {
236
+ let logger = Logger::new();
237
+ logger.log_message(LogLevel::Error, &format!("Failed to lex '{}': {}", path, e));
238
+ return HashMap::new();
239
+ }
240
+ };
197
241
 
198
242
  let mut parser = Parser::new();
199
243
  parser.set_current_module(path.clone());
@@ -295,10 +339,17 @@ impl ModuleLoader {
295
339
  bank_name: &str,
296
340
  alias_override: Option<String>,
297
341
  ) -> Result<Module, String> {
298
- let default_alias = bank_name.split('.').last().unwrap_or(bank_name).to_string();
342
+ let default_alias = bank_name
343
+ .split('.')
344
+ .next_back()
345
+ .unwrap_or(bank_name)
346
+ .to_string();
299
347
  let alias_ref = alias_override.as_deref().unwrap_or(&default_alias);
300
348
 
301
- let bank_path = Path::new("./.deva/bank").join(bank_name);
349
+ let bank_path = match devalang_utils::path::get_deva_dir() {
350
+ Ok(dir) => dir.join("banks").join(bank_name),
351
+ Err(_) => Path::new("./.deva").join("banks").join(bank_name),
352
+ };
302
353
  let bank_toml_path = bank_path.join("bank.toml");
303
354
 
304
355
  if !bank_toml_path.exists() {
@@ -314,9 +365,18 @@ impl ModuleLoader {
314
365
  let mut bank_map = HashMap::new();
315
366
 
316
367
  for bank_trigger in parsed_bankfile.triggers.unwrap_or_default() {
317
- let trigger_name = bank_trigger.name.clone().replace("./", "");
318
- let bank_trigger_path = format!("devalang://bank/{}/{}", bank_name, trigger_name);
319
-
368
+ // Use the configured path from the bank file as the entity reference so
369
+ // that bank entries can point to files or nested paths. Clean common
370
+ // local prefixes like "./" to keep the URI tidy.
371
+ let entity_ref = bank_trigger
372
+ .path
373
+ .clone()
374
+ .replace("\\", "/")
375
+ .replace("./", "");
376
+ let bank_trigger_path = format!("devalang://bank/{}/{}", bank_name, entity_ref);
377
+
378
+ // Keep the trigger key as declared (bank_trigger.name) but expose its
379
+ // value as a devalang://bank URI pointing to the configured path.
320
380
  bank_map.insert(
321
381
  bank_trigger.name.clone(),
322
382
  Value::String(bank_trigger_path.clone()),
@@ -344,6 +404,7 @@ impl ModuleLoader {
344
404
  Ok(module.clone())
345
405
  }
346
406
 
407
+ #[cfg_attr(not(feature = "cli"), allow(dead_code))]
347
408
  fn extract_bank_decls(&self, statements: &[Statement]) -> Vec<(String, Option<String>)> {
348
409
  let mut banks = Vec::new();
349
410
 
@@ -371,7 +432,7 @@ impl ModuleLoader {
371
432
  if let StatementKind::Use { name, alias } = &stmt.kind {
372
433
  let alias_name = alias
373
434
  .clone()
374
- .unwrap_or_else(|| name.split('.').last().unwrap_or(name).to_string());
435
+ .unwrap_or_else(|| name.split('.').next_back().unwrap_or(name).to_string());
375
436
  plugins.push((name.clone(), alias_name));
376
437
  }
377
438
  }
@@ -415,16 +476,19 @@ impl ModuleLoader {
415
476
  let expected_uri = format!("devalang://plugin/{}.{}", author, name);
416
477
 
417
478
  // Detect local presence (preferred and legacy layouts)
418
- let root = Path::new("./.deva");
419
- let plugin_dir_preferred = root.join("plugin").join(format!("{}.{}", author, name));
479
+ let root = match devalang_utils::path::get_deva_dir() {
480
+ Ok(dir) => dir,
481
+ Err(_) => Path::new("./.deva").to_path_buf(),
482
+ };
483
+ let plugin_dir_preferred = root.join("plugins").join(format!("{}.{}", author, name));
420
484
  let toml_path_preferred = plugin_dir_preferred.join("plugin.toml");
421
- let plugin_dir_fallback = root.join("plugin").join(author).join(name);
485
+ let plugin_dir_fallback = root.join("plugins").join(author).join(name);
422
486
  let toml_path_fallback = plugin_dir_fallback.join("plugin.toml");
423
487
  let exists_locally = toml_path_preferred.exists() || toml_path_fallback.exists();
424
488
 
425
489
  if exists_locally {
426
490
  // Load config and verify plugin is declared
427
- let cfg_opt = load_config(None);
491
+ let cfg_opt = crate::config::ops::load_config(None);
428
492
  let mut declared = false;
429
493
  if let Some(cfg) = cfg_opt {
430
494
  if let Some(list) = cfg.plugins {
@@ -457,6 +521,11 @@ impl ModuleLoader {
457
521
  module
458
522
  .variable_table
459
523
  .set(alias.to_string(), Value::String(uri.clone()));
524
+ // Also expose alias at global level so runtime can resolve it
525
+ global_store
526
+ .variables
527
+ .set(alias.to_string(), Value::String(uri.clone()));
528
+
460
529
  if let Some((plugin_info, _)) =
461
530
  global_store.plugins.get(&format!("{}:{}", author, name))
462
531
  {
@@ -504,7 +573,60 @@ impl ModuleLoader {
504
573
  Value::String(format!("{}.{}", alias, exp.name)),
505
574
  );
506
575
  }
507
- _ => {}
576
+ _ => {
577
+ // Fallback: if default is present, map it to a Value dynamically
578
+ if let Some(def) = &exp.default {
579
+ let val = match def {
580
+ toml::Value::String(s) => Value::String(s.clone()),
581
+ toml::Value::Integer(i) => Value::Number(*i as f32),
582
+ toml::Value::Float(f) => Value::Number(*f as f32),
583
+ toml::Value::Boolean(b) => Value::Boolean(*b),
584
+ toml::Value::Array(arr) => Value::Array(
585
+ arr.iter()
586
+ .map(|v| match v {
587
+ toml::Value::String(s) => {
588
+ Value::String(s.clone())
589
+ }
590
+ toml::Value::Integer(i) => {
591
+ Value::Number(*i as f32)
592
+ }
593
+ toml::Value::Float(f) => {
594
+ Value::Number(*f as f32)
595
+ }
596
+ toml::Value::Boolean(b) => Value::Boolean(*b),
597
+ _ => Value::Null,
598
+ })
599
+ .collect(),
600
+ ),
601
+ toml::Value::Table(t) => {
602
+ let mut m = std::collections::HashMap::new();
603
+ for (k, v) in t.iter() {
604
+ let vv = match v {
605
+ toml::Value::String(s) => {
606
+ Value::String(s.clone())
607
+ }
608
+ toml::Value::Integer(i) => {
609
+ Value::Number(*i as f32)
610
+ }
611
+ toml::Value::Float(f) => {
612
+ Value::Number(*f as f32)
613
+ }
614
+ toml::Value::Boolean(b) => Value::Boolean(*b),
615
+ _ => Value::Null,
616
+ };
617
+ m.insert(k.clone(), vv);
618
+ }
619
+ Value::Map(m)
620
+ }
621
+ _ => Value::Null,
622
+ };
623
+ if val != Value::Null {
624
+ module
625
+ .variable_table
626
+ .set(format!("{}.{}", alias, exp.name), val);
627
+ }
628
+ }
629
+ }
508
630
  }
509
631
  }
510
632
  }
@@ -3,10 +3,10 @@ use std::{collections::HashMap, path::Path};
3
3
  use crate::core::{
4
4
  parser::statement::StatementKind,
5
5
  preprocessor::loader::ModuleLoader,
6
- shared::value::Value,
7
6
  store::global::GlobalStore,
8
7
  utils::path::{normalize_path, resolve_relative_path},
9
8
  };
9
+ use devalang_types::Value;
10
10
 
11
11
  pub fn process_modules(_module_loader: &ModuleLoader, global_store: &mut GlobalStore) {
12
12
  for module in global_store.modules.values_mut() {
@@ -46,7 +46,7 @@ pub fn process_modules(_module_loader: &ModuleLoader, global_store: &mut GlobalS
46
46
  StatementKind::Load { source, alias } => {
47
47
  let module_dir = Path::new(&module.path).parent().unwrap_or(Path::new(""));
48
48
 
49
- let resolved_path = normalize_path(&module_dir.join(source));
49
+ let resolved_path = normalize_path(module_dir.join(source));
50
50
 
51
51
  module
52
52
  .variable_table
@@ -1,12 +1,10 @@
1
- use crate::{
2
- core::{
3
- parser::statement::{Statement, StatementKind},
4
- preprocessor::module::Module,
5
- shared::value::Value,
6
- store::global::GlobalStore,
7
- },
8
- utils::logger::Logger,
1
+ use crate::core::{
2
+ parser::statement::{Statement, StatementKind},
3
+ preprocessor::module::Module,
4
+ store::global::GlobalStore,
9
5
  };
6
+ use devalang_types::Value;
7
+ use devalang_utils::logger::Logger;
10
8
 
11
9
  pub fn resolve_bank(
12
10
  stmt: &Statement,