├── .gitignore ├── Presentation.pdf ├── README.md ├── elm-tooling.json ├── elm.json ├── index.html ├── package.json ├── review ├── elm.json └── src │ └── ReviewConfig.elm ├── screenshot.png ├── src ├── API │ ├── Image.elm │ ├── Post.elm │ └── User.elm ├── Cmd │ └── Extra.elm ├── Html │ └── Extra.elm ├── Http │ └── Extra.elm ├── Main.elm ├── Page │ ├── Post.elm │ ├── Posts.elm │ └── User.elm ├── RemoteData │ └── Extra.elm ├── Route.elm ├── Store.elm ├── UI.elm └── UI │ └── Toast.elm ├── watch-compile.sh └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff/ 2 | node_modules/ 3 | elm.js 4 | -------------------------------------------------------------------------------- /Presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janiczek/elm-store-pattern/5d94ee898e4665b89614727f02077f2920f2d016/Presentation.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elm-store-pattern 2 | 3 | Presentation and accompanying code demo about the Store pattern in Elm. 4 | 5 | Here's the talk about it: https://youtu.be/BCmNX2Tx5xY 6 | 7 | See `Presentation.pdf` or run the app with `yarn && yarn start`. 8 | 9 | [![Screenshot](https://github.com/Janiczek/elm-store-pattern/raw/main/screenshot.png)](https://github.com/Janiczek/elm-store-pattern/raw/main/screenshot.png) 10 | -------------------------------------------------------------------------------- /elm-tooling.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": { 3 | "elm": "0.19.1", 4 | "elm-format": "0.8.5", 5 | "elm-json": "0.2.10", 6 | "elm-test-rs": "2.0.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.1", 7 | "dependencies": { 8 | "direct": { 9 | "elm/browser": "1.0.2", 10 | "elm/core": "1.0.5", 11 | "elm/html": "1.0.0", 12 | "elm/http": "2.0.0", 13 | "elm/svg": "1.0.1", 14 | "elm/url": "1.0.0", 15 | "emilianobovetti/elm-toast": "1.1.0", 16 | "krisajenkins/remotedata": "6.0.1" 17 | }, 18 | "indirect": { 19 | "elm/bytes": "1.0.8", 20 | "elm/file": "1.0.5", 21 | "elm/json": "1.1.3", 22 | "elm/time": "1.0.0", 23 | "elm/virtual-dom": "1.0.2" 24 | } 25 | }, 26 | "test-dependencies": { 27 | "direct": {}, 28 | "indirect": {} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Store pattern example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "license": "BSD-3-Clause", 3 | "scripts": { 4 | "postinstall": "elm-tooling install", 5 | "build": "elm make src/Main.elm --output=elm.js", 6 | "start": "elm-live src/Main.elm --hot --open --pushstate -- --output=elm.js" 7 | }, 8 | "devDependencies": { 9 | "elm-live": "^4.0.2", 10 | "elm-review": "^2.7.2", 11 | "elm-tooling": "^1.8.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /review/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.1", 7 | "dependencies": { 8 | "direct": { 9 | "elm/core": "1.0.5", 10 | "elm/json": "1.1.3", 11 | "elm/project-metadata-utils": "1.0.2", 12 | "jfmengels/elm-review": "2.7.2", 13 | "jfmengels/elm-review-code-style": "1.0.0", 14 | "jfmengels/elm-review-common": "1.2.0", 15 | "jfmengels/elm-review-debug": "1.0.6", 16 | "jfmengels/elm-review-documentation": "2.0.1", 17 | "jfmengels/elm-review-simplify": "2.0.14", 18 | "jfmengels/elm-review-unused": "1.1.22", 19 | "stil4m/elm-syntax": "7.2.9" 20 | }, 21 | "indirect": { 22 | "elm/html": "1.0.0", 23 | "elm/parser": "1.1.0", 24 | "elm/random": "1.0.0", 25 | "elm/regex": "1.0.0", 26 | "elm/time": "1.0.0", 27 | "elm/virtual-dom": "1.0.2", 28 | "elm-community/list-extra": "8.5.2", 29 | "elm-explorations/test": "1.2.2", 30 | "miniBill/elm-unicode": "1.0.2", 31 | "rtfeldman/elm-hex": "1.0.0", 32 | "stil4m/structured-writer": "1.0.3" 33 | } 34 | }, 35 | "test-dependencies": { 36 | "direct": { 37 | "elm-explorations/test": "1.2.2" 38 | }, 39 | "indirect": {} 40 | } 41 | } -------------------------------------------------------------------------------- /review/src/ReviewConfig.elm: -------------------------------------------------------------------------------- 1 | module ReviewConfig exposing (config) 2 | 3 | {-| Do not rename the ReviewConfig module or the config function, because 4 | `elm-review` will look for these. 5 | 6 | To add packages that contain rules, add them to this review project using 7 | 8 | `elm install author/packagename` 9 | 10 | when inside the directory containing this file. 11 | 12 | -} 13 | 14 | import Docs.ReviewAtDocs 15 | import NoDebug.Log 16 | import NoDebug.TodoOrToString 17 | import NoExposingEverything 18 | import NoImportingEverything 19 | import NoMissingTypeAnnotation 20 | import NoMissingTypeAnnotationInLetIn 21 | import NoMissingTypeExpose 22 | import NoPrematureLetComputation 23 | import NoSimpleLetBody 24 | import NoUnused.CustomTypeConstructorArgs 25 | import NoUnused.CustomTypeConstructors 26 | import NoUnused.Dependencies 27 | import NoUnused.Exports 28 | import NoUnused.Modules 29 | import NoUnused.Parameters 30 | import NoUnused.Patterns 31 | import NoUnused.Variables 32 | import Review.Rule as Rule exposing (Rule) 33 | import Simplify 34 | 35 | 36 | config : List Rule 37 | config = 38 | [ Docs.ReviewAtDocs.rule 39 | , NoExposingEverything.rule 40 | , NoImportingEverything.rule [] 41 | , NoMissingTypeAnnotation.rule 42 | , NoMissingTypeAnnotationInLetIn.rule 43 | , NoMissingTypeExpose.rule 44 | , NoSimpleLetBody.rule 45 | , NoPrematureLetComputation.rule 46 | , NoUnused.CustomTypeConstructors.rule [] 47 | , NoUnused.CustomTypeConstructorArgs.rule 48 | , NoUnused.Dependencies.rule 49 | , NoUnused.Exports.rule 50 | , NoUnused.Modules.rule 51 | , NoUnused.Parameters.rule 52 | , NoUnused.Patterns.rule 53 | , NoUnused.Variables.rule 54 | , Simplify.rule Simplify.defaults 55 | ] 56 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Janiczek/elm-store-pattern/5d94ee898e4665b89614727f02077f2920f2d016/screenshot.png -------------------------------------------------------------------------------- /src/API/Image.elm: -------------------------------------------------------------------------------- 1 | module API.Image exposing 2 | ( Image, ImageId 3 | , get 4 | ) 5 | 6 | {-| 7 | 8 | @docs Image, ImageId 9 | @docs get 10 | 11 | -} 12 | 13 | import Http 14 | import Http.Extra as Http 15 | 16 | 17 | type alias Image = 18 | { id : ImageId 19 | , content : String 20 | } 21 | 22 | 23 | type alias ImageId = 24 | String 25 | 26 | 27 | get : ImageId -> (Result Http.Error Image -> msg) -> Cmd msg 28 | get imageId toMsg = 29 | if imageId == "400/300" then 30 | Http.mockFailNetworkError 4000 toMsg 31 | 32 | else 33 | Http.mockSuccess 1200 34 | { id = imageId 35 | , content = "https://www.fillmurray.com/" ++ imageId 36 | } 37 | toMsg 38 | -------------------------------------------------------------------------------- /src/API/Post.elm: -------------------------------------------------------------------------------- 1 | module API.Post exposing 2 | ( Post, PostId 3 | , getAll 4 | , create, PostCreateData 5 | ) 6 | 7 | {-| 8 | 9 | @docs Post, PostId 10 | @docs getAll 11 | @docs create, PostCreateData 12 | 13 | -} 14 | 15 | import API.Image exposing (ImageId) 16 | import API.User exposing (UserId) 17 | import Http 18 | import Http.Extra as Http 19 | 20 | 21 | type alias Post = 22 | { id : PostId 23 | , title : String 24 | , authorId : UserId 25 | , content : String 26 | , imageIds : List ImageId 27 | } 28 | 29 | 30 | type alias PostId = 31 | String 32 | 33 | 34 | getAll : (Result Http.Error (List Post) -> msg) -> Cmd msg 35 | getAll toMsg = 36 | Http.mockSuccess 1500 37 | [ Post "1" "First" "42" "Hello" [ "200/300" ] 38 | , Post "2" "Second" "999" "Foo bar" [ "400/300", "300/300" ] 39 | , Post "4" "Other" "42" "Some other post" [ "500/300", "600/300" ] 40 | ] 41 | toMsg 42 | 43 | 44 | type alias PostCreateData = 45 | { title : String 46 | , authorId : UserId 47 | , content : String 48 | } 49 | 50 | 51 | create : PostCreateData -> (Result Http.Error Post -> msg) -> Cmd msg 52 | create data toMsg = 53 | if data.title == "B" then 54 | Http.mockFailBadRequestError 1200 toMsg 55 | 56 | else 57 | Http.mockSuccess 2500 58 | { id = String.fromInt (String.length data.title + String.length data.content) -- whatever 59 | , title = data.title 60 | , authorId = data.authorId 61 | , content = data.content 62 | , imageIds = [] 63 | } 64 | toMsg 65 | -------------------------------------------------------------------------------- /src/API/User.elm: -------------------------------------------------------------------------------- 1 | module API.User exposing (User, UserId, getAll) 2 | 3 | {-| 4 | 5 | @docs User, UserId, getAll 6 | 7 | -} 8 | 9 | import Http 10 | import Http.Extra as Http 11 | 12 | 13 | type alias User = 14 | { id : UserId 15 | , name : String 16 | } 17 | 18 | 19 | type alias UserId = 20 | String 21 | 22 | 23 | getAll : (Result Http.Error (List User) -> msg) -> Cmd msg 24 | getAll toMsg = 25 | --Http.mockFailNetworkError 2000 toMsg 26 | Http.mockSuccess 2000 27 | [ User "999" "Martin" 28 | , User "42" "Peter" 29 | , User "4" "Casey" 30 | ] 31 | toMsg 32 | -------------------------------------------------------------------------------- /src/Cmd/Extra.elm: -------------------------------------------------------------------------------- 1 | module Cmd.Extra exposing (andThen, andThenMaybe) 2 | 3 | 4 | andThen : 5 | (model -> ( model, Cmd msg )) 6 | -> ( model, Cmd msg ) 7 | -> ( model, Cmd msg ) 8 | andThen fn ( oldModel, oldCmd ) = 9 | let 10 | ( newModel, additionalCmd ) = 11 | fn oldModel 12 | in 13 | ( newModel 14 | , Cmd.batch [ oldCmd, additionalCmd ] 15 | ) 16 | 17 | 18 | andThenMaybe : 19 | (a -> model -> ( model, Cmd msg )) 20 | -> Maybe a 21 | -> ( model, Cmd msg ) 22 | -> ( model, Cmd msg ) 23 | andThenMaybe fn maybe modelAndCmd = 24 | case maybe of 25 | Nothing -> 26 | modelAndCmd 27 | 28 | Just thing -> 29 | modelAndCmd 30 | |> andThen (fn thing) 31 | -------------------------------------------------------------------------------- /src/Html/Extra.elm: -------------------------------------------------------------------------------- 1 | module Html.Extra exposing (spinner, todo) 2 | 3 | import Html exposing (Html) 4 | import Html.Attributes as Attrs 5 | import Svg 6 | import Svg.Attributes as SvgAttrs 7 | 8 | 9 | todo : String -> Html msg 10 | todo message = 11 | Html.div 12 | [ Attrs.class "p-4 bg-red-100 border-red-200 border-2" ] 13 | [ Html.text message ] 14 | 15 | 16 | spinner : Html msg 17 | spinner = 18 | Svg.svg 19 | [ SvgAttrs.class "animate-spin text-red w-6 h-6" 20 | , SvgAttrs.fill "none" 21 | , SvgAttrs.viewBox "0 0 24 24" 22 | ] 23 | [ Svg.circle 24 | [ SvgAttrs.class "opacity-25" 25 | , SvgAttrs.cx "12" 26 | , SvgAttrs.cy "12" 27 | , SvgAttrs.r "10" 28 | , SvgAttrs.stroke "currentColor" 29 | , SvgAttrs.strokeWidth "4" 30 | ] 31 | [] 32 | , Svg.path 33 | [ SvgAttrs.class "opacity-75" 34 | , SvgAttrs.fill "currentColor" 35 | , SvgAttrs.d "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" 36 | ] 37 | [] 38 | ] 39 | -------------------------------------------------------------------------------- /src/Http/Extra.elm: -------------------------------------------------------------------------------- 1 | module Http.Extra exposing 2 | ( mockFailBadRequestError 3 | --, TODO mockFailDecoderError 4 | 5 | , mockFailNetworkError 6 | , mockSuccess 7 | ) 8 | 9 | import Http 10 | import Process 11 | import Task 12 | 13 | 14 | mockSuccess : Float -> a -> (Result Http.Error a -> msg) -> Cmd msg 15 | mockSuccess delay value toMsg = 16 | emitWithDelay delay 17 | (toMsg (Ok value)) 18 | 19 | 20 | mockFailNetworkError : Float -> (Result Http.Error a -> msg) -> Cmd msg 21 | mockFailNetworkError delay toMsg = 22 | emitWithDelay delay 23 | (toMsg (Err Http.NetworkError)) 24 | 25 | 26 | mockFailBadRequestError : Float -> (Result Http.Error a -> msg) -> Cmd msg 27 | mockFailBadRequestError delay toMsg = 28 | emitWithDelay delay 29 | (toMsg (Err <| Http.BadStatus 400)) 30 | 31 | 32 | emitWithDelay : Float -> msg -> Cmd msg 33 | emitWithDelay delay msg = 34 | let 35 | _ = 36 | Debug.log "mock request" msg 37 | in 38 | Process.sleep delay 39 | |> Task.perform (\() -> Debug.log "mock response" msg) 40 | -------------------------------------------------------------------------------- /src/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (Flags, Model, Msg, Toast, main) 2 | 3 | import API.Post exposing (PostCreateData) 4 | import Browser 5 | import Browser.Navigation 6 | import Cmd.Extra as Cmd 7 | import Dict exposing (Dict) 8 | import Html exposing (Attribute, Html) 9 | import Html.Attributes as Attrs 10 | import Html.Events as Events 11 | import Http 12 | import Page.Post 13 | import Page.Posts 14 | import Page.User 15 | import RemoteData exposing (RemoteData(..), WebData) 16 | import RemoteData.Extra as RemoteData 17 | import Route exposing (Route(..)) 18 | import Store exposing (Store) 19 | import Toast 20 | import UI 21 | import UI.Toast 22 | import Url exposing (Url) 23 | 24 | 25 | main : Program Flags Model Msg 26 | main = 27 | Browser.application 28 | { init = init 29 | , update = update 30 | , subscriptions = subscriptions 31 | , view = view 32 | , onUrlChange = UrlChanged 33 | , onUrlRequest = UrlRequested 34 | } 35 | 36 | 37 | type alias Flags = 38 | () 39 | 40 | 41 | type Toast 42 | = StoreActionSent Store.Action 43 | | StoreActionSuccess Store.Action 44 | | StoreActionFailure Store.Action Http.Error 45 | 46 | 47 | type alias Model = 48 | { store : Store 49 | , route : Route 50 | , navKey : Browser.Navigation.Key 51 | , notifications : Toast.Tray Toast 52 | , failureDetailsModal : Maybe ( Store.Action, Http.Error ) 53 | } 54 | 55 | 56 | type Msg 57 | = StoreMsg Store.Msg 58 | | ToastMsg Toast.Msg 59 | | UrlChanged Url 60 | | UrlRequested Browser.UrlRequest 61 | | CreatePost PostCreateData 62 | | OpenFailureDetails Store.Action Http.Error 63 | | CloseFailureDetails 64 | | CopyFailureDetails 65 | 66 | 67 | init : Flags -> Url -> Browser.Navigation.Key -> ( Model, Cmd Msg ) 68 | init () url navKey = 69 | let 70 | route : Route 71 | route = 72 | Route.fromUrl url 73 | 74 | store : Store 75 | store = 76 | Store.init 77 | 78 | requests : List Store.Action 79 | requests = 80 | dataRequests store route 81 | in 82 | { store = store 83 | , route = route 84 | , navKey = navKey 85 | , notifications = Toast.tray 86 | , failureDetailsModal = Nothing 87 | } 88 | |> sendDataRequests requests 89 | 90 | 91 | sendDataRequest : Store.Action -> Model -> ( Model, Cmd Msg ) 92 | sendDataRequest request model = 93 | let 94 | ( newStore, storeCmd ) = 95 | model.store 96 | |> Store.runAction request 97 | in 98 | ( { model | store = newStore } 99 | , Cmd.map StoreMsg storeCmd 100 | ) 101 | 102 | 103 | sendDataRequests : List Store.Action -> Model -> ( Model, Cmd Msg ) 104 | sendDataRequests requests model = 105 | List.foldl 106 | (\request modelAndCmd -> 107 | modelAndCmd 108 | |> Cmd.andThen (sendDataRequest request) 109 | ) 110 | ( model, Cmd.none ) 111 | requests 112 | 113 | 114 | storeMsgToast : Store.Msg -> Maybe Toast 115 | storeMsgToast storeMsg = 116 | case storeMsg of 117 | Store.HttpError action err -> 118 | Just (StoreActionFailure action err) 119 | 120 | Store.GotPosts _ -> 121 | Nothing 122 | 123 | Store.GotUsers _ -> 124 | Nothing 125 | 126 | Store.GotImage _ -> 127 | Nothing 128 | 129 | Store.CreatedPost action _ -> 130 | Just (StoreActionSuccess action) 131 | 132 | 133 | update : Msg -> Model -> ( Model, Cmd Msg ) 134 | update msg model = 135 | case msg of 136 | StoreMsg storeMsg -> 137 | let 138 | ( newStore, storeCmd ) = 139 | Store.update storeMsg model.store 140 | 141 | requests : List Store.Action 142 | requests = 143 | dataRequests newStore model.route 144 | 145 | maybeToast : Maybe Toast 146 | maybeToast = 147 | storeMsgToast storeMsg 148 | in 149 | ( { model | store = newStore } 150 | , Cmd.map StoreMsg storeCmd 151 | ) 152 | |> Cmd.andThen (sendDataRequests requests) 153 | |> Cmd.andThenMaybe addToast maybeToast 154 | 155 | ToastMsg toastMsg -> 156 | let 157 | ( newNotifications, toastCmd ) = 158 | Toast.update toastMsg model.notifications 159 | in 160 | ( { model | notifications = newNotifications } 161 | , Cmd.map ToastMsg toastCmd 162 | ) 163 | 164 | UrlChanged url -> 165 | let 166 | newRoute : Route 167 | newRoute = 168 | Route.fromUrl url 169 | 170 | requests : List Store.Action 171 | requests = 172 | dataRequests model.store newRoute 173 | in 174 | ( { model | route = newRoute } 175 | , Cmd.none 176 | ) 177 | |> Cmd.andThen (sendDataRequests requests) 178 | 179 | UrlRequested request -> 180 | case request of 181 | Browser.Internal url -> 182 | ( model, Browser.Navigation.pushUrl model.navKey (Url.toString url) ) 183 | 184 | Browser.External urlString -> 185 | ( model, Browser.Navigation.load urlString ) 186 | 187 | CreatePost data -> 188 | let 189 | request : Store.Action 190 | request = 191 | Store.CreatePost data 192 | in 193 | model 194 | |> sendDataRequest request 195 | |> Cmd.andThen (addToast (StoreActionSent request)) 196 | 197 | OpenFailureDetails action err -> 198 | ( { model | failureDetailsModal = Just ( action, err ) } 199 | , Cmd.none 200 | ) 201 | 202 | CloseFailureDetails -> 203 | ( { model | failureDetailsModal = Nothing } 204 | , Cmd.none 205 | ) 206 | 207 | CopyFailureDetails -> 208 | let 209 | _ = 210 | Debug.log "TODO copy failure" 211 | in 212 | ( model 213 | , Cmd.none 214 | ) 215 | 216 | 217 | addToast : Toast -> Model -> ( Model, Cmd Msg ) 218 | addToast toast model = 219 | let 220 | ( newNotifications, toastCmd ) = 221 | Toast.add model.notifications (createToast toast) 222 | in 223 | ( { model | notifications = newNotifications } 224 | , Cmd.map ToastMsg toastCmd 225 | ) 226 | 227 | 228 | dataRequests : Store -> Route -> List Store.Action 229 | dataRequests store route = 230 | case route of 231 | PostsRoute -> 232 | Page.Posts.dataRequests 233 | 234 | PostRoute postId -> 235 | Page.Post.dataRequests store postId 236 | 237 | UserRoute _ -> 238 | Page.User.dataRequests 239 | 240 | NotFoundRoute -> 241 | [] 242 | 243 | 244 | subscriptions : Model -> Sub Msg 245 | subscriptions _ = 246 | Sub.none 247 | 248 | 249 | postsPageConfig : Page.Posts.Config Msg 250 | postsPageConfig = 251 | { createPost = CreatePost 252 | } 253 | 254 | 255 | view : Model -> Browser.Document Msg 256 | view model = 257 | { title = "Example Store app" 258 | , body = 259 | [ Html.div 260 | [ Attrs.class "p-4 gap-4 flex flex-col" ] 261 | [ storeLoadView model.store 262 | , navView model.store model.route 263 | , case model.route of 264 | PostsRoute -> 265 | Page.Posts.view postsPageConfig model.store 266 | 267 | PostRoute postId -> 268 | Page.Post.view model.store postId 269 | 270 | UserRoute userId -> 271 | Page.User.view model.store userId 272 | 273 | NotFoundRoute -> 274 | Html.text "Page not found" 275 | , model.failureDetailsModal 276 | |> Maybe.map failureDetailsModalView 277 | |> Maybe.withDefault (Html.text "") 278 | ] 279 | , notificationsView model.notifications 280 | ] 281 | } 282 | 283 | 284 | failureDetailsModalView : ( Store.Action, Http.Error ) -> Html Msg 285 | failureDetailsModalView _ = 286 | let 287 | url : String 288 | url = 289 | "POST https://api.example.com/endpoint" 290 | 291 | request : String 292 | request = 293 | """{ 294 | "title": "New post", 295 | "authorId": "123", 296 | "content": "Hello there!" 297 | }""" 298 | 299 | response : String 300 | response = 301 | """{ 302 | "error": "name_already_exists" 303 | }""" 304 | 305 | curl : String 306 | curl = 307 | """curl 'https://api.example.com/endpoint' \\ 308 | -X 'POST' \\ 309 | --data-binary '{ "title": "New post", "authorId": "123", "content": "Hello there!" }'""" 310 | in 311 | Html.div [ Attrs.class "absolute inset-20 h-min z-20 flex flex-col border-2 border-orange-300 bg-orange-200" ] 312 | [ Html.div 313 | [ Attrs.class "p-2 flex flex-col gap-2 overflow-y-auto" ] 314 | [ Html.p [] [ Html.text "Something went wrong... Please send this to our support:" ] 315 | , Html.div [] 316 | [ Html.h3 [] [ Html.text "Endpoint:" ] 317 | , Html.code [ Attrs.class UI.code ] [ Html.text url ] 318 | ] 319 | , Html.div [] 320 | [ Html.h3 [] [ Html.text "Request:" ] 321 | , Html.code [ Attrs.class UI.code ] [ Html.text request ] 322 | ] 323 | , Html.div [] 324 | [ Html.h3 [] [ Html.text "Response:" ] 325 | , Html.code [ Attrs.class UI.code ] [ Html.text response ] 326 | ] 327 | , Html.div [] 328 | [ Html.h3 [] [ Html.text "CURL:" ] 329 | , Html.code [ Attrs.class UI.code ] [ Html.text curl ] 330 | ] 331 | ] 332 | , Html.div [ Attrs.class "flex flex-row gap-2 p-2" ] 333 | [ Html.button 334 | [ Events.onClick CopyFailureDetails 335 | , Attrs.class UI.blueButton 336 | ] 337 | [ Html.text "Copy to clipboard" ] 338 | , Html.button 339 | [ Events.onClick CloseFailureDetails 340 | , Attrs.class UI.redButton 341 | ] 342 | [ Html.text "Close" ] 343 | ] 344 | ] 345 | 346 | 347 | notificationsView : Toast.Tray Toast -> Html Msg 348 | notificationsView tray = 349 | Html.div 350 | [ Attrs.class "absolute top-4 right-4 z-10" ] 351 | [ Toast.render toastView tray (Toast.config ToastMsg) ] 352 | 353 | 354 | createToast : Toast -> Toast.Toast Toast 355 | createToast toast = 356 | case toast of 357 | StoreActionSent _ -> 358 | toast 359 | |> Toast.expireIn 3000 360 | |> Toast.withExitTransition 900 361 | 362 | StoreActionSuccess _ -> 363 | toast 364 | |> Toast.expireIn 3000 365 | |> Toast.withExitTransition 900 366 | 367 | StoreActionFailure _ _ -> 368 | toast 369 | |> Toast.persistent 370 | |> Toast.withExitTransition 900 371 | 372 | 373 | toastView : List (Attribute Msg) -> Toast.Info Toast -> Html Msg 374 | toastView attrs toast = 375 | (case toast.content of 376 | StoreActionSent action -> 377 | case action of 378 | Store.GetPosts -> 379 | Nothing 380 | 381 | Store.GetUsers -> 382 | Nothing 383 | 384 | Store.GetImage _ -> 385 | Nothing 386 | 387 | Store.CreatePost post -> 388 | Just <| 389 | UI.Toast.sent 390 | { close = ToastMsg (Toast.exit toast.id) } 391 | ("Creating post '" 392 | ++ post.title 393 | ++ "'" 394 | ) 395 | 396 | StoreActionSuccess action -> 397 | case action of 398 | Store.GetPosts -> 399 | Nothing 400 | 401 | Store.GetUsers -> 402 | Nothing 403 | 404 | Store.GetImage _ -> 405 | Nothing 406 | 407 | Store.CreatePost post -> 408 | Just <| 409 | UI.Toast.success 410 | { close = ToastMsg (Toast.exit toast.id) } 411 | ("Created post '" 412 | ++ post.title 413 | ++ "'" 414 | ) 415 | 416 | StoreActionFailure action err -> 417 | let 418 | failure : String -> Maybe (Html Msg) 419 | failure message = 420 | Just <| 421 | UI.Toast.failure 422 | { close = ToastMsg (Toast.exit toast.id) 423 | , openDetails = OpenFailureDetails action err 424 | } 425 | message 426 | in 427 | case action of 428 | Store.GetPosts -> 429 | failure "Failed to get posts" 430 | 431 | Store.GetUsers -> 432 | failure "Failed to get users" 433 | 434 | Store.GetImage id -> 435 | failure 436 | ("Failed to get image '" 437 | ++ id 438 | ++ "'" 439 | ) 440 | 441 | Store.CreatePost post -> 442 | failure 443 | ("Failed to create post '" 444 | ++ post.title 445 | ++ "'" 446 | ) 447 | ) 448 | |> Maybe.map 449 | (\html -> 450 | Html.div 451 | (Attrs.classList 452 | [ ( "transition duration-300 transform-gpu", True ) 453 | , ( "opacity-0 translate-x-full", toast.phase == Toast.Exit ) 454 | ] 455 | :: attrs 456 | ) 457 | [ html ] 458 | ) 459 | |> Maybe.withDefault (Html.text "") 460 | 461 | 462 | navView : Store -> Route -> Html Msg 463 | navView store currentRoute = 464 | let 465 | routeLabel : Route -> String 466 | routeLabel route = 467 | case route of 468 | PostsRoute -> 469 | "All Posts" 470 | 471 | PostRoute id -> 472 | "Post: " 473 | ++ (RemoteData.get id store.posts 474 | |> RemoteData.map (\{ title } -> "\"" ++ title ++ "\"") 475 | |> RemoteData.withDefault ("#" ++ id) 476 | ) 477 | 478 | UserRoute id -> 479 | "User: " 480 | ++ (RemoteData.get id store.users 481 | |> RemoteData.map .name 482 | |> RemoteData.withDefault ("#" ++ id) 483 | ) 484 | 485 | NotFoundRoute -> 486 | "Not Found" 487 | 488 | stableRoutes : List Route 489 | stableRoutes = 490 | [ PostsRoute ] 491 | 492 | allRoutes : List Route 493 | allRoutes = 494 | if List.member currentRoute stableRoutes then 495 | stableRoutes 496 | 497 | else 498 | stableRoutes ++ [ currentRoute ] 499 | in 500 | Html.div [ Attrs.class "flex flex-row gap-2" ] 501 | (Html.text "Nav: " 502 | :: (allRoutes 503 | |> List.map 504 | (\route -> 505 | Html.a 506 | [ Attrs.href (Route.toString route) 507 | , Attrs.classList 508 | [ ( UI.a, True ) 509 | , ( "font-bold", route == currentRoute ) 510 | ] 511 | ] 512 | [ Html.text (routeLabel route) ] 513 | ) 514 | ) 515 | ) 516 | 517 | 518 | storeLoadView : Store -> Html msg 519 | storeLoadView store = 520 | let 521 | row : String -> String -> Html msg 522 | row label content = 523 | Html.tr [] 524 | [ Html.td [ Attrs.class UI.td ] [ Html.text label ] 525 | , Html.td [ Attrs.class UI.td ] [ Html.text content ] 526 | ] 527 | 528 | webdataSquare : WebData a -> String 529 | webdataSquare data = 530 | case data of 531 | NotAsked -> 532 | "⬜️" 533 | 534 | Loading -> 535 | "🟨" 536 | 537 | Failure _ -> 538 | "🟥" 539 | 540 | Success _ -> 541 | "🟩" 542 | 543 | webdataDict : String -> WebData (Dict comparable a) -> Html msg 544 | webdataDict label data = 545 | row label 546 | (webdataSquare data 547 | ++ (data 548 | |> RemoteData.map (\dict_ -> " Dict (" ++ String.fromInt (Dict.size dict_) ++ " items)") 549 | |> RemoteData.withDefault "" 550 | ) 551 | ) 552 | 553 | dictWebdata : String -> Dict comparable (WebData a) -> Html msg 554 | dictWebdata label dict = 555 | let 556 | contents : String 557 | contents = 558 | if Dict.isEmpty dict then 559 | "empty" 560 | 561 | else 562 | dict 563 | |> Dict.values 564 | |> List.map webdataSquare 565 | |> String.concat 566 | in 567 | row label ("Dict (" ++ contents ++ ")") 568 | in 569 | Html.div 570 | [ Attrs.class "text-slate-400" ] 571 | [ Html.h2 572 | [ Attrs.class "font-bold mb-2 border-b border-b-4 border-slate-200" ] 573 | [ Html.text "Store status" ] 574 | , Html.table 575 | [] 576 | [ webdataDict "posts" store.posts 577 | , webdataDict "users" store.users 578 | , dictWebdata "images" store.images 579 | ] 580 | ] 581 | -------------------------------------------------------------------------------- /src/Page/Post.elm: -------------------------------------------------------------------------------- 1 | module Page.Post exposing (dataRequests, view) 2 | 3 | import API.Image exposing (Image) 4 | import API.Post exposing (Post, PostId) 5 | import API.User exposing (User) 6 | import Html exposing (Html) 7 | import Html.Attributes as Attrs 8 | import RemoteData 9 | import RemoteData.Extra as RemoteData 10 | import Route exposing (Route(..)) 11 | import Store exposing (Store) 12 | import UI 13 | 14 | 15 | dataRequests : Store -> PostId -> List Store.Action 16 | dataRequests store postId = 17 | let 18 | staticRequests : List Store.Action 19 | staticRequests = 20 | [ Store.GetPosts 21 | , Store.GetUsers 22 | ] 23 | 24 | dynamicRequests : List Store.Action 25 | dynamicRequests = 26 | RemoteData.get postId store.posts 27 | |> RemoteData.map .imageIds 28 | |> RemoteData.withDefault [] 29 | |> List.map Store.GetImage 30 | in 31 | staticRequests ++ dynamicRequests 32 | 33 | 34 | view : Store -> PostId -> Html msg 35 | view store postId = 36 | let 37 | title : String 38 | title = 39 | RemoteData.get postId store.posts 40 | |> RemoteData.map (.title >> (\s -> "\"" ++ s ++ "\"")) 41 | |> RemoteData.withDefault ("#" ++ postId) 42 | |> (\s -> "Post: " ++ s) 43 | in 44 | Html.div 45 | [] 46 | [ Html.h1 47 | [ Attrs.class UI.h1 ] 48 | [ Html.text title ] 49 | , RemoteData.view title (RemoteData.get postId store.posts) <| 50 | \post -> 51 | RemoteData.view "Author" (RemoteData.get post.authorId store.users) <| 52 | \user -> 53 | RemoteData.view "Images" (RemoteData.traverse (\imageId -> RemoteData.get_ imageId store.images) post.imageIds) <| 54 | \images -> postView post user images 55 | ] 56 | 57 | 58 | postView : Post -> User -> List Image -> Html msg 59 | postView post user images = 60 | Html.div [ Attrs.class "gap-2 flex flex-col" ] 61 | [ Html.div [] 62 | [ Html.text "Author: " 63 | , Html.a 64 | [ Attrs.href (Route.toString (UserRoute user.id)) 65 | , Attrs.class UI.a 66 | ] 67 | [ Html.text user.name ] 68 | ] 69 | , Html.div [] [ Html.text post.content ] 70 | , Html.div [] (images |> List.map (\image -> Html.img [ Attrs.src image.content ] [])) 71 | ] 72 | -------------------------------------------------------------------------------- /src/Page/Posts.elm: -------------------------------------------------------------------------------- 1 | module Page.Posts exposing (Config, dataRequests, view) 2 | 3 | import API.Post exposing (Post, PostCreateData, PostId) 4 | import API.User exposing (User, UserId) 5 | import Dict exposing (Dict) 6 | import Html exposing (Html) 7 | import Html.Attributes as Attrs 8 | import Html.Events as Events 9 | import RemoteData.Extra as RemoteData 10 | import Route exposing (Route(..)) 11 | import Store exposing (Store) 12 | import UI 13 | 14 | 15 | dataRequests : List Store.Action 16 | dataRequests = 17 | [ Store.GetPosts 18 | , Store.GetUsers 19 | ] 20 | 21 | 22 | type alias Config msg = 23 | { createPost : PostCreateData -> msg 24 | } 25 | 26 | 27 | view : Config msg -> Store -> Html msg 28 | view config store = 29 | Html.div 30 | [] 31 | [ Html.h1 32 | [ Attrs.class UI.h1 ] 33 | [ Html.text "Posts" ] 34 | , RemoteData.view "Posts" store.posts <| 35 | \posts -> 36 | RemoteData.view "Users" store.users <| 37 | \users -> 38 | postsView config posts users 39 | ] 40 | 41 | 42 | postsView : Config msg -> Dict PostId Post -> Dict UserId User -> Html msg 43 | postsView config posts users = 44 | Html.div 45 | [ Attrs.class "flex flex-col gap-2" ] 46 | [ Html.table [ Attrs.class "w-min" ] 47 | [ Html.thead [] 48 | [ Html.tr [] 49 | ([ "ID" 50 | , "Title" 51 | , "Author" 52 | , "Images" 53 | ] 54 | |> List.map 55 | (\title -> 56 | Html.th 57 | [ Attrs.class UI.th ] 58 | [ Html.text title ] 59 | ) 60 | ) 61 | ] 62 | , Html.tbody [] 63 | (posts 64 | |> Dict.values 65 | |> List.map (postRowView users) 66 | ) 67 | ] 68 | , Html.div 69 | [ Attrs.class "flex flex-row gap-2" ] 70 | [ Html.button 71 | [ Events.onClick 72 | (config.createPost 73 | { title = "A" 74 | , authorId = "4" 75 | , content = "I am A!" 76 | } 77 | ) 78 | , Attrs.class UI.blueButton 79 | ] 80 | [ Html.text "Create post A" ] 81 | , Html.button 82 | [ Events.onClick 83 | (config.createPost 84 | { title = "B" 85 | , authorId = "42" 86 | , content = "B side" 87 | } 88 | ) 89 | , Attrs.class UI.blueButton 90 | ] 91 | [ Html.text "Create post B" ] 92 | , Html.button 93 | [ Events.onClick 94 | (config.createPost 95 | { title = "C" 96 | , authorId = "999" 97 | , content = "C C C C C" 98 | } 99 | ) 100 | , Attrs.class UI.blueButton 101 | ] 102 | [ Html.text "Create post C" ] 103 | ] 104 | ] 105 | 106 | 107 | postRowView : Dict UserId User -> Post -> Html msg 108 | postRowView users post = 109 | let 110 | cell : ( String, Maybe Route ) -> Html msg 111 | cell ( text, maybeRoute ) = 112 | Html.td 113 | [ Attrs.class UI.td 114 | ] 115 | [ maybeRoute 116 | |> Maybe.map 117 | (\route -> 118 | Html.a 119 | [ Attrs.href (Route.toString route) 120 | , Attrs.class UI.a 121 | ] 122 | [ Html.text text ] 123 | ) 124 | |> Maybe.withDefault (Html.text text) 125 | ] 126 | in 127 | Html.tr [] 128 | ([ ( post.id, Just (PostRoute post.id) ) 129 | , ( post.title, Just (PostRoute post.id) ) 130 | , ( Dict.get post.authorId users 131 | |> Maybe.map .name 132 | |> Maybe.withDefault ("#" ++ post.authorId) 133 | , Just (UserRoute post.authorId) 134 | ) 135 | , ( String.repeat (List.length post.imageIds) "🌆" 136 | , Nothing 137 | ) 138 | ] 139 | |> List.map cell 140 | ) 141 | -------------------------------------------------------------------------------- /src/Page/User.elm: -------------------------------------------------------------------------------- 1 | module Page.User exposing (dataRequests, view) 2 | 3 | import API.User exposing (User, UserId) 4 | import Html exposing (Html) 5 | import Html.Attributes as Attrs 6 | import RemoteData 7 | import RemoteData.Extra as RemoteData 8 | import Store exposing (Store) 9 | import UI 10 | 11 | 12 | dataRequests : List Store.Action 13 | dataRequests = 14 | [ Store.GetUsers ] 15 | 16 | 17 | view : Store -> UserId -> Html msg 18 | view store userId = 19 | let 20 | title : String 21 | title = 22 | RemoteData.get userId store.users 23 | |> RemoteData.map .name 24 | |> RemoteData.withDefault ("#" ++ userId) 25 | |> (\s -> "User: " ++ s) 26 | in 27 | Html.div 28 | [] 29 | [ Html.h1 30 | [ Attrs.class UI.h1 ] 31 | [ Html.text title ] 32 | , RemoteData.view title (RemoteData.get userId store.users) <| 33 | \user -> userView user 34 | ] 35 | 36 | 37 | userView : User -> Html msg 38 | userView user = 39 | Html.ul [] 40 | [ Html.li [] [ Html.text <| "ID: " ++ user.id ] 41 | , Html.li [] [ Html.text <| "Name: " ++ user.name ] 42 | ] 43 | -------------------------------------------------------------------------------- /src/RemoteData/Extra.elm: -------------------------------------------------------------------------------- 1 | module RemoteData.Extra exposing (get, get_, traverse, view) 2 | 3 | import Dict exposing (Dict) 4 | import Html exposing (Html) 5 | import Html.Attributes as Attrs 6 | import Html.Extra as Html 7 | import Http 8 | import RemoteData exposing (RemoteData(..), WebData) 9 | 10 | 11 | view : String -> WebData a -> (a -> Html msg) -> Html msg 12 | view label data successView = 13 | case data of 14 | NotAsked -> 15 | Html.text "" 16 | 17 | Loading -> 18 | Html.div [ Attrs.class "inline-flex flex-col shrink items-center gap-2" ] 19 | [ Html.span [] [ Html.text <| "Loading " ++ label ++ "..." ] 20 | , Html.spinner 21 | ] 22 | 23 | Failure err -> 24 | Html.todo <| "Error view for " ++ label ++ ": " ++ Debug.toString err 25 | 26 | Success value -> 27 | successView value 28 | 29 | 30 | get : comparable -> WebData (Dict comparable a) -> WebData a 31 | get key data = 32 | data 33 | |> RemoteData.andThen 34 | (\dict -> 35 | Dict.get key dict 36 | |> RemoteData.fromMaybe 37 | (Http.BadBody ("key '" ++ Debug.toString key ++ "' not found")) 38 | ) 39 | 40 | 41 | get_ : comparable -> Dict comparable (WebData a) -> WebData a 42 | get_ key dict = 43 | Dict.get key dict 44 | |> Maybe.withDefault (Failure (Http.BadBody ("key '" ++ Debug.toString key ++ "' not found"))) 45 | 46 | 47 | traverse : (a -> RemoteData x b) -> List a -> RemoteData x (List b) 48 | traverse fn list = 49 | List.foldl 50 | (\x acc -> RemoteData.map2 (::) (fn x) acc) 51 | (Success []) 52 | list 53 | -------------------------------------------------------------------------------- /src/Route.elm: -------------------------------------------------------------------------------- 1 | module Route exposing (Route(..), fromUrl, toString) 2 | 3 | import API.Post exposing (PostId) 4 | import API.User exposing (UserId) 5 | import Url exposing (Url) 6 | import Url.Builder 7 | import Url.Parser exposing ((), Parser) 8 | 9 | 10 | type Route 11 | = PostsRoute 12 | | PostRoute PostId 13 | | UserRoute UserId 14 | | NotFoundRoute 15 | 16 | 17 | toString : Route -> String 18 | toString route = 19 | let 20 | segments : List String 21 | segments = 22 | case route of 23 | PostsRoute -> 24 | [ "posts" ] 25 | 26 | PostRoute postId -> 27 | [ "posts", postId ] 28 | 29 | UserRoute userId -> 30 | [ "users", userId ] 31 | 32 | NotFoundRoute -> 33 | [ "not-found" ] 34 | in 35 | Url.Builder.absolute segments [] 36 | 37 | 38 | fromUrl : Url -> Route 39 | fromUrl url = 40 | Url.Parser.parse parser url 41 | |> Maybe.withDefault NotFoundRoute 42 | 43 | 44 | parser : Parser (Route -> Route) Route 45 | parser = 46 | Url.Parser.oneOf 47 | [ Url.Parser.map PostsRoute Url.Parser.top 48 | , Url.Parser.map PostsRoute <| Url.Parser.s "posts" 49 | , Url.Parser.map PostRoute <| Url.Parser.s "posts" Url.Parser.string 50 | , Url.Parser.map UserRoute <| Url.Parser.s "users" Url.Parser.string 51 | , Url.Parser.map NotFoundRoute <| Url.Parser.s "not-found" 52 | ] 53 | -------------------------------------------------------------------------------- /src/Store.elm: -------------------------------------------------------------------------------- 1 | module Store exposing 2 | ( Store, init 3 | , Action(..), runAction 4 | , Msg(..), update 5 | ) 6 | 7 | {-| 8 | 9 | @docs Store, init 10 | @docs Action, runAction 11 | @docs Msg, update 12 | 13 | -} 14 | 15 | import API.Image exposing (Image, ImageId) 16 | import API.Post exposing (Post, PostCreateData, PostId) 17 | import API.User exposing (User, UserId) 18 | import Dict exposing (Dict) 19 | import Http 20 | import RemoteData exposing (RemoteData(..), WebData) 21 | 22 | 23 | type alias Store = 24 | { -- we're loading all posts at once 25 | -- GET /api/posts 26 | posts : WebData (Dict PostId Post) 27 | , -- we're loading all users at once 28 | -- GET /api/users/ 29 | users : WebData (Dict UserId User) 30 | , -- we're lazy loading images as needed 31 | -- GET /api/images/ 32 | images : Dict ImageId (WebData Image) 33 | } 34 | 35 | 36 | {-| As in, Request 37 | -} 38 | type Action 39 | = GetPosts 40 | | GetUsers 41 | | GetImage ImageId 42 | | CreatePost PostCreateData 43 | 44 | 45 | {-| As in, Response 46 | -} 47 | type Msg 48 | = HttpError Action Http.Error -- ! 49 | | GotPosts (List Post) 50 | | GotUsers (List User) 51 | | GotImage Image 52 | | CreatedPost Action Post 53 | 54 | 55 | init : Store 56 | init = 57 | { posts = NotAsked 58 | , users = NotAsked 59 | , images = Dict.empty 60 | } 61 | 62 | 63 | runAction : Action -> Store -> ( Store, Cmd Msg ) 64 | runAction action store = 65 | case action of 66 | GetPosts -> 67 | if shouldSendRequest store.posts then 68 | ( { store | posts = Loading } 69 | , send action API.Post.getAll GotPosts 70 | ) 71 | 72 | else 73 | ( store, Cmd.none ) 74 | 75 | GetUsers -> 76 | if shouldSendRequest store.users then 77 | ( { store | users = Loading } 78 | , send action API.User.getAll GotUsers 79 | ) 80 | 81 | else 82 | ( store, Cmd.none ) 83 | 84 | GetImage imageId -> 85 | if shouldSendRequest_ (Dict.get imageId store.images) then 86 | ( { store | images = Dict.insert imageId Loading store.images } 87 | , send action (API.Image.get imageId) GotImage 88 | ) 89 | 90 | else 91 | ( store, Cmd.none ) 92 | 93 | CreatePost postCreateData -> 94 | ( store 95 | , send action (API.Post.create postCreateData) (CreatedPost action) 96 | ) 97 | 98 | 99 | shouldSendRequest : WebData a -> Bool 100 | shouldSendRequest webdata = 101 | case webdata of 102 | NotAsked -> 103 | True 104 | 105 | Loading -> 106 | False 107 | 108 | Failure _ -> 109 | False 110 | 111 | Success _ -> 112 | False 113 | 114 | 115 | shouldSendRequest_ : Maybe (WebData a) -> Bool 116 | shouldSendRequest_ maybeWebdata = 117 | case maybeWebdata of 118 | Nothing -> 119 | True 120 | 121 | Just webdata -> 122 | shouldSendRequest webdata 123 | 124 | 125 | send : Action -> ((Result Http.Error a -> Msg) -> Cmd Msg) -> (a -> Msg) -> Cmd Msg 126 | send action toCmd toSuccessMsg = 127 | toCmd 128 | (\result -> 129 | case result of 130 | Err err -> 131 | HttpError action err 132 | 133 | Ok success -> 134 | toSuccessMsg success 135 | ) 136 | 137 | 138 | update : Msg -> Store -> ( Store, Cmd Msg ) 139 | update msg store = 140 | case msg of 141 | GotPosts posts -> 142 | ( { store | posts = Success (dictByIds posts) } 143 | , Cmd.none 144 | ) 145 | 146 | GotUsers users -> 147 | ( { store | users = Success (dictByIds users) } 148 | , Cmd.none 149 | ) 150 | 151 | GotImage image -> 152 | ( { store | images = Dict.insert image.id (Success image) store.images } 153 | , Cmd.none 154 | ) 155 | 156 | CreatedPost _ post -> 157 | ( { store | posts = RemoteData.map (Dict.insert post.id post) store.posts } 158 | , Cmd.none 159 | ) 160 | 161 | HttpError action error -> 162 | ( saveFailure action error store 163 | , Cmd.none 164 | ) 165 | 166 | 167 | saveFailure : Action -> Http.Error -> Store -> Store 168 | saveFailure action err store = 169 | case action of 170 | GetPosts -> 171 | { store | posts = Failure err } 172 | 173 | GetUsers -> 174 | { store | users = Failure err } 175 | 176 | GetImage imageId -> 177 | { store | images = Dict.insert imageId (Failure err) store.images } 178 | 179 | CreatePost _ -> 180 | store 181 | 182 | 183 | dictByIds : List { a | id : String } -> Dict String { a | id : String } 184 | dictByIds list = 185 | list 186 | |> List.map (\item -> ( item.id, item )) 187 | |> Dict.fromList 188 | -------------------------------------------------------------------------------- /src/UI.elm: -------------------------------------------------------------------------------- 1 | module UI exposing (a, blueButton, code, h1, redButton, td, th) 2 | 3 | 4 | td : String 5 | td = 6 | "px-2 border-x border-y border-slate-200" 7 | 8 | 9 | th : String 10 | th = 11 | "px-2 bg-slate-100 border-t border-x border-slate-200" 12 | 13 | 14 | h1 : String 15 | h1 = 16 | "font-bold mb-2 border-b border-b-4 border-slate-200" 17 | 18 | 19 | a : String 20 | a = 21 | "underline text-sky-600 hover:text-sky-500" 22 | 23 | 24 | blueButton : String 25 | blueButton = 26 | "px-4 py-2 font-semibold text-sm bg-sky-500 hover:bg-sky-400 text-white rounded-none shadow-sm hover:shadow-md active:bg-sky-300" 27 | 28 | 29 | redButton : String 30 | redButton = 31 | "px-4 py-2 font-semibold text-sm bg-red-500 hover:bg-red-400 text-white rounded-none shadow-sm hover:shadow-md active:bg-red-300" 32 | 33 | 34 | code : String 35 | code = 36 | "flex p-2 text-xs rounded border-2 border-slate-300 bg-slate-50 whitespace-pre-wrap" 37 | -------------------------------------------------------------------------------- /src/UI/Toast.elm: -------------------------------------------------------------------------------- 1 | module UI.Toast exposing (failure, sent, success) 2 | 3 | import Html exposing (Html) 4 | import Html.Attributes as Attrs 5 | import Html.Events as Events 6 | import Svg 7 | import Svg.Attributes as SvgAttrs 8 | import UI 9 | 10 | 11 | htmlToast : Html msg -> { close : msg } -> Html msg -> Html msg 12 | htmlToast icon { close } contents = 13 | Html.div 14 | [ Attrs.class "flex items-center w-full max-w-xs p-4 mb-4 text-gray-500 bg-white rounded-lg shadow-md dark:text-gray-400 dark:bg-gray-800" 15 | , Attrs.attribute "role" "alert" 16 | ] 17 | [ icon 18 | , Html.div 19 | [ Attrs.class "ml-3" ] 20 | [ contents ] 21 | , Html.button 22 | [ Attrs.type_ "button" 23 | , Attrs.class "ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700" 24 | , Attrs.attribute "aria-label" "Close" 25 | , Events.onClick close 26 | ] 27 | [ Html.span [ Attrs.class "sr-only" ] [ Html.text "Close" ] 28 | , Svg.svg 29 | [ SvgAttrs.class "w-5 h-5" 30 | , SvgAttrs.fill "currentColor" 31 | , SvgAttrs.viewBox "0 0 20 20" 32 | ] 33 | [ Svg.path 34 | [ SvgAttrs.fillRule "evenodd" 35 | , SvgAttrs.d "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" 36 | , SvgAttrs.clipRule "evenodd" 37 | ] 38 | [] 39 | ] 40 | ] 41 | ] 42 | 43 | 44 | textToast : Html msg -> { close : msg } -> String -> Html msg 45 | textToast icon c message = 46 | htmlToast 47 | icon 48 | c 49 | (Html.span 50 | [ Attrs.class "text-sm font-normal" ] 51 | [ Html.text message ] 52 | ) 53 | 54 | 55 | success : { close : msg } -> String -> Html msg 56 | success close message = 57 | let 58 | successIcon : Html msg 59 | successIcon = 60 | Html.div 61 | [ Attrs.class "inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200" ] 62 | [ Svg.svg 63 | [ SvgAttrs.class "w-5 h-5" 64 | , SvgAttrs.fill "currentColor" 65 | , SvgAttrs.viewBox "0 0 20 20" 66 | ] 67 | [ Svg.path 68 | [ SvgAttrs.fillRule "evenodd" 69 | , SvgAttrs.d "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" 70 | , SvgAttrs.clipRule "evenodd" 71 | ] 72 | [] 73 | ] 74 | ] 75 | in 76 | textToast successIcon close message 77 | 78 | 79 | failure : { close : msg, openDetails : msg } -> String -> Html msg 80 | failure c message = 81 | let 82 | content : Html msg 83 | content = 84 | Html.div 85 | [ Attrs.class "text-sm font-normal" ] 86 | [ Html.p [] [ Html.text message ] 87 | , Html.button 88 | [ Events.onClick c.openDetails 89 | , Attrs.class UI.redButton 90 | ] 91 | [ Html.text "Details" ] 92 | ] 93 | 94 | failureIcon : Html msg 95 | failureIcon = 96 | Html.div 97 | [ Attrs.class "inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-red-500 bg-red-100 rounded-lg dark:bg-red-800 dark:text-red-200" ] 98 | [ Svg.svg 99 | [ SvgAttrs.class "w-5 h-5" 100 | , SvgAttrs.fill "currentColor" 101 | , SvgAttrs.viewBox "0 0 20 20" 102 | ] 103 | [ Svg.path 104 | [ SvgAttrs.fillRule "evenodd" 105 | , SvgAttrs.d "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" 106 | , SvgAttrs.clipRule "evenodd" 107 | ] 108 | [] 109 | ] 110 | ] 111 | in 112 | htmlToast failureIcon { close = c.close } content 113 | 114 | 115 | sent : { close : msg } -> String -> Html msg 116 | sent close message = 117 | let 118 | sentIcon : Html msg 119 | sentIcon = 120 | Html.div 121 | [ Attrs.class "inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-blue-500 bg-blue-100 rounded-lg dark:bg-blue-800 dark:text-blue-200" ] 122 | [ Svg.svg 123 | [ SvgAttrs.class "w-5 h-5 text-blue-600 dark:text-blue-500" 124 | , SvgAttrs.viewBox "0 0 448 512" 125 | ] 126 | [ Svg.path 127 | [ SvgAttrs.fill "currentColor" 128 | , SvgAttrs.d "M438.6 278.6l-160 160C272.4 444.9 264.2 448 256 448s-16.38-3.125-22.62-9.375c-12.5-12.5-12.5-32.75 0-45.25L338.8 288H32C14.33 288 .0016 273.7 .0016 256S14.33 224 32 224h306.8l-105.4-105.4c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0l160 160C451.1 245.9 451.1 266.1 438.6 278.6z" 129 | ] 130 | [] 131 | ] 132 | ] 133 | in 134 | textToast sentIcon close message 135 | -------------------------------------------------------------------------------- /watch-compile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | COLOR_OFF="\e[0m"; 4 | DIM="\e[2m"; 5 | 6 | function compile { 7 | find src -type f -name '*.elm' | xargs elm make --output=/dev/null 8 | } 9 | 10 | function run { 11 | clear; 12 | tput reset; 13 | echo -en "\033c\033[3J"; 14 | 15 | echo -en "${DIM}"; 16 | date -R; 17 | echo -en "${COLOR_OFF}"; 18 | 19 | compile; 20 | } 21 | 22 | run; 23 | 24 | find src -type f -name '*.elm' | xargs chokidar | while read WHATEVER; do 25 | run; 26 | done; 27 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@sindresorhus/is@^2.0.0": 6 | version "2.1.1" 7 | resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1" 8 | integrity sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg== 9 | 10 | "@szmarczak/http-timer@^4.0.0": 11 | version "4.0.6" 12 | resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" 13 | integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== 14 | dependencies: 15 | defer-to-connect "^2.0.0" 16 | 17 | "@types/cacheable-request@^6.0.1": 18 | version "6.0.2" 19 | resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" 20 | integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== 21 | dependencies: 22 | "@types/http-cache-semantics" "*" 23 | "@types/keyv" "*" 24 | "@types/node" "*" 25 | "@types/responselike" "*" 26 | 27 | "@types/http-cache-semantics@*": 28 | version "4.0.1" 29 | resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" 30 | integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== 31 | 32 | "@types/json-buffer@~3.0.0": 33 | version "3.0.0" 34 | resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" 35 | integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ== 36 | 37 | "@types/keyv@*", "@types/keyv@^3.1.1": 38 | version "3.1.4" 39 | resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" 40 | integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== 41 | dependencies: 42 | "@types/node" "*" 43 | 44 | "@types/node@*": 45 | version "17.0.29" 46 | resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.29.tgz#7f2e1159231d4a077bb660edab0fde373e375a3d" 47 | integrity sha512-tx5jMmMFwx7wBwq/V7OohKDVb/JwJU5qCVkeLMh1//xycAJ/ESuw9aJ9SEtlCZDYi2pBfe4JkisSoAtbOsBNAA== 48 | 49 | "@types/responselike@*": 50 | version "1.0.0" 51 | resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" 52 | integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== 53 | dependencies: 54 | "@types/node" "*" 55 | 56 | ansi-escapes@^4.2.1: 57 | version "4.3.2" 58 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" 59 | integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== 60 | dependencies: 61 | type-fest "^0.21.3" 62 | 63 | ansi-regex@^2.0.0: 64 | version "2.1.1" 65 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 66 | integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= 67 | 68 | ansi-regex@^5.0.1: 69 | version "5.0.1" 70 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 71 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 72 | 73 | ansi-styles@^2.2.1: 74 | version "2.2.1" 75 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 76 | integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= 77 | 78 | ansi-styles@^4.0.0, ansi-styles@^4.1.0: 79 | version "4.3.0" 80 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" 81 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 82 | dependencies: 83 | color-convert "^2.0.1" 84 | 85 | anymatch@^3.0.1, anymatch@~3.1.2: 86 | version "3.1.2" 87 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" 88 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== 89 | dependencies: 90 | normalize-path "^3.0.0" 91 | picomatch "^2.0.4" 92 | 93 | async-limiter@^1.0.0: 94 | version "1.0.1" 95 | resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" 96 | integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== 97 | 98 | at-least-node@^1.0.0: 99 | version "1.0.0" 100 | resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" 101 | integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== 102 | 103 | balanced-match@^1.0.0: 104 | version "1.0.2" 105 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 106 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 107 | 108 | base64-js@^1.3.1: 109 | version "1.5.1" 110 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" 111 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 112 | 113 | binary-extensions@^2.0.0: 114 | version "2.2.0" 115 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 116 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 117 | 118 | bl@^4.1.0: 119 | version "4.1.0" 120 | resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" 121 | integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== 122 | dependencies: 123 | buffer "^5.5.0" 124 | inherits "^2.0.4" 125 | readable-stream "^3.4.0" 126 | 127 | brace-expansion@^1.1.7: 128 | version "1.1.11" 129 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 130 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 131 | dependencies: 132 | balanced-match "^1.0.0" 133 | concat-map "0.0.1" 134 | 135 | braces@^3.0.2, braces@~3.0.2: 136 | version "3.0.2" 137 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 138 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 139 | dependencies: 140 | fill-range "^7.0.1" 141 | 142 | buffer@^5.5.0: 143 | version "5.7.1" 144 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" 145 | integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== 146 | dependencies: 147 | base64-js "^1.3.1" 148 | ieee754 "^1.1.13" 149 | 150 | cacheable-lookup@^2.0.0: 151 | version "2.0.1" 152 | resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz#87be64a18b925234875e10a9bb1ebca4adce6b38" 153 | integrity sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg== 154 | dependencies: 155 | "@types/keyv" "^3.1.1" 156 | keyv "^4.0.0" 157 | 158 | cacheable-request@^7.0.1: 159 | version "7.0.2" 160 | resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" 161 | integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== 162 | dependencies: 163 | clone-response "^1.0.2" 164 | get-stream "^5.1.0" 165 | http-cache-semantics "^4.0.0" 166 | keyv "^4.0.0" 167 | lowercase-keys "^2.0.0" 168 | normalize-url "^6.0.1" 169 | responselike "^2.0.0" 170 | 171 | chalk@^1.1.1: 172 | version "1.1.3" 173 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 174 | integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= 175 | dependencies: 176 | ansi-styles "^2.2.1" 177 | escape-string-regexp "^1.0.2" 178 | has-ansi "^2.0.0" 179 | strip-ansi "^3.0.0" 180 | supports-color "^2.0.0" 181 | 182 | chalk@^4.0.0, chalk@^4.1.0: 183 | version "4.1.2" 184 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" 185 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 186 | dependencies: 187 | ansi-styles "^4.1.0" 188 | supports-color "^7.1.0" 189 | 190 | charenc@0.0.2: 191 | version "0.0.2" 192 | resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" 193 | integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= 194 | 195 | chokidar@3.0.2: 196 | version "3.0.2" 197 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.0.2.tgz#0d1cd6d04eb2df0327446188cd13736a3367d681" 198 | integrity sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA== 199 | dependencies: 200 | anymatch "^3.0.1" 201 | braces "^3.0.2" 202 | glob-parent "^5.0.0" 203 | is-binary-path "^2.1.0" 204 | is-glob "^4.0.1" 205 | normalize-path "^3.0.0" 206 | readdirp "^3.1.1" 207 | optionalDependencies: 208 | fsevents "^2.0.6" 209 | 210 | chokidar@^3.5.2: 211 | version "3.5.3" 212 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 213 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 214 | dependencies: 215 | anymatch "~3.1.2" 216 | braces "~3.0.2" 217 | glob-parent "~5.1.2" 218 | is-binary-path "~2.1.0" 219 | is-glob "~4.0.1" 220 | normalize-path "~3.0.0" 221 | readdirp "~3.6.0" 222 | optionalDependencies: 223 | fsevents "~2.3.2" 224 | 225 | cli-cursor@^3.1.0: 226 | version "3.1.0" 227 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" 228 | integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== 229 | dependencies: 230 | restore-cursor "^3.1.0" 231 | 232 | cli-spinners@^2.5.0: 233 | version "2.6.1" 234 | resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" 235 | integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== 236 | 237 | clone-response@^1.0.2: 238 | version "1.0.2" 239 | resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" 240 | integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= 241 | dependencies: 242 | mimic-response "^1.0.0" 243 | 244 | clone@^1.0.2: 245 | version "1.0.4" 246 | resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" 247 | integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= 248 | 249 | color-convert@^2.0.1: 250 | version "2.0.1" 251 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 252 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 253 | dependencies: 254 | color-name "~1.1.4" 255 | 256 | color-name@~1.1.4: 257 | version "1.1.4" 258 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 259 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 260 | 261 | commander@2.17.1: 262 | version "2.17.1" 263 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" 264 | integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== 265 | 266 | compress-brotli@^1.3.6: 267 | version "1.3.6" 268 | resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.6.tgz#64bd6f21f4f3e9841dbac392f4c29218caf5e9d9" 269 | integrity sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ== 270 | dependencies: 271 | "@types/json-buffer" "~3.0.0" 272 | json-buffer "~3.0.1" 273 | 274 | concat-map@0.0.1: 275 | version "0.0.1" 276 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 277 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 278 | 279 | crocks@0.12.1: 280 | version "0.12.1" 281 | resolved "https://registry.yarnpkg.com/crocks/-/crocks-0.12.1.tgz#cd5d552da2c0291b0fb72c35f7f66751f458d604" 282 | integrity sha512-2qCRJwBmPlRQXzd50k9gt9PaItultOP8lj/cKSH2Eai9aeBuNqAnDuyolAm9TGn6Pw/4BgbxtPJLU1S+tQ4WMQ== 283 | 284 | cross-spawn@5.0.1: 285 | version "5.0.1" 286 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.0.1.tgz#a3bbb302db2297cbea3c04edf36941f4613aa399" 287 | integrity sha1-o7uzAtsil8vqPATt82lB9GE6o5k= 288 | dependencies: 289 | lru-cache "^4.0.1" 290 | shebang-command "^1.2.0" 291 | which "^1.2.9" 292 | 293 | cross-spawn@^6.0.0: 294 | version "6.0.5" 295 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 296 | integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== 297 | dependencies: 298 | nice-try "^1.0.4" 299 | path-key "^2.0.1" 300 | semver "^5.5.0" 301 | shebang-command "^1.2.0" 302 | which "^1.2.9" 303 | 304 | cross-spawn@^7.0.3: 305 | version "7.0.3" 306 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 307 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 308 | dependencies: 309 | path-key "^3.1.0" 310 | shebang-command "^2.0.0" 311 | which "^2.0.1" 312 | 313 | crypt@0.0.2: 314 | version "0.0.2" 315 | resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" 316 | integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= 317 | 318 | debug@2.6.9: 319 | version "2.6.9" 320 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 321 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 322 | dependencies: 323 | ms "2.0.0" 324 | 325 | debug@^4.1.1: 326 | version "4.3.4" 327 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 328 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 329 | dependencies: 330 | ms "2.1.2" 331 | 332 | decompress-response@^5.0.0: 333 | version "5.0.0" 334 | resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-5.0.0.tgz#7849396e80e3d1eba8cb2f75ef4930f76461cb0f" 335 | integrity sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw== 336 | dependencies: 337 | mimic-response "^2.0.0" 338 | 339 | default-gateway@^4.2.0: 340 | version "4.2.0" 341 | resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" 342 | integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== 343 | dependencies: 344 | execa "^1.0.0" 345 | ip-regex "^2.1.0" 346 | 347 | defaults@^1.0.3: 348 | version "1.0.3" 349 | resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" 350 | integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= 351 | dependencies: 352 | clone "^1.0.2" 353 | 354 | defer-to-connect@^2.0.0: 355 | version "2.0.1" 356 | resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" 357 | integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== 358 | 359 | depd@~1.1.2: 360 | version "1.1.2" 361 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 362 | integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= 363 | 364 | destroy@~1.0.4: 365 | version "1.0.4" 366 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" 367 | integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= 368 | 369 | duplexer3@^0.1.4: 370 | version "0.1.4" 371 | resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" 372 | integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= 373 | 374 | ee-first@1.1.1: 375 | version "1.1.1" 376 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 377 | integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= 378 | 379 | elm-hot@1.1.4: 380 | version "1.1.4" 381 | resolved "https://registry.yarnpkg.com/elm-hot/-/elm-hot-1.1.4.tgz#4984592b3554670255ea4e08f4e98aa9103ab51f" 382 | integrity sha512-qPDP/o/Fkifriaxaf3E7hHFB5L6Ijihyg8is4A6xna6/h/zebUiNssbQrxywI2oxNUkr6W/leEu/WlIC1tmVnw== 383 | 384 | elm-live@^4.0.2: 385 | version "4.0.2" 386 | resolved "https://registry.yarnpkg.com/elm-live/-/elm-live-4.0.2.tgz#c943f348961e406a7963115a3eeb7cfec38cf4d6" 387 | integrity sha512-4I3UvJxF6MubC14VsgtV11B0zBxaaKtdKKsWquoaa5a3UHBIGW83qgTnt/NxOj4omOLfupaftmDaE4yRMTgTcw== 388 | dependencies: 389 | chalk "^1.1.1" 390 | chokidar "3.0.2" 391 | commander "2.17.1" 392 | crocks "0.12.1" 393 | cross-spawn "5.0.1" 394 | elm-hot "1.1.4" 395 | finalhandler "1.1.2" 396 | http-proxy "1.17.0" 397 | internal-ip "4.3.0" 398 | mime "2.4.3" 399 | open "6.4.0" 400 | pem "1.14.2" 401 | serve-static "1.14.1" 402 | ws "7.1.1" 403 | 404 | elm-review@^2.7.2: 405 | version "2.7.2" 406 | resolved "https://registry.yarnpkg.com/elm-review/-/elm-review-2.7.2.tgz#63ca79687bf0317cfaf2e36357b0914e9ce032ab" 407 | integrity sha512-llkRvxgR2GOmtc7wi2vAM+u9j61Pf1fmPsAbpsE4X0ZfusJv0Dddor3lHNNZ0MmpYzNxpm7npxNqYRXs5JIz9A== 408 | dependencies: 409 | chalk "^4.0.0" 410 | chokidar "^3.5.2" 411 | cross-spawn "^7.0.3" 412 | elm-tooling "^1.6.0" 413 | fast-levenshtein "^3.0.0" 414 | find-up "^4.1.0" 415 | folder-hash "^3.3.0" 416 | fs-extra "^9.0.0" 417 | glob "^7.1.4" 418 | got "^10.7.0" 419 | minimist "^1.2.0" 420 | ora "^5.4.0" 421 | path-key "^3.1.1" 422 | prompts "^2.2.1" 423 | strip-ansi "^6.0.0" 424 | temp "^0.9.1" 425 | terminal-link "^2.1.1" 426 | which "^2.0.2" 427 | wrap-ansi "^6.2.0" 428 | 429 | elm-tooling@^1.6.0, elm-tooling@^1.8.0: 430 | version "1.8.0" 431 | resolved "https://registry.yarnpkg.com/elm-tooling/-/elm-tooling-1.8.0.tgz#366870f95c1eb678f89ac8b7608869c7d3e1d86c" 432 | integrity sha512-IjMvW/VHqxLidlJSAocBGDBmqiZ1NS0lK/UCMRU4ULEEaTVjpSd/9Dv0mH2ok0H0egSTYx19GnrdL4Lq9h+z+A== 433 | 434 | emoji-regex@^8.0.0: 435 | version "8.0.0" 436 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 437 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 438 | 439 | encodeurl@~1.0.2: 440 | version "1.0.2" 441 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 442 | integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= 443 | 444 | end-of-stream@^1.1.0: 445 | version "1.4.4" 446 | resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" 447 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 448 | dependencies: 449 | once "^1.4.0" 450 | 451 | es6-promisify@^6.0.0: 452 | version "6.1.1" 453 | resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.1.1.tgz#46837651b7b06bf6fff893d03f29393668d01621" 454 | integrity sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg== 455 | 456 | escape-html@~1.0.3: 457 | version "1.0.3" 458 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 459 | integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= 460 | 461 | escape-string-regexp@^1.0.2: 462 | version "1.0.5" 463 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 464 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 465 | 466 | etag@~1.8.1: 467 | version "1.8.1" 468 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 469 | integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= 470 | 471 | eventemitter3@^3.0.0: 472 | version "3.1.2" 473 | resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" 474 | integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== 475 | 476 | execa@^1.0.0: 477 | version "1.0.0" 478 | resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" 479 | integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== 480 | dependencies: 481 | cross-spawn "^6.0.0" 482 | get-stream "^4.0.0" 483 | is-stream "^1.1.0" 484 | npm-run-path "^2.0.0" 485 | p-finally "^1.0.0" 486 | signal-exit "^3.0.0" 487 | strip-eof "^1.0.0" 488 | 489 | fast-levenshtein@^3.0.0: 490 | version "3.0.0" 491 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz#37b899ae47e1090e40e3fd2318e4d5f0142ca912" 492 | integrity sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ== 493 | dependencies: 494 | fastest-levenshtein "^1.0.7" 495 | 496 | fastest-levenshtein@^1.0.7: 497 | version "1.0.12" 498 | resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" 499 | integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== 500 | 501 | fill-range@^7.0.1: 502 | version "7.0.1" 503 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 504 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 505 | dependencies: 506 | to-regex-range "^5.0.1" 507 | 508 | finalhandler@1.1.2: 509 | version "1.1.2" 510 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" 511 | integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== 512 | dependencies: 513 | debug "2.6.9" 514 | encodeurl "~1.0.2" 515 | escape-html "~1.0.3" 516 | on-finished "~2.3.0" 517 | parseurl "~1.3.3" 518 | statuses "~1.5.0" 519 | unpipe "~1.0.0" 520 | 521 | find-up@^4.1.0: 522 | version "4.1.0" 523 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" 524 | integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== 525 | dependencies: 526 | locate-path "^5.0.0" 527 | path-exists "^4.0.0" 528 | 529 | folder-hash@^3.3.0: 530 | version "3.3.3" 531 | resolved "https://registry.yarnpkg.com/folder-hash/-/folder-hash-3.3.3.tgz#883c8359d54f91b3f02c1a646c00c30e5831365b" 532 | integrity sha512-SDgHBgV+RCjrYs8aUwCb9rTgbTVuSdzvFmLaChsLre1yf+D64khCW++VYciaByZ8Rm0uKF8R/XEpXuTRSGUM1A== 533 | dependencies: 534 | debug "^4.1.1" 535 | graceful-fs "~4.2.0" 536 | minimatch "~3.0.4" 537 | 538 | follow-redirects@^1.0.0: 539 | version "1.14.9" 540 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" 541 | integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== 542 | 543 | fresh@0.5.2: 544 | version "0.5.2" 545 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 546 | integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= 547 | 548 | fs-extra@^9.0.0: 549 | version "9.1.0" 550 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" 551 | integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== 552 | dependencies: 553 | at-least-node "^1.0.0" 554 | graceful-fs "^4.2.0" 555 | jsonfile "^6.0.1" 556 | universalify "^2.0.0" 557 | 558 | fs.realpath@^1.0.0: 559 | version "1.0.0" 560 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 561 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 562 | 563 | fsevents@^2.0.6, fsevents@~2.3.2: 564 | version "2.3.2" 565 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 566 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 567 | 568 | get-stream@^4.0.0: 569 | version "4.1.0" 570 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" 571 | integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== 572 | dependencies: 573 | pump "^3.0.0" 574 | 575 | get-stream@^5.0.0, get-stream@^5.1.0: 576 | version "5.2.0" 577 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" 578 | integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== 579 | dependencies: 580 | pump "^3.0.0" 581 | 582 | glob-parent@^5.0.0, glob-parent@~5.1.2: 583 | version "5.1.2" 584 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 585 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 586 | dependencies: 587 | is-glob "^4.0.1" 588 | 589 | glob@^7.1.3, glob@^7.1.4: 590 | version "7.2.0" 591 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" 592 | integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== 593 | dependencies: 594 | fs.realpath "^1.0.0" 595 | inflight "^1.0.4" 596 | inherits "2" 597 | minimatch "^3.0.4" 598 | once "^1.3.0" 599 | path-is-absolute "^1.0.0" 600 | 601 | got@^10.7.0: 602 | version "10.7.0" 603 | resolved "https://registry.yarnpkg.com/got/-/got-10.7.0.tgz#62889dbcd6cca32cd6a154cc2d0c6895121d091f" 604 | integrity sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg== 605 | dependencies: 606 | "@sindresorhus/is" "^2.0.0" 607 | "@szmarczak/http-timer" "^4.0.0" 608 | "@types/cacheable-request" "^6.0.1" 609 | cacheable-lookup "^2.0.0" 610 | cacheable-request "^7.0.1" 611 | decompress-response "^5.0.0" 612 | duplexer3 "^0.1.4" 613 | get-stream "^5.0.0" 614 | lowercase-keys "^2.0.0" 615 | mimic-response "^2.1.0" 616 | p-cancelable "^2.0.0" 617 | p-event "^4.0.0" 618 | responselike "^2.0.0" 619 | to-readable-stream "^2.0.0" 620 | type-fest "^0.10.0" 621 | 622 | graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@~4.2.0: 623 | version "4.2.10" 624 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" 625 | integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== 626 | 627 | has-ansi@^2.0.0: 628 | version "2.0.0" 629 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 630 | integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= 631 | dependencies: 632 | ansi-regex "^2.0.0" 633 | 634 | has-flag@^4.0.0: 635 | version "4.0.0" 636 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" 637 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 638 | 639 | http-cache-semantics@^4.0.0: 640 | version "4.1.0" 641 | resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" 642 | integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== 643 | 644 | http-errors@~1.7.2: 645 | version "1.7.3" 646 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" 647 | integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== 648 | dependencies: 649 | depd "~1.1.2" 650 | inherits "2.0.4" 651 | setprototypeof "1.1.1" 652 | statuses ">= 1.5.0 < 2" 653 | toidentifier "1.0.0" 654 | 655 | http-proxy@1.17.0: 656 | version "1.17.0" 657 | resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" 658 | integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== 659 | dependencies: 660 | eventemitter3 "^3.0.0" 661 | follow-redirects "^1.0.0" 662 | requires-port "^1.0.0" 663 | 664 | ieee754@^1.1.13: 665 | version "1.2.1" 666 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 667 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 668 | 669 | inflight@^1.0.4: 670 | version "1.0.6" 671 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 672 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 673 | dependencies: 674 | once "^1.3.0" 675 | wrappy "1" 676 | 677 | inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: 678 | version "2.0.4" 679 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 680 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 681 | 682 | internal-ip@4.3.0: 683 | version "4.3.0" 684 | resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" 685 | integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== 686 | dependencies: 687 | default-gateway "^4.2.0" 688 | ipaddr.js "^1.9.0" 689 | 690 | ip-regex@^2.1.0: 691 | version "2.1.0" 692 | resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" 693 | integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= 694 | 695 | ipaddr.js@^1.9.0: 696 | version "1.9.1" 697 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" 698 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 699 | 700 | is-binary-path@^2.1.0, is-binary-path@~2.1.0: 701 | version "2.1.0" 702 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 703 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 704 | dependencies: 705 | binary-extensions "^2.0.0" 706 | 707 | is-buffer@~1.1.6: 708 | version "1.1.6" 709 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 710 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 711 | 712 | is-extglob@^2.1.1: 713 | version "2.1.1" 714 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 715 | integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= 716 | 717 | is-fullwidth-code-point@^3.0.0: 718 | version "3.0.0" 719 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 720 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 721 | 722 | is-glob@^4.0.1, is-glob@~4.0.1: 723 | version "4.0.3" 724 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 725 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 726 | dependencies: 727 | is-extglob "^2.1.1" 728 | 729 | is-interactive@^1.0.0: 730 | version "1.0.0" 731 | resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" 732 | integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== 733 | 734 | is-number@^7.0.0: 735 | version "7.0.0" 736 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 737 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 738 | 739 | is-stream@^1.1.0: 740 | version "1.1.0" 741 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 742 | integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= 743 | 744 | is-unicode-supported@^0.1.0: 745 | version "0.1.0" 746 | resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" 747 | integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== 748 | 749 | is-wsl@^1.1.0: 750 | version "1.1.0" 751 | resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" 752 | integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= 753 | 754 | isexe@^2.0.0: 755 | version "2.0.0" 756 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 757 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 758 | 759 | json-buffer@3.0.1, json-buffer@~3.0.1: 760 | version "3.0.1" 761 | resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" 762 | integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== 763 | 764 | jsonfile@^6.0.1: 765 | version "6.1.0" 766 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" 767 | integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== 768 | dependencies: 769 | universalify "^2.0.0" 770 | optionalDependencies: 771 | graceful-fs "^4.1.6" 772 | 773 | keyv@^4.0.0: 774 | version "4.2.2" 775 | resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.2.2.tgz#4b6f602c0228ef4d8214c03c520bef469ed6b768" 776 | integrity sha512-uYS0vKTlBIjNCAUqrjlxmruxOEiZxZIHXyp32sdcGmP+ukFrmWUnE//RcPXJH3Vxrni1H2gsQbjHE0bH7MtMQQ== 777 | dependencies: 778 | compress-brotli "^1.3.6" 779 | json-buffer "3.0.1" 780 | 781 | kleur@^3.0.3: 782 | version "3.0.3" 783 | resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" 784 | integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== 785 | 786 | locate-path@^5.0.0: 787 | version "5.0.0" 788 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" 789 | integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== 790 | dependencies: 791 | p-locate "^4.1.0" 792 | 793 | log-symbols@^4.1.0: 794 | version "4.1.0" 795 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" 796 | integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== 797 | dependencies: 798 | chalk "^4.1.0" 799 | is-unicode-supported "^0.1.0" 800 | 801 | lowercase-keys@^2.0.0: 802 | version "2.0.0" 803 | resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" 804 | integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== 805 | 806 | lru-cache@^4.0.1: 807 | version "4.1.5" 808 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" 809 | integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== 810 | dependencies: 811 | pseudomap "^1.0.2" 812 | yallist "^2.1.2" 813 | 814 | md5@^2.2.1: 815 | version "2.3.0" 816 | resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" 817 | integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== 818 | dependencies: 819 | charenc "0.0.2" 820 | crypt "0.0.2" 821 | is-buffer "~1.1.6" 822 | 823 | mime@1.6.0: 824 | version "1.6.0" 825 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 826 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 827 | 828 | mime@2.4.3: 829 | version "2.4.3" 830 | resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.3.tgz#229687331e86f68924e6cb59e1cdd937f18275fe" 831 | integrity sha512-QgrPRJfE+riq5TPZMcHZOtm8c6K/yYrMbKIoRfapfiGLxS8OTeIfRhUGW5LU7MlRa52KOAGCfUNruqLrIBvWZw== 832 | 833 | mimic-fn@^2.1.0: 834 | version "2.1.0" 835 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" 836 | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== 837 | 838 | mimic-response@^1.0.0: 839 | version "1.0.1" 840 | resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" 841 | integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== 842 | 843 | mimic-response@^2.0.0, mimic-response@^2.1.0: 844 | version "2.1.0" 845 | resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" 846 | integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== 847 | 848 | minimatch@^3.0.4: 849 | version "3.1.2" 850 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 851 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 852 | dependencies: 853 | brace-expansion "^1.1.7" 854 | 855 | minimatch@~3.0.4: 856 | version "3.0.8" 857 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1" 858 | integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== 859 | dependencies: 860 | brace-expansion "^1.1.7" 861 | 862 | minimist@^1.2.0, minimist@^1.2.6: 863 | version "1.2.6" 864 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" 865 | integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== 866 | 867 | mkdirp@^0.5.1: 868 | version "0.5.6" 869 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" 870 | integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== 871 | dependencies: 872 | minimist "^1.2.6" 873 | 874 | ms@2.0.0: 875 | version "2.0.0" 876 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 877 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 878 | 879 | ms@2.1.1: 880 | version "2.1.1" 881 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 882 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 883 | 884 | ms@2.1.2: 885 | version "2.1.2" 886 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 887 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 888 | 889 | nice-try@^1.0.4: 890 | version "1.0.5" 891 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" 892 | integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== 893 | 894 | normalize-path@^3.0.0, normalize-path@~3.0.0: 895 | version "3.0.0" 896 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 897 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 898 | 899 | normalize-url@^6.0.1: 900 | version "6.1.0" 901 | resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" 902 | integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== 903 | 904 | npm-run-path@^2.0.0: 905 | version "2.0.2" 906 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" 907 | integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= 908 | dependencies: 909 | path-key "^2.0.0" 910 | 911 | on-finished@~2.3.0: 912 | version "2.3.0" 913 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 914 | integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= 915 | dependencies: 916 | ee-first "1.1.1" 917 | 918 | once@^1.3.0, once@^1.3.1, once@^1.4.0: 919 | version "1.4.0" 920 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 921 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 922 | dependencies: 923 | wrappy "1" 924 | 925 | onetime@^5.1.0: 926 | version "5.1.2" 927 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" 928 | integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== 929 | dependencies: 930 | mimic-fn "^2.1.0" 931 | 932 | open@6.4.0: 933 | version "6.4.0" 934 | resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" 935 | integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== 936 | dependencies: 937 | is-wsl "^1.1.0" 938 | 939 | ora@^5.4.0: 940 | version "5.4.1" 941 | resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" 942 | integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== 943 | dependencies: 944 | bl "^4.1.0" 945 | chalk "^4.1.0" 946 | cli-cursor "^3.1.0" 947 | cli-spinners "^2.5.0" 948 | is-interactive "^1.0.0" 949 | is-unicode-supported "^0.1.0" 950 | log-symbols "^4.1.0" 951 | strip-ansi "^6.0.0" 952 | wcwidth "^1.0.1" 953 | 954 | os-tmpdir@^1.0.1: 955 | version "1.0.2" 956 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 957 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= 958 | 959 | p-cancelable@^2.0.0: 960 | version "2.1.1" 961 | resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" 962 | integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== 963 | 964 | p-event@^4.0.0: 965 | version "4.2.0" 966 | resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" 967 | integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== 968 | dependencies: 969 | p-timeout "^3.1.0" 970 | 971 | p-finally@^1.0.0: 972 | version "1.0.0" 973 | resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" 974 | integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= 975 | 976 | p-limit@^2.2.0: 977 | version "2.3.0" 978 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" 979 | integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== 980 | dependencies: 981 | p-try "^2.0.0" 982 | 983 | p-locate@^4.1.0: 984 | version "4.1.0" 985 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" 986 | integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== 987 | dependencies: 988 | p-limit "^2.2.0" 989 | 990 | p-timeout@^3.1.0: 991 | version "3.2.0" 992 | resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" 993 | integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== 994 | dependencies: 995 | p-finally "^1.0.0" 996 | 997 | p-try@^2.0.0: 998 | version "2.2.0" 999 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 1000 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 1001 | 1002 | parseurl@~1.3.3: 1003 | version "1.3.3" 1004 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 1005 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 1006 | 1007 | path-exists@^4.0.0: 1008 | version "4.0.0" 1009 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 1010 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 1011 | 1012 | path-is-absolute@^1.0.0: 1013 | version "1.0.1" 1014 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1015 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 1016 | 1017 | path-key@^2.0.0, path-key@^2.0.1: 1018 | version "2.0.1" 1019 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 1020 | integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= 1021 | 1022 | path-key@^3.1.0, path-key@^3.1.1: 1023 | version "3.1.1" 1024 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 1025 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 1026 | 1027 | pem@1.14.2: 1028 | version "1.14.2" 1029 | resolved "https://registry.yarnpkg.com/pem/-/pem-1.14.2.tgz#ab29350416bc3a532c30beeee0d541af897fb9ac" 1030 | integrity sha512-TOnPtq3ZFnCniOZ+rka4pk8UIze9xG1qI+wNE7EmkiR/cg+53uVvk5QbkWZ7M6RsuOxzz62FW1hlAobJr/lTOA== 1031 | dependencies: 1032 | es6-promisify "^6.0.0" 1033 | md5 "^2.2.1" 1034 | os-tmpdir "^1.0.1" 1035 | which "^1.3.1" 1036 | 1037 | picomatch@^2.0.4, picomatch@^2.2.1: 1038 | version "2.3.1" 1039 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 1040 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1041 | 1042 | prompts@^2.2.1: 1043 | version "2.4.2" 1044 | resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" 1045 | integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== 1046 | dependencies: 1047 | kleur "^3.0.3" 1048 | sisteransi "^1.0.5" 1049 | 1050 | pseudomap@^1.0.2: 1051 | version "1.0.2" 1052 | resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 1053 | integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= 1054 | 1055 | pump@^3.0.0: 1056 | version "3.0.0" 1057 | resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" 1058 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== 1059 | dependencies: 1060 | end-of-stream "^1.1.0" 1061 | once "^1.3.1" 1062 | 1063 | range-parser@~1.2.1: 1064 | version "1.2.1" 1065 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 1066 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 1067 | 1068 | readable-stream@^3.4.0: 1069 | version "3.6.0" 1070 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 1071 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 1072 | dependencies: 1073 | inherits "^2.0.3" 1074 | string_decoder "^1.1.1" 1075 | util-deprecate "^1.0.1" 1076 | 1077 | readdirp@^3.1.1, readdirp@~3.6.0: 1078 | version "3.6.0" 1079 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 1080 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 1081 | dependencies: 1082 | picomatch "^2.2.1" 1083 | 1084 | requires-port@^1.0.0: 1085 | version "1.0.0" 1086 | resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" 1087 | integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= 1088 | 1089 | responselike@^2.0.0: 1090 | version "2.0.0" 1091 | resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" 1092 | integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== 1093 | dependencies: 1094 | lowercase-keys "^2.0.0" 1095 | 1096 | restore-cursor@^3.1.0: 1097 | version "3.1.0" 1098 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" 1099 | integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== 1100 | dependencies: 1101 | onetime "^5.1.0" 1102 | signal-exit "^3.0.2" 1103 | 1104 | rimraf@~2.6.2: 1105 | version "2.6.3" 1106 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 1107 | integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== 1108 | dependencies: 1109 | glob "^7.1.3" 1110 | 1111 | safe-buffer@~5.2.0: 1112 | version "5.2.1" 1113 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 1114 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1115 | 1116 | semver@^5.5.0: 1117 | version "5.7.1" 1118 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 1119 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 1120 | 1121 | send@0.17.1: 1122 | version "0.17.1" 1123 | resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" 1124 | integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== 1125 | dependencies: 1126 | debug "2.6.9" 1127 | depd "~1.1.2" 1128 | destroy "~1.0.4" 1129 | encodeurl "~1.0.2" 1130 | escape-html "~1.0.3" 1131 | etag "~1.8.1" 1132 | fresh "0.5.2" 1133 | http-errors "~1.7.2" 1134 | mime "1.6.0" 1135 | ms "2.1.1" 1136 | on-finished "~2.3.0" 1137 | range-parser "~1.2.1" 1138 | statuses "~1.5.0" 1139 | 1140 | serve-static@1.14.1: 1141 | version "1.14.1" 1142 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" 1143 | integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== 1144 | dependencies: 1145 | encodeurl "~1.0.2" 1146 | escape-html "~1.0.3" 1147 | parseurl "~1.3.3" 1148 | send "0.17.1" 1149 | 1150 | setprototypeof@1.1.1: 1151 | version "1.1.1" 1152 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" 1153 | integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== 1154 | 1155 | shebang-command@^1.2.0: 1156 | version "1.2.0" 1157 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 1158 | integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= 1159 | dependencies: 1160 | shebang-regex "^1.0.0" 1161 | 1162 | shebang-command@^2.0.0: 1163 | version "2.0.0" 1164 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 1165 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 1166 | dependencies: 1167 | shebang-regex "^3.0.0" 1168 | 1169 | shebang-regex@^1.0.0: 1170 | version "1.0.0" 1171 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 1172 | integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= 1173 | 1174 | shebang-regex@^3.0.0: 1175 | version "3.0.0" 1176 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 1177 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 1178 | 1179 | signal-exit@^3.0.0, signal-exit@^3.0.2: 1180 | version "3.0.7" 1181 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" 1182 | integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== 1183 | 1184 | sisteransi@^1.0.5: 1185 | version "1.0.5" 1186 | resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" 1187 | integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== 1188 | 1189 | "statuses@>= 1.5.0 < 2", statuses@~1.5.0: 1190 | version "1.5.0" 1191 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" 1192 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= 1193 | 1194 | string-width@^4.1.0: 1195 | version "4.2.3" 1196 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 1197 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 1198 | dependencies: 1199 | emoji-regex "^8.0.0" 1200 | is-fullwidth-code-point "^3.0.0" 1201 | strip-ansi "^6.0.1" 1202 | 1203 | string_decoder@^1.1.1: 1204 | version "1.3.0" 1205 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 1206 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 1207 | dependencies: 1208 | safe-buffer "~5.2.0" 1209 | 1210 | strip-ansi@^3.0.0: 1211 | version "3.0.1" 1212 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1213 | integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= 1214 | dependencies: 1215 | ansi-regex "^2.0.0" 1216 | 1217 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 1218 | version "6.0.1" 1219 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 1220 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1221 | dependencies: 1222 | ansi-regex "^5.0.1" 1223 | 1224 | strip-eof@^1.0.0: 1225 | version "1.0.0" 1226 | resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" 1227 | integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= 1228 | 1229 | supports-color@^2.0.0: 1230 | version "2.0.0" 1231 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 1232 | integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= 1233 | 1234 | supports-color@^7.0.0, supports-color@^7.1.0: 1235 | version "7.2.0" 1236 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" 1237 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1238 | dependencies: 1239 | has-flag "^4.0.0" 1240 | 1241 | supports-hyperlinks@^2.0.0: 1242 | version "2.2.0" 1243 | resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" 1244 | integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== 1245 | dependencies: 1246 | has-flag "^4.0.0" 1247 | supports-color "^7.0.0" 1248 | 1249 | temp@^0.9.1: 1250 | version "0.9.4" 1251 | resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.4.tgz#cd20a8580cb63635d0e4e9d4bd989d44286e7620" 1252 | integrity sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA== 1253 | dependencies: 1254 | mkdirp "^0.5.1" 1255 | rimraf "~2.6.2" 1256 | 1257 | terminal-link@^2.1.1: 1258 | version "2.1.1" 1259 | resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" 1260 | integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== 1261 | dependencies: 1262 | ansi-escapes "^4.2.1" 1263 | supports-hyperlinks "^2.0.0" 1264 | 1265 | to-readable-stream@^2.0.0: 1266 | version "2.1.0" 1267 | resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-2.1.0.tgz#82880316121bea662cdc226adb30addb50cb06e8" 1268 | integrity sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w== 1269 | 1270 | to-regex-range@^5.0.1: 1271 | version "5.0.1" 1272 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 1273 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1274 | dependencies: 1275 | is-number "^7.0.0" 1276 | 1277 | toidentifier@1.0.0: 1278 | version "1.0.0" 1279 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" 1280 | integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== 1281 | 1282 | type-fest@^0.10.0: 1283 | version "0.10.0" 1284 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642" 1285 | integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw== 1286 | 1287 | type-fest@^0.21.3: 1288 | version "0.21.3" 1289 | resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" 1290 | integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== 1291 | 1292 | universalify@^2.0.0: 1293 | version "2.0.0" 1294 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" 1295 | integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== 1296 | 1297 | unpipe@~1.0.0: 1298 | version "1.0.0" 1299 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 1300 | integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= 1301 | 1302 | util-deprecate@^1.0.1: 1303 | version "1.0.2" 1304 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1305 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 1306 | 1307 | wcwidth@^1.0.1: 1308 | version "1.0.1" 1309 | resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" 1310 | integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= 1311 | dependencies: 1312 | defaults "^1.0.3" 1313 | 1314 | which@^1.2.9, which@^1.3.1: 1315 | version "1.3.1" 1316 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1317 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 1318 | dependencies: 1319 | isexe "^2.0.0" 1320 | 1321 | which@^2.0.1, which@^2.0.2: 1322 | version "2.0.2" 1323 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 1324 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1325 | dependencies: 1326 | isexe "^2.0.0" 1327 | 1328 | wrap-ansi@^6.2.0: 1329 | version "6.2.0" 1330 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" 1331 | integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== 1332 | dependencies: 1333 | ansi-styles "^4.0.0" 1334 | string-width "^4.1.0" 1335 | strip-ansi "^6.0.0" 1336 | 1337 | wrappy@1: 1338 | version "1.0.2" 1339 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1340 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 1341 | 1342 | ws@7.1.1: 1343 | version "7.1.1" 1344 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.1.1.tgz#f9942dc868b6dffb72c14fd8f2ba05f77a4d5983" 1345 | integrity sha512-o41D/WmDeca0BqYhsr3nJzQyg9NF5X8l/UdnFNux9cS3lwB+swm8qGWX5rn+aD6xfBU3rGmtHij7g7x6LxFU3A== 1346 | dependencies: 1347 | async-limiter "^1.0.0" 1348 | 1349 | yallist@^2.1.2: 1350 | version "2.1.2" 1351 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 1352 | integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= 1353 | --------------------------------------------------------------------------------