├── .gitignore ├── .lein-failures ├── README.md ├── package-lock.json ├── package.json ├── pom.xml.asc ├── project.clj ├── src └── forms │ ├── core.cljs │ ├── dirty.cljs │ ├── re_frame.cljs │ ├── util.cljs │ └── validator.cljs └── test └── forms └── test ├── common.cljs ├── core.cljs ├── dirty.cljs ├── re_frame.cljs ├── test.cljs └── validator.cljs /.gitignore: -------------------------------------------------------------------------------- 1 | /resources/public/js/compiled/** 2 | figwheel_server.log 3 | pom.xml 4 | *jar 5 | /lib/ 6 | /classes/ 7 | /out/ 8 | /target/ 9 | .lein-deps-sum 10 | .lein-repl-history 11 | .lein-plugins/ 12 | .repl 13 | .nrepl-port 14 | /node_modules/ 15 | *.iml -------------------------------------------------------------------------------- /.lein-failures: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keechma Forms 2 | 3 | [![Clojars Project](https://img.shields.io/clojars/v/keechma/forms.svg)](https://clojars.org/keechma/forms) 4 | 5 | - [Read Announcement](http://keechma.com/news/announcing-keechma-forms/) 6 | - [API Docs](http://keechma.com/api/forms/) 7 | - [Demo](http://retroaktive.me/keechma-forms/), [Annotated Source](http://keechma.com/annotated/form-example/) 8 | 9 | Keechma Forms library allows you to build forms (using the Reagent library) that have a great UX. It implements the following features: 10 | 11 | - Validation system that can validate nested data of arbitrary depth 12 | - Dirty fields tracking - so you can show only the relevant errors to the user 13 | 14 | Keechma Forms library is agnostic to your UI code, so you can use it with any Reagent UI library (like [Reagent Forms](https://github.com/reagent-project/reagent-forms)). 15 | 16 | **Keechma Forms is not coupled with the rest of the Keechma ecosystem and can be used in any Reagent based application.** 17 | 18 | 19 | ## License 20 | 21 | Copyright © 2016 Mihael Konjevic 22 | 23 | Distributed under the MIT License. 24 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forms", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.3", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", 10 | "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", 11 | "requires": { 12 | "mime-types": "~2.1.11", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "after": { 17 | "version": "0.8.2", 18 | "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", 19 | "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" 20 | }, 21 | "anymatch": { 22 | "version": "1.3.2", 23 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", 24 | "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", 25 | "requires": { 26 | "micromatch": "^2.1.5", 27 | "normalize-path": "^2.0.0" 28 | } 29 | }, 30 | "arr-diff": { 31 | "version": "2.0.0", 32 | "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", 33 | "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", 34 | "requires": { 35 | "arr-flatten": "^1.0.1" 36 | } 37 | }, 38 | "arr-flatten": { 39 | "version": "1.1.0", 40 | "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", 41 | "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" 42 | }, 43 | "arr-union": { 44 | "version": "3.1.0", 45 | "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", 46 | "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" 47 | }, 48 | "array-slice": { 49 | "version": "0.2.3", 50 | "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", 51 | "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=" 52 | }, 53 | "array-unique": { 54 | "version": "0.2.1", 55 | "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", 56 | "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" 57 | }, 58 | "arraybuffer.slice": { 59 | "version": "0.0.6", 60 | "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", 61 | "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=" 62 | }, 63 | "assign-symbols": { 64 | "version": "1.0.0", 65 | "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", 66 | "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" 67 | }, 68 | "async-each": { 69 | "version": "1.0.1", 70 | "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", 71 | "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" 72 | }, 73 | "atob": { 74 | "version": "2.1.2", 75 | "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", 76 | "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" 77 | }, 78 | "backo2": { 79 | "version": "1.0.2", 80 | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", 81 | "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" 82 | }, 83 | "balanced-match": { 84 | "version": "1.0.0", 85 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 86 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 87 | }, 88 | "base": { 89 | "version": "0.11.2", 90 | "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", 91 | "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", 92 | "requires": { 93 | "cache-base": "^1.0.1", 94 | "class-utils": "^0.3.5", 95 | "component-emitter": "^1.2.1", 96 | "define-property": "^1.0.0", 97 | "isobject": "^3.0.1", 98 | "mixin-deep": "^1.2.0", 99 | "pascalcase": "^0.1.1" 100 | }, 101 | "dependencies": { 102 | "define-property": { 103 | "version": "1.0.0", 104 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 105 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 106 | "requires": { 107 | "is-descriptor": "^1.0.0" 108 | } 109 | }, 110 | "is-accessor-descriptor": { 111 | "version": "1.0.0", 112 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 113 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 114 | "requires": { 115 | "kind-of": "^6.0.0" 116 | } 117 | }, 118 | "is-data-descriptor": { 119 | "version": "1.0.0", 120 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 121 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 122 | "requires": { 123 | "kind-of": "^6.0.0" 124 | } 125 | }, 126 | "is-descriptor": { 127 | "version": "1.0.2", 128 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 129 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 130 | "requires": { 131 | "is-accessor-descriptor": "^1.0.0", 132 | "is-data-descriptor": "^1.0.0", 133 | "kind-of": "^6.0.2" 134 | } 135 | }, 136 | "isobject": { 137 | "version": "3.0.1", 138 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 139 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 140 | }, 141 | "kind-of": { 142 | "version": "6.0.2", 143 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", 144 | "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" 145 | } 146 | } 147 | }, 148 | "base64-arraybuffer": { 149 | "version": "0.1.5", 150 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", 151 | "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" 152 | }, 153 | "base64id": { 154 | "version": "1.0.0", 155 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", 156 | "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" 157 | }, 158 | "batch": { 159 | "version": "0.5.3", 160 | "resolved": "https://registry.npmjs.org/batch/-/batch-0.5.3.tgz", 161 | "integrity": "sha1-PzQU84AyF0O/wQQvmoP/HVgk1GQ=" 162 | }, 163 | "better-assert": { 164 | "version": "1.0.2", 165 | "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", 166 | "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", 167 | "requires": { 168 | "callsite": "1.0.0" 169 | } 170 | }, 171 | "binary-extensions": { 172 | "version": "1.13.0", 173 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", 174 | "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==" 175 | }, 176 | "blob": { 177 | "version": "0.0.4", 178 | "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", 179 | "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=" 180 | }, 181 | "bluebird": { 182 | "version": "2.11.0", 183 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", 184 | "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=" 185 | }, 186 | "body-parser": { 187 | "version": "1.18.3", 188 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 189 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 190 | "requires": { 191 | "bytes": "3.0.0", 192 | "content-type": "~1.0.4", 193 | "debug": "2.6.9", 194 | "depd": "~1.1.2", 195 | "http-errors": "~1.6.3", 196 | "iconv-lite": "0.4.23", 197 | "on-finished": "~2.3.0", 198 | "qs": "6.5.2", 199 | "raw-body": "2.3.3", 200 | "type-is": "~1.6.16" 201 | } 202 | }, 203 | "brace-expansion": { 204 | "version": "1.1.11", 205 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 206 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 207 | "requires": { 208 | "balanced-match": "^1.0.0", 209 | "concat-map": "0.0.1" 210 | } 211 | }, 212 | "braces": { 213 | "version": "1.8.5", 214 | "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", 215 | "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", 216 | "requires": { 217 | "expand-range": "^1.8.1", 218 | "preserve": "^0.2.0", 219 | "repeat-element": "^1.1.2" 220 | } 221 | }, 222 | "buffer-alloc": { 223 | "version": "1.2.0", 224 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 225 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 226 | "requires": { 227 | "buffer-alloc-unsafe": "^1.1.0", 228 | "buffer-fill": "^1.0.0" 229 | } 230 | }, 231 | "buffer-alloc-unsafe": { 232 | "version": "1.1.0", 233 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 234 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 235 | }, 236 | "buffer-fill": { 237 | "version": "1.0.0", 238 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 239 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" 240 | }, 241 | "bytes": { 242 | "version": "3.0.0", 243 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 244 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 245 | }, 246 | "cache-base": { 247 | "version": "1.0.1", 248 | "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", 249 | "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", 250 | "requires": { 251 | "collection-visit": "^1.0.0", 252 | "component-emitter": "^1.2.1", 253 | "get-value": "^2.0.6", 254 | "has-value": "^1.0.0", 255 | "isobject": "^3.0.1", 256 | "set-value": "^2.0.0", 257 | "to-object-path": "^0.3.0", 258 | "union-value": "^1.0.0", 259 | "unset-value": "^1.0.0" 260 | }, 261 | "dependencies": { 262 | "isobject": { 263 | "version": "3.0.1", 264 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 265 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 266 | } 267 | } 268 | }, 269 | "callsite": { 270 | "version": "1.0.0", 271 | "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", 272 | "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" 273 | }, 274 | "chokidar": { 275 | "version": "1.7.0", 276 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", 277 | "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", 278 | "requires": { 279 | "anymatch": "^1.3.0", 280 | "async-each": "^1.0.0", 281 | "fsevents": "^1.0.0", 282 | "glob-parent": "^2.0.0", 283 | "inherits": "^2.0.1", 284 | "is-binary-path": "^1.0.0", 285 | "is-glob": "^2.0.0", 286 | "path-is-absolute": "^1.0.0", 287 | "readdirp": "^2.0.0" 288 | } 289 | }, 290 | "class-utils": { 291 | "version": "0.3.6", 292 | "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", 293 | "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", 294 | "requires": { 295 | "arr-union": "^3.1.0", 296 | "define-property": "^0.2.5", 297 | "isobject": "^3.0.0", 298 | "static-extend": "^0.1.1" 299 | }, 300 | "dependencies": { 301 | "define-property": { 302 | "version": "0.2.5", 303 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 304 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 305 | "requires": { 306 | "is-descriptor": "^0.1.0" 307 | } 308 | }, 309 | "isobject": { 310 | "version": "3.0.1", 311 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 312 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 313 | } 314 | } 315 | }, 316 | "collection-visit": { 317 | "version": "1.0.0", 318 | "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", 319 | "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", 320 | "requires": { 321 | "map-visit": "^1.0.0", 322 | "object-visit": "^1.0.0" 323 | } 324 | }, 325 | "colors": { 326 | "version": "1.3.3", 327 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", 328 | "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==" 329 | }, 330 | "component-bind": { 331 | "version": "1.0.0", 332 | "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", 333 | "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" 334 | }, 335 | "component-emitter": { 336 | "version": "1.2.1", 337 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", 338 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" 339 | }, 340 | "component-inherit": { 341 | "version": "0.0.3", 342 | "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", 343 | "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" 344 | }, 345 | "concat-map": { 346 | "version": "0.0.1", 347 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 348 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 349 | }, 350 | "connect": { 351 | "version": "3.6.6", 352 | "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", 353 | "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", 354 | "requires": { 355 | "debug": "2.6.9", 356 | "finalhandler": "1.1.0", 357 | "parseurl": "~1.3.2", 358 | "utils-merge": "1.0.1" 359 | } 360 | }, 361 | "content-type": { 362 | "version": "1.0.4", 363 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 364 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 365 | }, 366 | "cookie": { 367 | "version": "0.3.1", 368 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 369 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 370 | }, 371 | "copy-descriptor": { 372 | "version": "0.1.1", 373 | "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", 374 | "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" 375 | }, 376 | "core-js": { 377 | "version": "2.6.5", 378 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", 379 | "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" 380 | }, 381 | "core-util-is": { 382 | "version": "1.0.2", 383 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 384 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 385 | }, 386 | "custom-event": { 387 | "version": "1.0.1", 388 | "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", 389 | "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=" 390 | }, 391 | "debug": { 392 | "version": "2.6.9", 393 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 394 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 395 | "requires": { 396 | "ms": "2.0.0" 397 | } 398 | }, 399 | "decode-uri-component": { 400 | "version": "0.2.0", 401 | "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", 402 | "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" 403 | }, 404 | "define-property": { 405 | "version": "2.0.2", 406 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", 407 | "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", 408 | "requires": { 409 | "is-descriptor": "^1.0.2", 410 | "isobject": "^3.0.1" 411 | }, 412 | "dependencies": { 413 | "is-accessor-descriptor": { 414 | "version": "1.0.0", 415 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 416 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 417 | "requires": { 418 | "kind-of": "^6.0.0" 419 | } 420 | }, 421 | "is-data-descriptor": { 422 | "version": "1.0.0", 423 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 424 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 425 | "requires": { 426 | "kind-of": "^6.0.0" 427 | } 428 | }, 429 | "is-descriptor": { 430 | "version": "1.0.2", 431 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 432 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 433 | "requires": { 434 | "is-accessor-descriptor": "^1.0.0", 435 | "is-data-descriptor": "^1.0.0", 436 | "kind-of": "^6.0.2" 437 | } 438 | }, 439 | "isobject": { 440 | "version": "3.0.1", 441 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 442 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 443 | }, 444 | "kind-of": { 445 | "version": "6.0.2", 446 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", 447 | "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" 448 | } 449 | } 450 | }, 451 | "depd": { 452 | "version": "1.1.2", 453 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 454 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 455 | }, 456 | "di": { 457 | "version": "0.0.1", 458 | "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", 459 | "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=" 460 | }, 461 | "dom-serialize": { 462 | "version": "2.2.1", 463 | "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", 464 | "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", 465 | "requires": { 466 | "custom-event": "~1.0.0", 467 | "ent": "~2.2.0", 468 | "extend": "^3.0.0", 469 | "void-elements": "^2.0.0" 470 | } 471 | }, 472 | "ee-first": { 473 | "version": "1.1.1", 474 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 475 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 476 | }, 477 | "encodeurl": { 478 | "version": "1.0.2", 479 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 480 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 481 | }, 482 | "engine.io": { 483 | "version": "1.8.5", 484 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.5.tgz", 485 | "integrity": "sha512-j1DWIcktw4hRwrv6nWx++5nFH2X64x16MAG2P0Lmi5Dvdfi3I+Jhc7JKJIdAmDJa+5aZ/imHV7dWRPy2Cqjh3A==", 486 | "requires": { 487 | "accepts": "1.3.3", 488 | "base64id": "1.0.0", 489 | "cookie": "0.3.1", 490 | "debug": "2.3.3", 491 | "engine.io-parser": "1.3.2", 492 | "ws": "~1.1.5" 493 | }, 494 | "dependencies": { 495 | "debug": { 496 | "version": "2.3.3", 497 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", 498 | "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", 499 | "requires": { 500 | "ms": "0.7.2" 501 | } 502 | }, 503 | "ms": { 504 | "version": "0.7.2", 505 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", 506 | "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" 507 | } 508 | } 509 | }, 510 | "engine.io-client": { 511 | "version": "1.8.5", 512 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.5.tgz", 513 | "integrity": "sha512-AYTgHyeVUPitsseqjoedjhYJapNVoSPShbZ+tEUX9/73jgZ/Z3sUlJf9oYgdEBBdVhupUpUqSxH0kBCXlQnmZg==", 514 | "requires": { 515 | "component-emitter": "1.2.1", 516 | "component-inherit": "0.0.3", 517 | "debug": "2.3.3", 518 | "engine.io-parser": "1.3.2", 519 | "has-cors": "1.1.0", 520 | "indexof": "0.0.1", 521 | "parsejson": "0.0.3", 522 | "parseqs": "0.0.5", 523 | "parseuri": "0.0.5", 524 | "ws": "~1.1.5", 525 | "xmlhttprequest-ssl": "1.5.3", 526 | "yeast": "0.1.2" 527 | }, 528 | "dependencies": { 529 | "debug": { 530 | "version": "2.3.3", 531 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", 532 | "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", 533 | "requires": { 534 | "ms": "0.7.2" 535 | } 536 | }, 537 | "ms": { 538 | "version": "0.7.2", 539 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", 540 | "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" 541 | } 542 | } 543 | }, 544 | "engine.io-parser": { 545 | "version": "1.3.2", 546 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", 547 | "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", 548 | "requires": { 549 | "after": "0.8.2", 550 | "arraybuffer.slice": "0.0.6", 551 | "base64-arraybuffer": "0.1.5", 552 | "blob": "0.0.4", 553 | "has-binary": "0.1.7", 554 | "wtf-8": "1.0.0" 555 | } 556 | }, 557 | "ent": { 558 | "version": "2.2.0", 559 | "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", 560 | "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=" 561 | }, 562 | "escape-html": { 563 | "version": "1.0.3", 564 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 565 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 566 | }, 567 | "eventemitter3": { 568 | "version": "3.1.0", 569 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", 570 | "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==" 571 | }, 572 | "expand-braces": { 573 | "version": "0.1.2", 574 | "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", 575 | "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", 576 | "requires": { 577 | "array-slice": "^0.2.3", 578 | "array-unique": "^0.2.1", 579 | "braces": "^0.1.2" 580 | }, 581 | "dependencies": { 582 | "braces": { 583 | "version": "0.1.5", 584 | "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", 585 | "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", 586 | "requires": { 587 | "expand-range": "^0.1.0" 588 | } 589 | }, 590 | "expand-range": { 591 | "version": "0.1.1", 592 | "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", 593 | "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", 594 | "requires": { 595 | "is-number": "^0.1.1", 596 | "repeat-string": "^0.2.2" 597 | } 598 | }, 599 | "is-number": { 600 | "version": "0.1.1", 601 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", 602 | "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=" 603 | }, 604 | "repeat-string": { 605 | "version": "0.2.2", 606 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", 607 | "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=" 608 | } 609 | } 610 | }, 611 | "expand-brackets": { 612 | "version": "0.1.5", 613 | "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", 614 | "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", 615 | "requires": { 616 | "is-posix-bracket": "^0.1.0" 617 | } 618 | }, 619 | "expand-range": { 620 | "version": "1.8.2", 621 | "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", 622 | "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", 623 | "requires": { 624 | "fill-range": "^2.1.0" 625 | } 626 | }, 627 | "extend": { 628 | "version": "3.0.2", 629 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 630 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 631 | }, 632 | "extend-shallow": { 633 | "version": "3.0.2", 634 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", 635 | "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", 636 | "requires": { 637 | "assign-symbols": "^1.0.0", 638 | "is-extendable": "^1.0.1" 639 | }, 640 | "dependencies": { 641 | "is-extendable": { 642 | "version": "1.0.1", 643 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", 644 | "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", 645 | "requires": { 646 | "is-plain-object": "^2.0.4" 647 | } 648 | } 649 | } 650 | }, 651 | "extglob": { 652 | "version": "0.3.2", 653 | "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", 654 | "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", 655 | "requires": { 656 | "is-extglob": "^1.0.0" 657 | } 658 | }, 659 | "filename-regex": { 660 | "version": "2.0.1", 661 | "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", 662 | "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" 663 | }, 664 | "fill-range": { 665 | "version": "2.2.4", 666 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", 667 | "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", 668 | "requires": { 669 | "is-number": "^2.1.0", 670 | "isobject": "^2.0.0", 671 | "randomatic": "^3.0.0", 672 | "repeat-element": "^1.1.2", 673 | "repeat-string": "^1.5.2" 674 | } 675 | }, 676 | "finalhandler": { 677 | "version": "1.1.0", 678 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", 679 | "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", 680 | "requires": { 681 | "debug": "2.6.9", 682 | "encodeurl": "~1.0.1", 683 | "escape-html": "~1.0.3", 684 | "on-finished": "~2.3.0", 685 | "parseurl": "~1.3.2", 686 | "statuses": "~1.3.1", 687 | "unpipe": "~1.0.0" 688 | }, 689 | "dependencies": { 690 | "statuses": { 691 | "version": "1.3.1", 692 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 693 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" 694 | } 695 | } 696 | }, 697 | "follow-redirects": { 698 | "version": "1.7.0", 699 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", 700 | "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", 701 | "requires": { 702 | "debug": "^3.2.6" 703 | }, 704 | "dependencies": { 705 | "debug": { 706 | "version": "3.2.6", 707 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 708 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 709 | "requires": { 710 | "ms": "^2.1.1" 711 | } 712 | }, 713 | "ms": { 714 | "version": "2.1.1", 715 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 716 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 717 | } 718 | } 719 | }, 720 | "for-in": { 721 | "version": "1.0.2", 722 | "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", 723 | "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" 724 | }, 725 | "for-own": { 726 | "version": "0.1.5", 727 | "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", 728 | "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", 729 | "requires": { 730 | "for-in": "^1.0.1" 731 | } 732 | }, 733 | "fragment-cache": { 734 | "version": "0.2.1", 735 | "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", 736 | "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", 737 | "requires": { 738 | "map-cache": "^0.2.2" 739 | } 740 | }, 741 | "fs-access": { 742 | "version": "1.0.1", 743 | "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", 744 | "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", 745 | "requires": { 746 | "null-check": "^1.0.0" 747 | } 748 | }, 749 | "fs.realpath": { 750 | "version": "1.0.0", 751 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 752 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 753 | }, 754 | "fsevents": { 755 | "version": "1.2.7", 756 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", 757 | "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", 758 | "optional": true, 759 | "requires": { 760 | "nan": "^2.9.2", 761 | "node-pre-gyp": "^0.10.0" 762 | }, 763 | "dependencies": { 764 | "abbrev": { 765 | "version": "1.1.1", 766 | "bundled": true, 767 | "optional": true 768 | }, 769 | "ansi-regex": { 770 | "version": "2.1.1", 771 | "bundled": true, 772 | "optional": true 773 | }, 774 | "aproba": { 775 | "version": "1.2.0", 776 | "bundled": true, 777 | "optional": true 778 | }, 779 | "are-we-there-yet": { 780 | "version": "1.1.5", 781 | "bundled": true, 782 | "optional": true, 783 | "requires": { 784 | "delegates": "^1.0.0", 785 | "readable-stream": "^2.0.6" 786 | } 787 | }, 788 | "balanced-match": { 789 | "version": "1.0.0", 790 | "bundled": true, 791 | "optional": true 792 | }, 793 | "brace-expansion": { 794 | "version": "1.1.11", 795 | "bundled": true, 796 | "optional": true, 797 | "requires": { 798 | "balanced-match": "^1.0.0", 799 | "concat-map": "0.0.1" 800 | } 801 | }, 802 | "chownr": { 803 | "version": "1.1.1", 804 | "bundled": true, 805 | "optional": true 806 | }, 807 | "code-point-at": { 808 | "version": "1.1.0", 809 | "bundled": true, 810 | "optional": true 811 | }, 812 | "concat-map": { 813 | "version": "0.0.1", 814 | "bundled": true, 815 | "optional": true 816 | }, 817 | "console-control-strings": { 818 | "version": "1.1.0", 819 | "bundled": true, 820 | "optional": true 821 | }, 822 | "core-util-is": { 823 | "version": "1.0.2", 824 | "bundled": true, 825 | "optional": true 826 | }, 827 | "debug": { 828 | "version": "2.6.9", 829 | "bundled": true, 830 | "optional": true, 831 | "requires": { 832 | "ms": "2.0.0" 833 | } 834 | }, 835 | "deep-extend": { 836 | "version": "0.6.0", 837 | "bundled": true, 838 | "optional": true 839 | }, 840 | "delegates": { 841 | "version": "1.0.0", 842 | "bundled": true, 843 | "optional": true 844 | }, 845 | "detect-libc": { 846 | "version": "1.0.3", 847 | "bundled": true, 848 | "optional": true 849 | }, 850 | "fs-minipass": { 851 | "version": "1.2.5", 852 | "bundled": true, 853 | "optional": true, 854 | "requires": { 855 | "minipass": "^2.2.1" 856 | } 857 | }, 858 | "fs.realpath": { 859 | "version": "1.0.0", 860 | "bundled": true, 861 | "optional": true 862 | }, 863 | "gauge": { 864 | "version": "2.7.4", 865 | "bundled": true, 866 | "optional": true, 867 | "requires": { 868 | "aproba": "^1.0.3", 869 | "console-control-strings": "^1.0.0", 870 | "has-unicode": "^2.0.0", 871 | "object-assign": "^4.1.0", 872 | "signal-exit": "^3.0.0", 873 | "string-width": "^1.0.1", 874 | "strip-ansi": "^3.0.1", 875 | "wide-align": "^1.1.0" 876 | } 877 | }, 878 | "glob": { 879 | "version": "7.1.3", 880 | "bundled": true, 881 | "optional": true, 882 | "requires": { 883 | "fs.realpath": "^1.0.0", 884 | "inflight": "^1.0.4", 885 | "inherits": "2", 886 | "minimatch": "^3.0.4", 887 | "once": "^1.3.0", 888 | "path-is-absolute": "^1.0.0" 889 | } 890 | }, 891 | "has-unicode": { 892 | "version": "2.0.1", 893 | "bundled": true, 894 | "optional": true 895 | }, 896 | "iconv-lite": { 897 | "version": "0.4.24", 898 | "bundled": true, 899 | "optional": true, 900 | "requires": { 901 | "safer-buffer": ">= 2.1.2 < 3" 902 | } 903 | }, 904 | "ignore-walk": { 905 | "version": "3.0.1", 906 | "bundled": true, 907 | "optional": true, 908 | "requires": { 909 | "minimatch": "^3.0.4" 910 | } 911 | }, 912 | "inflight": { 913 | "version": "1.0.6", 914 | "bundled": true, 915 | "optional": true, 916 | "requires": { 917 | "once": "^1.3.0", 918 | "wrappy": "1" 919 | } 920 | }, 921 | "inherits": { 922 | "version": "2.0.3", 923 | "bundled": true, 924 | "optional": true 925 | }, 926 | "ini": { 927 | "version": "1.3.5", 928 | "bundled": true, 929 | "optional": true 930 | }, 931 | "is-fullwidth-code-point": { 932 | "version": "1.0.0", 933 | "bundled": true, 934 | "optional": true, 935 | "requires": { 936 | "number-is-nan": "^1.0.0" 937 | } 938 | }, 939 | "isarray": { 940 | "version": "1.0.0", 941 | "bundled": true, 942 | "optional": true 943 | }, 944 | "minimatch": { 945 | "version": "3.0.4", 946 | "bundled": true, 947 | "optional": true, 948 | "requires": { 949 | "brace-expansion": "^1.1.7" 950 | } 951 | }, 952 | "minimist": { 953 | "version": "0.0.8", 954 | "bundled": true, 955 | "optional": true 956 | }, 957 | "minipass": { 958 | "version": "2.3.5", 959 | "bundled": true, 960 | "optional": true, 961 | "requires": { 962 | "safe-buffer": "^5.1.2", 963 | "yallist": "^3.0.0" 964 | } 965 | }, 966 | "minizlib": { 967 | "version": "1.2.1", 968 | "bundled": true, 969 | "optional": true, 970 | "requires": { 971 | "minipass": "^2.2.1" 972 | } 973 | }, 974 | "mkdirp": { 975 | "version": "0.5.1", 976 | "bundled": true, 977 | "optional": true, 978 | "requires": { 979 | "minimist": "0.0.8" 980 | } 981 | }, 982 | "ms": { 983 | "version": "2.0.0", 984 | "bundled": true, 985 | "optional": true 986 | }, 987 | "needle": { 988 | "version": "2.2.4", 989 | "bundled": true, 990 | "optional": true, 991 | "requires": { 992 | "debug": "^2.1.2", 993 | "iconv-lite": "^0.4.4", 994 | "sax": "^1.2.4" 995 | } 996 | }, 997 | "node-pre-gyp": { 998 | "version": "0.10.3", 999 | "bundled": true, 1000 | "optional": true, 1001 | "requires": { 1002 | "detect-libc": "^1.0.2", 1003 | "mkdirp": "^0.5.1", 1004 | "needle": "^2.2.1", 1005 | "nopt": "^4.0.1", 1006 | "npm-packlist": "^1.1.6", 1007 | "npmlog": "^4.0.2", 1008 | "rc": "^1.2.7", 1009 | "rimraf": "^2.6.1", 1010 | "semver": "^5.3.0", 1011 | "tar": "^4" 1012 | } 1013 | }, 1014 | "nopt": { 1015 | "version": "4.0.1", 1016 | "bundled": true, 1017 | "optional": true, 1018 | "requires": { 1019 | "abbrev": "1", 1020 | "osenv": "^0.1.4" 1021 | } 1022 | }, 1023 | "npm-bundled": { 1024 | "version": "1.0.5", 1025 | "bundled": true, 1026 | "optional": true 1027 | }, 1028 | "npm-packlist": { 1029 | "version": "1.2.0", 1030 | "bundled": true, 1031 | "optional": true, 1032 | "requires": { 1033 | "ignore-walk": "^3.0.1", 1034 | "npm-bundled": "^1.0.1" 1035 | } 1036 | }, 1037 | "npmlog": { 1038 | "version": "4.1.2", 1039 | "bundled": true, 1040 | "optional": true, 1041 | "requires": { 1042 | "are-we-there-yet": "~1.1.2", 1043 | "console-control-strings": "~1.1.0", 1044 | "gauge": "~2.7.3", 1045 | "set-blocking": "~2.0.0" 1046 | } 1047 | }, 1048 | "number-is-nan": { 1049 | "version": "1.0.1", 1050 | "bundled": true, 1051 | "optional": true 1052 | }, 1053 | "object-assign": { 1054 | "version": "4.1.1", 1055 | "bundled": true, 1056 | "optional": true 1057 | }, 1058 | "once": { 1059 | "version": "1.4.0", 1060 | "bundled": true, 1061 | "optional": true, 1062 | "requires": { 1063 | "wrappy": "1" 1064 | } 1065 | }, 1066 | "os-homedir": { 1067 | "version": "1.0.2", 1068 | "bundled": true, 1069 | "optional": true 1070 | }, 1071 | "os-tmpdir": { 1072 | "version": "1.0.2", 1073 | "bundled": true, 1074 | "optional": true 1075 | }, 1076 | "osenv": { 1077 | "version": "0.1.5", 1078 | "bundled": true, 1079 | "optional": true, 1080 | "requires": { 1081 | "os-homedir": "^1.0.0", 1082 | "os-tmpdir": "^1.0.0" 1083 | } 1084 | }, 1085 | "path-is-absolute": { 1086 | "version": "1.0.1", 1087 | "bundled": true, 1088 | "optional": true 1089 | }, 1090 | "process-nextick-args": { 1091 | "version": "2.0.0", 1092 | "bundled": true, 1093 | "optional": true 1094 | }, 1095 | "rc": { 1096 | "version": "1.2.8", 1097 | "bundled": true, 1098 | "optional": true, 1099 | "requires": { 1100 | "deep-extend": "^0.6.0", 1101 | "ini": "~1.3.0", 1102 | "minimist": "^1.2.0", 1103 | "strip-json-comments": "~2.0.1" 1104 | }, 1105 | "dependencies": { 1106 | "minimist": { 1107 | "version": "1.2.0", 1108 | "bundled": true, 1109 | "optional": true 1110 | } 1111 | } 1112 | }, 1113 | "readable-stream": { 1114 | "version": "2.3.6", 1115 | "bundled": true, 1116 | "optional": true, 1117 | "requires": { 1118 | "core-util-is": "~1.0.0", 1119 | "inherits": "~2.0.3", 1120 | "isarray": "~1.0.0", 1121 | "process-nextick-args": "~2.0.0", 1122 | "safe-buffer": "~5.1.1", 1123 | "string_decoder": "~1.1.1", 1124 | "util-deprecate": "~1.0.1" 1125 | } 1126 | }, 1127 | "rimraf": { 1128 | "version": "2.6.3", 1129 | "bundled": true, 1130 | "optional": true, 1131 | "requires": { 1132 | "glob": "^7.1.3" 1133 | } 1134 | }, 1135 | "safe-buffer": { 1136 | "version": "5.1.2", 1137 | "bundled": true, 1138 | "optional": true 1139 | }, 1140 | "safer-buffer": { 1141 | "version": "2.1.2", 1142 | "bundled": true, 1143 | "optional": true 1144 | }, 1145 | "sax": { 1146 | "version": "1.2.4", 1147 | "bundled": true, 1148 | "optional": true 1149 | }, 1150 | "semver": { 1151 | "version": "5.6.0", 1152 | "bundled": true, 1153 | "optional": true 1154 | }, 1155 | "set-blocking": { 1156 | "version": "2.0.0", 1157 | "bundled": true, 1158 | "optional": true 1159 | }, 1160 | "signal-exit": { 1161 | "version": "3.0.2", 1162 | "bundled": true, 1163 | "optional": true 1164 | }, 1165 | "string-width": { 1166 | "version": "1.0.2", 1167 | "bundled": true, 1168 | "optional": true, 1169 | "requires": { 1170 | "code-point-at": "^1.0.0", 1171 | "is-fullwidth-code-point": "^1.0.0", 1172 | "strip-ansi": "^3.0.0" 1173 | } 1174 | }, 1175 | "string_decoder": { 1176 | "version": "1.1.1", 1177 | "bundled": true, 1178 | "optional": true, 1179 | "requires": { 1180 | "safe-buffer": "~5.1.0" 1181 | } 1182 | }, 1183 | "strip-ansi": { 1184 | "version": "3.0.1", 1185 | "bundled": true, 1186 | "optional": true, 1187 | "requires": { 1188 | "ansi-regex": "^2.0.0" 1189 | } 1190 | }, 1191 | "strip-json-comments": { 1192 | "version": "2.0.1", 1193 | "bundled": true, 1194 | "optional": true 1195 | }, 1196 | "tar": { 1197 | "version": "4.4.8", 1198 | "bundled": true, 1199 | "optional": true, 1200 | "requires": { 1201 | "chownr": "^1.1.1", 1202 | "fs-minipass": "^1.2.5", 1203 | "minipass": "^2.3.4", 1204 | "minizlib": "^1.1.1", 1205 | "mkdirp": "^0.5.0", 1206 | "safe-buffer": "^5.1.2", 1207 | "yallist": "^3.0.2" 1208 | } 1209 | }, 1210 | "util-deprecate": { 1211 | "version": "1.0.2", 1212 | "bundled": true, 1213 | "optional": true 1214 | }, 1215 | "wide-align": { 1216 | "version": "1.1.3", 1217 | "bundled": true, 1218 | "optional": true, 1219 | "requires": { 1220 | "string-width": "^1.0.2 || 2" 1221 | } 1222 | }, 1223 | "wrappy": { 1224 | "version": "1.0.2", 1225 | "bundled": true, 1226 | "optional": true 1227 | }, 1228 | "yallist": { 1229 | "version": "3.0.3", 1230 | "bundled": true, 1231 | "optional": true 1232 | } 1233 | } 1234 | }, 1235 | "get-value": { 1236 | "version": "2.0.6", 1237 | "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", 1238 | "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" 1239 | }, 1240 | "glob": { 1241 | "version": "7.1.3", 1242 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 1243 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 1244 | "requires": { 1245 | "fs.realpath": "^1.0.0", 1246 | "inflight": "^1.0.4", 1247 | "inherits": "2", 1248 | "minimatch": "^3.0.4", 1249 | "once": "^1.3.0", 1250 | "path-is-absolute": "^1.0.0" 1251 | } 1252 | }, 1253 | "glob-base": { 1254 | "version": "0.3.0", 1255 | "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", 1256 | "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", 1257 | "requires": { 1258 | "glob-parent": "^2.0.0", 1259 | "is-glob": "^2.0.0" 1260 | } 1261 | }, 1262 | "glob-parent": { 1263 | "version": "2.0.0", 1264 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", 1265 | "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", 1266 | "requires": { 1267 | "is-glob": "^2.0.0" 1268 | } 1269 | }, 1270 | "graceful-fs": { 1271 | "version": "4.1.15", 1272 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", 1273 | "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" 1274 | }, 1275 | "has-binary": { 1276 | "version": "0.1.7", 1277 | "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", 1278 | "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", 1279 | "requires": { 1280 | "isarray": "0.0.1" 1281 | }, 1282 | "dependencies": { 1283 | "isarray": { 1284 | "version": "0.0.1", 1285 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1286 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 1287 | } 1288 | } 1289 | }, 1290 | "has-cors": { 1291 | "version": "1.1.0", 1292 | "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", 1293 | "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" 1294 | }, 1295 | "has-value": { 1296 | "version": "1.0.0", 1297 | "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", 1298 | "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", 1299 | "requires": { 1300 | "get-value": "^2.0.6", 1301 | "has-values": "^1.0.0", 1302 | "isobject": "^3.0.0" 1303 | }, 1304 | "dependencies": { 1305 | "isobject": { 1306 | "version": "3.0.1", 1307 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 1308 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 1309 | } 1310 | } 1311 | }, 1312 | "has-values": { 1313 | "version": "1.0.0", 1314 | "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", 1315 | "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", 1316 | "requires": { 1317 | "is-number": "^3.0.0", 1318 | "kind-of": "^4.0.0" 1319 | }, 1320 | "dependencies": { 1321 | "is-number": { 1322 | "version": "3.0.0", 1323 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", 1324 | "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", 1325 | "requires": { 1326 | "kind-of": "^3.0.2" 1327 | }, 1328 | "dependencies": { 1329 | "kind-of": { 1330 | "version": "3.2.2", 1331 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1332 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1333 | "requires": { 1334 | "is-buffer": "^1.1.5" 1335 | } 1336 | } 1337 | } 1338 | }, 1339 | "kind-of": { 1340 | "version": "4.0.0", 1341 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", 1342 | "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", 1343 | "requires": { 1344 | "is-buffer": "^1.1.5" 1345 | } 1346 | } 1347 | } 1348 | }, 1349 | "http-errors": { 1350 | "version": "1.6.3", 1351 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 1352 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 1353 | "requires": { 1354 | "depd": "~1.1.2", 1355 | "inherits": "2.0.3", 1356 | "setprototypeof": "1.1.0", 1357 | "statuses": ">= 1.4.0 < 2" 1358 | } 1359 | }, 1360 | "http-proxy": { 1361 | "version": "1.17.0", 1362 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", 1363 | "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", 1364 | "requires": { 1365 | "eventemitter3": "^3.0.0", 1366 | "follow-redirects": "^1.0.0", 1367 | "requires-port": "^1.0.0" 1368 | } 1369 | }, 1370 | "iconv-lite": { 1371 | "version": "0.4.23", 1372 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 1373 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 1374 | "requires": { 1375 | "safer-buffer": ">= 2.1.2 < 3" 1376 | } 1377 | }, 1378 | "indexof": { 1379 | "version": "0.0.1", 1380 | "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", 1381 | "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" 1382 | }, 1383 | "inflight": { 1384 | "version": "1.0.6", 1385 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1386 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1387 | "requires": { 1388 | "once": "^1.3.0", 1389 | "wrappy": "1" 1390 | } 1391 | }, 1392 | "inherits": { 1393 | "version": "2.0.3", 1394 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1395 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 1396 | }, 1397 | "is-accessor-descriptor": { 1398 | "version": "0.1.6", 1399 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", 1400 | "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", 1401 | "requires": { 1402 | "kind-of": "^3.0.2" 1403 | } 1404 | }, 1405 | "is-binary-path": { 1406 | "version": "1.0.1", 1407 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", 1408 | "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", 1409 | "requires": { 1410 | "binary-extensions": "^1.0.0" 1411 | } 1412 | }, 1413 | "is-buffer": { 1414 | "version": "1.1.6", 1415 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 1416 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 1417 | }, 1418 | "is-data-descriptor": { 1419 | "version": "0.1.4", 1420 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", 1421 | "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", 1422 | "requires": { 1423 | "kind-of": "^3.0.2" 1424 | } 1425 | }, 1426 | "is-descriptor": { 1427 | "version": "0.1.6", 1428 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", 1429 | "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", 1430 | "requires": { 1431 | "is-accessor-descriptor": "^0.1.6", 1432 | "is-data-descriptor": "^0.1.4", 1433 | "kind-of": "^5.0.0" 1434 | }, 1435 | "dependencies": { 1436 | "kind-of": { 1437 | "version": "5.1.0", 1438 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", 1439 | "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" 1440 | } 1441 | } 1442 | }, 1443 | "is-dotfile": { 1444 | "version": "1.0.3", 1445 | "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", 1446 | "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" 1447 | }, 1448 | "is-equal-shallow": { 1449 | "version": "0.1.3", 1450 | "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", 1451 | "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", 1452 | "requires": { 1453 | "is-primitive": "^2.0.0" 1454 | } 1455 | }, 1456 | "is-extendable": { 1457 | "version": "0.1.1", 1458 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", 1459 | "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" 1460 | }, 1461 | "is-extglob": { 1462 | "version": "1.0.0", 1463 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", 1464 | "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" 1465 | }, 1466 | "is-glob": { 1467 | "version": "2.0.1", 1468 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", 1469 | "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", 1470 | "requires": { 1471 | "is-extglob": "^1.0.0" 1472 | } 1473 | }, 1474 | "is-number": { 1475 | "version": "2.1.0", 1476 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", 1477 | "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", 1478 | "requires": { 1479 | "kind-of": "^3.0.2" 1480 | } 1481 | }, 1482 | "is-plain-object": { 1483 | "version": "2.0.4", 1484 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", 1485 | "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", 1486 | "requires": { 1487 | "isobject": "^3.0.1" 1488 | }, 1489 | "dependencies": { 1490 | "isobject": { 1491 | "version": "3.0.1", 1492 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 1493 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 1494 | } 1495 | } 1496 | }, 1497 | "is-posix-bracket": { 1498 | "version": "0.1.1", 1499 | "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", 1500 | "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" 1501 | }, 1502 | "is-primitive": { 1503 | "version": "2.0.0", 1504 | "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", 1505 | "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" 1506 | }, 1507 | "is-windows": { 1508 | "version": "1.0.2", 1509 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 1510 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" 1511 | }, 1512 | "isarray": { 1513 | "version": "1.0.0", 1514 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1515 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 1516 | }, 1517 | "isbinaryfile": { 1518 | "version": "3.0.3", 1519 | "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", 1520 | "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", 1521 | "requires": { 1522 | "buffer-alloc": "^1.2.0" 1523 | } 1524 | }, 1525 | "isexe": { 1526 | "version": "2.0.0", 1527 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1528 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 1529 | }, 1530 | "isobject": { 1531 | "version": "2.1.0", 1532 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", 1533 | "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", 1534 | "requires": { 1535 | "isarray": "1.0.0" 1536 | } 1537 | }, 1538 | "json3": { 1539 | "version": "3.3.2", 1540 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", 1541 | "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" 1542 | }, 1543 | "karma": { 1544 | "version": "0.13.22", 1545 | "resolved": "https://registry.npmjs.org/karma/-/karma-0.13.22.tgz", 1546 | "integrity": "sha1-B3ULG9Bj1+fnuRvNLmNU2PKqh0Q=", 1547 | "requires": { 1548 | "batch": "^0.5.3", 1549 | "bluebird": "^2.9.27", 1550 | "body-parser": "^1.12.4", 1551 | "chokidar": "^1.4.1", 1552 | "colors": "^1.1.0", 1553 | "connect": "^3.3.5", 1554 | "core-js": "^2.1.0", 1555 | "di": "^0.0.1", 1556 | "dom-serialize": "^2.2.0", 1557 | "expand-braces": "^0.1.1", 1558 | "glob": "^7.0.0", 1559 | "graceful-fs": "^4.1.2", 1560 | "http-proxy": "^1.13.0", 1561 | "isbinaryfile": "^3.0.0", 1562 | "lodash": "^3.8.0", 1563 | "log4js": "^0.6.31", 1564 | "mime": "^1.3.4", 1565 | "minimatch": "^3.0.0", 1566 | "optimist": "^0.6.1", 1567 | "rimraf": "^2.3.3", 1568 | "socket.io": "^1.4.5", 1569 | "source-map": "^0.5.3", 1570 | "useragent": "^2.1.6" 1571 | } 1572 | }, 1573 | "karma-chrome-launcher": { 1574 | "version": "0.2.3", 1575 | "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-0.2.3.tgz", 1576 | "integrity": "sha1-TG1wDRY6nTTGGO/YeRi+SeekqMk=", 1577 | "requires": { 1578 | "fs-access": "^1.0.0", 1579 | "which": "^1.2.1" 1580 | } 1581 | }, 1582 | "karma-cljs-test": { 1583 | "version": "0.1.0", 1584 | "resolved": "https://registry.npmjs.org/karma-cljs-test/-/karma-cljs-test-0.1.0.tgz", 1585 | "integrity": "sha1-y4YF7w4R+ab20o9Wul298m84mSM=" 1586 | }, 1587 | "kind-of": { 1588 | "version": "3.2.2", 1589 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 1590 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 1591 | "requires": { 1592 | "is-buffer": "^1.1.5" 1593 | } 1594 | }, 1595 | "lodash": { 1596 | "version": "3.10.1", 1597 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", 1598 | "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=" 1599 | }, 1600 | "log4js": { 1601 | "version": "0.6.38", 1602 | "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", 1603 | "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", 1604 | "requires": { 1605 | "readable-stream": "~1.0.2", 1606 | "semver": "~4.3.3" 1607 | }, 1608 | "dependencies": { 1609 | "isarray": { 1610 | "version": "0.0.1", 1611 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 1612 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 1613 | }, 1614 | "readable-stream": { 1615 | "version": "1.0.34", 1616 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 1617 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 1618 | "requires": { 1619 | "core-util-is": "~1.0.0", 1620 | "inherits": "~2.0.1", 1621 | "isarray": "0.0.1", 1622 | "string_decoder": "~0.10.x" 1623 | } 1624 | }, 1625 | "string_decoder": { 1626 | "version": "0.10.31", 1627 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1628 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 1629 | } 1630 | } 1631 | }, 1632 | "lru-cache": { 1633 | "version": "4.1.5", 1634 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", 1635 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", 1636 | "requires": { 1637 | "pseudomap": "^1.0.2", 1638 | "yallist": "^2.1.2" 1639 | } 1640 | }, 1641 | "map-cache": { 1642 | "version": "0.2.2", 1643 | "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", 1644 | "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" 1645 | }, 1646 | "map-visit": { 1647 | "version": "1.0.0", 1648 | "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", 1649 | "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", 1650 | "requires": { 1651 | "object-visit": "^1.0.0" 1652 | } 1653 | }, 1654 | "math-random": { 1655 | "version": "1.0.4", 1656 | "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", 1657 | "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==" 1658 | }, 1659 | "media-typer": { 1660 | "version": "0.3.0", 1661 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1662 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 1663 | }, 1664 | "micromatch": { 1665 | "version": "2.3.11", 1666 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", 1667 | "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", 1668 | "requires": { 1669 | "arr-diff": "^2.0.0", 1670 | "array-unique": "^0.2.1", 1671 | "braces": "^1.8.2", 1672 | "expand-brackets": "^0.1.4", 1673 | "extglob": "^0.3.1", 1674 | "filename-regex": "^2.0.0", 1675 | "is-extglob": "^1.0.0", 1676 | "is-glob": "^2.0.1", 1677 | "kind-of": "^3.0.2", 1678 | "normalize-path": "^2.0.1", 1679 | "object.omit": "^2.0.0", 1680 | "parse-glob": "^3.0.4", 1681 | "regex-cache": "^0.4.2" 1682 | } 1683 | }, 1684 | "mime": { 1685 | "version": "1.6.0", 1686 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1687 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 1688 | }, 1689 | "mime-db": { 1690 | "version": "1.38.0", 1691 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", 1692 | "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" 1693 | }, 1694 | "mime-types": { 1695 | "version": "2.1.22", 1696 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", 1697 | "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", 1698 | "requires": { 1699 | "mime-db": "~1.38.0" 1700 | } 1701 | }, 1702 | "minimatch": { 1703 | "version": "3.0.4", 1704 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1705 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1706 | "requires": { 1707 | "brace-expansion": "^1.1.7" 1708 | } 1709 | }, 1710 | "minimist": { 1711 | "version": "0.0.10", 1712 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", 1713 | "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" 1714 | }, 1715 | "mixin-deep": { 1716 | "version": "1.3.2", 1717 | "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", 1718 | "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", 1719 | "requires": { 1720 | "for-in": "^1.0.2", 1721 | "is-extendable": "^1.0.1" 1722 | }, 1723 | "dependencies": { 1724 | "is-extendable": { 1725 | "version": "1.0.1", 1726 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", 1727 | "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", 1728 | "requires": { 1729 | "is-plain-object": "^2.0.4" 1730 | } 1731 | } 1732 | } 1733 | }, 1734 | "ms": { 1735 | "version": "2.0.0", 1736 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1737 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1738 | }, 1739 | "nan": { 1740 | "version": "2.12.1", 1741 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", 1742 | "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", 1743 | "optional": true 1744 | }, 1745 | "nanomatch": { 1746 | "version": "1.2.13", 1747 | "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", 1748 | "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", 1749 | "requires": { 1750 | "arr-diff": "^4.0.0", 1751 | "array-unique": "^0.3.2", 1752 | "define-property": "^2.0.2", 1753 | "extend-shallow": "^3.0.2", 1754 | "fragment-cache": "^0.2.1", 1755 | "is-windows": "^1.0.2", 1756 | "kind-of": "^6.0.2", 1757 | "object.pick": "^1.3.0", 1758 | "regex-not": "^1.0.0", 1759 | "snapdragon": "^0.8.1", 1760 | "to-regex": "^3.0.1" 1761 | }, 1762 | "dependencies": { 1763 | "arr-diff": { 1764 | "version": "4.0.0", 1765 | "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", 1766 | "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" 1767 | }, 1768 | "array-unique": { 1769 | "version": "0.3.2", 1770 | "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", 1771 | "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" 1772 | }, 1773 | "kind-of": { 1774 | "version": "6.0.2", 1775 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", 1776 | "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" 1777 | } 1778 | } 1779 | }, 1780 | "negotiator": { 1781 | "version": "0.6.1", 1782 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 1783 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 1784 | }, 1785 | "normalize-path": { 1786 | "version": "2.1.1", 1787 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", 1788 | "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", 1789 | "requires": { 1790 | "remove-trailing-separator": "^1.0.1" 1791 | } 1792 | }, 1793 | "null-check": { 1794 | "version": "1.0.0", 1795 | "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", 1796 | "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=" 1797 | }, 1798 | "object-assign": { 1799 | "version": "4.1.0", 1800 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", 1801 | "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=" 1802 | }, 1803 | "object-component": { 1804 | "version": "0.0.3", 1805 | "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", 1806 | "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" 1807 | }, 1808 | "object-copy": { 1809 | "version": "0.1.0", 1810 | "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", 1811 | "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", 1812 | "requires": { 1813 | "copy-descriptor": "^0.1.0", 1814 | "define-property": "^0.2.5", 1815 | "kind-of": "^3.0.3" 1816 | }, 1817 | "dependencies": { 1818 | "define-property": { 1819 | "version": "0.2.5", 1820 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 1821 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 1822 | "requires": { 1823 | "is-descriptor": "^0.1.0" 1824 | } 1825 | } 1826 | } 1827 | }, 1828 | "object-visit": { 1829 | "version": "1.0.1", 1830 | "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", 1831 | "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", 1832 | "requires": { 1833 | "isobject": "^3.0.0" 1834 | }, 1835 | "dependencies": { 1836 | "isobject": { 1837 | "version": "3.0.1", 1838 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 1839 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 1840 | } 1841 | } 1842 | }, 1843 | "object.omit": { 1844 | "version": "2.0.1", 1845 | "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", 1846 | "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", 1847 | "requires": { 1848 | "for-own": "^0.1.4", 1849 | "is-extendable": "^0.1.1" 1850 | } 1851 | }, 1852 | "object.pick": { 1853 | "version": "1.3.0", 1854 | "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", 1855 | "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", 1856 | "requires": { 1857 | "isobject": "^3.0.1" 1858 | }, 1859 | "dependencies": { 1860 | "isobject": { 1861 | "version": "3.0.1", 1862 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 1863 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 1864 | } 1865 | } 1866 | }, 1867 | "on-finished": { 1868 | "version": "2.3.0", 1869 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1870 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1871 | "requires": { 1872 | "ee-first": "1.1.1" 1873 | } 1874 | }, 1875 | "once": { 1876 | "version": "1.4.0", 1877 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1878 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1879 | "requires": { 1880 | "wrappy": "1" 1881 | } 1882 | }, 1883 | "optimist": { 1884 | "version": "0.6.1", 1885 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 1886 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 1887 | "requires": { 1888 | "minimist": "~0.0.1", 1889 | "wordwrap": "~0.0.2" 1890 | } 1891 | }, 1892 | "options": { 1893 | "version": "0.0.6", 1894 | "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", 1895 | "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=" 1896 | }, 1897 | "os-tmpdir": { 1898 | "version": "1.0.2", 1899 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1900 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 1901 | }, 1902 | "parse-glob": { 1903 | "version": "3.0.4", 1904 | "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", 1905 | "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", 1906 | "requires": { 1907 | "glob-base": "^0.3.0", 1908 | "is-dotfile": "^1.0.0", 1909 | "is-extglob": "^1.0.0", 1910 | "is-glob": "^2.0.0" 1911 | } 1912 | }, 1913 | "parsejson": { 1914 | "version": "0.0.3", 1915 | "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", 1916 | "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", 1917 | "requires": { 1918 | "better-assert": "~1.0.0" 1919 | } 1920 | }, 1921 | "parseqs": { 1922 | "version": "0.0.5", 1923 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", 1924 | "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", 1925 | "requires": { 1926 | "better-assert": "~1.0.0" 1927 | } 1928 | }, 1929 | "parseuri": { 1930 | "version": "0.0.5", 1931 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", 1932 | "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", 1933 | "requires": { 1934 | "better-assert": "~1.0.0" 1935 | } 1936 | }, 1937 | "parseurl": { 1938 | "version": "1.3.2", 1939 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 1940 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 1941 | }, 1942 | "pascalcase": { 1943 | "version": "0.1.1", 1944 | "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", 1945 | "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" 1946 | }, 1947 | "path-is-absolute": { 1948 | "version": "1.0.1", 1949 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1950 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1951 | }, 1952 | "posix-character-classes": { 1953 | "version": "0.1.1", 1954 | "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", 1955 | "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" 1956 | }, 1957 | "preserve": { 1958 | "version": "0.2.0", 1959 | "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", 1960 | "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" 1961 | }, 1962 | "process-nextick-args": { 1963 | "version": "2.0.0", 1964 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1965 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 1966 | }, 1967 | "pseudomap": { 1968 | "version": "1.0.2", 1969 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 1970 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 1971 | }, 1972 | "qs": { 1973 | "version": "6.5.2", 1974 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1975 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1976 | }, 1977 | "randomatic": { 1978 | "version": "3.1.1", 1979 | "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", 1980 | "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", 1981 | "requires": { 1982 | "is-number": "^4.0.0", 1983 | "kind-of": "^6.0.0", 1984 | "math-random": "^1.0.1" 1985 | }, 1986 | "dependencies": { 1987 | "is-number": { 1988 | "version": "4.0.0", 1989 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", 1990 | "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" 1991 | }, 1992 | "kind-of": { 1993 | "version": "6.0.2", 1994 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", 1995 | "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" 1996 | } 1997 | } 1998 | }, 1999 | "raw-body": { 2000 | "version": "2.3.3", 2001 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 2002 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 2003 | "requires": { 2004 | "bytes": "3.0.0", 2005 | "http-errors": "1.6.3", 2006 | "iconv-lite": "0.4.23", 2007 | "unpipe": "1.0.0" 2008 | } 2009 | }, 2010 | "readable-stream": { 2011 | "version": "2.3.6", 2012 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 2013 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 2014 | "requires": { 2015 | "core-util-is": "~1.0.0", 2016 | "inherits": "~2.0.3", 2017 | "isarray": "~1.0.0", 2018 | "process-nextick-args": "~2.0.0", 2019 | "safe-buffer": "~5.1.1", 2020 | "string_decoder": "~1.1.1", 2021 | "util-deprecate": "~1.0.1" 2022 | } 2023 | }, 2024 | "readdirp": { 2025 | "version": "2.2.1", 2026 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", 2027 | "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", 2028 | "requires": { 2029 | "graceful-fs": "^4.1.11", 2030 | "micromatch": "^3.1.10", 2031 | "readable-stream": "^2.0.2" 2032 | }, 2033 | "dependencies": { 2034 | "arr-diff": { 2035 | "version": "4.0.0", 2036 | "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", 2037 | "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" 2038 | }, 2039 | "array-unique": { 2040 | "version": "0.3.2", 2041 | "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", 2042 | "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" 2043 | }, 2044 | "braces": { 2045 | "version": "2.3.2", 2046 | "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", 2047 | "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", 2048 | "requires": { 2049 | "arr-flatten": "^1.1.0", 2050 | "array-unique": "^0.3.2", 2051 | "extend-shallow": "^2.0.1", 2052 | "fill-range": "^4.0.0", 2053 | "isobject": "^3.0.1", 2054 | "repeat-element": "^1.1.2", 2055 | "snapdragon": "^0.8.1", 2056 | "snapdragon-node": "^2.0.1", 2057 | "split-string": "^3.0.2", 2058 | "to-regex": "^3.0.1" 2059 | }, 2060 | "dependencies": { 2061 | "extend-shallow": { 2062 | "version": "2.0.1", 2063 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 2064 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2065 | "requires": { 2066 | "is-extendable": "^0.1.0" 2067 | } 2068 | } 2069 | } 2070 | }, 2071 | "expand-brackets": { 2072 | "version": "2.1.4", 2073 | "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", 2074 | "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", 2075 | "requires": { 2076 | "debug": "^2.3.3", 2077 | "define-property": "^0.2.5", 2078 | "extend-shallow": "^2.0.1", 2079 | "posix-character-classes": "^0.1.0", 2080 | "regex-not": "^1.0.0", 2081 | "snapdragon": "^0.8.1", 2082 | "to-regex": "^3.0.1" 2083 | }, 2084 | "dependencies": { 2085 | "define-property": { 2086 | "version": "0.2.5", 2087 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 2088 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 2089 | "requires": { 2090 | "is-descriptor": "^0.1.0" 2091 | } 2092 | }, 2093 | "extend-shallow": { 2094 | "version": "2.0.1", 2095 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 2096 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2097 | "requires": { 2098 | "is-extendable": "^0.1.0" 2099 | } 2100 | }, 2101 | "is-accessor-descriptor": { 2102 | "version": "0.1.6", 2103 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", 2104 | "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", 2105 | "requires": { 2106 | "kind-of": "^3.0.2" 2107 | }, 2108 | "dependencies": { 2109 | "kind-of": { 2110 | "version": "3.2.2", 2111 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 2112 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2113 | "requires": { 2114 | "is-buffer": "^1.1.5" 2115 | } 2116 | } 2117 | } 2118 | }, 2119 | "is-data-descriptor": { 2120 | "version": "0.1.4", 2121 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", 2122 | "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", 2123 | "requires": { 2124 | "kind-of": "^3.0.2" 2125 | }, 2126 | "dependencies": { 2127 | "kind-of": { 2128 | "version": "3.2.2", 2129 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 2130 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2131 | "requires": { 2132 | "is-buffer": "^1.1.5" 2133 | } 2134 | } 2135 | } 2136 | }, 2137 | "is-descriptor": { 2138 | "version": "0.1.6", 2139 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", 2140 | "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", 2141 | "requires": { 2142 | "is-accessor-descriptor": "^0.1.6", 2143 | "is-data-descriptor": "^0.1.4", 2144 | "kind-of": "^5.0.0" 2145 | } 2146 | }, 2147 | "kind-of": { 2148 | "version": "5.1.0", 2149 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", 2150 | "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" 2151 | } 2152 | } 2153 | }, 2154 | "extglob": { 2155 | "version": "2.0.4", 2156 | "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", 2157 | "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", 2158 | "requires": { 2159 | "array-unique": "^0.3.2", 2160 | "define-property": "^1.0.0", 2161 | "expand-brackets": "^2.1.4", 2162 | "extend-shallow": "^2.0.1", 2163 | "fragment-cache": "^0.2.1", 2164 | "regex-not": "^1.0.0", 2165 | "snapdragon": "^0.8.1", 2166 | "to-regex": "^3.0.1" 2167 | }, 2168 | "dependencies": { 2169 | "define-property": { 2170 | "version": "1.0.0", 2171 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 2172 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 2173 | "requires": { 2174 | "is-descriptor": "^1.0.0" 2175 | } 2176 | }, 2177 | "extend-shallow": { 2178 | "version": "2.0.1", 2179 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 2180 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2181 | "requires": { 2182 | "is-extendable": "^0.1.0" 2183 | } 2184 | } 2185 | } 2186 | }, 2187 | "fill-range": { 2188 | "version": "4.0.0", 2189 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", 2190 | "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", 2191 | "requires": { 2192 | "extend-shallow": "^2.0.1", 2193 | "is-number": "^3.0.0", 2194 | "repeat-string": "^1.6.1", 2195 | "to-regex-range": "^2.1.0" 2196 | }, 2197 | "dependencies": { 2198 | "extend-shallow": { 2199 | "version": "2.0.1", 2200 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 2201 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2202 | "requires": { 2203 | "is-extendable": "^0.1.0" 2204 | } 2205 | } 2206 | } 2207 | }, 2208 | "is-accessor-descriptor": { 2209 | "version": "1.0.0", 2210 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 2211 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 2212 | "requires": { 2213 | "kind-of": "^6.0.0" 2214 | } 2215 | }, 2216 | "is-data-descriptor": { 2217 | "version": "1.0.0", 2218 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 2219 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 2220 | "requires": { 2221 | "kind-of": "^6.0.0" 2222 | } 2223 | }, 2224 | "is-descriptor": { 2225 | "version": "1.0.2", 2226 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 2227 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 2228 | "requires": { 2229 | "is-accessor-descriptor": "^1.0.0", 2230 | "is-data-descriptor": "^1.0.0", 2231 | "kind-of": "^6.0.2" 2232 | } 2233 | }, 2234 | "is-number": { 2235 | "version": "3.0.0", 2236 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", 2237 | "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", 2238 | "requires": { 2239 | "kind-of": "^3.0.2" 2240 | }, 2241 | "dependencies": { 2242 | "kind-of": { 2243 | "version": "3.2.2", 2244 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 2245 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2246 | "requires": { 2247 | "is-buffer": "^1.1.5" 2248 | } 2249 | } 2250 | } 2251 | }, 2252 | "isobject": { 2253 | "version": "3.0.1", 2254 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 2255 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 2256 | }, 2257 | "kind-of": { 2258 | "version": "6.0.2", 2259 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", 2260 | "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" 2261 | }, 2262 | "micromatch": { 2263 | "version": "3.1.10", 2264 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", 2265 | "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", 2266 | "requires": { 2267 | "arr-diff": "^4.0.0", 2268 | "array-unique": "^0.3.2", 2269 | "braces": "^2.3.1", 2270 | "define-property": "^2.0.2", 2271 | "extend-shallow": "^3.0.2", 2272 | "extglob": "^2.0.4", 2273 | "fragment-cache": "^0.2.1", 2274 | "kind-of": "^6.0.2", 2275 | "nanomatch": "^1.2.9", 2276 | "object.pick": "^1.3.0", 2277 | "regex-not": "^1.0.0", 2278 | "snapdragon": "^0.8.1", 2279 | "to-regex": "^3.0.2" 2280 | } 2281 | } 2282 | } 2283 | }, 2284 | "regex-cache": { 2285 | "version": "0.4.4", 2286 | "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", 2287 | "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", 2288 | "requires": { 2289 | "is-equal-shallow": "^0.1.3" 2290 | } 2291 | }, 2292 | "regex-not": { 2293 | "version": "1.0.2", 2294 | "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", 2295 | "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", 2296 | "requires": { 2297 | "extend-shallow": "^3.0.2", 2298 | "safe-regex": "^1.1.0" 2299 | } 2300 | }, 2301 | "remove-trailing-separator": { 2302 | "version": "1.1.0", 2303 | "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", 2304 | "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" 2305 | }, 2306 | "repeat-element": { 2307 | "version": "1.1.3", 2308 | "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", 2309 | "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" 2310 | }, 2311 | "repeat-string": { 2312 | "version": "1.6.1", 2313 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 2314 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" 2315 | }, 2316 | "requires-port": { 2317 | "version": "1.0.0", 2318 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 2319 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 2320 | }, 2321 | "resolve-url": { 2322 | "version": "0.2.1", 2323 | "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", 2324 | "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" 2325 | }, 2326 | "ret": { 2327 | "version": "0.1.15", 2328 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", 2329 | "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" 2330 | }, 2331 | "rimraf": { 2332 | "version": "2.6.3", 2333 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 2334 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 2335 | "requires": { 2336 | "glob": "^7.1.3" 2337 | } 2338 | }, 2339 | "safe-buffer": { 2340 | "version": "5.1.2", 2341 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 2342 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 2343 | }, 2344 | "safe-regex": { 2345 | "version": "1.1.0", 2346 | "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", 2347 | "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", 2348 | "requires": { 2349 | "ret": "~0.1.10" 2350 | } 2351 | }, 2352 | "safer-buffer": { 2353 | "version": "2.1.2", 2354 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 2355 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 2356 | }, 2357 | "semver": { 2358 | "version": "4.3.6", 2359 | "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", 2360 | "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" 2361 | }, 2362 | "set-value": { 2363 | "version": "2.0.0", 2364 | "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", 2365 | "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", 2366 | "requires": { 2367 | "extend-shallow": "^2.0.1", 2368 | "is-extendable": "^0.1.1", 2369 | "is-plain-object": "^2.0.3", 2370 | "split-string": "^3.0.1" 2371 | }, 2372 | "dependencies": { 2373 | "extend-shallow": { 2374 | "version": "2.0.1", 2375 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 2376 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2377 | "requires": { 2378 | "is-extendable": "^0.1.0" 2379 | } 2380 | } 2381 | } 2382 | }, 2383 | "setprototypeof": { 2384 | "version": "1.1.0", 2385 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 2386 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 2387 | }, 2388 | "snapdragon": { 2389 | "version": "0.8.2", 2390 | "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", 2391 | "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", 2392 | "requires": { 2393 | "base": "^0.11.1", 2394 | "debug": "^2.2.0", 2395 | "define-property": "^0.2.5", 2396 | "extend-shallow": "^2.0.1", 2397 | "map-cache": "^0.2.2", 2398 | "source-map": "^0.5.6", 2399 | "source-map-resolve": "^0.5.0", 2400 | "use": "^3.1.0" 2401 | }, 2402 | "dependencies": { 2403 | "define-property": { 2404 | "version": "0.2.5", 2405 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 2406 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 2407 | "requires": { 2408 | "is-descriptor": "^0.1.0" 2409 | } 2410 | }, 2411 | "extend-shallow": { 2412 | "version": "2.0.1", 2413 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 2414 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2415 | "requires": { 2416 | "is-extendable": "^0.1.0" 2417 | } 2418 | } 2419 | } 2420 | }, 2421 | "snapdragon-node": { 2422 | "version": "2.1.1", 2423 | "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", 2424 | "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", 2425 | "requires": { 2426 | "define-property": "^1.0.0", 2427 | "isobject": "^3.0.0", 2428 | "snapdragon-util": "^3.0.1" 2429 | }, 2430 | "dependencies": { 2431 | "define-property": { 2432 | "version": "1.0.0", 2433 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 2434 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 2435 | "requires": { 2436 | "is-descriptor": "^1.0.0" 2437 | } 2438 | }, 2439 | "is-accessor-descriptor": { 2440 | "version": "1.0.0", 2441 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 2442 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 2443 | "requires": { 2444 | "kind-of": "^6.0.0" 2445 | } 2446 | }, 2447 | "is-data-descriptor": { 2448 | "version": "1.0.0", 2449 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 2450 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 2451 | "requires": { 2452 | "kind-of": "^6.0.0" 2453 | } 2454 | }, 2455 | "is-descriptor": { 2456 | "version": "1.0.2", 2457 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 2458 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 2459 | "requires": { 2460 | "is-accessor-descriptor": "^1.0.0", 2461 | "is-data-descriptor": "^1.0.0", 2462 | "kind-of": "^6.0.2" 2463 | } 2464 | }, 2465 | "isobject": { 2466 | "version": "3.0.1", 2467 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 2468 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 2469 | }, 2470 | "kind-of": { 2471 | "version": "6.0.2", 2472 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", 2473 | "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" 2474 | } 2475 | } 2476 | }, 2477 | "snapdragon-util": { 2478 | "version": "3.0.1", 2479 | "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", 2480 | "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", 2481 | "requires": { 2482 | "kind-of": "^3.2.0" 2483 | } 2484 | }, 2485 | "socket.io": { 2486 | "version": "1.7.4", 2487 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.4.tgz", 2488 | "integrity": "sha1-L37O3DORvy1cc+KR/iM+bjTU3QA=", 2489 | "requires": { 2490 | "debug": "2.3.3", 2491 | "engine.io": "~1.8.4", 2492 | "has-binary": "0.1.7", 2493 | "object-assign": "4.1.0", 2494 | "socket.io-adapter": "0.5.0", 2495 | "socket.io-client": "1.7.4", 2496 | "socket.io-parser": "2.3.1" 2497 | }, 2498 | "dependencies": { 2499 | "debug": { 2500 | "version": "2.3.3", 2501 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", 2502 | "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", 2503 | "requires": { 2504 | "ms": "0.7.2" 2505 | } 2506 | }, 2507 | "ms": { 2508 | "version": "0.7.2", 2509 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", 2510 | "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" 2511 | } 2512 | } 2513 | }, 2514 | "socket.io-adapter": { 2515 | "version": "0.5.0", 2516 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", 2517 | "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", 2518 | "requires": { 2519 | "debug": "2.3.3", 2520 | "socket.io-parser": "2.3.1" 2521 | }, 2522 | "dependencies": { 2523 | "debug": { 2524 | "version": "2.3.3", 2525 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", 2526 | "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", 2527 | "requires": { 2528 | "ms": "0.7.2" 2529 | } 2530 | }, 2531 | "ms": { 2532 | "version": "0.7.2", 2533 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", 2534 | "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" 2535 | } 2536 | } 2537 | }, 2538 | "socket.io-client": { 2539 | "version": "1.7.4", 2540 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.4.tgz", 2541 | "integrity": "sha1-7J+CA1btme9tNX8HVtZIcXvdQoE=", 2542 | "requires": { 2543 | "backo2": "1.0.2", 2544 | "component-bind": "1.0.0", 2545 | "component-emitter": "1.2.1", 2546 | "debug": "2.3.3", 2547 | "engine.io-client": "~1.8.4", 2548 | "has-binary": "0.1.7", 2549 | "indexof": "0.0.1", 2550 | "object-component": "0.0.3", 2551 | "parseuri": "0.0.5", 2552 | "socket.io-parser": "2.3.1", 2553 | "to-array": "0.1.4" 2554 | }, 2555 | "dependencies": { 2556 | "debug": { 2557 | "version": "2.3.3", 2558 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", 2559 | "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", 2560 | "requires": { 2561 | "ms": "0.7.2" 2562 | } 2563 | }, 2564 | "ms": { 2565 | "version": "0.7.2", 2566 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", 2567 | "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=" 2568 | } 2569 | } 2570 | }, 2571 | "socket.io-parser": { 2572 | "version": "2.3.1", 2573 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", 2574 | "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", 2575 | "requires": { 2576 | "component-emitter": "1.1.2", 2577 | "debug": "2.2.0", 2578 | "isarray": "0.0.1", 2579 | "json3": "3.3.2" 2580 | }, 2581 | "dependencies": { 2582 | "component-emitter": { 2583 | "version": "1.1.2", 2584 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", 2585 | "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=" 2586 | }, 2587 | "debug": { 2588 | "version": "2.2.0", 2589 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 2590 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", 2591 | "requires": { 2592 | "ms": "0.7.1" 2593 | } 2594 | }, 2595 | "isarray": { 2596 | "version": "0.0.1", 2597 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 2598 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 2599 | }, 2600 | "ms": { 2601 | "version": "0.7.1", 2602 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 2603 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" 2604 | } 2605 | } 2606 | }, 2607 | "source-map": { 2608 | "version": "0.5.7", 2609 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 2610 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" 2611 | }, 2612 | "source-map-resolve": { 2613 | "version": "0.5.2", 2614 | "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", 2615 | "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", 2616 | "requires": { 2617 | "atob": "^2.1.1", 2618 | "decode-uri-component": "^0.2.0", 2619 | "resolve-url": "^0.2.1", 2620 | "source-map-url": "^0.4.0", 2621 | "urix": "^0.1.0" 2622 | } 2623 | }, 2624 | "source-map-url": { 2625 | "version": "0.4.0", 2626 | "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", 2627 | "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" 2628 | }, 2629 | "split-string": { 2630 | "version": "3.1.0", 2631 | "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", 2632 | "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", 2633 | "requires": { 2634 | "extend-shallow": "^3.0.0" 2635 | } 2636 | }, 2637 | "static-extend": { 2638 | "version": "0.1.2", 2639 | "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", 2640 | "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", 2641 | "requires": { 2642 | "define-property": "^0.2.5", 2643 | "object-copy": "^0.1.0" 2644 | }, 2645 | "dependencies": { 2646 | "define-property": { 2647 | "version": "0.2.5", 2648 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 2649 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 2650 | "requires": { 2651 | "is-descriptor": "^0.1.0" 2652 | } 2653 | } 2654 | } 2655 | }, 2656 | "statuses": { 2657 | "version": "1.5.0", 2658 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 2659 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 2660 | }, 2661 | "string_decoder": { 2662 | "version": "1.1.1", 2663 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 2664 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 2665 | "requires": { 2666 | "safe-buffer": "~5.1.0" 2667 | } 2668 | }, 2669 | "tmp": { 2670 | "version": "0.0.33", 2671 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 2672 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 2673 | "requires": { 2674 | "os-tmpdir": "~1.0.2" 2675 | } 2676 | }, 2677 | "to-array": { 2678 | "version": "0.1.4", 2679 | "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", 2680 | "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" 2681 | }, 2682 | "to-object-path": { 2683 | "version": "0.3.0", 2684 | "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", 2685 | "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", 2686 | "requires": { 2687 | "kind-of": "^3.0.2" 2688 | } 2689 | }, 2690 | "to-regex": { 2691 | "version": "3.0.2", 2692 | "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", 2693 | "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", 2694 | "requires": { 2695 | "define-property": "^2.0.2", 2696 | "extend-shallow": "^3.0.2", 2697 | "regex-not": "^1.0.2", 2698 | "safe-regex": "^1.1.0" 2699 | } 2700 | }, 2701 | "to-regex-range": { 2702 | "version": "2.1.1", 2703 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", 2704 | "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", 2705 | "requires": { 2706 | "is-number": "^3.0.0", 2707 | "repeat-string": "^1.6.1" 2708 | }, 2709 | "dependencies": { 2710 | "is-number": { 2711 | "version": "3.0.0", 2712 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", 2713 | "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", 2714 | "requires": { 2715 | "kind-of": "^3.0.2" 2716 | } 2717 | } 2718 | } 2719 | }, 2720 | "type-is": { 2721 | "version": "1.6.16", 2722 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 2723 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 2724 | "requires": { 2725 | "media-typer": "0.3.0", 2726 | "mime-types": "~2.1.18" 2727 | } 2728 | }, 2729 | "ultron": { 2730 | "version": "1.0.2", 2731 | "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", 2732 | "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=" 2733 | }, 2734 | "union-value": { 2735 | "version": "1.0.0", 2736 | "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", 2737 | "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", 2738 | "requires": { 2739 | "arr-union": "^3.1.0", 2740 | "get-value": "^2.0.6", 2741 | "is-extendable": "^0.1.1", 2742 | "set-value": "^0.4.3" 2743 | }, 2744 | "dependencies": { 2745 | "extend-shallow": { 2746 | "version": "2.0.1", 2747 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 2748 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2749 | "requires": { 2750 | "is-extendable": "^0.1.0" 2751 | } 2752 | }, 2753 | "set-value": { 2754 | "version": "0.4.3", 2755 | "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", 2756 | "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", 2757 | "requires": { 2758 | "extend-shallow": "^2.0.1", 2759 | "is-extendable": "^0.1.1", 2760 | "is-plain-object": "^2.0.1", 2761 | "to-object-path": "^0.3.0" 2762 | } 2763 | } 2764 | } 2765 | }, 2766 | "unpipe": { 2767 | "version": "1.0.0", 2768 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2769 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 2770 | }, 2771 | "unset-value": { 2772 | "version": "1.0.0", 2773 | "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", 2774 | "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", 2775 | "requires": { 2776 | "has-value": "^0.3.1", 2777 | "isobject": "^3.0.0" 2778 | }, 2779 | "dependencies": { 2780 | "has-value": { 2781 | "version": "0.3.1", 2782 | "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", 2783 | "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", 2784 | "requires": { 2785 | "get-value": "^2.0.3", 2786 | "has-values": "^0.1.4", 2787 | "isobject": "^2.0.0" 2788 | }, 2789 | "dependencies": { 2790 | "isobject": { 2791 | "version": "2.1.0", 2792 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", 2793 | "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", 2794 | "requires": { 2795 | "isarray": "1.0.0" 2796 | } 2797 | } 2798 | } 2799 | }, 2800 | "has-values": { 2801 | "version": "0.1.4", 2802 | "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", 2803 | "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" 2804 | }, 2805 | "isobject": { 2806 | "version": "3.0.1", 2807 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 2808 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 2809 | } 2810 | } 2811 | }, 2812 | "urix": { 2813 | "version": "0.1.0", 2814 | "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", 2815 | "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" 2816 | }, 2817 | "use": { 2818 | "version": "3.1.1", 2819 | "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", 2820 | "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" 2821 | }, 2822 | "useragent": { 2823 | "version": "2.3.0", 2824 | "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", 2825 | "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", 2826 | "requires": { 2827 | "lru-cache": "4.1.x", 2828 | "tmp": "0.0.x" 2829 | } 2830 | }, 2831 | "util-deprecate": { 2832 | "version": "1.0.2", 2833 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 2834 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 2835 | }, 2836 | "utils-merge": { 2837 | "version": "1.0.1", 2838 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 2839 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 2840 | }, 2841 | "void-elements": { 2842 | "version": "2.0.1", 2843 | "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", 2844 | "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" 2845 | }, 2846 | "which": { 2847 | "version": "1.3.1", 2848 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 2849 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 2850 | "requires": { 2851 | "isexe": "^2.0.0" 2852 | } 2853 | }, 2854 | "wordwrap": { 2855 | "version": "0.0.3", 2856 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 2857 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" 2858 | }, 2859 | "wrappy": { 2860 | "version": "1.0.2", 2861 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2862 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 2863 | }, 2864 | "ws": { 2865 | "version": "1.1.5", 2866 | "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz", 2867 | "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==", 2868 | "requires": { 2869 | "options": ">=0.0.5", 2870 | "ultron": "1.0.x" 2871 | } 2872 | }, 2873 | "wtf-8": { 2874 | "version": "1.0.0", 2875 | "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", 2876 | "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=" 2877 | }, 2878 | "xmlhttprequest-ssl": { 2879 | "version": "1.5.3", 2880 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", 2881 | "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=" 2882 | }, 2883 | "yallist": { 2884 | "version": "2.1.2", 2885 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 2886 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 2887 | }, 2888 | "yeast": { 2889 | "version": "0.1.2", 2890 | "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", 2891 | "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" 2892 | } 2893 | } 2894 | } 2895 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "forms", 3 | "version": "1.0.0", 4 | "description": "FIXME: Write a one-line description of your library/project.", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "karma": "^0.13.22", 16 | "karma-chrome-launcher": "^0.2.2", 17 | "karma-cljs-test": "^0.1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /pom.xml.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNATURE----- 2 | Version: GnuPG v1 3 | Comment: GPGTools - https://gpgtools.org 4 | 5 | iQIcBAABCgAGBQJdCnmpAAoJEHoFEF5dD+IMnBsP/iGDvURhHZRTo+13bv+eFHG/ 6 | ueKCSfIx+lyCkU8CsbgQ+tzHRkqs8xmtbSxVF/g/uDEA0qQH9ygsz6+d9HhWsg2Q 7 | TPYOvp+GoE2MUXc5j3/HgSb+0uCnX34AsTatJbjQNCXVvheYa8yrYjYZfQWWrNc2 8 | 2JQb/+0Vq6o4wlT9ld7dQ3uCVhGbB7QMfE/q+mNyXdfJ4WQ3eY4C2XCfC6gxcdTt 9 | AzkaRJwg2PRepj2Idp8LLBePS3S8sg43wyLhQCVWT8wStTu8YiKRzkGuG1vZ8CdH 10 | mPybvCDYycT2QdGe2WaOfOw4JbvFzbZzbUdIroibTMcjk8EwjhY9W1fhOqNdoGGY 11 | kjH8nEXWMiCuk47Ss/+hGw3eW5WGvPjRqtsL42ZHwvUQVl2/uVy5gn/kePi/gYWb 12 | MZqffZpIn44oLUn2mU+61BDnDZO+Lc1BzkWkjxlgmnkXLVHYgU/hPQIoRlEIr+a+ 13 | M2I7bIUjk1C8eVS4CZBFAbQupw60B1wYfK68XKYU4L/dQ0t8LRcjeWlZkEJ81Nhj 14 | WIVdGVUDAEBgiuQX9Hmb5eCvAFGLHR60+4nCCwlZESXWe/3SHCUbMovLvIKnZiBs 15 | zU4cUnWuspjOMHm0rzXIGcOrWQQDJDmEBAn8XIG/uebK9aLCyeKDvvjgNMQMV4Fq 16 | VNqg/e/LhfPF0agrAYhF 17 | =OTVc 18 | -----END PGP SIGNATURE----- 19 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject keechma/forms "0.1.7" 2 | :description "Keechma Forms library allows you to build forms (using the Reagent library) that have a great UX." 3 | :url "http://github.com/keechma/forms" 4 | :license {:name "MIT"} 5 | 6 | :min-lein-version "2.6.1" 7 | 8 | :dependencies [[org.clojure/clojure "1.10.0"] 9 | [org.clojure/clojurescript "1.10.520"] 10 | [reagent "0.8.1"] 11 | [org.clojure/core.async "0.4.490" 12 | :exclusions [org.clojure/tools.reader]]] 13 | 14 | :plugins [[lein-figwheel "0.5.18"] 15 | [lein-codox "0.9.3"] 16 | [lein-cljsbuild "1.1.7" :exclusions [[org.clojure/clojure]]]] 17 | 18 | :source-paths ["src"] 19 | 20 | :clean-targets ^{:protect false} ["resources/public/js/compiled" "target"] 21 | 22 | :codox {:language :clojurescript 23 | :metadata {:doc/format :markdown} 24 | :namespaces [forms.core forms.dirty forms.validator]} 25 | 26 | :aliases {"test" ["with-profile" "test" "doo" "chrome" "test"]} 27 | 28 | :profiles {:test {:dependencies [[lein-doo "0.1.6"] 29 | [re-frame "0.10.6"] 30 | [day8.re-frame/test "0.1.5"]] 31 | :plugins [[lein-doo "0.1.6"]]}} 32 | 33 | :cljsbuild {:builds 34 | [{:id "dev" 35 | :source-paths ["src"] 36 | 37 | ;; If no code is to be run, set :figwheel true for continued automagical reloading 38 | :figwheel {:on-jsload "forms.core/on-js-reload"} 39 | 40 | :compiler {:main forms.core 41 | :asset-path "js/compiled/out" 42 | :output-to "resources/public/js/compiled/forms.js" 43 | :output-dir "resources/public/js/compiled/out" 44 | :source-map-timestamp true}} 45 | ;; This next build is an compressed minified build for 46 | ;; production. You can build this with: 47 | ;; lein cljsbuild once min 48 | {:id "min" 49 | :source-paths ["src"] 50 | :compiler {:output-to "resources/public/js/compiled/forms.js" 51 | :main forms.core 52 | :optimizations :advanced 53 | :pretty-print false}} 54 | {:id "test" 55 | :source-paths ["src", "test"] 56 | :compiler {:output-to 57 | "resources/public/js/compiled/test.js" 58 | :optimizations :none 59 | :main forms.test.test}}]} 60 | 61 | :figwheel {;; :http-server-root "public" ;; default and assumes "resources" 62 | ;; :server-port 3449 ;; default 63 | ;; :server-ip "127.0.0.1" 64 | 65 | :css-dirs ["resources/public/css"] ;; watch and update CSS 66 | 67 | ;; Start an nREPL server into the running figwheel process 68 | ;; :nrepl-port 7888 69 | 70 | ;; Server Ring Handler (optional) 71 | ;; if you want to embed a ring handler into the figwheel http-kit 72 | ;; server, this is for simple ring servers, if this 73 | ;; doesn't work for you just run your own server :) 74 | ;; :ring-handler hello_world.server/handler 75 | 76 | ;; To be able to open files in your editor from the heads up display 77 | ;; you will need to put a script on your path. 78 | ;; that script will have to take a file path and a line number 79 | ;; ie. in ~/bin/myfile-opener 80 | ;; #! /bin/sh 81 | ;; emacsclient -n +$2 $1 82 | ;; 83 | ;; :open-file-command "myfile-opener" 84 | 85 | ;; if you want to disable the REPL 86 | ;; :repl false 87 | 88 | ;; to configure a different figwheel logfile path 89 | ;; :server-logfile "tmp/logs/figwheel-logfile.log" 90 | }) 91 | -------------------------------------------------------------------------------- /src/forms/core.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.core 2 | (:require [reagent.core :as r] 3 | [clojure.string :as str] 4 | [forms.util :refer [key-to-path]] 5 | [forms.dirty :refer [calculate-dirty-fields]]) 6 | (:require-macros [reagent.ratom :refer [reaction]])) 7 | 8 | (declare init-state) 9 | 10 | (defn errors-keypaths 11 | "Calculates the error key paths from the error map. It is used to mark 12 | all invalid key paths as dirty" 13 | ([data] (distinct (:results (errors-keypaths data [] {:results []})))) 14 | ([data path results] 15 | (reduce-kv (fn [m k v] 16 | (if (= k :$errors$) 17 | (assoc m :results (conj (:results m) path)) 18 | (if (or (vector? v) (map? v)) 19 | (let [{:keys [results lengths]} m 20 | new-path (conj path k) 21 | child-paths (errors-keypaths v new-path {:results []}) 22 | new-results (:results child-paths)] 23 | {:results (concat results new-results)}) 24 | (if (nil? v) 25 | m 26 | (assoc m :results (conj (:results m) (conj path k))))))) results data))) 27 | 28 | (defprotocol IForm 29 | "IForm protocol defines the form behavior" 30 | (init! [this] 31 | "Initializes the form. If the form constructor was called with the 32 | `auto-validate?` option set to `true` it will add a watch to the 33 | internal state atom and validate the form every time data was changed") 34 | (state [this] 35 | "Returns inner state atom. The state map holds the following properties: 36 | 37 | - `:errors` - map of the current form errors 38 | - `:init-data` - initial form data that was passed to the constructor 39 | - `:data` - current form data 40 | - `:cached-dirty-key-paths` - set of the key paths that were dirty when 41 | the whole form was validated 42 | - `:dirty-key-paths` - set of the dirty key paths") 43 | (errors [this] 44 | "Returns the atom that holds the current error map. This map holds all 45 | errors, regardless of the key path `dirty` state.") 46 | (errors-for-path [this key-path] 47 | "Returns errors for the key path. It is possible that this function will 48 | return `nil` even though the errors exist in the `:errors` map because 49 | this function accounts for the key path `dirty` state. If the field has 50 | errors, but it's not dirty this function will return `nil`") 51 | (data [this] 52 | "Returns the atom that holds the current data map.") 53 | (data-for-path [this key-path] 54 | "Returns data for the key path") 55 | (validate! [this] [this dirty-only?] 56 | "Validates the form. 57 | 58 | When called without the second argument - `(validate! form)` it will validate 59 | the whole form and mark all invalid fields as `dirty` This should happen on 60 | `submit` so you can show all form errors to the user. 61 | 62 | When called with `true` as the second argument - `(validate! form true)` it wil 63 | validate only the fields that are marked as dirty - fields that are not `nil` and 64 | that have a different value than in the `:init-data`. This can be called on `change` 65 | or `blur` events.") 66 | (commit! [this] 67 | "Commits the form. It will validate the form and mark dirty key paths. After that 68 | it will call the `:on-commit` function that can be used to persist the form.") 69 | (update! [this data] 70 | "Updates the data, marks dirty key paths and validates the form") 71 | (mark-dirty! [this] 72 | "Mark all invalid key paths as dirty. It will validate the whole form and 73 | cache any key paths that have errors.") 74 | (mark-dirty-paths! [this] 75 | "Creates a diff between the initial data and the current data. Based on that diff 76 | it marks the key paths that are dirty.") 77 | (clear-cached-dirty-key-paths! [this] 78 | "Clear dirty key paths that are cached after caling the `mark-dirty!` function") 79 | (is-valid? [this] 80 | "Is the form in the valid state") 81 | (is-valid-path? [this key-path] 82 | "Is the key path in the valid state") 83 | (dirty-paths-valid? [this] 84 | "Are the dirty key paths in the valid state") 85 | (reset-form! [this] [this init-data] 86 | "Reset form to the initial state")) 87 | 88 | (defrecord Form [state-atom validator opts] 89 | IForm 90 | (init! [this] 91 | 92 | (let [new-this (assoc this 93 | :data-cursor (r/cursor (:state-atom this) [:data]) 94 | :errors-cursor (r/cursor (:state-atom this) [:errors])) 95 | auto-validate? (get-in new-this [:opts :auto-validate?])] 96 | (remove-watch (state new-this) :__form__) 97 | (when auto-validate? 98 | (add-watch (state new-this) :__form__ 99 | (fn [_ _ old-val new-val] 100 | (let [old-data (:data old-val) 101 | new-data (:data new-val)] 102 | (when (not= old-data new-data) 103 | (mark-dirty-paths! new-this) 104 | (validate! new-this true)))))) 105 | new-this)) 106 | 107 | (state [this] 108 | (:state-atom this)) 109 | 110 | (errors [this] 111 | (:errors-cursor this)) 112 | 113 | (errors-for-path [this key-path] 114 | (reaction 115 | (let [path (key-to-path key-path) 116 | current-state @(state this) 117 | is-dirty? (contains? (:dirty-key-paths current-state) path)] 118 | (when is-dirty? (get-in @(errors this) (conj path :$errors$)))))) 119 | 120 | (data [this] 121 | (:data-cursor this)) 122 | 123 | (data-for-path [this key-path] 124 | (reaction 125 | (get-in @(data this) (key-to-path key-path)))) 126 | 127 | (validate! [this] 128 | (validate! this false)) 129 | 130 | (validate! [this dirty-only?] 131 | (if dirty-only? 132 | (mark-dirty-paths! this) 133 | (mark-dirty! this)) 134 | (let [validator (:validator this)] 135 | (swap! (state this) assoc :errors (validator @(data this))))) 136 | 137 | (commit! [this] 138 | (let [commit-fn (get-in this [:opts :on-commit])] 139 | (mark-dirty! this) 140 | (validate! this) 141 | (commit-fn this))) 142 | 143 | (update! [this data] 144 | (swap! (state this) assoc :data data) 145 | (mark-dirty-paths! this) 146 | (validate! this true)) 147 | 148 | (mark-dirty! [this] 149 | (let [errors (validator @(data this)) 150 | errors-keypaths (errors-keypaths errors) 151 | current-state @(state this) 152 | current-dirty-paths (:dirty-key-paths state)] 153 | (reset! (state this) 154 | (assoc current-state 155 | :cached-dirty-key-paths (set (concat (:cached-dirty-key-paths state) errors-keypaths)) 156 | :dirty-key-paths (set errors-keypaths))))) 157 | 158 | (mark-dirty-paths! [this] 159 | (let [current-state @(state this) 160 | dirty-paths (calculate-dirty-fields (:init-data current-state) (:data current-state))] 161 | (swap! (state this) assoc :dirty-key-paths (set (concat dirty-paths 162 | (:cached-dirty-key-paths current-state)))))) 163 | 164 | (dirty-paths-valid? [this] 165 | (reaction 166 | (let [current-state @(state this) 167 | current-errors (:errors current-state)] 168 | (let [dirty-paths (:dirty-key-paths current-state) 169 | valid-paths (take-while 170 | (fn [path] 171 | (nil? (get-in current-errors path))) dirty-paths)] 172 | (= (count valid-paths) (count dirty-paths)))))) 173 | 174 | (clear-cached-dirty-key-paths! [this] 175 | (swap! (state this) assoc :cached-dirty-key-paths (set {}))) 176 | 177 | (is-valid? [this] 178 | (reaction 179 | (= {} @(errors this)))) 180 | 181 | (is-valid-path? [this key-path] 182 | (reaction 183 | (nil? @(errors-for-path this key-path)))) 184 | 185 | (reset-form! [this] 186 | (reset-form! this (:init-data @(state this)))) 187 | 188 | (reset-form! [this init-data] 189 | (reset! (state this) (init-state init-data)))) 190 | 191 | (defn ^:private init-state [data] 192 | {:errors {} 193 | :init-data data 194 | :data (or data {}) 195 | :cached-dirty-key-paths (set {}) 196 | :dirty-key-paths (set {})}) 197 | 198 | (defn ^:private with-default-opts [opts] 199 | (merge {:on-commit (fn [_]) 200 | :auto-validate? false} opts)) 201 | 202 | (defn constructor 203 | "Form constructor. It accepts the following arguments: 204 | 205 | - `validator` - returned either by the `form.validator/validator` or `form.validator/comp-validators` function 206 | - `data` - initial data map 207 | - `opts` - map with the form options: 208 | + `:on-commit` - function to be called when the form is commited (by calling `(commit! form)`) 209 | + `:auto-validate?` - should the form be validated on any data change" 210 | 211 | ([validator] (partial constructor validator)) 212 | ([validator data] (constructor validator data {})) 213 | ([validator data opts] 214 | (init! (->Form (r/atom (init-state data)) validator (with-default-opts opts))))) 215 | -------------------------------------------------------------------------------- /src/forms/dirty.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.dirty 2 | (:require [clojure.data :refer [diff]])) 3 | 4 | (defn ^:private analyze-diff 5 | ([data] (analyze-diff data [] {:results [] :lengths {}})) 6 | ([data path results] 7 | (reduce-kv (fn [m k v] 8 | (if (or (vector? v) (map? v)) 9 | (let [{:keys [results lengths]} m 10 | new-path (conj path k) 11 | child-diff (analyze-diff v new-path {:results [] :lengths {}}) 12 | new-results (:results child-diff) 13 | new-lengths (:lengths child-diff) 14 | lengths-with-current (if (vector? v) 15 | (assoc lengths new-path (count v)) 16 | lengths)] 17 | {:results (into results new-results) 18 | :lengths (merge new-lengths lengths-with-current)}) 19 | (if (nil? v) 20 | m 21 | (assoc m :results (conj (:results m) (conj path k)))))) results data))) 22 | 23 | (defn calculate-dirty-fields 24 | "Calculates the key paths that are dirty by diffing the initial and current form data." 25 | [prev current] 26 | (let [[p-diff c-diff] (into [] (diff prev current)) 27 | p-report (analyze-diff p-diff) 28 | c-report (analyze-diff c-diff) 29 | [p-lengths-diff c-lengths-diff] (into [] (diff (:lengths p-report) (:lengths c-report)))] 30 | (set (concat (:results p-report) 31 | (:results c-report) 32 | (keys p-lengths-diff) 33 | (keys c-lengths-diff))))) 34 | -------------------------------------------------------------------------------- /src/forms/re_frame.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.re-frame 2 | (:require [clojure.string :as str] 3 | [forms.util :refer [key-to-path]] 4 | [forms.dirty :refer [calculate-dirty-fields]] 5 | [re-frame.core :as rf])) 6 | 7 | (defn ^:private init-state 8 | [data validator opts] 9 | {:errors {} 10 | :init-data data 11 | :data (or data {}) 12 | :cached-dirty-key-paths #{} 13 | :dirty-key-paths #{} 14 | :validator validator 15 | :opts opts}) 16 | 17 | (defn errors-keypaths 18 | "Calculates the error key paths from the error map. It is used to mark 19 | all invalid key paths as dirty" 20 | ([data] (distinct (:results (errors-keypaths data [] {:results []})))) 21 | ([data path results] 22 | (reduce-kv (fn [m k v] 23 | (if (= k :$errors$) 24 | (assoc m :results (conj (:results m) path)) 25 | (if (or (vector? v) (map? v)) 26 | (let [{:keys [results lengths]} m 27 | new-path (conj path k) 28 | child-paths (errors-keypaths v new-path m) 29 | new-results (:results child-paths)] 30 | {:results (concat results new-results)}) 31 | (if (nil? v) 32 | m 33 | (assoc m :results (conj (:results m) (conj path k))))))) results data))) 34 | 35 | (defn form-path->db-path 36 | [form-path] 37 | (conj form-path [::form])) 38 | 39 | (rf/reg-sub ::form 40 | (fn form [db [_ form-path]] 41 | (get-in db (form-path->db-path form-path)))) 42 | 43 | (rf/reg-sub ::data 44 | (fn data [db [_ form-path]] 45 | (get-in db (conj (form-path->db-path form-path) :data)))) 46 | 47 | (rf/reg-sub ::cached-dirty-key-paths 48 | (fn data [db [_ form-path]] 49 | (get-in db (conj (form-path->db-path form-path) :cached-dirty-key-paths)))) 50 | 51 | (rf/reg-sub ::dirty-key-paths 52 | (fn data [db [_ form-path]] 53 | (get-in db (conj (form-path->db-path form-path) :dirty-key-paths)))) 54 | 55 | (rf/reg-sub ::data-for-path 56 | (fn data-for-path [db [_ form-path key-path]] 57 | (get-in db (into (conj (form-path->db-path form-path) :data) (key-to-path key-path))))) 58 | 59 | (rf/reg-sub ::errors 60 | (fn errors [db [_ form-path]] 61 | (get-in db (conj (form-path->db-path form-path) :errors)))) 62 | 63 | (defn errors-for-path 64 | [db [_ form-path key-path]] 65 | (let [path (key-to-path key-path) 66 | is-dirty? (contains? (:dirty-key-paths (get-in db (form-path->db-path form-path))) path)] 67 | (when is-dirty? 68 | (get-in db (into (conj (form-path->db-path form-path) :errors) (conj path :$errors$)))))) 69 | 70 | (rf/reg-sub ::errors-for-path 71 | errors-for-path) 72 | 73 | (rf/reg-event-db ::init! 74 | (fn init! [db [_ form-path form-data]] 75 | (assoc-in db (form-path->db-path form-path) form-data))) 76 | 77 | (defn mark-dirty! 78 | [form-state] 79 | (let [validator (:validator form-state) 80 | errors (validator (:data form-state)) 81 | errors-keypaths (errors-keypaths errors) 82 | current-dirty-paths (:dirty-key-paths form-state)] 83 | (assoc form-state 84 | :cached-dirty-key-paths (set (concat (:cached-dirty-key-paths form-state) errors-keypaths)) 85 | :dirty-key-paths (set errors-keypaths)))) 86 | 87 | (defn mark-dirty-paths! 88 | [form-state] 89 | (let [dirty-paths (calculate-dirty-fields (:init-data form-state) (:data form-state))] 90 | (assoc form-state :dirty-key-paths (set (concat dirty-paths 91 | (:cached-dirty-key-paths form-state)))))) 92 | 93 | (defn validate! 94 | [db [_ form-path dirty-only?]] 95 | (let [dirty-db (if dirty-only? 96 | (update-in db (form-path->db-path form-path) mark-dirty-paths!) 97 | (update-in db (form-path->db-path form-path) mark-dirty!)) 98 | validator (:validator (get-in dirty-db (form-path->db-path form-path)))] 99 | (update-in dirty-db (conj (form-path->db-path form-path)) assoc :errors (validator (:data (get-in dirty-db (form-path->db-path form-path))))))) 100 | 101 | (rf/reg-event-db ::validate! 102 | validate!) 103 | 104 | (rf/reg-event-db ::commit! 105 | (fn commit! [db [_ form-path]] 106 | (let [form-state (get-in db (form-path->db-path form-path)) 107 | commit-fn (get-in form-state [:opts :on-commit]) 108 | dirty-db (update-in db (form-path->db-path form-path) mark-dirty!) 109 | validated-db (validate! dirty-db [nil form-path]) 110 | new-form-state (get-in validated-db (form-path->db-path form-path))] 111 | (commit-fn new-form-state) 112 | validated-db))) 113 | 114 | (rf/reg-event-db ::update! 115 | (fn update! [db [_ form-path data]] 116 | (let [updated-db (update-in db (form-path->db-path form-path) assoc :data data) 117 | dirty-db (update-in updated-db (form-path->db-path form-path) mark-dirty-paths!)] 118 | (validate! dirty-db [nil form-path true])))) 119 | 120 | (rf/reg-event-db ::mark-dirty! 121 | (fn mark-dirty!-ev [db [_ form-path]] 122 | (update-in db (form-path->db-path form-path) mark-dirty!))) 123 | 124 | (rf/reg-event-db ::mark-dirty-paths! 125 | (fn mark-dirty-paths!-ev [db [_ form-path]] 126 | (update-in db (form-path->db-path form-path) mark-dirty-paths!))) 127 | 128 | (rf/reg-sub ::dirty-paths-valid? 129 | (fn dirty-paths-valid? [db [_ form-path]] 130 | (let [form-state (get-in db (form-path->db-path form-path)) 131 | errors (:errors form-state) 132 | dirty-paths (:dirty-key-paths form-state) 133 | valid-paths (take-while 134 | (fn [path] 135 | (nil? (get-in errors path))) dirty-paths)] 136 | (= (count valid-paths) (count dirty-paths))))) 137 | 138 | (rf/reg-event-db ::clear-cached-dirty-key-paths! 139 | (fn mark-dirty-paths!-ev [db [_ form-path]] 140 | (update-in db (form-path->db-path form-path) assoc :cached-dirty-key-paths #{}))) 141 | 142 | (defn is-valid? 143 | [form-state] 144 | (let [errors (:errors form-state)] 145 | (= errors {}))) 146 | 147 | (rf/reg-sub ::is-valid? 148 | (fn is-valid?-ev [db [_ form-path]] 149 | (let [form-state (get-in db (form-path->db-path form-path))] 150 | (is-valid? form-state)))) 151 | 152 | (rf/reg-sub ::is-valid-path? 153 | (fn is-valid-path? [db [_ form-path key-path]] 154 | (nil? (errors-for-path db [nil form-path key-path])))) 155 | 156 | (defn ^:private with-default-opts [opts] 157 | (merge {:on-commit (fn on-commit-placeholder [_]) 158 | :auto-validate? false} opts)) 159 | 160 | (rf/reg-event-db ::reset-form! 161 | (fn reset-form! [db [_ form-path init-data*]] 162 | (let [{:keys [:init-data :validator :opts]} (get-in db (form-path->db-path form-path))] 163 | (if init-data* 164 | (update-in db (form-path->db-path form-path) merge (init-state init-data* validator (with-default-opts opts))) 165 | (update-in db (form-path->db-path form-path) merge (init-state init-data validator (with-default-opts opts))))))) 166 | 167 | (defn set-value 168 | [db [_ form-path key-path value]] 169 | (let [form-state (get-in db (form-path->db-path form-path)) 170 | auto-validate? (get-in form-state [:opts :auto-validate?]) 171 | setted-db (assoc-in db (into (conj (form-path->db-path form-path) :data) (key-to-path key-path)) value)] 172 | (if auto-validate? 173 | (let [old-value (get-in db (into (conj (form-path->db-path form-path) :data) (key-to-path key-path)))] 174 | (if (= value old-value) 175 | setted-db 176 | (-> setted-db 177 | (update-in (form-path->db-path form-path) mark-dirty-paths!) 178 | (validate! [nil form-path true])))) 179 | setted-db))) 180 | 181 | (rf/reg-event-db ::set! set-value) 182 | 183 | (defn constructor 184 | "Form constructor. It accepts the following arguments: 185 | 186 | - `validator` - returned either by the `form.validator/validator` or `form.validator/comp-validators` function 187 | - `path` - path in the db where to put the form 188 | - `data` - initial data map 189 | - `opts` - map with the form options: 190 | + `:on-commit` - function to be called when the form is commited (by calling `(commit! form)`) 191 | + `:auto-validate?` - should the form be validated on any data change" 192 | 193 | ([validator] (partial constructor validator)) 194 | ([validator path] (partial constructor validator path)) 195 | ([validator path data] (constructor validator path data {})) 196 | ([validator path data opts] 197 | (rf/dispatch [::init! path (init-state data validator (with-default-opts opts))]))) 198 | -------------------------------------------------------------------------------- /src/forms/util.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.util 2 | (:require [clojure.string :as str])) 3 | 4 | (defn keyword-or-integer [key] 5 | (cond 6 | (keyword? key) key 7 | (number? key) key 8 | (re-matches #"[0-9]+" key) (js/parseInt key 10) 9 | :else (keyword key))) 10 | 11 | (defn split-key [key] 12 | (let [ns (when (keyword? key) (namespace key)) 13 | path (str/split (name key) ".")] 14 | (if ns 15 | (concat [(keyword ns (first path))] (rest path)) 16 | path))) 17 | 18 | (defn key-to-path [key] 19 | (let [path (if (vector? key) key (split-key key))] 20 | (mapv keyword-or-integer path))) 21 | 22 | (defn dissoc-in 23 | "Dissociates an entry from a nested associative structure returning a new 24 | nested structure. keys is a sequence of keys. Any empty maps that result 25 | will not be present in the new structure." 26 | [m [k & ks :as keys]] 27 | (if ks 28 | (if-let [nextmap (get m k)] 29 | (let [newmap (dissoc-in nextmap ks)] 30 | (if (seq newmap) 31 | (assoc m k newmap) 32 | (dissoc m k))) 33 | m) 34 | (dissoc m k))) 35 | -------------------------------------------------------------------------------- /src/forms/validator.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.validator 2 | (:require [forms.util :refer [key-to-path]])) 3 | 4 | (enable-console-print!) 5 | 6 | (defn ^:private get-by-key [key next parent-data parent-errors full-data prev-path] 7 | (let [data (get parent-data key) 8 | errors (or (get parent-errors key) {}) 9 | res (next data errors full-data (conj prev-path key))] 10 | (if (not (or (nil? res) (= {} res))) 11 | (assoc parent-errors key res) 12 | parent-errors))) 13 | 14 | (defn ^:private get-list [next parent-data parent-errors full-data prev-path] 15 | (let [data (reduce-kv (fn [m k v] 16 | (let [errors (or (get m k) {}) 17 | res (next v errors full-data (conj prev-path k))] 18 | (if (not (or (nil? res) (= {} res))) 19 | (assoc m k res) 20 | m))) parent-errors parent-data)] 21 | (if (= data {}) nil data))) 22 | 23 | (defn ^:private validate-attr [validators value full-data path] 24 | (reduce (fn [failed v] 25 | (let [[name validator] v] 26 | (if (ifn? validator) 27 | (if (not (validator value full-data path)) 28 | (conj failed name) 29 | failed) 30 | (throw (js/Error. (str "Validator is not a function: " name)))))) [] validators)) 31 | 32 | (defn ^:private validate-with-nested-validators [nested-validators value errors full-data path] 33 | (if (not (empty? nested-validators)) 34 | (reduce (fn [acc v] 35 | (v value acc full-data path)) errors nested-validators) 36 | errors)) 37 | 38 | (defn ^:private attr-errors [validators value errors full-data path] 39 | (let [nested-validators (vec (filter fn? validators)) 40 | normal-validators (vec (filter (complement fn?) validators)) 41 | with-nested-errors (validate-with-nested-validators nested-validators value errors full-data path) 42 | failed (validate-attr normal-validators value full-data path)] 43 | (if (pos? (count failed)) 44 | {:$errors$ {:value value 45 | :failed (concat (or (get-in with-nested-errors [:$errors$ :failed]) []) failed)}} 46 | with-nested-errors))) 47 | 48 | (defn ^:private key-to-getter [key] 49 | (if (= key :*) get-list (partial get-by-key key))) 50 | 51 | (defn ^:private make-validator [path validators] 52 | (let [iterator (reverse (map key-to-getter path))] 53 | (reduce (fn [acc v] 54 | (if (nil? acc) 55 | (partial v (partial attr-errors validators)) 56 | (partial v acc))) nil iterator))) 57 | 58 | (defn ^:private validate-map 59 | ([input errors key attr-validators] (validate-map input errors key attr-validators nil nil)) 60 | ([input errors key attr-validators full-data prev-path] 61 | (let [path (key-to-path key) 62 | validator (make-validator path attr-validators)] 63 | (validator input errors (or full-data input) (or prev-path []))))) 64 | 65 | (defn ^:private validator-runner 66 | ([validators input] (validator-runner validators input {})) 67 | ([validators input errors] (validator-runner validators input errors nil nil)) 68 | ([validators input errors full-data prev-path] 69 | (reduce-kv (fn [errors key attr-validators] 70 | (validate-map input errors key attr-validators full-data prev-path)) 71 | errors validators))) 72 | 73 | (defn validator 74 | "Creates a form validator. Validator is a map where keys represent the path 75 | to data that will be validated and the value is a vector of attribute validators. 76 | 77 | Attribute validators are tuples where the first element is the attribute validator name 78 | and the second one is the validation function. Validation function receives the value for 79 | the key path and returns a boolean. It should return `true` if the attribute is valid and 80 | `false` if it's invalid. Attribute validators receive `full-data` (whole object that is 81 | being validated) and the attribute `path` as the second and third arguments. 82 | 83 | **Example attribute validator** 84 | 85 | ```clojure 86 | (def not-empty [:not-empty ;; Name of the attribute validator 87 | (fn [value _ _] 88 | (not (empty? v)))] 89 | ``` 90 | 91 | If you want to build more complex validators `full-data` and `path` arguments allow you 92 | to do so. For instance, let's say we're writing the validator that can check if the email 93 | confirmation is the same as the email: 94 | 95 | ```clojure 96 | (def email-confirmation [:confirmed-email? 97 | (fn [value full-data path] 98 | (let [email (:email full-data) 99 | email-confirmation (:email-confirmation full-data)] 100 | (= email email-confirmation)))] 101 | ``` 102 | 103 | 104 | `validator` returns the function that accepts the data and returns the map of validation 105 | errors. 106 | 107 | **Simple example:** 108 | 109 | ```clojure 110 | (def not-empty [:not-empty (fn [v _ _] (not (empty? v)))]) 111 | (def form-validator-1 (validator {:username [not-empty]})) 112 | 113 | (form-validator-1 {:username \"\"}) 114 | ;; returns {:username {:$errors$ {:value \"\" :failed [:not-empty]}}} 115 | ``` 116 | 117 | **Validators can validate nested paths:** 118 | 119 | ```clojure 120 | (def form-validator-2 (validator {:user.name [not-empty]})) 121 | (form-validator-2 {:user {:username \"\"}}) 122 | ;; returns {:user {:username {:$errors$ {:value \"\" :failed [:not-empty]}}}} 123 | ``` 124 | 125 | **Validators can validate objects in the list:** 126 | 127 | ```clojure 128 | (def form-validator-3 (validator {:user.accounts.*.network [not-empty]})) 129 | (form-validator-3 {:user {:accounts [{:network \"\"}]}}) 130 | ;; returns {:user {:accounts {0 {:network {:$errors$ {:value \"\" :failed [:not-empty]}}}}}} 131 | ``` 132 | 133 | **Validators can validate values in the list:** 134 | 135 | ```clojure 136 | (def form-validator-4 (validator {:user.phone-numbers.* [not-empty]})) 137 | (form-validator-3 {:user {:phone-numbers [\"\"]}}) 138 | ;; returns {:user {:phone-numbers {0 {:$errors$ {:value \"\" :failed [:not-empty]}}}}} 139 | ``` 140 | 141 | **Validators can be nested inside other validators:** 142 | 143 | ```clojure 144 | (def user-validator (validator {:username [not-empty]})) 145 | (def article-validator (validator {:title [not-empty] 146 | :user [user-validator]})) 147 | 148 | (article-validator {:title \"\" :user {:username \"\"}}) 149 | ;; returns {:title {:$errors {:value \"\" :failed [:not-empty]}} 150 | ;; :user {:username {:$errors$ {:value \"\" :failed [:not-nil]}}}} 151 | ``` 152 | 153 | Features provided by the validator ensure that you can validate any data structure, no matter how deeply nested it is. You can also create small focused validators that can be nested or composed which ensures 154 | that your validation logic stays DRY and allows reuse of the validators. 155 | " 156 | [validators] 157 | (partial validator-runner validators)) 158 | 159 | (defn comp-validators 160 | "Creates a validator that is a composition of the validators passed as the arguments: 161 | 162 | ```clojure 163 | (def not-empty [:not-empty (fn [v] (not (empty? v)))]) 164 | 165 | (def username-validator (validator {:username [not-empty]})) 166 | (def password-validator (validator {:password [not-empty]})) 167 | 168 | (def user-validator (comp-validators username-validator password-validator)) 169 | 170 | (user-validator {:username \"\" :password \"\"}) 171 | ;; returns {:username {:$errors$ {:value \"\" :failed [:not-empty]}} 172 | ;; :password {:$errors$ {:value \"\" :failed [:not-empty]}}} 173 | ```" 174 | [& validators] 175 | (fn [input] 176 | (reduce (fn [acc v] (or (v input acc) {})) {} validators))) 177 | -------------------------------------------------------------------------------- /test/forms/test/common.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.test.common) 2 | 3 | (def not-nil? [:not-nil (fn [v path full-data] (not (nil? v)))]) 4 | (def not-empty? [:not-empty (fn [v path full-data] (not (empty? v)))]) 5 | (def is-one? [:is-one (fn [v path full-data] (= 1 v))]) 6 | (def is-twitter? [:is-twitter (fn [v path full-data] (= v "twitter"))]) 7 | (def is-facebook? [:is-facebook (fn [v path full-data] (= v "facebook"))]) 8 | -------------------------------------------------------------------------------- /test/forms/test/core.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.test.core 2 | (:require [cljs.test :refer-macros [deftest is]] 3 | [forms.validator :as v] 4 | [forms.core :as core] 5 | [forms.test.common :refer [not-nil? not-empty? is-one? is-twitter? is-facebook?]] 6 | [forms.util :as util])) 7 | 8 | (deftest errors-keypaths [] 9 | (is (= [[:user :username] [:user :social-networks 0 :network] [:user :phone-numbers 0]] 10 | (core/errors-keypaths {:user {:username :retro 11 | :social-networks [{:network :twitter}] 12 | :phone-numbers [0000]}})))) 13 | 14 | 15 | (deftest validate! [] 16 | (let [validator (v/validator {:username [not-nil?] 17 | :password [not-nil?]}) 18 | form (core/constructor validator) 19 | inited-form (form {}) 20 | data (core/data inited-form)] 21 | (is (core/is-valid? inited-form)) 22 | (swap! data assoc :username "retro") 23 | (core/validate! inited-form) 24 | (is (= @(core/errors inited-form)) 25 | {:password {:$errors$ {:value nil :failed [:not-nil]}}}) 26 | (is (not @(core/is-valid? inited-form))) 27 | (is @(core/is-valid-path? inited-form :username)) 28 | (is (not @(core/is-valid-path? inited-form :password))))) 29 | 30 | (deftest auto-validate [] 31 | (let [validator (v/validator {:username [not-nil?] 32 | :password [not-nil?]}) 33 | form (core/constructor validator) 34 | inited-form (form {} {:auto-validate? true}) 35 | data (core/data inited-form)] 36 | (is @(core/is-valid? inited-form)) 37 | (swap! data assoc :username "retro") 38 | (is (not @(core/is-valid? inited-form))) 39 | (is (nil? @(core/errors-for-path inited-form :password))) 40 | (core/commit! inited-form) 41 | (is (not (nil? @(core/errors-for-path inited-form :password)))))) 42 | 43 | (deftest data-for-path [] 44 | (let [validator (v/validator {}) 45 | form (core/constructor validator) 46 | inited-form (form {:username "foo"}) 47 | data (core/data inited-form)] 48 | (is (= @(core/data-for-path inited-form :username) "foo")) 49 | (swap! data assoc :username "bar") 50 | (is (= @(core/data-for-path inited-form :username) "bar")))) 51 | 52 | (deftest update! [] 53 | (let [validator (v/validator {}) 54 | form (core/constructor validator) 55 | inited-form (form {:username "foo"})] 56 | (core/update! inited-form {:username "bar"}) 57 | (is (= @(core/data-for-path inited-form :username) "bar")))) 58 | 59 | (deftest commit! [] 60 | (let [commit-called (atom 0) 61 | validator (v/validator {:username [not-nil?]}) 62 | on-commit (fn [f] 63 | (when (= 0 @commit-called) 64 | (is (not @(core/is-valid? f)))) 65 | (when (= 1 @commit-called) 66 | (is @(core/is-valid? f))) 67 | (swap! commit-called inc)) 68 | form (core/constructor validator) 69 | inited-form (form {} {:on-commit on-commit})] 70 | (core/commit! inited-form) 71 | (swap! (core/data inited-form) assoc :username "foo") 72 | (core/commit! inited-form) 73 | (is (= 2 @commit-called)))) 74 | 75 | (deftest errors [] 76 | (let [validator (v/validator {:username [not-nil?] 77 | :password [not-nil?]}) 78 | form (core/constructor validator) 79 | inited-form (form {} {:auto-validate? true})] 80 | (is (= @(core/errors inited-form) {})) 81 | (is (= nil @(core/errors-for-path inited-form :username))) 82 | (is (= nil @(core/errors-for-path inited-form :password))) 83 | (swap! (core/data inited-form) assoc :username "foo") 84 | (is (= @(core/errors inited-form) {:password {:$errors$ {:value nil :failed [:not-nil]}}})) 85 | (is (= nil @(core/errors-for-path inited-form :password))) 86 | (core/mark-dirty! inited-form) 87 | (is (= {:value nil :failed [:not-nil]} @(core/errors-for-path inited-form :password))))) 88 | 89 | (deftest dirty-paths-valid? [] 90 | (let [validator (v/validator {:username [not-empty?] 91 | :password [not-empty?]}) 92 | form (core/constructor validator) 93 | inited-form (form {} {:auto-validate? true})] 94 | (is @(core/dirty-paths-valid? inited-form)) 95 | (swap! (core/data inited-form) assoc :username "foo") 96 | (is @(core/dirty-paths-valid? inited-form)) 97 | (swap! (core/data inited-form) assoc :password "") 98 | (is (not @(core/dirty-paths-valid? inited-form))))) 99 | 100 | (deftest dirty-paths-valid?-when-all-dirty [] 101 | (let [validator (v/validator {:username [not-nil?] 102 | :password [not-nil?]}) 103 | form (core/constructor validator) 104 | inited-form (form {})] 105 | (is @(core/dirty-paths-valid? inited-form)) 106 | (core/validate! inited-form) 107 | (is (not @(core/dirty-paths-valid? inited-form))))) 108 | 109 | (deftest is-valid-path? [] 110 | (let [validator (v/validator {:username [not-empty?]}) 111 | form (core/constructor validator) 112 | inited-form (form {})] 113 | (is @(core/is-valid-path? inited-form :username)) 114 | (swap! (core/data inited-form) assoc :username "") 115 | (core/validate! inited-form true) 116 | (is (not @(core/is-valid-path? inited-form :username))))) 117 | 118 | (deftest reset-form! [] 119 | (let [validator (v/validator {:username [not-nil?]}) 120 | form (core/constructor validator) 121 | inited-form (form {})] 122 | (swap! (core/data inited-form) assoc :username nil) 123 | (core/validate! inited-form) 124 | (is (not @(core/is-valid? inited-form))) 125 | (core/reset-form! inited-form) 126 | (is (= {} @(core/data inited-form))) 127 | (is @(core/is-valid? inited-form)) 128 | (core/validate! inited-form) 129 | (is (not @(core/is-valid? inited-form))) 130 | (core/reset-form! inited-form {:username "retro"}) 131 | (is @(core/is-valid? inited-form)) 132 | (core/validate! inited-form) 133 | (is @(core/is-valid? inited-form)))) 134 | 135 | (deftest validate-dirty-keys-behavior [] 136 | (let [validator (v/validator {:username [not-empty?] 137 | :password [not-empty?] 138 | :phone-numbers.* [not-empty?]}) 139 | form (core/constructor validator) 140 | inited-form (form {} {:auto-validate? true})] 141 | (swap! (core/data inited-form) assoc :username "") 142 | (core/validate! inited-form) 143 | (is (= #{[:username] [:password]} 144 | (:cached-dirty-key-paths @(core/state inited-form)))) 145 | (swap! (core/data inited-form) assoc :phone-numbers [nil]) 146 | (is (= #{[:username] [:password] [:phone-numbers]} 147 | (:dirty-key-paths @(core/state inited-form)))) 148 | (core/validate! inited-form) 149 | (is (= #{[:username] [:password] [:phone-numbers 0]} 150 | (:dirty-key-paths @(core/state inited-form)) 151 | (:cached-dirty-key-paths @(core/state inited-form)))))) 152 | 153 | (deftest key-to-path [] 154 | (is (= [:foo] (util/key-to-path :foo))) 155 | (is (= [:foo :bar]) (util/key-to-path :foo.bar)) 156 | (is (= [:foo/bar] (util/key-to-path :foo/bar))) 157 | (is (= [:foo/bar] (util/key-to-path [:foo/bar]))) 158 | (is (= [:foo/bar :baz] (util/key-to-path :foo/bar.baz))) 159 | (is (= [:foo/bar :baz/qux] (util/key-to-path "foo/bar.baz/qux")))) 160 | 161 | (deftest stress-test [] 162 | (let [errors {:listOfPreferredProviderNetworks 163 | {:$errors$ {:value nil,:failed [:not-empty]}}, 164 | 165 | :healthInsuranceProvider 166 | {:$errors$ {:value nil,:failed [:not-empty]}} 167 | 168 | :email 169 | {:$errors$ {:value nil, :failed [:not-empty]}} 170 | 171 | :listInNetworkHospitals 172 | {:$errors$ {:value nil, :failed [:not-empty]}} 173 | 174 | :employerAddress2 175 | {:$errors$ {:value nil, :failed [:not-empty]}} 176 | 177 | :employerState 178 | {:$errors$ {:value nil, :failed [:not-empty]}} 179 | 180 | :employerZipCode 181 | {:$errors$ {:value nil, :failed [:not-empty]}} 182 | 183 | :otherFees 184 | {:$errors$ {:value nil, :failed [:not-empty]}} 185 | 186 | :administrationFees 187 | {:$errors$ {:value nil, :failed [:not-empty]}} 188 | 189 | :monthlyPremiumForEmployeesAndDependents 190 | {:$errors$ {:value nil,:failed [:not-empty]}} 191 | 192 | :nrEmployeesEnrolled 193 | {:$errors$ {:value nil, :failed [:not-empty]}} 194 | 195 | :employerCity 196 | {:$errors$ {:value nil, :failed [:not-empty]}} 197 | 198 | :employerPrimaryPhone 199 | {:$errors$ {:value nil, :failed [:not-empty]}} 200 | 201 | :firstName 202 | {:$errors$ {:value nil, :failed [:not-empty]}} 203 | 204 | :nrOfEmployees 205 | {:$errors$ {:value nil, :failed [:not-empty]}} 206 | 207 | :monthlyPremiumForEmployees 208 | {:$errors$ {:value nil,:failed [:not-empty]}} 209 | 210 | :employerAddress1 211 | {:$errors$ {:value nil, :failed [:not-empty]}} 212 | 213 | :lastName 214 | {:$errors$ {:value nil, :failed [:not-empty]}} 215 | 216 | :stopLossAllocation 217 | {:$errors$ {:value nil, :failed [:not-empty]}} 218 | 219 | :healthCareCost 220 | {:$errors$ {:value nil, :failed [:not-empty]}} 221 | 222 | :nameOfEmployer 223 | {:$errors$ {:value nil, :failed [:not-empty]}} 224 | 225 | :employerLocations 226 | {:$errors$ {:value nil, :failed [:not-empty]}} 227 | 228 | :nrEmployeesAndDependentsEnrolled 229 | {:$errors$ {:value nil,:failed [:not-empty]}} 230 | 231 | :employerCountry 232 | {:$errors$ {:value nil, :failed [:not-empty]}} 233 | 234 | :isConfidentialAgreementAproved 235 | {:$errors$ {:value nil,:failed [:true]}} 236 | }] 237 | (is (= [[:listOfPreferredProviderNetworks] 238 | [:healthInsuranceProvider] 239 | [:email] 240 | [:listInNetworkHospitals] 241 | [:employerAddress2] 242 | [:employerState] 243 | [:employerZipCode] 244 | [:otherFees] 245 | [:administrationFees] 246 | [:monthlyPremiumForEmployeesAndDependents] 247 | [:nrEmployeesEnrolled] 248 | [:employerCity] 249 | [:employerPrimaryPhone] 250 | [:firstName] 251 | [:nrOfEmployees] 252 | [:monthlyPremiumForEmployees] 253 | [:employerAddress1] 254 | [:lastName] 255 | [:stopLossAllocation] 256 | [:healthCareCost] 257 | [:nameOfEmployer] 258 | [:employerLocations] 259 | [:nrEmployeesAndDependentsEnrolled] 260 | [:employerCountry] 261 | [:isConfidentialAgreementAproved]] 262 | (core/errors-keypaths errors))))) 263 | -------------------------------------------------------------------------------- /test/forms/test/dirty.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.test.dirty 2 | (:require [cljs.test :refer-macros [deftest is]] 3 | [forms.dirty :refer [calculate-dirty-fields]])) 4 | 5 | (deftest dirty-fields-calculation [] 6 | (let [dirty-fields (calculate-dirty-fields 7 | {:username "retro" 8 | :password "foo" 9 | :social-networks [{:service "twitter" 10 | :test [{:aa "bb"}] 11 | :foo "bar"}]} 12 | {:username "mihaelkonjevic" 13 | :password "foo" 14 | :social-networks [{:service "twitter" 15 | :test [{:aa "bb" :cc "dd"}] 16 | :username "aaa"} 17 | {:service "facebook"}]})] 18 | (is (= dirty-fields 19 | #{[:username] [:social-networks 0 :foo] [:social-networks 0 :username] [:social-networks 0 :test 0 :cc] [:social-networks 1 :service] [:social-networks] [:social-networks 0 :test]})))) 20 | 21 | (deftest stress-test 22 | (let [f1 (map (fn [i] {:id i :name (str i "-name") :address {:street (str i "-address") :city i}}) (range 0 1002)) 23 | f2 (map (fn [i] {:id i :name (str i "-name") :address {:street (str i "-address") :city i}}) (range 1 1003)) 24 | result (calculate-dirty-fields f1 f2)] 25 | (is (= 4008 (count result))))) -------------------------------------------------------------------------------- /test/forms/test/re_frame.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.test.re-frame 2 | (:require [cljs.test :refer-macros [deftest is]] 3 | [forms.validator :as v] 4 | [forms.re-frame :as f] 5 | [day8.re-frame.test :as rf-test] 6 | [forms.test.common :refer [not-nil? not-empty? is-one? is-twitter? is-facebook?]] 7 | [re-frame.core :as rf])) 8 | 9 | (deftest errors-keypaths [] 10 | (is (= [[:user :username] [:user :social-networks 0 :network] [:user :phone-numbers 0]] 11 | (f/errors-keypaths {:user {:username :retro 12 | :social-networks [{:network :twitter}] 13 | :phone-numbers [0000]}})))) 14 | 15 | (deftest validate! 16 | (rf-test/run-test-sync 17 | (let [validator (v/validator {:username [not-nil?] 18 | :password [not-nil?]}) 19 | form-path [:my-form] 20 | form (f/constructor validator form-path) 21 | _ (form {}) 22 | errors (rf/subscribe [::f/errors form-path]) 23 | is-valid? (rf/subscribe [::f/is-valid? form-path]) 24 | valid-path-username (rf/subscribe [::f/is-valid-path? form-path :username]) 25 | valid-path-password (rf/subscribe [::f/is-valid-path? form-path :password])] 26 | (is @is-valid?) 27 | (rf/dispatch [::f/set! form-path :username "retro"]) 28 | (rf/dispatch [::f/validate! form-path]) 29 | (is (= {:password {:$errors$ {:value nil :failed [:not-nil]}}} 30 | @errors)) 31 | (is (not @is-valid?)) 32 | (is @valid-path-username) 33 | (is (not @valid-path-password))))) 34 | 35 | (deftest auto-validate! 36 | (rf-test/run-test-sync 37 | (let [validator (v/validator {:username [not-nil?] 38 | :password [not-nil?]}) 39 | form-path [:my-form :nested] 40 | form (f/constructor validator form-path) 41 | _ (form {} {:auto-validate? true}) 42 | is-valid? (rf/subscribe [::f/is-valid? form-path]) 43 | errors-for-password (rf/subscribe [::f/errors-for-path form-path :password]) 44 | form (rf/subscribe [::f/form form-path]) 45 | ] 46 | (is @is-valid?) 47 | (rf/dispatch [::f/set! form-path :username "retro"]) 48 | (is (not @is-valid?)) 49 | (is (nil? @errors-for-password)) 50 | (rf/dispatch [::f/commit! form-path]) 51 | (is (not (nil? @errors-for-password)))))) 52 | 53 | (deftest data-for-path 54 | (rf-test/run-test-sync 55 | (let [validator (v/validator {}) 56 | form-path [:my-form :nested] 57 | form (f/constructor validator form-path) 58 | _ (form {:username "foo"}) 59 | data-for-username (rf/subscribe [::f/data-for-path form-path :username])] 60 | (is (= "foo" @data-for-username)) 61 | (rf/dispatch [::f/set! form-path :username "bar"]) 62 | (is (= "bar" @data-for-username))))) 63 | 64 | (deftest update! 65 | (rf-test/run-test-sync 66 | (let [validator (v/validator {}) 67 | form-path [:my-form :nested] 68 | form (f/constructor validator form-path) 69 | _ (form {:username "foo"}) 70 | data-for-username (rf/subscribe [::f/data-for-path form-path :username])] 71 | (rf/dispatch [::f/update! form-path {:username "bar"}]) 72 | (is (= "bar" @data-for-username))))) 73 | 74 | (deftest commit! 75 | (rf-test/run-test-sync 76 | (let [commit-called (atom 0) 77 | form-path [:my-form :nested] 78 | validator (v/validator {:username [not-nil?]}) 79 | is-valid? (rf/subscribe [::f/is-valid? form-path]) 80 | on-commit (fn [f] 81 | (when (= 0 @commit-called) 82 | (is (not (f/is-valid? f)))) 83 | (when (= 1 @commit-called) 84 | (is (f/is-valid? f))) 85 | (swap! commit-called inc)) 86 | form (f/constructor validator form-path) 87 | _ (form {} {:on-commit on-commit})] 88 | (rf/dispatch [::f/commit! form-path]) 89 | (rf/dispatch [::f/set! form-path :username "foo"]) 90 | (rf/dispatch [::f/commit! form-path]) 91 | (is (= 2 @commit-called))))) 92 | 93 | (deftest errors 94 | (rf-test/run-test-sync 95 | (let [validator (v/validator {:username [not-nil?] 96 | :password [not-nil?]}) 97 | form-path [:my-form] 98 | form (f/constructor validator form-path) 99 | _ (form {} {:auto-validate? true}) 100 | errors (rf/subscribe [::f/errors form-path]) 101 | errors-for-username (rf/subscribe [::f/errors-for-path form-path :username]) 102 | errors-for-password (rf/subscribe [::f/errors-for-path form-path :password])] 103 | (is (= @errors {})) 104 | (is (= nil @errors-for-username)) 105 | (is (= nil @errors-for-password)) 106 | (rf/dispatch [::f/set! form-path :username "foo"]) 107 | (is (= @errors {:password {:$errors$ {:value nil :failed [:not-nil]}}})) 108 | (is (= nil @errors-for-password)) 109 | (rf/dispatch [::f/mark-dirty! form-path]) 110 | (is (= {:value nil :failed [:not-nil]} @errors-for-password))))) 111 | 112 | (deftest dirty-paths-valid? 113 | (rf-test/run-test-sync 114 | (let [validator (v/validator {:username [not-empty?] 115 | :password [not-empty?]}) 116 | form-path [:my-form] 117 | form (f/constructor validator form-path) 118 | _ (form {} {:auto-validate? true}) 119 | dirty-paths-valid? (rf/subscribe [::f/dirty-paths-valid? form-path])] 120 | (is @dirty-paths-valid?) 121 | (rf/dispatch [::f/set! form-path :username "foo"]) 122 | (is @dirty-paths-valid?) 123 | (rf/dispatch [::f/set! form-path :password ""]) 124 | (is (not @dirty-paths-valid?))))) 125 | 126 | (deftest dirty-paths-valid?-when-all-dirty 127 | (rf-test/run-test-sync 128 | (let [validator (v/validator {:username [not-nil?] 129 | :password [not-nil?]}) 130 | form-path [:my-form] 131 | form (f/constructor validator form-path) 132 | _ (form {}) 133 | dirty-paths-valid? (rf/subscribe [::f/dirty-paths-valid? form-path])] 134 | (is @dirty-paths-valid?) 135 | (rf/dispatch [::f/validate! form-path]) 136 | (is (not @dirty-paths-valid?))))) 137 | 138 | (deftest is-valid-path? 139 | (rf-test/run-test-sync 140 | (let [validator (v/validator {:username [not-empty?]}) 141 | form-path [:my-form] 142 | form (f/constructor validator form-path) 143 | _ (form {}) 144 | valid-path-username (rf/subscribe [::f/is-valid-path? form-path :username]) 145 | ] 146 | (is @valid-path-username) 147 | (rf/dispatch [::f/set! form-path :username ""]) 148 | (rf/dispatch [::f/validate! form-path true]) 149 | (is (not @valid-path-username))))) 150 | 151 | (deftest reset-form! 152 | (rf-test/run-test-sync 153 | (let [validator (v/validator {:username [not-nil?]}) 154 | form-path [:my-form] 155 | form (f/constructor validator form-path) 156 | _ (form {}) 157 | is-valid? (rf/subscribe [::f/is-valid? form-path]) 158 | data (rf/subscribe [::f/data form-path])] 159 | (rf/dispatch [::f/set! form-path :username nil]) 160 | (rf/dispatch [::f/validate! form-path]) 161 | (is (not @is-valid?)) 162 | (rf/dispatch [::f/reset-form! form-path]) 163 | (is (= {} @data)) 164 | (is @is-valid?) 165 | (rf/dispatch [::f/validate! form-path]) 166 | (is (not @is-valid?)) 167 | (rf/dispatch [::f/reset-form! form-path {:username "retro"}]) 168 | (is @is-valid?) 169 | (rf/dispatch [::f/validate! form-path]) 170 | (is @is-valid?)))) 171 | 172 | (deftest validate-dirty-keys-behavior 173 | (rf-test/run-test-sync 174 | (let [validator (v/validator {:username [not-empty?] 175 | :password [not-empty?] 176 | :phone-numbers.* [not-empty?]}) 177 | form-path [:my-form] 178 | form (f/constructor validator form-path) 179 | _ (form {} {:auto-validate? true}) 180 | cached-dirty-key-paths (rf/subscribe [::f/cached-dirty-key-paths form-path]) 181 | dirty-key-paths (rf/subscribe [::f/dirty-key-paths form-path]) 182 | ] 183 | (rf/dispatch [::f/set! form-path :username ""]) 184 | (rf/dispatch [::f/validate! form-path]) 185 | (is (= #{[:username] [:password]} 186 | @cached-dirty-key-paths)) 187 | (rf/dispatch [::f/set! form-path :phone-numbers [nil]]) 188 | (is (= #{[:username] [:password] [:phone-numbers]} 189 | @dirty-key-paths)) 190 | (rf/dispatch [::f/validate! form-path]) 191 | (is (= #{[:username] [:password] [:phone-numbers 0]} 192 | @dirty-key-paths 193 | @cached-dirty-key-paths))))) 194 | -------------------------------------------------------------------------------- /test/forms/test/test.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.test.test 2 | (:require [doo.runner :refer-macros [doo-tests]] 3 | [forms.test.core] 4 | [forms.test.re-frame] 5 | [forms.test.validator] 6 | [forms.test.dirty])) 7 | 8 | (doo-tests 9 | 'forms.test.core 10 | 'forms.test.re-frame 11 | 'forms.test.validator 12 | 'forms.test.dirty) -------------------------------------------------------------------------------- /test/forms/test/validator.cljs: -------------------------------------------------------------------------------- 1 | (ns forms.test.validator 2 | (:require [cljs.test :refer-macros [deftest is]] 3 | [forms.validator :as v] 4 | [forms.test.common :refer [not-nil? is-one? is-twitter? is-facebook?]])) 5 | 6 | (deftest simple-validator [] 7 | (let [validator (v/validator {:input [not-nil?]})] 8 | (is (= {:input {:$errors$ {:value nil :failed [:not-nil]}}} 9 | (validator {}))))) 10 | 11 | (deftest multiple-validators [] 12 | (let [validator (v/validator {:input [not-nil? is-one?]})] 13 | (is (= {:input {:$errors$ {:value nil :failed [:not-nil :is-one]}}} 14 | (validator {}))))) 15 | 16 | (deftest nested-path [] 17 | (let [validator (v/validator {:user.username [not-nil?] 18 | :user.foo [is-one?]})] 19 | (is (= {:user {:username {:$errors$ {:value nil :failed [:not-nil]}}}} 20 | (validator {:user {:foo 1}}))))) 21 | 22 | (deftest path-in-vector [] 23 | (let [validator (v/validator {:user.social-networks.0.service [is-twitter?] 24 | :user.username [not-nil?] 25 | :user.social-networks.1.service [is-facebook?]})] 26 | (is (= {:user {:social-networks {0 {:service {:$errors$ {:value "snapchat" :failed [:is-twitter]}}}} 27 | :username {:$errors$ {:value nil :failed [:not-nil]}}}} 28 | (validator {:user {:social-networks [{:service "snapchat"} 29 | {:service "facebook"}]}}))))) 30 | 31 | (deftest nested-in-list [] 32 | (let [validator (v/validator {:user.social-networks.*.service [not-nil?]})] 33 | (is (= {:user {:social-networks {1 {:service {:$errors$ {:value nil :failed [:not-nil]}}}}}}) 34 | (validator {:user {:social-networks [{:service "twitter"} 35 | {:service nil}]}})))) 36 | 37 | 38 | (deftest deeply-nested-lists [] 39 | (let [validator (v/validator {:user.profiles.*.social-networks.*.service [not-nil?]})] 40 | (is (= {:user {:profiles {1 {:social-networks {1 {:service {:$errors$ {:value nil :failed [:not-nil]}}}}}}}} 41 | (validator {:user {:profiles [{:social-networks [{:service "twitter"}]} 42 | {:social-networks [{:service "facebook"} 43 | {:service nil}]}]}}))))) 44 | 45 | (deftest strings-in-vector [] 46 | (let [validator (v/validator {:tags.* [not-nil?]})] 47 | (is (= {:tags {1 {:$errors$ {:value nil :failed [:not-nil]}}}} 48 | (validator {:tags ["foo" nil "bar"]}))))) 49 | 50 | (deftest comp-validators [] 51 | (let [validator-a (v/validator {:username [not-nil?]}) 52 | validator-b (v/validator {:password [not-nil?]}) 53 | composed-validators (v/comp-validators validator-a validator-b)] 54 | (is (= {:username {:$errors$ {:value nil :failed [:not-nil]}} 55 | :password {:$errors$ {:value nil :failed [:not-nil]}}} 56 | (composed-validators {:real-name "Mihael Konjevic"}))))) 57 | 58 | (deftest comp-validators-for-vectors [] 59 | (let [validator-a (v/validator {:tags.* [not-nil?]}) 60 | validator-b (v/validator {:tags.* [is-twitter?]}) 61 | composed-validators (v/comp-validators validator-a validator-b)] 62 | (is (= {:tags {1 {:$errors$ {:value nil :failed [:not-nil :is-twitter]}}}} 63 | (composed-validators {:tags ["twitter" nil]}))))) 64 | 65 | (deftest comp-validators-for-same-attr [] 66 | (let [validator-a (v/validator {:social-network [not-nil?]}) 67 | validator-b (v/validator {:social-network [is-twitter?]}) 68 | composed-validators (v/comp-validators validator-a validator-b)] 69 | (is (= {:social-network {:$errors$ {:value nil :failed [:not-nil :is-twitter]}}} 70 | (composed-validators {}))))) 71 | 72 | (deftest nested-validators [] 73 | (let [validator-a (v/validator {:username [not-nil?] 74 | :password [not-nil?]}) 75 | validator-b (v/validator {:title [not-nil?] 76 | :user.username [is-twitter?] 77 | :user [validator-a]})] 78 | (is (= {:title {:$errors$ {:value nil :failed [:not-nil]}} 79 | :user {:username {:$errors$ {:value nil :failed [:is-twitter :not-nil]}}}} 80 | (validator-b {:title nil 81 | :user {:username nil 82 | :password "foo"}}))))) 83 | 84 | (deftest validator-full-data-path [] 85 | (let [data {:username "mihael" 86 | :sites ["http://retroaktive.me"] 87 | :avatar {:url "http://example.com"} 88 | :social-networks [{:account "mihaelkonjevic" 89 | :tags [{:value "clojurescript"} {:value "clojure"}]}]} 90 | avatar-validator (v/validator {:url [[:_ (fn [value full-data path] 91 | (is (= full-data data)) 92 | (is (= path [:avatar :url])))]]}) 93 | validator (v/validator {:username 94 | [[:_ (fn [value full-data path] 95 | (is (= full-data data)) 96 | (is (= path [:username])))]] 97 | 98 | :avatar [avatar-validator] 99 | 100 | :sites.* 101 | [[:_ (fn [value full-data path] 102 | (is (= full-data data)) 103 | (is (= path [:sites 0])))]] 104 | 105 | :social-networks.*.tags.*.value 106 | [[:_ (fn [value full-data path] 107 | (is (= full-data data)) 108 | (if (= value "clojurescript") 109 | (is (= path [:social-networks 0 :tags 0 :value])) 110 | (is (= path [:social-networks 0 :tags 1 :value]))))]] 111 | 112 | :social-networks.*.account 113 | [[:_ (fn [value full-data path] 114 | (is (= full-data data)) 115 | (is (= path [:social-networks 0 :account])))]]})] 116 | (validator data))) 117 | 118 | (deftest dependant-validator [] 119 | (let [invalid-data {:email "test2@example.com" 120 | :email-confirmation "test@example.com"} 121 | ok-data {:email "test@example.com" 122 | :email-confirmation "test@example.com"} 123 | ;; This is the validator that will be applied both to the 124 | ;; email and the email-confirmation fields. 125 | confirmed-email? [:confirmed-email? (fn [value full-data path] 126 | (let [email (:email full-data) 127 | email-confirmation (:email-confirmation full-data)] 128 | (= email email-confirmation)))] 129 | validator (v/validator {:email [confirmed-email?] 130 | :email-confirmation [confirmed-email?]})] 131 | (= {:email {:$errors$ {:value "test2@example.com" :failed [:confirmed-email?]}} 132 | :email-confirmation {:$errors {:value "test@example.com" :failed [:confirmed-email?]}}} 133 | (validator invalid-data)) 134 | (= nil (validator ok-data)))) 135 | --------------------------------------------------------------------------------