diff --git a/CHANGELOG.md b/CHANGELOG.md index b5757496df3..37230e09242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ Looking for changes that affect our C API? See the [C API Changelog](lib/c-api/C ## Added +- [#3317](https://github.com/wasmerio/wasmer/pull/3317) Add a `wasmer add` command for adding bindings for a WAPM package to your project (only Python and JavaScript are supported for now) + ## Changed - [#3318](https://github.com/wasmerio/wasmer/pull/3318) Bump the Minimum Supported Rust Version (MSRV) to 1.63 diff --git a/Cargo.lock b/Cargo.lock index 09a8a64a3d7..3f7b818fc9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4222,6 +4222,7 @@ dependencies = [ "dirs 4.0.0", "flate2", "graphql_client", + "log", "lzma-rs", "rand 0.8.5", "reqwest", diff --git a/Makefile b/Makefile index 2241b1b33f6..69012a0b907 100644 --- a/Makefile +++ b/Makefile @@ -748,3 +748,6 @@ install-local: package test-minimal-versions: rm -f Cargo.lock cargo +nightly build --tests -Z minimal-versions --all-features + +update-graphql-schema: + curl -sSfL https://registry.wapm.io/graphql/schema.graphql > lib/registry/graphql/schema.graphql diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index 04114e920b3..a89d14d37f2 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -44,7 +44,7 @@ atty = "0.2" colored = "2.0" anyhow = "1.0" spinner = "0.5.0" -clap = { version = "3.2.22", features = ["derive"] } +clap = { version = "3.2.22", features = ["derive", "env"] } # For the function names autosuggestion distance = "0.4" # For the inspect subcommand @@ -56,7 +56,7 @@ log = { version = "0.4", optional = true } tempfile = "3" tempdir = "0.3.7" http_req = { version="^0.8", default-features = false, features = ["rust-tls"], optional = true } -reqwest = { version = "^0.11", default-features = false, feature = ["rustls-tls", "json"], optional = true } +reqwest = { version = "^0.11", default-features = false, features = ["rustls-tls", "json"], optional = true } serde = { version = "1.0.147", features = ["derive"], optional = true } dirs = { version = "4.0", optional = true } serde_json = { version = "1.0", optional = true } diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 969d541a95f..34c2c1835d2 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -10,10 +10,10 @@ use crate::commands::CreateExe; use crate::commands::CreateObj; #[cfg(feature = "wast")] use crate::commands::Wast; -use crate::commands::{Cache, Config, Inspect, List, Login, Run, SelfUpdate, Validate}; +use crate::commands::{Add, Cache, Config, Inspect, List, Login, Run, SelfUpdate, Validate}; use crate::error::PrettyError; use clap::{CommandFactory, ErrorKind, Parser}; -use std::fmt; +use std::{fmt, str::FromStr}; #[derive(Parser, Debug)] #[cfg_attr( @@ -150,6 +150,9 @@ enum WasmerCLIOptions { #[cfg(target_os = "linux")] #[clap(name = "binfmt")] Binfmt(Binfmt), + + /// Add a WAPM package's bindings to your application. + Add(Add), } impl WasmerCLIOptions { @@ -173,6 +176,7 @@ impl WasmerCLIOptions { Self::Wast(wast) => wast.execute(), #[cfg(target_os = "linux")] Self::Binfmt(binfmt) => binfmt.execute(), + Self::Add(install) => install.execute(), } } } @@ -224,7 +228,7 @@ fn wasmer_main_inner() -> Result<(), anyhow::Error> { WasmerCLIOptions::Run(Run::from_binfmt_args()) } else { match command.unwrap_or(&"".to_string()).as_ref() { - "cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run" + "add" | "cache" | "compile" | "config" | "create-exe" | "help" | "inspect" | "run" | "self-update" | "validate" | "wast" | "binfmt" | "list" | "login" => { WasmerCLIOptions::parse() } @@ -278,7 +282,7 @@ impl fmt::Display for SplitVersion { #[test] fn test_split_version() { assert_eq!( - SplitVersion::new("registry.wapm.io/graphql/python/python").unwrap(), + SplitVersion::parse("registry.wapm.io/graphql/python/python").unwrap(), SplitVersion { original: "registry.wapm.io/graphql/python/python".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), @@ -288,7 +292,7 @@ fn test_split_version() { } ); assert_eq!( - SplitVersion::new("registry.wapm.io/python/python").unwrap(), + SplitVersion::parse("registry.wapm.io/python/python").unwrap(), SplitVersion { original: "registry.wapm.io/python/python".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), @@ -298,7 +302,7 @@ fn test_split_version() { } ); assert_eq!( - SplitVersion::new("namespace/name@version:command").unwrap(), + SplitVersion::parse("namespace/name@version:command").unwrap(), SplitVersion { original: "namespace/name@version:command".to_string(), registry: None, @@ -308,7 +312,7 @@ fn test_split_version() { } ); assert_eq!( - SplitVersion::new("namespace/name@version").unwrap(), + SplitVersion::parse("namespace/name@version").unwrap(), SplitVersion { original: "namespace/name@version".to_string(), registry: None, @@ -318,7 +322,7 @@ fn test_split_version() { } ); assert_eq!( - SplitVersion::new("namespace/name").unwrap(), + SplitVersion::parse("namespace/name").unwrap(), SplitVersion { original: "namespace/name".to_string(), registry: None, @@ -328,7 +332,7 @@ fn test_split_version() { } ); assert_eq!( - SplitVersion::new("registry.wapm.io/namespace/name").unwrap(), + SplitVersion::parse("registry.wapm.io/namespace/name").unwrap(), SplitVersion { original: "registry.wapm.io/namespace/name".to_string(), registry: Some("https://registry.wapm.io/graphql".to_string()), @@ -338,13 +342,21 @@ fn test_split_version() { } ); assert_eq!( - format!("{}", SplitVersion::new("namespace").unwrap_err()), + format!("{}", SplitVersion::parse("namespace").unwrap_err()), "Invalid package version: \"namespace\"".to_string(), ); } impl SplitVersion { - pub fn new(s: &str) -> Result { + pub fn parse(s: &str) -> Result { + s.parse() + } +} + +impl FromStr for SplitVersion { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { let command = WasmerCLIOptions::command(); let mut prohibited_package_names = command.get_subcommands().map(|s| s.get_name()); diff --git a/lib/cli/src/commands.rs b/lib/cli/src/commands.rs index cff06ea16ba..964bde1abae 100644 --- a/lib/cli/src/commands.rs +++ b/lib/cli/src/commands.rs @@ -1,4 +1,5 @@ //! The commands available in the Wasmer binary. +mod add; #[cfg(target_os = "linux")] mod binfmt; mod cache; @@ -28,7 +29,9 @@ pub use create_exe::*; pub use create_obj::*; #[cfg(feature = "wast")] pub use wast::*; -pub use {cache::*, config::*, inspect::*, list::*, login::*, run::*, self_update::*, validate::*}; +pub use { + add::*, cache::*, config::*, inspect::*, list::*, login::*, run::*, self_update::*, validate::*, +}; /// The kind of object format to emit. #[derive(Debug, Copy, Clone, clap::Parser)] diff --git a/lib/cli/src/commands/add.rs b/lib/cli/src/commands/add.rs new file mode 100644 index 00000000000..76c4ffb2c3a --- /dev/null +++ b/lib/cli/src/commands/add.rs @@ -0,0 +1,181 @@ +use std::process::{Command, Stdio}; + +use anyhow::{Context, Error}; +use clap::Parser; +use wasmer_registry::{Bindings, PartialWapmConfig, ProgrammingLanguage}; + +use crate::cli::SplitVersion; + +/// Add a WAPM package's bindings to your application. +#[derive(Debug, Parser)] +pub struct Add { + /// The registry to fetch bindings from. + #[clap(long, env = "WAPM_REGISTRY")] + registry: Option, + /// Add the JavaScript bindings using "npm install". + #[clap(long, groups = &["bindings", "js"])] + npm: bool, + /// Add the JavaScript bindings using "yarn add". + #[clap(long, groups = &["bindings", "js"])] + yarn: bool, + /// Add the package as a dev-dependency. + #[clap(long, requires = "js")] + dev: bool, + /// Add the Python bindings using "pip install". + #[clap(long, groups = &["bindings", "py"])] + pip: bool, + /// The packages to add (e.g. "wasmer/wasmer-pack@0.5.0" or "python/python") + #[clap(parse(try_from_str))] + packages: Vec, +} + +impl Add { + /// Execute [`Add`]. + pub fn execute(&self) -> Result<(), Error> { + anyhow::ensure!(!self.packages.is_empty(), "No packages specified"); + + let registry = self + .registry() + .context("Unable to determine which registry to use")?; + + let bindings = self.lookup_bindings(®istry)?; + + let mut cmd = self.target().command(&bindings); + + #[cfg(feature = "debug")] + log::debug!("Running {cmd:?}"); + + let status = cmd + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .status() + .with_context(|| { + format!( + "Unable to start \"{:?}\". Is it installed?", + cmd.get_program() + ) + })?; + + anyhow::ensure!(status.success(), "Command failed: {:?}", cmd); + + Ok(()) + } + + fn lookup_bindings(&self, registry: &str) -> Result, Error> { + #[cfg(feature = "debug")] + log::debug!("Querying WAPM for the bindings packages"); + + let mut bindings_to_add = Vec::new(); + let language = self.target().language(); + + for pkg in &self.packages { + let bindings = lookup_bindings_for_package(registry, pkg, &language) + .with_context(|| format!("Unable to find bindings for {pkg}"))?; + bindings_to_add.push(bindings); + } + + Ok(bindings_to_add) + } + + fn registry(&self) -> Result { + match &self.registry { + Some(r) => Ok(r.clone()), + None => { + let cfg = PartialWapmConfig::from_file() + .map_err(Error::msg) + .context("Unable to load WAPM's config file")?; + Ok(cfg.registry.get_current_registry()) + } + } + } + + fn target(&self) -> Target { + match (self.pip, self.npm, self.yarn) { + (true, false, false) => Target::Pip, + (false, true, false) => Target::Npm { dev: self.dev }, + (false, false, true) => Target::Yarn { dev: self.dev }, + _ => unreachable!( + "Clap should ensure at least one item in the \"bindings\" group is specified" + ), + } + } +} + +fn lookup_bindings_for_package( + registry: &str, + pkg: &SplitVersion, + language: &ProgrammingLanguage, +) -> Result { + let all_bindings = + wasmer_registry::list_bindings(registry, &pkg.package, pkg.version.as_deref())?; + + match all_bindings.iter().find(|b| b.language == *language) { + Some(b) => { + #[cfg(feature = "debug")] + { + let Bindings { url, generator, .. } = b; + log::debug!("Found {pkg} bindings generated by {generator} at {url}"); + } + + Ok(b.clone()) + } + None => { + if all_bindings.is_empty() { + anyhow::bail!("The package doesn't contain any bindings"); + } else { + todo!(); + } + } + } +} + +#[derive(Debug, Copy, Clone)] +enum Target { + Pip, + Yarn { dev: bool }, + Npm { dev: bool }, +} + +impl Target { + fn language(self) -> ProgrammingLanguage { + match self { + Target::Pip => ProgrammingLanguage::PYTHON, + Target::Yarn { .. } | Target::Npm { .. } => ProgrammingLanguage::JAVASCRIPT, + } + } + + /// Construct a command which we can run to add packages. + /// + /// This deliberately runs the command using the OS shell instead of + /// invoking the tool directly. That way we can handle when a version + /// manager (e.g. `nvm` or `asdf`) replaces the tool with a script (e.g. + /// `npm.cmd` or `yarn.ps1`). + /// + /// See for more. + fn command(self, packages: &[Bindings]) -> Command { + let command_line = match self { + Target::Pip => "pip install", + Target::Yarn { dev: true } => "yarn add --dev", + Target::Yarn { dev: false } => "yarn add", + Target::Npm { dev: true } => "npm install --dev", + Target::Npm { dev: false } => "npm install", + }; + let mut command_line = command_line.to_string(); + + for pkg in packages { + command_line.push(' '); + command_line.push_str(&pkg.url); + } + + if cfg!(windows) { + let mut cmd = Command::new("cmd"); + cmd.arg("/C").arg(command_line); + cmd + } else { + let mut cmd = Command::new("sh"); + cmd.arg("-c").arg(command_line); + cmd + } + } +} diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 2b5dad57bb9..ab019ae6d28 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -845,7 +845,7 @@ pub(crate) fn try_run_package_or_file( let package = format!("{}", r.path.display()); let mut is_fake_sv = false; - let mut sv = match SplitVersion::new(&package) { + let mut sv = match SplitVersion::parse(&package) { Ok(o) => o, Err(_) => { let mut fake_sv = SplitVersion { diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index 25ea4e0dd80..359c8723813 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -23,4 +23,5 @@ wapm-toml = "0.2.0" tar = "0.4.38" flate2 = "1.0.24" semver = "1.0.14" -lzma-rs = "0.2.0" \ No newline at end of file +lzma-rs = "0.2.0" +log = "0.4.17" diff --git a/lib/registry/graphql/queries/get_bindings.graphql b/lib/registry/graphql/queries/get_bindings.graphql new file mode 100644 index 00000000000..bb4d8f96603 --- /dev/null +++ b/lib/registry/graphql/queries/get_bindings.graphql @@ -0,0 +1,22 @@ +query GetBindingsQuery ($name: String!, $version: String = "latest") { + packageVersion: getPackageVersion(name:$name, version:$version) { + bindings { + id + language + url + + generator { + packageVersion { + id + version + package { + name + } + } + commandName + } + + __typename + } + } +} diff --git a/lib/registry/graphql/schema.graphql b/lib/registry/graphql/schema.graphql index 07687311d1c..c79035d9663 100644 --- a/lib/registry/graphql/schema.graphql +++ b/lib/registry/graphql/schema.graphql @@ -1,1401 +1,1602 @@ -type APIToken { - createdAt: DateTime! +interface Node { + """The ID of the object""" id: ID! - identifier: String - lastUsedAt: DateTime - revokedAt: DateTime - user: User! } -type APITokenConnection { - # Contains the nodes in this connection. - edges: [APITokenEdge]! - - # Pagination data for this connection. - pageInfo: PageInfo! +type PublicKey implements Node { + """The ID of the object""" + id: ID! + owner: User! + keyId: String! + key: String! + revokedAt: DateTime + uploadedAt: DateTime! + verifyingSignature: Signature + revoked: Boolean! } -# A Relay edge containing a `APIToken` and its cursor. -type APITokenEdge { - # A cursor for use in pagination - cursor: String! +type User implements Node & PackageOwner { + """Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.""" + username: String! + firstName: String! + lastName: String! + email: String! + dateJoined: DateTime! + isEmailValidated: Boolean! + bio: String + location: String + websiteUrl: String - # The item at the end of the edge - node: APIToken + """The ID of the object""" + id: ID! + globalName: String! + avatar(size: Int = 80): String! + isViewer: Boolean! + hasUsablePassword: Boolean + fullName: String! + githubUrl: String + twitterUrl: String + publicActivity(before: String, after: String, first: Int, last: Int): ActivityEventConnection! + namespaces(before: String, after: String, first: Int, last: Int): NamespaceConnection! + packages(collaborating: Boolean = false, before: String, after: String, first: Int, last: Int): PackageConnection! + packageVersions(before: String, after: String, first: Int, last: Int): PackageVersionConnection! + packageTransfersIncoming(before: String, after: String, first: Int, last: Int): PackageTransferRequestConnection! + packageInvitesIncoming(before: String, after: String, first: Int, last: Int): PackageCollaboratorInviteConnection! + namespaceInvitesIncoming(before: String, after: String, first: Int, last: Int): NamespaceCollaboratorInviteConnection! + apiTokens(before: String, after: String, first: Int, last: Int): APITokenConnection! + notifications(before: String, after: String, first: Int, last: Int): UserNotificationConnection! } -input AcceptNamespaceCollaboratorInviteInput { - clientMutationId: String - inviteId: ID! +interface PackageOwner { + globalName: String! } -type AcceptNamespaceCollaboratorInvitePayload { - clientMutationId: String - namespaceCollaboratorInvite: NamespaceCollaboratorInvite! -} +""" +The `DateTime` scalar type represents a DateTime +value as specified by +[iso8601](https://en.wikipedia.org/wiki/ISO_8601). +""" +scalar DateTime -input AcceptPackageCollaboratorInviteInput { - clientMutationId: String - inviteId: ID! -} +type ActivityEventConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! -type AcceptPackageCollaboratorInvitePayload { - clientMutationId: String - packageCollaboratorInvite: PackageCollaboratorInvite! + """Contains the nodes in this connection.""" + edges: [ActivityEventEdge]! } -input AcceptPackageTransferRequestInput { - clientMutationId: String - packageTransferRequestId: ID! +""" +The Relay compliant `PageInfo` type, containing data necessary to paginate this connection. +""" +type PageInfo { + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String } -type AcceptPackageTransferRequestPayload { - clientMutationId: String - package: Package! - packageTransferRequest: PackageTransferRequest! +"""A Relay edge containing a `ActivityEvent` and its cursor.""" +type ActivityEventEdge { + """The item at the end of the edge""" + node: ActivityEvent + + """A cursor for use in pagination""" + cursor: String! } type ActivityEvent implements Node { - actorIcon: String! + """The ID of the object""" + id: ID! body: ActivityEventBody! + actorIcon: String! createdAt: DateTime! - - # The ID of the object - id: ID! } type ActivityEventBody { - ranges: [NodeBodyRange!]! text: String! + ranges: [NodeBodyRange!]! } -type ActivityEventConnection { - # Contains the nodes in this connection. - edges: [ActivityEventEdge]! +type NodeBodyRange { + entity: Node! + offset: Int! + length: Int! +} - # Pagination data for this connection. +type NamespaceConnection { + """Pagination data for this connection.""" pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [NamespaceEdge]! } -# A Relay edge containing a `ActivityEvent` and its cursor. -type ActivityEventEdge { - # A cursor for use in pagination - cursor: String! +"""A Relay edge containing a `Namespace` and its cursor.""" +type NamespaceEdge { + """The item at the end of the edge""" + node: Namespace - # The item at the end of the edge - node: ActivityEvent + """A cursor for use in pagination""" + cursor: String! } -input ArchivePackageInput { - clientMutationId: String - packageId: ID! +type Namespace implements Node & PackageOwner { + """The ID of the object""" + id: ID! + name: String! + displayName: String + description: String! + avatar: String! + avatarUpdatedAt: DateTime + createdAt: DateTime! + updatedAt: DateTime! + maintainerInvites(offset: Int, before: String, after: String, first: Int, last: Int): NamespaceCollaboratorInviteConnection! + userSet(offset: Int, before: String, after: String, first: Int, last: Int): UserConnection! + globalName: String! + packages(before: String, after: String, first: Int, last: Int): PackageConnection! + packageVersions(before: String, after: String, first: Int, last: Int): PackageVersionConnection! + collaborators(before: String, after: String, first: Int, last: Int): NamespaceCollaboratorConnection! + publicActivity(before: String, after: String, first: Int, last: Int): ActivityEventConnection! + pendingInvites(before: String, after: String, first: Int, last: Int): NamespaceCollaboratorInviteConnection! + viewerHasRole(role: Role!): Boolean! + packageTransfersIncoming(before: String, after: String, first: Int, last: Int): PackageTransferRequestConnection! } -type ArchivePackagePayload { - clientMutationId: String - package: Package! -} +type NamespaceCollaboratorInviteConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! -input ChangePackageVersionArchivedStatusInput { - clientMutationId: String - isArchived: Boolean - packageVersionId: ID! + """Contains the nodes in this connection.""" + edges: [NamespaceCollaboratorInviteEdge]! } -type ChangePackageVersionArchivedStatusPayload { - clientMutationId: String - packageVersion: PackageVersion! -} +""" +A Relay edge containing a `NamespaceCollaboratorInvite` and its cursor. +""" +type NamespaceCollaboratorInviteEdge { + """The item at the end of the edge""" + node: NamespaceCollaboratorInvite -input ChangeUserEmailInput { - clientMutationId: String - newEmail: String! + """A cursor for use in pagination""" + cursor: String! } -type ChangeUserEmailPayload { - clientMutationId: String - user: User! +type NamespaceCollaboratorInvite implements Node { + """The ID of the object""" + id: ID! + requestedBy: User! + user: User + inviteEmail: String + namespace: Namespace! + role: RegistryNamespaceMaintainerInviteRoleChoices! + accepted: NamespaceCollaborator + approvedBy: User + declinedBy: User + createdAt: DateTime! + expiresAt: DateTime! + closedAt: DateTime } -input ChangeUserPasswordInput { - clientMutationId: String - password: String! +"""An enumeration.""" +enum RegistryNamespaceMaintainerInviteRoleChoices { + """Admin""" + ADMIN - # The token associated to change the password. If not existing it will use the request user by default - token: String + """Editor""" + EDITOR + + """Viewer""" + VIEWER } -type ChangeUserPasswordPayload { - clientMutationId: String - token: String +type NamespaceCollaborator implements Node { + """The ID of the object""" + id: ID! + user: User! + role: RegistryNamespaceMaintainerRoleChoices! + namespace: Namespace! + createdAt: DateTime! + updatedAt: DateTime! + invite: NamespaceCollaboratorInvite } -input ChangeUserUsernameInput { - clientMutationId: String +"""An enumeration.""" +enum RegistryNamespaceMaintainerRoleChoices { + """Admin""" + ADMIN - # The new user username - username: String! -} + """Editor""" + EDITOR -type ChangeUserUsernamePayload { - clientMutationId: String - token: String - user: User + """Viewer""" + VIEWER } -input CheckUserExistsInput { - clientMutationId: String +type UserConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! - # The user - user: String! + """Contains the nodes in this connection.""" + edges: [UserEdge]! } -type CheckUserExistsPayload { - clientMutationId: String - exists: Boolean! +"""A Relay edge containing a `User` and its cursor.""" +type UserEdge { + """The item at the end of the edge""" + node: User - # The user is only returned if the user input was the username - user: User + """A cursor for use in pagination""" + cursor: String! } -type Command { - command: String! - module: PackageVersionModule! - packageVersion: PackageVersion! +type PackageConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageEdge]! } -input CreateNamespaceInput { - # The namespace avatar - avatar: String - clientMutationId: String +"""A Relay edge containing a `Package` and its cursor.""" +type PackageEdge { + """The item at the end of the edge""" + node: Package - # The namespace description - description: String + """A cursor for use in pagination""" + cursor: String! +} - # The namespace display name - displayName: String +type Package implements Likeable & Node & PackageOwner { + """The ID of the object""" + id: ID! name: String! -} + namespace: String + private: Boolean! + createdAt: DateTime! + updatedAt: DateTime! + maintainers: [User]! @deprecated(reason: "Please use collaborators instead") + curated: Boolean! + ownerObjectId: Int! + lastVersion: PackageVersion -type CreateNamespacePayload { - clientMutationId: String - namespace: Namespace! - user: User! -} + """The app icon. It should be formatted in the same way as Apple icons""" + icon: String! + totalDownloads: Int! + iconUpdatedAt: DateTime + watchersCount: Int! + versions: [PackageVersion]! + collectionSet: [Collection!]! + likersCount: Int! + viewerHasLiked: Boolean! + globalName: String! + alias: String + displayName: String! -# The `DateTime` scalar type represents a DateTime -# value as specified by -# [iso8601](https://en.wikipedia.org/wiki/ISO_8601). -scalar DateTime + """The name of the package without the owner""" + packageName: String! -input DeleteNamespaceInput { - clientMutationId: String - namespaceId: ID! -} + """The app icon. It should be formatted in the same way as Apple icons""" + appIcon: String! @deprecated(reason: "Please use icon instead") -type DeleteNamespacePayload { - clientMutationId: String - success: Boolean! -} + """The total number of downloads of the package""" + downloadsCount: Int -type ErrorType { - field: String! - messages: [String!]! + """The public keys for all the published versions""" + publicKeys: [PublicKey!]! + collaborators(before: String, after: String, first: Int, last: Int): PackageCollaboratorConnection! + pendingInvites(before: String, after: String, first: Int, last: Int): PackageCollaboratorInviteConnection! + viewerHasRole(role: Role!): Boolean! + owner: PackageOwner! + isTransferring: Boolean! + activeTransferRequest: PackageTransferRequest + isArchived: Boolean! + viewerIsWatching: Boolean! } -input GenerateAPITokenInput { - clientMutationId: String - identifier: String +interface Likeable { + id: ID! + likersCount: Int! + viewerHasLiked: Boolean! } -type GenerateAPITokenPayload { - clientMutationId: String - token: APIToken - tokenRaw: String - user: User +type PackageVersion implements Node { + """The ID of the object""" + id: ID! + package: Package! + version: String! + description: String! + manifest: String! + license: String + licenseFile: String + readme: String + witMd: String + repository: String + homepage: String + createdAt: DateTime! + updatedAt: DateTime! + staticObjectsCompiled: Boolean! + nativeExecutablesCompiled: Boolean! + publishedBy: User! + signature: Signature + isArchived: Boolean! + file: String! + + """""" + fileSize: BigInt! + piritaFile: String + + """""" + piritaFileSize: BigInt! + piritaManifest: JSONString + piritaVolumes: JSONString + totalDownloads: Int! + hasBindings: Boolean! + lastversionPackage(offset: Int, before: String, after: String, first: Int, last: Int): PackageConnection! + commands: [Command!]! + nativeexecutableSet(offset: Int, before: String, after: String, first: Int, last: Int): NativeExecutableConnection! + bindingsgeneratorSet(offset: Int, before: String, after: String, first: Int, last: Int): BindingsGeneratorConnection! + javascriptlanguagebindingSet(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionNPMBindingConnection! + pythonlanguagebindingSet(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionPythonBindingConnection! + distribution: PackageDistribution! + filesystem: [PackageVersionFilesystem]! + isLastVersion: Boolean! + witFile: String + isSigned: Boolean! + moduleInterfaces: [InterfaceVersion!]! + modules: [PackageVersionModule!]! + getPiritaContents(volume: String! = "atom", root: String! = ""): [PiritaFilesystemItem!]! + nativeExecutables(triple: String, wasmerCompilerVersion: String): [NativeExecutable] + bindings: [PackageVersionLanguageBinding]! + npmBindings: PackageVersionNPMBinding + pythonBindings: PackageVersionPythonBinding } -# The `GenericScalar` scalar type represents a generic -# GraphQL scalar value that could be: -# String, Boolean, Int, Float, List or Object. -scalar GenericScalar +""" +The `BigInt` scalar type represents non-fractional whole numeric values. +`BigInt` is not constrained to 32-bit like the `Int` type and thus is a less +compatible type. +""" +scalar BigInt -type GetPasswordResetToken { - user: User - valid: Boolean! -} +""" +Allows use of a JSON String for input / output from the GraphQL schema. -union GlobalObject = Namespace | User +Use of this type is *not recommended* as you lose the benefits of having a defined, static +schema (one of the key benefits of GraphQL). +""" +scalar JSONString -input InputSignature { - data: String! - publicKeyKeyId: String! +type Command { + command: String! + packageVersion: PackageVersion! + module: PackageVersionModule! } -type Interface implements Node { - createdAt: DateTime! - description: String! - displayName: String! - homepage: String - icon: String - - # The ID of the object - id: ID! - lastVersion: InterfaceVersion +type PackageVersionModule { name: String! - updatedAt: DateTime! - versions(after: String = null, before: String = null, first: Int = null, last: Int = null, offset: Int = null): InterfaceVersionConnection! + source: String! + abi: String + publicUrl: String! } -type InterfaceVersion implements Node { - content: String! - createdAt: DateTime! +type NativeExecutableConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! - # The ID of the object - id: ID! - interface: Interface! - packageVersions(after: String = null, before: String = null, first: Int = null, last: Int = null, offset: Int = null): PackageVersionConnection! - publishedBy: User! - updatedAt: DateTime! - version: String! + """Contains the nodes in this connection.""" + edges: [NativeExecutableEdge]! } -type InterfaceVersionConnection { - # Contains the nodes in this connection. - edges: [InterfaceVersionEdge]! - - # Pagination data for this connection. - pageInfo: PageInfo! -} +"""A Relay edge containing a `NativeExecutable` and its cursor.""" +type NativeExecutableEdge { + """The item at the end of the edge""" + node: NativeExecutable -# A Relay edge containing a `InterfaceVersion` and its cursor. -type InterfaceVersionEdge { - # A cursor for use in pagination + """A cursor for use in pagination""" cursor: String! - - # The item at the end of the edge - node: InterfaceVersion -} - -input InviteNamespaceCollaboratorInput { - clientMutationId: String - email: String - namespaceId: ID! - role: Role! - username: String } -type InviteNamespaceCollaboratorPayload { - clientMutationId: String - invite: NamespaceCollaboratorInvite! - namespace: Namespace! +type NativeExecutable implements Node { + """The ID of the object""" + id: ID! + module: String! @deprecated(reason: "Use filename instead") + filename: String! + targetTriple: String! + downloadUrl: String! } -input InvitePackageCollaboratorInput { - clientMutationId: String - email: String - packageName: String! - role: Role! - username: String -} +type BindingsGeneratorConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! -type InvitePackageCollaboratorPayload { - clientMutationId: String - invite: PackageCollaboratorInvite! - package: Package! + """Contains the nodes in this connection.""" + edges: [BindingsGeneratorEdge]! } -input LikePackageInput { - clientMutationId: String - packageId: ID! -} +"""A Relay edge containing a `BindingsGenerator` and its cursor.""" +type BindingsGeneratorEdge { + """The item at the end of the edge""" + node: BindingsGenerator -type LikePackagePayload { - clientMutationId: String - package: Package! + """A cursor for use in pagination""" + cursor: String! } -interface Likeable { +type BindingsGenerator implements Node { + """The ID of the object""" id: ID! - likersCount: Int! - viewerHasLiked: Boolean! + packageVersion: PackageVersion! + commandName: String! + registryJavascriptlanguagebindings(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionNPMBindingConnection! + registryPythonlanguagebindings(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionPythonBindingConnection! } -type Mutation { - acceptNamespaceCollaboratorInvite(input: AcceptNamespaceCollaboratorInviteInput!): AcceptNamespaceCollaboratorInvitePayload - acceptPackageCollaboratorInvite(input: AcceptPackageCollaboratorInviteInput!): AcceptPackageCollaboratorInvitePayload - acceptPackageTransferRequest(input: AcceptPackageTransferRequestInput!): AcceptPackageTransferRequestPayload - archivePackage(input: ArchivePackageInput!): ArchivePackagePayload - changePackageVersionArchivedStatus(input: ChangePackageVersionArchivedStatusInput!): ChangePackageVersionArchivedStatusPayload - changeUserEmail(input: ChangeUserEmailInput!): ChangeUserEmailPayload - changeUserPassword(input: ChangeUserPasswordInput!): ChangeUserPasswordPayload - changeUserUsername(input: ChangeUserUsernameInput!): ChangeUserUsernamePayload - checkUserExists(input: CheckUserExistsInput!): CheckUserExistsPayload - createNamespace(input: CreateNamespaceInput!): CreateNamespacePayload - deleteNamespace(input: DeleteNamespaceInput!): DeleteNamespacePayload - generateApiToken(input: GenerateAPITokenInput!): GenerateAPITokenPayload - inviteNamespaceCollaborator(input: InviteNamespaceCollaboratorInput!): InviteNamespaceCollaboratorPayload - invitePackageCollaborator(input: InvitePackageCollaboratorInput!): InvitePackageCollaboratorPayload - likePackage(input: LikePackageInput!): LikePackagePayload - publishPackage(input: PublishPackageInput!): PublishPackagePayload - publishPublicKey(input: PublishPublicKeyInput!): PublishPublicKeyPayload - readNotification(input: ReadNotificationInput!): ReadNotificationPayload - refreshToken(input: RefreshInput!): RefreshPayload - registerUser(input: RegisterUserInput!): RegisterUserPayload - removeNamespaceCollaborator(input: RemoveNamespaceCollaboratorInput!): RemoveNamespaceCollaboratorPayload - removeNamespaceCollaboratorInvite(input: RemoveNamespaceCollaboratorInviteInput!): RemoveNamespaceCollaboratorInvitePayload - removePackageCollaborator(input: RemovePackageCollaboratorInput!): RemovePackageCollaboratorPayload - removePackageCollaboratorInvite(input: RemovePackageCollaboratorInviteInput!): RemovePackageCollaboratorInvitePayload - removePackageTransferRequest(input: RemovePackageTransferRequestInput!): RemovePackageTransferRequestPayload - requestPackageTransfer(input: RequestPackageTransferInput!): RequestPackageTransferPayload - requestPasswordReset(input: RequestPasswordResetInput!): RequestPasswordResetPayload - requestValidationEmail(input: RequestValidationEmailInput!): RequestValidationEmailPayload - revokeApiToken(input: RevokeAPITokenInput!): RevokeAPITokenPayload - seePendingNotifications(input: SeePendingNotificationsInput!): SeePendingNotificationsPayload - - # Social Auth for JSON Web Token (JWT) - socialAuth(input: SocialAuthJWTInput!): SocialAuthJWTPayload +type PackageVersionNPMBindingConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! - # Obtain JSON Web Token mutation - tokenAuth(input: ObtainJSONWebTokenInput!): ObtainJSONWebTokenPayload - unlikePackage(input: UnlikePackageInput!): UnlikePackagePayload - unwatchPackage(input: UnwatchPackageInput!): UnwatchPackagePayload - updateNamespace(input: UpdateNamespaceInput!): UpdateNamespacePayload - updateNamespaceCollaboratorRole(input: UpdateNamespaceCollaboratorRoleInput!): UpdateNamespaceCollaboratorRolePayload - updatePackage(input: UpdatePackageInput!): UpdatePackagePayload - updatePackageCollaboratorRole(input: UpdatePackageCollaboratorRoleInput!): UpdatePackageCollaboratorRolePayload - updateUserInfo(input: UpdateUserInfoInput!): UpdateUserInfoPayload - validateUserEmail(input: ValidateUserEmailInput!): ValidateUserEmailPayload - validateUserPassword(input: ValidateUserPasswordInput!): ValidateUserPasswordPayload - verifyToken(input: VerifyInput!): VerifyPayload - watchPackage(input: WatchPackageInput!): WatchPackagePayload + """Contains the nodes in this connection.""" + edges: [PackageVersionNPMBindingEdge]! } -type Namespace implements Node & PackageOwner { - avatar: String! - avatarUpdatedAt: DateTime - collaborators(after: String = null, before: String = null, first: Int = null, last: Int = null): NamespaceCollaboratorConnection - createdAt: DateTime! - description: String! - displayName: String - globalName: String! +"""A Relay edge containing a `PackageVersionNPMBinding` and its cursor.""" +type PackageVersionNPMBindingEdge { + """The item at the end of the edge""" + node: PackageVersionNPMBinding - # The ID of the object - id: ID! - maintainerInvites: [NamespaceCollaboratorInvite!]! - maintainersWithRoles(after: String = null, before: String = null, first: Int = null, last: Int = null, offset: Int = null): NamespaceMaintainerConnection! - name: String! - packageVersions(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageVersionConnection - packages(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageConnection - pendingInvites(after: String = null, before: String = null, first: Int = null, last: Int = null): NamespaceCollaboratorInviteConnection - publicActivity(after: String = null, before: String = null, first: Int = null, last: Int = null): ActivityEventConnection! - updatedAt: DateTime! - userSet(after: String = null, before: String = null, first: Int = null, last: Int = null, offset: Int = null): UserConnection! - viewerHasRole(role: Role!): Boolean! + """A cursor for use in pagination""" + cursor: String! } -type NamespaceCollaborator { - createdAt: DateTime! +type PackageVersionNPMBinding implements PackageVersionLanguageBinding & Node { + """The ID of the object""" id: ID! - invite: NamespaceCollaboratorInvite - namespace: Namespace! - role: RegistryNamespaceMaintainerRoleChoices! - updatedAt: DateTime! - user: User! -} + language: ProgrammingLanguage! -type NamespaceCollaboratorConnection { - # Contains the nodes in this connection. - edges: [NamespaceCollaboratorEdge]! + """The URL of the generated artifacts on WAPM's CDN.""" + url: String! - # Pagination data for this connection. - pageInfo: PageInfo! -} + """When the binding was generated""" + createdAt: DateTime! -# A Relay edge containing a `NamespaceCollaborator` and its cursor. -type NamespaceCollaboratorEdge { - # A cursor for use in pagination - cursor: String! + """Package version used to generate this binding""" + generator: BindingsGenerator! + name: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + kind: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") - # The item at the end of the edge - node: NamespaceCollaborator + """Name of package source""" + packageName: String! + module: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + npmDefaultInstallPackageName(url: String): String! @deprecated(reason: "Please use packageName instead") } -type NamespaceCollaboratorInvite { - accepted: NamespaceMaintainer - approvedBy: User - closedAt: DateTime - createdAt: DateTime! - declinedBy: User - expiresAt: DateTime! +interface PackageVersionLanguageBinding { id: ID! - inviteEmail: String - namespace: Namespace! - requestedBy: User! - role: RegistryNamespaceMaintainerInviteRoleChoices! - user: User -} + language: ProgrammingLanguage! -type NamespaceCollaboratorInviteConnection { - # Contains the nodes in this connection. - edges: [NamespaceCollaboratorInviteEdge]! + """The URL of the generated artifacts on WAPM's CDN.""" + url: String! - # Pagination data for this connection. - pageInfo: PageInfo! -} + """When the binding was generated""" + createdAt: DateTime! -# A Relay edge containing a `NamespaceCollaboratorInvite` and its cursor. -type NamespaceCollaboratorInviteEdge { - # A cursor for use in pagination - cursor: String! + """Package version used to generate this binding""" + generator: BindingsGenerator! + name: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + kind: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") - # The item at the end of the edge - node: NamespaceCollaboratorInvite + """Name of package source""" + packageName: String! + module: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") } -type NamespaceConnection { - # Contains the nodes in this connection. - edges: [NamespaceEdge]! - - # Pagination data for this connection. - pageInfo: PageInfo! +enum ProgrammingLanguage { + PYTHON + JAVASCRIPT } -# A Relay edge containing a `Namespace` and its cursor. -type NamespaceEdge { - # A cursor for use in pagination - cursor: String! +type PackageVersionPythonBindingConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! - # The item at the end of the edge - node: Namespace + """Contains the nodes in this connection.""" + edges: [PackageVersionPythonBindingEdge]! } -type NamespaceMaintainer implements Node { - createdAt: DateTime! +""" +A Relay edge containing a `PackageVersionPythonBinding` and its cursor. +""" +type PackageVersionPythonBindingEdge { + """The item at the end of the edge""" + node: PackageVersionPythonBinding - # The ID of the object - id: ID! - invite: NamespaceCollaboratorInvite - namespace: Namespace! - role: RegistryNamespaceMaintainerRoleChoices! - updatedAt: DateTime! - user: User! + """A cursor for use in pagination""" + cursor: String! } -type NamespaceMaintainerConnection { - # Contains the nodes in this connection. - edges: [NamespaceMaintainerEdge]! +type PackageVersionPythonBinding implements PackageVersionLanguageBinding & Node { + """The ID of the object""" + id: ID! + language: ProgrammingLanguage! - # Pagination data for this connection. - pageInfo: PageInfo! -} + """The URL of the generated artifacts on WAPM's CDN.""" + url: String! -# A Relay edge containing a `NamespaceMaintainer` and its cursor. -type NamespaceMaintainerEdge { - # A cursor for use in pagination - cursor: String! + """When the binding was generated""" + createdAt: DateTime! - # The item at the end of the edge - node: NamespaceMaintainer -} + """Package version used to generate this binding""" + generator: BindingsGenerator! + name: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + kind: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") -# An object with an ID -interface Node { - # The ID of the object - id: ID! + """Name of package source""" + packageName: String! + module: String! @deprecated(reason: "Do not use this field, since bindings for all modules are generated at once now.") + pythonDefaultInstallPackageName(url: String): String! } -type NodeBodyRange { - entity: Node! - length: Int! - offset: Int! +type PackageDistribution { + downloadUrl: String! + size: Int! + piritaDownloadUrl: String + piritaSize: Int! } -input ObtainJSONWebTokenInput { - clientMutationId: String - password: String! - username: String! +type PackageVersionFilesystem { + wasm: String! + host: String! } -# Obtain JSON Web Token mutation -type ObtainJSONWebTokenPayload { - clientMutationId: String - payload: GenericScalar! - refreshExpiresIn: Int! - refreshToken: String! - token: String! +type InterfaceVersion implements Node { + """The ID of the object""" + id: ID! + interface: Interface! + version: String! + content: String! + createdAt: DateTime! + updatedAt: DateTime! + publishedBy: User! + packageVersions(offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionConnection! } -type Package implements Likeable & Node & PackageOwner { - alias: String - - # The app icon. It should be formatted in the same way as Apple icons - appIcon: String! @deprecated(reason: "Please use icon instead") - collaborators(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageCollaboratorConnection - createdAt: DateTime! - curated: Boolean! +type Interface implements Node { + """The ID of the object""" + id: ID! + name: String! displayName: String! + description: String! + homepage: String + icon: String + createdAt: DateTime! + updatedAt: DateTime! + versions(offset: Int, before: String, after: String, first: Int, last: Int): InterfaceVersionConnection! + lastVersion: InterfaceVersion +} - # The total number of downloads of the package - downloadsCount: Int - globalName: String! +type InterfaceVersionConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! - # The app icon. It should be formatted in the same way as Apple icons - icon: String! - iconUpdatedAt: DateTime + """Contains the nodes in this connection.""" + edges: [InterfaceVersionEdge]! +} - # The ID of the object - id: ID! - isTransferring: Boolean! - lastVersion: PackageVersion - likeCount: Int! - likersCount: Int! - maintainers: [User]! @deprecated(reason: "Please use collaborators instead") - name: String! - namespace: String - owner: PackageOwner - ownerObjectId: Int! +"""A Relay edge containing a `InterfaceVersion` and its cursor.""" +type InterfaceVersionEdge { + """The item at the end of the edge""" + node: InterfaceVersion - # The name of the package without the owner - packageName: String! - pendingInvites(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageCollaboratorInviteConnection - private: Boolean! + """A cursor for use in pagination""" + cursor: String! +} - # The public keys for all the published versions - publicKeys: [PublicKey!]! - updatedAt: DateTime! - versions: [PackageVersion] - viewerHasLiked: Boolean! - viewerHasRole(role: Role!): Boolean! - viewerIsWatching: Boolean! - watchCount: Int! +type PackageVersionConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageVersionEdge]! } -type PackageCollaborator implements Node { - createdAt: DateTime! +"""A Relay edge containing a `PackageVersion` and its cursor.""" +type PackageVersionEdge { + """The item at the end of the edge""" + node: PackageVersion - # The ID of the object - id: ID! - invite: PackageCollaboratorInvite - package: Package! - role: RegistryPackageMaintainerRoleChoices! - updatedAt: DateTime! - user: User! + """A cursor for use in pagination""" + cursor: String! } -type PackageCollaboratorConnection { - # Contains the nodes in this connection. - edges: [PackageCollaboratorEdge]! +union PiritaFilesystemItem = PiritaFilesystemFile | PiritaFilesystemDir + +type PiritaFilesystemFile { + name(display: PiritaFilesystemNameDisplay): String! + size: Int! + offset: Int! +} + +enum PiritaFilesystemNameDisplay { + RELATIVE + ABSOLUTE +} + +type PiritaFilesystemDir { + name(display: PiritaFilesystemNameDisplay): String! +} - # Pagination data for this connection. +type Collection { + slug: String! + displayName: String! + description: String! + createdAt: DateTime! + banner: String! + packages(before: String, after: String, first: Int, last: Int): PackageConnection! +} + +type PackageCollaboratorConnection { + """Pagination data for this connection.""" pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageCollaboratorEdge]! } -# A Relay edge containing a `PackageCollaborator` and its cursor. +"""A Relay edge containing a `PackageCollaborator` and its cursor.""" type PackageCollaboratorEdge { - # A cursor for use in pagination + """The item at the end of the edge""" + node: PackageCollaborator + + """A cursor for use in pagination""" cursor: String! +} - # The item at the end of the edge - node: PackageCollaborator +type PackageCollaborator implements Node { + """The ID of the object""" + id: ID! + user: User! + role: RegistryPackageMaintainerRoleChoices! + package: Package! + createdAt: DateTime! + updatedAt: DateTime! + invite: PackageCollaboratorInvite +} + +"""An enumeration.""" +enum RegistryPackageMaintainerRoleChoices { + """Admin""" + ADMIN + + """Editor""" + EDITOR + + """Viewer""" + VIEWER } type PackageCollaboratorInvite implements Node { + """The ID of the object""" + id: ID! + requestedBy: User! + user: User + inviteEmail: String + package: Package! + role: RegistryPackageMaintainerInviteRoleChoices! accepted: PackageCollaborator approvedBy: User - closedAt: DateTime - createdAt: DateTime! declinedBy: User + createdAt: DateTime! expiresAt: DateTime! + closedAt: DateTime +} - # The ID of the object - id: ID! - inviteEmail: String - package: Package! - requestedBy: User! - role: RegistryPackageMaintainerInviteRoleChoices! - user: User +"""An enumeration.""" +enum RegistryPackageMaintainerInviteRoleChoices { + """Admin""" + ADMIN + + """Editor""" + EDITOR + + """Viewer""" + VIEWER } type PackageCollaboratorInviteConnection { - # Contains the nodes in this connection. - edges: [PackageCollaboratorInviteEdge]! - - # Pagination data for this connection. + """Pagination data for this connection.""" pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageCollaboratorInviteEdge]! } -# A Relay edge containing a `PackageCollaboratorInvite` and its cursor. +"""A Relay edge containing a `PackageCollaboratorInvite` and its cursor.""" type PackageCollaboratorInviteEdge { - # A cursor for use in pagination + """The item at the end of the edge""" + node: PackageCollaboratorInvite + + """A cursor for use in pagination""" cursor: String! +} - # The item at the end of the edge - node: PackageCollaboratorInvite +enum Role { + ADMIN + EDITOR + VIEWER } -type PackageConnection { - # Contains the nodes in this connection. - edges: [PackageEdge]! +type PackageTransferRequest implements Node { + """The ID of the object""" + id: ID! + requestedBy: User! + previousOwnerObjectId: Int! + newOwnerObjectId: Int! + package: Package! + approvedBy: User + declinedBy: User + createdAt: DateTime! + expiresAt: DateTime! + closedAt: DateTime + previousOwner: PackageOwner! + newOwner: PackageOwner! +} - # Pagination data for this connection. +type NamespaceCollaboratorConnection { + """Pagination data for this connection.""" pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [NamespaceCollaboratorEdge]! } -type PackageDistribution { - downloadUrl: String! - size: Int! +"""A Relay edge containing a `NamespaceCollaborator` and its cursor.""" +type NamespaceCollaboratorEdge { + """The item at the end of the edge""" + node: NamespaceCollaborator + + """A cursor for use in pagination""" + cursor: String! } -# A Relay edge containing a `Package` and its cursor. -type PackageEdge { - # A cursor for use in pagination +type PackageTransferRequestConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [PackageTransferRequestEdge]! +} + +"""A Relay edge containing a `PackageTransferRequest` and its cursor.""" +type PackageTransferRequestEdge { + """The item at the end of the edge""" + node: PackageTransferRequest + + """A cursor for use in pagination""" cursor: String! +} - # The item at the end of the edge - node: Package +type APITokenConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [APITokenEdge]! } -interface PackageOwner { - globalName: String! +"""A Relay edge containing a `APIToken` and its cursor.""" +type APITokenEdge { + """The item at the end of the edge""" + node: APIToken + + """A cursor for use in pagination""" + cursor: String! } -type PackageTransferRequest implements Node { - approvedBy: User - closedAt: DateTime +type APIToken { + id: ID! + user: User! + identifier: String createdAt: DateTime! - declinedBy: User - expiresAt: DateTime! + revokedAt: DateTime + lastUsedAt: DateTime +} - # The ID of the object +type UserNotificationConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [UserNotificationEdge]! + hasPendingNotifications: Boolean! +} + +"""A Relay edge containing a `UserNotification` and its cursor.""" +type UserNotificationEdge { + """The item at the end of the edge""" + node: UserNotification + + """A cursor for use in pagination""" + cursor: String! +} + +type UserNotification implements Node { + """The ID of the object""" id: ID! - newOwnerObjectId: Int! - package: Package! - previousOwnerObjectId: Int! - requestedBy: User! + icon: String + body: UserNotificationBody! + seenState: UserNotificationSeenState! + kind: UserNotificationKind + createdAt: DateTime! } -type PackageTransferRequestConnection { - # Contains the nodes in this connection. - edges: [PackageTransferRequestEdge]! +type UserNotificationBody { + text: String! + ranges: [NodeBodyRange]! +} - # Pagination data for this connection. - pageInfo: PageInfo! +enum UserNotificationSeenState { + UNSEEN + SEEN + SEEN_AND_READ } -# A Relay edge containing a `PackageTransferRequest` and its cursor. -type PackageTransferRequestEdge { - # A cursor for use in pagination - cursor: String! +union UserNotificationKind = UserNotificationKindPublishedPackageVersion | UserNotificationKindIncomingPackageTransfer | UserNotificationKindIncomingPackageInvite | UserNotificationKindIncomingNamespaceInvite - # The item at the end of the edge - node: PackageTransferRequest +type UserNotificationKindPublishedPackageVersion { + packageVersion: PackageVersion! } -type PackageVersion implements Node { - bindings: [PackageVersionBinding]! - commands: [Command!]! - createdAt: DateTime! - description: String! - distribution: PackageDistribution! - file: String! - fileSize: Int! - filesystem: [PackageVersionFilesystem]! - homepage: String +type UserNotificationKindIncomingNamespaceInvite { + namespaceInvite: NamespaceCollaboratorInvite! +} - # The ID of the object +type Signature { id: ID! - isArchived: Boolean! - isLastVersion: Boolean! - isSigned: Boolean! - license: String - licenseFile: String - manifest: String! - moduleInterfaces: [InterfaceVersion!]! - modules: [PackageVersionModule!]! - package: Package! - publishedBy: User! - readme: String - repository: String - signature: Signature - updatedAt: DateTime! - version: String! + publicKey: PublicKey! + data: String! + createdAt: DateTime! } -interface PackageVersionBinding { - # The module these bindings are associated with. - module: String! +type UserNotificationKindIncomingPackageTransfer { + packageTransferRequest: PackageTransferRequest! } -type PackageVersionNPMBinding implements PackageVersionBinding { - npmDefaultInstallPackageName: String! +type UserNotificationKindIncomingPackageInvite { + packageInvite: PackageCollaboratorInvite! } -type PackageVersionPythonBinding implements PackageVersionBinding { - pythonDefaultInstallPackageName: String! +type Query { + viewer: User + getUser(username: String!): User + getPasswordResetToken(token: String!): GetPasswordResetToken + packages(before: String, after: String, first: Int, last: Int): PackageConnection + recentPackageVersions(curated: Boolean, offset: Int, before: String, after: String, first: Int, last: Int): PackageVersionConnection! + getNamespace(name: String!): Namespace + getPackage(name: String!): Package + getPackages(names: [String!]!): [Package]! + getPackageVersion(name: String!, version: String = "latest"): PackageVersion + getPackageVersions(names: [String!]!): [PackageVersion] + getInterface(name: String!): Interface + getInterfaces(names: [String!]!): [Interface]! + getInterfaceVersion(name: String!, version: String = "latest"): InterfaceVersion + getContract(name: String!): Interface @deprecated(reason: "Please use getInterface instead") + getContracts(names: [String!]!): [Interface]! @deprecated(reason: "Please use getInterfaces instead") + getContractVersion(name: String!, version: String): InterfaceVersion @deprecated(reason: "Please use getInterfaceVersion instead") + getCommand(name: String!): Command + getCommands(names: [String!]!): [Command] + getCollections(before: String, after: String, first: Int, last: Int): CollectionConnection + getSignedUrlForPackageUpload(name: String!, version: String = "latest", expiresAfterSeconds: Int = 60): SignedUrl + search(query: String!, curated: Boolean, orderBy: SearchOrderBy, sort: SearchOrderSort, kind: [SearchKind!], publishDate: SearchPublishDate, hasBindings: Boolean, isStandalone: Boolean, withInterfaces: [String!], before: String, after: String, first: Int, last: Int): SearchConnection! + searchAutocomplete(kind: [SearchKind!], query: String!, before: String, after: String, first: Int, last: Int): SearchConnection! + getGlobalObject(slug: String!): GlobalObject + node( + """The ID of the object""" + id: ID! + ): Node } -type PackageVersionConnection { - # Contains the nodes in this connection. - edges: [PackageVersionEdge]! +type GetPasswordResetToken { + valid: Boolean! + user: User +} - # Pagination data for this connection. +type CollectionConnection { + """Pagination data for this connection.""" pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [CollectionEdge]! } -# A Relay edge containing a `PackageVersion` and its cursor. -type PackageVersionEdge { - # A cursor for use in pagination +"""A Relay edge containing a `Collection` and its cursor.""" +type CollectionEdge { + """The item at the end of the edge""" + node: Collection + + """A cursor for use in pagination""" cursor: String! +} - # The item at the end of the edge - node: PackageVersion +type SignedUrl { + url: String! } -type PackageVersionFilesystem { - host: String! - wasm: String! +type SearchConnection { + """Pagination data for this connection.""" + pageInfo: PageInfo! + + """Contains the nodes in this connection.""" + edges: [SearchEdge]! } -type PackageVersionModule { - abi: String - name: String! - publicUrl: String! - source: String! +"""A Relay edge containing a `Search` and its cursor.""" +type SearchEdge { + """The item at the end of the edge""" + node: SearchResult + + """A cursor for use in pagination""" + cursor: String! } -# The Relay compliant `PageInfo` type, containing data necessary to paginate this connection. -type PageInfo { - # When paginating forwards, the cursor to continue. - endCursor: String +union SearchResult = PackageVersion | User | Namespace - # When paginating forwards, are there more items? - hasNextPage: Boolean! +enum SearchOrderBy { + ALPHABETICALLY + SIZE + TOTAL_DOWNLOADS + PUBLISHED_DATE +} - # When paginating backwards, are there more items? - hasPreviousPage: Boolean! +enum SearchOrderSort { + ASC + DESC +} - # When paginating backwards, the cursor to continue. - startCursor: String +enum SearchKind { + PACKAGE + NAMESPACE + USER } -type PublicKey implements Node { - # The ID of the object - id: ID! - key: String! - keyId: String! - owner: User! - revoked: Boolean! - revokedAt: DateTime - uploadedAt: DateTime! - verifyingSignature: Signature +enum SearchPublishDate { + LAST_DAY + LAST_WEEK + LAST_MONTH + LAST_YEAR } -input PublishPackageInput { +union GlobalObject = User | Namespace + +type Mutation { + tokenAuth(input: ObtainJSONWebTokenInput!): ObtainJSONWebTokenPayload + registerUser(input: RegisterUserInput!): RegisterUserPayload + socialAuth(input: SocialAuthJWTInput!): SocialAuthJWTPayload + validateUserEmail(input: ValidateUserEmailInput!): ValidateUserEmailPayload + requestPasswordReset(input: RequestPasswordResetInput!): RequestPasswordResetPayload + requestValidationEmail(input: RequestValidationEmailInput!): RequestValidationEmailPayload + changeUserPassword(input: ChangeUserPasswordInput!): ChangeUserPasswordPayload + changeUserUsername(input: ChangeUserUsernameInput!): ChangeUserUsernamePayload + changeUserEmail(input: ChangeUserEmailInput!): ChangeUserEmailPayload + updateUserInfo(input: UpdateUserInfoInput!): UpdateUserInfoPayload + validateUserPassword(input: ValidateUserPasswordInput!): ValidateUserPasswordPayload + generateApiToken(input: GenerateAPITokenInput!): GenerateAPITokenPayload + revokeApiToken(input: RevokeAPITokenInput!): RevokeAPITokenPayload + checkUserExists(input: CheckUserExistsInput!): CheckUserExistsPayload + readNotification(input: ReadNotificationInput!): ReadNotificationPayload + seePendingNotifications(input: SeePendingNotificationsInput!): SeePendingNotificationsPayload + publishPublicKey(input: PublishPublicKeyInput!): PublishPublicKeyPayload + publishPackage(input: PublishPackageInput!): PublishPackagePayload + updatePackage(input: UpdatePackageInput!): UpdatePackagePayload + likePackage(input: LikePackageInput!): LikePackagePayload + unlikePackage(input: UnlikePackageInput!): UnlikePackagePayload + watchPackage(input: WatchPackageInput!): WatchPackagePayload + unwatchPackage(input: UnwatchPackageInput!): UnwatchPackagePayload + archivePackage(input: ArchivePackageInput!): ArchivePackagePayload + changePackageVersionArchivedStatus(input: ChangePackageVersionArchivedStatusInput!): ChangePackageVersionArchivedStatusPayload + createNamespace(input: CreateNamespaceInput!): CreateNamespacePayload + updateNamespace(input: UpdateNamespaceInput!): UpdateNamespacePayload + deleteNamespace(input: DeleteNamespaceInput!): DeleteNamespacePayload + inviteNamespaceCollaborator(input: InviteNamespaceCollaboratorInput!): InviteNamespaceCollaboratorPayload + acceptNamespaceCollaboratorInvite(input: AcceptNamespaceCollaboratorInviteInput!): AcceptNamespaceCollaboratorInvitePayload + removeNamespaceCollaboratorInvite(input: RemoveNamespaceCollaboratorInviteInput!): RemoveNamespaceCollaboratorInvitePayload + removeNamespaceCollaborator(input: RemoveNamespaceCollaboratorInput!): RemoveNamespaceCollaboratorPayload + updateNamespaceCollaboratorRole(input: UpdateNamespaceCollaboratorRoleInput!): UpdateNamespaceCollaboratorRolePayload + updateNamespaceCollaboratorInviteRole(input: UpdateNamespaceCollaboratorInviteRoleInput!): UpdateNamespaceCollaboratorInviteRolePayload + invitePackageCollaborator(input: InvitePackageCollaboratorInput!): InvitePackageCollaboratorPayload + acceptPackageCollaboratorInvite(input: AcceptPackageCollaboratorInviteInput!): AcceptPackageCollaboratorInvitePayload + removePackageCollaboratorInvite(input: RemovePackageCollaboratorInviteInput!): RemovePackageCollaboratorInvitePayload + updatePackageCollaboratorRole(input: UpdatePackageCollaboratorRoleInput!): UpdatePackageCollaboratorRolePayload + updatePackageCollaboratorInviteRole(input: UpdatePackageCollaboratorInviteRoleInput!): UpdatePackageCollaboratorInviteRolePayload + removePackageCollaborator(input: RemovePackageCollaboratorInput!): RemovePackageCollaboratorPayload + requestPackageTransfer(input: RequestPackageTransferInput!): RequestPackageTransferPayload + acceptPackageTransferRequest(input: AcceptPackageTransferRequestInput!): AcceptPackageTransferRequestPayload + removePackageTransferRequest(input: RemovePackageTransferRequestInput!): RemovePackageTransferRequestPayload +} + +type ObtainJSONWebTokenPayload { + payload: GenericScalar! + refreshExpiresIn: Int! clientMutationId: String - description: String! - file: String - homepage: String + token: String! + refreshToken: String! +} - # The package icon - icon: String - license: String - licenseFile: String - manifest: String! - name: String! - readme: String - repository: String - signature: InputSignature - version: String! +""" +The `GenericScalar` scalar type represents a generic +GraphQL scalar value that could be: +String, Boolean, Int, Float, List or Object. +""" +scalar GenericScalar + +input ObtainJSONWebTokenInput { + clientMutationId: String + username: String! + password: String! } -type PublishPackagePayload { +type RegisterUserPayload { + token: String clientMutationId: String - packageVersion: PackageVersion! - success: Boolean! } -input PublishPublicKeyInput { +input RegisterUserInput { + fullName: String! + email: String! + username: String! + password: String! clientMutationId: String - key: String! - keyId: String! - verifyingSignatureId: String } -type PublishPublicKeyPayload { +type SocialAuthJWTPayload { + social: SocialAuth + token: String clientMutationId: String - publicKey: PublicKey! - success: Boolean! } -type SignedUrl { - url: String! +type SocialAuth implements Node { + """The ID of the object""" + id: ID! + user: User! + provider: String! + uid: String! + extraData: String! + created: DateTime! + modified: DateTime! } -type Query { - getCommand(name: String!): Command - getCommands(names: [String!]!): [Command] - getContract(name: String!): Interface @deprecated(reason: "Please use getInterface instead") - getContractVersion(name: String!, version: String = null): InterfaceVersion @deprecated(reason: "Please use getInterfaceVersion instead") - getContracts(names: [String!]!): [Interface]! @deprecated(reason: "Please use getInterfaces instead") - getGlobalObject(slug: String!): GlobalObject - getInterface(name: String!): Interface - getInterfaceVersion(name: String!, version: String = "latest"): InterfaceVersion - getInterfaces(names: [String!]!): [Interface]! - getNamespace(name: String!): Namespace - getPackage(name: String!): Package - getPackageVersion(name: String!, version: String = "latest"): PackageVersion - getPackageVersions(names: [String!]!): [PackageVersion] - getPackages(names: [String!]!): [Package]! - getPasswordResetToken(token: String!): GetPasswordResetToken - getSignedUrlForPackageUpload(name:String!,version:String!): SignedUrl - getUser(username: String!): User - node( - # The ID of the object - id: ID! - ): Node - packages(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageConnection - recentPackageVersions(after: String = null, before: String = null, curated: Boolean = null, first: Int = null, last: Int = null, offset: Int = null): PackageVersionConnection - search(after: String = null, before: String = null, curated: Boolean = null, first: Int = null, hasBindings: Boolean = null, isStandalone: Boolean = null, kind: [SearchKind!] = null, last: Int = null, orderBy: SearchOrderBy = null, publishDate: SearchPublishDate = null, query: String!, sort: SearchOrderSort = null, withInterfaces: [String!] = null): SearchConnection! - searchAutocomplete(after: String = null, before: String = null, first: Int = null, kind: [SearchKind!] = null, last: Int = null, query: String!): SearchConnection! - viewer: User +input SocialAuthJWTInput { + provider: String! + accessToken: String! + clientMutationId: String +} + +type ValidateUserEmailPayload { + user: User + clientMutationId: String +} + +input ValidateUserEmailInput { + """The user id""" + userId: ID + challenge: String! + clientMutationId: String +} + +type RequestPasswordResetPayload { + email: String! + errors: [ErrorType] + clientMutationId: String } -input ReadNotificationInput { - clientMutationId: String - notificationId: ID! +type ErrorType { + field: String! + messages: [String!]! } -type ReadNotificationPayload { +input RequestPasswordResetInput { + email: String! clientMutationId: String - notification: UserNotification } -input RefreshInput { +type RequestValidationEmailPayload { + user: User + success: Boolean! clientMutationId: String - refreshToken: String } -type RefreshPayload { +input RequestValidationEmailInput { + """The user id""" + userId: ID clientMutationId: String - payload: GenericScalar! - refreshExpiresIn: Int! - refreshToken: String! - token: String! } -input RegisterUserInput { +type ChangeUserPasswordPayload { + token: String clientMutationId: String - email: String! - fullName: String! - password: String! - username: String! } -type RegisterUserPayload { - clientMutationId: String +input ChangeUserPasswordInput { + """ + The token associated to change the password. If not existing it will use the request user by default + """ token: String + oldPassword: String + password: String! + clientMutationId: String } -# An enumeration. -enum RegistryNamespaceMaintainerInviteRoleChoices { - # Admin - ADMIN - - # Editor - EDITOR +type ChangeUserUsernamePayload { + user: User + token: String + clientMutationId: String +} - # Viewer - VIEWER +input ChangeUserUsernameInput { + """The new user username""" + username: String! + clientMutationId: String } -# An enumeration. -enum RegistryNamespaceMaintainerRoleChoices { - # Admin - ADMIN +type ChangeUserEmailPayload { + user: User! + clientMutationId: String +} - # Editor - EDITOR +input ChangeUserEmailInput { + newEmail: String! + clientMutationId: String +} - # Viewer - VIEWER +type UpdateUserInfoPayload { + user: User + clientMutationId: String } -# An enumeration. -enum RegistryPackageMaintainerInviteRoleChoices { - # Admin - ADMIN +input UpdateUserInfoInput { + """The user id""" + userId: ID - # Editor - EDITOR + """The user full name""" + fullName: String - # Viewer - VIEWER -} + """The user bio""" + bio: String -# An enumeration. -enum RegistryPackageMaintainerRoleChoices { - # Admin - ADMIN + """The user avatar""" + avatar: String - # Editor - EDITOR + """ + The user Twitter (it can be the url, or the handle with or without the @) + """ + twitter: String - # Viewer - VIEWER -} + """ + The user Github (it can be the url, or the handle with or without the @) + """ + github: String -input RemoveNamespaceCollaboratorInput { - clientMutationId: String - namespaceCollaboratorId: ID! -} + """The user website (it must be a valid url)""" + websiteUrl: String -input RemoveNamespaceCollaboratorInviteInput { + """The user location""" + location: String clientMutationId: String - inviteId: ID! } -type RemoveNamespaceCollaboratorInvitePayload { +type ValidateUserPasswordPayload { + success: Boolean clientMutationId: String - namespace: Namespace! } -type RemoveNamespaceCollaboratorPayload { +input ValidateUserPasswordInput { + password: String! clientMutationId: String - namespace: Namespace! } -input RemovePackageCollaboratorInput { +type GenerateAPITokenPayload { + token: APIToken + tokenRaw: String + user: User clientMutationId: String - packageCollaboratorId: ID! } -input RemovePackageCollaboratorInviteInput { +input GenerateAPITokenInput { + identifier: String clientMutationId: String - inviteId: ID! } -type RemovePackageCollaboratorInvitePayload { +type RevokeAPITokenPayload { + token: APIToken + success: Boolean clientMutationId: String - package: Package! } -type RemovePackageCollaboratorPayload { +input RevokeAPITokenInput { + """The API token ID""" + tokenId: ID! clientMutationId: String - package: Package! } -input RemovePackageTransferRequestInput { - clientMutationId: String - packageTransferRequestId: ID! -} +type CheckUserExistsPayload { + exists: Boolean! -type RemovePackageTransferRequestPayload { + """The user is only returned if the user input was the username""" + user: User clientMutationId: String - package: Package! } -input RequestPackageTransferInput { +input CheckUserExistsInput { + """The user""" + user: String! clientMutationId: String - newOwnerId: ID! - packageId: ID! } -type RequestPackageTransferPayload { +type ReadNotificationPayload { + notification: UserNotification clientMutationId: String - package: Package! } -input RequestPasswordResetInput { +input ReadNotificationInput { + notificationId: ID! clientMutationId: String - email: String! } -type RequestPasswordResetPayload { +type SeePendingNotificationsPayload { + success: Boolean clientMutationId: String - email: String! - errors: [ErrorType] } -input RequestValidationEmailInput { +input SeePendingNotificationsInput { clientMutationId: String - - # The user id - userId: ID } -type RequestValidationEmailPayload { - clientMutationId: String +type PublishPublicKeyPayload { success: Boolean! - user: User -} - -input RevokeAPITokenInput { + publicKey: PublicKey! clientMutationId: String - - # The API token ID - tokenId: ID! } -type RevokeAPITokenPayload { +input PublishPublicKeyInput { + keyId: String! + key: String! + verifyingSignatureId: String clientMutationId: String - success: Boolean - token: APIToken } -enum Role { - ADMIN - EDITOR - VIEWER +type PublishPackagePayload { + success: Boolean! + packageVersion: PackageVersion! + clientMutationId: String } -type SearchConnection { - # Contains the nodes in this connection. - edges: [SearchEdge]! +input PublishPackageInput { + name: String! + version: String! + description: String! + manifest: String! + license: String + licenseFile: String + readme: String + repository: String + homepage: String + file: String + signedUrl: String + signature: InputSignature - # Pagination data for this connection. - pageInfo: PageInfo! + """The package icon""" + icon: String + clientMutationId: String } -# A Relay edge containing a `Search` and its cursor. -type SearchEdge { - # A cursor for use in pagination - cursor: String! - - # The item at the end of the edge - node: SearchResult +input InputSignature { + publicKeyKeyId: String! + data: String! } -enum SearchKind { - NAMESPACE - PACKAGE - USER +type UpdatePackagePayload { + package: Package! + clientMutationId: String } -enum SearchOrderBy { - ALPHABETICALLY - PUBLISHED_DATE - SIZE - TOTAL_DOWNLOADS -} +input UpdatePackageInput { + packageId: ID! -enum SearchOrderSort { - ASC - DESC + """The package icon""" + icon: String + clientMutationId: String } -enum SearchPublishDate { - LAST_DAY - LAST_MONTH - LAST_WEEK - LAST_YEAR +type LikePackagePayload { + package: Package! + clientMutationId: String } -union SearchResult = Namespace | PackageVersion | User - -input SeePendingNotificationsInput { +input LikePackageInput { + packageId: ID! clientMutationId: String } -type SeePendingNotificationsPayload { +type UnlikePackagePayload { + package: Package! clientMutationId: String - success: Boolean } -type Signature { - createdAt: DateTime! - data: String! - id: ID! - publicKey: PublicKey! +input UnlikePackageInput { + packageId: ID! + clientMutationId: String } -input SocialAuthJWTInput { - accessToken: String! +type WatchPackagePayload { + package: Package! clientMutationId: String - provider: String! } -# Social Auth for JSON Web Token (JWT) -type SocialAuthJWTPayload { +input WatchPackageInput { + packageId: ID! clientMutationId: String - social: SocialNode - token: String } -scalar SocialCamelJSON - -type SocialNode implements Node { - created: DateTime! - extraData: SocialCamelJSON - - # The ID of the object - id: ID! - modified: DateTime! - provider: String! - uid: String! - user: User! +type UnwatchPackagePayload { + package: Package! + clientMutationId: String } -type Subscription { - packageVersionCreated(ownerId: ID = null, publishedBy: ID = null): PackageVersion! - userNotificationCreated(userId: ID!): UserNotificationCreated! +input UnwatchPackageInput { + packageId: ID! + clientMutationId: String } -input UnlikePackageInput { +type ArchivePackagePayload { + package: Package! clientMutationId: String +} + +input ArchivePackageInput { packageId: ID! + clientMutationId: String } -type UnlikePackagePayload { +type ChangePackageVersionArchivedStatusPayload { + packageVersion: PackageVersion! clientMutationId: String - package: Package! } -input UnwatchPackageInput { +input ChangePackageVersionArchivedStatusInput { + packageVersionId: ID! + isArchived: Boolean clientMutationId: String - packageId: ID! } -type UnwatchPackagePayload { +type CreateNamespacePayload { + namespace: Namespace! + user: User! clientMutationId: String - package: Package! } -input UpdateNamespaceCollaboratorRoleInput { +input CreateNamespaceInput { + name: String! + + """The namespace display name""" + displayName: String + + """The namespace description""" + description: String + + """The namespace avatar""" + avatar: String clientMutationId: String - namespaceCollaboratorId: ID! - role: Role! } -type UpdateNamespaceCollaboratorRolePayload { +type UpdateNamespacePayload { + namespace: Namespace! clientMutationId: String - collaborator: NamespaceCollaborator! } input UpdateNamespaceInput { - # The namespace avatar - avatar: String - clientMutationId: String + namespaceId: ID! - # The namespace description - description: String + """The namespace slug name""" + name: String - # The namespace display name + """The namespace display name""" displayName: String - # The namespace slug name - name: String - namespaceId: ID! -} + """The namespace description""" + description: String -type UpdateNamespacePayload { + """The namespace avatar""" + avatar: String clientMutationId: String - namespace: Namespace! } -input UpdatePackageCollaboratorRoleInput { +type DeleteNamespacePayload { + success: Boolean! clientMutationId: String - packageCollaboratorId: ID! - role: Role! } -type UpdatePackageCollaboratorRolePayload { +input DeleteNamespaceInput { + namespaceId: ID! clientMutationId: String - collaborator: PackageCollaborator! } -input UpdatePackageInput { +type InviteNamespaceCollaboratorPayload { + invite: NamespaceCollaboratorInvite! + namespace: Namespace! clientMutationId: String - - # The package icon - icon: String - packageId: ID! } -type UpdatePackagePayload { +input InviteNamespaceCollaboratorInput { + namespaceId: ID! + role: Role! + username: String + email: String clientMutationId: String - package: Package! } -input UpdateUserInfoInput { - # The user avatar - avatar: String - - # The user bio - bio: String +type AcceptNamespaceCollaboratorInvitePayload { + namespaceCollaboratorInvite: NamespaceCollaboratorInvite! clientMutationId: String - - # The user full name - fullName: String - - # The user Github (it can be the url, or the handle with or without the @) - github: String - - # The user location - location: String - - # The user Twitter (it can be the url, or the handle with or without the @) - twitter: String - - # The user id - userId: ID - - # The user website (it must be a valid url) - websiteUrl: String } -type UpdateUserInfoPayload { +input AcceptNamespaceCollaboratorInviteInput { + inviteId: ID! clientMutationId: String - user: User } -type User implements Node & PackageOwner { - apiTokens(after: String = null, before: String = null, first: Int = null, last: Int = null): APITokenConnection - avatar(size: Int = 80): String! - bio: String - dateJoined: DateTime! - email: String! - firstName: String! - fullName: String! - githubUrl: String - globalName: String! - - # The ID of the object - id: ID! - isEmailValidated: Boolean! - isViewer: Boolean! - lastName: String! - location: String - namespaceInvitesIncoming(after: String = null, before: String = null, first: Int = null, last: Int = null): NamespaceCollaboratorInviteConnection - namespaces(after: String = null, before: String = null, first: Int = null, last: Int = null): NamespaceConnection - notifications(after: String = null, before: String = null, first: Int = null, last: Int = null): UserNotificationConnection - packageInvitesIncoming(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageCollaboratorInviteConnection - packageTransfersIncoming(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageTransferRequestConnection - packageVersions(after: String = null, before: String = null, first: Int = null, last: Int = null): PackageVersionConnection - packages(after: String = null, before: String = null, collaborating: Boolean = null, first: Int = null, last: Int = null): PackageConnection - publicActivity(after: String = null, before: String = null, first: Int = null, last: Int = null): ActivityEventConnection! - twitterUrl: String - - # Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only. - username: String! - websiteUrl: String +type RemoveNamespaceCollaboratorInvitePayload { + namespace: Namespace! + clientMutationId: String } -type UserConnection { - # Contains the nodes in this connection. - edges: [UserEdge]! - - # Pagination data for this connection. - pageInfo: PageInfo! +input RemoveNamespaceCollaboratorInviteInput { + inviteId: ID! + clientMutationId: String } -# A Relay edge containing a `User` and its cursor. -type UserEdge { - # A cursor for use in pagination - cursor: String! +type RemoveNamespaceCollaboratorPayload { + namespace: Namespace! + clientMutationId: String +} - # The item at the end of the edge - node: User +input RemoveNamespaceCollaboratorInput { + namespaceCollaboratorId: ID! + clientMutationId: String } -type UserNotification implements Node { - body: UserNotificationBody! - createdAt: DateTime! - icon: String +type UpdateNamespaceCollaboratorRolePayload { + collaborator: NamespaceCollaborator! + clientMutationId: String +} - # The ID of the object - id: ID! - kind: UserNotificationKind - seenState: UserNotificationSeenState! +input UpdateNamespaceCollaboratorRoleInput { + namespaceCollaboratorId: ID! + role: Role! + clientMutationId: String } -type UserNotificationBody { - ranges: [NodeBodyRange]! - text: String! +type UpdateNamespaceCollaboratorInviteRolePayload { + collaboratorInvite: NamespaceCollaboratorInvite! + clientMutationId: String } -type UserNotificationConnection { - # Contains the nodes in this connection. - edges: [UserNotificationEdge]! - hasPendingNotifications: Boolean! +input UpdateNamespaceCollaboratorInviteRoleInput { + namespaceCollaboratorInviteId: ID! + role: Role! + clientMutationId: String +} - # Pagination data for this connection. - pageInfo: PageInfo! +type InvitePackageCollaboratorPayload { + invite: PackageCollaboratorInvite! + package: Package! + clientMutationId: String } -type UserNotificationCreated { - notification: UserNotification - notificationDeletedId: ID +input InvitePackageCollaboratorInput { + packageName: String! + role: Role! + username: String + email: String + clientMutationId: String } -# A Relay edge containing a `UserNotification` and its cursor. -type UserNotificationEdge { - # A cursor for use in pagination - cursor: String! +type AcceptPackageCollaboratorInvitePayload { + packageCollaboratorInvite: PackageCollaboratorInvite! + clientMutationId: String +} - # The item at the end of the edge - node: UserNotification +input AcceptPackageCollaboratorInviteInput { + inviteId: ID! + clientMutationId: String } -union UserNotificationKind = UserNotificationKindIncomingPackageInvite | UserNotificationKindIncomingPackageTransfer | UserNotificationKindPublishedPackageVersion +type RemovePackageCollaboratorInvitePayload { + package: Package! + clientMutationId: String +} -type UserNotificationKindIncomingPackageInvite { - packageInvite: PackageCollaboratorInvite! +input RemovePackageCollaboratorInviteInput { + inviteId: ID! + clientMutationId: String } -type UserNotificationKindIncomingPackageTransfer { - packageTransferRequest: PackageTransferRequest! +type UpdatePackageCollaboratorRolePayload { + collaborator: PackageCollaborator! + clientMutationId: String } -type UserNotificationKindPublishedPackageVersion { - packageVersion: PackageVersion! +input UpdatePackageCollaboratorRoleInput { + packageCollaboratorId: ID! + role: Role! + clientMutationId: String } -enum UserNotificationSeenState { - SEEN - SEEN_AND_READ - UNSEEN +type UpdatePackageCollaboratorInviteRolePayload { + collaboratorInvite: PackageCollaboratorInvite! + clientMutationId: String } -input ValidateUserEmailInput { - challenge: String! +input UpdatePackageCollaboratorInviteRoleInput { + packageCollaboratorInviteId: ID! + role: Role! clientMutationId: String +} - # The user id - userId: ID +type RemovePackageCollaboratorPayload { + package: Package! + clientMutationId: String } -type ValidateUserEmailPayload { +input RemovePackageCollaboratorInput { + packageCollaboratorId: ID! clientMutationId: String - user: User } -input ValidateUserPasswordInput { +type RequestPackageTransferPayload { + package: Package! clientMutationId: String - password: String! } -type ValidateUserPasswordPayload { +input RequestPackageTransferInput { + packageId: ID! + newOwnerId: ID! clientMutationId: String - success: Boolean } -input VerifyInput { +type AcceptPackageTransferRequestPayload { + package: Package! + packageTransferRequest: PackageTransferRequest! clientMutationId: String - token: String } -type VerifyPayload { +input AcceptPackageTransferRequestInput { + packageTransferRequestId: ID! clientMutationId: String - payload: GenericScalar! } -input WatchPackageInput { +type RemovePackageTransferRequestPayload { + package: Package! clientMutationId: String - packageId: ID! } -type WatchPackagePayload { +input RemovePackageTransferRequestInput { + packageTransferRequestId: ID! clientMutationId: String - package: Package! +} + +type Subscription { + packageVersionCreated(publishedBy: ID, ownerId: ID): PackageVersion! + userNotificationCreated(userId: ID!): UserNotificationCreated! +} + +type UserNotificationCreated { + notification: UserNotification + notificationDeletedId: ID } diff --git a/lib/registry/src/graphql.rs b/lib/registry/src/graphql.rs index 26cf05c5313..730c7599a97 100644 --- a/lib/registry/src/graphql.rs +++ b/lib/registry/src/graphql.rs @@ -100,6 +100,14 @@ pub(crate) struct GetPackageByCommandQuery; )] pub(crate) struct TestIfRegistryPresent; +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "graphql/schema.graphql", + query_path = "graphql/queries/get_bindings.graphql", + response_derives = "Debug,Clone,PartialEq,Eq" +)] +pub(crate) struct GetBindingsQuery; + #[cfg(target_os = "wasi")] pub fn whoami_distro() -> String { whoami::os().to_lowercase() diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index f1d4b0b34a5..baec1ba1c19 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1,16 +1,33 @@ -use std::collections::BTreeMap; +//! High-level interactions with the WAPM backend. +//! +//! The GraphQL schema can be updated by running `make` in the Wasmer repo's +//! root directory. +//! +//! ```console +//! $ make update-graphql-schema +//! curl -sSfL https://registry.wapm.io/graphql/schema.graphql > lib/registry/graphql/schema.graphql +//! ``` + use std::fmt; use std::path::{Path, PathBuf}; use std::time::Duration; +use std::{ + collections::BTreeMap, + fmt::{Display, Formatter}, +}; pub mod config; pub mod graphql; pub mod login; pub mod utils; -pub use crate::config::format_graphql; +pub use crate::{ + config::{format_graphql, PartialWapmConfig}, + graphql::get_bindings_query::ProgrammingLanguage, +}; + use crate::config::Registries; -pub use config::PartialWapmConfig; +use anyhow::Context; pub static GLOBAL_CONFIG_FILE_NAME: &str = if cfg!(target_os = "wasi") { "/.private/wapm.toml" @@ -319,7 +336,7 @@ pub enum QueryPackageError { } impl fmt::Display for QueryPackageError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { QueryPackageError::ErrorSendingQuery(q) => write!(f, "error sending query: {q}"), QueryPackageError::NoPackageFound { name, version } => { @@ -964,3 +981,90 @@ fn test_install_package() { println!("ok, done"); } + +/// A library that exposes bindings to a WAPM package. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Bindings { + /// A unique ID specifying this set of bindings. + pub id: String, + /// The URL which can be used to download the files that were generated + /// (typically as a `*.tar.gz` file). + pub url: String, + /// The programming language these bindings are written in. + pub language: graphql::get_bindings_query::ProgrammingLanguage, + /// The generator used to generate these bindings. + pub generator: BindingsGenerator, +} + +/// The generator used to create [`Bindings`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BindingsGenerator { + /// A unique ID specifying this generator. + pub id: String, + /// The generator package's name (e.g. `wasmer/wasmer-pack`). + pub package_name: String, + /// The exact package version. + pub version: String, + /// The name of the command that was used for generating bindings. + pub command: String, +} + +impl Display for BindingsGenerator { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let BindingsGenerator { + package_name, + version, + command, + .. + } = self; + + write!(f, "{package_name}@{version}:{command}")?; + + Ok(()) + } +} + +/// List all bindings associated with a particular package. +/// +/// If a version number isn't provided, this will default to the most recently +/// published version. +pub fn list_bindings( + registry: &str, + name: &str, + version: Option<&str>, +) -> Result, anyhow::Error> { + use crate::graphql::{ + get_bindings_query::{ResponseData, Variables}, + GetBindingsQuery, + }; + use graphql_client::GraphQLQuery; + + let variables = Variables { + name: name.to_string(), + version: version.map(String::from), + }; + + let q = GetBindingsQuery::build_query(variables); + let response: ResponseData = crate::graphql::execute_query(registry, "", &q)?; + + let package_version = response.package_version.context("Package not found")?; + + let mut bindings_packages = Vec::new(); + + for b in package_version.bindings.into_iter().flatten() { + let pkg = Bindings { + id: b.id, + url: b.url, + language: b.language, + generator: BindingsGenerator { + id: b.generator.package_version.id, + package_name: b.generator.package_version.package.name, + version: b.generator.package_version.version, + command: b.generator.command_name, + }, + }; + bindings_packages.push(pkg); + } + + Ok(bindings_packages) +}