@devaloop/devalang 0.0.1-beta.1 → 0.0.1-beta.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 (207) hide show
  1. package/.devalang +9 -10
  2. package/Cargo.toml +84 -80
  3. package/README.md +10 -7
  4. package/docs/CHANGELOG.md +83 -0
  5. package/docs/ROADMAP.md +6 -2
  6. package/docs/TODO.md +3 -14
  7. package/examples/bus.deva +10 -0
  8. package/examples/chain.deva +19 -0
  9. package/examples/effect.deva +2 -0
  10. package/examples/filter.deva +11 -0
  11. package/examples/lfo.deva +9 -0
  12. package/examples/plugin.deva +10 -10
  13. package/examples/routing.deva +23 -0
  14. package/examples/synth.deva +11 -1
  15. package/examples/synth_types.deva +17 -0
  16. package/out-tsc/bin/project-version.json +6 -0
  17. package/out-tsc/core/functions/index.d.ts +5 -0
  18. package/out-tsc/core/functions/index.js +11 -0
  19. package/out-tsc/pkg/devalang_core.d.ts +2 -0
  20. package/out-tsc/pkg/devalang_core.js +17 -2
  21. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +1 -0
  22. package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
  23. package/out-tsc/scripts/version/copy-to-binary.js +79 -0
  24. package/package.json +23 -10
  25. package/project-version.json +3 -3
  26. package/rust/bindings/Cargo.toml +9 -0
  27. package/rust/bindings/src/lib.rs +86 -0
  28. package/rust/cli/addon/commands.rs +35 -0
  29. package/rust/cli/addon/download.rs +234 -0
  30. package/rust/cli/addon/install.rs +33 -0
  31. package/rust/cli/addon/list.rs +224 -0
  32. package/rust/cli/addon/metadata.rs +124 -0
  33. package/rust/cli/addon/mod.rs +8 -0
  34. package/rust/cli/addon/remove.rs +271 -0
  35. package/rust/cli/addon/update.rs +305 -0
  36. package/rust/cli/{install/addon.rs → addon/utils.rs} +34 -43
  37. package/rust/cli/build/commands.rs +153 -103
  38. package/rust/cli/build/mod.rs +2 -2
  39. package/rust/cli/build/process.rs +165 -146
  40. package/rust/cli/check/mod.rs +208 -208
  41. package/rust/cli/discover/commands.rs +53 -31
  42. package/rust/cli/discover/config.rs +2 -4
  43. package/rust/cli/discover/install.rs +139 -28
  44. package/rust/cli/discover/metadata.rs +3 -3
  45. package/rust/cli/login/commands.rs +124 -124
  46. package/rust/cli/me/commands.rs +52 -0
  47. package/rust/cli/me/mod.rs +1 -0
  48. package/rust/cli/mod.rs +2 -2
  49. package/rust/cli/parser.rs +76 -70
  50. package/rust/cli/play/commands.rs +375 -324
  51. package/rust/cli/play/mod.rs +5 -5
  52. package/rust/cli/play/process.rs +159 -150
  53. package/rust/cli/play/realtime.rs +91 -91
  54. package/rust/cli/telemetry/commands.rs +22 -22
  55. package/rust/cli/telemetry/event_creator.rs +80 -80
  56. package/rust/cli/telemetry/mod.rs +3 -3
  57. package/rust/cli/telemetry/send.rs +51 -51
  58. package/rust/cli/template/commands.rs +69 -69
  59. package/rust/config/driver.rs +112 -103
  60. package/rust/config/mod.rs +3 -3
  61. package/rust/config/ops.rs +26 -26
  62. package/rust/config/settings.rs +101 -101
  63. package/rust/core/audio/engine/driver.rs +237 -0
  64. package/rust/core/audio/engine/export.rs +169 -0
  65. package/rust/core/audio/engine/helpers.rs +178 -170
  66. package/rust/core/audio/engine/mod.rs +56 -7
  67. package/rust/core/audio/engine/notes/dsp.rs +88 -0
  68. package/rust/core/audio/engine/notes/mod.rs +53 -0
  69. package/rust/core/audio/engine/notes/params.rs +294 -0
  70. package/rust/core/audio/engine/sample/insert.rs +300 -0
  71. package/rust/core/audio/engine/sample/mod.rs +40 -0
  72. package/rust/core/audio/engine/sample/padding.rs +170 -0
  73. package/rust/core/audio/evaluator/condition.rs +61 -0
  74. package/rust/core/audio/evaluator/mod.rs +9 -0
  75. package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +152 -310
  76. package/rust/core/audio/evaluator/rhs.rs +16 -0
  77. package/rust/core/audio/evaluator/string_expr.rs +94 -0
  78. package/rust/core/audio/interpreter/driver.rs +574 -542
  79. package/rust/core/audio/interpreter/mod.rs +2 -14
  80. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +179 -0
  81. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -0
  82. package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
  83. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +3 -0
  84. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +371 -0
  85. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
  86. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
  87. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
  88. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
  89. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
  90. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
  91. package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +2 -4
  92. package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +36 -5
  93. package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +72 -71
  94. package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +24 -26
  95. package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +36 -38
  96. package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +17 -19
  97. package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +115 -114
  98. package/rust/core/audio/interpreter/statements/mod.rs +12 -0
  99. package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
  100. package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +54 -4
  101. package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
  102. package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +242 -239
  103. package/rust/core/audio/loader/trigger.rs +98 -97
  104. package/rust/core/audio/mod.rs +6 -7
  105. package/rust/core/audio/special/easing.rs +189 -189
  106. package/rust/core/audio/special/env.rs +45 -45
  107. package/rust/core/audio/special/math.rs +134 -134
  108. package/rust/core/audio/special/modulator.rs +143 -143
  109. package/rust/core/builder/mod.rs +129 -86
  110. package/rust/core/debugger/{module.rs → logs.rs} +52 -55
  111. package/rust/core/debugger/mod.rs +30 -30
  112. package/rust/core/debugger/store.rs +38 -40
  113. package/rust/core/error/mod.rs +269 -269
  114. package/rust/core/lexer/driver.rs +2 -4
  115. package/rust/core/mod.rs +9 -10
  116. package/rust/core/parser/driver/block.rs +111 -0
  117. package/rust/core/parser/driver/cursor.rs +82 -0
  118. package/rust/core/parser/driver/driver_impl.rs +159 -0
  119. package/rust/core/parser/driver/mod.rs +6 -0
  120. package/rust/core/parser/driver/parse_array.rs +120 -0
  121. package/rust/core/parser/driver/parse_map.rs +247 -0
  122. package/rust/core/parser/driver/parser.rs +160 -0
  123. package/rust/core/parser/handler/arrow_call.rs +90 -15
  124. package/rust/core/parser/handler/at.rs +279 -279
  125. package/rust/core/parser/handler/bank.rs +104 -104
  126. package/rust/core/parser/handler/condition.rs +83 -83
  127. package/rust/core/parser/handler/dot.rs +148 -148
  128. package/rust/core/parser/handler/identifier/automate.rs +254 -254
  129. package/rust/core/parser/handler/identifier/call.rs +91 -91
  130. package/rust/core/parser/handler/identifier/emit.rs +70 -70
  131. package/rust/core/parser/handler/identifier/function.rs +113 -113
  132. package/rust/core/parser/handler/identifier/group.rs +89 -89
  133. package/rust/core/parser/handler/identifier/let_.rs +173 -173
  134. package/rust/core/parser/handler/identifier/mod.rs +55 -55
  135. package/rust/core/parser/handler/identifier/on.rs +107 -107
  136. package/rust/core/parser/handler/identifier/print.rs +49 -49
  137. package/rust/core/parser/handler/identifier/sleep.rs +96 -43
  138. package/rust/core/parser/handler/identifier/spawn.rs +91 -91
  139. package/rust/core/parser/handler/identifier/synth.rs +39 -3
  140. package/rust/core/parser/handler/loop_.rs +194 -194
  141. package/rust/core/parser/handler/pattern.rs +25 -2
  142. package/rust/core/parser/handler/tempo.rs +105 -57
  143. package/rust/core/parser/statement.rs +10 -11
  144. package/rust/core/plugin/loader.rs +137 -137
  145. package/rust/core/plugin/runner/mod.rs +11 -0
  146. package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +206 -72
  147. package/rust/core/plugin/runner/wasm32.rs +44 -0
  148. package/rust/core/preprocessor/loader/inject.rs +313 -0
  149. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
  150. package/rust/core/preprocessor/loader/mod.rs +235 -0
  151. package/rust/core/preprocessor/module.rs +55 -60
  152. package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +107 -114
  153. package/rust/core/preprocessor/processor/mod.rs +1 -0
  154. package/rust/core/preprocessor/resolver/function.rs +69 -69
  155. package/rust/core/preprocessor/resolver/group.rs +122 -94
  156. package/rust/core/preprocessor/resolver/pattern.rs +14 -2
  157. package/rust/core/store/global.rs +57 -61
  158. package/rust/core/store/mod.rs +1 -5
  159. package/rust/lib.rs +323 -308
  160. package/rust/macros/Cargo.toml +14 -0
  161. package/rust/macros/src/lib.rs +52 -0
  162. package/rust/main.rs +336 -143
  163. package/rust/types/Cargo.toml +1 -1
  164. package/rust/types/src/addons.rs +57 -55
  165. package/rust/types/src/config.rs +82 -74
  166. package/rust/types/src/lib.rs +15 -12
  167. package/rust/types/src/plugin.rs +20 -0
  168. package/rust/types/src/store.rs +139 -0
  169. package/rust/types/src/telemetry.rs +85 -85
  170. package/rust/utils/Cargo.toml +5 -2
  171. package/rust/utils/src/file.rs +477 -94
  172. package/rust/utils/src/first_usage.rs +97 -97
  173. package/rust/utils/src/lib.rs +9 -9
  174. package/rust/utils/src/logger.rs +200 -200
  175. package/rust/utils/src/path.rs +158 -88
  176. package/rust/utils/src/signature.rs +41 -41
  177. package/rust/utils/src/spinner.rs +20 -20
  178. package/rust/utils/src/version.rs +58 -27
  179. package/rust/utils/src/watcher.rs +46 -46
  180. package/rust/web/api.rs +5 -5
  181. package/rust/web/auth.rs +5 -0
  182. package/rust/web/cdn.rs +34 -34
  183. package/rust/web/forge.rs +5 -0
  184. package/rust/web/mod.rs +2 -0
  185. package/tests/integration.rs +21 -21
  186. package/typescript/core/functions/index.ts +11 -0
  187. package/typescript/pkg/devalang_core.ts +20 -4
  188. package/typescript/scripts/version/copy-to-binary.ts +82 -0
  189. package/rust/cli/bank/api.rs +0 -122
  190. package/rust/cli/bank/commands.rs +0 -275
  191. package/rust/cli/bank/mod.rs +0 -29
  192. package/rust/cli/install/bank.rs +0 -53
  193. package/rust/cli/install/commands.rs +0 -35
  194. package/rust/cli/install/mod.rs +0 -4
  195. package/rust/cli/install/plugin.rs +0 -61
  196. package/rust/core/audio/engine/sample.rs +0 -366
  197. package/rust/core/audio/engine/synth.rs +0 -325
  198. package/rust/core/audio/interpreter/arrow_call.rs +0 -311
  199. package/rust/core/audio/renderer.rs +0 -54
  200. package/rust/core/parser/driver.rs +0 -584
  201. package/rust/core/preprocessor/loader.rs +0 -637
  202. package/rust/core/store/export.rs +0 -28
  203. package/rust/core/store/function.rs +0 -40
  204. package/rust/core/store/import.rs +0 -28
  205. package/rust/core/store/variable.rs +0 -51
  206. package/rust/core/utils/mod.rs +0 -1
  207. package/rust/core/utils/path.rs +0 -37
@@ -1,208 +1,208 @@
1
- use crate::{
2
- config::driver::ProjectConfig,
3
- core::{
4
- debugger::{
5
- lexer::write_lexer_log_file,
6
- module::{write_module_function_log_file, write_module_variable_log_file},
7
- preprocessor::write_preprocessor_log_file,
8
- store::{write_function_log_file, write_variables_log_file},
9
- },
10
- preprocessor::loader::ModuleLoader,
11
- store::global::GlobalStore,
12
- utils::path::{find_entry_file, normalize_path},
13
- },
14
- };
15
-
16
- use devalang_utils::{
17
- logger::{LogLevel, Logger},
18
- spinner::start_spinner,
19
- watcher::watch_directory,
20
- };
21
-
22
- #[cfg(feature = "cli")]
23
- pub fn handle_check_command(
24
- config: Option<ProjectConfig>,
25
- entry: Option<String>,
26
- output: Option<String>,
27
- watch: bool,
28
- debug: bool,
29
- ) -> Result<(), String> {
30
- let fetched_entry = if entry.is_none() {
31
- config
32
- .as_ref()
33
- .and_then(|c| c.defaults.entry.clone())
34
- .unwrap_or_default()
35
- } else {
36
- entry.clone().unwrap_or_default()
37
- };
38
-
39
- let fetched_output = if output.is_none() {
40
- config
41
- .as_ref()
42
- .and_then(|c| c.defaults.output.clone())
43
- .unwrap_or_default()
44
- } else {
45
- output.clone().unwrap_or_default()
46
- };
47
-
48
- let fetched_watch = if watch {
49
- watch
50
- } else {
51
- config
52
- .as_ref()
53
- .and_then(|c| c.defaults.watch)
54
- .unwrap_or(false)
55
- };
56
-
57
- let logger = Logger::new();
58
-
59
- if fetched_entry.is_empty() {
60
- logger.log_message(
61
- LogLevel::Error,
62
- "Entry path is not specified. Please provide a valid entry path.",
63
- );
64
- return Err("missing entry path".to_string());
65
- }
66
- if fetched_output.is_empty() {
67
- logger.log_message(
68
- LogLevel::Error,
69
- "Output directory is not specified. Please provide a valid output directory.",
70
- );
71
- return Err("missing output directory".to_string());
72
- }
73
-
74
- let entry_file = match find_entry_file(&fetched_entry) {
75
- Some(p) => p,
76
- None => {
77
- logger.log_message(
78
- LogLevel::Error,
79
- &format!("❌ index.deva not found in directory: {}", fetched_entry),
80
- );
81
- return Err("index.deva not found".to_string());
82
- }
83
- };
84
-
85
- // SECTION Begin check
86
- if fetched_watch {
87
- let _ = begin_check(
88
- entry_file.clone(),
89
- fetched_output.clone(),
90
- debug,
91
- config.clone(),
92
- );
93
-
94
- logger.log_message(
95
- LogLevel::Watcher,
96
- &format!("Watching for changes in '{}'...", fetched_entry),
97
- );
98
-
99
- let cfg_for_watch = config.clone();
100
- watch_directory(entry_file.clone(), move || {
101
- logger.log_message(LogLevel::Watcher, "Detected changes, re-checking...");
102
- if let Err(e) = begin_check(
103
- entry_file.clone(),
104
- fetched_output.clone(),
105
- debug,
106
- cfg_for_watch.clone(),
107
- ) {
108
- eprintln!("[check] failed: {}", e);
109
- }
110
- })
111
- .unwrap();
112
- } else {
113
- begin_check(
114
- entry_file.clone(),
115
- fetched_output.clone(),
116
- debug,
117
- config.clone(),
118
- )?;
119
- }
120
- Ok(())
121
- }
122
-
123
- fn begin_check(
124
- entry: String,
125
- output: String,
126
- debug: bool,
127
- _config: Option<ProjectConfig>,
128
- ) -> Result<(), String> {
129
- let spinner = start_spinner("Checking...");
130
-
131
- let duration = std::time::Instant::now();
132
-
133
- let normalized_entry_file = normalize_path(&entry);
134
- let normalized_output_dir = normalize_path(&output);
135
-
136
- let mut global_store = GlobalStore::new();
137
- let module_loader = ModuleLoader::new(&normalized_entry_file, &normalized_output_dir);
138
-
139
- // SECTION Load
140
- // NOTE: We don't use modules in the check command, but we still need to load them
141
- let modules = module_loader.load_all_modules(&mut global_store);
142
-
143
- // Debugging: Log loaded modules and errors
144
- let logger = Logger::new();
145
- logger.log_message(LogLevel::Info, "Loaded modules:");
146
- for module_name in modules.0.keys() {
147
- logger.log_message(LogLevel::Info, &format!("- {}", module_name));
148
- }
149
-
150
- if debug {
151
- for (module_path, module) in global_store.modules.clone() {
152
- write_module_variable_log_file(
153
- &normalized_output_dir,
154
- &module_path,
155
- &module.variable_table,
156
- );
157
- write_module_function_log_file(
158
- &normalized_output_dir,
159
- &module_path,
160
- &module.function_table,
161
- );
162
- }
163
-
164
- write_lexer_log_file(
165
- &normalized_output_dir,
166
- "lexer_tokens.log",
167
- modules.0.clone(),
168
- );
169
- write_preprocessor_log_file(
170
- &normalized_output_dir,
171
- "resolved_statements.log",
172
- modules.1.clone(),
173
- );
174
- write_variables_log_file(
175
- &normalized_output_dir,
176
- "global_variables.log",
177
- global_store.variables.clone(),
178
- );
179
- write_function_log_file(
180
- &normalized_output_dir,
181
- "global_functions.log",
182
- global_store.functions.clone(),
183
- );
184
- }
185
-
186
- let all_errors = crate::core::error::collect_all_errors_with_modules(&modules.1);
187
-
188
- let (warnings, criticals) = crate::core::error::partition_errors(all_errors);
189
- crate::core::error::log_errors_with_stack("Check", &warnings, &criticals);
190
-
191
- if !criticals.is_empty() {
192
- spinner.finish_and_clear();
193
- return Err("check failed with critical errors".to_string());
194
- } else {
195
- logger.log_message(LogLevel::Success, "No errors detected.");
196
-
197
- let success_message = format!(
198
- "Check completed successfully in {:.2?}. Output files written to: '{}'",
199
- duration.elapsed(),
200
- normalized_output_dir
201
- );
202
-
203
- spinner.finish_and_clear();
204
- logger.log_message(LogLevel::Success, &success_message);
205
- }
206
-
207
- Ok(())
208
- }
1
+ use crate::{
2
+ config::driver::ProjectConfig,
3
+ core::{
4
+ debugger::{
5
+ lexer::write_lexer_log_file,
6
+ logs::{write_module_function_log_file, write_module_variable_log_file},
7
+ preprocessor::write_preprocessor_log_file,
8
+ store::{write_function_log_file, write_variables_log_file},
9
+ },
10
+ preprocessor::loader::ModuleLoader,
11
+ store::global::GlobalStore,
12
+ },
13
+ };
14
+ use devalang_utils::path::{find_entry_file, normalize_path};
15
+
16
+ use devalang_utils::{
17
+ logger::{LogLevel, Logger},
18
+ spinner::start_spinner,
19
+ watcher::watch_directory,
20
+ };
21
+
22
+ #[cfg(feature = "cli")]
23
+ pub fn handle_check_command(
24
+ config: Option<ProjectConfig>,
25
+ entry: Option<String>,
26
+ output: Option<String>,
27
+ watch: bool,
28
+ debug: bool,
29
+ ) -> Result<(), String> {
30
+ let fetched_entry = if entry.is_none() {
31
+ config
32
+ .as_ref()
33
+ .and_then(|c| c.defaults.entry.clone())
34
+ .unwrap_or_default()
35
+ } else {
36
+ entry.clone().unwrap_or_default()
37
+ };
38
+
39
+ let fetched_output = if output.is_none() {
40
+ config
41
+ .as_ref()
42
+ .and_then(|c| c.defaults.output.clone())
43
+ .unwrap_or_default()
44
+ } else {
45
+ output.clone().unwrap_or_default()
46
+ };
47
+
48
+ let fetched_watch = if watch {
49
+ watch
50
+ } else {
51
+ config
52
+ .as_ref()
53
+ .and_then(|c| c.defaults.watch)
54
+ .unwrap_or(false)
55
+ };
56
+
57
+ let logger = Logger::new();
58
+
59
+ if fetched_entry.is_empty() {
60
+ logger.log_message(
61
+ LogLevel::Error,
62
+ "Entry path is not specified. Please provide a valid entry path.",
63
+ );
64
+ return Err("missing entry path".to_string());
65
+ }
66
+ if fetched_output.is_empty() {
67
+ logger.log_message(
68
+ LogLevel::Error,
69
+ "Output directory is not specified. Please provide a valid output directory.",
70
+ );
71
+ return Err("missing output directory".to_string());
72
+ }
73
+
74
+ let entry_file = match find_entry_file(&fetched_entry) {
75
+ Some(p) => p,
76
+ None => {
77
+ logger.log_message(
78
+ LogLevel::Error,
79
+ &format!("❌ index.deva not found in directory: {}", fetched_entry),
80
+ );
81
+ return Err("index.deva not found".to_string());
82
+ }
83
+ };
84
+
85
+ // SECTION Begin check
86
+ if fetched_watch {
87
+ let _ = begin_check(
88
+ entry_file.clone(),
89
+ fetched_output.clone(),
90
+ debug,
91
+ config.clone(),
92
+ );
93
+
94
+ logger.log_message(
95
+ LogLevel::Watcher,
96
+ &format!("Watching for changes in '{}'...", fetched_entry),
97
+ );
98
+
99
+ let cfg_for_watch = config.clone();
100
+ watch_directory(entry_file.clone(), move || {
101
+ logger.log_message(LogLevel::Watcher, "Detected changes, re-checking...");
102
+ if let Err(e) = begin_check(
103
+ entry_file.clone(),
104
+ fetched_output.clone(),
105
+ debug,
106
+ cfg_for_watch.clone(),
107
+ ) {
108
+ eprintln!("[check] failed: {}", e);
109
+ }
110
+ })
111
+ .unwrap();
112
+ } else {
113
+ begin_check(
114
+ entry_file.clone(),
115
+ fetched_output.clone(),
116
+ debug,
117
+ config.clone(),
118
+ )?;
119
+ }
120
+ Ok(())
121
+ }
122
+
123
+ fn begin_check(
124
+ entry: String,
125
+ output: String,
126
+ debug: bool,
127
+ _config: Option<ProjectConfig>,
128
+ ) -> Result<(), String> {
129
+ let spinner = start_spinner("Checking...");
130
+
131
+ let duration = std::time::Instant::now();
132
+
133
+ let normalized_entry_file = normalize_path(&entry);
134
+ let normalized_output_dir = normalize_path(&output);
135
+
136
+ let mut global_store = GlobalStore::new();
137
+ let module_loader = ModuleLoader::new(&normalized_entry_file, &normalized_output_dir);
138
+
139
+ // SECTION Load
140
+ // NOTE: We don't use modules in the check command, but we still need to load them
141
+ let modules = module_loader.load_all_modules(&mut global_store);
142
+
143
+ // Debugging: Log loaded modules and errors
144
+ let logger = Logger::new();
145
+ logger.log_message(LogLevel::Info, "Loaded modules:");
146
+ for module_name in modules.0.keys() {
147
+ logger.log_message(LogLevel::Info, &format!("- {}", module_name));
148
+ }
149
+
150
+ if debug {
151
+ for (module_path, module) in global_store.modules.clone() {
152
+ write_module_variable_log_file(
153
+ &normalized_output_dir,
154
+ &module_path,
155
+ &module.variable_table,
156
+ );
157
+ write_module_function_log_file(
158
+ &normalized_output_dir,
159
+ &module_path,
160
+ &module.function_table,
161
+ );
162
+ }
163
+
164
+ write_lexer_log_file(
165
+ &normalized_output_dir,
166
+ "lexer_tokens.log",
167
+ modules.0.clone(),
168
+ );
169
+ write_preprocessor_log_file(
170
+ &normalized_output_dir,
171
+ "resolved_statements.log",
172
+ modules.1.clone(),
173
+ );
174
+ write_variables_log_file(
175
+ &normalized_output_dir,
176
+ "global_variables.log",
177
+ global_store.variables.clone(),
178
+ );
179
+ write_function_log_file(
180
+ &normalized_output_dir,
181
+ "global_functions.log",
182
+ global_store.functions.clone(),
183
+ );
184
+ }
185
+
186
+ let all_errors = crate::core::error::collect_all_errors_with_modules(&modules.1);
187
+
188
+ let (warnings, criticals) = crate::core::error::partition_errors(all_errors);
189
+ crate::core::error::log_errors_with_stack("Check", &warnings, &criticals);
190
+
191
+ if !criticals.is_empty() {
192
+ spinner.finish_and_clear();
193
+ return Err("check failed with critical errors".to_string());
194
+ } else {
195
+ logger.log_message(LogLevel::Success, "No errors detected.");
196
+
197
+ let success_message = format!(
198
+ "Check completed successfully in {:.2?}. Output files written to: '{}'",
199
+ duration.elapsed(),
200
+ normalized_output_dir
201
+ );
202
+
203
+ spinner.finish_and_clear();
204
+ logger.log_message(LogLevel::Success, &success_message);
205
+ }
206
+
207
+ Ok(())
208
+ }
@@ -1,57 +1,69 @@
1
1
  use crate::cli::discover::config::add_addons_to_config;
2
2
  use crate::cli::discover::install::install_selected_addons;
3
3
  use devalang_types::DiscoveredAddon;
4
+ use devalang_utils::spinner::start_spinner;
4
5
  use devalang_utils::{
5
6
  logger::{LogLevel, Logger},
6
7
  path as path_utils,
7
- spinner::start_spinner,
8
8
  };
9
9
 
10
- pub async fn handle_discover_command() -> Result<(), String> {
10
+ pub async fn handle_discover_command(no_clear_tmp: bool) -> Result<(), String> {
11
11
  let deva_dir = path_utils::ensure_deva_dir()?;
12
12
 
13
- // Search for addons (banks, plugins, presets, templates) in the .deva directory
14
- let valid_addons_extensions = ["devabank", "devaplugin", "devapreset", "devatemplate"];
13
+ // Search for compiled addon archives in the .deva directory
14
+ // New norm: archives are packaged as `.tar.gz`. We recursively find all
15
+ // files ending with `.tar.gz` and propose them for installation. The
16
+ // install flow will inspect the extracted content to determine the exact
17
+ // addon type (bank/plugin/preset/template) when possible.
15
18
 
16
19
  let mut addons_found = Vec::new();
17
20
 
18
- // Recursively walk the .deva directory and collect addon files matching
19
- // the known addon extensions. This allows discovery in nested folders.
20
- fn walk_dir_collect(base: &std::path::Path, exts: &[&str], out: &mut Vec<DiscoveredAddon>) {
21
+ fn walk_dir_collect_tar_gz(base: &std::path::Path, out: &mut Vec<DiscoveredAddon>) {
21
22
  if let Ok(entries) = std::fs::read_dir(base) {
22
23
  for entry in entries.filter_map(|e| e.ok()) {
23
24
  let p = entry.path();
24
25
  if p.is_dir() {
25
- walk_dir_collect(&p, exts, out);
26
+ walk_dir_collect_tar_gz(&p, out);
26
27
  } else if p.is_file() {
27
- if let Some(ext) = p.extension().and_then(|s| s.to_str()) {
28
- if exts.contains(&ext) {
29
- let name = p
30
- .file_stem()
31
- .map(|s| s.to_string_lossy().to_string())
32
- .unwrap_or_default();
33
- let addon_type = match ext {
34
- "devabank" => "bank",
35
- "devaplugin" => "plugin",
36
- "devapreset" => "preset",
37
- "devatemplate" => "template",
38
- _ => "unknown",
39
- };
40
-
41
- out.push(DiscoveredAddon {
42
- path: p.clone(),
43
- name,
44
- extension: ext.to_string(),
45
- addon_type: addon_type.to_string(),
46
- });
47
- }
28
+ let name = p.file_name().and_then(|s| s.to_str()).unwrap_or("");
29
+ if name.ends_with(".tar.gz") || name.ends_with(".tgz") {
30
+ // derive a friendly name (strip extensions)
31
+ let stem = if name.ends_with(".tar.gz") {
32
+ name.trim_end_matches(".tar.gz").to_string()
33
+ } else {
34
+ name.trim_end_matches(".tgz").to_string()
35
+ };
36
+
37
+ let publisher = p
38
+ .parent()
39
+ .and_then(|parent| parent.file_name())
40
+ .and_then(|s| s.to_str())
41
+ .unwrap_or("unknown")
42
+ .to_string();
43
+
44
+ out.push(DiscoveredAddon {
45
+ name: stem,
46
+ path: p.clone(),
47
+ publisher: publisher.clone(),
48
+ extension: "tar.gz".to_string(),
49
+ addon_type: "unknown".to_string(),
50
+ });
48
51
  }
49
52
  }
50
53
  }
51
54
  }
52
55
  }
53
56
 
54
- walk_dir_collect(&deva_dir, &valid_addons_extensions, &mut addons_found);
57
+ walk_dir_collect_tar_gz(&deva_dir, &mut addons_found);
58
+
59
+ // Pre-classify discovered archives by inspecting their contents for metadata
60
+ for addon in addons_found.iter_mut() {
61
+ if let Ok(t) = devalang_utils::file::detect_addon_type_in_archive(&addon.path) {
62
+ if t != "unknown" {
63
+ addon.addon_type = t;
64
+ }
65
+ }
66
+ }
55
67
 
56
68
  let logger = Logger::new();
57
69
 
@@ -79,6 +91,10 @@ pub async fn handle_discover_command() -> Result<(), String> {
79
91
  .cloned()
80
92
  .collect::<Vec<_>>();
81
93
 
94
+ // Combine discovered known-type addons. If nothing was classified (all
95
+ // entries have `addon_type == "unknown"`), fall back to the raw
96
+ // `addons_found` so users can still install archives discovered in
97
+ // `.deva`.
82
98
  let mut all_addons = Vec::with_capacity(
83
99
  banks_found.len() + plugins_found.len() + presets_found.len() + templates_found.len(),
84
100
  );
@@ -87,6 +103,11 @@ pub async fn handle_discover_command() -> Result<(), String> {
87
103
  all_addons.extend(presets_found.iter().cloned());
88
104
  all_addons.extend(templates_found.iter().cloned());
89
105
 
106
+ if all_addons.is_empty() {
107
+ // No known types detected — include everything we discovered.
108
+ all_addons = addons_found.clone();
109
+ }
110
+
90
111
  println!();
91
112
 
92
113
  if all_addons.is_empty() {
@@ -220,7 +241,8 @@ pub async fn handle_discover_command() -> Result<(), String> {
220
241
  .cloned()
221
242
  .collect::<Vec<_>>();
222
243
 
223
- let install_selected_addons_result = install_selected_addons(addons_to_install).await;
244
+ let install_selected_addons_result =
245
+ install_selected_addons(addons_to_install, no_clear_tmp).await;
224
246
  match install_selected_addons_result {
225
247
  Ok(addons_enriched) => {
226
248
  if let Err(e) = add_addons_to_config(addons_enriched).await {
@@ -9,8 +9,8 @@ pub async fn add_addons_to_config(addons: Vec<AddonWithMetadata>) -> Result<(),
9
9
 
10
10
  for addon in addons {
11
11
  let addon_path_as_devalang_protocol = format!(
12
- "devalang://{}/{}.{}",
13
- addon.addon_type, addon.metadata.author, addon.metadata.name
12
+ "devalang://{}/{}/{}",
13
+ addon.addon_type, addon.metadata.publisher, addon.metadata.name
14
14
  );
15
15
 
16
16
  match addon.addon_type.as_str() {
@@ -31,7 +31,6 @@ pub async fn add_addons_to_config(addons: Vec<AddonWithMetadata>) -> Result<(),
31
31
 
32
32
  banks.push(ProjectConfigBankEntry {
33
33
  path: addon_path_as_devalang_protocol,
34
- version: Some(addon.metadata.version.clone()),
35
34
  });
36
35
  }
37
36
 
@@ -52,7 +51,6 @@ pub async fn add_addons_to_config(addons: Vec<AddonWithMetadata>) -> Result<(),
52
51
 
53
52
  plugins.push(ProjectConfigPluginEntry {
54
53
  path: addon_path_as_devalang_protocol,
55
- version: Some(addon.metadata.version.clone()),
56
54
  });
57
55
  }
58
56