Quellcode durchsuchen

refactor(core): remove shell APIs (#6749)

Lucas Fernandes Nogueira vor 2 Jahren
Ursprung
Commit
96639ca239

+ 8 - 0
.changes/move-shell.md

@@ -0,0 +1,8 @@
+---
+"api": patch
+"tauri": patch
+"tauri-codegen": patch
+"tauri-macros": patch
+---
+
+Moved the `shell` functionality to its own plugin in the plugins-workspace repository.

+ 5 - 0
.changes/process-mod-refactor.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+Moved the `tauri::api::process` module to `tauri::process`.

+ 0 - 2
core/tauri-codegen/Cargo.toml

@@ -23,7 +23,6 @@ tauri-utils = { version = "2.0.0-alpha.4", path = "../tauri-utils", features = [
 thiserror = "1"
 walkdir = "2"
 brotli = { version = "3", optional = true, default-features = false, features = [ "std" ] }
-regex = { version = "1.7.1", optional = true }
 uuid = { version = "1", features = [ "v4" ] }
 semver = "1"
 ico = "0.3"
@@ -39,6 +38,5 @@ time = { version = "0.3", features = [ "parsing", "formatting" ] }
 default = [ "compression" ]
 compression = [ "brotli", "tauri-utils/compression" ]
 isolation = [ "tauri-utils/isolation" ]
-shell-scope = [ "regex" ]
 config-json5 = [ "tauri-utils/config-json5" ]
 config-toml = [ "tauri-utils/config-toml" ]

+ 0 - 111
core/tauri-codegen/src/context.rs

@@ -16,9 +16,6 @@ use tauri_utils::html::{
   inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node,
 };
 
-#[cfg(feature = "shell-scope")]
-use tauri_utils::config::{ShellAllowedArg, ShellAllowedArgs, ShellAllowlistScope};
-
 use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError};
 
 /// Necessary data needed by [`context_codegen`] to generate code for a Tauri application context.
@@ -424,38 +421,6 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
     }
   };
 
-  #[cfg(feature = "shell-scope")]
-  let with_shell_scope_code = {
-    use regex::Regex;
-    use tauri_utils::config::ShellAllowlistOpen;
-
-    let shell_scopes = get_allowed_clis(&root, &config.tauri.allowlist.shell.scope);
-
-    let shell_scope_constructor = match &config.tauri.allowlist.shell.open {
-      ShellAllowlistOpen::Flag(false) => quote!(#root::ShellScopeConfig::new().skip_validation()),
-      ShellAllowlistOpen::Flag(true) => quote!(#root::ShellScopeConfig::new()),
-      ShellAllowlistOpen::Validate(regex) => match Regex::new(regex) {
-        Ok(_) => {
-          quote!(#root::ShellScopeConfig::with_validator(#root::regex::Regex::new(#regex).unwrap()))
-        }
-        Err(error) => {
-          let error = error.to_string();
-          quote!({
-            compile_error!(#error);
-            #root::ShellScopeConfig::with_validator(#root::regex::Regex::new(#regex).unwrap())
-          })
-        }
-      },
-      _ => panic!("unknown shell open format, unable to prepare"),
-    };
-    let shell_scope = quote!(#shell_scope_constructor.set_allowed_commands(#shell_scopes));
-
-    quote!(context.set_shell_scope(#shell_scope);)
-  };
-
-  #[cfg(not(feature = "shell-scope"))]
-  let with_shell_scope_code = quote!();
-
   Ok(quote!({
     #[allow(unused_mut, clippy::let_and_return)]
     let mut context = #root::Context::new(
@@ -468,7 +433,6 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
       #pattern,
     );
     #with_system_tray_icon_code
-    #with_shell_scope_code
     context
   }))
 }
@@ -587,78 +551,3 @@ fn find_icon<F: Fn(&&String) -> bool>(
     .unwrap_or_else(|| default.to_string());
   config_parent.join(icon_path)
 }
-
-#[cfg(feature = "shell-scope")]
-fn get_allowed_clis(root: &TokenStream, scope: &ShellAllowlistScope) -> TokenStream {
-  let commands = scope
-    .0
-    .iter()
-    .map(|scope| {
-      let sidecar = &scope.sidecar;
-
-      let name = &scope.name;
-      let name = quote!(#name.into());
-
-      let command = scope.command.to_string_lossy();
-      let command = quote!(::std::path::PathBuf::from(#command));
-
-      let args = match &scope.args {
-        ShellAllowedArgs::Flag(true) => quote!(::std::option::Option::None),
-        ShellAllowedArgs::Flag(false) => quote!(::std::option::Option::Some(::std::vec![])),
-        ShellAllowedArgs::List(list) => {
-          let list = list.iter().map(|arg| match arg {
-            ShellAllowedArg::Fixed(fixed) => {
-              quote!(#root::scope::ShellScopeAllowedArg::Fixed(#fixed.into()))
-            }
-            ShellAllowedArg::Var { validator } => {
-              let validator = match regex::Regex::new(validator) {
-                Ok(regex) => {
-                  let regex = regex.as_str();
-                  quote!(#root::regex::Regex::new(#regex).unwrap())
-                }
-                Err(error) => {
-                  let error = error.to_string();
-                  quote!({
-                    compile_error!(#error);
-                    #root::regex::Regex::new(#validator).unwrap()
-                  })
-                }
-              };
-
-              quote!(#root::scope::ShellScopeAllowedArg::Var { validator: #validator })
-            }
-            _ => panic!("unknown shell scope arg, unable to prepare"),
-          });
-
-          quote!(::std::option::Option::Some(::std::vec![#(#list),*]))
-        }
-        _ => panic!("unknown shell scope command, unable to prepare"),
-      };
-
-      (
-        quote!(#name),
-        quote!(
-          #root::scope::ShellScopeAllowedCommand {
-            command: #command,
-            args: #args,
-            sidecar: #sidecar,
-          }
-        ),
-      )
-    })
-    .collect::<Vec<_>>();
-
-  if commands.is_empty() {
-    quote!(::std::collections::HashMap::new())
-  } else {
-    let insertions = commands
-      .iter()
-      .map(|(name, value)| quote!(hashmap.insert(#name, #value);));
-
-    quote!({
-      let mut hashmap = ::std::collections::HashMap::new();
-      #(#insertions)*
-      hashmap
-    })
-  }
-}

+ 0 - 1
core/tauri-macros/Cargo.toml

@@ -27,6 +27,5 @@ tauri-utils = { version = "2.0.0-alpha.4", path = "../tauri-utils" }
 custom-protocol = [ ]
 compression = [ "tauri-codegen/compression" ]
 isolation = [ "tauri-codegen/isolation" ]
-shell-scope = [ "tauri-codegen/shell-scope" ]
 config-json5 = [ "tauri-codegen/config-json5", "tauri-utils/config-json5" ]
 config-toml = [ "tauri-codegen/config-toml", "tauri-utils/config-toml" ]

+ 3 - 9
core/tauri/Cargo.toml

@@ -63,14 +63,10 @@ percent-encoding = "2.2"
 base64 = { version = "0.21", optional = true }
 reqwest = { version = "0.11", default-features = false, features = [ "json", "stream" ] }
 bytes = { version = "1", features = [ "serde" ] }
-open = { version = "3.0", optional = true }
-shared_child = { version = "1.0", optional = true }
-os_pipe = { version = "1.0", optional = true }
 raw-window-handle = "0.5"
 minisign-verify = { version = "0.2", optional = true }
 time = { version = "0.3", features = [ "parsing", "formatting" ], optional = true }
 os_info = { version = "3", optional = true }
-regex = { version = "1.6.0", optional = true }
 glob = "0.3"
 data-url = { version = "0.2", optional = true }
 serialize-to-javascript = "=0.1.1"
@@ -141,12 +137,10 @@ updater = [
   "dialog-ask",
   "fs-extract-api"
 ]
-shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ]
 fs-extract-api = [ "zip" ]
 native-tls = [ "reqwest/native-tls" ]
 native-tls-vendored = [ "reqwest/native-tls-vendored" ]
 rustls-tls = [ "reqwest/rustls-tls" ]
-process-command-api = [ "shared_child", "os_pipe" ]
 system-tray = [ "tauri-runtime/system-tray", "tauri-runtime-wry/system-tray" ]
 devtools = [ "tauri-runtime/devtools", "tauri-runtime-wry/devtools" ]
 dox = [ "tauri-runtime-wry/dox" ]
@@ -212,9 +206,9 @@ process-relaunch-dangerous-allow-symlink-macos = [ "tauri-utils/process-relaunch
 protocol-all = [ "protocol-asset" ]
 protocol-asset = [ ]
 shell-all = [ "shell-execute", "shell-sidecar", "shell-open" ]
-shell-execute = [ "process-command-api", "regex", "tauri-macros/shell-scope" ]
-shell-sidecar = [ "process-command-api", "regex", "tauri-macros/shell-scope" ]
-shell-open = [ "shell-open-api" ]
+shell-execute = [ ]
+shell-sidecar = [ ]
+shell-open = [ ]
 window-all = [
   "window-create",
   "window-center",

+ 0 - 4
core/tauri/build.rs

@@ -108,10 +108,6 @@ fn main() {
   );
 
   alias_module("shell", &["execute", "sidecar", "open"], api_all);
-  // helper for the command module macro
-  let shell_script = has_feature("shell-execute") || has_feature("shell-sidecar");
-  alias("shell_script", shell_script);
-  alias("shell_scope", has_feature("shell-open-api") || shell_script);
 
   if !mobile {
     alias_module(

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
core/tauri/scripts/bundle.global.js


+ 0 - 4
core/tauri/src/api/mod.rs

@@ -7,10 +7,6 @@
 pub mod dir;
 pub mod file;
 pub mod ipc;
-pub mod process;
-#[cfg(feature = "shell-open-api")]
-#[cfg_attr(doc_cfg, doc(cfg(feature = "shell-open-api")))]
-pub mod shell;
 pub mod version;
 
 mod error;

+ 0 - 531
core/tauri/src/api/process/command.rs

@@ -1,531 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use std::{
-  collections::HashMap,
-  io::{BufReader, Write},
-  path::PathBuf,
-  process::{Command as StdCommand, Stdio},
-  sync::{Arc, Mutex, RwLock},
-  thread::spawn,
-};
-
-#[cfg(unix)]
-use std::os::unix::process::ExitStatusExt;
-#[cfg(windows)]
-use std::os::windows::process::CommandExt;
-
-#[cfg(windows)]
-const CREATE_NO_WINDOW: u32 = 0x0800_0000;
-const NEWLINE_BYTE: u8 = b'\n';
-
-use crate::async_runtime::{block_on as block_on_task, channel, Receiver, Sender};
-
-pub use encoding_rs::Encoding;
-use os_pipe::{pipe, PipeReader, PipeWriter};
-use serde::Serialize;
-use shared_child::SharedChild;
-use tauri_utils::platform;
-
-type ChildStore = Arc<Mutex<HashMap<u32, Arc<SharedChild>>>>;
-
-fn commands() -> &'static ChildStore {
-  use once_cell::sync::Lazy;
-  static STORE: Lazy<ChildStore> = Lazy::new(Default::default);
-  &STORE
-}
-
-/// Kills all child processes created with [`Command`].
-/// By default it's called before the [`crate::App`] exits.
-pub fn kill_children() {
-  let commands = commands().lock().unwrap();
-  let children = commands.values();
-  for child in children {
-    let _ = child.kill();
-  }
-}
-
-/// Payload for the [`CommandEvent::Terminated`] command event.
-#[derive(Debug, Clone, Serialize)]
-pub struct TerminatedPayload {
-  /// Exit code of the process.
-  pub code: Option<i32>,
-  /// If the process was terminated by a signal, represents that signal.
-  pub signal: Option<i32>,
-}
-
-/// A event sent to the command callback.
-#[derive(Debug, Clone)]
-#[non_exhaustive]
-pub enum CommandEvent {
-  /// Stderr bytes until a newline (\n) or carriage return (\r) is found.
-  Stderr(Vec<u8>),
-  /// Stdout bytes until a newline (\n) or carriage return (\r) is found.
-  Stdout(Vec<u8>),
-  /// An error happened waiting for the command to finish or converting the stdout/stderr bytes to an UTF-8 string.
-  Error(String),
-  /// Command process terminated.
-  Terminated(TerminatedPayload),
-}
-
-/// The type to spawn commands.
-#[derive(Debug)]
-pub struct Command {
-  program: String,
-  args: Vec<String>,
-  env_clear: bool,
-  env: HashMap<String, String>,
-  current_dir: Option<PathBuf>,
-}
-
-/// Spawned child process.
-#[derive(Debug)]
-pub struct CommandChild {
-  inner: Arc<SharedChild>,
-  stdin_writer: PipeWriter,
-}
-
-impl CommandChild {
-  /// Writes to process stdin.
-  pub fn write(&mut self, buf: &[u8]) -> crate::api::Result<()> {
-    self.stdin_writer.write_all(buf)?;
-    Ok(())
-  }
-
-  /// Sends a kill signal to the child.
-  pub fn kill(self) -> crate::api::Result<()> {
-    self.inner.kill()?;
-    Ok(())
-  }
-
-  /// Returns the process pid.
-  pub fn pid(&self) -> u32 {
-    self.inner.id()
-  }
-}
-
-/// Describes the result of a process after it has terminated.
-#[derive(Debug)]
-pub struct ExitStatus {
-  code: Option<i32>,
-}
-
-impl ExitStatus {
-  /// Returns the exit code of the process, if any.
-  pub fn code(&self) -> Option<i32> {
-    self.code
-  }
-
-  /// Returns true if exit status is zero. Signal termination is not considered a success, and success is defined as a zero exit status.
-  pub fn success(&self) -> bool {
-    self.code == Some(0)
-  }
-}
-
-/// The output of a finished process.
-#[derive(Debug)]
-pub struct Output {
-  /// The status (exit code) of the process.
-  pub status: ExitStatus,
-  /// The data that the process wrote to stdout.
-  pub stdout: Vec<u8>,
-  /// The data that the process wrote to stderr.
-  pub stderr: Vec<u8>,
-}
-
-fn relative_command_path(command: String) -> crate::Result<String> {
-  match platform::current_exe()?.parent() {
-    #[cfg(windows)]
-    Some(exe_dir) => Ok(format!("{}\\{command}.exe", exe_dir.display())),
-    #[cfg(not(windows))]
-    Some(exe_dir) => Ok(format!("{}/{command}", exe_dir.display())),
-    None => Err(crate::api::Error::Command("Could not evaluate executable dir".to_string()).into()),
-  }
-}
-
-impl From<Command> for StdCommand {
-  fn from(cmd: Command) -> StdCommand {
-    let mut command = StdCommand::new(cmd.program);
-    command.args(cmd.args);
-    command.stdout(Stdio::piped());
-    command.stdin(Stdio::piped());
-    command.stderr(Stdio::piped());
-    if cmd.env_clear {
-      command.env_clear();
-    }
-    command.envs(cmd.env);
-    if let Some(current_dir) = cmd.current_dir {
-      command.current_dir(current_dir);
-    }
-    #[cfg(windows)]
-    command.creation_flags(CREATE_NO_WINDOW);
-    command
-  }
-}
-
-impl Command {
-  /// Creates a new Command for launching the given program.
-  pub fn new<S: Into<String>>(program: S) -> Self {
-    Self {
-      program: program.into(),
-      args: Default::default(),
-      env_clear: false,
-      env: Default::default(),
-      current_dir: None,
-    }
-  }
-
-  /// Creates a new Command for launching the given sidecar program.
-  ///
-  /// A sidecar program is a embedded external binary in order to make your application work
-  /// or to prevent users having to install additional dependencies (e.g. Node.js, Python, etc).
-  pub fn new_sidecar<S: Into<String>>(program: S) -> crate::Result<Self> {
-    Ok(Self::new(relative_command_path(program.into())?))
-  }
-
-  /// Appends arguments to the command.
-  #[must_use]
-  pub fn args<I, S>(mut self, args: I) -> Self
-  where
-    I: IntoIterator<Item = S>,
-    S: AsRef<str>,
-  {
-    for arg in args {
-      self.args.push(arg.as_ref().to_string());
-    }
-    self
-  }
-
-  /// Clears the entire environment map for the child process.
-  #[must_use]
-  pub fn env_clear(mut self) -> Self {
-    self.env_clear = true;
-    self
-  }
-
-  /// Adds or updates multiple environment variable mappings.
-  #[must_use]
-  pub fn envs(mut self, env: HashMap<String, String>) -> Self {
-    self.env = env;
-    self
-  }
-
-  /// Sets the working directory for the child process.
-  #[must_use]
-  pub fn current_dir(mut self, current_dir: PathBuf) -> Self {
-    self.current_dir.replace(current_dir);
-    self
-  }
-
-  /// Spawns the command.
-  ///
-  /// # Examples
-  ///
-  /// ```rust,no_run
-  /// use tauri::api::process::{Command, CommandEvent};
-  /// tauri::async_runtime::spawn(async move {
-  ///   let (mut rx, mut child) = Command::new("cargo")
-  ///     .args(["tauri", "dev"])
-  ///     .spawn()
-  ///     .expect("Failed to spawn cargo");
-  ///
-  ///   let mut i = 0;
-  ///   while let Some(event) = rx.recv().await {
-  ///     if let CommandEvent::Stdout(line) = event {
-  ///       println!("got: {}", String::from_utf8(line).unwrap());
-  ///       i += 1;
-  ///       if i == 4 {
-  ///         child.write("message from Rust\n".as_bytes()).unwrap();
-  ///         i = 0;
-  ///       }
-  ///     }
-  ///   }
-  /// });
-  /// ```
-  pub fn spawn(self) -> crate::api::Result<(Receiver<CommandEvent>, CommandChild)> {
-    let mut command: StdCommand = self.into();
-    let (stdout_reader, stdout_writer) = pipe()?;
-    let (stderr_reader, stderr_writer) = pipe()?;
-    let (stdin_reader, stdin_writer) = pipe()?;
-    command.stdout(stdout_writer);
-    command.stderr(stderr_writer);
-    command.stdin(stdin_reader);
-
-    let shared_child = SharedChild::spawn(&mut command)?;
-    let child = Arc::new(shared_child);
-    let child_ = child.clone();
-    let guard = Arc::new(RwLock::new(()));
-
-    commands().lock().unwrap().insert(child.id(), child.clone());
-
-    let (tx, rx) = channel(1);
-
-    spawn_pipe_reader(
-      tx.clone(),
-      guard.clone(),
-      stdout_reader,
-      CommandEvent::Stdout,
-    );
-    spawn_pipe_reader(
-      tx.clone(),
-      guard.clone(),
-      stderr_reader,
-      CommandEvent::Stderr,
-    );
-
-    spawn(move || {
-      let _ = match child_.wait() {
-        Ok(status) => {
-          let _l = guard.write().unwrap();
-          commands().lock().unwrap().remove(&child_.id());
-          block_on_task(async move {
-            tx.send(CommandEvent::Terminated(TerminatedPayload {
-              code: status.code(),
-              #[cfg(windows)]
-              signal: None,
-              #[cfg(unix)]
-              signal: status.signal(),
-            }))
-            .await
-          })
-        }
-        Err(e) => {
-          let _l = guard.write().unwrap();
-          block_on_task(async move { tx.send(CommandEvent::Error(e.to_string())).await })
-        }
-      };
-    });
-
-    Ok((
-      rx,
-      CommandChild {
-        inner: child,
-        stdin_writer,
-      },
-    ))
-  }
-
-  /// Executes a command as a child process, waiting for it to finish and collecting its exit status.
-  /// Stdin, stdout and stderr are ignored.
-  ///
-  /// # Examples
-  /// ```rust,no_run
-  /// use tauri::api::process::Command;
-  /// let status = Command::new("which").args(["ls"]).status().unwrap();
-  /// println!("`which` finished with status: {:?}", status.code());
-  /// ```
-  pub fn status(self) -> crate::api::Result<ExitStatus> {
-    let (mut rx, _child) = self.spawn()?;
-    let code = crate::async_runtime::safe_block_on(async move {
-      let mut code = None;
-      #[allow(clippy::collapsible_match)]
-      while let Some(event) = rx.recv().await {
-        if let CommandEvent::Terminated(payload) = event {
-          code = payload.code;
-        }
-      }
-      code
-    });
-    Ok(ExitStatus { code })
-  }
-
-  /// Executes the command as a child process, waiting for it to finish and collecting all of its output.
-  /// Stdin is ignored.
-  ///
-  /// # Examples
-  ///
-  /// ```rust,no_run
-  /// use tauri::api::process::Command;
-  /// let output = Command::new("echo").args(["TAURI"]).output().unwrap();
-  /// assert!(output.status.success());
-  /// assert_eq!(String::from_utf8(output.stdout).unwrap(), "TAURI");
-  /// ```
-  pub fn output(self) -> crate::api::Result<Output> {
-    let (mut rx, _child) = self.spawn()?;
-
-    let output = crate::async_runtime::safe_block_on(async move {
-      let mut code = None;
-      let mut stdout = Vec::new();
-      let mut stderr = Vec::new();
-
-      while let Some(event) = rx.recv().await {
-        match event {
-          CommandEvent::Terminated(payload) => {
-            code = payload.code;
-          }
-          CommandEvent::Stdout(line) => {
-            stdout.extend(line);
-            stdout.push(NEWLINE_BYTE);
-          }
-          CommandEvent::Stderr(line) => {
-            stderr.extend(line);
-            stderr.push(NEWLINE_BYTE);
-          }
-          CommandEvent::Error(_) => {}
-        }
-      }
-      Output {
-        status: ExitStatus { code },
-        stdout,
-        stderr,
-      }
-    });
-
-    Ok(output)
-  }
-}
-
-fn spawn_pipe_reader<F: Fn(Vec<u8>) -> CommandEvent + Send + Copy + 'static>(
-  tx: Sender<CommandEvent>,
-  guard: Arc<RwLock<()>>,
-  pipe_reader: PipeReader,
-  wrapper: F,
-) {
-  spawn(move || {
-    let _lock = guard.read().unwrap();
-    let mut reader = BufReader::new(pipe_reader);
-
-    loop {
-      let mut buf = Vec::new();
-      match tauri_utils::io::read_line(&mut reader, &mut buf) {
-        Ok(n) => {
-          if n == 0 {
-            break;
-          }
-          let tx_ = tx.clone();
-          let _ = block_on_task(async move { tx_.send(wrapper(buf)).await });
-        }
-        Err(e) => {
-          let tx_ = tx.clone();
-          let _ = block_on_task(async move { tx_.send(CommandEvent::Error(e.to_string())).await });
-        }
-      }
-    }
-  });
-}
-
-// tests for the commands functions.
-#[cfg(test)]
-mod tests {
-  #[cfg(not(windows))]
-  use super::*;
-
-  #[cfg(not(windows))]
-  #[test]
-  fn test_cmd_spawn_output() {
-    let cmd = Command::new("cat").args(["test/api/test.txt"]);
-    let (mut rx, _) = cmd.spawn().unwrap();
-
-    crate::async_runtime::block_on(async move {
-      while let Some(event) = rx.recv().await {
-        match event {
-          CommandEvent::Terminated(payload) => {
-            assert_eq!(payload.code, Some(0));
-          }
-          CommandEvent::Stdout(line) => {
-            assert_eq!(String::from_utf8(line).unwrap(), "This is a test doc!");
-          }
-          _ => {}
-        }
-      }
-    });
-  }
-
-  #[cfg(not(windows))]
-  #[test]
-  fn test_cmd_spawn_raw_output() {
-    let cmd = Command::new("cat").args(["test/api/test.txt"]);
-    let (mut rx, _) = cmd.spawn().unwrap();
-
-    crate::async_runtime::block_on(async move {
-      while let Some(event) = rx.recv().await {
-        match event {
-          CommandEvent::Terminated(payload) => {
-            assert_eq!(payload.code, Some(0));
-          }
-          CommandEvent::Stdout(line) => {
-            assert_eq!(String::from_utf8(line).unwrap(), "This is a test doc!");
-          }
-          _ => {}
-        }
-      }
-    });
-  }
-
-  #[cfg(not(windows))]
-  #[test]
-  // test the failure case
-  fn test_cmd_spawn_fail() {
-    let cmd = Command::new("cat").args(["test/api/"]);
-    let (mut rx, _) = cmd.spawn().unwrap();
-
-    crate::async_runtime::block_on(async move {
-      while let Some(event) = rx.recv().await {
-        match event {
-          CommandEvent::Terminated(payload) => {
-            assert_eq!(payload.code, Some(1));
-          }
-          CommandEvent::Stderr(line) => {
-            assert_eq!(
-              String::from_utf8(line).unwrap(),
-              "cat: test/api/: Is a directory"
-            );
-          }
-          _ => {}
-        }
-      }
-    });
-  }
-
-  #[cfg(not(windows))]
-  #[test]
-  // test the failure case (raw encoding)
-  fn test_cmd_spawn_raw_fail() {
-    let cmd = Command::new("cat").args(["test/api/"]);
-    let (mut rx, _) = cmd.spawn().unwrap();
-
-    crate::async_runtime::block_on(async move {
-      while let Some(event) = rx.recv().await {
-        match event {
-          CommandEvent::Terminated(payload) => {
-            assert_eq!(payload.code, Some(1));
-          }
-          CommandEvent::Stderr(line) => {
-            assert_eq!(
-              String::from_utf8(line).unwrap(),
-              "cat: test/api/: Is a directory"
-            );
-          }
-          _ => {}
-        }
-      }
-    });
-  }
-
-  #[cfg(not(windows))]
-  #[test]
-  fn test_cmd_output_output() {
-    let cmd = Command::new("cat").args(["test/api/test.txt"]);
-    let output = cmd.output().unwrap();
-
-    assert_eq!(String::from_utf8(output.stderr).unwrap(), "");
-    assert_eq!(
-      String::from_utf8(output.stdout).unwrap(),
-      "This is a test doc!\n"
-    );
-  }
-
-  #[cfg(not(windows))]
-  #[test]
-  fn test_cmd_output_output_fail() {
-    let cmd = Command::new("cat").args(["test/api/"]);
-    let output = cmd.output().unwrap();
-
-    assert_eq!(String::from_utf8(output.stdout).unwrap(), "");
-    assert_eq!(
-      String::from_utf8(output.stderr).unwrap(),
-      "cat: test/api/: Is a directory\n"
-    );
-  }
-}

+ 0 - 116
core/tauri/src/api/shell.rs

@@ -1,116 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-//! Types and functions related to shell.
-
-use crate::ShellScope;
-use std::str::FromStr;
-
-/// Program to use on the [`open()`] call.
-pub enum Program {
-  /// Use the `open` program.
-  Open,
-  /// Use the `start` program.
-  Start,
-  /// Use the `xdg-open` program.
-  XdgOpen,
-  /// Use the `gio` program.
-  Gio,
-  /// Use the `gnome-open` program.
-  GnomeOpen,
-  /// Use the `kde-open` program.
-  KdeOpen,
-  /// Use the `wslview` program.
-  WslView,
-  /// Use the `Firefox` program.
-  Firefox,
-  /// Use the `Google Chrome` program.
-  Chrome,
-  /// Use the `Chromium` program.
-  Chromium,
-  /// Use the `Safari` program.
-  Safari,
-}
-
-impl FromStr for Program {
-  type Err = super::Error;
-
-  fn from_str(s: &str) -> Result<Self, Self::Err> {
-    let p = match s.to_lowercase().as_str() {
-      "open" => Self::Open,
-      "start" => Self::Start,
-      "xdg-open" => Self::XdgOpen,
-      "gio" => Self::Gio,
-      "gnome-open" => Self::GnomeOpen,
-      "kde-open" => Self::KdeOpen,
-      "wslview" => Self::WslView,
-      "firefox" => Self::Firefox,
-      "chrome" | "google chrome" => Self::Chrome,
-      "chromium" => Self::Chromium,
-      "safari" => Self::Safari,
-      _ => return Err(super::Error::UnknownProgramName(s.to_string())),
-    };
-    Ok(p)
-  }
-}
-
-impl Program {
-  pub(crate) fn name(self) -> &'static str {
-    match self {
-      Self::Open => "open",
-      Self::Start => "start",
-      Self::XdgOpen => "xdg-open",
-      Self::Gio => "gio",
-      Self::GnomeOpen => "gnome-open",
-      Self::KdeOpen => "kde-open",
-      Self::WslView => "wslview",
-
-      #[cfg(target_os = "macos")]
-      Self::Firefox => "Firefox",
-      #[cfg(not(target_os = "macos"))]
-      Self::Firefox => "firefox",
-
-      #[cfg(target_os = "macos")]
-      Self::Chrome => "Google Chrome",
-      #[cfg(not(target_os = "macos"))]
-      Self::Chrome => "google-chrome",
-
-      #[cfg(target_os = "macos")]
-      Self::Chromium => "Chromium",
-      #[cfg(not(target_os = "macos"))]
-      Self::Chromium => "chromium",
-
-      #[cfg(target_os = "macos")]
-      Self::Safari => "Safari",
-      #[cfg(not(target_os = "macos"))]
-      Self::Safari => "safari",
-    }
-  }
-}
-
-/// Opens path or URL with the program specified in `with`, or system default if `None`.
-///
-/// The path will be matched against the shell open validation regex, defaulting to `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`.
-/// A custom validation regex may be supplied in the config in `tauri > allowlist > scope > open`.
-///
-/// # Examples
-///
-/// ```rust,no_run
-/// use tauri::{api::shell::open, Manager};
-/// tauri::Builder::default()
-///   .setup(|app| {
-///     // open the given URL on the system default browser
-///     open(&app.shell_scope(), "https://github.com/tauri-apps/tauri", None)?;
-///     Ok(())
-///   });
-/// ```
-pub fn open<P: AsRef<str>>(
-  scope: &ShellScope,
-  path: P,
-  with: Option<Program>,
-) -> crate::api::Result<()> {
-  scope
-    .open(path.as_ref(), with)
-    .map_err(|err| crate::api::Error::Shell(format!("failed to open: {err}")))
-}

+ 7 - 23
core/tauri/src/app.rs

@@ -27,9 +27,6 @@ use crate::{
   Runtime, Scopes, StateManager, Theme, Window,
 };
 
-#[cfg(shell_scope)]
-use crate::scope::ShellScope;
-
 use raw_window_handle::HasRawDisplayHandle;
 use tauri_macros::default_runtime;
 use tauri_runtime::window::{
@@ -420,18 +417,14 @@ impl<R: Runtime> AppHandle<R> {
     std::process::exit(exit_code);
   }
 
-  /// Restarts the app. This is the same as [`crate::api::process::restart`], but it performs cleanup on this application.
+  /// Restarts the app. This is the same as [`crate::process::restart`], but it performs cleanup on this application.
   pub fn restart(&self) {
     self.cleanup_before_exit();
-    crate::api::process::restart(&self.env());
+    crate::process::restart(&self.env());
   }
 
   /// Runs necessary cleanup tasks before exiting the process
   fn cleanup_before_exit(&self) {
-    #[cfg(any(shell_execute, shell_sidecar))]
-    {
-      crate::api::process::kill_children();
-    }
     #[cfg(all(windows, feature = "system-tray"))]
     {
       for tray in self.manager().trays().values() {
@@ -764,7 +757,7 @@ impl<R: Runtime> App<R> {
   /// Runs a iteration of the runtime event loop and immediately return.
   ///
   /// Note that when using this API, app cleanup is not automatically done.
-  /// The cleanup calls [`crate::api::process::kill_children`] so you may want to call that function before exiting the application.
+  /// The cleanup calls [`crate::process::kill_children`] so you may want to call that function before exiting the application.
   /// Additionally, the cleanup calls [AppHandle#remove_system_tray](`AppHandle#method.remove_system_tray`) (Windows only).
   ///
   /// # Examples
@@ -1203,19 +1196,15 @@ impl<R: Runtime> Builder<R> {
   ///     MenuEntry::Submenu(Submenu::new(
   ///       "File",
   ///       Menu::with_items([
-  ///         CustomMenuItem::new("New", "New").into(),
-  ///         CustomMenuItem::new("Learn More", "Learn More").into(),
+  ///         CustomMenuItem::new("new", "New").into(),
+  ///         CustomMenuItem::new("learn-more", "Learn More").into(),
   ///       ]),
   ///     )),
   ///   ]))
   ///   .on_menu_event(|event| {
   ///     match event.menu_item_id() {
-  ///       "Learn More" => {
-  ///         // open in browser (requires the `shell-open-api` feature)
-  #[cfg_attr(
-    feature = "shell-open-api",
-    doc = r#"         api::shell::open(&event.window().shell_scope(), "https://github.com/tauri-apps/tauri".to_string(), None).unwrap();"#
-  )]
+  ///       "learn-more" => {
+  ///         // open a link in the browser using tauri-plugin-shell
   ///       }
   ///       id => {
   ///         // do something with other events
@@ -1384,9 +1373,6 @@ impl<R: Runtime> Builder<R> {
       self.menu = Some(Menu::os_default(&context.package_info().name));
     }
 
-    #[cfg(shell_scope)]
-    let shell_scope = context.shell_scope.clone();
-
     let manager = WindowManager::with_handlers(
       context,
       self.plugins,
@@ -1462,8 +1448,6 @@ impl<R: Runtime> Builder<R> {
         &app,
         &app.config().tauri.allowlist.protocol.asset_scope,
       )?,
-      #[cfg(shell_scope)]
-      shell: ShellScope::new(&app, shell_scope),
     });
 
     #[cfg(windows)]

+ 0 - 11
core/tauri/src/endpoints.rs

@@ -18,8 +18,6 @@ mod event;
 mod operating_system;
 #[cfg(process_any)]
 mod process;
-#[cfg(shell_any)]
-mod shell;
 mod window;
 
 /// The context passed to the invoke handler.
@@ -62,8 +60,6 @@ enum Module {
   #[cfg(os_any)]
   Os(operating_system::Cmd),
   Window(Box<window::Cmd>),
-  #[cfg(shell_any)]
-  Shell(shell::Cmd),
   Event(event::Cmd),
 }
 
@@ -108,13 +104,6 @@ impl Module {
           .and_then(|r| r.json)
           .map_err(InvokeError::from_anyhow)
       }),
-      #[cfg(shell_any)]
-      Self::Shell(cmd) => resolver.respond_async(async move {
-        cmd
-          .run(context)
-          .and_then(|r| r.json)
-          .map_err(InvokeError::from_anyhow)
-      }),
       Self::Event(cmd) => resolver.respond_async(async move {
         cmd
           .run(context)

+ 0 - 345
core/tauri/src/endpoints/shell.rs

@@ -1,345 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-#![allow(unused_imports)]
-
-use super::InvokeContext;
-#[cfg(any(shell_execute, shell_sidecar))]
-use crate::api::process::{CommandEvent, TerminatedPayload};
-use crate::{api::ipc::CallbackFn, Runtime};
-#[cfg(shell_scope)]
-use crate::{Manager, Scopes};
-use encoding_rs::Encoding;
-use serde::{Deserialize, Serialize};
-use tauri_macros::{command_enum, module_command_handler, CommandModule};
-
-#[cfg(shell_scope)]
-use crate::ExecuteArgs;
-#[cfg(not(shell_scope))]
-type ExecuteArgs = ();
-
-#[cfg(any(shell_execute, shell_sidecar))]
-use std::sync::{Arc, Mutex};
-use std::{collections::HashMap, path::PathBuf, string::FromUtf8Error};
-
-type ChildId = u32;
-#[cfg(any(shell_execute, shell_sidecar))]
-type ChildStore = Arc<Mutex<HashMap<ChildId, crate::api::process::CommandChild>>>;
-
-#[cfg(any(shell_execute, shell_sidecar))]
-fn command_child_store() -> &'static ChildStore {
-  use once_cell::sync::Lazy;
-  static STORE: Lazy<ChildStore> = Lazy::new(Default::default);
-  &STORE
-}
-
-#[cfg(any(shell_execute, shell_sidecar))]
-#[derive(Debug, Clone, Serialize)]
-#[serde(tag = "event", content = "payload")]
-#[non_exhaustive]
-enum JSCommandEvent {
-  /// Stderr bytes until a newline (\n) or carriage return (\r) is found.
-  Stderr(Buffer),
-  /// Stdout bytes until a newline (\n) or carriage return (\r) is found.
-  Stdout(Buffer),
-  /// An error happened waiting for the command to finish or converting the stdout/stderr bytes to an UTF-8 string.
-  Error(String),
-  /// Command process terminated.
-  Terminated(TerminatedPayload),
-}
-
-#[cfg(any(shell_execute, shell_sidecar))]
-fn get_event_buffer(line: Vec<u8>, encoding: EncodingWrapper) -> Result<Buffer, FromUtf8Error> {
-  match encoding {
-    EncodingWrapper::Text(character_encoding) => match character_encoding {
-      Some(encoding) => Ok(Buffer::Text(
-        encoding.decode_with_bom_removal(&line).0.into(),
-      )),
-      None => String::from_utf8(line).map(Buffer::Text),
-    },
-    EncodingWrapper::Raw => Ok(Buffer::Raw(line)),
-  }
-}
-
-#[cfg(any(shell_execute, shell_sidecar))]
-impl JSCommandEvent {
-  pub fn new(event: CommandEvent, encoding: EncodingWrapper) -> Self {
-    match event {
-      CommandEvent::Terminated(payload) => JSCommandEvent::Terminated(payload),
-      CommandEvent::Error(error) => JSCommandEvent::Error(error),
-      CommandEvent::Stderr(line) => get_event_buffer(line, encoding)
-        .map(JSCommandEvent::Stderr)
-        .unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())),
-      CommandEvent::Stdout(line) => get_event_buffer(line, encoding)
-        .map(JSCommandEvent::Stdout)
-        .unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())),
-    }
-  }
-}
-
-#[derive(Debug, Clone, Deserialize, Serialize)]
-#[serde(untagged)]
-#[allow(missing_docs)]
-pub enum Buffer {
-  Text(String),
-  Raw(Vec<u8>),
-}
-
-#[cfg(any(shell_execute, shell_sidecar))]
-#[derive(Debug, Copy, Clone)]
-pub enum EncodingWrapper {
-  Raw,
-  Text(Option<&'static Encoding>),
-}
-
-#[allow(clippy::unnecessary_wraps)]
-fn default_env() -> Option<HashMap<String, String>> {
-  Some(HashMap::default())
-}
-
-#[allow(dead_code)]
-#[derive(Debug, Clone, Default, Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct CommandOptions {
-  #[serde(default)]
-  sidecar: bool,
-  cwd: Option<PathBuf>,
-  // by default we don't add any env variables to the spawned process
-  // but the env is an `Option` so when it's `None` we clear the env.
-  #[serde(default = "default_env")]
-  env: Option<HashMap<String, String>>,
-  // Character encoding for stdout/stderr
-  encoding: Option<String>,
-}
-
-/// The API descriptor.
-#[command_enum]
-#[derive(Deserialize, CommandModule)]
-#[serde(tag = "cmd", rename_all = "camelCase")]
-pub enum Cmd {
-  /// The execute script API.
-  #[cmd(shell_script, "shell > execute or shell > sidecar")]
-  #[serde(rename_all = "camelCase")]
-  Execute {
-    program: String,
-    args: ExecuteArgs,
-    on_event_fn: CallbackFn,
-    #[serde(default)]
-    options: CommandOptions,
-  },
-  #[cmd(shell_script, "shell > execute or shell > sidecar")]
-  StdinWrite { pid: ChildId, buffer: Buffer },
-  #[cmd(shell_script, "shell > execute or shell > sidecar")]
-  KillChild { pid: ChildId },
-  #[cmd(shell_open, "shell > open")]
-  Open { path: String, with: Option<String> },
-}
-
-impl Cmd {
-  #[module_command_handler(shell_script)]
-  #[allow(unused_variables)]
-  fn execute<R: Runtime>(
-    context: InvokeContext<R>,
-    program: String,
-    args: ExecuteArgs,
-    on_event_fn: CallbackFn,
-    options: CommandOptions,
-  ) -> super::Result<ChildId> {
-    let mut command = if options.sidecar {
-      #[cfg(not(shell_sidecar))]
-      return Err(crate::Error::ApiNotAllowlisted("shell > sidecar".to_string()).into_anyhow());
-      #[cfg(shell_sidecar)]
-      {
-        let program = PathBuf::from(program);
-        let program_as_string = program.display().to_string();
-        let program_no_ext_as_string = program.with_extension("").display().to_string();
-        let configured_sidecar = context
-          .config
-          .tauri
-          .bundle
-          .external_bin
-          .as_ref()
-          .map(|bins| {
-            bins
-              .iter()
-              .find(|b| b == &&program_as_string || b == &&program_no_ext_as_string)
-          })
-          .unwrap_or_default();
-        if let Some(sidecar) = configured_sidecar {
-          context
-            .window
-            .state::<Scopes>()
-            .shell
-            .prepare_sidecar(&program.to_string_lossy(), sidecar, args)
-            .map_err(crate::error::into_anyhow)?
-        } else {
-          return Err(crate::Error::SidecarNotAllowed(program).into_anyhow());
-        }
-      }
-    } else {
-      #[cfg(not(shell_execute))]
-      return Err(crate::Error::ApiNotAllowlisted("shell > execute".to_string()).into_anyhow());
-      #[cfg(shell_execute)]
-      match context
-        .window
-        .state::<Scopes>()
-        .shell
-        .prepare(&program, args)
-      {
-        Ok(cmd) => cmd,
-        Err(e) => {
-          #[cfg(debug_assertions)]
-          eprintln!("{e}");
-          return Err(crate::Error::ProgramNotAllowed(PathBuf::from(program)).into_anyhow());
-        }
-      }
-    };
-    #[cfg(any(shell_execute, shell_sidecar))]
-    {
-      if let Some(cwd) = options.cwd {
-        command = command.current_dir(cwd);
-      }
-      if let Some(env) = options.env {
-        command = command.envs(env);
-      } else {
-        command = command.env_clear();
-      }
-      let encoding = match options.encoding {
-        Option::None => EncodingWrapper::Text(None),
-        Some(encoding) => match encoding.as_str() {
-          "raw" => EncodingWrapper::Raw,
-          _ => {
-            if let Some(text_encoding) =
-              crate::api::process::Encoding::for_label(encoding.as_bytes())
-            {
-              EncodingWrapper::Text(Some(text_encoding))
-            } else {
-              return Err(anyhow::anyhow!(format!("unknown encoding {encoding}")));
-            }
-          }
-        },
-      };
-
-      let (mut rx, child) = command.spawn()?;
-
-      let pid = child.pid();
-      command_child_store().lock().unwrap().insert(pid, child);
-
-      crate::async_runtime::spawn(async move {
-        while let Some(event) = rx.recv().await {
-          if matches!(event, crate::api::process::CommandEvent::Terminated(_)) {
-            command_child_store().lock().unwrap().remove(&pid);
-          };
-          let js_event = JSCommandEvent::new(event, encoding);
-          let js = crate::api::ipc::format_callback(on_event_fn, &js_event)
-            .expect("unable to serialize CommandEvent");
-
-          let _ = context.window.eval(js.as_str());
-        }
-      });
-
-      Ok(pid)
-    }
-  }
-
-  #[module_command_handler(shell_script)]
-  fn stdin_write<R: Runtime>(
-    _context: InvokeContext<R>,
-    pid: ChildId,
-    buffer: Buffer,
-  ) -> super::Result<()> {
-    if let Some(child) = command_child_store().lock().unwrap().get_mut(&pid) {
-      match buffer {
-        Buffer::Text(t) => child.write(t.as_bytes())?,
-        Buffer::Raw(r) => child.write(&r)?,
-      }
-    }
-    Ok(())
-  }
-
-  #[module_command_handler(shell_script)]
-  fn kill_child<R: Runtime>(_context: InvokeContext<R>, pid: ChildId) -> super::Result<()> {
-    if let Some(child) = command_child_store().lock().unwrap().remove(&pid) {
-      child.kill()?;
-    }
-    Ok(())
-  }
-
-  /// Open a (url) path with a default or specific browser opening program.
-  ///
-  /// See [`crate::api::shell::open`] for how it handles security-related measures.
-  #[module_command_handler(shell_open)]
-  fn open<R: Runtime>(
-    context: InvokeContext<R>,
-    path: String,
-    with: Option<String>,
-  ) -> super::Result<()> {
-    use std::str::FromStr;
-
-    with
-      .as_deref()
-      // only allow pre-determined programs to be specified
-      .map(crate::api::shell::Program::from_str)
-      .transpose()
-      .map_err(Into::into)
-      // validate and open path
-      .and_then(|with| {
-        crate::api::shell::open(&context.window.state::<Scopes>().shell, path, with)
-          .map_err(Into::into)
-      })
-  }
-}
-
-#[cfg(test)]
-mod tests {
-  use super::{Buffer, ChildId, CommandOptions, ExecuteArgs};
-  use crate::api::ipc::CallbackFn;
-  use quickcheck::{Arbitrary, Gen};
-
-  impl Arbitrary for CommandOptions {
-    fn arbitrary(g: &mut Gen) -> Self {
-      Self {
-        sidecar: false,
-        cwd: Option::arbitrary(g),
-        env: Option::arbitrary(g),
-        encoding: Option::arbitrary(g),
-      }
-    }
-  }
-
-  impl Arbitrary for Buffer {
-    fn arbitrary(g: &mut Gen) -> Self {
-      Buffer::Text(String::arbitrary(g))
-    }
-  }
-
-  #[cfg(shell_scope)]
-  impl Arbitrary for ExecuteArgs {
-    fn arbitrary(_: &mut Gen) -> Self {
-      ExecuteArgs::None
-    }
-  }
-
-  #[tauri_macros::module_command_test(shell_execute, "shell > execute")]
-  #[quickcheck_macros::quickcheck]
-  fn execute(
-    _program: String,
-    _args: ExecuteArgs,
-    _on_event_fn: CallbackFn,
-    _options: CommandOptions,
-  ) {
-  }
-
-  #[tauri_macros::module_command_test(shell_execute, "shell > execute or shell > sidecar")]
-  #[quickcheck_macros::quickcheck]
-  fn stdin_write(_pid: ChildId, _buffer: Buffer) {}
-
-  #[tauri_macros::module_command_test(shell_execute, "shell > execute or shell > sidecar")]
-  #[quickcheck_macros::quickcheck]
-  fn kill_child(_pid: ChildId) {}
-
-  #[tauri_macros::module_command_test(shell_open, "shell > open")]
-  #[quickcheck_macros::quickcheck]
-  fn open(_path: String, _with: Option<String>) {}
-}

+ 1 - 15
core/tauri/src/error.rs

@@ -2,7 +2,7 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: MIT
 
-use std::{fmt, path::PathBuf};
+use std::fmt;
 
 /// A generic boxed error.
 #[derive(Debug)]
@@ -85,23 +85,9 @@ pub enum Error {
   /// Error initializing plugin.
   #[error("failed to initialize plugin `{0}`: {1}")]
   PluginInitialization(String, String),
-  /// A part of the URL is malformed or invalid. This may occur when parsing and combining
-  /// user-provided URLs and paths.
-  #[error("invalid url: {0}")]
-  InvalidUrl(url::ParseError),
   /// Task join error.
   #[error(transparent)]
   JoinError(#[from] tokio::task::JoinError),
-  /// Sidecar not allowed by the configuration.
-  #[error("sidecar not configured under `tauri.conf.json > tauri > bundle > externalBin`: {0}")]
-  SidecarNotAllowed(PathBuf),
-  /// Sidecar was not found by the configuration.
-  #[cfg(shell_scope)]
-  #[error("sidecar configuration found, but unable to create a path to it: {0}")]
-  SidecarNotFound(#[from] Box<crate::ShellScopeError>),
-  /// Program not allowed by the scope.
-  #[error("program not allowed on the configured shell scope: {0}")]
-  ProgramNotAllowed(PathBuf),
   /// An error happened inside the isolation pattern.
   #[cfg(feature = "isolation")]
   #[error("isolation pattern error: {0}")]

+ 2 - 26
core/tauri/src/lib.rs

@@ -19,12 +19,10 @@
 //! - **updater**: Enables the application auto updater. Enabled by default if the `updater` config is defined on the `tauri.conf.json` file.
 //! - **devtools**: Enables the developer tools (Web inspector) and [`Window::open_devtools`]. Enabled by default on debug builds.
 //! On macOS it uses private APIs, so you can't enable it if your app will be published to the App Store.
-//! - **shell-open-api**: Enables the [`api::shell`] module.
 //! - **native-tls**: Provides TLS support to connect over HTTPS.
 //! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL.
 //! - **rustls-tls**: Provides TLS support to connect over HTTPS using rustls.
-//! - **process-command-api**: Enables the [`api::process::Command`] APIs.
-//! - **process-relaunch-dangerous-allow-symlink-macos**: Allows the [`api::process::current_binary`] function to allow symlinks on macOS (this is dangerous, see the Security section in the documentation website).
+//! - **process-relaunch-dangerous-allow-symlink-macos**: Allows the [`process::current_binary`] function to allow symlinks on macOS (this is dangerous, see the Security section in the documentation website).
 //! - **dialog**: Enables the [`api::dialog`] module.
 //! - **fs-extract-api**: Enabled the `tauri::api::file::Extract` API.
 //! - **system-tray**: Enables application system tray API. Enabled by default if the `systemTray` config is defined on the `tauri.conf.json` file.
@@ -170,9 +168,6 @@ pub use cocoa;
 pub use embed_plist;
 /// The Tauri error enum.
 pub use error::Error;
-#[cfg(shell_scope)]
-#[doc(hidden)]
-pub use regex;
 #[cfg(target_os = "ios")]
 #[doc(hidden)]
 pub use swift_rs;
@@ -200,6 +195,7 @@ mod ios;
 mod jni_helpers;
 /// Path APIs.
 pub mod path;
+pub mod process;
 /// The allowlist scopes.
 pub mod scope;
 mod state;
@@ -543,8 +539,6 @@ pub struct Context<A: Assets> {
   pub(crate) package_info: PackageInfo,
   pub(crate) _info_plist: (),
   pub(crate) pattern: Pattern,
-  #[cfg(shell_scope)]
-  pub(crate) shell_scope: scope::ShellScopeConfig,
 }
 
 impl<A: Assets> fmt::Debug for Context<A> {
@@ -559,8 +553,6 @@ impl<A: Assets> fmt::Debug for Context<A> {
     #[cfg(desktop)]
     d.field("system_tray_icon", &self.system_tray_icon);
 
-    #[cfg(shell_scope)]
-    d.field("shell_scope", &self.shell_scope);
     d.finish()
   }
 }
@@ -634,13 +626,6 @@ impl<A: Assets> Context<A> {
     &self.pattern
   }
 
-  /// The scoped shell commands, where the `HashMap` key is the name each configuration.
-  #[cfg(shell_scope)]
-  #[inline(always)]
-  pub fn allowed_commands(&self) -> &scope::ShellScopeConfig {
-    &self.shell_scope
-  }
-
   /// Create a new [`Context`] from the minimal required items.
   #[inline(always)]
   #[allow(clippy::too_many_arguments)]
@@ -663,8 +648,6 @@ impl<A: Assets> Context<A> {
       package_info,
       _info_plist: info_plist,
       pattern,
-      #[cfg(shell_scope)]
-      shell_scope: Default::default(),
     }
   }
 
@@ -889,12 +872,6 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
     self.state::<Scopes>().inner().asset_protocol.clone()
   }
 
-  /// Gets the scope for the shell execute APIs.
-  #[cfg(shell_scope)]
-  fn shell_scope(&self) -> ShellScope {
-    self.state::<Scopes>().inner().shell.clone()
-  }
-
   /// The path resolver.
   fn path(&self) -> &crate::path::PathResolver<R> {
     self.state::<crate::path::PathResolver<R>>().inner()
@@ -991,7 +968,6 @@ mod tests {
     // features that look like an allowlist feature, but are not
     let allowed = [
       "fs-extract-api",
-      "process-command-api",
       "process-relaunch-dangerous-allow-symlink-macos",
       "window-data-url",
     ];

+ 0 - 1
core/tauri/src/manager.rs

@@ -1242,7 +1242,6 @@ impl<R: Runtime> WindowManager<R> {
           if path.to_str() != Some("index.html") {
             url
               .join(&path.to_string_lossy())
-              .map_err(crate::Error::InvalidUrl)
               // this will never fail
               .unwrap()
           } else {

+ 2 - 9
core/tauri/src/api/process.rs → core/tauri/src/process.rs

@@ -8,13 +8,6 @@ use crate::Env;
 
 use std::path::PathBuf;
 
-#[cfg(feature = "process-command-api")]
-#[cfg_attr(doc_cfg, doc(cfg(feature = "process-command-api")))]
-mod command;
-#[cfg(feature = "process-command-api")]
-#[cfg_attr(doc_cfg, doc(cfg(feature = "process-command-api")))]
-pub use command::*;
-
 /// Finds the current running binary's path.
 ///
 /// With exception to any following platform-specific behavior, the path is cached as soon as
@@ -41,7 +34,7 @@ pub use command::*;
 /// # Examples
 ///
 /// ```rust,no_run
-/// use tauri::{api::process::current_binary, Env, Manager};
+/// use tauri::{process::current_binary, Env, Manager};
 /// let current_binary_path = current_binary(&Env::default()).unwrap();
 ///
 /// tauri::Builder::default()
@@ -70,7 +63,7 @@ pub fn current_binary(_env: &Env) -> std::io::Result<PathBuf> {
 /// # Examples
 ///
 /// ```rust,no_run
-/// use tauri::{api::process::restart, Env, Manager};
+/// use tauri::{process::restart, Env, Manager};
 ///
 /// tauri::Builder::default()
 ///   .setup(|app| {

+ 0 - 10
core/tauri/src/scope/mod.rs

@@ -3,24 +3,14 @@
 // SPDX-License-Identifier: MIT
 
 mod fs;
-#[cfg(shell_scope)]
-mod shell;
 
 pub use fs::{Event as FsScopeEvent, Pattern as GlobPattern, Scope as FsScope};
-#[cfg(shell_scope)]
-pub use shell::{
-  ExecuteArgs, Scope as ShellScope, ScopeAllowedArg as ShellScopeAllowedArg,
-  ScopeAllowedCommand as ShellScopeAllowedCommand, ScopeConfig as ShellScopeConfig,
-  ScopeError as ShellScopeError,
-};
 use std::path::Path;
 
 pub(crate) struct Scopes {
   pub fs: FsScope,
   #[cfg(protocol_asset)]
   pub asset_protocol: FsScope,
-  #[cfg(shell_scope)]
-  pub shell: ShellScope,
 }
 
 impl Scopes {

+ 0 - 361
core/tauri/src/scope/shell.rs

@@ -1,361 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-#[cfg(any(shell_execute, shell_sidecar))]
-use crate::api::process::Command;
-#[cfg(feature = "shell-open-api")]
-use crate::api::shell::Program;
-use crate::{Manager, Runtime};
-
-use regex::Regex;
-
-use std::collections::HashMap;
-
-const DEFAULT_OPEN_REGEX: &str = r#"^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+"#;
-
-/// Allowed representation of `Execute` command arguments.
-#[derive(Debug, Clone, serde::Deserialize)]
-#[serde(untagged, deny_unknown_fields)]
-#[non_exhaustive]
-pub enum ExecuteArgs {
-  /// No arguments
-  None,
-
-  /// A single string argument
-  Single(String),
-
-  /// Multiple string arguments
-  List(Vec<String>),
-}
-
-impl ExecuteArgs {
-  /// Whether the argument list is empty or not.
-  pub fn is_empty(&self) -> bool {
-    match self {
-      Self::None => true,
-      Self::Single(s) if s.is_empty() => true,
-      Self::List(l) => l.is_empty(),
-      _ => false,
-    }
-  }
-}
-
-impl From<()> for ExecuteArgs {
-  fn from(_: ()) -> Self {
-    Self::None
-  }
-}
-
-impl From<String> for ExecuteArgs {
-  fn from(string: String) -> Self {
-    Self::Single(string)
-  }
-}
-
-impl From<Vec<String>> for ExecuteArgs {
-  fn from(vec: Vec<String>) -> Self {
-    Self::List(vec)
-  }
-}
-
-/// Shell scope configuration.
-#[derive(Debug, Clone)]
-pub struct ScopeConfig {
-  /// The validation regex that `shell > open` paths must match against.
-  pub open: Option<Regex>,
-
-  /// All allowed commands, using their unique command name as the keys.
-  pub scopes: HashMap<String, ScopeAllowedCommand>,
-}
-
-impl Default for ScopeConfig {
-  fn default() -> Self {
-    Self {
-      open: Some(Regex::new(DEFAULT_OPEN_REGEX).unwrap()),
-      scopes: Default::default(),
-    }
-  }
-}
-
-impl ScopeConfig {
-  /// Creates a new scope configuration with the default validation regex ^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+.
-  pub fn new() -> Self {
-    Self::default()
-  }
-
-  /// Creates a new scope configuration with the specified validation regex.
-  pub fn with_validator(regex: Regex) -> Self {
-    Self {
-      open: Some(regex),
-      scopes: Default::default(),
-    }
-  }
-
-  /// Unsets the validator regex, allowing any path to be opened.
-  pub fn skip_validation(mut self) -> Self {
-    self.open = None;
-    self
-  }
-
-  /// Sets the commands that are allowed to be executed.
-  pub fn set_allowed_commands<I: IntoIterator<Item = (String, ScopeAllowedCommand)>>(
-    mut self,
-    scopes: I,
-  ) -> Self {
-    self.scopes = scopes.into_iter().collect();
-    self
-  }
-}
-
-/// A configured scoped shell command.
-#[derive(Debug, Clone)]
-pub struct ScopeAllowedCommand {
-  /// The shell command to be called.
-  pub command: std::path::PathBuf,
-
-  /// The arguments the command is allowed to be called with.
-  pub args: Option<Vec<ScopeAllowedArg>>,
-
-  /// If this command is a sidecar command.
-  pub sidecar: bool,
-}
-
-/// A configured argument to a scoped shell command.
-#[derive(Debug, Clone)]
-pub enum ScopeAllowedArg {
-  /// A non-configurable argument.
-  Fixed(String),
-
-  /// An argument with a value to be evaluated at runtime, must pass a regex validation.
-  Var {
-    /// The validation that the variable value must pass in order to be called.
-    validator: Regex,
-  },
-}
-
-impl ScopeAllowedArg {
-  /// If the argument is fixed.
-  pub fn is_fixed(&self) -> bool {
-    matches!(self, Self::Fixed(_))
-  }
-
-  /// If the argument is a variable value.
-  pub fn is_var(&self) -> bool {
-    matches!(self, Self::Var { .. })
-  }
-}
-
-/// Scope for filesystem access.
-#[derive(Clone)]
-pub struct Scope(ScopeConfig);
-
-/// All errors that can happen while validating a scoped command.
-#[derive(Debug, thiserror::Error)]
-pub enum ScopeError {
-  /// At least one argument did not pass input validation.
-  #[cfg(any(shell_execute, shell_sidecar))]
-  #[cfg_attr(
-    doc_cfg,
-    doc(cfg(any(feature = "shell-execute", feature = "shell-sidecar")))
-  )]
-  #[error("The scoped command was called with the improper sidecar flag set")]
-  BadSidecarFlag,
-
-  /// The sidecar program validated but failed to find the sidecar path.
-  ///
-  /// Note: This can be called on `shell-execute` feature too due to [`Scope::prepare`] checking if
-  /// it's a sidecar from the config.
-  #[cfg(any(shell_execute, shell_sidecar))]
-  #[cfg_attr(
-    doc_cfg,
-    doc(cfg(any(feature = "shell-execute", feature = "shell-sidecar")))
-  )]
-  #[error(
-    "The scoped sidecar command was validated, but failed to create the path to the command: {0}"
-  )]
-  Sidecar(crate::Error),
-
-  /// The named command was not found in the scoped config.
-  #[error("Scoped command {0} not found")]
-  #[cfg(any(shell_execute, shell_sidecar))]
-  #[cfg_attr(
-    doc_cfg,
-    doc(cfg(any(feature = "shell-execute", feature = "shell-sidecar")))
-  )]
-  NotFound(String),
-
-  /// A command variable has no value set in the arguments.
-  #[error(
-    "Scoped command argument at position {0} must match regex validation {1} but it was not found"
-  )]
-  #[cfg(any(shell_execute, shell_sidecar))]
-  #[cfg_attr(
-    doc_cfg,
-    doc(cfg(any(feature = "shell-execute", feature = "shell-sidecar")))
-  )]
-  MissingVar(usize, String),
-
-  /// At least one argument did not pass input validation.
-  #[cfg(shell_scope)]
-  #[cfg_attr(
-    doc_cfg,
-    doc(cfg(any(feature = "shell-execute", feature = "shell-open")))
-  )]
-  #[error("Scoped command argument at position {index} was found, but failed regex validation {validation}")]
-  Validation {
-    /// Index of the variable.
-    index: usize,
-
-    /// Regex that the variable value failed to match.
-    validation: String,
-  },
-
-  /// The format of the passed input does not match the expected shape.
-  ///
-  /// This can happen from passing a string or array of strings to a command that is expecting
-  /// named variables, and vice-versa.
-  #[cfg(any(shell_execute, shell_sidecar))]
-  #[cfg_attr(
-    doc_cfg,
-    doc(cfg(any(feature = "shell-execute", feature = "shell-sidecar")))
-  )]
-  #[error("Scoped command {0} received arguments in an unexpected format")]
-  InvalidInput(String),
-
-  /// A generic IO error that occurs while executing specified shell commands.
-  #[cfg(shell_scope)]
-  #[cfg_attr(
-    doc_cfg,
-    doc(cfg(any(feature = "shell-execute", feature = "shell-sidecar")))
-  )]
-  #[error("Scoped shell IO error: {0}")]
-  Io(#[from] std::io::Error),
-}
-
-impl Scope {
-  /// Creates a new shell scope.
-  pub(crate) fn new<R: Runtime, M: Manager<R>>(manager: &M, mut scope: ScopeConfig) -> Self {
-    for cmd in scope.scopes.values_mut() {
-      if let Ok(path) = manager.path().parse(&cmd.command) {
-        cmd.command = path;
-      }
-    }
-    Self(scope)
-  }
-
-  /// Validates argument inputs and creates a Tauri sidecar [`Command`].
-  #[cfg(shell_sidecar)]
-  pub fn prepare_sidecar(
-    &self,
-    command_name: &str,
-    command_script: &str,
-    args: ExecuteArgs,
-  ) -> Result<Command, ScopeError> {
-    self._prepare(command_name, args, Some(command_script))
-  }
-
-  /// Validates argument inputs and creates a Tauri [`Command`].
-  #[cfg(shell_execute)]
-  pub fn prepare(&self, command_name: &str, args: ExecuteArgs) -> Result<Command, ScopeError> {
-    self._prepare(command_name, args, None)
-  }
-
-  /// Validates argument inputs and creates a Tauri [`Command`].
-  #[cfg(any(shell_execute, shell_sidecar))]
-  pub fn _prepare(
-    &self,
-    command_name: &str,
-    args: ExecuteArgs,
-    sidecar: Option<&str>,
-  ) -> Result<Command, ScopeError> {
-    let command = match self.0.scopes.get(command_name) {
-      Some(command) => command,
-      None => return Err(ScopeError::NotFound(command_name.into())),
-    };
-
-    if command.sidecar != sidecar.is_some() {
-      return Err(ScopeError::BadSidecarFlag);
-    }
-
-    let args = match (&command.args, args) {
-      (None, ExecuteArgs::None) => Ok(vec![]),
-      (None, ExecuteArgs::List(list)) => Ok(list),
-      (None, ExecuteArgs::Single(string)) => Ok(vec![string]),
-      (Some(list), ExecuteArgs::List(args)) => list
-        .iter()
-        .enumerate()
-        .map(|(i, arg)| match arg {
-          ScopeAllowedArg::Fixed(fixed) => Ok(fixed.to_string()),
-          ScopeAllowedArg::Var { validator } => {
-            let value = args
-              .get(i)
-              .ok_or_else(|| ScopeError::MissingVar(i, validator.to_string()))?
-              .to_string();
-            if validator.is_match(&value) {
-              Ok(value)
-            } else {
-              Err(ScopeError::Validation {
-                index: i,
-                validation: validator.to_string(),
-              })
-            }
-          }
-        })
-        .collect(),
-      (Some(list), arg) if arg.is_empty() && list.iter().all(ScopeAllowedArg::is_fixed) => list
-        .iter()
-        .map(|arg| match arg {
-          ScopeAllowedArg::Fixed(fixed) => Ok(fixed.to_string()),
-          _ => unreachable!(),
-        })
-        .collect(),
-      (Some(list), _) if list.is_empty() => Err(ScopeError::InvalidInput(command_name.into())),
-      (Some(_), _) => Err(ScopeError::InvalidInput(command_name.into())),
-    }?;
-
-    let command_s = sidecar
-      .map(|s| {
-        std::path::PathBuf::from(s)
-          .components()
-          .last()
-          .unwrap()
-          .as_os_str()
-          .to_string_lossy()
-          .into_owned()
-      })
-      .unwrap_or_else(|| command.command.to_string_lossy().into_owned());
-    let command = if command.sidecar {
-      Command::new_sidecar(command_s).map_err(ScopeError::Sidecar)?
-    } else {
-      Command::new(command_s)
-    };
-
-    Ok(command.args(args))
-  }
-
-  /// Open a path in the default (or specified) browser.
-  ///
-  /// The path is validated against the `tauri > allowlist > shell > open` validation regex, which
-  /// defaults to `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`.
-  #[cfg(feature = "shell-open-api")]
-  pub fn open(&self, path: &str, with: Option<Program>) -> Result<(), ScopeError> {
-    // ensure we pass validation if the configuration has one
-    if let Some(regex) = &self.0.open {
-      if !regex.is_match(path) {
-        return Err(ScopeError::Validation {
-          index: 0,
-          validation: regex.as_str().into(),
-        });
-      }
-    }
-
-    // The prevention of argument escaping is handled by the usage of std::process::Command::arg by
-    // the `open` dependency. This behavior should be re-confirmed during upgrades of `open`.
-    match with.map(Program::name) {
-      Some(program) => ::open::with(path, program),
-      None => ::open::that(path),
-    }
-    .map_err(Into::into)
-  }
-}

+ 0 - 9
core/tauri/src/test/mod.rs

@@ -7,12 +7,8 @@
 mod mock_runtime;
 pub use mock_runtime::*;
 
-#[cfg(shell_scope)]
-use std::collections::HashMap;
 use std::{borrow::Cow, sync::Arc};
 
-#[cfg(shell_scope)]
-use crate::ShellScopeConfig;
 use crate::{Manager, Pattern, WindowBuilder};
 use tauri_utils::{
   assets::{AssetKey, Assets, CspHash},
@@ -71,11 +67,6 @@ pub fn mock_context<A: Assets>(assets: A) -> crate::Context<A> {
     },
     _info_plist: (),
     pattern: Pattern::Brownfield(std::marker::PhantomData),
-    #[cfg(shell_scope)]
-    shell_scope: ShellScopeConfig {
-      open: None,
-      scopes: HashMap::new(),
-    },
   }
 }
 

+ 3 - 3
core/tests/restart/src/main.rs

@@ -13,8 +13,8 @@ fn main() {
 
   println!(
     "{}",
-    tauri::api::process::current_binary(&Default::default())
-      .expect("tauri::api::process::current_binary could not resolve")
+    tauri::process::current_binary(&Default::default())
+      .expect("tauri::process::current_binary could not resolve")
       .display()
   );
 
@@ -22,7 +22,7 @@ fn main() {
     Some("restart") => {
       let mut env = Env::default();
       env.args.clear();
-      tauri::api::process::restart(&env)
+      tauri::process::restart(&env)
     }
     Some(invalid) => panic!("only argument `restart` is allowed, {invalid} is invalid"),
     None => {}

+ 1 - 1
core/tests/restart/tests/restart.rs

@@ -45,7 +45,7 @@ fn symlink_runner(create_symlinks: impl Fn(&Path) -> io::Result<Symlink>) -> Res
     // run the command from the symlink, so that we can test if restart resolves it correctly
     let mut cmd = Command::new(link);
 
-    // add the restart parameter so that the invocation will call tauri::api::process::restart
+    // add the restart parameter so that the invocation will call tauri::process::restart
     cmd.arg("restart");
 
     let output = cmd.output()?;

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
examples/api/dist/assets/index.css


Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
examples/api/dist/assets/index.js


+ 0 - 8
examples/api/src/App.svelte

@@ -1,6 +1,5 @@
 <script>
   import { writable } from 'svelte/store'
-  import { open } from '@tauri-apps/api/shell'
   import { appWindow, getCurrent } from '@tauri-apps/api/window'
   import * as os from '@tauri-apps/api/os'
 
@@ -8,7 +7,6 @@
   import Cli from './views/Cli.svelte'
   import Communication from './views/Communication.svelte'
   import Window from './views/Window.svelte'
-  import Shell from './views/Shell.svelte'
   import Updater from './views/Updater.svelte'
   import WebRTC from './views/WebRTC.svelte'
   import App from './views/App.svelte'
@@ -49,11 +47,6 @@
       component: Window,
       icon: 'i-codicon-window'
     },
-    {
-      label: 'Shell',
-      component: Shell,
-      icon: 'i-codicon-terminal-bash'
-    },
     !isMobile && {
       label: 'Updater',
       component: Updater,
@@ -302,7 +295,6 @@
       bg-darkPrimaryLighter transition-colors-250 overflow-hidden grid select-none px-2"
   >
     <img
-      on:click={() => open('https://tauri.app/')}
       class="self-center p-7 cursor-pointer"
       src="tauri_logo.png"
       alt="Tauri logo"

+ 0 - 100
examples/api/src/views/Shell.svelte

@@ -1,100 +0,0 @@
-<script>
-  import { Command } from '@tauri-apps/api/shell'
-
-  const windows = navigator.userAgent.includes('Windows')
-  let cmd = windows ? 'cmd' : 'sh'
-  let args = windows ? ['/C'] : ['-c']
-
-  export let onMessage
-
-  let script = 'echo "hello world"'
-  let cwd = null
-  let env = 'SOMETHING=value ANOTHER=2'
-  let encoding = ''
-  let stdin = ''
-  let child
-
-  function _getEnv() {
-    return env.split(' ').reduce((env, clause) => {
-      let [key, value] = clause.split('=')
-      return {
-        ...env,
-        [key]: value
-      }
-    }, {})
-  }
-
-  function spawn() {
-    child = null
-    const command = Command.create(cmd, [...args, script], {
-      cwd: cwd || null,
-      env: _getEnv(),
-      encoding: encoding || undefined,
-    })
-
-    command.on('close', (data) => {
-      onMessage(
-        `command finished with code ${data.code} and signal ${data.signal}`
-      )
-      child = null
-    })
-    command.on('error', (error) => onMessage(`command error: "${error}"`))
-
-    command.stdout.on('data', (line) => onMessage(`command stdout: "${line}"`))
-    command.stderr.on('data', (line) => onMessage(`command stderr: "${line}"`))
-
-    command
-      .spawn()
-      .then((c) => {
-        child = c
-      })
-      .catch(onMessage)
-  }
-
-  function kill() {
-    child
-      .kill()
-      .then(() => onMessage('killed child process'))
-      .catch(onMessage)
-  }
-
-  function writeToStdin() {
-    child.write(stdin).catch(onMessage)
-  }
-</script>
-
-<div class="flex flex-col childre:grow gap-1">
-  <div class="flex items-center gap-1">
-    Script:
-    <input class="grow input" bind:value={script} />
-  </div>
-  <div class="flex items-center gap-1">
-    Encoding:
-    <input class="grow input" bind:value={encoding} />
-  </div>
-  <div class="flex items-center gap-1">
-    Working directory:
-    <input
-      class="grow input"
-      bind:value={cwd}
-      placeholder="Working directory"
-    />
-  </div>
-  <div class="flex items-center gap-1">
-    Arguments:
-    <input
-      class="grow input"
-      bind:value={env}
-      placeholder="Environment variables"
-    />
-  </div>
-  <div class="flex children:grow gap-1">
-    <button class="btn" on:click={spawn}>Run</button>
-    <button class="btn" on:click={kill}>Kill</button>
-  </div>
-  {#if child}
-    <br />
-    <input class="input" placeholder="write to stdin" bind:value={stdin} />
-    <button class="btn" on:click={writeToStdin}>Write</button>
-  {/if}
-</div>

+ 0 - 9
examples/api/src/views/Window.svelte

@@ -7,7 +7,6 @@
     PhysicalSize,
     PhysicalPosition
   } from '@tauri-apps/api/window'
-  import { open } from '@tauri-apps/api/shell'
 
   let selectedWindow = appWindow.label
   const windowMap = {
@@ -90,10 +89,6 @@
   let cursorIgnoreEvents = false
   let windowTitle = 'Awesome Tauri Example!'
 
-  function openUrl() {
-    open(urlValue)
-  }
-
   function setTitle_() {
     windowMap[selectedWindow].setTitle(windowTitle)
   }
@@ -438,10 +433,6 @@
         <input class="input grow" id="title" bind:value={windowTitle} />
         <button class="btn" type="submit">Set title</button>
       </form>
-      <form class="flex gap-1" on:submit|preventDefault={openUrl}>
-        <input class="input grow" id="url" bind:value={urlValue} />
-        <button class="btn" id="open-url"> Open URL </button>
-      </form>
     </div>
   {/if}
 </div>

+ 1 - 1
examples/resources/src-tauri/src/main.rs

@@ -6,7 +6,7 @@
 
 fn main() {
   use tauri::{
-    api::process::{Command, CommandEvent},
+    process::{Command, CommandEvent},
     Manager,
   };
 

+ 1 - 1
examples/sidecar/src-tauri/src/main.rs

@@ -5,7 +5,7 @@
 #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
 
 use tauri::{
-  api::process::{Command, CommandEvent},
+  process::{Command, CommandEvent},
   Manager,
 };
 

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
tooling/api/docs/js-api.json


+ 1 - 2
tooling/api/src/index.ts

@@ -17,7 +17,6 @@ import * as app from './app'
 import * as event from './event'
 import * as path from './path'
 import * as process from './process'
-import * as shell from './shell'
 import * as tauri from './tauri'
 import * as updater from './updater'
 import * as window from './window'
@@ -26,4 +25,4 @@ import * as os from './os'
 /** @ignore */
 const invoke = tauri.invoke
 
-export { invoke, app, event, path, process, shell, tauri, updater, window, os }
+export { invoke, app, event, path, process, tauri, updater, window, os }

+ 0 - 675
tooling/api/src/shell.ts

@@ -1,675 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-/**
- * Access the system shell.
- * Allows you to spawn child processes and manage files and URLs using their default application.
- *
- * This package is also accessible with `window.__TAURI__.shell` when [`build.withGlobalTauri`](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in `tauri.conf.json` is set to `true`.
- *
- * The APIs must be added to [`tauri.allowlist.shell`](https://tauri.app/v1/api/config/#allowlistconfig.shell) in `tauri.conf.json`:
- * ```json
- * {
- *   "tauri": {
- *     "allowlist": {
- *       "shell": {
- *         "all": true, // enable all shell APIs
- *         "execute": true, // enable process spawn APIs
- *         "sidecar": true, // enable spawning sidecars
- *         "open": true // enable opening files/URLs using the default program
- *       }
- *     }
- *   }
- * }
- * ```
- * It is recommended to allowlist only the APIs you use for optimal bundle size and security.
- *
- * ## Security
- *
- * This API has a scope configuration that forces you to restrict the programs and arguments that can be used.
- *
- * ### Restricting access to the {@link open | `open`} API
- *
- * On the allowlist, `open: true` means that the {@link open} API can be used with any URL,
- * as the argument is validated with the `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+` regex.
- * You can change that regex by changing the boolean value to a string, e.g. `open: ^https://github.com/`.
- *
- * ### Restricting access to the {@link Command | `Command`} APIs
- *
- * The `shell` allowlist object has a `scope` field that defines an array of CLIs that can be used.
- * Each CLI is a configuration object `{ name: string, cmd: string, sidecar?: bool, args?: boolean | Arg[] }`.
- *
- * - `name`: the unique identifier of the command, passed to the {@link Command.create | Command.create function}.
- * If it's a sidecar, this must be the value defined on `tauri.conf.json > tauri > bundle > externalBin`.
- * - `cmd`: the program that is executed on this configuration. If it's a sidecar, this value is ignored.
- * - `sidecar`: whether the object configures a sidecar or a system program.
- * - `args`: the arguments that can be passed to the program. By default no arguments are allowed.
- *   - `true` means that any argument list is allowed.
- *   - `false` means that no arguments are allowed.
- *   - otherwise an array can be configured. Each item is either a string representing the fixed argument value
- *     or a `{ validator: string }` that defines a regex validating the argument value.
- *
- * #### Example scope configuration
- *
- * CLI: `git commit -m "the commit message"`
- *
- * Configuration:
- * ```json
- * {
- *   "scope": [
- *     {
- *       "name": "run-git-commit",
- *       "cmd": "git",
- *       "args": ["commit", "-m", { "validator": "\\S+" }]
- *     }
- *   ]
- * }
- * ```
- * Usage:
- * ```typescript
- * import { Command } from '@tauri-apps/api/shell'
- * Command.create('run-git-commit', ['commit', '-m', 'the commit message'])
- * ```
- *
- * Trying to execute any API with a program not configured on the scope results in a promise rejection due to denied access.
- *
- * @module
- */
-
-import { invokeTauriCommand } from './helpers/tauri'
-import { transformCallback } from './tauri'
-
-/**
- * @since 1.0.0
- */
-interface SpawnOptions {
-  /** Current working directory. */
-  cwd?: string
-  /** Environment variables. set to `null` to clear the process env. */
-  env?: Record<string, string>
-  /**
-   * Character encoding for stdout/stderr
-   *
-   * @since 1.1.0
-   *  */
-  encoding?: string
-}
-
-/** @ignore */
-interface InternalSpawnOptions extends SpawnOptions {
-  sidecar?: boolean
-}
-
-/**
- * @since 1.0.0
- */
-interface ChildProcess<O extends IOPayload> {
-  /** Exit code of the process. `null` if the process was terminated by a signal on Unix. */
-  code: number | null
-  /** If the process was terminated by a signal, represents that signal. */
-  signal: number | null
-  /** The data that the process wrote to `stdout`. */
-  stdout: O
-  /** The data that the process wrote to `stderr`. */
-  stderr: O
-}
-
-/**
- * Spawns a process.
- *
- * @ignore
- * @param program The name of the scoped command.
- * @param onEvent Event handler.
- * @param args Program arguments.
- * @param options Configuration for the process spawn.
- * @returns A promise resolving to the process id.
- */
-async function execute<O extends IOPayload>(
-  onEvent: (event: CommandEvent<O>) => void,
-  program: string,
-  args: string | string[] = [],
-  options?: InternalSpawnOptions
-): Promise<number> {
-  if (typeof args === 'object') {
-    Object.freeze(args)
-  }
-
-  return invokeTauriCommand<number>({
-    __tauriModule: 'Shell',
-    message: {
-      cmd: 'execute',
-      program,
-      args,
-      options,
-      onEventFn: transformCallback(onEvent)
-    }
-  })
-}
-
-/**
- * @since 1.0.0
- */
-class EventEmitter<E extends Record<string, any>> {
-  /** @ignore */
-  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
-  private eventListeners: Record<keyof E, Array<(arg: any) => void>> =
-    Object.create(null)
-
-  /**
-   * Alias for `emitter.on(eventName, listener)`.
-   *
-   * @since 1.1.0
-   */
-  addListener<N extends keyof E>(
-    eventName: N,
-    listener: (arg: E[typeof eventName]) => void
-  ): this {
-    return this.on(eventName, listener)
-  }
-
-  /**
-   * Alias for `emitter.off(eventName, listener)`.
-   *
-   * @since 1.1.0
-   */
-  removeListener<N extends keyof E>(
-    eventName: N,
-    listener: (arg: E[typeof eventName]) => void
-  ): this {
-    return this.off(eventName, listener)
-  }
-
-  /**
-   * Adds the `listener` function to the end of the listeners array for the
-   * event named `eventName`. No checks are made to see if the `listener` has
-   * already been added. Multiple calls passing the same combination of `eventName`and `listener` will result in the `listener` being added, and called, multiple
-   * times.
-   *
-   * Returns a reference to the `EventEmitter`, so that calls can be chained.
-   *
-   * @since 1.0.0
-   */
-  on<N extends keyof E>(
-    eventName: N,
-    listener: (arg: E[typeof eventName]) => void
-  ): this {
-    if (eventName in this.eventListeners) {
-      // eslint-disable-next-line security/detect-object-injection
-      this.eventListeners[eventName].push(listener)
-    } else {
-      // eslint-disable-next-line security/detect-object-injection
-      this.eventListeners[eventName] = [listener]
-    }
-    return this
-  }
-
-  /**
-   * Adds a **one-time**`listener` function for the event named `eventName`. The
-   * next time `eventName` is triggered, this listener is removed and then invoked.
-   *
-   * Returns a reference to the `EventEmitter`, so that calls can be chained.
-   *
-   * @since 1.1.0
-   */
-  once<N extends keyof E>(
-    eventName: N,
-    listener: (arg: E[typeof eventName]) => void
-  ): this {
-    const wrapper = (arg: E[typeof eventName]): void => {
-      this.removeListener(eventName, wrapper)
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
-      listener(arg)
-    }
-    return this.addListener(eventName, wrapper)
-  }
-
-  /**
-   * Removes the all specified listener from the listener array for the event eventName
-   * Returns a reference to the `EventEmitter`, so that calls can be chained.
-   *
-   * @since 1.1.0
-   */
-  off<N extends keyof E>(
-    eventName: N,
-    listener: (arg: E[typeof eventName]) => void
-  ): this {
-    if (eventName in this.eventListeners) {
-      // eslint-disable-next-line security/detect-object-injection
-      this.eventListeners[eventName] = this.eventListeners[eventName].filter(
-        (l) => l !== listener
-      )
-    }
-    return this
-  }
-
-  /**
-   * Removes all listeners, or those of the specified eventName.
-   *
-   * Returns a reference to the `EventEmitter`, so that calls can be chained.
-   *
-   * @since 1.1.0
-   */
-  removeAllListeners<N extends keyof E>(event?: N): this {
-    if (event) {
-      // eslint-disable-next-line @typescript-eslint/no-dynamic-delete,security/detect-object-injection
-      delete this.eventListeners[event]
-    } else {
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
-      this.eventListeners = Object.create(null)
-    }
-    return this
-  }
-
-  /**
-   * @ignore
-   * Synchronously calls each of the listeners registered for the event named`eventName`, in the order they were registered, passing the supplied arguments
-   * to each.
-   *
-   * @returns `true` if the event had listeners, `false` otherwise.
-   */
-  emit<N extends keyof E>(eventName: N, arg: E[typeof eventName]): boolean {
-    if (eventName in this.eventListeners) {
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,security/detect-object-injection
-      const listeners = this.eventListeners[eventName]
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
-      for (const listener of listeners) listener(arg)
-      return true
-    }
-    return false
-  }
-
-  /**
-   * Returns the number of listeners listening to the event named `eventName`.
-   *
-   * @since 1.1.0
-   */
-  listenerCount<N extends keyof E>(eventName: N): number {
-    if (eventName in this.eventListeners)
-      // eslint-disable-next-line security/detect-object-injection
-      return this.eventListeners[eventName].length
-    return 0
-  }
-
-  /**
-   * Adds the `listener` function to the _beginning_ of the listeners array for the
-   * event named `eventName`. No checks are made to see if the `listener` has
-   * already been added. Multiple calls passing the same combination of `eventName`and `listener` will result in the `listener` being added, and called, multiple
-   * times.
-   *
-   * Returns a reference to the `EventEmitter`, so that calls can be chained.
-   *
-   * @since 1.1.0
-   */
-  prependListener<N extends keyof E>(
-    eventName: N,
-    listener: (arg: E[typeof eventName]) => void
-  ): this {
-    if (eventName in this.eventListeners) {
-      // eslint-disable-next-line security/detect-object-injection
-      this.eventListeners[eventName].unshift(listener)
-    } else {
-      // eslint-disable-next-line security/detect-object-injection
-      this.eventListeners[eventName] = [listener]
-    }
-    return this
-  }
-
-  /**
-   * Adds a **one-time**`listener` function for the event named `eventName` to the_beginning_ of the listeners array. The next time `eventName` is triggered, this
-   * listener is removed, and then invoked.
-   *
-   * Returns a reference to the `EventEmitter`, so that calls can be chained.
-   *
-   * @since 1.1.0
-   */
-  prependOnceListener<N extends keyof E>(
-    eventName: N,
-    listener: (arg: E[typeof eventName]) => void
-  ): this {
-    const wrapper = (arg: any): void => {
-      this.removeListener(eventName, wrapper)
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
-      listener(arg)
-    }
-    return this.prependListener(eventName, wrapper)
-  }
-}
-
-/**
- * @since 1.1.0
- */
-class Child {
-  /** The child process `pid`. */
-  pid: number
-
-  constructor(pid: number) {
-    this.pid = pid
-  }
-
-  /**
-   * Writes `data` to the `stdin`.
-   *
-   * @param data The message to write, either a string or a byte array.
-   * @example
-   * ```typescript
-   * import { Command } from '@tauri-apps/api/shell';
-   * const command = Command.create('node');
-   * const child = await command.spawn();
-   * await child.write('message');
-   * await child.write([0, 1, 2, 3, 4, 5]);
-   * ```
-   *
-   * @returns A promise indicating the success or failure of the operation.
-   */
-  async write(data: IOPayload): Promise<void> {
-    return invokeTauriCommand({
-      __tauriModule: 'Shell',
-      message: {
-        cmd: 'stdinWrite',
-        pid: this.pid,
-        // correctly serialize Uint8Arrays
-        buffer: typeof data === 'string' ? data : Array.from(data)
-      }
-    })
-  }
-
-  /**
-   * Kills the child process.
-   *
-   * @returns A promise indicating the success or failure of the operation.
-   */
-  async kill(): Promise<void> {
-    return invokeTauriCommand({
-      __tauriModule: 'Shell',
-      message: {
-        cmd: 'killChild',
-        pid: this.pid
-      }
-    })
-  }
-}
-
-interface CommandEvents {
-  close: TerminatedPayload
-  error: string
-}
-
-interface OutputEvents<O extends IOPayload> {
-  data: O
-}
-
-/**
- * The entry point for spawning child processes.
- * It emits the `close` and `error` events.
- * @example
- * ```typescript
- * import { Command } from '@tauri-apps/api/shell';
- * const command = Command.create('node');
- * command.on('close', data => {
- *   console.log(`command finished with code ${data.code} and signal ${data.signal}`)
- * });
- * command.on('error', error => console.error(`command error: "${error}"`));
- * command.stdout.on('data', line => console.log(`command stdout: "${line}"`));
- * command.stderr.on('data', line => console.log(`command stderr: "${line}"`));
- *
- * const child = await command.spawn();
- * console.log('pid:', child.pid);
- * ```
- *
- * @since 1.1.0
- *
- */
-class Command<O extends IOPayload> extends EventEmitter<CommandEvents> {
-  /** @ignore Program to execute. */
-  private readonly program: string
-  /** @ignore Program arguments */
-  private readonly args: string[]
-  /** @ignore Spawn options. */
-  private readonly options: InternalSpawnOptions
-  /** Event emitter for the `stdout`. Emits the `data` event. */
-  readonly stdout = new EventEmitter<OutputEvents<O>>()
-  /** Event emitter for the `stderr`. Emits the `data` event. */
-  readonly stderr = new EventEmitter<OutputEvents<O>>()
-
-  /**
-   * @ignore
-   * Creates a new `Command` instance.
-   *
-   * @param program The program name to execute.
-   * It must be configured on `tauri.conf.json > tauri > allowlist > shell > scope`.
-   * @param args Program arguments.
-   * @param options Spawn options.
-   */
-  private constructor(
-    program: string,
-    args: string | string[] = [],
-    options?: SpawnOptions
-  ) {
-    super()
-    this.program = program
-    this.args = typeof args === 'string' ? [args] : args
-    this.options = options ?? {}
-  }
-
-  static create(program: string, args?: string | string[]): Command<string>
-  static create(
-    program: string,
-    args?: string | string[],
-    options?: SpawnOptions & { encoding: 'raw' }
-  ): Command<Uint8Array>
-  static create(
-    program: string,
-    args?: string | string[],
-    options?: SpawnOptions
-  ): Command<string>
-
-  /**
-   * Creates a command to execute the given program.
-   * @example
-   * ```typescript
-   * import { Command } from '@tauri-apps/api/shell';
-   * const command = Command.create('my-app', ['run', 'tauri']);
-   * const output = await command.execute();
-   * ```
-   *
-   * @param program The program to execute.
-   * It must be configured on `tauri.conf.json > tauri > allowlist > shell > scope`.
-   */
-  static create<O extends IOPayload>(
-    program: string,
-    args: string | string[] = [],
-    options?: SpawnOptions
-  ): Command<O> {
-    return new Command(program, args, options)
-  }
-
-  static sidecar(program: string, args?: string | string[]): Command<string>
-  static sidecar(
-    program: string,
-    args?: string | string[],
-    options?: SpawnOptions & { encoding: 'raw' }
-  ): Command<Uint8Array>
-  static sidecar(
-    program: string,
-    args?: string | string[],
-    options?: SpawnOptions
-  ): Command<string>
-
-  /**
-   * Creates a command to execute the given sidecar program.
-   * @example
-   * ```typescript
-   * import { Command } from '@tauri-apps/api/shell';
-   * const command = Command.sidecar('my-sidecar');
-   * const output = await command.execute();
-   * ```
-   *
-   * @param program The program to execute.
-   * It must be configured on `tauri.conf.json > tauri > allowlist > shell > scope`.
-   */
-  static sidecar<O extends IOPayload>(
-    program: string,
-    args: string | string[] = [],
-    options?: SpawnOptions
-  ): Command<O> {
-    const instance = new Command<O>(program, args, options)
-    instance.options.sidecar = true
-    return instance
-  }
-
-  /**
-   * Executes the command as a child process, returning a handle to it.
-   *
-   * @returns A promise resolving to the child process handle.
-   */
-  async spawn(): Promise<Child> {
-    return execute<O>(
-      (event) => {
-        switch (event.event) {
-          case 'Error':
-            this.emit('error', event.payload)
-            break
-          case 'Terminated':
-            this.emit('close', event.payload)
-            break
-          case 'Stdout':
-            this.stdout.emit('data', event.payload)
-            break
-          case 'Stderr':
-            this.stderr.emit('data', event.payload)
-            break
-        }
-      },
-      this.program,
-      this.args,
-      this.options
-    ).then((pid) => new Child(pid))
-  }
-
-  /**
-   * Executes the command as a child process, waiting for it to finish and collecting all of its output.
-   * @example
-   * ```typescript
-   * import { Command } from '@tauri-apps/api/shell';
-   * const output = await Command.create('echo', 'message').execute();
-   * assert(output.code === 0);
-   * assert(output.signal === null);
-   * assert(output.stdout === 'message');
-   * assert(output.stderr === '');
-   * ```
-   *
-   * @returns A promise resolving to the child process output.
-   */
-  async execute(): Promise<ChildProcess<O>> {
-    return new Promise((resolve, reject) => {
-      this.on('error', reject)
-
-      const stdout: O[] = []
-      const stderr: O[] = []
-      this.stdout.on('data', (line: O) => {
-        stdout.push(line)
-      })
-      this.stderr.on('data', (line: O) => {
-        stderr.push(line)
-      })
-
-      this.on('close', (payload: TerminatedPayload) => {
-        resolve({
-          code: payload.code,
-          signal: payload.signal,
-          stdout: this.collectOutput(stdout) as O,
-          stderr: this.collectOutput(stderr) as O
-        })
-      })
-
-      this.spawn().catch(reject)
-    })
-  }
-
-  /** @ignore */
-  private collectOutput(events: O[]): string | Uint8Array {
-    if (this.options.encoding === 'raw') {
-      return events.reduce<Uint8Array>((p, c) => {
-        return new Uint8Array([...p, ...(c as Uint8Array), 10])
-      }, new Uint8Array())
-    } else {
-      return events.join('\n')
-    }
-  }
-}
-
-/**
- * Describes the event message received from the command.
- */
-interface Event<T, V> {
-  event: T
-  payload: V
-}
-
-/**
- * Payload for the `Terminated` command event.
- */
-interface TerminatedPayload {
-  /** Exit code of the process. `null` if the process was terminated by a signal on Unix. */
-  code: number | null
-  /** If the process was terminated by a signal, represents that signal. */
-  signal: number | null
-}
-
-/** Event payload type */
-type IOPayload = string | Uint8Array
-
-/** Events emitted by the child process. */
-type CommandEvent<O extends IOPayload> =
-  | Event<'Stdout', O>
-  | Event<'Stderr', O>
-  | Event<'Terminated', TerminatedPayload>
-  | Event<'Error', string>
-
-/**
- * Opens a path or URL with the system's default app,
- * or the one specified with `openWith`.
- *
- * The `openWith` value must be one of `firefox`, `google chrome`, `chromium` `safari`,
- * `open`, `start`, `xdg-open`, `gio`, `gnome-open`, `kde-open` or `wslview`.
- *
- * @example
- * ```typescript
- * import { open } from '@tauri-apps/api/shell';
- * // opens the given URL on the default browser:
- * await open('https://github.com/tauri-apps/tauri');
- * // opens the given URL using `firefox`:
- * await open('https://github.com/tauri-apps/tauri', 'firefox');
- * // opens a file using the default program:
- * await open('/path/to/file');
- * ```
- *
- * @param path The path or URL to open.
- * This value is matched against the string regex defined on `tauri.conf.json > tauri > allowlist > shell > open`,
- * which defaults to `^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+`.
- * @param openWith The app to open the file or URL with.
- * Defaults to the system default application for the specified path type.
- *
- * @since 1.0.0
- */
-async function open(path: string, openWith?: string): Promise<void> {
-  return invokeTauriCommand({
-    __tauriModule: 'Shell',
-    message: {
-      cmd: 'open',
-      path,
-      with: openWith
-    }
-  })
-}
-
-export { Command, Child, EventEmitter, open }
-export type {
-  IOPayload,
-  CommandEvents,
-  TerminatedPayload,
-  OutputEvents,
-  ChildProcess,
-  SpawnOptions
-}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.