├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── benchmark ├── elm.json ├── src │ └── Main.elm └── tests │ └── elm-verify-examples.json ├── elm.json ├── examples ├── Main.elm └── elm.json ├── package-lock.json ├── package.json ├── performance ├── README.md ├── construction.png ├── linux_lazy.png ├── linux_strict.png ├── mac_lazy.png └── mac_strict.png ├── src ├── Lazy.elm └── Lazy │ ├── LList.elm │ ├── Tree.elm │ └── Tree │ ├── Force.elm │ └── Zipper.elm └── tests └── elm-verify-examples.json /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff/ 2 | /TAGS 3 | node_modules 4 | /tests/VerifyExamples/ 5 | result 6 | index.html 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | os: 4 | - linux 5 | 6 | cache: 7 | directories: 8 | - test/elm-stuff/build-artifacts 9 | - sysconfcpus 10 | 11 | language: node_js 12 | 13 | env: 14 | - ELM_VERSION=0.19.0 TARGET_NODE_VERSION=8 15 | 16 | before_install: 17 | - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config 18 | - | # epic build time improvement - see https://github.com/elm-lang/elm-compiler/issues/1473#issuecomment-245704142 19 | if [ ! -d sysconfcpus/bin ]; 20 | then 21 | git clone https://github.com/obmarg/libsysconfcpus.git; 22 | cd libsysconfcpus; 23 | ./configure --prefix=$TRAVIS_BUILD_DIR/sysconfcpus; 24 | make && make install; 25 | cd ..; 26 | fi 27 | 28 | install: 29 | - nvm install $TARGET_NODE_VERSION 30 | - nvm use $TARGET_NODE_VERSION 31 | - node --version 32 | - npm --version 33 | - npm install -g elm@$ELM_VERSION 34 | - mv $(npm config get prefix)/bin/elm $(npm config get prefix)/bin/elm-old 35 | - printf '%s\n\n' '#!/bin/bash' 'echo "Running elm-make with sysconfcpus -n 2"' '$TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm-old "$@"' > $(npm config get prefix)/bin/elm 36 | - chmod +x $(npm config get prefix)/bin/elm 37 | - npm install 38 | 39 | script: 40 | - pushd examples && elm make Main.elm && popd 41 | - npm test 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Marek Fajkus 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lazy Tree with Zipper 2 | 3 | [![Build Status](https://travis-ci.org/turboMaCk/lazy-tree-with-zipper.svg?branch=master)](https://travis-ci.org/turboMaCk/lazy-tree-with-zipper) 4 | 5 | This is pure Elm [rose tree](https://en.wikipedia.org/wiki/Rose_tree) 6 | with [zipper](https://en.wikipedia.org/wiki/Zipper_%28data_structure%29) implementation. 7 | In the context of Elm, this data structure is mostly useful for building hierarchical interfaces 8 | like menus, data browsers or filters. 9 | 10 | Main features of this library are things like easy building tree structure from flat Lists 11 | with very good performance characteristics, powerful and extensible zipper and feature-rich API. 12 | 13 | # Performance 14 | 15 | `Tree` is using custom List like implementation (`LList`) to enable lazy level after level evaluation 16 | of tree. In fact `LList` is just a function that constructs plain old `List`. This approach is the main performance optimization used in this library. 17 | 18 | There is another library implementing same idea in slightly different way [tomjkidd/elm-multiway-tree-zipper](https://github.com/tomjkidd/elm-multiway-tree-zipper). 19 | The main difference is that `elm-multiway-tree-zipper` implementation is strict so the whole Tree is immediately evaluated. 20 | Implementation provided by this package is optimized for situations in which it isn't necessary to construct the whole 21 | structure immediately. In situations where Tree is expanded level by level this implementation yields 22 | much better performance than strict implementation, especially for large trees. 23 | You can find basic comparison in [performance](https://github.com/turboMaCk/lazy-tree-with-zipper/blob/master/performance). 24 | 25 | Feedback and contributions to both code and documentation are very welcome. 26 | 27 | # Usage 28 | 29 | To install this package run: 30 | 31 | ``` 32 | $ elm install turboMaCk/lazy-tree-with-zipper 33 | ``` 34 | 35 | This is an example application that renders levels of items as nested tree 36 | with toggling between open and closed state in every level: 37 | 38 | ```elm 39 | module Main exposing (main) 40 | 41 | import Browser 42 | import Html exposing (Html) 43 | import Html.Events as Events 44 | import Lazy.Tree as Tree exposing (Tree(..)) 45 | import Lazy.Tree.Zipper as Zipper exposing (Zipper) 46 | 47 | 48 | main : Program () Model Msg 49 | main = 50 | Browser.sandbox 51 | { init = init 52 | , update = update 53 | , view = view 54 | } 55 | 56 | 57 | 58 | -- Model 59 | 60 | 61 | type alias Item = 62 | { id : Int 63 | , name : String 64 | , parent : Maybe Int 65 | } 66 | 67 | 68 | items : List Item 69 | items = 70 | [ { id = 1, name = "Foo", parent = Nothing } 71 | , { id = 2, name = "Bar", parent = Nothing } 72 | , { id = 3, name = "Baz", parent = Nothing } 73 | , { id = 4, name = "Foobar", parent = Just 1 } 74 | , { id = 5, name = "Bar child", parent = Just 2 } 75 | , { id = 6, name = "Foobar child", parent = Just 4 } 76 | ] 77 | 78 | 79 | {-| Zipper of pair where first value means `isOpen` and second contain Item details. 80 | -} 81 | type alias Model = 82 | Zipper ( Bool, Item ) 83 | 84 | 85 | init : Model 86 | init = 87 | let 88 | root = 89 | { id = -1, name = "root", parent = Nothing } 90 | in 91 | List.map (\b -> ( False, b )) items 92 | |> Tree.fromList (\p ( _, i ) -> Maybe.map (.id << Tuple.second) p == i.parent) 93 | |> Tree ( False, root ) 94 | |> Zipper.fromTree 95 | 96 | 97 | 98 | -- Update 99 | 100 | 101 | type Msg 102 | = Toggle (Zipper ( Bool, Item )) 103 | 104 | 105 | update : Msg -> Model -> Model 106 | update (Toggle zipper) _ = 107 | Zipper.updateItem (\( s, i ) -> ( not s, i )) zipper 108 | 109 | 110 | 111 | -- View 112 | 113 | 114 | view : Model -> Html Msg 115 | view zipper = 116 | Html.ul [] [ viewLevel (Zipper.root zipper) ] 117 | 118 | 119 | viewLevel : Zipper ( Bool, Item ) -> Html Msg 120 | viewLevel zipper = 121 | let 122 | ( isOpen, item ) = 123 | Zipper.current zipper 124 | in 125 | Html.li [] 126 | [ Html.a [ Events.onClick <| Toggle zipper ] 127 | [ if not (Zipper.isEmpty zipper) then 128 | Html.span [] 129 | [ if isOpen then 130 | Html.text "- " 131 | 132 | else 133 | Html.text "+ " 134 | ] 135 | 136 | else 137 | Html.text "" 138 | , Html.text item.name 139 | ] 140 | , Html.ul [] <| 141 | if isOpen then 142 | Zipper.openAll zipper 143 | |> List.map viewLevel 144 | 145 | else 146 | [] 147 | ] 148 | ``` 149 | 150 | # Background 151 | 152 | I've spent about a year experimenting with different ideas of Rose Tree implementation 153 | optimized for needs of building UIs for recursive data. The biggest problem turned out to be performance. 154 | Usually, data for web applications are coming from a server which uses SQL database as storage. 155 | API usually then renders flat JSON or any other data format which uses references to describe recursive relationships. 156 | Therefore one of the main features that are needed is an efficient and easy way to build tree from a list of data. 157 | This usually results in quadratic complexity. Since one item might be a child of multiple other things 158 | there has to be at least one iteration over the whole list of data. Also by definition using such data 159 | for building rose tree might result in infinitely deep resulting tree. 160 | 161 | Those are the things I've experimented with over time: 162 | 163 | - Strict based (`Tree a (List a)`) - not that great performance but OK until you hit too much recursion. 164 | - Lazy List based implementation (`Tree a (LazyList a)`) - runs into too much recursion even on simpler data. 165 | - Continuation Passing / CPS - very slow (hitting scripts runs for too long) - might have been an issue with a particular algorithm. 166 | - Lazy List construction - this is what this package is using - very best performance. 167 | 168 | # License 169 | 170 | This package is released under BSD-3-Clause license. See [LICENSE](LICENSE) file for more info. 171 | -------------------------------------------------------------------------------- /benchmark/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "elm/browser": "1.0.2", 11 | "elm/core": "1.0.5", 12 | "elm/html": "1.0.0", 13 | "elm-explorations/benchmark": "1.0.1" 14 | }, 15 | "indirect": { 16 | "BrianHicks/elm-trend": "2.1.3", 17 | "Skinney/murmur3": "2.0.8", 18 | "elm/json": "1.1.3", 19 | "elm/regex": "1.0.0", 20 | "elm/time": "1.0.0", 21 | "elm/url": "1.0.0", 22 | "elm/virtual-dom": "1.0.2", 23 | "mdgriffith/style-elements": "5.0.1" 24 | } 25 | }, 26 | "test-dependencies": { 27 | "direct": {}, 28 | "indirect": {} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /benchmark/src/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | import Benchmark exposing (..) 4 | import Benchmark.Runner exposing (BenchmarkProgram) 5 | import Lazy.Tree as Tree exposing (Tree) 6 | import Lazy.Tree.Force as Tree 7 | 8 | 9 | resolveParents = 10 | Maybe.withDefault [] << Maybe.map List.singleton << .parent 11 | 12 | 13 | main : BenchmarkProgram 14 | main = 15 | Benchmark.Runner.program <| 16 | describe "fromList vs fromKeyedList" 17 | [ createOnly 18 | , createAndExpand 19 | ] 20 | 21 | 22 | createAndExpand : Benchmark 23 | createAndExpand = 24 | describe "Create and expand everything" 25 | [ benchmark "fromList (10 elems)" <| 26 | \_ -> 27 | noodle10 28 | |> Tree.fromList (\p i -> Maybe.map .id p == i.parent) 29 | |> Tree.forceForest 30 | , benchmark "fromKeyedList (10 elems)" <| 31 | \_ -> 32 | noodle10 33 | |> Tree.fromKeyedList .id resolveParents 34 | |> Tree.forceForest 35 | , benchmark "fromList (100 elems)" <| 36 | \_ -> 37 | noodle100 38 | |> Tree.fromList (\p i -> Maybe.map .id p == i.parent) 39 | |> Tree.forceForest 40 | , benchmark "fromKeyedList (100 elems)" <| 41 | \_ -> 42 | noodle100 43 | |> Tree.fromKeyedList .id resolveParents 44 | |> Tree.forceForest 45 | ] 46 | 47 | 48 | createOnly : Benchmark 49 | createOnly = 50 | describe "Create only" 51 | [ benchmark "fromList (10)" <| 52 | \_ -> 53 | noodle10 54 | |> Tree.fromList (\p i -> Maybe.map .id p == i.parent) 55 | , benchmark "fromKeyedList (10)" <| 56 | \_ -> 57 | noodle10 58 | |> Tree.fromKeyedList .id resolveParents 59 | , benchmark "fromList (100)" <| 60 | \_ -> 61 | noodle100 62 | |> Tree.fromList (\p i -> Maybe.map .id p == i.parent) 63 | , benchmark "fromKeyedList (100)" <| 64 | \_ -> 65 | noodle100 66 | |> Tree.fromKeyedList .id resolveParents 67 | ] 68 | 69 | 70 | type alias Item = 71 | { id : Int 72 | , parent : Maybe Int 73 | } 74 | 75 | 76 | {-| Tree that looks like "1 -> 2 -> 3 -> ... -> n" 77 | -} 78 | noodle : Int -> List Item 79 | noodle n = 80 | List.range 1 n 81 | |> List.map 82 | (\i -> 83 | { parent = Just i 84 | , id = i + 1 85 | } 86 | ) 87 | |> (::) 88 | { parent = Nothing 89 | , id = 1 90 | } 91 | 92 | 93 | noodle10 : List Item 94 | noodle10 = 95 | noodle 10 96 | 97 | 98 | noodle100 : List Item 99 | noodle100 = 100 | noodle 100 101 | -------------------------------------------------------------------------------- /benchmark/tests/elm-verify-examples.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "../src", 3 | "tests": [] 4 | } 5 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "turboMaCk/lazy-tree-with-zipper", 4 | "summary": "Lazy rose tree (multiway tree) with zipper.", 5 | "license": "BSD-3-Clause", 6 | "version": "3.4.1", 7 | "exposed-modules": [ 8 | "Lazy.Tree", 9 | "Lazy.Tree.Zipper", 10 | "Lazy.LList", 11 | "Lazy" 12 | ], 13 | "elm-version": "0.19.0 <= v < 0.20.0", 14 | "dependencies": { 15 | "elm/core": "1.0.0 <= v < 2.0.0" 16 | }, 17 | "test-dependencies": { 18 | "elm-explorations/test": "2.0.0 <= v < 3.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (main) 2 | 3 | import Browser 4 | import Html exposing (Html) 5 | import Html.Events as Events 6 | import Lazy.Tree as Tree exposing (Tree(..)) 7 | import Lazy.Tree.Zipper as Zipper exposing (Zipper) 8 | 9 | 10 | main : Program () Model Msg 11 | main = 12 | Browser.sandbox 13 | { init = init 14 | , update = update 15 | , view = view 16 | } 17 | 18 | 19 | 20 | -- Model 21 | 22 | 23 | type alias Item = 24 | { id : Int 25 | , name : String 26 | , parent : Maybe Int 27 | } 28 | 29 | 30 | items : List Item 31 | items = 32 | [ { id = 1, name = "Foo", parent = Nothing } 33 | , { id = 2, name = "Bar", parent = Nothing } 34 | , { id = 3, name = "Baz", parent = Nothing } 35 | , { id = 4, name = "Foobar", parent = Just 1 } 36 | , { id = 5, name = "Bar child", parent = Just 2 } 37 | , { id = 6, name = "Foobar child", parent = Just 4 } 38 | ] 39 | 40 | 41 | {-| Zipper of pair where first value means `isOpen` and second contain Item details. 42 | -} 43 | type alias Model = 44 | Zipper ( Bool, Item ) 45 | 46 | 47 | init : Model 48 | init = 49 | let 50 | root = 51 | { id = -1, name = "root", parent = Nothing } 52 | in 53 | List.map (\b -> ( False, b )) items 54 | |> Tree.fromList (\p ( _, i ) -> Maybe.map (.id << Tuple.second) p == i.parent) 55 | |> Tree ( False, root ) 56 | |> Zipper.fromTree 57 | 58 | 59 | 60 | -- Update 61 | 62 | 63 | type Msg 64 | = Toggle (Zipper ( Bool, Item )) 65 | 66 | 67 | update : Msg -> Model -> Model 68 | update (Toggle zipper) _ = 69 | Zipper.updateItem (\( s, i ) -> ( not s, i )) zipper 70 | 71 | 72 | 73 | -- View 74 | 75 | 76 | view : Model -> Html Msg 77 | view zipper = 78 | Html.ul [] [ viewLevel (Zipper.root zipper) ] 79 | 80 | 81 | viewLevel : Zipper ( Bool, Item ) -> Html Msg 82 | viewLevel zipper = 83 | let 84 | ( isOpen, item ) = 85 | Zipper.current zipper 86 | in 87 | Html.li [] 88 | [ Html.a [ Events.onClick <| Toggle zipper ] 89 | [ if not (Zipper.isEmpty zipper) then 90 | Html.span [] 91 | [ if isOpen then 92 | Html.text "- " 93 | 94 | else 95 | Html.text "+ " 96 | ] 97 | 98 | else 99 | Html.text "" 100 | , Html.text item.name 101 | ] 102 | , Html.ul [] <| 103 | if isOpen then 104 | Zipper.openAll zipper 105 | |> List.map viewLevel 106 | 107 | else 108 | [] 109 | ] 110 | -------------------------------------------------------------------------------- /examples/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | ".", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.0", 8 | "dependencies": { 9 | "direct": { 10 | "elm/browser": "1.0.1", 11 | "elm/core": "1.0.0", 12 | "elm/html": "1.0.0" 13 | }, 14 | "indirect": { 15 | "elm/json": "1.0.0", 16 | "elm/time": "1.0.0", 17 | "elm/url": "1.0.0", 18 | "elm/virtual-dom": "1.0.2" 19 | } 20 | }, 21 | "test-dependencies": { 22 | "direct": {}, 23 | "indirect": {} 24 | } 25 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elm-lazy-tree-with-zipper", 3 | "version": "3.4.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "5.5.2", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 10 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 11 | "requires": { 12 | "co": "4.6.0", 13 | "fast-deep-equal": "1.1.0", 14 | "fast-json-stable-stringify": "2.0.0", 15 | "json-schema-traverse": "0.3.1" 16 | } 17 | }, 18 | "ansi-regex": { 19 | "version": "4.1.1", 20 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", 21 | "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" 22 | }, 23 | "ansi-styles": { 24 | "version": "3.2.1", 25 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 26 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 27 | "requires": { 28 | "color-convert": "^1.9.0" 29 | } 30 | }, 31 | "anymatch": { 32 | "version": "3.1.3", 33 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 34 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 35 | "requires": { 36 | "normalize-path": "^3.0.0", 37 | "picomatch": "^2.0.4" 38 | } 39 | }, 40 | "asn1": { 41 | "version": "0.2.4", 42 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 43 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 44 | "requires": { 45 | "safer-buffer": "2.1.2" 46 | } 47 | }, 48 | "assert-plus": { 49 | "version": "1.0.0", 50 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 51 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 52 | }, 53 | "asynckit": { 54 | "version": "0.4.0", 55 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 56 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 57 | }, 58 | "aws-sign2": { 59 | "version": "0.7.0", 60 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 61 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 62 | }, 63 | "aws4": { 64 | "version": "1.8.0", 65 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 66 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 67 | }, 68 | "balanced-match": { 69 | "version": "1.0.0", 70 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 71 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 72 | }, 73 | "bcrypt-pbkdf": { 74 | "version": "1.0.2", 75 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 76 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 77 | "requires": { 78 | "tweetnacl": "0.14.5" 79 | } 80 | }, 81 | "binary": { 82 | "version": "0.3.0", 83 | "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", 84 | "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", 85 | "requires": { 86 | "buffers": "0.1.1", 87 | "chainsaw": "0.1.0" 88 | } 89 | }, 90 | "binary-extensions": { 91 | "version": "2.2.0", 92 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 93 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 94 | }, 95 | "binwrap": { 96 | "version": "0.2.2", 97 | "resolved": "https://registry.npmjs.org/binwrap/-/binwrap-0.2.2.tgz", 98 | "integrity": "sha512-Y+Wvypk3JhH5GPZAvlwJAWOVH/OsOhQMSj37vySuWHwQivoALplPxfBA8b973rFJI7OS+O+1YmmYXIiEXVMAcw==", 99 | "requires": { 100 | "mustache": "^3.0.1", 101 | "request": "^2.88.0", 102 | "request-promise": "^4.2.4", 103 | "tar": "^4.4.10", 104 | "unzip-stream": "^0.3.0" 105 | }, 106 | "dependencies": { 107 | "lodash": { 108 | "version": "4.17.21", 109 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 110 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 111 | }, 112 | "minimist": { 113 | "version": "1.2.8", 114 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 115 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" 116 | }, 117 | "mkdirp": { 118 | "version": "0.5.6", 119 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", 120 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", 121 | "requires": { 122 | "minimist": "^1.2.6" 123 | } 124 | }, 125 | "request-promise": { 126 | "version": "4.2.6", 127 | "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", 128 | "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", 129 | "requires": { 130 | "bluebird": "^3.5.0", 131 | "request-promise-core": "1.1.4", 132 | "stealthy-require": "^1.1.1", 133 | "tough-cookie": "^2.3.3" 134 | } 135 | }, 136 | "request-promise-core": { 137 | "version": "1.1.4", 138 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", 139 | "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", 140 | "requires": { 141 | "lodash": "^4.17.19" 142 | } 143 | }, 144 | "safe-buffer": { 145 | "version": "5.2.1", 146 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 147 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 148 | }, 149 | "tar": { 150 | "version": "4.4.19", 151 | "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", 152 | "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", 153 | "requires": { 154 | "chownr": "^1.1.4", 155 | "fs-minipass": "^1.2.7", 156 | "minipass": "^2.9.0", 157 | "minizlib": "^1.3.3", 158 | "mkdirp": "^0.5.5", 159 | "safe-buffer": "^5.2.1", 160 | "yallist": "^3.1.1" 161 | } 162 | } 163 | } 164 | }, 165 | "block-stream": { 166 | "version": "0.0.9", 167 | "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", 168 | "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", 169 | "requires": { 170 | "inherits": "2.0.3" 171 | } 172 | }, 173 | "bluebird": { 174 | "version": "3.5.2", 175 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", 176 | "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==" 177 | }, 178 | "brace-expansion": { 179 | "version": "1.1.11", 180 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 181 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 182 | "requires": { 183 | "balanced-match": "1.0.0", 184 | "concat-map": "0.0.1" 185 | } 186 | }, 187 | "braces": { 188 | "version": "3.0.3", 189 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 190 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 191 | "requires": { 192 | "fill-range": "^7.1.1" 193 | }, 194 | "dependencies": { 195 | "fill-range": { 196 | "version": "7.1.1", 197 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 198 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 199 | "requires": { 200 | "to-regex-range": "^5.0.1" 201 | } 202 | } 203 | } 204 | }, 205 | "buffers": { 206 | "version": "0.1.1", 207 | "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", 208 | "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" 209 | }, 210 | "camelcase": { 211 | "version": "5.3.1", 212 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 213 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 214 | }, 215 | "caseless": { 216 | "version": "0.12.0", 217 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 218 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 219 | }, 220 | "chainsaw": { 221 | "version": "0.1.0", 222 | "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", 223 | "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", 224 | "requires": { 225 | "traverse": "0.3.9" 226 | } 227 | }, 228 | "chalk": { 229 | "version": "2.4.2", 230 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 231 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 232 | "requires": { 233 | "ansi-styles": "^3.2.1", 234 | "escape-string-regexp": "^1.0.5", 235 | "supports-color": "^5.3.0" 236 | }, 237 | "dependencies": { 238 | "supports-color": { 239 | "version": "5.5.0", 240 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 241 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 242 | "requires": { 243 | "has-flag": "^3.0.0" 244 | } 245 | } 246 | } 247 | }, 248 | "chokidar": { 249 | "version": "3.2.1", 250 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.2.1.tgz", 251 | "integrity": "sha512-/j5PPkb5Feyps9e+jo07jUZGvkB5Aj953NrI4s8xSVScrAo/RHeILrtdb4uzR7N6aaFFxxJ+gt8mA8HfNpw76w==", 252 | "requires": { 253 | "anymatch": "~3.1.1", 254 | "braces": "~3.0.2", 255 | "fsevents": "~2.1.0", 256 | "glob-parent": "~5.1.0", 257 | "is-binary-path": "~2.1.0", 258 | "is-glob": "~4.0.1", 259 | "normalize-path": "~3.0.0", 260 | "readdirp": "~3.1.3" 261 | } 262 | }, 263 | "chownr": { 264 | "version": "1.1.4", 265 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 266 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" 267 | }, 268 | "cliui": { 269 | "version": "5.0.0", 270 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 271 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 272 | "requires": { 273 | "string-width": "^3.1.0", 274 | "strip-ansi": "^5.2.0", 275 | "wrap-ansi": "^5.1.0" 276 | } 277 | }, 278 | "co": { 279 | "version": "4.6.0", 280 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 281 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 282 | }, 283 | "color-convert": { 284 | "version": "1.9.3", 285 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 286 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 287 | "requires": { 288 | "color-name": "1.1.3" 289 | } 290 | }, 291 | "color-name": { 292 | "version": "1.1.3", 293 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 294 | "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" 295 | }, 296 | "combined-stream": { 297 | "version": "1.0.7", 298 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 299 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 300 | "requires": { 301 | "delayed-stream": "1.0.0" 302 | } 303 | }, 304 | "commander": { 305 | "version": "9.5.0", 306 | "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", 307 | "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" 308 | }, 309 | "concat-map": { 310 | "version": "0.0.1", 311 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 312 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 313 | }, 314 | "core-util-is": { 315 | "version": "1.0.2", 316 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 317 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 318 | }, 319 | "cross-spawn": { 320 | "version": "7.0.0", 321 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.0.tgz", 322 | "integrity": "sha512-6U/8SMK2FBNnB21oQ4+6Nsodxanw1gTkntYA2zBdkFYFu3ZDx65P2ONEXGSvob/QS6REjVHQ9zxzdOafwFdstw==", 323 | "requires": { 324 | "path-key": "^3.1.0", 325 | "shebang-command": "^1.2.0", 326 | "which": "^1.2.9" 327 | }, 328 | "dependencies": { 329 | "shebang-command": { 330 | "version": "1.2.0", 331 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 332 | "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", 333 | "requires": { 334 | "shebang-regex": "^1.0.0" 335 | } 336 | }, 337 | "shebang-regex": { 338 | "version": "1.0.0", 339 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 340 | "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" 341 | }, 342 | "which": { 343 | "version": "1.3.1", 344 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 345 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 346 | "requires": { 347 | "isexe": "^2.0.0" 348 | } 349 | } 350 | } 351 | }, 352 | "dashdash": { 353 | "version": "1.14.1", 354 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 355 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 356 | "requires": { 357 | "assert-plus": "1.0.0" 358 | } 359 | }, 360 | "decamelize": { 361 | "version": "1.2.0", 362 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 363 | "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" 364 | }, 365 | "delayed-stream": { 366 | "version": "1.0.0", 367 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 368 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 369 | }, 370 | "ecc-jsbn": { 371 | "version": "0.1.2", 372 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 373 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 374 | "requires": { 375 | "jsbn": "0.1.1", 376 | "safer-buffer": "2.1.2" 377 | } 378 | }, 379 | "elm": { 380 | "version": "0.19.0", 381 | "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.0.tgz", 382 | "integrity": "sha512-CYgewByRByMOilPk5/yrW1mtflaS/vp+026gwb0EEX6aqUl+TGYoTSTW+uf44XB/FOKgUauV3TDH3Bl0IHZ8Ag==", 383 | "requires": { 384 | "binwrap": "0.1.4" 385 | }, 386 | "dependencies": { 387 | "binwrap": { 388 | "version": "0.1.4", 389 | "resolved": "https://registry.npmjs.org/binwrap/-/binwrap-0.1.4.tgz", 390 | "integrity": "sha1-yh94cDAiElGPoksHcm+cUKFcdVk=", 391 | "requires": { 392 | "request": "2.88.0", 393 | "request-promise": "4.2.2", 394 | "tar": "2.2.1", 395 | "unzip": "0.1.11" 396 | } 397 | } 398 | } 399 | }, 400 | "elm-solve-deps-wasm": { 401 | "version": "1.0.2", 402 | "resolved": "https://registry.npmjs.org/elm-solve-deps-wasm/-/elm-solve-deps-wasm-1.0.2.tgz", 403 | "integrity": "sha512-qnwo7RO9IO7jd9SLHvIy0rSOEIlc/tNMTE9Cras0kl+b161PVidW4FvXo0MtXU8GAKi/2s/HYvhcnpR/NNQ1zw==" 404 | }, 405 | "elm-test": { 406 | "version": "0.19.1", 407 | "resolved": "https://registry.npmjs.org/elm-test/-/elm-test-0.19.1.tgz", 408 | "integrity": "sha512-SyZgZ/hxq62budS3k0M1Qj1E8fIRvldSxFSm4XfzE6qRRuHAT2a82fxprZRZl1yG2GwnImGmhuKH5hSyjPpzjA==", 409 | "requires": { 410 | "chalk": "2.4.2", 411 | "chokidar": "3.2.1", 412 | "cross-spawn": "7.0.0", 413 | "elmi-to-json": "1.2.0", 414 | "find-parent-dir": "^0.3.0", 415 | "firstline": "2.0.2", 416 | "fs-extra": "8.1.0", 417 | "glob": "7.1.4", 418 | "lodash": "4.17.15", 419 | "minimist": "^1.2.0", 420 | "murmur-hash-js": "1.0.0", 421 | "node-elm-compiler": "5.0.4", 422 | "split": "1.0.1", 423 | "supports-color": "7.1.0", 424 | "temp": "0.9.0", 425 | "which": "2.0.1", 426 | "xmlbuilder": "^13.0.2" 427 | }, 428 | "dependencies": { 429 | "glob": { 430 | "version": "7.1.4", 431 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 432 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 433 | "requires": { 434 | "fs.realpath": "^1.0.0", 435 | "inflight": "^1.0.4", 436 | "inherits": "2", 437 | "minimatch": "^3.0.4", 438 | "once": "^1.3.0", 439 | "path-is-absolute": "^1.0.0" 440 | } 441 | }, 442 | "lodash": { 443 | "version": "4.17.15", 444 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 445 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 446 | }, 447 | "minimist": { 448 | "version": "1.2.8", 449 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 450 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" 451 | } 452 | } 453 | }, 454 | "elm-verify-examples": { 455 | "version": "5.2.0", 456 | "resolved": "https://registry.npmjs.org/elm-verify-examples/-/elm-verify-examples-5.2.0.tgz", 457 | "integrity": "sha512-YhBRf4S4WxvjzyhHHMmXc2CHVtwwHqXU4h8jxUwcZ+elMz3GiGB4nYRCdZIWrKE1bw5W0TDRcQ4gsc9SgpSxbw==", 458 | "requires": { 459 | "chalk": "^2.4.2", 460 | "elm-test": "^0.19.1-revision7", 461 | "fs-extra": "^5.0.0", 462 | "mkdirp": "^0.5.1", 463 | "rimraf": "^2.6.3", 464 | "yargs": "^13.3.0" 465 | }, 466 | "dependencies": { 467 | "anymatch": { 468 | "version": "3.1.3", 469 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 470 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 471 | "requires": { 472 | "normalize-path": "^3.0.0", 473 | "picomatch": "^2.0.4" 474 | } 475 | }, 476 | "binary-extensions": { 477 | "version": "2.2.0", 478 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 479 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 480 | }, 481 | "brace-expansion": { 482 | "version": "2.0.1", 483 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 484 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 485 | "requires": { 486 | "balanced-match": "^1.0.0" 487 | } 488 | }, 489 | "chalk": { 490 | "version": "2.4.2", 491 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 492 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 493 | "requires": { 494 | "ansi-styles": "^3.2.1", 495 | "escape-string-regexp": "^1.0.5", 496 | "supports-color": "^5.3.0" 497 | } 498 | }, 499 | "chokidar": { 500 | "version": "3.5.3", 501 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 502 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 503 | "requires": { 504 | "anymatch": "~3.1.2", 505 | "braces": "~3.0.2", 506 | "fsevents": "~2.3.2", 507 | "glob-parent": "~5.1.2", 508 | "is-binary-path": "~2.1.0", 509 | "is-glob": "~4.0.1", 510 | "normalize-path": "~3.0.0", 511 | "readdirp": "~3.6.0" 512 | } 513 | }, 514 | "color-convert": { 515 | "version": "2.0.1", 516 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 517 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 518 | "requires": { 519 | "color-name": "~1.1.4" 520 | } 521 | }, 522 | "color-name": { 523 | "version": "1.1.4", 524 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 525 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 526 | }, 527 | "cross-spawn": { 528 | "version": "7.0.3", 529 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 530 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 531 | "requires": { 532 | "path-key": "^3.1.0", 533 | "shebang-command": "^2.0.0", 534 | "which": "^2.0.1" 535 | } 536 | }, 537 | "elm-test": { 538 | "version": "0.19.1-revision9", 539 | "resolved": "https://registry.npmjs.org/elm-test/-/elm-test-0.19.1-revision9.tgz", 540 | "integrity": "sha512-lvHJbh16sy7oUBBPIEMN+0v6dGDZFmfvwq+V+TnGPk5ZlI64vcykx8E8+zLt/E+Ja42UKMMXXgN1ZHQ1V6Ic2Q==", 541 | "requires": { 542 | "chalk": "^4.1.2", 543 | "chokidar": "^3.5.3", 544 | "commander": "^9.3.0", 545 | "cross-spawn": "^7.0.3", 546 | "elm-solve-deps-wasm": "^1.0.1", 547 | "glob": "^8.0.3", 548 | "graceful-fs": "^4.2.10", 549 | "split": "^1.0.1", 550 | "which": "^2.0.2", 551 | "xmlbuilder": "^15.1.1" 552 | }, 553 | "dependencies": { 554 | "ansi-styles": { 555 | "version": "4.3.0", 556 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 557 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 558 | "requires": { 559 | "color-convert": "^2.0.1" 560 | } 561 | }, 562 | "chalk": { 563 | "version": "4.1.2", 564 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 565 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 566 | "requires": { 567 | "ansi-styles": "^4.1.0", 568 | "supports-color": "^7.1.0" 569 | } 570 | }, 571 | "has-flag": { 572 | "version": "4.0.0", 573 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 574 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 575 | }, 576 | "supports-color": { 577 | "version": "7.2.0", 578 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 579 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 580 | "requires": { 581 | "has-flag": "^4.0.0" 582 | } 583 | } 584 | } 585 | }, 586 | "fill-range": { 587 | "version": "7.0.1", 588 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 589 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 590 | "requires": { 591 | "to-regex-range": "^5.0.1" 592 | } 593 | }, 594 | "fs-extra": { 595 | "version": "5.0.0", 596 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", 597 | "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", 598 | "requires": { 599 | "graceful-fs": "^4.1.2", 600 | "jsonfile": "^4.0.0", 601 | "universalify": "^0.1.0" 602 | } 603 | }, 604 | "fsevents": { 605 | "version": "2.3.2", 606 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 607 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 608 | "optional": true 609 | }, 610 | "glob": { 611 | "version": "8.1.0", 612 | "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", 613 | "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", 614 | "requires": { 615 | "fs.realpath": "^1.0.0", 616 | "inflight": "^1.0.4", 617 | "inherits": "2", 618 | "minimatch": "^5.0.1", 619 | "once": "^1.3.0" 620 | } 621 | }, 622 | "glob-parent": { 623 | "version": "5.1.2", 624 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 625 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 626 | "requires": { 627 | "is-glob": "^4.0.1" 628 | } 629 | }, 630 | "graceful-fs": { 631 | "version": "4.2.11", 632 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 633 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" 634 | }, 635 | "has-flag": { 636 | "version": "3.0.0", 637 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 638 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" 639 | }, 640 | "is-binary-path": { 641 | "version": "2.1.0", 642 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 643 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 644 | "requires": { 645 | "binary-extensions": "^2.0.0" 646 | } 647 | }, 648 | "is-number": { 649 | "version": "7.0.0", 650 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 651 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 652 | }, 653 | "jsonfile": { 654 | "version": "4.0.0", 655 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 656 | "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", 657 | "requires": { 658 | "graceful-fs": "^4.1.6" 659 | } 660 | }, 661 | "minimatch": { 662 | "version": "5.1.6", 663 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 664 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 665 | "requires": { 666 | "brace-expansion": "^2.0.1" 667 | } 668 | }, 669 | "readdirp": { 670 | "version": "3.6.0", 671 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 672 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 673 | "requires": { 674 | "picomatch": "^2.2.1" 675 | } 676 | }, 677 | "rimraf": { 678 | "version": "2.7.1", 679 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 680 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 681 | "requires": { 682 | "glob": "^7.1.3" 683 | }, 684 | "dependencies": { 685 | "brace-expansion": { 686 | "version": "1.1.11", 687 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 688 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 689 | "requires": { 690 | "balanced-match": "^1.0.0", 691 | "concat-map": "0.0.1" 692 | } 693 | }, 694 | "glob": { 695 | "version": "7.2.3", 696 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 697 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 698 | "requires": { 699 | "fs.realpath": "^1.0.0", 700 | "inflight": "^1.0.4", 701 | "inherits": "2", 702 | "minimatch": "^3.1.1", 703 | "once": "^1.3.0", 704 | "path-is-absolute": "^1.0.0" 705 | } 706 | }, 707 | "minimatch": { 708 | "version": "3.1.2", 709 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 710 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 711 | "requires": { 712 | "brace-expansion": "^1.1.7" 713 | } 714 | } 715 | } 716 | }, 717 | "supports-color": { 718 | "version": "5.5.0", 719 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 720 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 721 | "requires": { 722 | "has-flag": "^3.0.0" 723 | } 724 | }, 725 | "to-regex-range": { 726 | "version": "5.0.1", 727 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 728 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 729 | "requires": { 730 | "is-number": "^7.0.0" 731 | } 732 | }, 733 | "which": { 734 | "version": "2.0.2", 735 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 736 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 737 | "requires": { 738 | "isexe": "^2.0.0" 739 | } 740 | }, 741 | "xmlbuilder": { 742 | "version": "15.1.1", 743 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", 744 | "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==" 745 | } 746 | } 747 | }, 748 | "elmi-to-json": { 749 | "version": "1.2.0", 750 | "resolved": "https://registry.npmjs.org/elmi-to-json/-/elmi-to-json-1.2.0.tgz", 751 | "integrity": "sha512-zNinzt6/YMr11HgeBlC9Z0UM3qHkYrGsWJTjrCmgBkKnaOLUzTP5K9N3z1RltyunItXtHAxb8DFPvMxlYRPv/Q==", 752 | "requires": { 753 | "binwrap": "0.2.2" 754 | } 755 | }, 756 | "emoji-regex": { 757 | "version": "7.0.3", 758 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 759 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 760 | }, 761 | "escape-string-regexp": { 762 | "version": "1.0.5", 763 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 764 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" 765 | }, 766 | "extend": { 767 | "version": "3.0.2", 768 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 769 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 770 | }, 771 | "extsprintf": { 772 | "version": "1.3.0", 773 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 774 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 775 | }, 776 | "fast-deep-equal": { 777 | "version": "1.1.0", 778 | "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 779 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" 780 | }, 781 | "fast-json-stable-stringify": { 782 | "version": "2.0.0", 783 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 784 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 785 | }, 786 | "find-elm-dependencies": { 787 | "version": "2.0.2", 788 | "resolved": "https://registry.npmjs.org/find-elm-dependencies/-/find-elm-dependencies-2.0.2.tgz", 789 | "integrity": "sha512-nM5UCbccD1G8CGK2GsM7ykG3ksOAl9E+34jiDfl07CAl2OPnLpBVWY2hlxEmIkSBfdJjSopEowWHrO0cI8RhxQ==", 790 | "requires": { 791 | "firstline": "1.2.0", 792 | "lodash": "4.17.15" 793 | }, 794 | "dependencies": { 795 | "firstline": { 796 | "version": "1.2.0", 797 | "resolved": "https://registry.npmjs.org/firstline/-/firstline-1.2.0.tgz", 798 | "integrity": "sha512-JBzSqQYZvZaD3aHRG1okOog1aZXuXCWHcf9BzkpHYIN9s9zQQCjFajawWxGzDco50p9wtg93A5qC90gnnfngKg==" 799 | }, 800 | "lodash": { 801 | "version": "4.17.15", 802 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 803 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 804 | } 805 | } 806 | }, 807 | "find-parent-dir": { 808 | "version": "0.3.1", 809 | "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.1.tgz", 810 | "integrity": "sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A==" 811 | }, 812 | "find-up": { 813 | "version": "3.0.0", 814 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 815 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 816 | "requires": { 817 | "locate-path": "^3.0.0" 818 | } 819 | }, 820 | "firstline": { 821 | "version": "2.0.2", 822 | "resolved": "https://registry.npmjs.org/firstline/-/firstline-2.0.2.tgz", 823 | "integrity": "sha512-8KcmfI0jgSECnzdhucm0i7vrwef3BWwgjimW2YkRC5eSFwjb5DibVoA0YvgkYwwxuJi9c+7M7X3b3lX8o9B6wg==" 824 | }, 825 | "forever-agent": { 826 | "version": "0.6.1", 827 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 828 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 829 | }, 830 | "form-data": { 831 | "version": "2.3.3", 832 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 833 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 834 | "requires": { 835 | "asynckit": "0.4.0", 836 | "combined-stream": "1.0.7", 837 | "mime-types": "2.1.21" 838 | } 839 | }, 840 | "fs-extra": { 841 | "version": "8.1.0", 842 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", 843 | "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", 844 | "requires": { 845 | "graceful-fs": "^4.2.0", 846 | "jsonfile": "^4.0.0", 847 | "universalify": "^0.1.0" 848 | }, 849 | "dependencies": { 850 | "graceful-fs": { 851 | "version": "4.2.11", 852 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 853 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" 854 | } 855 | } 856 | }, 857 | "fs-minipass": { 858 | "version": "1.2.7", 859 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", 860 | "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", 861 | "requires": { 862 | "minipass": "^2.6.0" 863 | } 864 | }, 865 | "fs.realpath": { 866 | "version": "1.0.0", 867 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 868 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 869 | }, 870 | "fsevents": { 871 | "version": "2.1.3", 872 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 873 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 874 | "optional": true 875 | }, 876 | "fstream": { 877 | "version": "1.0.11", 878 | "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", 879 | "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", 880 | "requires": { 881 | "graceful-fs": "4.1.11", 882 | "inherits": "2.0.3", 883 | "mkdirp": "0.5.1", 884 | "rimraf": "2.6.2" 885 | } 886 | }, 887 | "get-caller-file": { 888 | "version": "2.0.5", 889 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 890 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 891 | }, 892 | "getpass": { 893 | "version": "0.1.7", 894 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 895 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 896 | "requires": { 897 | "assert-plus": "1.0.0" 898 | } 899 | }, 900 | "glob": { 901 | "version": "7.1.1", 902 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 903 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 904 | "requires": { 905 | "fs.realpath": "1.0.0", 906 | "inflight": "1.0.6", 907 | "inherits": "2.0.3", 908 | "minimatch": "3.0.4", 909 | "once": "1.4.0", 910 | "path-is-absolute": "1.0.1" 911 | } 912 | }, 913 | "glob-parent": { 914 | "version": "5.1.2", 915 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 916 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 917 | "requires": { 918 | "is-glob": "^4.0.1" 919 | } 920 | }, 921 | "graceful-fs": { 922 | "version": "4.1.11", 923 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 924 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" 925 | }, 926 | "har-schema": { 927 | "version": "2.0.0", 928 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 929 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 930 | }, 931 | "har-validator": { 932 | "version": "5.1.0", 933 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", 934 | "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", 935 | "requires": { 936 | "ajv": "^5.3.0", 937 | "har-schema": "^2.0.0" 938 | } 939 | }, 940 | "has-flag": { 941 | "version": "3.0.0", 942 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 943 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" 944 | }, 945 | "http-signature": { 946 | "version": "1.2.0", 947 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 948 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 949 | "requires": { 950 | "assert-plus": "1.0.0", 951 | "jsprim": "1.4.1", 952 | "sshpk": "1.15.2" 953 | } 954 | }, 955 | "inflight": { 956 | "version": "1.0.6", 957 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 958 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 959 | "requires": { 960 | "once": "1.4.0", 961 | "wrappy": "1.0.2" 962 | } 963 | }, 964 | "inherits": { 965 | "version": "2.0.3", 966 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 967 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 968 | }, 969 | "is-binary-path": { 970 | "version": "2.1.0", 971 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 972 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 973 | "requires": { 974 | "binary-extensions": "^2.0.0" 975 | } 976 | }, 977 | "is-extglob": { 978 | "version": "2.1.1", 979 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 980 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" 981 | }, 982 | "is-fullwidth-code-point": { 983 | "version": "2.0.0", 984 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 985 | "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==" 986 | }, 987 | "is-glob": { 988 | "version": "4.0.3", 989 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 990 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 991 | "requires": { 992 | "is-extglob": "^2.1.1" 993 | } 994 | }, 995 | "is-number": { 996 | "version": "7.0.0", 997 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 998 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 999 | }, 1000 | "is-typedarray": { 1001 | "version": "1.0.0", 1002 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 1003 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 1004 | }, 1005 | "isexe": { 1006 | "version": "2.0.0", 1007 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1008 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" 1009 | }, 1010 | "isstream": { 1011 | "version": "0.1.2", 1012 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 1013 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 1014 | }, 1015 | "jsbn": { 1016 | "version": "0.1.1", 1017 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 1018 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 1019 | }, 1020 | "json-schema": { 1021 | "version": "0.2.3", 1022 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 1023 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 1024 | }, 1025 | "json-schema-traverse": { 1026 | "version": "0.3.1", 1027 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 1028 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 1029 | }, 1030 | "json-stringify-safe": { 1031 | "version": "5.0.1", 1032 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 1033 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 1034 | }, 1035 | "jsonfile": { 1036 | "version": "4.0.0", 1037 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", 1038 | "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", 1039 | "requires": { 1040 | "graceful-fs": "^4.1.6" 1041 | } 1042 | }, 1043 | "jsprim": { 1044 | "version": "1.4.1", 1045 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 1046 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 1047 | "requires": { 1048 | "assert-plus": "1.0.0", 1049 | "extsprintf": "1.3.0", 1050 | "json-schema": "0.2.3", 1051 | "verror": "1.10.0" 1052 | } 1053 | }, 1054 | "locate-path": { 1055 | "version": "3.0.0", 1056 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 1057 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 1058 | "requires": { 1059 | "p-locate": "^3.0.0", 1060 | "path-exists": "^3.0.0" 1061 | } 1062 | }, 1063 | "lodash": { 1064 | "version": "4.17.10", 1065 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", 1066 | "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" 1067 | }, 1068 | "match-stream": { 1069 | "version": "0.0.2", 1070 | "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz", 1071 | "integrity": "sha1-mesFAJOzTf+t5CG5rAtBCpz6F88=", 1072 | "requires": { 1073 | "buffers": "0.1.1", 1074 | "readable-stream": "1.0.34" 1075 | }, 1076 | "dependencies": { 1077 | "isarray": { 1078 | "version": "0.0.1", 1079 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1080 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 1081 | }, 1082 | "readable-stream": { 1083 | "version": "1.0.34", 1084 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 1085 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 1086 | "requires": { 1087 | "core-util-is": "1.0.2", 1088 | "inherits": "2.0.3", 1089 | "isarray": "0.0.1", 1090 | "string_decoder": "0.10.31" 1091 | } 1092 | }, 1093 | "string_decoder": { 1094 | "version": "0.10.31", 1095 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1096 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 1097 | } 1098 | } 1099 | }, 1100 | "mime-db": { 1101 | "version": "1.37.0", 1102 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", 1103 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" 1104 | }, 1105 | "mime-types": { 1106 | "version": "2.1.21", 1107 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", 1108 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", 1109 | "requires": { 1110 | "mime-db": "1.37.0" 1111 | } 1112 | }, 1113 | "minimatch": { 1114 | "version": "3.0.4", 1115 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1116 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1117 | "requires": { 1118 | "brace-expansion": "^1.1.7" 1119 | } 1120 | }, 1121 | "minimist": { 1122 | "version": "0.0.8", 1123 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1124 | "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==" 1125 | }, 1126 | "minipass": { 1127 | "version": "2.9.0", 1128 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", 1129 | "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", 1130 | "requires": { 1131 | "safe-buffer": "^5.1.2", 1132 | "yallist": "^3.0.0" 1133 | } 1134 | }, 1135 | "minizlib": { 1136 | "version": "1.3.3", 1137 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", 1138 | "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", 1139 | "requires": { 1140 | "minipass": "^2.9.0" 1141 | } 1142 | }, 1143 | "mkdirp": { 1144 | "version": "0.5.1", 1145 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1146 | "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", 1147 | "requires": { 1148 | "minimist": "0.0.8" 1149 | } 1150 | }, 1151 | "murmur-hash-js": { 1152 | "version": "1.0.0", 1153 | "resolved": "https://registry.npmjs.org/murmur-hash-js/-/murmur-hash-js-1.0.0.tgz", 1154 | "integrity": "sha512-g3vtW36bHHcmcGOLlI+cVUBPtaoLdPkBNVNkHE+1mKgaYfjWXF6bWOlhsU6r+V+yG8tqUyM7WveRAXvHa98dZg==" 1155 | }, 1156 | "mustache": { 1157 | "version": "3.2.1", 1158 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.2.1.tgz", 1159 | "integrity": "sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==" 1160 | }, 1161 | "natives": { 1162 | "version": "1.1.6", 1163 | "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", 1164 | "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==" 1165 | }, 1166 | "nice-try": { 1167 | "version": "1.0.5", 1168 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 1169 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" 1170 | }, 1171 | "node-elm-compiler": { 1172 | "version": "5.0.4", 1173 | "resolved": "https://registry.npmjs.org/node-elm-compiler/-/node-elm-compiler-5.0.4.tgz", 1174 | "integrity": "sha512-VQsT8QSierYGkHzRed+b4MnccQVF1+qPHunE8jBoU7jD6YpuRqCDPzEoC2zfyEJS80qVnlMZrqobLnyjzX9lJg==", 1175 | "requires": { 1176 | "cross-spawn": "6.0.5", 1177 | "find-elm-dependencies": "2.0.2", 1178 | "lodash": "4.17.15", 1179 | "temp": "^0.9.0" 1180 | }, 1181 | "dependencies": { 1182 | "cross-spawn": { 1183 | "version": "6.0.5", 1184 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 1185 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 1186 | "requires": { 1187 | "nice-try": "^1.0.4", 1188 | "path-key": "^2.0.1", 1189 | "semver": "^5.5.0", 1190 | "shebang-command": "^1.2.0", 1191 | "which": "^1.2.9" 1192 | } 1193 | }, 1194 | "lodash": { 1195 | "version": "4.17.15", 1196 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", 1197 | "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" 1198 | }, 1199 | "path-key": { 1200 | "version": "2.0.1", 1201 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 1202 | "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" 1203 | }, 1204 | "shebang-command": { 1205 | "version": "1.2.0", 1206 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1207 | "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", 1208 | "requires": { 1209 | "shebang-regex": "^1.0.0" 1210 | } 1211 | }, 1212 | "shebang-regex": { 1213 | "version": "1.0.0", 1214 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1215 | "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" 1216 | }, 1217 | "which": { 1218 | "version": "1.3.1", 1219 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1220 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1221 | "requires": { 1222 | "isexe": "^2.0.0" 1223 | } 1224 | } 1225 | } 1226 | }, 1227 | "normalize-path": { 1228 | "version": "3.0.0", 1229 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1230 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 1231 | }, 1232 | "oauth-sign": { 1233 | "version": "0.9.0", 1234 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 1235 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 1236 | }, 1237 | "once": { 1238 | "version": "1.4.0", 1239 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1240 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1241 | "requires": { 1242 | "wrappy": "1.0.2" 1243 | } 1244 | }, 1245 | "over": { 1246 | "version": "0.0.5", 1247 | "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz", 1248 | "integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg=" 1249 | }, 1250 | "p-limit": { 1251 | "version": "2.3.0", 1252 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 1253 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 1254 | "requires": { 1255 | "p-try": "^2.0.0" 1256 | } 1257 | }, 1258 | "p-locate": { 1259 | "version": "3.0.0", 1260 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 1261 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 1262 | "requires": { 1263 | "p-limit": "^2.0.0" 1264 | } 1265 | }, 1266 | "p-try": { 1267 | "version": "2.2.0", 1268 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1269 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" 1270 | }, 1271 | "path-exists": { 1272 | "version": "3.0.0", 1273 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 1274 | "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" 1275 | }, 1276 | "path-is-absolute": { 1277 | "version": "1.0.1", 1278 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1279 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1280 | }, 1281 | "path-key": { 1282 | "version": "3.1.1", 1283 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1284 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" 1285 | }, 1286 | "performance-now": { 1287 | "version": "2.1.0", 1288 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 1289 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 1290 | }, 1291 | "picomatch": { 1292 | "version": "2.3.1", 1293 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1294 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" 1295 | }, 1296 | "psl": { 1297 | "version": "1.1.29", 1298 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", 1299 | "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" 1300 | }, 1301 | "pullstream": { 1302 | "version": "0.4.1", 1303 | "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz", 1304 | "integrity": "sha1-1vs79a7Wl+gxFQ6xACwlo/iuExQ=", 1305 | "requires": { 1306 | "over": "0.0.5", 1307 | "readable-stream": "1.0.34", 1308 | "setimmediate": "1.0.5", 1309 | "slice-stream": "1.0.0" 1310 | }, 1311 | "dependencies": { 1312 | "isarray": { 1313 | "version": "0.0.1", 1314 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1315 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 1316 | }, 1317 | "readable-stream": { 1318 | "version": "1.0.34", 1319 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 1320 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 1321 | "requires": { 1322 | "core-util-is": "1.0.2", 1323 | "inherits": "2.0.3", 1324 | "isarray": "0.0.1", 1325 | "string_decoder": "0.10.31" 1326 | } 1327 | }, 1328 | "string_decoder": { 1329 | "version": "0.10.31", 1330 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1331 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 1332 | } 1333 | } 1334 | }, 1335 | "punycode": { 1336 | "version": "1.4.1", 1337 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1338 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 1339 | }, 1340 | "qs": { 1341 | "version": "6.5.2", 1342 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1343 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1344 | }, 1345 | "readdirp": { 1346 | "version": "3.1.3", 1347 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.1.3.tgz", 1348 | "integrity": "sha512-ZOsfTGkjO2kqeR5Mzr5RYDbTGYneSkdNKX2fOX2P5jF7vMrd/GNnIAUtDldeHHumHUCQ3V05YfWUdxMPAsRu9Q==", 1349 | "requires": { 1350 | "picomatch": "^2.0.4" 1351 | } 1352 | }, 1353 | "request": { 1354 | "version": "2.88.0", 1355 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 1356 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 1357 | "requires": { 1358 | "aws-sign2": "0.7.0", 1359 | "aws4": "1.8.0", 1360 | "caseless": "0.12.0", 1361 | "combined-stream": "1.0.7", 1362 | "extend": "3.0.2", 1363 | "forever-agent": "0.6.1", 1364 | "form-data": "2.3.3", 1365 | "har-validator": "5.1.0", 1366 | "http-signature": "1.2.0", 1367 | "is-typedarray": "1.0.0", 1368 | "isstream": "0.1.2", 1369 | "json-stringify-safe": "5.0.1", 1370 | "mime-types": "2.1.21", 1371 | "oauth-sign": "0.9.0", 1372 | "performance-now": "2.1.0", 1373 | "qs": "6.5.2", 1374 | "safe-buffer": "5.1.2", 1375 | "tough-cookie": "2.4.3", 1376 | "tunnel-agent": "0.6.0", 1377 | "uuid": "3.3.2" 1378 | } 1379 | }, 1380 | "request-promise": { 1381 | "version": "4.2.2", 1382 | "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz", 1383 | "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=", 1384 | "requires": { 1385 | "bluebird": "3.5.2", 1386 | "request-promise-core": "1.1.1", 1387 | "stealthy-require": "1.1.1", 1388 | "tough-cookie": "2.4.3" 1389 | } 1390 | }, 1391 | "request-promise-core": { 1392 | "version": "1.1.1", 1393 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", 1394 | "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", 1395 | "requires": { 1396 | "lodash": "4.17.10" 1397 | } 1398 | }, 1399 | "require-directory": { 1400 | "version": "2.1.1", 1401 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1402 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" 1403 | }, 1404 | "require-main-filename": { 1405 | "version": "2.0.0", 1406 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1407 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" 1408 | }, 1409 | "rimraf": { 1410 | "version": "2.6.2", 1411 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 1412 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 1413 | "requires": { 1414 | "glob": "7.1.1" 1415 | } 1416 | }, 1417 | "safe-buffer": { 1418 | "version": "5.1.2", 1419 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1420 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1421 | }, 1422 | "safer-buffer": { 1423 | "version": "2.1.2", 1424 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1425 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1426 | }, 1427 | "semver": { 1428 | "version": "5.7.2", 1429 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", 1430 | "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" 1431 | }, 1432 | "set-blocking": { 1433 | "version": "2.0.0", 1434 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1435 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" 1436 | }, 1437 | "setimmediate": { 1438 | "version": "1.0.5", 1439 | "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", 1440 | "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" 1441 | }, 1442 | "shebang-command": { 1443 | "version": "2.0.0", 1444 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1445 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1446 | "requires": { 1447 | "shebang-regex": "^3.0.0" 1448 | } 1449 | }, 1450 | "shebang-regex": { 1451 | "version": "3.0.0", 1452 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1453 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" 1454 | }, 1455 | "slice-stream": { 1456 | "version": "1.0.0", 1457 | "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz", 1458 | "integrity": "sha1-WzO9ZvATsaf4ZGCwPUY97DmtPqA=", 1459 | "requires": { 1460 | "readable-stream": "1.0.34" 1461 | }, 1462 | "dependencies": { 1463 | "isarray": { 1464 | "version": "0.0.1", 1465 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1466 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 1467 | }, 1468 | "readable-stream": { 1469 | "version": "1.0.34", 1470 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 1471 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 1472 | "requires": { 1473 | "core-util-is": "1.0.2", 1474 | "inherits": "2.0.3", 1475 | "isarray": "0.0.1", 1476 | "string_decoder": "0.10.31" 1477 | } 1478 | }, 1479 | "string_decoder": { 1480 | "version": "0.10.31", 1481 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1482 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 1483 | } 1484 | } 1485 | }, 1486 | "split": { 1487 | "version": "1.0.1", 1488 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", 1489 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", 1490 | "requires": { 1491 | "through": "2" 1492 | } 1493 | }, 1494 | "sshpk": { 1495 | "version": "1.15.2", 1496 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", 1497 | "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", 1498 | "requires": { 1499 | "asn1": "0.2.4", 1500 | "assert-plus": "1.0.0", 1501 | "bcrypt-pbkdf": "1.0.2", 1502 | "dashdash": "1.14.1", 1503 | "ecc-jsbn": "0.1.2", 1504 | "getpass": "0.1.7", 1505 | "jsbn": "0.1.1", 1506 | "safer-buffer": "2.1.2", 1507 | "tweetnacl": "0.14.5" 1508 | } 1509 | }, 1510 | "stealthy-require": { 1511 | "version": "1.1.1", 1512 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", 1513 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" 1514 | }, 1515 | "string-width": { 1516 | "version": "3.1.0", 1517 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1518 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1519 | "requires": { 1520 | "emoji-regex": "^7.0.1", 1521 | "is-fullwidth-code-point": "^2.0.0", 1522 | "strip-ansi": "^5.1.0" 1523 | } 1524 | }, 1525 | "strip-ansi": { 1526 | "version": "5.2.0", 1527 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1528 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1529 | "requires": { 1530 | "ansi-regex": "^4.1.0" 1531 | } 1532 | }, 1533 | "supports-color": { 1534 | "version": "7.1.0", 1535 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 1536 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 1537 | "requires": { 1538 | "has-flag": "^4.0.0" 1539 | }, 1540 | "dependencies": { 1541 | "has-flag": { 1542 | "version": "4.0.0", 1543 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1544 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 1545 | } 1546 | } 1547 | }, 1548 | "tar": { 1549 | "version": "2.2.1", 1550 | "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", 1551 | "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", 1552 | "requires": { 1553 | "block-stream": "0.0.9", 1554 | "fstream": "1.0.11", 1555 | "inherits": "2.0.3" 1556 | } 1557 | }, 1558 | "temp": { 1559 | "version": "0.9.0", 1560 | "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.0.tgz", 1561 | "integrity": "sha512-YfUhPQCJoNQE5N+FJQcdPz63O3x3sdT4Xju69Gj4iZe0lBKOtnAMi0SLj9xKhGkcGhsxThvTJ/usxtFPo438zQ==", 1562 | "requires": { 1563 | "rimraf": "~2.6.2" 1564 | } 1565 | }, 1566 | "through": { 1567 | "version": "2.3.8", 1568 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1569 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" 1570 | }, 1571 | "to-regex-range": { 1572 | "version": "5.0.1", 1573 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1574 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1575 | "requires": { 1576 | "is-number": "^7.0.0" 1577 | } 1578 | }, 1579 | "tough-cookie": { 1580 | "version": "2.4.3", 1581 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 1582 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 1583 | "requires": { 1584 | "psl": "1.1.29", 1585 | "punycode": "1.4.1" 1586 | } 1587 | }, 1588 | "traverse": { 1589 | "version": "0.3.9", 1590 | "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", 1591 | "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" 1592 | }, 1593 | "tunnel-agent": { 1594 | "version": "0.6.0", 1595 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1596 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1597 | "requires": { 1598 | "safe-buffer": "5.1.2" 1599 | } 1600 | }, 1601 | "tweetnacl": { 1602 | "version": "0.14.5", 1603 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1604 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1605 | }, 1606 | "universalify": { 1607 | "version": "0.1.2", 1608 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", 1609 | "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" 1610 | }, 1611 | "unzip": { 1612 | "version": "0.1.11", 1613 | "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.11.tgz", 1614 | "integrity": "sha1-iXScY7BY19kNYZ+GuYqhU107l/A=", 1615 | "requires": { 1616 | "binary": "0.3.0", 1617 | "fstream": "0.1.31", 1618 | "match-stream": "0.0.2", 1619 | "pullstream": "0.4.1", 1620 | "readable-stream": "1.0.34", 1621 | "setimmediate": "1.0.5" 1622 | }, 1623 | "dependencies": { 1624 | "fstream": { 1625 | "version": "0.1.31", 1626 | "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", 1627 | "integrity": "sha1-czfwWPu7vvqMn1YaKMqwhJICyYg=", 1628 | "requires": { 1629 | "graceful-fs": "3.0.11", 1630 | "inherits": "2.0.3", 1631 | "mkdirp": "0.5.1", 1632 | "rimraf": "2.6.2" 1633 | } 1634 | }, 1635 | "graceful-fs": { 1636 | "version": "3.0.11", 1637 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", 1638 | "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", 1639 | "requires": { 1640 | "natives": "1.1.6" 1641 | } 1642 | }, 1643 | "isarray": { 1644 | "version": "0.0.1", 1645 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1646 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 1647 | }, 1648 | "readable-stream": { 1649 | "version": "1.0.34", 1650 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 1651 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 1652 | "requires": { 1653 | "core-util-is": "1.0.2", 1654 | "inherits": "2.0.3", 1655 | "isarray": "0.0.1", 1656 | "string_decoder": "0.10.31" 1657 | } 1658 | }, 1659 | "string_decoder": { 1660 | "version": "0.10.31", 1661 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1662 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 1663 | } 1664 | } 1665 | }, 1666 | "unzip-stream": { 1667 | "version": "0.3.4", 1668 | "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.4.tgz", 1669 | "integrity": "sha512-PyofABPVv+d7fL7GOpusx7eRT9YETY2X04PhwbSipdj6bMxVCFJrr+nm0Mxqbf9hUiTin/UsnuFWBXlDZFy0Cw==", 1670 | "requires": { 1671 | "binary": "^0.3.0", 1672 | "mkdirp": "^0.5.1" 1673 | } 1674 | }, 1675 | "uuid": { 1676 | "version": "3.3.2", 1677 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1678 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1679 | }, 1680 | "verror": { 1681 | "version": "1.10.0", 1682 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1683 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1684 | "requires": { 1685 | "assert-plus": "1.0.0", 1686 | "core-util-is": "1.0.2", 1687 | "extsprintf": "1.3.0" 1688 | } 1689 | }, 1690 | "which": { 1691 | "version": "2.0.1", 1692 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz", 1693 | "integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==", 1694 | "requires": { 1695 | "isexe": "^2.0.0" 1696 | } 1697 | }, 1698 | "which-module": { 1699 | "version": "2.0.1", 1700 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", 1701 | "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" 1702 | }, 1703 | "wrap-ansi": { 1704 | "version": "5.1.0", 1705 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 1706 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 1707 | "requires": { 1708 | "ansi-styles": "^3.2.0", 1709 | "string-width": "^3.0.0", 1710 | "strip-ansi": "^5.0.0" 1711 | } 1712 | }, 1713 | "wrappy": { 1714 | "version": "1.0.2", 1715 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1716 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1717 | }, 1718 | "xmlbuilder": { 1719 | "version": "13.0.2", 1720 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", 1721 | "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==" 1722 | }, 1723 | "y18n": { 1724 | "version": "4.0.3", 1725 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", 1726 | "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" 1727 | }, 1728 | "yallist": { 1729 | "version": "3.1.1", 1730 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 1731 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" 1732 | }, 1733 | "yargs": { 1734 | "version": "13.3.2", 1735 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 1736 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 1737 | "requires": { 1738 | "cliui": "^5.0.0", 1739 | "find-up": "^3.0.0", 1740 | "get-caller-file": "^2.0.1", 1741 | "require-directory": "^2.1.1", 1742 | "require-main-filename": "^2.0.0", 1743 | "set-blocking": "^2.0.0", 1744 | "string-width": "^3.0.0", 1745 | "which-module": "^2.0.0", 1746 | "y18n": "^4.0.0", 1747 | "yargs-parser": "^13.1.2" 1748 | } 1749 | }, 1750 | "yargs-parser": { 1751 | "version": "13.1.2", 1752 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 1753 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 1754 | "requires": { 1755 | "camelcase": "^5.0.0", 1756 | "decamelize": "^1.2.0" 1757 | } 1758 | } 1759 | } 1760 | } 1761 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elm-lazy-tree-with-zipper", 3 | "version": "3.4.1", 4 | "scripts": { 5 | "test": "./node_modules/.bin/elm-verify-examples --elm-test=elm-test" 6 | }, 7 | "dependencies": { 8 | "elm": "^0.19.0-nodeps", 9 | "elm-test": "^0.19.1", 10 | "elm-verify-examples": "^5.2.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /performance/README.md: -------------------------------------------------------------------------------- 1 | # Performance 2 | 3 | ## fromList vs fromKeyedList 4 | 5 | This is the benchmark result between the two function for construction from `List` from FireFox on my laptop: 6 | 7 | ![](construction.png) 8 | 9 | ## Lazy Implementation vs Strict Implementation 10 | 11 | I've implemented the same feature within one of our application using 12 | [tomjkidd/elm-multiway-tree-zipper](http://package.elm-lang.org/packages/tomjkidd/elm-multiway-tree-zipper/latest) 13 | and this package and profiled performance with real production data. 14 | 15 | Application fetches data from two endpoints and then builds a Tree from them. The total number of nodes within resulting tree is 4278. 16 | 17 | ### Lazy Implementation (this package) 18 | 19 | MacOS Chrome: 20 | ![](mac_lazy.png) 21 | 22 | Arch Linux Chromium: 23 | ![](linux_lazy.png) 24 | 25 | ### Strict Implementation (tomjkidd/elm-multiway-tree-zipper) 26 | 27 | MacOS Chrome: 28 | ![](mac_strict.png) 29 | 30 | Arch Linux Chromium: 31 | ![](linux_strict.png) 32 | 33 | 34 | ### Conclusion 35 | 36 | For large trees where it's not necessary to evaluate whole structure at once lazy tree is definitely much more performant. 37 | However if you're working with much smaller data strict version like `tomjkidd/elm-multiway-tree-zipper` should be fine. 38 | 39 | ### HW specifications 40 | 41 | #### MacOS Chrome 42 | 43 | ``` 44 | MacBook Pro (Retina, 15-inch, Mid 2015) 45 | 2,2 GHz Intel Core i7 46 | 16 GB 1600 MHz DDR3 47 | Intel Iris Pro 1536 MB 48 | ``` 49 | 50 | ``` 51 | Chrome Version 63.0.3239.108 (Official Build) (64-bit) 52 | ``` 53 | 54 | 55 | #### Arch Linux Chromium 56 | 57 | ``` 58 | OS: Arch Linux 59 | Kernel: x86_64 Linux 4.14.9-1-ARCH 60 | Packages: 1448 61 | Shell: zsh 5.4.2 62 | Resolution: 1920x1200 63 | WM: XMonad 64 | CPU: AMD Ryzen 7 1800X Eight-Core @ 16x 3.6GHz [41.0°C] 65 | GPU: AMD/ATI Lexa PRO [Radeon RX 550] 66 | RAM: 2985MiB / 32183MiB 67 | ``` 68 | 69 | ``` 70 | Chromium Version 63.0.3239.108 (Developer Build) (64-bit) 71 | ``` 72 | -------------------------------------------------------------------------------- /performance/construction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboMaCk/lazy-tree-with-zipper/9685e2eaa1a66900fe4ed63452925e8fdd268964/performance/construction.png -------------------------------------------------------------------------------- /performance/linux_lazy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboMaCk/lazy-tree-with-zipper/9685e2eaa1a66900fe4ed63452925e8fdd268964/performance/linux_lazy.png -------------------------------------------------------------------------------- /performance/linux_strict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboMaCk/lazy-tree-with-zipper/9685e2eaa1a66900fe4ed63452925e8fdd268964/performance/linux_strict.png -------------------------------------------------------------------------------- /performance/mac_lazy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboMaCk/lazy-tree-with-zipper/9685e2eaa1a66900fe4ed63452925e8fdd268964/performance/mac_lazy.png -------------------------------------------------------------------------------- /performance/mac_strict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turboMaCk/lazy-tree-with-zipper/9685e2eaa1a66900fe4ed63452925e8fdd268964/performance/mac_strict.png -------------------------------------------------------------------------------- /src/Lazy.elm: -------------------------------------------------------------------------------- 1 | module Lazy exposing 2 | ( Lazy, lazy, force, evaluate 3 | , map, map2, map3, map4, map5 4 | , apply, andMap, andThen 5 | ) 6 | 7 | {-| This library lets you delay a computation until later. 8 | 9 | 10 | # Basics 11 | 12 | @docs Lazy, lazy, force, evaluate 13 | 14 | 15 | # Mapping 16 | 17 | @docs map, map2, map3, map4, map5 18 | 19 | 20 | # Chaining 21 | 22 | @docs apply, andMap, andThen 23 | 24 | -} 25 | 26 | -- PRIMITIVES 27 | 28 | 29 | {-| A wrapper around a value that will be lazily evaluated. 30 | -} 31 | type Lazy a 32 | = Lazy (() -> a) 33 | | Evaluated a 34 | 35 | 36 | {-| Delay the evaluation of a value until later. For example, maybe we will 37 | need to generate a very long list and find its sum, but we do not want to do 38 | it unless it is absolutely necessary. 39 | 40 | lazySum : Lazy Int 41 | lazySum = 42 | lazy (() -> sum <| List.range 1 1000000) 43 | 44 | Now we only pay for `lazySum` if we actually need it. 45 | 46 | -} 47 | lazy : (() -> a) -> Lazy a 48 | lazy thunk = 49 | Lazy thunk 50 | 51 | 52 | {-| Force the evaluation of a lazy value. This means we only pay for the 53 | computation when we need it. Here is a rather contrived example. 54 | 55 | lazySum : Lazy Int 56 | lazySum = 57 | lazy (() -> List.sum <| List.range 1 1000000) 58 | 59 | sums : ( Int, Int, Int ) 60 | sums = 61 | ( force lazySum, force lazySum, force lazySum ) 62 | 63 | -} 64 | force : Lazy a -> a 65 | force piece = 66 | case piece of 67 | Evaluated a -> 68 | a 69 | 70 | Lazy thunk -> 71 | thunk () 72 | 73 | 74 | {-| Evaluate the lazy value if it has not already been evaluated. If it has, 75 | do nothing. 76 | 77 | lazySum : Lazy Int 78 | lazySum = 79 | lazy (() -> List.sum <| List.range 1 1000000) 80 | 81 | sums : ( Int, Int, Int ) 82 | sums = 83 | let 84 | evaledSum = 85 | evaluate lazySum 86 | 87 | in 88 | ( force evaledSum, force evaledSum, force evaledSum ) 89 | 90 | This is mainly useful for cases where you may want to store a lazy value as a 91 | lazy value and pass it around. For example, in a list. Where possible, it is better to use 92 | `force` and simply store the computed value separately. 93 | 94 | -} 95 | evaluate : Lazy a -> Lazy a 96 | evaluate piece = 97 | case piece of 98 | Evaluated a -> 99 | Evaluated a 100 | 101 | Lazy thunk -> 102 | thunk () 103 | |> Evaluated 104 | 105 | 106 | 107 | -- COMPOSING LAZINESS 108 | 109 | 110 | {-| Lazily apply a function to a lazy value. 111 | 112 | lazySum : Lazy Int 113 | lazySum = 114 | map List.sum (lazy (() -> List.range 1 1000000) 115 | 116 | The resulting lazy value will create a big list and sum it up when it is 117 | finally forced. 118 | 119 | -} 120 | map : (a -> b) -> Lazy a -> Lazy b 121 | map f a = 122 | lazy (\() -> f (force a)) 123 | 124 | 125 | {-| Lazily apply a function to two lazy values. 126 | 127 | lazySum : Lazy Int 128 | lazySum = 129 | lazy (() -> List.sum <| List.range 1 1000000) 130 | 131 | lazySumPair : Lazy ( Int, Int ) 132 | lazySumPair = 133 | map2 (,) lazySum lazySum 134 | 135 | -} 136 | map2 : (a -> b -> result) -> Lazy a -> Lazy b -> Lazy result 137 | map2 f a b = 138 | lazy (\() -> f (force a) (force b)) 139 | 140 | 141 | {-| -} 142 | map3 : (a -> b -> c -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy result 143 | map3 f a b c = 144 | lazy (\() -> f (force a) (force b) (force c)) 145 | 146 | 147 | {-| -} 148 | map4 : (a -> b -> c -> d -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy d -> Lazy result 149 | map4 f a b c d = 150 | lazy (\() -> f (force a) (force b) (force c) (force d)) 151 | 152 | 153 | {-| -} 154 | map5 : (a -> b -> c -> d -> e -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy d -> Lazy e -> Lazy result 155 | map5 f a b c d e = 156 | lazy (\() -> f (force a) (force b) (force c) (force d) (force e)) 157 | 158 | 159 | {-| Lazily apply a lazy function to a lazy value. This is pretty rare on its 160 | own, but it lets you map as high as you want. 161 | 162 | map3 : (a -> b -> c -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy result 163 | map3 f a b c = 164 | apply (apply (apply (lazy (\() -> f)) a) b) c 165 | 166 | It is not the most beautiful, but it is equivalent and will let you create 167 | `map9` quite easily if you really need it. 168 | 169 | -} 170 | apply : Lazy (a -> b) -> Lazy a -> Lazy b 171 | apply f x = 172 | lazy (\() -> force f (force x)) 173 | 174 | 175 | {-| Piping-friendly version of `apply`. 176 | 177 | map3 : (a -> b -> c -> result) -> Lazy a -> Lazy b -> Lazy c -> Lazy result 178 | map3 f a b c = 179 | lazy (\() -> f) |> andMap a |> andMap b |> andMap c 180 | 181 | -} 182 | andMap : Lazy a -> Lazy (a -> b) -> Lazy b 183 | andMap x f = 184 | apply f x 185 | 186 | 187 | {-| Lazily chain lazy computations together, for when you have a series of 188 | steps that all need to be performed lazily. This can be nice when you need to 189 | pattern match on a value, for example, when appending lazy lists: 190 | 191 | type List a = Empty | Node a (Lazy (List a)) 192 | 193 | cons : a -> Lazy (List a) -> Lazy (List a) 194 | cons first rest = 195 | Lazy.map (Node first) rest 196 | 197 | append : Lazy (List a) -> Lazy (List a) -> Lazy (List a) 198 | append lazyList1 lazyList2 = 199 | let 200 | appendHelp list1 = 201 | case list1 of 202 | Empty -> 203 | lazyList2 204 | Node first rest -> 205 | cons first (append rest list2)) 206 | in 207 | lazyList1 |> Lazy.andThen appendHelp 208 | 209 | By using `andThen` we ensure that neither `lazyList1` or `lazyList2` are forced 210 | before they are needed. So as written, the `append` function delays the pattern 211 | matching until later. 212 | 213 | -} 214 | andThen : (a -> Lazy b) -> Lazy a -> Lazy b 215 | andThen callback a = 216 | lazy (\() -> force (callback (force a))) 217 | -------------------------------------------------------------------------------- /src/Lazy/LList.elm: -------------------------------------------------------------------------------- 1 | module Lazy.LList exposing 2 | ( LList, empty, singleton, llist, fromList 3 | , cons, append 4 | , isEmpty, toList, head, tail 5 | , map, map2, filter, filterMap, reverse, sort, sortBy, sortWith, stableSortWith, foldr, foldl, concat, andThen 6 | ) 7 | 8 | {-| This module implements lazy construction of strict List. 9 | It is not Lazy List implementation and it has different characteristics. 10 | It's mostly intended for internal purposes of this library and is exposed 11 | just in case some additional user extensions will need it. 12 | 13 | 14 | # Types & Constructors 15 | 16 | @docs LList, empty, singleton, llist, fromList 17 | 18 | 19 | # Operations 20 | 21 | @docs cons, append 22 | 23 | 24 | # Query 25 | 26 | @docs isEmpty, toList, head, tail 27 | 28 | 29 | # Transformations 30 | 31 | @docs map, map2, filter, filterMap, reverse, sort, sortBy, sortWith, stableSortWith, foldr, foldl, concat, andThen 32 | 33 | -} 34 | 35 | import Lazy exposing (Lazy) 36 | 37 | 38 | {-| **`LList` uses lazy implementation and therefore can't be compared using `==`** 39 | -} 40 | type alias LList a = 41 | Lazy (List a) 42 | 43 | 44 | {-| Initialize empty `LList`. 45 | 46 | toList empty 47 | --> [] 48 | 49 | -} 50 | empty : LList a 51 | empty = 52 | llist identity [] 53 | 54 | 55 | {-| Initialize singleton `LList`. 56 | 57 | singleton "foo" 58 | |> toList 59 | --> [ "foo" ] 60 | 61 | -} 62 | singleton : a -> LList a 63 | singleton = 64 | llist List.singleton 65 | 66 | 67 | {-| Init `LList` using constructor. 68 | 69 | `LList` is initialized using a function from `a -> List b`. 70 | Evaluation of this function is lazy and happens in time when 71 | actual value is needed, not when constructor is called. 72 | 73 | For instance you can use some `List` constructor: 74 | 75 | llist (List.range 0) 5 76 | |> toList 77 | --> [ 0, 1, 2, 3, 4, 5 ] 78 | 79 | Or use any other function you need`List`: 80 | 81 | llist (List.filter <| \a -> modBy 2 a == 0) (List.range 0 10) 82 | |> toList 83 | --> [ 0, 2, 4, 6, 8, 10 ] 84 | 85 | -} 86 | llist : (a -> List b) -> a -> LList b 87 | llist constructor arg = 88 | Lazy.lazy <| \() -> constructor arg 89 | 90 | 91 | {-| Construct `LList` from `List`. 92 | 93 | fromList [ "foo", "bar", "baz" ] 94 | |> toList 95 | --> [ "foo", "bar", "baz" ] 96 | 97 | -} 98 | fromList : List a -> LList a 99 | fromList = 100 | llist identity 101 | 102 | 103 | {-| Add element to `LList`. 104 | 105 | empty 106 | |> cons "bar" 107 | |> cons "foo" 108 | |> toList 109 | --> [ "foo", "bar" ] 110 | 111 | This function is performed lazily. 112 | 113 | -} 114 | cons : a -> LList a -> LList a 115 | cons a = 116 | Lazy.map ((::) a) 117 | 118 | 119 | {-| Append `LList` to `LList`. 120 | 121 | append (singleton "foo") (fromList [ "bar", "baz" ]) 122 | |> toList 123 | --> [ "foo", "bar", "baz" ] 124 | 125 | This function is performed lazily. 126 | 127 | -} 128 | append : LList a -> LList a -> LList a 129 | append = 130 | Lazy.map2 (++) 131 | 132 | 133 | {-| Check if `LList` is empty. 134 | 135 | isEmpty empty 136 | --> True 137 | 138 | isEmpty (singleton "foo") 139 | --> False 140 | 141 | llist (List.filter <| \a -> a > 10) [ 1, 2, 3 ] 142 | |> isEmpty 143 | --> True 144 | 145 | This function forces evaluation. 146 | 147 | -} 148 | isEmpty : LList a -> Bool 149 | isEmpty = 150 | List.isEmpty << toList 151 | 152 | 153 | {-| Build `List` from `LList`. 154 | 155 | toList empty 156 | --> [] 157 | 158 | toList <| llist (List.range 0) 2 159 | --> [ 0, 1, 2 ] 160 | 161 | This function forces evaluation. 162 | 163 | -} 164 | toList : LList a -> List a 165 | toList = 166 | Lazy.force 167 | 168 | 169 | {-| Get the first element from `LList`. 170 | 171 | head empty 172 | --> Nothing 173 | 174 | llist (List.range 0) 2 175 | |> head 176 | --> Just 0 177 | 178 | This function forces evaluation. 179 | 180 | -} 181 | head : LList a -> Maybe a 182 | head = 183 | List.head << toList 184 | 185 | 186 | {-| Get the first element from `LList`. 187 | 188 | tail empty 189 | --> Nothing 190 | 191 | tail (singleton "foo") 192 | |> Maybe.map toList 193 | --> Just [] 194 | 195 | llist (List.range 0) 2 196 | |> tail 197 | |> Maybe.map toList 198 | --> Just [ 1, 2 ] 199 | 200 | This function forces evaluation. 201 | 202 | -} 203 | tail : LList a -> Maybe (LList a) 204 | tail = 205 | Maybe.map fromList << List.tail << toList 206 | 207 | 208 | {-| Map function over `LList`. 209 | 210 | llist (List.range 0) 5 211 | |> map ((*) 2) 212 | |> toList 213 | --> [ 0, 2, 4, 6, 8, 10 ] 214 | 215 | This function is performed lazily. 216 | 217 | -} 218 | map : (a -> b) -> LList a -> LList b 219 | map f = 220 | Lazy.map (List.map f) 221 | 222 | 223 | {-| Map a functions over two `LList`s. 224 | 225 | llist (List.range 0) 5 226 | |> map2 (+) (llist (List.range 0) 5) 227 | |> toList 228 | --> [ 0, 2, 4, 6, 8, 10 ] 229 | 230 | This function is performed lazily. 231 | 232 | -} 233 | map2 : (a -> b -> c) -> LList a -> LList b -> LList c 234 | map2 constructor = 235 | Lazy.map2 (List.map2 constructor) 236 | 237 | 238 | {-| Filter `LList`. 239 | 240 | (cons 1 <| cons 2 <| cons 3 empty) 241 | |> filter ((<) 1) 242 | |> toList 243 | --> [ 2, 3 ] 244 | 245 | This function is performed lazily. 246 | 247 | -} 248 | filter : (a -> Bool) -> LList a -> LList a 249 | filter predicate = 250 | Lazy.map (List.filter predicate) 251 | 252 | 253 | {-| Similar to `List.filterMap` but for `LList`. 254 | 255 | (cons 1 <| cons 2 <| cons 3 empty) 256 | |> filterMap (\a -> if 1 < a then Just (2 * a) else Nothing) 257 | |> toList 258 | --> [ 4, 6 ] 259 | 260 | This function is performed lazily. 261 | 262 | -} 263 | filterMap : (a -> Maybe b) -> LList a -> LList b 264 | filterMap predicate = 265 | Lazy.map (List.filterMap predicate) 266 | 267 | 268 | {-| Reverse `LList`. 269 | 270 | fromList [ 1, 2, 3 ] 271 | |> reverse 272 | |> toList 273 | --> [ 3, 2, 1 ] 274 | 275 | This function is performed lazily. 276 | 277 | -} 278 | reverse : LList a -> LList a 279 | reverse = 280 | Lazy.map List.reverse 281 | 282 | 283 | {-| Sort by for `LList`. 284 | 285 | fromList [ 3, 1, 2 ] 286 | |> sort 287 | |> toList 288 | --> [ 1, 2, 3 ] 289 | 290 | This function is performed lazily. 291 | 292 | -} 293 | sort : LList comparable -> LList comparable 294 | sort = 295 | Lazy.map List.sort 296 | 297 | 298 | {-| Sort by for `LList`. 299 | 300 | fromList [ 3, 1, 2 ] 301 | |> sortBy identity 302 | |> toList 303 | --> [ 1, 2, 3 ] 304 | 305 | fromList [ { val = "c"} , { val = "b"}, { val = "a"} ] 306 | |> sortBy .val 307 | |> toList 308 | --> [ { val = "a"}, { val = "b"}, { val = "c"} ] 309 | 310 | This function is performed lazily. 311 | 312 | -} 313 | sortBy : (a -> comparable) -> LList a -> LList a 314 | sortBy predicate = 315 | Lazy.map (List.sortBy predicate) 316 | 317 | 318 | {-| Sort with for `LList` 319 | 320 | flippedComparison : comparable -> comparable -> Order 321 | flippedComparison a b = 322 | case Basics.compare a b of 323 | LT -> GT 324 | EQ -> EQ 325 | GT -> LT 326 | 327 | llist (List.range 1) 5 328 | |> sortWith flippedComparison 329 | |> toList 330 | --> [ 5, 4, 3, 2, 1 ] 331 | 332 | This function is performed lazily. 333 | 334 | -} 335 | sortWith : (a -> a -> Order) -> LList a -> LList a 336 | sortWith compare = 337 | Lazy.map (List.sortWith compare) 338 | 339 | 340 | {-| Stable sort with for `LList`. 341 | The original order is guaranteed to be kept if the comparison returns `EQ`. 342 | 343 | compareAge : { r | age : comparable } -> { r | age : comparable } -> Order 344 | compareAge a b = 345 | Basics.compare a.age b.age 346 | 347 | fromList [ { name = "Joe", age = 25 }, { name = "Sue", age = 25 }, { name = "Johann", age = 23 } ] 348 | |> stableSortWith compareAge 349 | |> toList 350 | --> [ { name = "Johann", age = 23 }, { name = "Joe", age = 25 }, { name = "Sue", age = 25 } ] 351 | 352 | This function is performed lazily. 353 | 354 | -} 355 | stableSortWith : (a -> a -> Order) -> LList a -> LList a 356 | stableSortWith compare = 357 | let 358 | addIndex = 359 | List.indexedMap (\i a -> ( a, i )) 360 | 361 | compareWithIndex ( a1, i1 ) ( a2, i2 ) = 362 | let 363 | result = 364 | compare a1 a2 365 | in 366 | case result of 367 | Basics.EQ -> 368 | Basics.compare i1 i2 369 | 370 | _ -> 371 | result 372 | in 373 | Lazy.map (addIndex >> List.sortWith compareWithIndex >> List.map Tuple.first) 374 | 375 | 376 | {-| Same as `List.foldr` but for `LLists`. 377 | 378 | llist (List.range 0) 5 379 | |> foldr (+) 0 380 | --> 15 381 | 382 | llist (List.range 0) 3 383 | |> foldr (::) [] 384 | --> [ 0, 1, 2, 3 ] 385 | 386 | This function forces evaluation. 387 | 388 | -} 389 | foldr : (a -> b -> b) -> b -> LList a -> b 390 | foldr predicate acc = 391 | List.foldr predicate acc << toList 392 | 393 | 394 | {-| Same as `List.foldl` but for `LLists`. 395 | 396 | llist (List.range 0) 5 397 | |> foldl (+) 0 398 | --> 15 399 | 400 | llist (List.range 0) 3 401 | |> foldl (::) [] 402 | --> [ 3, 2, 1, 0 ] 403 | 404 | This function forces evaluation. 405 | 406 | -} 407 | foldl : (a -> b -> b) -> b -> LList a -> b 408 | foldl predicate acc = 409 | List.foldl predicate acc << toList 410 | 411 | 412 | lazyFoldr : (a -> Lazy b -> Lazy b) -> Lazy b -> LList a -> Lazy b 413 | lazyFoldr predicate acc = 414 | Lazy.andThen (List.foldr predicate acc) 415 | 416 | 417 | lazyFoldl : (a -> Lazy b -> Lazy b) -> Lazy b -> LList a -> Lazy b 418 | lazyFoldl predicate acc = 419 | Lazy.andThen (List.foldl predicate acc) 420 | 421 | 422 | {-| Flatten `LList`. 423 | 424 | fromList [ singleton "foo", cons "bar" <| singleton "baz" ] 425 | |> concat 426 | |> toList 427 | --> [ "foo", "bar", "baz" ] 428 | 429 | This function is performed lazily. 430 | 431 | -} 432 | concat : LList (LList a) -> LList a 433 | concat = 434 | lazyFoldr append empty 435 | 436 | 437 | {-| Map `LList` construction over `LList`. 438 | 439 | cons "foo" (cons "bar" <| singleton "baz") 440 | |> andThen (\a -> cons a <| singleton (a ++ " fighter" )) 441 | |> toList 442 | --> [ "foo", "foo fighter", "bar", "bar fighter", "baz", "baz fighter" ] 443 | 444 | This function is performed lazily. 445 | 446 | -} 447 | andThen : (a -> LList b) -> LList a -> LList b 448 | andThen predicate = 449 | concat << map predicate 450 | -------------------------------------------------------------------------------- /src/Lazy/Tree.elm: -------------------------------------------------------------------------------- 1 | module Lazy.Tree exposing 2 | ( Tree(..), Forest, singleton, build, fromList, fromKeyedList 3 | , isEmpty, item, children, descendants 4 | , insert 5 | , map, map2, filter, filterMap, sort, sortBy, sortWith, stableSortWith, andMap, flatten, andThen, duplicate, extend 6 | , forestMap, forestMap2 7 | ) 8 | 9 | {-| This module implements Rose Tree data structure. 10 | 11 | > In computing, a multi-way tree or rose tree is a tree data structure 12 | > with a variable and unbounded number of branches per node. 13 | 14 | This particular implementation uses lazy list construction (using `LList` module) 15 | to lazily evaluate levels of Tree. 16 | 17 | 18 | # Types & Constructor 19 | 20 | @docs Tree, Forest, singleton, build, fromList, fromKeyedList 21 | 22 | 23 | # Query 24 | 25 | @docs isEmpty, item, children, descendants 26 | 27 | 28 | # Modify 29 | 30 | @docs insert 31 | 32 | 33 | # Transforms 34 | 35 | @docs map, map2, filter, filterMap, sort, sortBy, sortWith, stableSortWith, andMap, flatten, andThen, duplicate, extend 36 | 37 | 38 | # Forest 39 | 40 | @docs forestMap, forestMap2 41 | 42 | -} 43 | 44 | import Dict exposing (Dict) 45 | import Lazy 46 | import Lazy.LList as LL exposing (LList) 47 | 48 | 49 | {-| \*\* Be careful when comparing `Tree`s using `(==)`.\*\* 50 | Due to use of laziness `(==)` isn't reliable for comparing Trees. 51 | -} 52 | type Tree a 53 | = Tree a (Forest a) 54 | 55 | 56 | {-| \*\* Be careful when comparing `Forest`s using `(==)`.\*\* 57 | Due to use of laziness `(==)` isn't reliable for comparing Forests. 58 | -} 59 | type alias Forest a = 60 | LList (Tree a) 61 | 62 | 63 | 64 | -- Tree 65 | 66 | 67 | {-| Puts value in minimal `Tree` context 68 | 69 | singleton "foo" 70 | |> item 71 | --> "foo" 72 | 73 | -} 74 | singleton : a -> Tree a 75 | singleton a = 76 | Tree a LL.empty 77 | 78 | 79 | {-| Build `Tree` using custom constructor. 80 | 81 | This can be for instance used to build `Tree` from other recursive data structure: 82 | 83 | type Item = Item String (List Item) 84 | 85 | getChildren (Item _ children) = children 86 | 87 | Item "foo" [ Item "bar" [], Item "baz" []] 88 | |> build getChildren 89 | |> children 90 | -> [ Item "bar" [], Item "baz" [] ] 91 | 92 | Or lookups to some other data structure. 93 | 94 | import Dict exposing (Dict) 95 | 96 | rootItem : String 97 | rootItem = "foo" 98 | 99 | childrenDict : Dict String (List String) 100 | childrenDict = Dict.fromList [ ("foo", [ "bar", "baz" ]) ] 101 | 102 | build (\i -> Maybe.withDefault [] <| Dict.get i childrenDict) rootItem 103 | |> children 104 | --> [ "bar", "baz" ] 105 | 106 | -} 107 | build : (a -> List a) -> a -> Tree a 108 | build getChildren root = 109 | Tree root <| LL.map (build getChildren) <| LL.llist getChildren root 110 | 111 | 112 | {-| Check if `Tree` doesn't have any child. 113 | 114 | singleton "foo" 115 | |> isEmpty 116 | --> True 117 | 118 | singleton "foo" 119 | |> insert (singleton "bar") 120 | |> isEmpty 121 | --> False 122 | 123 | -} 124 | isEmpty : Tree a -> Bool 125 | isEmpty = 126 | List.isEmpty << children 127 | 128 | 129 | {-| Obtain item from `Tree`. 130 | 131 | singleton "foo" 132 | |> item 133 | |> "foo" 134 | 135 | -} 136 | item : Tree a -> a 137 | item (Tree i _) = 138 | i 139 | 140 | 141 | {-| Obtain children items of `Tree`. 142 | 143 | singleton "foo" 144 | |> insert (singleton "bar") 145 | |> insert (singleton "baz") 146 | |> children 147 | --> [ "bar", "baz" ] 148 | 149 | -} 150 | children : Tree a -> List a 151 | children = 152 | List.map item << LL.toList << descendants 153 | 154 | 155 | {-| Obtain descendants as `Forest` from the `Tree`. 156 | 157 | import Lazy.LList as LL 158 | 159 | singleton "foo" 160 | |> insert (singleton "bar") 161 | |> insert (singleton "baz") 162 | |> descendants 163 | |> LL.map item 164 | |> LL.toList 165 | --> [ "bar", "baz" ] 166 | 167 | singleton "foo" 168 | |> insert (singleton "bar" |> insert (singleton "baz")) 169 | |> descendants 170 | |> LL.map (children) 171 | |> LL.toList 172 | --> [ [ "baz" ] ] 173 | 174 | -} 175 | descendants : Tree a -> Forest a 176 | descendants (Tree _ d) = 177 | d 178 | 179 | 180 | {-| Map function over `Tree`. 181 | 182 | singleton 1 183 | |> map ((+) 1) 184 | |> item 185 | --> 2 186 | 187 | singleton 1 188 | |> insert (singleton 2) 189 | |> insert (singleton 3) 190 | |> map ((*) 2) 191 | |> children 192 | --> [ 4, 6 ] 193 | 194 | -} 195 | map : (a -> b) -> Tree a -> Tree b 196 | map fc (Tree a forest) = 197 | Tree (fc a) <| forestMap fc forest 198 | 199 | 200 | {-| Map function over two `Tree`s 201 | 202 | map2 (+) (singleton 1) (singleton 5) 203 | |> item 204 | --> 6 205 | 206 | import Lazy.LList as LL 207 | 208 | Tree 1 (LL.fromList [ singleton 2, singleton 3, singleton 4 ]) 209 | |> map2 (+) (Tree 5 <| LL.fromList [ singleton 6, singleton 7 ]) 210 | |> children 211 | --> [ 8, 10 ] 212 | 213 | -} 214 | map2 : (a -> b -> c) -> Tree a -> Tree b -> Tree c 215 | map2 fc (Tree a1 f1) (Tree a2 f2) = 216 | Tree (fc a1 a2) <| forestMap2 fc f1 f2 217 | 218 | 219 | {-| Filter `Tree` children by given function. 220 | 221 | This function goes from children of root downwards. 222 | This means that nodes that don't satisfy predicate 223 | are excluded and filter is never performed over their children 224 | even if on those it might pass. 225 | 226 | import Lazy.LList as LL 227 | 228 | Tree 1 (LL.fromList [ singleton 2, singleton 3, singleton 4 ]) 229 | |> filter ((>) 4) 230 | |> children 231 | --> [ 2, 3 ] 232 | 233 | Tree 1 (LL.fromList [ insert (singleton 5) <| singleton 2, insert (singleton 6) <| singleton 3, singleton 4 ]) 234 | |> filter ((<) 2) 235 | |> descendants 236 | |> LL.map children 237 | |> LL.toList 238 | --> [ [ 6 ], [] ] 239 | 240 | -} 241 | filter : (a -> Bool) -> Tree a -> Tree a 242 | filter predicate (Tree treeItem c) = 243 | Tree treeItem <| LL.filterMap (filter_ predicate) c 244 | 245 | 246 | filter_ : (a -> Bool) -> Tree a -> Maybe (Tree a) 247 | filter_ predicate (Tree treeItem c) = 248 | if predicate treeItem then 249 | Just <| Tree treeItem <| LL.filterMap (filter_ predicate) c 250 | 251 | else 252 | Nothing 253 | 254 | 255 | {-| FilterMap on `Tree`. Works similarly to `List.filterMap` with the same properties as [filter](#filter). 256 | In case of `filterMap` even root node has to satisfy predicate otherwise 257 | `Nothing` is returned. 258 | 259 | import Lazy.LList as LL 260 | 261 | Tree 1 (LL.fromList [ singleton 2, singleton 3, singleton 4 ]) 262 | |> filterMap (\a -> if a < 4 then Just (a * 2) else Nothing) 263 | |> Maybe.map children 264 | --> Just [ 4, 6 ] 265 | 266 | Tree 1 (LL.fromList [ singleton 2, singleton 3, singleton 4 ]) 267 | |> filterMap (\a -> if a > 2 then Just (a * 2) else Nothing) 268 | |> Maybe.map children 269 | --> Nothing 270 | 271 | -} 272 | filterMap : (a -> Maybe b) -> Tree a -> Maybe (Tree b) 273 | filterMap predicate (Tree treeItem c) = 274 | predicate treeItem 275 | |> Maybe.map (\i -> Tree i <| LL.filterMap (filterMap predicate) c) 276 | 277 | 278 | {-| Sort `Tree`. 279 | 280 | singleton 10 281 | |> insert (singleton 5) 282 | |> insert (singleton 2) 283 | |> sort 284 | |> children 285 | --> [ 2, 5 ] 286 | 287 | it applies to all levels: 288 | 289 | import Lazy.LList as LL 290 | 291 | singleton 10 292 | |> insert (Tree 20 <| LL.llist (List.reverse << List.map singleton << List.range 1) 5) 293 | |> sort 294 | |> descendants 295 | |> LL.map children 296 | |> LL.toList 297 | --> [ [ 1, 2, 3, 4, 5 ] ] 298 | 299 | -} 300 | sort : Tree comparable -> Tree comparable 301 | sort (Tree a f) = 302 | Tree a <| LL.map sort <| LL.sortBy item f 303 | 304 | 305 | {-| Sort `Tree` by a function. 306 | 307 | singleton { val = 10 } 308 | |> insert (singleton { val = 7 }) 309 | |> insert (singleton { val = 3 }) 310 | |> sortBy .val 311 | |> children 312 | --> [ { val = 3 }, { val = 7 } ] 313 | 314 | it applies to all levels: 315 | 316 | import Lazy.LList as LL 317 | 318 | singleton { a = 10 } 319 | |> insert (Tree { a = 20 } <| LL.llist (List.reverse << List.map (\v -> singleton { a = v }) << List.range 1) 3) 320 | |> sortBy .a 321 | |> descendants 322 | |> LL.map children 323 | |> LL.toList 324 | --> [ [ { a = 1 }, { a = 2 }, { a = 3 } ] ] 325 | 326 | -} 327 | sortBy : (a -> comparable) -> Tree a -> Tree a 328 | sortBy predicate (Tree a f) = 329 | Tree a <| 330 | LL.map (sortBy predicate) <| 331 | LL.sortBy (predicate << item) f 332 | 333 | 334 | {-| Sort `Tree` using custom comparison 335 | 336 | flippedComparison : comparable -> comparable -> Order 337 | flippedComparison a b = 338 | case Basics.compare a b of 339 | LT -> GT 340 | EQ -> EQ 341 | GT -> LT 342 | 343 | singleton 10 344 | |> insert (singleton 2) 345 | |> insert (singleton 5) 346 | |> sortWith flippedComparison 347 | |> children 348 | --> [ 5, 2 ] 349 | 350 | -} 351 | sortWith : (a -> a -> Order) -> Tree a -> Tree a 352 | sortWith compare (Tree a f) = 353 | Tree a <| 354 | LL.map (sortWith compare) <| 355 | LL.sortWith (\fst snd -> compare (item fst) (item snd)) f 356 | 357 | 358 | {-| Stable sort `Tree` using custom comparison. 359 | The original order is guaranteed to be kept if the comparison returns `EQ`. 360 | 361 | compareAge : { r | age : comparable } -> { r | age : comparable } -> Order 362 | compareAge a b = 363 | Basics.compare a.age b.age 364 | 365 | singleton { name = "Eve", age = 55 } 366 | |> insert (singleton { name = "Joe", age = 25 }) 367 | |> insert (singleton { name = "Sue", age = 25 }) 368 | |> insert (singleton { name = "Johann", age = 23 }) 369 | |> stableSortWith compareAge 370 | |> children 371 | --> [ { name = "Johann", age = 23 }, { name = "Joe", age = 25 }, { name = "Sue", age = 25 } ] 372 | 373 | -} 374 | stableSortWith : (a -> a -> Order) -> Tree a -> Tree a 375 | stableSortWith compare (Tree a f) = 376 | Tree a <| 377 | LL.map (stableSortWith compare) <| 378 | LL.stableSortWith (\fst snd -> compare (item fst) (item snd)) f 379 | 380 | 381 | {-| Chain map operations. 382 | 383 | import Lazy.LList as LL 384 | import Tuple 385 | 386 | Tree Tuple.pair (LL.fromList [ singleton Tuple.pair, singleton Tuple.pair, singleton Tuple.pair ]) 387 | |> andMap (Tree 1 <| LL.fromList [ singleton 2, singleton 3, singleton 4 ]) 388 | |> andMap (Tree 5 <| LL.fromList [ singleton 6, singleton 7 ]) 389 | |> children 390 | --> [ (2, 6), (3, 7) ] 391 | 392 | -} 393 | andMap : Tree a -> Tree (a -> b) -> Tree b 394 | andMap = 395 | map2 (|>) 396 | 397 | 398 | {-| Flatten `Tree` of Trees. 399 | 400 | singleton (singleton 1) 401 | |> flatten 402 | |> item 403 | --> 1 404 | 405 | import Lazy.LList as LL 406 | 407 | Tree (Tree "foo" <| LL.fromList [ singleton "bar"]) (LL.fromList [ singleton <| singleton "baz" ]) 408 | |> flatten 409 | |> children 410 | --> [ "baz", "bar" ] 411 | 412 | -} 413 | flatten : Tree (Tree a) -> Tree a 414 | flatten (Tree (Tree treeItem c) treeChildren) = 415 | Tree treeItem <| LL.append (LL.map flatten treeChildren) c 416 | 417 | 418 | {-| Map given function onto a `Tree` and flatten the result. 419 | 420 | import Lazy.LList as LL 421 | 422 | singleton "foo" 423 | |> insert (singleton "bar") 424 | |> insert (singleton "baz") 425 | |> andThen (\a -> Tree a <| LL.fromList [ singleton <| a ++ " fighter" ]) 426 | |> children 427 | --> [ "bar", "baz", "foo fighter" ] 428 | 429 | -} 430 | andThen : (a -> Tree b) -> Tree a -> Tree b 431 | andThen fc = 432 | flatten << map fc 433 | 434 | 435 | {-| Insert one `Tree` as children another. 436 | 437 | singleton 1 438 | |> insert (singleton 2) 439 | |> insert (singleton 3) 440 | |> children 441 | --> [ 2, 3 ] 442 | 443 | singleton 1 444 | |> insert (singleton 2) 445 | |> item 446 | --> 1 447 | 448 | -} 449 | insert : Tree a -> Tree a -> Tree a 450 | insert t (Tree treeItem c) = 451 | Tree treeItem <| LL.append c <| LL.fromList [ t ] 452 | 453 | 454 | 455 | -- Forest 456 | 457 | 458 | {-| Construct `Forest` from a list. 459 | 460 | import Lazy.LList as LL 461 | 462 | [ { id = 1, parent = Nothing } 463 | , { id = 2, parent = Nothing } 464 | , { id = 3, parent = Just 1 } 465 | ] 466 | |> fromList (\p i -> Maybe.map .id p == i.parent) 467 | |> LL.map (.id << item) 468 | |> LL.toList 469 | --> [ 1, 2 ] 470 | 471 | [ { id = 1, parent = Nothing } 472 | , { id = 2, parent = Nothing } 473 | , { id = 3, parent = Just 1 } 474 | , { id = 4, parent = Just 1 } 475 | , { id = 5, parent = Just 2 } 476 | ] 477 | |> fromList (\p i -> Maybe.map .id p == i.parent) 478 | |> LL.andThen descendants 479 | |> LL.map (.id << item) 480 | |> LL.toList 481 | --> [ 3, 4, 5 ] 482 | 483 | -} 484 | fromList : (Maybe a -> a -> Bool) -> List a -> Forest a 485 | fromList isParent = 486 | fromList_ Nothing isParent 487 | 488 | 489 | fromList_ : Maybe a -> (Maybe a -> a -> Bool) -> List a -> Forest a 490 | fromList_ parent isParent list = 491 | LL.llist (List.map (constructTree isParent list) << List.filter (isParent parent)) list 492 | 493 | 494 | {-| Construct `Forest` from a list of items that can be **uniquely identified** by comparable value. 495 | 496 | This function can yield much better performance than more general [`fromList`](#fromList) alternative. 497 | Be aware that this function pushes **more work on initial construction of the Tree** at the benefit of 498 | doing more optimal operations during evaluation of the Tree at later time. 499 | 500 | import Lazy.LList as LL 501 | 502 | [ { id = 1, parent = Nothing } 503 | , { id = 2, parent = Nothing } 504 | , { id = 3, parent = Just 1 } 505 | ] 506 | |> fromKeyedList .id (Maybe.withDefault [] << Maybe.map List.singleton << .parent) 507 | |> LL.map (.id << item) 508 | |> LL.toList 509 | --> [ 1, 2 ] 510 | 511 | [ { id = 1, parent = Nothing } 512 | , { id = 2, parent = Nothing } 513 | , { id = 3, parent = Just 1 } 514 | , { id = 4, parent = Just 1 } 515 | , { id = 5, parent = Just 2 } 516 | ] 517 | |> fromList (\p i -> Maybe.map .id p == i.parent) 518 | |> LL.andThen descendants 519 | |> LL.map (.id << item) 520 | |> LL.toList 521 | --> [ 3, 4, 5 ] 522 | 523 | -} 524 | fromKeyedList : (a -> comparable) -> (a -> List comparable) -> List a -> Forest a 525 | fromKeyedList getNodeId getParentIds list = 526 | let 527 | -- Split out list of roots (nodes with parent Nothing) 528 | -- and Dict which maps parentIds to list of children 529 | -- This means you have to pay `O(n*log(n))` once, when you call this function 530 | -- but then each subsequent level expansions will be cheaper (`O(log(n))` lookup in Dict) 531 | ( roots, parentIdToChildren ) = 532 | List.foldr 533 | (\a ( rts, ptch ) -> 534 | case getParentIds a of 535 | [] -> 536 | -- a is root, add it to list of roots 537 | ( a :: rts 538 | , ptch 539 | ) 540 | 541 | parentIds -> 542 | -- a is non-root - prepend it to the list of children under the id of its parent 543 | ( rts 544 | , List.foldr 545 | (\parentId acc -> 546 | Dict.update parentId 547 | (\mval -> 548 | case mval of 549 | Nothing -> 550 | Just [ a ] 551 | 552 | Just kids -> 553 | Just (a :: kids) 554 | ) 555 | acc 556 | ) 557 | ptch 558 | parentIds 559 | ) 560 | ) 561 | ( [], Dict.empty ) 562 | list 563 | 564 | buildTree : a -> Tree a 565 | buildTree = 566 | build 567 | (\a -> 568 | -- This is the `O(log(n))` work done when expanding level 569 | Dict.get (getNodeId a) parentIdToChildren |> Maybe.withDefault [] 570 | ) 571 | in 572 | LL.map buildTree <| LL.fromList roots 573 | 574 | 575 | {-| Map function over `Forest`. 576 | 577 | import Lazy.LList as LL 578 | 579 | [ 1, 2, 3 ] 580 | |> fromList (\m _ -> m == Nothing) 581 | |> forestMap ((+) 1) 582 | |> LL.map item 583 | |> LL.toList 584 | --> [ 2, 3, 4 ] 585 | 586 | -} 587 | forestMap : (a -> b) -> Forest a -> Forest b 588 | forestMap fc = 589 | LL.map (map fc) 590 | 591 | 592 | {-| Map function over two `Forest`s. 593 | 594 | import Lazy.LList as LL 595 | 596 | [ 1, 2, 3 ] 597 | |> fromList (\m _ -> m == Nothing) 598 | |> forestMap2 (+) (fromList (\m _ -> m == Nothing) [1, 2]) 599 | |> LL.map item 600 | |> LL.toList 601 | --> [ 2, 4 ] 602 | 603 | -} 604 | forestMap2 : (a -> b -> c) -> Forest a -> Forest b -> Forest c 605 | forestMap2 fc = 606 | LL.map2 (map2 fc) 607 | 608 | 609 | {-| Duplicates Tree (Comonad) 610 | -} 611 | duplicate : Tree a -> Tree (Tree a) 612 | duplicate ((Tree _ xs) as t) = 613 | Tree t <| LL.map duplicate xs 614 | 615 | 616 | {-| Extend tree (Comonad) 617 | -} 618 | extend : (Tree a -> b) -> Tree a -> Tree b 619 | extend f = 620 | map f << duplicate 621 | 622 | 623 | 624 | -- Private 625 | 626 | 627 | constructTree : (Maybe a -> a -> Bool) -> List a -> a -> Tree a 628 | constructTree isParent list treeItem = 629 | Tree treeItem <| fromList_ (Just treeItem) isParent list 630 | -------------------------------------------------------------------------------- /src/Lazy/Tree/Force.elm: -------------------------------------------------------------------------------- 1 | module Lazy.Tree.Force exposing (..) 2 | 3 | import Lazy 4 | import Lazy.LList as LL 5 | import Lazy.Tree exposing (Forest, Tree(..)) 6 | 7 | 8 | forceTree : Tree a -> Tree a 9 | forceTree (Tree x xs) = 10 | Tree x (forceForest xs) 11 | 12 | 13 | forceForest : Forest a -> Forest a 14 | forceForest = 15 | Lazy.evaluate << LL.map forceTree 16 | -------------------------------------------------------------------------------- /src/Lazy/Tree/Zipper.elm: -------------------------------------------------------------------------------- 1 | module Lazy.Tree.Zipper exposing 2 | ( Breadcrumb, Zipper(..), fromTree 3 | , current, children, isRoot, isEmpty, attempt, getTree, getPath 4 | , insert, delete, filter, update, updateItem, setTree 5 | , root, up, upwards, toLeft, toRight, open, openAll, openPath, attemptOpenPath 6 | , map, duplicate, extend 7 | , breadcrumbs, indexedBreadcrumbs 8 | ) 9 | 10 | {-| Zipper implementation for `Lazy.Tree`. 11 | 12 | > A zipper is a technique of representing an aggregate data structure so that it is convenient 13 | > for writing programs that traverse the structure arbitrarily and update its contents, 14 | > especially in purely functional programming languages. 15 | 16 | `Zipper` is a secret sauce that gives `Tree` real power. 17 | It provides an easy way to query and modify the `Tree` in a clever and very flexible way. 18 | 19 | Types within this module are exposed type aliases to make it easy to extend the default functionality of `Zipper`. 20 | 21 | 22 | # Types 23 | 24 | @docs Breadcrumb, Zipper, fromTree 25 | 26 | 27 | # Query 28 | 29 | @docs current, children, isRoot, isEmpty, attempt, getTree, getPath 30 | 31 | 32 | # Operations 33 | 34 | @docs insert, delete, filter, update, updateItem, setTree 35 | 36 | 37 | # Navigation 38 | 39 | @docs root, up, upwards, toLeft, toRight, open, openAll, openPath, attemptOpenPath 40 | 41 | 42 | # Transformations 43 | 44 | @docs map, duplicate, extend 45 | 46 | 47 | # Breadcrumbs 48 | 49 | @docs breadcrumbs, indexedBreadcrumbs 50 | 51 | -} 52 | 53 | import Lazy.LList as LL 54 | import Lazy.Tree as Tree exposing (Forest, Tree(..)) 55 | 56 | 57 | {-| \*\* Be careful when comparing `Breadcrumb`s using `(==)`.\*\* 58 | Due to use of laziness `(==)` isn't reliable for comparing Breadcrumbs. 59 | 60 | Breadcrumbs are private type not meant to be manipulated directly. 61 | However it's possible to extract breadcrumbs from `Zipper` in transformed 62 | format using `breadcrumbs` and `indexedBreadcrumbs` functions which are meant for public use. 63 | 64 | -} 65 | type Breadcrumb a 66 | = Breadcrumb 67 | { left : Forest a 68 | , parent : a 69 | , right : Forest a 70 | } 71 | 72 | 73 | {-| -} 74 | type Zipper a 75 | = Zipper (Tree a) (List (Breadcrumb a)) 76 | 77 | 78 | {-| Init `Zipper` for `Tree`. 79 | 80 | import Lazy.Tree as T 81 | 82 | T.singleton "foo" 83 | |> fromTree 84 | |> current 85 | --> "foo" 86 | 87 | -} 88 | fromTree : Tree a -> Zipper a 89 | fromTree tree = 90 | Zipper tree [] 91 | 92 | 93 | {-| Extract current `Tree` from a `Zipper`. 94 | 95 | useful in case where you don't want to use pattern matching 96 | 97 | import Lazy.Tree as T 98 | 99 | T.singleton "foo" 100 | |> fromTree 101 | |> getTree 102 | |> T.item 103 | --> "foo" 104 | 105 | -} 106 | getTree : Zipper a -> Tree a 107 | getTree (Zipper tree _) = 108 | tree 109 | 110 | 111 | {-| Get the root item of the current `Tree`. 112 | 113 | import Lazy.Tree as T 114 | 115 | T.singleton "foo" 116 | |> fromTree 117 | |> insert (T.singleton "bar") 118 | |> current 119 | --> "foo" 120 | 121 | -} 122 | current : Zipper a -> a 123 | current = 124 | Tree.item << getTree 125 | 126 | 127 | {-| Get children of current `Tree`. 128 | 129 | import Lazy.Tree as T 130 | 131 | T.singleton "foo" 132 | |> fromTree 133 | |> insert (T.singleton "bar") 134 | |> children 135 | --> [ "bar" ] 136 | 137 | -} 138 | children : Zipper a -> List a 139 | children = 140 | Tree.children << getTree 141 | 142 | 143 | {-| Check if `Zipper` is focused on the root `Tree`. 144 | 145 | import Lazy.Tree as T 146 | 147 | T.singleton "foo" 148 | |> fromTree 149 | |> isRoot 150 | --> True 151 | 152 | -} 153 | isRoot : Zipper a -> Bool 154 | isRoot (Zipper _ bs) = 155 | List.isEmpty bs 156 | 157 | 158 | {-| Check if current `Tree` in `Zipper` is empty. 159 | 160 | import Lazy.Tree as T 161 | 162 | T.singleton "foo" 163 | |> fromTree 164 | |> isEmpty 165 | --> True 166 | 167 | T.singleton "foo" 168 | |> fromTree 169 | |> insert (T.singleton "bar") 170 | |> isEmpty 171 | --> False 172 | 173 | -} 174 | isEmpty : Zipper a -> Bool 175 | isEmpty (Zipper tree _) = 176 | Tree.isEmpty tree 177 | 178 | 179 | {-| Insert sub `Tree` into current `Tree` in `Zipper`. 180 | 181 | import Lazy.Tree as T 182 | 183 | T.singleton "foo" 184 | |> fromTree 185 | |> insert (T.singleton "bar") 186 | |> insert (T.singleton "baz") 187 | |> children 188 | --> [ "bar", "baz" ] 189 | 190 | -} 191 | insert : Tree a -> Zipper a -> Zipper a 192 | insert tree (Zipper t zipperBreadcrumbs) = 193 | Zipper (Tree.insert tree t) zipperBreadcrumbs 194 | 195 | 196 | {-| Delete current `Tree` from `Zipper`. 197 | 198 | Returns Nothing if root node is removed. 199 | 200 | import Lazy.Tree as T 201 | 202 | T.singleton "foo" 203 | |> fromTree 204 | |> delete 205 | --> Nothing 206 | 207 | T.singleton "foo" 208 | |> fromTree 209 | |> insert (T.singleton "bar") 210 | |> open (always True) 211 | |> Maybe.andThen delete 212 | |> Maybe.map current 213 | --> Just "foo" 214 | 215 | -} 216 | delete : Zipper a -> Maybe (Zipper a) 217 | delete (Zipper _ zipperBreadcrumbs) = 218 | case zipperBreadcrumbs of 219 | [] -> 220 | Nothing 221 | 222 | (Breadcrumb { left, parent, right }) :: tail -> 223 | Just <| Zipper (Tree parent <| LL.append left right) tail 224 | 225 | 226 | {-| Replace current `Tree` with new one. 227 | 228 | import Lazy.Tree as T 229 | 230 | T.singleton "foo" 231 | |> fromTree 232 | |> setTree (T.singleton "bar") 233 | |> current 234 | --> "bar" 235 | 236 | -} 237 | setTree : Tree a -> Zipper a -> Zipper a 238 | setTree tree (Zipper _ zipperBreadcrumbs) = 239 | Zipper tree zipperBreadcrumbs 240 | 241 | 242 | {-| Update current `Tree` using given function. 243 | 244 | import Lazy.Tree as T 245 | 246 | T.singleton "foo" 247 | |> fromTree 248 | |> update (T.map (\a -> a ++ " fighter")) 249 | |> current 250 | --> "foo fighter" 251 | 252 | -} 253 | update : (Tree a -> Tree a) -> Zipper a -> Zipper a 254 | update f (Zipper t bs) = 255 | Zipper (f t) bs 256 | 257 | 258 | {-| Update the root item of the current `Tree` using given function. 259 | 260 | import Lazy.Tree as T 261 | 262 | T.singleton "foo" 263 | |> fromTree 264 | |> updateItem (\i -> i ++ " fighter") 265 | |> current 266 | --> "foo fighter" 267 | 268 | -} 269 | updateItem : (a -> a) -> Zipper a -> Zipper a 270 | updateItem fc (Zipper tree zipperBreadcrumbs) = 271 | Zipper (Tree (fc <| Tree.item tree) <| Tree.descendants tree) zipperBreadcrumbs 272 | 273 | 274 | {-| Map function over `Zipper`. 275 | 276 | import Lazy.Tree as T 277 | 278 | T.singleton 1 279 | |> fromTree 280 | |> map ((+) 1) 281 | |> current 282 | --> 2 283 | 284 | -} 285 | map : (a -> b) -> Zipper a -> Zipper b 286 | map fc (Zipper tree zipperBreadcrumbs) = 287 | Zipper (Tree.map fc tree) <| breadcrumbsMap fc zipperBreadcrumbs 288 | 289 | 290 | {-| Performs filter on current `Tree` in `Zipper`. See `Tree.filter` for more information. 291 | 292 | import Lazy.LList as LL 293 | import Lazy.Tree as T 294 | 295 | T.Tree 1 (LL.fromList [ T.singleton 2, T.singleton 3, T.singleton 4 ]) 296 | |> fromTree 297 | |> filter ((>) 4) 298 | |> children 299 | --> [ 2, 3 ] 300 | 301 | T.Tree 1 (LL.fromList [ T.singleton 2, T.singleton 3, T.singleton 4 ]) 302 | |> fromTree 303 | |> attempt (open ((==) 1)) 304 | |> filter ((<) 2) 305 | |> root 306 | |> children 307 | --> [ 3, 4 ] 308 | 309 | T.Tree 1 (LL.fromList [ T.insert (T.singleton 5) <| T.singleton 2, T.insert (T.singleton 6) <| T.singleton 3, T.singleton 4 ]) 310 | |> fromTree 311 | |> attempt (open ((==) 1)) 312 | |> filter ((<) 2) 313 | |> getTree 314 | |> T.descendants 315 | |> LL.andThen (LL.map T.item << T.descendants) 316 | |> LL.toList 317 | --> [ 6 ] 318 | 319 | -} 320 | filter : (a -> Bool) -> Zipper a -> Zipper a 321 | filter predicate (Zipper t bs) = 322 | Zipper (Tree.filter predicate t) bs 323 | 324 | 325 | {-| Attempt to perform action over zipper and return original `Zipper` in cases where this action returns `Nothing`. 326 | 327 | import Lazy.Tree as T 328 | 329 | T.singleton "foo" 330 | |> fromTree 331 | |> attempt delete 332 | |> current 333 | --> "foo" 334 | 335 | T.singleton "foo" 336 | |> fromTree 337 | |> insert (T.singleton "bar") 338 | |> attempt (open ((==) "foo")) 339 | |> attempt delete 340 | |> current 341 | --> "foo" 342 | 343 | -} 344 | attempt : (Zipper a -> Maybe (Zipper a)) -> Zipper a -> Zipper a 345 | attempt action zipper = 346 | Maybe.withDefault zipper <| action zipper 347 | 348 | 349 | {-| Return back to parent of current `Tree` in given `Zipper`. 350 | 351 | import Lazy.Tree as T 352 | 353 | T.singleton "foo" 354 | |> fromTree 355 | |> insert (T.singleton "bar") 356 | |> open ((==) "bar") 357 | |> Maybe.andThen up 358 | |> Maybe.map current 359 | --> Just "foo" 360 | 361 | T.singleton "baz" 362 | |> fromTree 363 | |> up 364 | --> Nothing 365 | 366 | -} 367 | up : Zipper a -> Maybe (Zipper a) 368 | up (Zipper tree zipperBreadcrumbs) = 369 | case zipperBreadcrumbs of 370 | [] -> 371 | Nothing 372 | 373 | (Breadcrumb { left, parent, right }) :: tail -> 374 | Just <| 375 | Zipper 376 | (Tree parent <| 377 | LL.append (LL.reverse left) <| 378 | LL.cons tree right 379 | ) 380 | tail 381 | 382 | 383 | {-| Move to the immediate left sibling, if it exists. 384 | -} 385 | toLeft : Zipper a -> Maybe (Zipper a) 386 | toLeft (Zipper tree bs) = 387 | case bs of 388 | (Breadcrumb { left, parent, right }) :: bs_ -> 389 | case ( LL.head left, LL.tail left ) of 390 | ( Just l, Just ls ) -> 391 | Just <| Zipper l (Breadcrumb { left = ls, parent = parent, right = LL.cons tree right } :: bs_) 392 | 393 | _ -> 394 | Nothing 395 | 396 | _ -> 397 | Nothing 398 | 399 | 400 | {-| Move to the immediate right sibling, if it exists. 401 | -} 402 | toRight : Zipper a -> Maybe (Zipper a) 403 | toRight (Zipper tree bs) = 404 | case bs of 405 | (Breadcrumb { left, parent, right }) :: bs_ -> 406 | case ( LL.head right, LL.tail right ) of 407 | ( Just r, Just rs ) -> 408 | Just <| Zipper r (Breadcrumb { left = LL.cons tree left, parent = parent, right = rs } :: bs_) 409 | 410 | _ -> 411 | Nothing 412 | 413 | _ -> 414 | Nothing 415 | 416 | 417 | {-| Perform [`up`](#up) n times. 418 | 419 | import Lazy.Tree as T 420 | 421 | T.singleton "foo" 422 | |> fromTree 423 | |> insert (T.singleton "bar" |> T.insert (T.singleton "baz") ) 424 | |> open ((==) "bar") 425 | |> Maybe.andThen (open ((==) "baz")) 426 | |> Maybe.andThen (upwards 2) 427 | |> Maybe.map current 428 | --> Just "foo" 429 | 430 | Returns given `Zipper` return if `0` is passed: 431 | 432 | T.singleton "foo" 433 | |> fromTree 434 | |> upwards 0 435 | |> Maybe.map current 436 | --> Just "foo" 437 | 438 | Return `Nothing` if there are not enough ancestors in `Zipper`: 439 | 440 | T.singleton 4 441 | |> fromTree 442 | |> upwards 1 443 | --> Nothing 444 | 445 | Return `Nothing` if negative integer is passed: 446 | 447 | T.singleton 4 448 | |> fromTree 449 | |> upwards -1 450 | --> Nothing 451 | 452 | -} 453 | upwards : Int -> Zipper a -> Maybe (Zipper a) 454 | upwards n zipper = 455 | if n < 0 then 456 | Nothing 457 | 458 | else if n == 0 then 459 | Just zipper 460 | 461 | else 462 | up zipper 463 | |> Maybe.andThen (upwards (n - 1)) 464 | 465 | 466 | {-| Navigate back to the root `Tree`. 467 | 468 | import Lazy.Tree as T 469 | 470 | T.singleton "foo" 471 | |> fromTree 472 | |> insert (T.singleton "bar" |> T.insert (T.singleton "baz") ) 473 | |> open ((==) "bar") 474 | |> Maybe.andThen (open ((==) "baz")) 475 | |> Maybe.map root 476 | |> Maybe.map current 477 | --> Just "foo" 478 | 479 | T.singleton "foo" 480 | |> fromTree 481 | |> root 482 | |> current 483 | --> "foo" 484 | 485 | -} 486 | root : Zipper a -> Zipper a 487 | root ((Zipper _ zipperBreadcrumbs) as zipper) = 488 | attempt (upwards <| List.length zipperBreadcrumbs) zipper 489 | 490 | 491 | {-| Open the first child that satisfies given condition. 492 | If there is no child satisfying the condition, returns Nothing. 493 | 494 | import Lazy.Tree as T 495 | 496 | T.singleton "foo" 497 | |> fromTree 498 | |> insert (T.singleton "bar") 499 | |> open ((==) "bar") 500 | |> Maybe.map current 501 | --> Just "bar" 502 | 503 | T.singleton "foo" 504 | |> fromTree 505 | |> insert (T.singleton "bar" |> T.insert (T.singleton "baz") ) 506 | |> attempt (open ((==) "bar")) 507 | |> attempt (open ((==) "baz")) 508 | |> current 509 | --> "baz" 510 | 511 | T.singleton "foo" 512 | |> fromTree 513 | |> open (always True) 514 | --> Nothing 515 | 516 | -} 517 | open : (a -> Bool) -> Zipper a -> Maybe (Zipper a) 518 | open predicate (Zipper zipperTree zipperBreadcrumbs) = 519 | let 520 | currentItem = 521 | Tree.item zipperTree 522 | 523 | treeChildren = 524 | Tree.descendants zipperTree 525 | 526 | ( left, item, right ) = 527 | cutForest predicate treeChildren 528 | in 529 | Maybe.map 530 | (\tree -> 531 | Zipper tree <| 532 | Breadcrumb 533 | { left = left 534 | , parent = currentItem 535 | , right = right 536 | } 537 | :: zipperBreadcrumbs 538 | ) 539 | item 540 | 541 | 542 | {-| Use given function to convert current breadcrumb path to a list 543 | 544 | Resulting list of breadcrumbs contains currently focused item as well. 545 | 546 | import Lazy.Tree as T 547 | 548 | T.singleton "foo" 549 | |> fromTree 550 | |> insert (T.singleton "bar") 551 | |> attemptOpenPath (==) [ "bar" ] 552 | |> getPath identity 553 | --> [ "foo", "bar" ] 554 | 555 | T.singleton "foo" 556 | |> fromTree 557 | |> insert (T.singleton "bar" |> T.insert (T.singleton "baz") ) 558 | |> attemptOpenPath (==) [ "bar", "baz" ] 559 | |> getPath identity 560 | --> [ "foo", "bar", "baz" ] 561 | 562 | -} 563 | getPath : (a -> b) -> Zipper a -> List b 564 | getPath fc (Zipper tree zipperBreadcrumbs) = 565 | List.foldl (\(Breadcrumb { parent }) acc -> fc parent :: acc) 566 | [ fc <| Tree.item tree ] 567 | zipperBreadcrumbs 568 | 569 | 570 | {-| Open multiple levels reducing list by given function. 571 | 572 | import Lazy.Tree as T 573 | 574 | T.singleton "foo" 575 | |> fromTree 576 | |> insert (T.singleton "bar" |> T.insert (T.singleton "baz") ) 577 | |> openPath (==) [ "bar", "baz" ] 578 | |> Result.map current 579 | --> Ok "baz" 580 | 581 | T.singleton "foo" 582 | |> fromTree 583 | |> insert (T.singleton "bar") 584 | |> openPath (==) [ "not-here", "baz" ] 585 | |> Result.map current 586 | --> Err "Can't resolve open" 587 | 588 | -} 589 | openPath : (b -> a -> Bool) -> List b -> Zipper a -> Result String (Zipper a) 590 | openPath predicate path zipper = 591 | let 592 | toResult _ = 593 | Result.fromMaybe "Can't resolve open" 594 | in 595 | List.foldl (\i acc -> Result.andThen (toResult i << (open <| predicate i)) acc) (Ok zipper) path 596 | 597 | 598 | {-| Get `List` of `Zipper`s for all children of current `Zipper` 599 | 600 | import Lazy.Tree as T 601 | 602 | T.singleton "foo" 603 | |> fromTree 604 | |> insert (T.singleton "bar") 605 | |> insert (T.singleton "baz") 606 | |> openAll 607 | |> List.map current 608 | --> [ "bar", "baz" ] 609 | 610 | -} 611 | openAll : Zipper a -> List (Zipper a) 612 | openAll (Zipper tree zipperBreadcrumbs) = 613 | sliceForest (Tree.descendants tree) 614 | |> List.map 615 | (\( left, parent, right ) -> 616 | Zipper parent <| 617 | Breadcrumb 618 | { left = left 619 | , parent = Tree.item tree 620 | , right = right 621 | } 622 | :: zipperBreadcrumbs 623 | ) 624 | 625 | 626 | {-| Similar to [`openPath`](#openPath) but ignore failed steps. 627 | 628 | import Lazy.Tree as T 629 | 630 | T.singleton "foo" 631 | |> fromTree 632 | |> insert (T.singleton "bar") 633 | |> attemptOpenPath (==) [ "not-here", "bar" ] 634 | |> current 635 | --> "bar" 636 | 637 | T.singleton "foo" 638 | |> fromTree 639 | |> attemptOpenPath (==) [ "baz" ] 640 | |> current 641 | --> "foo" 642 | 643 | T.singleton "foo" 644 | |> fromTree 645 | |> insert (T.singleton "bar" |> T.insert (T.singleton "baz")) 646 | |> attemptOpenPath (==) [ "not-here", "bar", "missing", "baz" ] 647 | |> current 648 | --> "baz" 649 | 650 | -} 651 | attemptOpenPath : (b -> a -> Bool) -> List b -> Zipper a -> Zipper a 652 | attemptOpenPath predicate path zipper = 653 | List.foldl (attempt << open << predicate) zipper path 654 | 655 | 656 | {-| Get `List` of `Breadcrumb`s . 657 | 658 | import Lazy.Tree as T 659 | 660 | T.singleton "foo" 661 | |> fromTree 662 | |> insert (T.singleton "bar" |> T.insert (T.singleton "baz")) 663 | |> attemptOpenPath (==) [ "bar", "baz" ] 664 | |> breadcrumbs 665 | --> [ "bar", "foo" ] 666 | 667 | -} 668 | breadcrumbs : Zipper a -> List a 669 | breadcrumbs (Zipper _ bs) = 670 | List.map (\(Breadcrumb { parent }) -> parent) bs 671 | 672 | 673 | {-| Get `Breadcrumb`s as indexed `List`. 674 | 675 | import Lazy.Tree as T 676 | 677 | T.singleton "foo" 678 | |> fromTree 679 | |> insert (T.singleton "bar" |> T.insert (T.singleton "baz")) 680 | |> attemptOpenPath (==) [ "bar", "baz" ] 681 | |> indexedBreadcrumbs 682 | --> [ ( 1, "bar" ), ( 2, "foo" )] 683 | 684 | -} 685 | indexedBreadcrumbs : Zipper a -> List ( Int, a ) 686 | indexedBreadcrumbs (Zipper _ bs) = 687 | List.indexedMap (\i (Breadcrumb { parent }) -> ( i + 1, parent )) bs 688 | 689 | 690 | {-| Duplicate Zipper (Comonad). 691 | Converts each node into its full Zipper context. 692 | For example, the [`extend`] function is defined as 693 | 694 | extend : (Zipper a -> b) -> Zipper a -> Zipper b 695 | extend f = 696 | map f << duplicate 697 | 698 | -} 699 | duplicate : Zipper a -> Zipper (Zipper a) 700 | duplicate zipper = 701 | let 702 | genLeft z = 703 | case toLeft z of 704 | Nothing -> 705 | LL.empty 706 | 707 | Just zl -> 708 | LL.cons (Tree.build openAll zl) <| genLeft zl 709 | 710 | genRight z = 711 | case toRight z of 712 | Nothing -> 713 | LL.empty 714 | 715 | Just zr -> 716 | LL.cons (Tree.build openAll zr) <| genRight zr 717 | 718 | genBreadcrumbs z = 719 | case up z of 720 | Nothing -> 721 | [] 722 | 723 | Just zp -> 724 | Breadcrumb { left = genLeft z, parent = zp, right = genRight z } :: genBreadcrumbs zp 725 | in 726 | Zipper (Tree.build openAll zipper) <| genBreadcrumbs zipper 727 | 728 | 729 | {-| Extend Zipper (Comonad). 730 | Sort of like a contextful `map` where instead of mapping only each element, 731 | you have access to the whole Zipper at each element's location. 732 | E.g.: 733 | 734 | extendWithPath : Zipper a -> Zipper ( a, List a ) 735 | extendWithPath = 736 | extend (\z -> ( current z, getPath identity z )) 737 | 738 | -} 739 | extend : (Zipper a -> b) -> Zipper a -> Zipper b 740 | extend f = 741 | map f << duplicate 742 | 743 | 744 | 745 | -- Private 746 | 747 | 748 | breadcrumbsMap : (a -> b) -> List (Breadcrumb a) -> List (Breadcrumb b) 749 | breadcrumbsMap predicate = 750 | List.map 751 | (\(Breadcrumb { left, parent, right }) -> 752 | Breadcrumb 753 | { left = Tree.forestMap predicate left 754 | , parent = predicate parent 755 | , right = Tree.forestMap predicate right 756 | } 757 | ) 758 | 759 | 760 | 761 | -- Tree helpers 762 | 763 | 764 | cutForest : (a -> Bool) -> Forest a -> ( Forest a, Maybe (Tree a), Forest a ) 765 | cutForest = 766 | cutForest_ LL.empty 767 | 768 | 769 | cutForest_ : Forest a -> (a -> Bool) -> Forest a -> ( Forest a, Maybe (Tree a), Forest a ) 770 | cutForest_ acc predicate forest = 771 | case LL.toList forest of 772 | [] -> 773 | ( acc, Nothing, LL.empty ) 774 | 775 | head :: tail -> 776 | if predicate <| Tree.item head then 777 | ( acc, Just head, LL.fromList tail ) 778 | 779 | else 780 | cutForest_ (LL.cons head acc) predicate (LL.fromList tail) 781 | 782 | 783 | sliceForest : Forest a -> List ( Forest a, Tree a, Forest a ) 784 | sliceForest = 785 | sliceForest_ [] LL.empty 786 | 787 | 788 | sliceForest_ : List ( Forest a, Tree a, Forest a ) -> Forest a -> Forest a -> List ( Forest a, Tree a, Forest a ) 789 | sliceForest_ acc left right = 790 | case LL.toList right of 791 | [] -> 792 | List.reverse acc 793 | 794 | head :: tail -> 795 | let 796 | newItem = 797 | ( left, head, LL.fromList tail ) 798 | in 799 | sliceForest_ (newItem :: acc) (LL.cons head left) (LL.fromList tail) 800 | -------------------------------------------------------------------------------- /tests/elm-verify-examples.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "../src", 3 | "tests": ["Lazy.Tree", "Lazy.LList", "Lazy.Tree.Zipper"] 4 | } 5 | --------------------------------------------------------------------------------