diff --git a/backend/src/procedure/js_scripts.rs b/backend/src/procedure/js_scripts.rs index e0c382710..2605053ae 100644 --- a/backend/src/procedure/js_scripts.rs +++ b/backend/src/procedure/js_scripts.rs @@ -1,4 +1,7 @@ -use std::{path::{PathBuf, Path}, time::Duration}; +use std::{ + path::{Path, PathBuf}, + time::Duration, +}; use models::VolumeId; use serde::{Deserialize, Serialize}; @@ -12,20 +15,31 @@ use js_engine::{JsExecutionEnvironment, PathForVolumeId}; use super::ProcedureName; -pub use js_engine::{JsError}; +pub use js_engine::JsError; +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] + +enum ErrorValue { + Error(String), + Result(serde_json::Value), +} impl PathForVolumeId for Volumes { - fn path_for(&self, data_dir: &Path, package_id: &PackageId, version: &Version, volume_id: &VolumeId) -> Option { - + fn path_for( + &self, + data_dir: &Path, + package_id: &PackageId, + version: &Version, + volume_id: &VolumeId, + ) -> Option { let volume = self.get(volume_id)?; Some(volume.path_for(data_dir, package_id, version, volume_id)) } - fn readonly(&self,volume_id: &VolumeId) -> bool { + fn readonly(&self, volume_id: &VolumeId) -> bool { self.get(volume_id).map(|x| x.readonly()).unwrap_or(false) } - } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -57,12 +71,13 @@ impl JsProcedure { ) .await? .run_action(name, input); - let output: O = match timeout { + let output: ErrorValue = match timeout { Some(timeout_duration) => tokio::time::timeout(timeout_duration, running_action) .await .map_err(|_| (JsError::Timeout, "Timed out. Retrying soon...".to_owned()))??, None => running_action.await?, }; + let output: O = unwrap_known_error(output)?; Ok(output) } .await @@ -90,12 +105,13 @@ impl JsProcedure { .await? .read_only_effects() .run_action(name, input); - let output: O = match timeout { + let output: ErrorValue = match timeout { Some(timeout_duration) => tokio::time::timeout(timeout_duration, running_action) .await .map_err(|_| (JsError::Timeout, "Timed out. Retrying soon...".to_owned()))??, None => running_action.await?, }; + let output: O = unwrap_known_error(output)?; Ok(output) } .await @@ -103,6 +119,28 @@ impl JsProcedure { } } +fn unwrap_known_error Deserialize<'de>>( + error_value: ErrorValue, +) -> Result { + match error_value { + ErrorValue::Error(error) => Err((JsError::Javascript, error)), + ErrorValue::Result(ref value) => match serde_json::from_value(value.clone()) { + Ok(a) => Ok(a), + Err(err) => { + tracing::error!("{}", err); + tracing::debug!("{:?}", err); + Err(( + JsError::BoundryLayerSerDe, + format!( + "Couldn't convert output = {:#?} to the correct type", + serde_json::to_string_pretty(&error_value).unwrap_or_default() + ), + )) + } + }, + } +} + #[tokio::test] async fn js_action_execute() { let js_action = JsProcedure {}; @@ -157,3 +195,47 @@ async fn js_action_execute() { ) .unwrap(); } + +#[tokio::test] +async fn js_action_execute_error() { + let js_action = JsProcedure {}; + let path: PathBuf = "test/js_action_execute/" + .parse::() + .unwrap() + .canonicalize() + .unwrap(); + let package_id = "test-package".parse().unwrap(); + let package_version: Version = "0.3.0.3".parse().unwrap(); + let name = ProcedureName::SetConfig; + let volumes: Volumes = serde_json::from_value(serde_json::json!({ + "main": { + "type": "data" + }, + "compat": { + "type": "assets" + }, + "filebrowser" :{ + "package-id": "filebrowser", + "path": "data", + "readonly": true, + "type": "pointer", + "volume-id": "main", + } + })) + .unwrap(); + let input: Option = None; + let timeout = Some(Duration::from_secs(10)); + let output: Result = js_action + .execute( + &path, + &package_id, + &package_version, + name, + &volumes, + input, + timeout, + ) + .await + .unwrap(); + assert_eq!("Err((2, \"Not setup\"))", &format!("{:?}", output)); +} diff --git a/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/embassy.d.ts b/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/embassy.d.ts deleted file mode 100644 index 27e837151..000000000 --- a/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/embassy.d.ts +++ /dev/null @@ -1,7 +0,0 @@ - -import {Effects, Config, ConfigRes, SetResult, Properties} from './types'; - - -export function properties(effects: Effects): Properties | Promise; -export function getConfig(effects: Effects): ConfigRes | Promise; -export function setConfig(effects: Effects, input: Config): SetResult | Promise; \ No newline at end of file diff --git a/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/embassy.js b/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/embassy.js index 1558afcb8..d25ca5f77 100644 --- a/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/embassy.js +++ b/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/embassy.js @@ -1,638 +1,684 @@ -// @ts-check - export function properties() { - return "Anything here" + return "Anything here"; } -/** - * - * @param {import('./types').Effects} effects - * @returns {Promise} - */ export async function getConfig(effects) { - try{ + try { await effects.writeFile({ path: "../test.log", toWrite: "This is a test", - volumeId: 'main', + volumeId: "main", }); - throw new Error("Expecting that the ../test.log should not be a valid path since we are breaking out of the parent") - } catch(e) {} - try{ + throw new Error( + "Expecting that the ../test.log should not be a valid path since we are breaking out of the parent" + ); + } catch (e) {} + try { await effects.writeFile({ path: "./hack_back/broken.log", toWrite: "This is a test", - volumeId: 'main', + volumeId: "main", }); - throw new Error("Expecting that using a symlink to break out of parent still fails for writing") - } catch(e) {} - try{ + throw new Error( + "Expecting that using a symlink to break out of parent still fails for writing" + ); + } catch (e) {} + try { await effects.createDir({ path: "./hack_back/broken_dir", - volumeId: 'main', + volumeId: "main", }); - throw new Error("Expecting that using a symlink to break out of parent still fails for writing dir") - } catch(e) {} - try{ + throw new Error( + "Expecting that using a symlink to break out of parent still fails for writing dir" + ); + } catch (e) {} + try { await effects.readFile({ path: "./hack_back/data/bad_file.txt", - volumeId: 'main', + volumeId: "main", }); - throw new Error("Expecting that using a symlink to break out of parent still fails for reading") - } catch(e) {} + throw new Error( + "Expecting that using a symlink to break out of parent still fails for reading" + ); + } catch (e) {} // Testing dir, create + delete - await effects.createDir({ - path: "./testing", - volumeId: 'main',}); - await effects.writeJsonFile({ - path: "./testing/test2.log", - toWrite: {value: "This is a test"}, - volumeId: 'main', - }); + await effects.createDir({ + path: "./testing", + volumeId: "main", + }); + await effects.writeJsonFile({ + path: "./testing/test2.log", + toWrite: { value: "This is a test" }, + volumeId: "main", + }); - (await effects.readJsonFile({ + ( + await effects.readJsonFile({ path: "./testing/test2.log", - volumeId: 'main', - // @ts-ignore - })).value; - - await effects.removeFile({ - path: "./testing/test2.log", - volumeId: 'main', + volumeId: "main", + // @ts-ignore }) - await effects.removeDir({ - path: "./testing", - volumeId: 'main',}); + ).value; + await effects.removeFile({ + path: "./testing/test2.log", + volumeId: "main", + }); + await effects.removeDir({ + path: "./testing", + volumeId: "main", + }); - // Testing reading + writing - await effects.writeFile({ + // Testing reading + writing + await effects.writeFile({ + path: "./test.log", + toWrite: "This is a test", + volumeId: "main", + }); + + effects.debug( + `Read results are ${await effects.readFile({ path: "./test.log", - toWrite: "This is a test", - volumeId: 'main', - }); - - effects.debug(`Read results are ${await effects.readFile({ - path: "./test.log", - volumeId: 'main', - })}`) - // Testing loging - effects.trace('trace') - effects.debug('debug') - effects.warn('warn') - effects.error('error') - effects.info('info') - return { - spec: { - "control-tor-address": { - "name": "Control Tor Address", - "description": "The Tor address for the control interface.", - "type": "pointer", - "subtype": "package", - "package-id": "lnd", - "target": "tor-address", - "interface": "control" + volumeId: "main", + })}` + ); + // Testing loging + effects.trace("trace"); + effects.debug("debug"); + effects.warn("warn"); + effects.error("error"); + effects.info("info"); + return { + result: { + spec: { + "control-tor-address": { + name: "Control Tor Address", + description: "The Tor address for the control interface.", + type: "pointer", + subtype: "package", + "package-id": "lnd", + target: "tor-address", + interface: "control", + }, + "peer-tor-address": { + name: "Peer Tor Address", + description: "The Tor address for the peer interface.", + type: "pointer", + subtype: "package", + "package-id": "lnd", + target: "tor-address", + interface: "peer", + }, + "watchtower-tor-address": { + name: "Watchtower Tor Address", + description: "The Tor address for the watchtower interface.", + type: "pointer", + subtype: "package", + "package-id": "lnd", + target: "tor-address", + interface: "watchtower", + }, + alias: { + type: "string", + name: "Alias", + description: "The public, human-readable name of your Lightning node", + nullable: true, + pattern: ".{1,32}", + "pattern-description": + "Must be at least 1 character and no more than 32 characters", + }, + color: { + type: "string", + name: "Color", + description: "The public color dot of your Lightning node", + nullable: false, + pattern: "[0-9a-fA-F]{6}", + "pattern-description": + "Must be a valid 6 digit hexadecimal RGB value. The first two digits are red, middle two are green, and final two are\nblue\n", + default: { + charset: "a-f,0-9", + len: 6, + }, + }, + "accept-keysend": { + type: "boolean", + name: "Accept Keysend", + description: + "Allow others to send payments directly to your public key through keysend instead of having to get a new invoice\n", + default: false, + }, + "accept-amp": { + type: "boolean", + name: "Accept Spontaneous AMPs", + description: + "If enabled, spontaneous payments through AMP will be accepted. Payments to AMP\ninvoices will be accepted regardless of this setting.\n", + default: false, + }, + "reject-htlc": { + type: "boolean", + name: "Reject Routing Requests", + description: + "If true, LND will not forward any HTLCs that are meant as onward payments. This option will still allow LND to send\nHTLCs and receive HTLCs but lnd won't be used as a hop.\n", + default: false, + }, + "min-chan-size": { + type: "number", + name: "Minimum Channel Size", + description: + "The smallest channel size that we should accept. Incoming channels smaller than this will be rejected.\n", + nullable: true, + range: "[1,16777215]", + integral: true, + units: "satoshis", + }, + "max-chan-size": { + type: "number", + name: "Maximum Channel Size", + description: + "The largest channel size that we should accept. Incoming channels larger than this will be rejected.\nFor non-Wumbo channels this limit remains 16777215 satoshis by default as specified in BOLT-0002. For wumbo\nchannels this limit is 1,000,000,000 satoshis (10 BTC). Set this config option explicitly to restrict your maximum\nchannel size to better align with your risk tolerance. Don't forget to enable Wumbo channels under 'Advanced,' if desired.\n", + nullable: true, + range: "[1,1000000000]", + integral: true, + units: "satoshis", + }, + tor: { + type: "object", + name: "Tor Config", + spec: { + "use-tor-only": { + type: "boolean", + name: "Use Tor for all traffic", + description: + "Use the tor proxy even for connections that are reachable on clearnet. This will hide your node's public IP address, but will slow down your node's performance", + default: false, }, - "peer-tor-address": { - "name": "Peer Tor Address", - "description": "The Tor address for the peer interface.", - "type": "pointer", - "subtype": "package", - "package-id": "lnd", - "target": "tor-address", - "interface": "peer" + "stream-isolation": { + type: "boolean", + name: "Stream Isolation", + description: + "Enable Tor stream isolation by randomizing user credentials for each connection. With this mode active, each connection will use a new circuit. This means that multiple applications (other than lnd) using Tor won't be mixed in with lnd's traffic.\nThis option may not be used when 'Use Tor for all traffic' is disabled, since direct connections compromise source IP privacy by default.", + default: false, }, - "watchtower-tor-address": { - "name": "Watchtower Tor Address", - "description": "The Tor address for the watchtower interface.", - "type": "pointer", - "subtype": "package", - "package-id": "lnd", - "target": "tor-address", - "interface": "watchtower" + }, + }, + bitcoind: { + type: "union", + name: "Bitcoin Core", + description: + "The Bitcoin Core node to connect to:\n - internal: The Bitcoin Core and Proxy services installed to your Embassy\n - external: An unpruned Bitcoin Core node running on a different device\n", + tag: { + id: "type", + name: "Type", + "variant-names": { + internal: "Internal (Bitcoin Core)", + "internal-proxy": "Internal (Bitcoin Proxy)", + external: "External", }, - "alias": { - "type": "string", - "name": "Alias", - "description": "The public, human-readable name of your Lightning node", - "nullable": true, - "pattern": ".{1,32}", - "pattern-description": "Must be at least 1 character and no more than 32 characters" - }, - "color": { - "type": "string", - "name": "Color", - "description": "The public color dot of your Lightning node", - "nullable": false, - "pattern": "[0-9a-fA-F]{6}", - "pattern-description": "Must be a valid 6 digit hexadecimal RGB value. The first two digits are red, middle two are green, and final two are\nblue\n", - "default": { - "charset": "a-f,0-9", - "len": 6 - } - }, - "accept-keysend": { - "type": "boolean", - "name": "Accept Keysend", - "description": "Allow others to send payments directly to your public key through keysend instead of having to get a new invoice\n", - "default": false - }, - "accept-amp": { - "type": "boolean", - "name": "Accept Spontaneous AMPs", - "description": "If enabled, spontaneous payments through AMP will be accepted. Payments to AMP\ninvoices will be accepted regardless of this setting.\n", - "default": false - }, - "reject-htlc": { - "type": "boolean", - "name": "Reject Routing Requests", - "description": "If true, LND will not forward any HTLCs that are meant as onward payments. This option will still allow LND to send\nHTLCs and receive HTLCs but lnd won't be used as a hop.\n", - "default": false - }, - "min-chan-size": { - "type": "number", - "name": "Minimum Channel Size", - "description": "The smallest channel size that we should accept. Incoming channels smaller than this will be rejected.\n", - "nullable": true, - "range": "[1,16777215]", - "integral": true, - "units": "satoshis" - }, - "max-chan-size": { - "type": "number", - "name": "Maximum Channel Size", - "description": "The largest channel size that we should accept. Incoming channels larger than this will be rejected.\nFor non-Wumbo channels this limit remains 16777215 satoshis by default as specified in BOLT-0002. For wumbo\nchannels this limit is 1,000,000,000 satoshis (10 BTC). Set this config option explicitly to restrict your maximum\nchannel size to better align with your risk tolerance. Don't forget to enable Wumbo channels under 'Advanced,' if desired.\n", - "nullable": true, - "range": "[1,1000000000]", - "integral": true, - "units": "satoshis" - }, - "tor": { - "type": "object", - "name": "Tor Config", - "spec": { - "use-tor-only": { - "type": "boolean", - "name": "Use Tor for all traffic", - "description": "Use the tor proxy even for connections that are reachable on clearnet. This will hide your node's public IP address, but will slow down your node's performance", - "default": false - }, - "stream-isolation": { - "type": "boolean", - "name": "Stream Isolation", - "description": "Enable Tor stream isolation by randomizing user credentials for each connection. With this mode active, each connection will use a new circuit. This means that multiple applications (other than lnd) using Tor won't be mixed in with lnd's traffic.\nThis option may not be used when 'Use Tor for all traffic' is disabled, since direct connections compromise source IP privacy by default.", - "default": false - } - } - }, - "bitcoind": { - "type": "union", - "name": "Bitcoin Core", - "description": "The Bitcoin Core node to connect to:\n - internal: The Bitcoin Core and Proxy services installed to your Embassy\n - external: An unpruned Bitcoin Core node running on a different device\n", - "tag": { - "id": "type", - "name": "Type", - "variant-names": { - "internal": "Internal (Bitcoin Core)", - "internal-proxy": "Internal (Bitcoin Proxy)", - "external": "External" - }, - "description": "The Bitcoin Core node to connect to:\n - internal: The Bitcoin Core and Proxy services installed to your Embassy\n - external: An unpruned Bitcoin Core node running on a different device\n" + description: + "The Bitcoin Core node to connect to:\n - internal: The Bitcoin Core and Proxy services installed to your Embassy\n - external: An unpruned Bitcoin Core node running on a different device\n", + }, + default: "internal", + variants: { + internal: { + user: { + type: "pointer", + name: "RPC Username", + description: "The username for Bitcoin Core's RPC interface", + subtype: "package", + "package-id": "bitcoind", + target: "config", + multi: false, + selector: "$.rpc.username", + }, + password: { + type: "pointer", + name: "RPC Password", + description: "The password for Bitcoin Core's RPC interface", + subtype: "package", + "package-id": "bitcoind", + target: "config", + multi: false, + selector: "$.rpc.password", }, - "default": "internal", - "variants": { - "internal": { - "user": { - "type": "pointer", - "name": "RPC Username", - "description": "The username for Bitcoin Core's RPC interface", - "subtype": "package", - "package-id": "bitcoind", - "target": "config", - "multi": false, - "selector": "$.rpc.username" - }, - "password": { - "type": "pointer", - "name": "RPC Password", - "description": "The password for Bitcoin Core's RPC interface", - "subtype": "package", - "package-id": "bitcoind", - "target": "config", - "multi": false, - "selector": "$.rpc.password" - } - }, - "internal-proxy": { - "user": { - "type": "pointer", - "name": "RPC Username", - "description": "The username for the RPC user allocated to lnd", - "subtype": "package", - "package-id": "btc-rpc-proxy", - "target": "config", - "multi": false, - "selector": "$.users[?(@.name == \"lnd\")].name" - }, - "password": { - "type": "pointer", - "name": "RPC Password", - "description": "The password for the RPC user allocated to lnd", - "subtype": "package", - "package-id": "btc-rpc-proxy", - "target": "config", - "multi": false, - "selector": "$.users[?(@.name == \"lnd\")].password" - } - }, - "external": { - "connection-settings": { - "type": "union", - "name": "Connection Settings", - "description": "Information to connect to an external unpruned Bitcoin Core node", - "tag": { - "id": "type", - "name": "Type", - "description": "- Manual: Raw information for finding a Bitcoin Core node\n- Quick Connect: A Quick Connect URL for a Bitcoin Core node\n", - "variant-names": { - "manual": "Manual", - "quick-connect": "Quick Connect" - } - }, - "default": "quick-connect", - "variants": { - "manual": { - "host": { - "type": "string", - "name": "Public Address", - "description": "The public address of your Bitcoin Core server", - "nullable": false - }, - "rpc-user": { - "type": "string", - "name": "RPC Username", - "description": "The username for the RPC user on your Bitcoin Core RPC server", - "nullable": false - }, - "rpc-password": { - "type": "string", - "name": "RPC Password", - "description": "The password for the RPC user on your Bitcoin Core RPC server", - "nullable": false - }, - "rpc-port": { - "type": "number", - "name": "RPC Port", - "description": "The port that your Bitcoin Core RPC server is bound to", - "nullable": false, - "range": "[0,65535]", - "integral": true, - "default": 8332 - }, - "zmq-block-port": { - "type": "number", - "name": "ZeroMQ Block Port", - "description": "The port that your Bitcoin Core ZeroMQ server is bound to for raw blocks", - "nullable": false, - "range": "[0,65535]", - "integral": true, - "default": 28332 - }, - "zmq-tx-port": { - "type": "number", - "name": "ZeroMQ Transaction Port", - "description": "The port that your Bitcoin Core ZeroMQ server is bound to for raw transactions", - "nullable": false, - "range": "[0,65535]", - "integral": true, - "default": 28333 - } - }, - "quick-connect": { - "quick-connect-url": { - "type": "string", - "name": "Quick Connect URL", - "description": "The Quick Connect URL for your Bitcoin Core RPC server\nNOTE: LND will not accept a .onion url for this option\n", - "nullable": false, - "pattern": "btcstandup://[^:]*:[^@]*@[a-zA-Z0-9.-]+:[0-9]+(/(\\?(label=.+)?)?)?", - "pattern-description": "Must be a valid Quick Connect URL. For help, check out https://github.com/BlockchainCommons/Gordian/blob/master/Docs/Quick-Connect-API.md" - }, - "zmq-block-port": { - "type": "number", - "name": "ZeroMQ Block Port", - "description": "The port that your Bitcoin Core ZeroMQ server is bound to for raw blocks", - "nullable": false, - "range": "[0,65535]", - "integral": true, - "default": 28332 - }, - "zmq-tx-port": { - "type": "number", - "name": "ZeroMQ Transaction Port", - "description": "The port that your Bitcoin Core ZeroMQ server is bound to for raw transactions", - "nullable": false, - "range": "[0,65535]", - "integral": true, - "default": 28333 - } - } - } - } - } - } }, - "autopilot": { - "type": "object", - "name": "Autopilot", - "description": "Autopilot Settings", - "spec": { - "enabled": { - "type": "boolean", - "name": "Enabled", - "description": "If the autopilot agent should be active or not. The autopilot agent will\nattempt to AUTOMATICALLY OPEN CHANNELS to put your node in an advantageous\nposition within the network graph. DO NOT ENABLE THIS IF YOU WANT TO MANAGE \nCHANNELS MANUALLY OR DO NOT UNDERSTAND IT.\n", - "default": false - }, - "private": { - "type": "boolean", - "name": "Private", - "description": "Whether the channels created by the autopilot agent should be private or not.\nPrivate channels won't be announced to the network.\n", - "default": false - }, - "maxchannels": { - "type": "number", - "name": "Maximum Channels", - "description": "The maximum number of channels that should be created.", - "nullable": false, - "range": "[1,*)", - "integral": true, - "default": 5 - }, - "allocation": { - "type": "number", - "name": "Allocation", - "description": "The fraction of total funds that should be committed to automatic channel\nestablishment. For example 60% means that 60% of the total funds available\nwithin the wallet should be used to automatically establish channels. The total\namount of attempted channels will still respect the \"Maximum Channels\" parameter.\n", - "nullable": false, - "range": "[0,100]", - "integral": false, - "default": 60, - "units": "%" - }, - "min-channel-size": { - "type": "number", - "name": "Minimum Channel Size", - "description": "The smallest channel that the autopilot agent should create.", - "nullable": false, - "range": "[0,*)", - "integral": true, - "default": 20000, - "units": "satoshis" - }, - "max-channel-size": { - "type": "number", - "name": "Maximum Channel Size", - "description": "The largest channel that the autopilot agent should create.", - "nullable": false, - "range": "[0,*)", - "integral": true, - "default": 16777215, - "units": "satoshis" - }, - "advanced": { - "type": "object", - "name": "Advanced", - "description": "Advanced Options", - "spec": { - "min-confirmations": { - "type": "number", - "name": "Minimum Confirmations", - "description": "The minimum number of confirmations each of your inputs in funding transactions\ncreated by the autopilot agent must have.\n", - "nullable": false, - "range": "[0,*)", - "integral": true, - "default": 1, - "units": "blocks" - }, - "confirmation-target": { - "type": "number", - "name": "Confirmation Target", - "description": "The confirmation target (in blocks) for channels opened by autopilot.", - "nullable": false, - "range": "[0,*)", - "integral": true, - "default": 1, - "units": "blocks" - } - } - } - } + "internal-proxy": { + user: { + type: "pointer", + name: "RPC Username", + description: "The username for the RPC user allocated to lnd", + subtype: "package", + "package-id": "btc-rpc-proxy", + target: "config", + multi: false, + selector: '$.users[?(@.name == "lnd")].name', + }, + password: { + type: "pointer", + name: "RPC Password", + description: "The password for the RPC user allocated to lnd", + subtype: "package", + "package-id": "btc-rpc-proxy", + target: "config", + multi: false, + selector: '$.users[?(@.name == "lnd")].password', + }, }, - "advanced": { - "type": "object", - "name": "Advanced", - "description": "Advanced Options", - "spec": { - "debug-level": { - "type": "enum", - "name": "Log Verbosity", - "values": [ - "trace", - "debug", - "info", - "warn", - "error", - "critical" - ], - "description": "Sets the level of log filtration. Trace is the most verbose, Critical is the least.\n", - "default": "info", - "value-names": {} + external: { + "connection-settings": { + type: "union", + name: "Connection Settings", + description: + "Information to connect to an external unpruned Bitcoin Core node", + tag: { + id: "type", + name: "Type", + description: + "- Manual: Raw information for finding a Bitcoin Core node\n- Quick Connect: A Quick Connect URL for a Bitcoin Core node\n", + "variant-names": { + manual: "Manual", + "quick-connect": "Quick Connect", + }, }, - "db-bolt-no-freelist-sync": { - "type": "boolean", - "name": "Disallow Bolt DB Freelist Sync", - "description": "If true, prevents the database from syncing its freelist to disk.\n", - "default": false - }, - "db-bolt-auto-compact": { - "type": "boolean", - "name": "Compact Database on Startup", - "description": "Performs database compaction on startup. This is necessary to keep disk usage down over time at the cost of\nhaving longer startup times.\n", - "default": true - }, - "db-bolt-auto-compact-min-age": { - "type": "number", - "name": "Minimum Autocompaction Age for Bolt DB", - "description": "How long ago (in hours) the last compaction of a database file must be for it to be considered for auto\ncompaction again. Can be set to 0 to compact on every startup.\n", - "nullable": false, - "range": "[0, *)", - "integral": true, - "default": 168, - "units": "hours" - }, - "db-bolt-db-timeout": { - "type": "number", - "name": "Bolt DB Timeout", - "description": "How long should LND try to open the database before giving up?", - "nullable": false, - "range": "[1, 86400]", - "integral": true, - "default": 60, - "units": "seconds" - }, - "recovery-window": { - "type": "number", - "name": "Recovery Window", - "description": "Number of blocks in the past that LND should scan for unknown transactions", - "nullable": true, - "range": "[1,*)", - "integral": true, - "units": "blocks" - }, - "payments-expiration-grace-period": { - "type": "number", - "name": "Payments Expiration Grace Period", - "description": "A period to wait before for closing channels with outgoing htlcs that have timed out and are a result of this\nnodes instead payment. In addition to our current block based deadline, is specified this grace period will\nalso be taken into account.\n", - "nullable": false, - "range": "[1,*)", - "integral": true, - "default": 30, - "units": "seconds" - }, - "default-remote-max-htlcs": { - "type": "number", - "name": "Maximum Remote HTLCs", - "description": "The default max_htlc applied when opening or accepting channels. This value limits the number of concurrent\nHTLCs that the remote party can add to the commitment. The maximum possible value is 483.\n", - "nullable": false, - "range": "[1,483]", - "integral": true, - "default": 483, - "units": "htlcs" - }, - "max-channel-fee-allocation": { - "type": "number", - "name": "Maximum Channel Fee Allocation", - "description": "The maximum percentage of total funds that can be allocated to a channel's commitment fee. This only applies for\nthe initiator of the channel.\n", - "nullable": false, - "range": "[0.1, 1]", - "integral": false, - "default": 0.5 - }, - "max-commit-fee-rate-anchors": { - "type": "number", - "name": "Maximum Commitment Fee for Anchor Channels", - "description": "The maximum fee rate in sat/vbyte that will be used for commitments of channels of the anchors type. Must be\nlarge enough to ensure transaction propagation.\n", - "nullable": false, - "range": "[1,*)", - "integral": true, - "default": 10 - }, - "protocol-wumbo-channels": { - "type": "boolean", - "name": "Enable Wumbo Channels", - "description": "If set, then lnd will create and accept requests for channels larger than 0.16 BTC\n", - "default": false - }, - "protocol-no-anchors": { - "type": "boolean", - "name": "Disable Anchor Channels", - "description": "Set to disable support for anchor commitments. Anchor channels allow you to determine your fees at close time by\nusing a Child Pays For Parent transaction.\n", - "default": false - }, - "protocol-disable-script-enforced-lease": { - "type": "boolean", - "name": "Disable Script Enforced Channel Leases", - "description": "Set to disable support for script enforced lease channel commitments. If not set, lnd will accept these channels by default if the remote channel party proposes them. Note that lnd will require 1 UTXO to be reserved for this channel type if it is enabled.\nNote: This may cause you to be unable to close a channel and your wallets may not understand why", - "default": false - }, - "gc-canceled-invoices-on-startup": { - "type": "boolean", - "name": "Cleanup Canceled Invoices on Startup", - "description": "If true, LND will attempt to garbage collect canceled invoices upon start.\n", - "default": false - }, - "bitcoin": { - "type": "object", - "name": "Bitcoin Channel Configuration", - "description": "Configuration options for lightning network channel management operating over the Bitcoin network", - "spec": { - "default-channel-confirmations": { - "type": "number", - "name": "Default Channel Confirmations", - "description": "The default number of confirmations a channel must have before it's considered\nopen. LND will require any incoming channel requests to wait this many\nconfirmations before it considers the channel active.\n", - "nullable": false, - "range": "[1,6]", - "integral": true, - "default": 3, - "units": "blocks" + default: "quick-connect", + variants: { + manual: { + host: { + type: "string", + name: "Public Address", + description: + "The public address of your Bitcoin Core server", + nullable: false, }, - "min-htlc": { - "type": "number", - "name": "Minimum Incoming HTLC Size", - "description": "The smallest HTLC LND will to accept on your channels, in millisatoshis.\n", - "nullable": false, - "range": "[1,*)", - "integral": true, - "default": 1, - "units": "millisatoshis" + "rpc-user": { + type: "string", + name: "RPC Username", + description: + "The username for the RPC user on your Bitcoin Core RPC server", + nullable: false, }, - "min-htlc-out": { - "type": "number", - "name": "Minimum Outgoing HTLC Size", - "description": "The smallest HTLC LND will send out on your channels, in millisatoshis.\n", - "nullable": false, - "range": "[1,*)", - "integral": true, - "default": 1000, - "units": "millisatoshis" + "rpc-password": { + type: "string", + name: "RPC Password", + description: + "The password for the RPC user on your Bitcoin Core RPC server", + nullable: false, }, - "base-fee": { - "type": "number", - "name": "Routing Base Fee", - "description": "The base fee in millisatoshi you will charge for forwarding payments on your\nchannels.\n", - "nullable": false, - "range": "[0,*)", - "integral": true, - "default": 1000, - "units": "millisatoshi" + "rpc-port": { + type: "number", + name: "RPC Port", + description: + "The port that your Bitcoin Core RPC server is bound to", + nullable: false, + range: "[0,65535]", + integral: true, + default: 8332, }, - "fee-rate": { - "type": "number", - "name": "Routing Fee Rate", - "description": "The fee rate used when forwarding payments on your channels. The total fee\ncharged is the Base Fee + (amount * Fee Rate / 1000000), where amount is the\nforwarded amount. Measured in sats per million\n", - "nullable": false, - "range": "[1,1000000)", - "integral": true, - "default": 1, - "units": "sats per million" + "zmq-block-port": { + type: "number", + name: "ZeroMQ Block Port", + description: + "The port that your Bitcoin Core ZeroMQ server is bound to for raw blocks", + nullable: false, + range: "[0,65535]", + integral: true, + default: 28332, }, - "time-lock-delta": { - "type": "number", - "name": "Time Lock Delta", - "description": "The CLTV delta we will subtract from a forwarded HTLC's timelock value.", - "nullable": false, - "range": "[6, 144]", - "integral": true, - "default": 40, - "units": "blocks" - } - } - } - } - } - } - } + "zmq-tx-port": { + type: "number", + name: "ZeroMQ Transaction Port", + description: + "The port that your Bitcoin Core ZeroMQ server is bound to for raw transactions", + nullable: false, + range: "[0,65535]", + integral: true, + default: 28333, + }, + }, + "quick-connect": { + "quick-connect-url": { + type: "string", + name: "Quick Connect URL", + description: + "The Quick Connect URL for your Bitcoin Core RPC server\nNOTE: LND will not accept a .onion url for this option\n", + nullable: false, + pattern: + "btcstandup://[^:]*:[^@]*@[a-zA-Z0-9.-]+:[0-9]+(/(\\?(label=.+)?)?)?", + "pattern-description": + "Must be a valid Quick Connect URL. For help, check out https://github.com/BlockchainCommons/Gordian/blob/master/Docs/Quick-Connect-API.md", + }, + "zmq-block-port": { + type: "number", + name: "ZeroMQ Block Port", + description: + "The port that your Bitcoin Core ZeroMQ server is bound to for raw blocks", + nullable: false, + range: "[0,65535]", + integral: true, + default: 28332, + }, + "zmq-tx-port": { + type: "number", + name: "ZeroMQ Transaction Port", + description: + "The port that your Bitcoin Core ZeroMQ server is bound to for raw transactions", + nullable: false, + range: "[0,65535]", + integral: true, + default: 28333, + }, + }, + }, + }, + }, + }, + }, + autopilot: { + type: "object", + name: "Autopilot", + description: "Autopilot Settings", + spec: { + enabled: { + type: "boolean", + name: "Enabled", + description: + "If the autopilot agent should be active or not. The autopilot agent will\nattempt to AUTOMATICALLY OPEN CHANNELS to put your node in an advantageous\nposition within the network graph. DO NOT ENABLE THIS IF YOU WANT TO MANAGE \nCHANNELS MANUALLY OR DO NOT UNDERSTAND IT.\n", + default: false, + }, + private: { + type: "boolean", + name: "Private", + description: + "Whether the channels created by the autopilot agent should be private or not.\nPrivate channels won't be announced to the network.\n", + default: false, + }, + maxchannels: { + type: "number", + name: "Maximum Channels", + description: + "The maximum number of channels that should be created.", + nullable: false, + range: "[1,*)", + integral: true, + default: 5, + }, + allocation: { + type: "number", + name: "Allocation", + description: + 'The fraction of total funds that should be committed to automatic channel\nestablishment. For example 60% means that 60% of the total funds available\nwithin the wallet should be used to automatically establish channels. The total\namount of attempted channels will still respect the "Maximum Channels" parameter.\n', + nullable: false, + range: "[0,100]", + integral: false, + default: 60, + units: "%", + }, + "min-channel-size": { + type: "number", + name: "Minimum Channel Size", + description: + "The smallest channel that the autopilot agent should create.", + nullable: false, + range: "[0,*)", + integral: true, + default: 20000, + units: "satoshis", + }, + "max-channel-size": { + type: "number", + name: "Maximum Channel Size", + description: + "The largest channel that the autopilot agent should create.", + nullable: false, + range: "[0,*)", + integral: true, + default: 16777215, + units: "satoshis", + }, + advanced: { + type: "object", + name: "Advanced", + description: "Advanced Options", + spec: { + "min-confirmations": { + type: "number", + name: "Minimum Confirmations", + description: + "The minimum number of confirmations each of your inputs in funding transactions\ncreated by the autopilot agent must have.\n", + nullable: false, + range: "[0,*)", + integral: true, + default: 1, + units: "blocks", + }, + "confirmation-target": { + type: "number", + name: "Confirmation Target", + description: + "The confirmation target (in blocks) for channels opened by autopilot.", + nullable: false, + range: "[0,*)", + integral: true, + default: 1, + units: "blocks", + }, + }, + }, + }, + }, + advanced: { + type: "object", + name: "Advanced", + description: "Advanced Options", + spec: { + "debug-level": { + type: "enum", + name: "Log Verbosity", + values: ["trace", "debug", "info", "warn", "error", "critical"], + description: + "Sets the level of log filtration. Trace is the most verbose, Critical is the least.\n", + default: "info", + "value-names": {}, + }, + "db-bolt-no-freelist-sync": { + type: "boolean", + name: "Disallow Bolt DB Freelist Sync", + description: + "If true, prevents the database from syncing its freelist to disk.\n", + default: false, + }, + "db-bolt-auto-compact": { + type: "boolean", + name: "Compact Database on Startup", + description: + "Performs database compaction on startup. This is necessary to keep disk usage down over time at the cost of\nhaving longer startup times.\n", + default: true, + }, + "db-bolt-auto-compact-min-age": { + type: "number", + name: "Minimum Autocompaction Age for Bolt DB", + description: + "How long ago (in hours) the last compaction of a database file must be for it to be considered for auto\ncompaction again. Can be set to 0 to compact on every startup.\n", + nullable: false, + range: "[0, *)", + integral: true, + default: 168, + units: "hours", + }, + "db-bolt-db-timeout": { + type: "number", + name: "Bolt DB Timeout", + description: + "How long should LND try to open the database before giving up?", + nullable: false, + range: "[1, 86400]", + integral: true, + default: 60, + units: "seconds", + }, + "recovery-window": { + type: "number", + name: "Recovery Window", + description: + "Number of blocks in the past that LND should scan for unknown transactions", + nullable: true, + range: "[1,*)", + integral: true, + units: "blocks", + }, + "payments-expiration-grace-period": { + type: "number", + name: "Payments Expiration Grace Period", + description: + "A period to wait before for closing channels with outgoing htlcs that have timed out and are a result of this\nnodes instead payment. In addition to our current block based deadline, is specified this grace period will\nalso be taken into account.\n", + nullable: false, + range: "[1,*)", + integral: true, + default: 30, + units: "seconds", + }, + "default-remote-max-htlcs": { + type: "number", + name: "Maximum Remote HTLCs", + description: + "The default max_htlc applied when opening or accepting channels. This value limits the number of concurrent\nHTLCs that the remote party can add to the commitment. The maximum possible value is 483.\n", + nullable: false, + range: "[1,483]", + integral: true, + default: 483, + units: "htlcs", + }, + "max-channel-fee-allocation": { + type: "number", + name: "Maximum Channel Fee Allocation", + description: + "The maximum percentage of total funds that can be allocated to a channel's commitment fee. This only applies for\nthe initiator of the channel.\n", + nullable: false, + range: "[0.1, 1]", + integral: false, + default: 0.5, + }, + "max-commit-fee-rate-anchors": { + type: "number", + name: "Maximum Commitment Fee for Anchor Channels", + description: + "The maximum fee rate in sat/vbyte that will be used for commitments of channels of the anchors type. Must be\nlarge enough to ensure transaction propagation.\n", + nullable: false, + range: "[1,*)", + integral: true, + default: 10, + }, + "protocol-wumbo-channels": { + type: "boolean", + name: "Enable Wumbo Channels", + description: + "If set, then lnd will create and accept requests for channels larger than 0.16 BTC\n", + default: false, + }, + "protocol-no-anchors": { + type: "boolean", + name: "Disable Anchor Channels", + description: + "Set to disable support for anchor commitments. Anchor channels allow you to determine your fees at close time by\nusing a Child Pays For Parent transaction.\n", + default: false, + }, + "protocol-disable-script-enforced-lease": { + type: "boolean", + name: "Disable Script Enforced Channel Leases", + description: + "Set to disable support for script enforced lease channel commitments. If not set, lnd will accept these channels by default if the remote channel party proposes them. Note that lnd will require 1 UTXO to be reserved for this channel type if it is enabled.\nNote: This may cause you to be unable to close a channel and your wallets may not understand why", + default: false, + }, + "gc-canceled-invoices-on-startup": { + type: "boolean", + name: "Cleanup Canceled Invoices on Startup", + description: + "If true, LND will attempt to garbage collect canceled invoices upon start.\n", + default: false, + }, + bitcoin: { + type: "object", + name: "Bitcoin Channel Configuration", + description: + "Configuration options for lightning network channel management operating over the Bitcoin network", + spec: { + "default-channel-confirmations": { + type: "number", + name: "Default Channel Confirmations", + description: + "The default number of confirmations a channel must have before it's considered\nopen. LND will require any incoming channel requests to wait this many\nconfirmations before it considers the channel active.\n", + nullable: false, + range: "[1,6]", + integral: true, + default: 3, + units: "blocks", + }, + "min-htlc": { + type: "number", + name: "Minimum Incoming HTLC Size", + description: + "The smallest HTLC LND will to accept on your channels, in millisatoshis.\n", + nullable: false, + range: "[1,*)", + integral: true, + default: 1, + units: "millisatoshis", + }, + "min-htlc-out": { + type: "number", + name: "Minimum Outgoing HTLC Size", + description: + "The smallest HTLC LND will send out on your channels, in millisatoshis.\n", + nullable: false, + range: "[1,*)", + integral: true, + default: 1000, + units: "millisatoshis", + }, + "base-fee": { + type: "number", + name: "Routing Base Fee", + description: + "The base fee in millisatoshi you will charge for forwarding payments on your\nchannels.\n", + nullable: false, + range: "[0,*)", + integral: true, + default: 1000, + units: "millisatoshi", + }, + "fee-rate": { + type: "number", + name: "Routing Fee Rate", + description: + "The fee rate used when forwarding payments on your channels. The total fee\ncharged is the Base Fee + (amount * Fee Rate / 1000000), where amount is the\nforwarded amount. Measured in sats per million\n", + nullable: false, + range: "[1,1000000)", + integral: true, + default: 1, + units: "sats per million", + }, + "time-lock-delta": { + type: "number", + name: "Time Lock Delta", + description: + "The CLTV delta we will subtract from a forwarded HTLC's timelock value.", + nullable: false, + range: "[6, 144]", + integral: true, + default: 40, + units: "blocks", + }, + }, + }, + }, + }, + }, + }, + }; } -/** - * @param {import ("./types").Effects} effects - * @param {import("./types").Config} input - * @returns {Promise} - */ -export async function setConfig(effects, input) { - - return { - signal: "SIGTERM", - "depends-on": {} - } +export async function setConfig(effects) { + return { + error: "Not setup" + }; } - diff --git a/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/types.d.ts b/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/types.d.ts deleted file mode 100644 index e14d2781e..000000000 --- a/backend/test/js_action_execute/package-data/scripts/test-package/0.3.0.3/types.d.ts +++ /dev/null @@ -1,266 +0,0 @@ -export type Effects = { - writeFile(input: { path: string; volumeId: string; toWrite: string }): Promise; - readFile(input: { volumeId: string; path: string }): Promise; - createDir(input: { volumeId: string; path: string }): Promise; - removeDir(input: { volumeId: string; path: string }): Promise; - removeFile(input: { volumeId: string; path: string }): Promise; - writeJsonFile(input: { volumeId: string; path: string; toWrite: object }): Promise; - readJsonFile(input: { volumeId: string; path: string }): Promise; - trace(whatToPrin: string): void; - warn(whatToPrin: string): void; - error(whatToPrin: string): void; - debug(whatToPrin: string): void; - info(whatToPrin: string): void; - is_sandboxed(): boolean; -}; - -export type ActionResult = { - version: "0"; - message: string; - value?: string; - copyable: boolean; - qr: boolean; -}; - -export type ConfigRes = { - config?: Config; - spec: ConfigSpec; -}; -export type Config = { - [value: string]: any; -}; - -export type ConfigSpec = { - [value: string]: ValueSpecAny; -}; -export type WithDefault = T & { - default?: Default; -}; - -export type WithDescription = T & { - description?: String; - name: string; - warning?: string; -}; - -export type ListSpec = { - spec: T; - range: string; -}; - -export type Tag = V & { - type: T; -}; - -export type Subtype = V & { - subtype: T; -}; - -export type Target = V & { - target: T; -}; - -export type UniqueBy = - | { - any: UniqueBy[]; - } - | { - all: UniqueBy[]; - } - | string - | null; - -export type WithNullable = T & { - nullable: boolean; -}; -export type DefaultString = - | String - | { - charset?: string; - len: number; - }; - -export type ValueSpecString = ( - | {} - | { - pattern: string; - "pattern-description": string; - } -) & { - copyable?: boolean; - masked?: boolean; - placeholder?: string; -}; -export type ValueSpecNumber = { - range?: string; - integral?: boolean; - units?: string; - placeholder?: number; -}; -export type ValueSpecBoolean = {}; -export type ValueSpecAny = - | Tag<"boolean", WithDescription>> - | Tag<"string", WithDescription, DefaultString>>> - | Tag<"number", WithDescription, number>>> - | Tag< - "enum", - WithDescription< - WithDefault< - { - values: string[]; - "value-names": { - [key: string]: string; - }; - }, - string - > - > - > - | Tag<"list", ValueSpecList> - | Tag<"object", WithDescription>> - | Tag<"union", WithDescription>> - | Tag< - "pointer", - WithDescription< - | Subtype< - "package", - | Target< - "tor-key", - { - "package-id": string; - interface: string; - } - > - | Target< - "tor-address", - { - "package-id": string; - interface: string; - } - > - | Target< - "lan-address", - { - "package-id": string; - interface: string; - } - > - | Target< - "config", - { - "package-id": string; - selector: string; - multi: boolean; - } - > - > - | Subtype<"system", {}> - > - >; -export type ValueSpecUnion = { - tag: { - id: string; - name: string; - description?: string; - "variant-names": { - [key: string]: string; - }; - }; - variants: { - [key: string]: ConfigSpec; - }; - "display-as"?: string; - "unique-by"?: UniqueBy; -}; -export type ValueSpecObject = { - spec: ConfigSpec; - "display-as"?: string; - "unique-by"?: UniqueBy; -}; -export type ValueSpecList = - | Subtype<"boolean", WithDescription, boolean>>> - | Subtype<"string", WithDescription, string>>> - | Subtype<"number", WithDescription, number>>> - | Subtype< - "enum", - WithDescription< - WithDefault< - { - values: string[]; - "value-names": { - [key: string]: string; - }; - }, - string - > - > - >; - -export type SetResult = { - signal: - | "SIGTERM" - | "SIGHUP" - | "SIGINT" - | "SIGQUIT" - | "SIGILL" - | "SIGTRAP" - | "SIGABRT" - | "SIGBUS" - | "SIGFPE" - | "SIGKILL" - | "SIGUSR1" - | "SIGSEGV" - | "SIGUSR2" - | "SIGPIPE" - | "SIGALRM" - | "SIGSTKFLT" - | "SIGCHLD" - | "SIGCONT" - | "SIGSTOP" - | "SIGTSTP" - | "SIGTTIN" - | "SIGTTOU" - | "SIGURG" - | "SIGXCPU" - | "SIGXFSZ" - | "SIGVTALRM" - | "SIGPROF" - | "SIGWINCH" - | "SIGIO" - | "SIGPWR" - | "SIGSYS" - | "SIGEMT" - | "SIGINFO"; - "depends-on": { - [packageId: string]: string[]; - }; -}; -export type PackagePropertiesV2 = { - [name: string]: PackagePropertyObject | PackagePropertyString; -}; -export type PackagePropertyString = { - type: "string"; - description?: string; - value: string; - copyable?: boolean; - qr?: boolean; - masked?: boolean; -}; -export type PackagePropertyObject = { - value: PackagePropertiesV2; - type: "object"; - description: string; -}; - -export type Properties = { - version: 2; - data: PackagePropertiesV2; -}; - - -export type Dependencies = { - [id: string]: { - check(effects: Effects, input: Config): Promise, - autoConfigure(effects: Effects, input: Config): Promise, - } -} \ No newline at end of file diff --git a/libs/artifacts/types.d.ts b/libs/artifacts/types.d.ts new file mode 100644 index 000000000..a5b9a75d3 --- /dev/null +++ b/libs/artifacts/types.d.ts @@ -0,0 +1,335 @@ +export namespace ExpectedExports { + /** Set configuration is called after we have modified and saved the configuration in the embassy ui. Use this to make a file for the docker to read from for configuration. */ + export type setConfig = ( + effects: Effects, + input: Config, + ) => Promise>; + /** Get configuration returns a shape that describes the format that the embassy ui will generate, and later send to the set config */ + export type getConfig = (effects: Effects) => Promise>; + /** These are how we make sure the our dependency configurations are valid and if not how to fix them. */ + export type dependencies = Dependencies; + /** Properties are used to get values from the docker, like a username + password, what ports we are hosting from */ + export type properties = ( + effects: Effects, + ) => Promise>; +} + +/** Used to reach out from the pure js runtime */ +export type Effects = { + /** Usable when not sandboxed */ + writeFile( + input: { path: string; volumeId: string; toWrite: string }, + ): Promise; + readFile(input: { volumeId: string; path: string }): Promise; + /** Create a directory. Usable when not sandboxed */ + createDir(input: { volumeId: string; path: string }): Promise; + /** Remove a directory. Usable when not sandboxed */ + removeDir(input: { volumeId: string; path: string }): Promise; + removeFile(input: { volumeId: string; path: string }): Promise; + + /** Write a json file into an object. Usable when not sandboxed */ + writeJsonFile( + input: { volumeId: string; path: string; toWrite: object }, + ): Promise; + + /** Read a json file into an object */ + readJsonFile(input: { volumeId: string; path: string }): Promise; + + /** Log at the trace level */ + trace(whatToPrint: string): void; + /** Log at the warn level */ + warn(whatToPrint: string): void; + /** Log at the error level */ + error(whatToPrint: string): void; + /** Log at the debug level */ + debug(whatToPrint: string): void; + /** Log at the info level */ + info(whatToPrint: string): void; + + /** Sandbox mode lets us read but not write */ + is_sandboxed(): boolean; +}; + +export type ActionResult = { + version: "0"; + message: string; + value?: string; + copyable: boolean; + qr: boolean; +}; + +export type ConfigRes = { + /** This should be the previous config, that way during set config we start with the previous */ + config?: Config; + /** Shape that is describing the form in the ui */ + spec: ConfigSpec; +}; +export type Config = { + [propertyName: string]: any; +}; + +export type ConfigSpec = { + /** Given a config value, define what it should render with the following spec */ + [configValue: string]: ValueSpecAny; +}; +export type WithDefault = T & { + default?: Default; +}; + +export type WithDescription = T & { + description?: String; + name: string; + warning?: string; +}; + +export type ListSpec = { + spec: T; + range: string; +}; + +export type Tag = V & { + type: T; +}; + +export type Subtype = V & { + subtype: T; +}; + +export type Target = V & { + target: T; +}; + +export type UniqueBy = + | { + any: UniqueBy[]; + } + | string + | null; + +export type WithNullable = T & { + nullable: boolean; +}; +export type DefaultString = + | String + | { + /** The chars available for the randome generation */ + charset?: string; + /** Length that we generate to */ + len: number; + }; + +export type ValueSpecString = + & ( + | {} + | { + pattern: string; + "pattern-description": string; + } + ) + & { + copyable?: boolean; + masked?: boolean; + placeholder?: string; + }; +export type ValueSpecNumber = { + /** Something like [3,6] or [0, *) */ + range?: string; + integral?: boolean; + /** Used a description of the units */ + units?: string; + placeholder?: number; +}; +export type ValueSpecBoolean = {}; +export type ValueSpecAny = + | Tag<"boolean", WithDescription>> + | Tag< + "string", + WithDescription, DefaultString>> + > + | Tag< + "number", + WithDescription, number>> + > + | Tag< + "enum", + WithDescription< + WithDefault< + { + values: string[]; + "value-names": { + [key: string]: string; + }; + }, + string + > + > + > + | Tag<"list", ValueSpecList> + | Tag<"object", WithDescription>> + | Tag<"union", WithDescription>> + | Tag< + "pointer", + WithDescription< + | Subtype< + "package", + | Target< + "tor-key", + { + "package-id": string; + interface: string; + } + > + | Target< + "tor-address", + { + "package-id": string; + interface: string; + } + > + | Target< + "lan-address", + { + "package-id": string; + interface: string; + } + > + | Target< + "config", + { + "package-id": string; + selector: string; + multi: boolean; + } + > + > + | Subtype<"system", {}> + > + >; +export type ValueSpecUnion = { + /** What tag for the specification, for tag unions */ + tag: { + id: string; + name: string; + description?: string; + "variant-names": { + [key: string]: string; + }; + }; + /** The possible enum values */ + variants: { + [key: string]: ConfigSpec; + }; + "display-as"?: string; + "unique-by"?: UniqueBy; +}; +export type ValueSpecObject = { + spec: ConfigSpec; + "display-as"?: string; + "unique-by"?: UniqueBy; +}; +export type ValueSpecList = + | Subtype< + "boolean", + WithDescription, boolean>> + > + | Subtype< + "string", + WithDescription, string>> + > + | Subtype< + "number", + WithDescription, number>> + > + | Subtype< + "enum", + WithDescription< + WithDefault< + { + values: string[]; + "value-names": { + [key: string]: string; + }; + }, + string + > + > + >; + +export type SetResult = { + /** These are the unix process signals */ + signal: + | "SIGTERM" + | "SIGHUP" + | "SIGINT" + | "SIGQUIT" + | "SIGILL" + | "SIGTRAP" + | "SIGABRT" + | "SIGBUS" + | "SIGFPE" + | "SIGKILL" + | "SIGUSR1" + | "SIGSEGV" + | "SIGUSR2" + | "SIGPIPE" + | "SIGALRM" + | "SIGSTKFLT" + | "SIGCHLD" + | "SIGCONT" + | "SIGSTOP" + | "SIGTSTP" + | "SIGTTIN" + | "SIGTTOU" + | "SIGURG" + | "SIGXCPU" + | "SIGXFSZ" + | "SIGVTALRM" + | "SIGPROF" + | "SIGWINCH" + | "SIGIO" + | "SIGPWR" + | "SIGSYS" + | "SIGEMT" + | "SIGINFO"; + "depends-on": { + [packageId: string]: string[]; + }; +}; + +export type KnownError = { error: String }; +export type ResultType = KnownError | { result: T }; + +export type PackagePropertiesV2 = { + [name: string]: PackagePropertyObject | PackagePropertyString; +}; +export type PackagePropertyString = { + type: "string"; + description?: string; + value: string; + /** Let's the ui make this copyable button */ + copyable?: boolean; + /** Let the ui create a qr for this field */ + qr?: boolean; + /** Hiding the value unless toggled off for field */ + masked?: boolean; +}; +export type PackagePropertyObject = { + value: PackagePropertiesV2; + type: "object"; + description: string; +}; + +export type Properties = { + version: 2; + data: PackagePropertiesV2; +}; + +export type Dependencies = { + /** Id is the id of the package, should be the same as the manifest */ + [id: string]: { + /** Checks are called to make sure that our dependency is in the correct shape. If a known error is returned we know that the dependency needs modification */ + check(effects: Effects, input: Config): Promise>; + /** This is called after we know that the dependency package needs a new configuration, this would be a transform for defaults */ + autoConfigure(effects: Effects, input: Config): Promise>; + }; +};