elmish-debugger


  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
125: 
126: 
127: 
128: 
129: 
130: 
131: 
132: 
133: 
134: 
135: 
136: 
137: 
138: 
139: 
140: 
141: 
142: 
143: 
144: 
145: 
146: 
147: 
148: 
149: 
150: 
151: 
152: 
153: 
154: 
155: 
namespace Elmish.Debug

open Fable.Import
open Fable.Import.RemoteDev
open Fable.Core.JsInterop
open Fable.Core
open Thoth.Json

[<RequireQualifiedAccess>]
module Debugger =
    open FSharp.Reflection

    let showError (msgs: obj list) = JS.console.error("[ELMISH DEBUGGER]", List.toArray msgs)
    let showWarning (msgs: obj list) = JS.console.warn("[ELMISH DEBUGGER]", List.toArray msgs)

    type ConnectionOptions =
        | ViaExtension
        | Remote of address:string * port:int
        | Secure of address:string * port:int

    let inline connect<'msg> opt =
        let makeMsgObj (case, fields) =
            createObj ["type" ==> case; "msg" ==> fields]

        let getCase (x: obj) =
            if Reflection.isUnion x then
                let rec getCaseName acc (x: obj) =
                    let acc = (Reflection.getCaseName x)::acc
                    let fields = Reflection.getCaseFields x
                    if fields.Length = 1 && Reflection.isUnion fields.[0] then
                        getCaseName acc fields.[0]
                    else
                        // Case names are intentionally left reverted so we see
                        // the most meaningfull message first
                        makeMsgObj(acc |> String.concat "/", fields)
                getCaseName [] x
            else
                makeMsgObj("NOT-AN-F#-UNION", x)

        let fallback = { Options.remote = true
                         hostname = "remotedev.io"
                         port = 443
                         secure = true
                         getActionType = Some getCase }

        match opt with
        | ViaExtension -> { fallback with remote = false; hostname = "localhost"; port = 8000; secure = false }
        | Remote (address,port) -> { fallback with hostname = address; port = port; secure = false }
        | Secure (address,port) -> { fallback with hostname = address; port = port }
        |> connectViaExtension

    type Send<'msg,'model> = 'msg*'model -> unit

[<RequireQualifiedAccess>]
module Program =
    open Elmish

    let inline private getTransformersWith encoder decoder =
        let deflate x =
            try encoder x
            with er ->
                Debugger.showWarning [er.Message]
                box x
        let inflate x =
            match Decode.fromValue "$" decoder x with
            | Ok x -> x
            | Error er -> failwith er
        deflate, inflate

    let inline private getTransformers<'model>() =
        try
            let coders =
                Extra.empty
                |> Extra.withDecimal
                |> Extra.withInt64
                |> Extra.withUInt64
                // |> Extra.withBigInt
            let encoder = Encode.Auto.generateEncoder<'model>(extra = coders)
            let decoder = Decode.Auto.generateDecoder<'model>(extra = coders)
            getTransformersWith encoder decoder
        with er ->
            Debugger.showWarning [er.Message]
            box, fun _ -> failwith "Cannot inflate model"

    let withDebuggerUsing (deflater: 'model->obj) (inflater: obj->'model) (connection:Connection) (program : Program<'a,'model,'msg,'view>) : Program<'a,'model,'msg,'view> =
        let init userInit a =
            let (model,cmd) = userInit a
            connection.init (deflater model, None)
            model,cmd

        let update userUpdate msg model : 'model * Cmd<'msg> =
            let (model',cmd) = userUpdate msg model
            connection.send(msg, deflater model')
            (model',cmd)

        let subscribe userSubscribe model =
            let sub dispatch =
                function
                | (msg:Msg) when msg.``type`` = MsgTypes.Dispatch ->
                    try
                        match msg.payload.``type`` with
                        | PayloadTypes.JumpToAction
                        | PayloadTypes.JumpToState ->
                            let state = extractState msg |> inflater
                            Program.setState program state dispatch
                        | PayloadTypes.ImportState ->
                            let state = msg.payload.nextLiftedState.computedStates |> Array.last
                            let state = inflater state?state
                            Program.setState program state dispatch
                            connection.send(null, msg.payload.nextLiftedState)
                        | _ -> ()
                    with ex ->
                        Debugger.showError ["Unable to process monitor command"; ex.Message; msg]
                | _ -> ()
                |> connection.subscribe
                |> ignore

            Cmd.batch
                [ [sub]
                  userSubscribe model ]

        let onError userOnError (text,ex: exn) =
            userOnError (text, ex)
            connection.error (text + ex.Message)

        program
        |> Program.map init update id id subscribe
        |> Program.mapErrorHandler onError

    let inline withDebuggerConnection connection program : Program<'a,'model,'msg,'view> =
        let deflater, inflater = getTransformers<'model>()
        withDebuggerUsing deflater inflater connection program

    let inline withDebuggerCoders (encoder: Encoder<'model>) (decoder: Decoder<'model>) program : Program<'a,'model,'msg,'view> =
        let deflater, inflater = getTransformersWith encoder decoder
        let connection = Debugger.connect<'msg> Debugger.ViaExtension
        withDebuggerUsing deflater inflater connection program

    let inline withDebuggerAt options program : Program<'a,'model,'msg,'view> =
        try
            let deflater, inflater = getTransformers<'model>()
            let connection = Debugger.connect<'msg> options
            withDebuggerUsing deflater inflater connection program
        with ex ->
            Debugger.showError ["Unable to connect to the monitor, continuing w/o debugger"; ex.Message]
            program

    let inline withDebugger (program : Program<'a,'model,'msg,'view>) : Program<'a,'model,'msg,'view> =
        try
            let deflater, inflater = getTransformers<'model>()
            let connection = Debugger.connect<'msg> Debugger.ViaExtension
            withDebuggerUsing deflater inflater connection program
        with ex ->
            Debugger.showError ["Unable to connect to the monitor, continuing w/o debugger"; ex.Message]
            program
Multiple items
type RequireQualifiedAccessAttribute =
  inherit Attribute
  new : unit -> RequireQualifiedAccessAttribute

--------------------
new : unit -> RequireQualifiedAccessAttribute
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Reflection
val showError : msgs:obj list -> 'a
val msgs : obj list
type obj = System.Object
type 'T list = List<'T>
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
    interface IReadOnlyList<'T>
    interface IReadOnlyCollection<'T>
    interface IEnumerable
    interface IEnumerable<'T>
    member GetSlice : startIndex:int option * endIndex:int option -> 'T list
    member Head : 'T
    member IsEmpty : bool
    member Item : index:int -> 'T with get
    member Length : int
    member Tail : 'T list
    ...
val toArray : list:'T list -> 'T []
val showWarning : msgs:obj list -> 'a
type ConnectionOptions =
  | ViaExtension
  | Remote of address: string * port: int
  | Secure of address: string * port: int
union case ConnectionOptions.ViaExtension: ConnectionOptions
union case ConnectionOptions.Remote: address: string * port: int -> ConnectionOptions
Multiple items
val string : value:'T -> string

--------------------
type string = System.String
Multiple items
val int : value:'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
union case ConnectionOptions.Secure: address: string * port: int -> ConnectionOptions
val connect<'msg> : opt:ConnectionOptions -> obj
val opt : ConnectionOptions
val makeMsgObj : ('a * 'b -> 'c)
val case : 'a
val fields : 'b
val getCase : (obj -> 'a)
val x : obj
val getCaseName : (string list -> obj -> 'b)
val acc : string list
val fields : obj
module String

from Microsoft.FSharp.Core
val concat : sep:string -> strings:seq<string> -> string
val fallback : obj
union case Option.Some: Value: 'T -> Option<'T>
val address : string
val port : int
type Send<'msg,'model> = 'msg * 'model -> unit
type unit = Unit
module Program

from Elmish.Debug
val private getTransformersWith : encoder:('a -> obj) -> decoder:'b -> ('a -> obj) * ('c -> 'd)
val encoder : ('a -> obj)
val decoder : 'b
val deflate : ('a -> obj)
val x : 'a
val er : exn
module Debugger

from Elmish.Debug
property System.Exception.Message: string
val box : value:'T -> obj
val inflate : ('e -> 'f)
val x : 'e
union case Result.Ok: ResultValue: 'T -> Result<'T,'TError>
val x : 'f
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
val er : string
val failwith : message:string -> 'T
val private getTransformers<'model> : unit -> (obj -> obj) * (obj -> obj)
val coders : obj
val encoder : (obj -> obj)
val decoder : obj
val withDebuggerUsing : deflater:('model -> obj) -> inflater:(obj -> 'model) -> connection:'a -> program:'b -> 'c
val deflater : ('model -> obj)
val inflater : (obj -> 'model)
val connection : 'a
val program : 'b
val init : (('d -> 'e * 'f) -> 'd -> 'e * 'f)
val userInit : ('d -> 'e * 'f)
val a : 'd
val model : 'e
val cmd : 'f
union case Option.None: Option<'T>
val update : (('d -> 'e -> 'model * 'f) -> 'd -> 'e -> 'model * 'f)
val userUpdate : ('d -> 'e -> 'model * 'f)
val msg : 'd
val model' : 'model
val subscribe : ('d -> 'e -> 'f)
val userSubscribe : 'd
val sub : ('g -> unit)
val dispatch : 'g
val msg : obj
module Array

from Microsoft.FSharp.Collections
val last : array:'T [] -> 'T
val ex : exn
val ignore : value:'T -> unit
val onError : (('d * exn -> unit) -> 'd * exn -> 'e)
val userOnError : ('d * exn -> unit)
val text : 'd
type exn = System.Exception
val id : x:'T -> 'T
val withDebuggerConnection : connection:'a -> program:'b -> 'c
val deflater : (obj -> obj)
val inflater : (obj -> obj)
val withDebuggerCoders : encoder:('a -> obj) -> decoder:'b -> program:'c -> 'd
val program : 'c
val deflater : ('a -> obj)
val inflater : (obj -> 'a)
val connection : obj
val connect<'msg> : opt:Debugger.ConnectionOptions -> obj
union case Debugger.ConnectionOptions.ViaExtension: Debugger.ConnectionOptions
val withDebuggerAt : options:Debugger.ConnectionOptions -> program:'a -> 'a
val options : Debugger.ConnectionOptions
val program : 'a
val withDebugger : program:'a -> 'a
F# Project