@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.
- package/.devalang +9 -10
- package/Cargo.toml +84 -80
- package/README.md +10 -7
- package/docs/CHANGELOG.md +83 -0
- package/docs/ROADMAP.md +6 -2
- package/docs/TODO.md +3 -14
- package/examples/bus.deva +10 -0
- package/examples/chain.deva +19 -0
- package/examples/effect.deva +2 -0
- package/examples/filter.deva +11 -0
- package/examples/lfo.deva +9 -0
- package/examples/plugin.deva +10 -10
- package/examples/routing.deva +23 -0
- package/examples/synth.deva +11 -1
- package/examples/synth_types.deva +17 -0
- package/out-tsc/bin/project-version.json +6 -0
- package/out-tsc/core/functions/index.d.ts +5 -0
- package/out-tsc/core/functions/index.js +11 -0
- package/out-tsc/pkg/devalang_core.d.ts +2 -0
- package/out-tsc/pkg/devalang_core.js +17 -2
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +1 -0
- package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
- package/out-tsc/scripts/version/copy-to-binary.js +79 -0
- package/package.json +23 -10
- package/project-version.json +3 -3
- package/rust/bindings/Cargo.toml +9 -0
- package/rust/bindings/src/lib.rs +86 -0
- package/rust/cli/addon/commands.rs +35 -0
- package/rust/cli/addon/download.rs +234 -0
- package/rust/cli/addon/install.rs +33 -0
- package/rust/cli/addon/list.rs +224 -0
- package/rust/cli/addon/metadata.rs +124 -0
- package/rust/cli/addon/mod.rs +8 -0
- package/rust/cli/addon/remove.rs +271 -0
- package/rust/cli/addon/update.rs +305 -0
- package/rust/cli/{install/addon.rs → addon/utils.rs} +34 -43
- package/rust/cli/build/commands.rs +153 -103
- package/rust/cli/build/mod.rs +2 -2
- package/rust/cli/build/process.rs +165 -146
- package/rust/cli/check/mod.rs +208 -208
- package/rust/cli/discover/commands.rs +53 -31
- package/rust/cli/discover/config.rs +2 -4
- package/rust/cli/discover/install.rs +139 -28
- package/rust/cli/discover/metadata.rs +3 -3
- package/rust/cli/login/commands.rs +124 -124
- package/rust/cli/me/commands.rs +52 -0
- package/rust/cli/me/mod.rs +1 -0
- package/rust/cli/mod.rs +2 -2
- package/rust/cli/parser.rs +76 -70
- package/rust/cli/play/commands.rs +375 -324
- package/rust/cli/play/mod.rs +5 -5
- package/rust/cli/play/process.rs +159 -150
- package/rust/cli/play/realtime.rs +91 -91
- package/rust/cli/telemetry/commands.rs +22 -22
- package/rust/cli/telemetry/event_creator.rs +80 -80
- package/rust/cli/telemetry/mod.rs +3 -3
- package/rust/cli/telemetry/send.rs +51 -51
- package/rust/cli/template/commands.rs +69 -69
- package/rust/config/driver.rs +112 -103
- package/rust/config/mod.rs +3 -3
- package/rust/config/ops.rs +26 -26
- package/rust/config/settings.rs +101 -101
- package/rust/core/audio/engine/driver.rs +237 -0
- package/rust/core/audio/engine/export.rs +169 -0
- package/rust/core/audio/engine/helpers.rs +178 -170
- package/rust/core/audio/engine/mod.rs +56 -7
- package/rust/core/audio/engine/notes/dsp.rs +88 -0
- package/rust/core/audio/engine/notes/mod.rs +53 -0
- package/rust/core/audio/engine/notes/params.rs +294 -0
- package/rust/core/audio/engine/sample/insert.rs +300 -0
- package/rust/core/audio/engine/sample/mod.rs +40 -0
- package/rust/core/audio/engine/sample/padding.rs +170 -0
- package/rust/core/audio/evaluator/condition.rs +61 -0
- package/rust/core/audio/evaluator/mod.rs +9 -0
- package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +152 -310
- package/rust/core/audio/evaluator/rhs.rs +16 -0
- package/rust/core/audio/evaluator/string_expr.rs +94 -0
- package/rust/core/audio/interpreter/driver.rs +574 -542
- package/rust/core/audio/interpreter/mod.rs +2 -14
- package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +179 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +3 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +371 -0
- package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
- package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +2 -4
- package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +36 -5
- package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +72 -71
- package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +24 -26
- package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +36 -38
- package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +17 -19
- package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +115 -114
- package/rust/core/audio/interpreter/statements/mod.rs +12 -0
- package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
- package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +54 -4
- package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
- package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +242 -239
- package/rust/core/audio/loader/trigger.rs +98 -97
- package/rust/core/audio/mod.rs +6 -7
- package/rust/core/audio/special/easing.rs +189 -189
- package/rust/core/audio/special/env.rs +45 -45
- package/rust/core/audio/special/math.rs +134 -134
- package/rust/core/audio/special/modulator.rs +143 -143
- package/rust/core/builder/mod.rs +129 -86
- package/rust/core/debugger/{module.rs → logs.rs} +52 -55
- package/rust/core/debugger/mod.rs +30 -30
- package/rust/core/debugger/store.rs +38 -40
- package/rust/core/error/mod.rs +269 -269
- package/rust/core/lexer/driver.rs +2 -4
- package/rust/core/mod.rs +9 -10
- package/rust/core/parser/driver/block.rs +111 -0
- package/rust/core/parser/driver/cursor.rs +82 -0
- package/rust/core/parser/driver/driver_impl.rs +159 -0
- package/rust/core/parser/driver/mod.rs +6 -0
- package/rust/core/parser/driver/parse_array.rs +120 -0
- package/rust/core/parser/driver/parse_map.rs +247 -0
- package/rust/core/parser/driver/parser.rs +160 -0
- package/rust/core/parser/handler/arrow_call.rs +90 -15
- package/rust/core/parser/handler/at.rs +279 -279
- package/rust/core/parser/handler/bank.rs +104 -104
- package/rust/core/parser/handler/condition.rs +83 -83
- package/rust/core/parser/handler/dot.rs +148 -148
- package/rust/core/parser/handler/identifier/automate.rs +254 -254
- package/rust/core/parser/handler/identifier/call.rs +91 -91
- package/rust/core/parser/handler/identifier/emit.rs +70 -70
- package/rust/core/parser/handler/identifier/function.rs +113 -113
- package/rust/core/parser/handler/identifier/group.rs +89 -89
- package/rust/core/parser/handler/identifier/let_.rs +173 -173
- package/rust/core/parser/handler/identifier/mod.rs +55 -55
- package/rust/core/parser/handler/identifier/on.rs +107 -107
- package/rust/core/parser/handler/identifier/print.rs +49 -49
- package/rust/core/parser/handler/identifier/sleep.rs +96 -43
- package/rust/core/parser/handler/identifier/spawn.rs +91 -91
- package/rust/core/parser/handler/identifier/synth.rs +39 -3
- package/rust/core/parser/handler/loop_.rs +194 -194
- package/rust/core/parser/handler/pattern.rs +25 -2
- package/rust/core/parser/handler/tempo.rs +105 -57
- package/rust/core/parser/statement.rs +10 -11
- package/rust/core/plugin/loader.rs +137 -137
- package/rust/core/plugin/runner/mod.rs +11 -0
- package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +206 -72
- package/rust/core/plugin/runner/wasm32.rs +44 -0
- package/rust/core/preprocessor/loader/inject.rs +313 -0
- package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
- package/rust/core/preprocessor/loader/mod.rs +235 -0
- package/rust/core/preprocessor/module.rs +55 -60
- package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +107 -114
- package/rust/core/preprocessor/processor/mod.rs +1 -0
- package/rust/core/preprocessor/resolver/function.rs +69 -69
- package/rust/core/preprocessor/resolver/group.rs +122 -94
- package/rust/core/preprocessor/resolver/pattern.rs +14 -2
- package/rust/core/store/global.rs +57 -61
- package/rust/core/store/mod.rs +1 -5
- package/rust/lib.rs +323 -308
- package/rust/macros/Cargo.toml +14 -0
- package/rust/macros/src/lib.rs +52 -0
- package/rust/main.rs +336 -143
- package/rust/types/Cargo.toml +1 -1
- package/rust/types/src/addons.rs +57 -55
- package/rust/types/src/config.rs +82 -74
- package/rust/types/src/lib.rs +15 -12
- package/rust/types/src/plugin.rs +20 -0
- package/rust/types/src/store.rs +139 -0
- package/rust/types/src/telemetry.rs +85 -85
- package/rust/utils/Cargo.toml +5 -2
- package/rust/utils/src/file.rs +477 -94
- package/rust/utils/src/first_usage.rs +97 -97
- package/rust/utils/src/lib.rs +9 -9
- package/rust/utils/src/logger.rs +200 -200
- package/rust/utils/src/path.rs +158 -88
- package/rust/utils/src/signature.rs +41 -41
- package/rust/utils/src/spinner.rs +20 -20
- package/rust/utils/src/version.rs +58 -27
- package/rust/utils/src/watcher.rs +46 -46
- package/rust/web/api.rs +5 -5
- package/rust/web/auth.rs +5 -0
- package/rust/web/cdn.rs +34 -34
- package/rust/web/forge.rs +5 -0
- package/rust/web/mod.rs +2 -0
- package/tests/integration.rs +21 -21
- package/typescript/core/functions/index.ts +11 -0
- package/typescript/pkg/devalang_core.ts +20 -4
- package/typescript/scripts/version/copy-to-binary.ts +82 -0
- package/rust/cli/bank/api.rs +0 -122
- package/rust/cli/bank/commands.rs +0 -275
- package/rust/cli/bank/mod.rs +0 -29
- package/rust/cli/install/bank.rs +0 -53
- package/rust/cli/install/commands.rs +0 -35
- package/rust/cli/install/mod.rs +0 -4
- package/rust/cli/install/plugin.rs +0 -61
- package/rust/core/audio/engine/sample.rs +0 -366
- package/rust/core/audio/engine/synth.rs +0 -325
- package/rust/core/audio/interpreter/arrow_call.rs +0 -311
- package/rust/core/audio/renderer.rs +0 -54
- package/rust/core/parser/driver.rs +0 -584
- package/rust/core/preprocessor/loader.rs +0 -637
- package/rust/core/store/export.rs +0 -28
- package/rust/core/store/function.rs +0 -40
- package/rust/core/store/import.rs +0 -28
- package/rust/core/store/variable.rs +0 -51
- package/rust/core/utils/mod.rs +0 -1
- package/rust/core/utils/path.rs +0 -37
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
use crate::config::ops::load_config;
|
|
2
|
+
use crate::{
|
|
3
|
+
cli::addon::{
|
|
4
|
+
download::download_addon,
|
|
5
|
+
metadata::{get_addon_from_api, get_addon_publisher_from_api},
|
|
6
|
+
},
|
|
7
|
+
web::cdn::get_cdn_url,
|
|
8
|
+
};
|
|
9
|
+
use devalang_core::config::driver::ProjectConfigExt;
|
|
10
|
+
use devalang_types::AddonType;
|
|
11
|
+
use devalang_utils::path as path_utils;
|
|
12
|
+
use std::fs;
|
|
13
|
+
use toml::Value as TomlValue;
|
|
14
|
+
|
|
15
|
+
#[derive(serde::Deserialize)]
|
|
16
|
+
pub struct AddonVersion {
|
|
17
|
+
pub version: String,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async fn fetch_latest_version(
|
|
21
|
+
addon_type: AddonType,
|
|
22
|
+
addon_name: String,
|
|
23
|
+
) -> Result<AddonVersion, Box<dyn std::error::Error>> {
|
|
24
|
+
let cdn_url = get_cdn_url();
|
|
25
|
+
|
|
26
|
+
let addon_type = match addon_type {
|
|
27
|
+
AddonType::Bank => "bank",
|
|
28
|
+
AddonType::Plugin => "plugin",
|
|
29
|
+
AddonType::Preset => "preset",
|
|
30
|
+
AddonType::Template => "template",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let publisher_identifier = get_addon_publisher_from_api(&addon_name)
|
|
34
|
+
.await
|
|
35
|
+
.unwrap_or("unknown".to_string());
|
|
36
|
+
|
|
37
|
+
let url = format!(
|
|
38
|
+
"{}/{}/{}/{}/version",
|
|
39
|
+
cdn_url, addon_type, publisher_identifier, addon_name
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
let response = reqwest::get(url).await?;
|
|
43
|
+
|
|
44
|
+
if !response.status().is_success() {
|
|
45
|
+
return Err(format!("❌ Failed to fetch version: HTTP {}", response.status()).into());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let bytes = response.bytes().await?;
|
|
49
|
+
|
|
50
|
+
let version: AddonVersion = serde_json::from_slice(&bytes)?;
|
|
51
|
+
|
|
52
|
+
Ok(version)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pub async fn update_addon(slug: String) -> Result<(), String> {
|
|
56
|
+
let addon_metadata = get_addon_from_api(&slug).await?;
|
|
57
|
+
let deva_dir = path_utils::ensure_deva_dir()?;
|
|
58
|
+
|
|
59
|
+
// Represent the addon as "<publisher>.<addon>" for user-visible names
|
|
60
|
+
let publisher_and_name = format!("{}.{}", addon_metadata.publisher, addon_metadata.name);
|
|
61
|
+
|
|
62
|
+
match addon_metadata.addon_type {
|
|
63
|
+
devalang_types::AddonType::Bank => {
|
|
64
|
+
match fetch_latest_version(
|
|
65
|
+
addon_metadata.addon_type.clone(),
|
|
66
|
+
addon_metadata.name.clone(),
|
|
67
|
+
)
|
|
68
|
+
.await
|
|
69
|
+
{
|
|
70
|
+
Ok(latest) => {
|
|
71
|
+
// Determine local version from bank.toml if available
|
|
72
|
+
let local_bank_path = deva_dir
|
|
73
|
+
.join("banks")
|
|
74
|
+
.join(&addon_metadata.publisher)
|
|
75
|
+
.join(&addon_metadata.name);
|
|
76
|
+
let local_version = if local_bank_path.exists() {
|
|
77
|
+
let bank_toml = local_bank_path.join("bank.toml");
|
|
78
|
+
if bank_toml.exists() {
|
|
79
|
+
if let Ok(content) = fs::read_to_string(&bank_toml) {
|
|
80
|
+
if let Ok(parsed) =
|
|
81
|
+
toml::from_str::<devalang_types::BankFile>(&content)
|
|
82
|
+
{
|
|
83
|
+
parsed.bank.version
|
|
84
|
+
} else {
|
|
85
|
+
String::new()
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
String::new()
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
String::new()
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
String::new()
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if local_version != latest.version {
|
|
98
|
+
println!(
|
|
99
|
+
"Updating bank '{}' from '{}' to '{}'...",
|
|
100
|
+
publisher_and_name, local_version, latest.version
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// remove existing folder if present
|
|
104
|
+
let bank_dir = deva_dir
|
|
105
|
+
.join("banks")
|
|
106
|
+
.join(&addon_metadata.publisher)
|
|
107
|
+
.join(&addon_metadata.name);
|
|
108
|
+
if bank_dir.exists() {
|
|
109
|
+
fs::remove_dir_all(&bank_dir)
|
|
110
|
+
.map_err(|e| format!("Failed to remove old bank files: {}", e))?;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// download new (use publisher.addon as the external identifier)
|
|
114
|
+
download_addon(&publisher_and_name, &addon_metadata).await?;
|
|
115
|
+
|
|
116
|
+
// update config version when present
|
|
117
|
+
if let Ok(config_path) = path_utils::get_devalang_config_path() {
|
|
118
|
+
if let Some(mut config) = load_config(Some(&config_path)) {
|
|
119
|
+
if let Some(banks) = config.banks.as_mut() {
|
|
120
|
+
for bank in banks.iter_mut() {
|
|
121
|
+
let name_in_path = bank
|
|
122
|
+
.path
|
|
123
|
+
.strip_prefix("devalang://bank/")
|
|
124
|
+
.unwrap_or(&bank.path);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if let Err(e) = config.write_config(&config) {
|
|
128
|
+
eprintln!("Warning: failed to write updated config: {}", e);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
println!(
|
|
135
|
+
"✅ Bank '{}' updated to version '{}'",
|
|
136
|
+
publisher_and_name, latest.version
|
|
137
|
+
);
|
|
138
|
+
} else {
|
|
139
|
+
println!(
|
|
140
|
+
"Bank '{}' is already up-to-date (version {})",
|
|
141
|
+
publisher_and_name, latest.version
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
Err(e) => {
|
|
146
|
+
return Err(format!(
|
|
147
|
+
"Failed to fetch latest version for bank '{}': {}",
|
|
148
|
+
publisher_and_name, e
|
|
149
|
+
));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
devalang_types::AddonType::Plugin => {
|
|
155
|
+
// Try to fetch latest version via CDN API; if available compare, otherwise fallback to redownload
|
|
156
|
+
match fetch_latest_version(
|
|
157
|
+
addon_metadata.addon_type.clone(),
|
|
158
|
+
addon_metadata.name.clone(),
|
|
159
|
+
)
|
|
160
|
+
.await
|
|
161
|
+
{
|
|
162
|
+
Ok(latest) => {
|
|
163
|
+
// Determine local plugin version by reading plugin.toml from preferred or fallback layout
|
|
164
|
+
let preferred = deva_dir.join("plugins").join(format!(
|
|
165
|
+
"{}/{}",
|
|
166
|
+
addon_metadata.publisher, addon_metadata.name
|
|
167
|
+
));
|
|
168
|
+
let fallback = deva_dir
|
|
169
|
+
.join("plugins")
|
|
170
|
+
.join(&addon_metadata.publisher)
|
|
171
|
+
.join(&addon_metadata.name);
|
|
172
|
+
|
|
173
|
+
let mut local_version = String::new();
|
|
174
|
+
for candidate in [&preferred, &fallback] {
|
|
175
|
+
let toml_path = candidate.join("plugin.toml");
|
|
176
|
+
if toml_path.exists() {
|
|
177
|
+
if let Ok(content) = fs::read_to_string(&toml_path) {
|
|
178
|
+
if let Ok(value) = toml::from_str::<TomlValue>(&content) {
|
|
179
|
+
if let Some(v) = value
|
|
180
|
+
.get("plugin")
|
|
181
|
+
.and_then(|p| p.get("version"))
|
|
182
|
+
.and_then(|s| s.as_str())
|
|
183
|
+
{
|
|
184
|
+
local_version = v.to_string();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if local_version != latest.version {
|
|
193
|
+
println!(
|
|
194
|
+
"Updating plugin '{}' from '{}' to '{}'...",
|
|
195
|
+
publisher_and_name, local_version, latest.version
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// remove any existing layout
|
|
199
|
+
if preferred.exists() {
|
|
200
|
+
fs::remove_dir_all(&preferred)
|
|
201
|
+
.map_err(|e| format!("Failed to remove old plugin files: {}", e))?;
|
|
202
|
+
}
|
|
203
|
+
if fallback.exists() {
|
|
204
|
+
fs::remove_dir_all(&fallback)
|
|
205
|
+
.map_err(|e| format!("Failed to remove old plugin files: {}", e))?;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// download new
|
|
209
|
+
download_addon(&publisher_and_name, &addon_metadata).await?;
|
|
210
|
+
|
|
211
|
+
// update config version when present
|
|
212
|
+
if let Ok(config_path) = path_utils::get_devalang_config_path() {
|
|
213
|
+
if let Some(mut config) = load_config(Some(&config_path)) {
|
|
214
|
+
if let Some(plugins) = config.plugins.as_mut() {
|
|
215
|
+
for p in plugins.iter_mut() {
|
|
216
|
+
let name_in_path = p
|
|
217
|
+
.path
|
|
218
|
+
.strip_prefix("devalang://plugin/")
|
|
219
|
+
.unwrap_or(&p.path);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if let Err(e) = config.write_config(&config) {
|
|
223
|
+
eprintln!("Warning: failed to write updated config: {}", e);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
println!(
|
|
230
|
+
"✅ Plugin '{}' updated to version '{}'",
|
|
231
|
+
publisher_and_name, latest.version
|
|
232
|
+
);
|
|
233
|
+
} else {
|
|
234
|
+
println!(
|
|
235
|
+
"Plugin '{}' is already up-to-date (version {})",
|
|
236
|
+
publisher_and_name, latest.version
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
Err(_) => {
|
|
241
|
+
// Fallback: redownload everything
|
|
242
|
+
println!(
|
|
243
|
+
"No version info for plugin '{}', redownloading to ensure latest.",
|
|
244
|
+
publisher_and_name
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
let plugin_dir = deva_dir
|
|
248
|
+
.join("plugins")
|
|
249
|
+
.join(&addon_metadata.publisher)
|
|
250
|
+
.join(&addon_metadata.name);
|
|
251
|
+
if plugin_dir.exists() {
|
|
252
|
+
fs::remove_dir_all(&plugin_dir)
|
|
253
|
+
.map_err(|e| format!("Failed to remove old plugin files: {}", e))?;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
download_addon(&publisher_and_name, &addon_metadata).await?;
|
|
257
|
+
|
|
258
|
+
// clear version in config (unknown)
|
|
259
|
+
if let Ok(config_path) = path_utils::get_devalang_config_path() {
|
|
260
|
+
if let Some(mut config) = load_config(Some(&config_path)) {
|
|
261
|
+
if let Some(plugins) = config.plugins.as_mut() {
|
|
262
|
+
for p in plugins.iter_mut() {
|
|
263
|
+
let name_in_path = p
|
|
264
|
+
.path
|
|
265
|
+
.strip_prefix("devalang://plugin/")
|
|
266
|
+
.unwrap_or(&p.path);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if let Err(e) = config.write_config(&config) {
|
|
270
|
+
eprintln!("Warning: failed to write updated config: {}", e);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
println!("✅ Plugin '{}' updated", publisher_and_name);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
devalang_types::AddonType::Preset | devalang_types::AddonType::Template => {
|
|
282
|
+
println!(
|
|
283
|
+
"Update for presets/templates is not yet implemented; reinstalling to be safe."
|
|
284
|
+
);
|
|
285
|
+
let target_dir = match addon_metadata.addon_type {
|
|
286
|
+
devalang_types::AddonType::Preset => deva_dir.join("presets"),
|
|
287
|
+
_ => deva_dir.join("templates"),
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
let candidate = target_dir
|
|
291
|
+
.join(&addon_metadata.publisher)
|
|
292
|
+
.join(&addon_metadata.name);
|
|
293
|
+
if candidate.exists() {
|
|
294
|
+
fs::remove_dir_all(&candidate)
|
|
295
|
+
.map_err(|e| format!("Failed to remove old files: {}", e))?;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// use publisher.addon representation for user-facing messaging
|
|
299
|
+
download_addon(&publisher_and_name, &addon_metadata).await?;
|
|
300
|
+
println!("✅ Addon '{}' updated (reinstalled)", publisher_and_name);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
Ok(())
|
|
305
|
+
}
|
|
@@ -1,26 +1,12 @@
|
|
|
1
|
-
use crate::{
|
|
2
|
-
cli::install::{bank::install_bank, plugin::install_plugin},
|
|
3
|
-
config::settings::get_user_config,
|
|
4
|
-
web::api::get_api_url,
|
|
5
|
-
};
|
|
1
|
+
use crate::{config::settings::get_user_config, web::forge::get_forge_api_url};
|
|
6
2
|
use devalang_types::AddonType;
|
|
7
|
-
use std::path::Path;
|
|
8
3
|
|
|
9
|
-
pub async fn
|
|
4
|
+
pub async fn ask_api_for_signed_url(
|
|
10
5
|
addon_type: AddonType,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
) -> Result<
|
|
14
|
-
|
|
15
|
-
AddonType::Bank => install_bank(name, target_dir).await,
|
|
16
|
-
AddonType::Plugin => install_plugin(name, target_dir).await,
|
|
17
|
-
AddonType::Preset => Err("Preset installation not implemented".into()),
|
|
18
|
-
AddonType::Template => Err("Template installation not implemented".into()),
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
pub async fn ask_api_for_signed_url(addon_type: AddonType, slug: &str) -> Result<String, String> {
|
|
23
|
-
let api_url = get_api_url();
|
|
6
|
+
publisher: String,
|
|
7
|
+
slug: &str,
|
|
8
|
+
) -> Result<String, String> {
|
|
9
|
+
let forge_api_url = get_forge_api_url();
|
|
24
10
|
|
|
25
11
|
use devalang_utils::logger::LogLevel;
|
|
26
12
|
use devalang_utils::logger::Logger;
|
|
@@ -38,31 +24,36 @@ pub async fn ask_api_for_signed_url(addon_type: AddonType, slug: &str) -> Result
|
|
|
38
24
|
return Err("Authentication required: run 'devalang login' to authenticate".to_string());
|
|
39
25
|
}
|
|
40
26
|
|
|
27
|
+
// Build request URL. If publisher is empty, omit the publisher param so the API
|
|
28
|
+
// can resolve publisher by addon name itself (server-side lookup).
|
|
29
|
+
let kind = match addon_type {
|
|
30
|
+
AddonType::Bank => "bank",
|
|
31
|
+
AddonType::Plugin => "plugin",
|
|
32
|
+
AddonType::Preset => "preset",
|
|
33
|
+
AddonType::Template => "template",
|
|
34
|
+
};
|
|
35
|
+
|
|
41
36
|
let request_url = if let Some(token) = &stored_token_opt {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
37
|
+
if publisher.trim().is_empty() {
|
|
38
|
+
format!(
|
|
39
|
+
"{}/v1/addon/url?type={}&slug={}&token={}",
|
|
40
|
+
forge_api_url, kind, slug, token
|
|
41
|
+
)
|
|
42
|
+
} else {
|
|
43
|
+
format!(
|
|
44
|
+
"{}/v1/addon/url?type={}&publisher={}&slug={}&token={}",
|
|
45
|
+
forge_api_url, kind, publisher, slug, token
|
|
46
|
+
)
|
|
47
|
+
}
|
|
54
48
|
} else {
|
|
55
|
-
|
|
56
|
-
"{}/v1/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
},
|
|
64
|
-
slug
|
|
65
|
-
)
|
|
49
|
+
if publisher.trim().is_empty() {
|
|
50
|
+
format!("{}/v1/addon/url?type={}&slug={}", forge_api_url, kind, slug)
|
|
51
|
+
} else {
|
|
52
|
+
format!(
|
|
53
|
+
"{}/v1/addon/url?type={}&publisher={}&slug={}",
|
|
54
|
+
forge_api_url, kind, publisher, slug
|
|
55
|
+
)
|
|
56
|
+
}
|
|
66
57
|
};
|
|
67
58
|
|
|
68
59
|
let mut headers = reqwest::header::HeaderMap::new();
|
|
@@ -1,103 +1,153 @@
|
|
|
1
|
-
use crate::
|
|
2
|
-
use devalang_utils::logger::{LogLevel, Logger};
|
|
3
|
-
use devalang_utils::
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
1
|
+
use crate::config::driver::ProjectConfig;
|
|
2
|
+
use devalang_utils::logger::{LogLevel, Logger};
|
|
3
|
+
use devalang_utils::path::find_entry_file;
|
|
4
|
+
use devalang_utils::watcher::watch_directory;
|
|
5
|
+
|
|
6
|
+
#[cfg(feature = "cli")]
|
|
7
|
+
pub fn handle_build_command(
|
|
8
|
+
config: Option<ProjectConfig>,
|
|
9
|
+
entry: Option<String>,
|
|
10
|
+
output: Option<String>,
|
|
11
|
+
output_format: Vec<crate::cli::parser::OutputFormat>,
|
|
12
|
+
audio_format: crate::cli::parser::AudioFormat,
|
|
13
|
+
sample_rate: u32,
|
|
14
|
+
watch: bool,
|
|
15
|
+
debug: bool,
|
|
16
|
+
compress: bool,
|
|
17
|
+
) -> Result<(), String> {
|
|
18
|
+
// determine fetched values from config or CLI
|
|
19
|
+
let fetched_entry = if entry.is_none() {
|
|
20
|
+
config
|
|
21
|
+
.as_ref()
|
|
22
|
+
.and_then(|c| c.defaults.entry.clone())
|
|
23
|
+
.unwrap_or_default()
|
|
24
|
+
} else {
|
|
25
|
+
entry.clone().unwrap_or_default()
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let fetched_output = if output.is_none() {
|
|
29
|
+
config
|
|
30
|
+
.as_ref()
|
|
31
|
+
.and_then(|c| c.defaults.output.clone())
|
|
32
|
+
.unwrap_or_default()
|
|
33
|
+
} else {
|
|
34
|
+
output.clone().unwrap_or_default()
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
let fetched_watch = if watch {
|
|
38
|
+
watch
|
|
39
|
+
} else {
|
|
40
|
+
config
|
|
41
|
+
.as_ref()
|
|
42
|
+
.and_then(|c| c.defaults.watch)
|
|
43
|
+
.unwrap_or(false)
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
let logger = Logger::new();
|
|
47
|
+
|
|
48
|
+
// Determine final audio_format: prefer CLI, else config default if present
|
|
49
|
+
let mut final_audio_format = audio_format;
|
|
50
|
+
if let Some(cfg) = config.as_ref() {
|
|
51
|
+
if let Some(af) = cfg.defaults.audio_format.as_ref() {
|
|
52
|
+
// Only override if CLI provided an empty/placeholder — clap provides a default, so we only override when CLI wasn't explicit (rare).
|
|
53
|
+
// We'll accept values: "wav16", "wav24", "wav32" (case-insensitive)
|
|
54
|
+
let af_low = af.to_lowercase();
|
|
55
|
+
final_audio_format = match af_low.as_str() {
|
|
56
|
+
"wav24" => crate::cli::parser::AudioFormat::Wav24,
|
|
57
|
+
"wav32" => crate::cli::parser::AudioFormat::Wav32,
|
|
58
|
+
_ => crate::cli::parser::AudioFormat::Wav16,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Determine final output_format: prefer CLI vector, else config default list
|
|
64
|
+
let mut final_output_format: Vec<crate::cli::parser::OutputFormat> = output_format.clone();
|
|
65
|
+
if final_output_format.is_empty() {
|
|
66
|
+
if let Some(cfg) = config.as_ref() {
|
|
67
|
+
if let Some(ofs) = cfg.defaults.output_format.as_ref() {
|
|
68
|
+
for s in ofs {
|
|
69
|
+
match s.to_lowercase().as_str() {
|
|
70
|
+
"mid" | "midi" => {
|
|
71
|
+
final_output_format.push(crate::cli::parser::OutputFormat::Mid);
|
|
72
|
+
}
|
|
73
|
+
_ => final_output_format.push(crate::cli::parser::OutputFormat::Wav),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if fetched_entry.is_empty() {
|
|
81
|
+
logger.log_message(
|
|
82
|
+
LogLevel::Error,
|
|
83
|
+
"Entry path is not specified. Please provide a valid entry path.",
|
|
84
|
+
);
|
|
85
|
+
return Err("missing entry path".to_string());
|
|
86
|
+
}
|
|
87
|
+
if fetched_output.is_empty() {
|
|
88
|
+
logger.log_message(
|
|
89
|
+
LogLevel::Error,
|
|
90
|
+
"Output directory is not specified. Please provide a valid output directory.",
|
|
91
|
+
);
|
|
92
|
+
return Err("missing output directory".to_string());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let entry_file = match find_entry_file(&fetched_entry) {
|
|
96
|
+
Some(p) => p,
|
|
97
|
+
None => {
|
|
98
|
+
logger.log_message(
|
|
99
|
+
LogLevel::Error,
|
|
100
|
+
&format!("❌ index.deva not found in directory: {}", fetched_entry),
|
|
101
|
+
);
|
|
102
|
+
return Err("index.deva not found".to_string());
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// SECTION Begin build
|
|
107
|
+
if fetched_watch {
|
|
108
|
+
let _ = crate::cli::build::process::process_build(
|
|
109
|
+
entry_file.clone(),
|
|
110
|
+
fetched_output.clone(),
|
|
111
|
+
final_output_format.clone(),
|
|
112
|
+
final_audio_format,
|
|
113
|
+
sample_rate,
|
|
114
|
+
debug,
|
|
115
|
+
compress,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
logger.log_message(
|
|
119
|
+
LogLevel::Watcher,
|
|
120
|
+
&format!("Watching for changes in '{}'...", fetched_entry),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
watch_directory(entry_file.clone(), move || {
|
|
124
|
+
logger.log_message(LogLevel::Watcher, "Detected changes, re-building...");
|
|
125
|
+
|
|
126
|
+
let _ = crate::cli::build::process::process_build(
|
|
127
|
+
entry_file.clone(),
|
|
128
|
+
fetched_output.clone(),
|
|
129
|
+
final_output_format.clone(),
|
|
130
|
+
final_audio_format,
|
|
131
|
+
sample_rate,
|
|
132
|
+
debug,
|
|
133
|
+
compress,
|
|
134
|
+
);
|
|
135
|
+
})
|
|
136
|
+
.unwrap();
|
|
137
|
+
} else {
|
|
138
|
+
let res = crate::cli::build::process::process_build(
|
|
139
|
+
entry_file,
|
|
140
|
+
fetched_output,
|
|
141
|
+
final_output_format,
|
|
142
|
+
final_audio_format,
|
|
143
|
+
sample_rate,
|
|
144
|
+
debug,
|
|
145
|
+
compress,
|
|
146
|
+
);
|
|
147
|
+
if let Err(e) = res {
|
|
148
|
+
return Err(e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
Ok(())
|
|
153
|
+
}
|
package/rust/cli/build/mod.rs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
pub mod commands;
|
|
2
|
-
pub mod process;
|
|
1
|
+
pub mod commands;
|
|
2
|
+
pub mod process;
|