///|
/// The in-browser IDE a `GET` on the GraphQL endpoint serves. Four of them are on
/// offer and the whole thing can be turned off:
///
///   * **the gilded page** (`gilded`, the default) — the library's own IDE, built
///     in `ide_gilded.mbt`. It is one HTML string with no CDN and no bundler: a
///     document on the left, the answer on the right, a schema drawer, variables,
///     and the same linking the documentation uses — a field lit on one side
///     lights the same field on the other. An operation is sent when it is run,
///     over the same `POST` every client uses.
///
///   * **GraphiQL** (`graphiql`) — the GraphQL Foundation's
///     reference IDE, MIT-licensed and the one kept current with the
///     specification. Version 5 carries the Monaco editor, a plugin system (the
///     explorer and history plugins are enabled here) and a fetcher that speaks
///     `graphql-transport-ws`, so it runs subscriptions too.
///
///   * **Apollo Sandbox** (`apollo_sandbox`) — Apollo's Explorer, embedded as an
///     iframe. It needs no Apollo account and introspects the endpoint on load.
///
///   * **Altair** (`altair`) — the fullest of the three: environments, saved
///     collections, and subscriptions over WebSocket or SSE.
///
/// Every asset is pinned to an exact version, so a page served today keeps
/// working when the CDN moves on. The IDE is a development surface: it
/// introspects the schema and runs arbitrary operations, which is why `off`
/// exists for anything but a development deployment.

///|
/// Which IDE the endpoint serves.
pub(all) enum IdeKind {
  Gilded
  GraphiQL
  ApolloSandbox
  Altair
  Off
} derive(Eq)

///|
/// The IDE a `GET` without a `?query=` is answered with. `subscription_endpoint`
/// is where the IDE opens its WebSocket: left as `None`, each page derives one
/// from the page's own location (the same host, `wss:` over HTTPS), which is
/// right for an endpoint served next to the IDE.
pub(all) struct Ide {
  kind : IdeKind
  subscription_endpoint : String?
}

///|
/// The gilded page: the library's own IDE, and the default.
pub let gilded : Ide = { kind: Gilded, subscription_endpoint: None, }

///|
/// GraphiQL, the reference IDE.
pub let graphiql : Ide = { kind: GraphiQL, subscription_endpoint: None, }

///|
/// Apollo Sandbox, embedded as an iframe.
pub let apollo_sandbox : Ide = {
  kind: ApolloSandbox,
  subscription_endpoint: None,
}

///|
/// Altair, the IDE with environments and saved collections.
pub let altair : Ide = { kind: Altair, subscription_endpoint: None, }

///|
/// No IDE at all: a `GET` without a `?query=` is a 405.
pub let off : Ide = { kind: Off, subscription_endpoint: None, }

///|
/// Whether this configuration serves no IDE.
pub fn Ide::is_off(self : Ide) -> Bool {
  self.kind == Off
}

///|
/// The page title the IDE's HTML carries.
pub fn Ide::title(self : Ide) -> String {
  match self.kind {
    Gilded => "MoonQL"
    GraphiQL => "GraphiQL"
    ApolloSandbox => "Apollo Sandbox"
    Altair => "Altair"
    Off => ""
  }
}

///|
/// The full HTML page mounting this IDE against `endpoint`. `Off` has no page,
/// and answers the empty string.
pub fn Ide::html(self : Ide, endpoint~ : String = "/graphql") -> String {
  match self.kind {
    Gilded => gilded_page(endpoint)
    GraphiQL => graphiql_page(endpoint, self.subscription_endpoint)
    ApolloSandbox => apollo_page(endpoint)
    Altair => altair_page(endpoint, self.subscription_endpoint)
    Off => ""
  }
}

///|
/// The GraphiQL page up to the fetcher's endpoint literal. React, GraphiQL and
/// the plugins load from esm.sh through an import map, so nothing is bundled
/// into the page: it is a string, not a build artefact.
fn graphiql_head() -> String {
  let head =
    #|
    #|
    #|
    #|  
    #|  GraphiQL
    #|  
    #|  
    #|  
    #|  
    #|  
    #|
    #|
    #|  
Loading GraphiQL…
#| #| #| sub + tail } ///| /// The GraphiQL page, wired to fetch against `endpoint`. fn graphiql_page(endpoint : String, subscription : String?) -> String { graphiql_head() + endpoint + graphiql_tail(subscription) } ///| /// The Apollo Sandbox page: a full-height container the embedded Sandbox mounts /// an iframe into, pointed at an absolute endpoint URL (the iframe's requests /// are sent by this page, but a relative URL would read as relative to the /// iframe). Sandbox opens its subscription socket on the endpoint itself, so /// `subscription_endpoint` has nothing to say to it. fn apollo_page(endpoint : String) -> String { let head = #| #| #| #| #| Apollo Sandbox #| #| #| #| #|
#| #| #| head + endpoint + tail } ///| /// The Altair page. Altair's static build reads its endpoint and its subscription /// endpoint from the page's globals before its own bundle loads, so the whole /// configuration is three assignments followed by the two scripts. Every asset is /// pinned to an exact version, so a page served today keeps working when the CDN /// moves on. fn altair_page(endpoint : String, subscription : String?) -> String { let sub = match subscription { Some(u) => "'" + u + "'" None => "(u => (u.protocol === 'https:' ? 'wss:' : 'ws:') + '//' + u.host + u.pathname)(new URL(endpoint, location.href))" } let head = #| #| #| #| #| Altair #| #| #| #| #| #| #| #| #| #| head + endpoint + mid + " " + sub + tail } ///| pub extend IdeKind with Eq::{not_equal, equal}