Browse Source

update gundb and info

Nikolay Suslov 5 years ago
parent
commit
af4e7db671

+ 220 - 127
package-lock.json

@@ -90,26 +90,45 @@
       "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE="
     },
     "body-parser": {
-      "version": "1.18.3",
-      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
-      "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+      "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
       "requires": {
-        "bytes": "3.0.0",
+        "bytes": "3.1.0",
         "content-type": "~1.0.4",
         "debug": "2.6.9",
         "depd": "~1.1.2",
-        "http-errors": "~1.6.3",
-        "iconv-lite": "0.4.23",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
         "on-finished": "~2.3.0",
-        "qs": "6.5.2",
-        "raw-body": "2.3.3",
-        "type-is": "~1.6.16"
+        "qs": "6.7.0",
+        "raw-body": "2.4.0",
+        "type-is": "~1.6.17"
+      },
+      "dependencies": {
+        "http-errors": {
+          "version": "1.7.2",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+          "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+          "requires": {
+            "depd": "~1.1.2",
+            "inherits": "2.0.3",
+            "setprototypeof": "1.1.1",
+            "statuses": ">= 1.5.0 < 2",
+            "toidentifier": "1.0.0"
+          }
+        },
+        "setprototypeof": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+          "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+        }
       }
     },
     "bytes": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
-      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+      "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
     },
     "callsite": {
       "version": "1.0.0",
@@ -153,14 +172,14 @@
       "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y="
     },
     "colors": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz",
-      "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ=="
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
+      "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg=="
     },
     "colorspace": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.1.tgz",
-      "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz",
+      "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==",
       "requires": {
         "color": "3.0.x",
         "text-hex": "1.0.x"
@@ -182,38 +201,43 @@
       "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
     },
     "compressible": {
-      "version": "2.0.14",
-      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz",
-      "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=",
+      "version": "2.0.17",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz",
+      "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==",
       "requires": {
-        "mime-db": ">= 1.34.0 < 2"
+        "mime-db": ">= 1.40.0 < 2"
       },
       "dependencies": {
         "mime-db": {
-          "version": "1.36.0",
-          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz",
-          "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw=="
+          "version": "1.40.0",
+          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
+          "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
         }
       }
     },
     "compression": {
-      "version": "1.7.3",
-      "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
-      "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+      "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
       "requires": {
         "accepts": "~1.3.5",
         "bytes": "3.0.0",
-        "compressible": "~2.0.14",
+        "compressible": "~2.0.16",
         "debug": "2.6.9",
-        "on-headers": "~1.0.1",
+        "on-headers": "~1.0.2",
         "safe-buffer": "5.1.2",
         "vary": "~1.1.2"
       },
       "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        "bytes": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+          "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+        },
+        "on-headers": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+          "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
         }
       }
     },
@@ -343,9 +367,9 @@
       }
     },
     "env-variable": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.4.tgz",
-      "integrity": "sha512-+jpGxSWG4vr6gVxUHOc4p+ilPnql7NzZxOZBxNldsKGjCF+97df3CbuX7XMaDa5oAVkKQj4rKp38rYdC4VcpDg=="
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz",
+      "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA=="
     },
     "escape-html": {
       "version": "1.0.3",
@@ -358,13 +382,13 @@
       "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
     },
     "express": {
-      "version": "4.16.3",
-      "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
-      "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
+      "version": "4.16.4",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
+      "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
       "requires": {
         "accepts": "~1.3.5",
         "array-flatten": "1.1.1",
-        "body-parser": "1.18.2",
+        "body-parser": "1.18.3",
         "content-disposition": "0.5.2",
         "content-type": "~1.0.4",
         "cookie": "0.3.1",
@@ -381,10 +405,10 @@
         "on-finished": "~2.3.0",
         "parseurl": "~1.3.2",
         "path-to-regexp": "0.1.7",
-        "proxy-addr": "~2.0.3",
-        "qs": "6.5.1",
+        "proxy-addr": "~2.0.4",
+        "qs": "6.5.2",
         "range-parser": "~1.2.0",
-        "safe-buffer": "5.1.1",
+        "safe-buffer": "5.1.2",
         "send": "0.16.2",
         "serve-static": "1.13.2",
         "setprototypeof": "1.1.0",
@@ -395,64 +419,49 @@
       },
       "dependencies": {
         "body-parser": {
-          "version": "1.18.2",
-          "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
-          "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
+          "version": "1.18.3",
+          "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+          "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
           "requires": {
             "bytes": "3.0.0",
             "content-type": "~1.0.4",
             "debug": "2.6.9",
-            "depd": "~1.1.1",
-            "http-errors": "~1.6.2",
-            "iconv-lite": "0.4.19",
+            "depd": "~1.1.2",
+            "http-errors": "~1.6.3",
+            "iconv-lite": "0.4.23",
             "on-finished": "~2.3.0",
-            "qs": "6.5.1",
-            "raw-body": "2.3.2",
-            "type-is": "~1.6.15"
+            "qs": "6.5.2",
+            "raw-body": "2.3.3",
+            "type-is": "~1.6.16"
           }
         },
+        "bytes": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+          "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+        },
         "iconv-lite": {
-          "version": "0.4.19",
-          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
-          "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
+          "version": "0.4.23",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+          "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
         },
         "qs": {
-          "version": "6.5.1",
-          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
-          "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
+          "version": "6.5.2",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+          "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
         },
         "raw-body": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
-          "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+          "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
           "requires": {
             "bytes": "3.0.0",
-            "http-errors": "1.6.2",
-            "iconv-lite": "0.4.19",
+            "http-errors": "1.6.3",
+            "iconv-lite": "0.4.23",
             "unpipe": "1.0.0"
-          },
-          "dependencies": {
-            "depd": {
-              "version": "1.1.1",
-              "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
-              "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
-            },
-            "http-errors": {
-              "version": "1.6.2",
-              "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
-              "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
-              "requires": {
-                "depd": "1.1.1",
-                "inherits": "2.0.3",
-                "setprototypeof": "1.0.3",
-                "statuses": ">= 1.3.1 < 2"
-              }
-            },
-            "setprototypeof": {
-              "version": "1.0.3",
-              "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
-              "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
-            }
           }
         },
         "statuses": {
@@ -535,9 +544,9 @@
       }
     },
     "iconv-lite": {
-      "version": "0.4.23",
-      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
-      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
       "requires": {
         "safer-buffer": ">= 2.1.2 < 3"
       }
@@ -553,9 +562,9 @@
       "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
     },
     "ipaddr.js": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz",
-      "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
+      "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
     },
     "is-arrayish": {
       "version": "0.3.2",
@@ -573,9 +582,9 @@
       "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
     },
     "kuler": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.0.tgz",
-      "integrity": "sha512-oyy6pu/yWRjiVfCoJebNUKFL061sNtrs9ejKTbirIwY3oiHmENVCSkHhxDV85Dkm7JYR/czMCBeoM87WilTdSg==",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz",
+      "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==",
       "requires": {
         "colornames": "^1.1.1"
       }
@@ -598,6 +607,22 @@
           "version": "2.4.0",
           "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
           "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w=="
+        },
+        "winston": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/winston/-/winston-3.1.0.tgz",
+          "integrity": "sha512-FsQfEE+8YIEeuZEYhHDk5cILo1HOcWkGwvoidLrDgPog0r4bser1lEIOco2dN9zpDJ1M88hfDgZvxe5z4xNcwg==",
+          "requires": {
+            "async": "^2.6.0",
+            "diagnostics": "^1.1.1",
+            "is-stream": "^1.1.0",
+            "logform": "^1.9.1",
+            "one-time": "0.0.4",
+            "readable-stream": "^2.3.6",
+            "stack-trace": "0.0.x",
+            "triple-beam": "^1.3.0",
+            "winston-transport": "^4.2.0"
+          }
         }
       }
     },
@@ -754,18 +779,18 @@
       "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
     },
     "proxy-addr": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
-      "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==",
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
+      "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
       "requires": {
         "forwarded": "~0.1.2",
-        "ipaddr.js": "1.6.0"
+        "ipaddr.js": "1.9.0"
       }
     },
     "qs": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+      "version": "6.7.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+      "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
     },
     "range-parser": {
       "version": "1.2.0",
@@ -773,19 +798,38 @@
       "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
     },
     "raw-body": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
-      "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+      "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
       "requires": {
-        "bytes": "3.0.0",
-        "http-errors": "1.6.3",
-        "iconv-lite": "0.4.23",
+        "bytes": "3.1.0",
+        "http-errors": "1.7.2",
+        "iconv-lite": "0.4.24",
         "unpipe": "1.0.0"
+      },
+      "dependencies": {
+        "http-errors": {
+          "version": "1.7.2",
+          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+          "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+          "requires": {
+            "depd": "~1.1.2",
+            "inherits": "2.0.3",
+            "setprototypeof": "1.1.1",
+            "statuses": ">= 1.5.0 < 2",
+            "toidentifier": "1.0.0"
+          }
+        },
+        "setprototypeof": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+          "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+        }
       }
     },
     "readable-stream": {
       "version": "2.3.6",
-      "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
       "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
       "requires": {
         "core-util-is": "~1.0.0",
@@ -798,9 +842,9 @@
       }
     },
     "safe-buffer": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
     },
     "safer-buffer": {
       "version": "2.1.2",
@@ -1124,18 +1168,38 @@
       "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
       "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
     },
+    "toidentifier": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+      "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+    },
     "triple-beam": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
       "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
     },
     "type-is": {
-      "version": "1.6.16",
-      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
-      "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
+      "version": "1.6.18",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
       "requires": {
         "media-typer": "0.3.0",
-        "mime-types": "~2.1.18"
+        "mime-types": "~2.1.24"
+      },
+      "dependencies": {
+        "mime-db": {
+          "version": "1.40.0",
+          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
+          "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
+        },
+        "mime-types": {
+          "version": "2.1.24",
+          "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
+          "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+          "requires": {
+            "mime-db": "1.40.0"
+          }
+        }
       }
     },
     "unpipe": {
@@ -1159,25 +1223,54 @@
       "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
     },
     "winston": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/winston/-/winston-3.1.0.tgz",
-      "integrity": "sha512-FsQfEE+8YIEeuZEYhHDk5cILo1HOcWkGwvoidLrDgPog0r4bser1lEIOco2dN9zpDJ1M88hfDgZvxe5z4xNcwg==",
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz",
+      "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==",
       "requires": {
-        "async": "^2.6.0",
+        "async": "^2.6.1",
         "diagnostics": "^1.1.1",
         "is-stream": "^1.1.0",
-        "logform": "^1.9.1",
+        "logform": "^2.1.1",
         "one-time": "0.0.4",
-        "readable-stream": "^2.3.6",
+        "readable-stream": "^3.1.1",
         "stack-trace": "0.0.x",
         "triple-beam": "^1.3.0",
-        "winston-transport": "^4.2.0"
+        "winston-transport": "^4.3.0"
+      },
+      "dependencies": {
+        "logform": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz",
+          "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==",
+          "requires": {
+            "colors": "^1.2.1",
+            "fast-safe-stringify": "^2.0.4",
+            "fecha": "^2.3.3",
+            "ms": "^2.1.1",
+            "triple-beam": "^1.3.0"
+          }
+        },
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        },
+        "readable-stream": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz",
+          "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==",
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        }
       }
     },
     "winston-transport": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.2.0.tgz",
-      "integrity": "sha512-0R1bvFqxSlK/ZKTH86nymOuKv/cT1PQBMuDdA7k7f0S9fM44dNH6bXnuxwXPrN8lefJgtZq08BKdyZ0DZIy/rg==",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz",
+      "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==",
       "requires": {
         "readable-stream": "^2.3.6",
         "triple-beam": "^1.2.0"

+ 4 - 4
package.json

@@ -10,15 +10,15 @@
   },
   "main": "index.js",
   "dependencies": {
-    "body-parser": "^1.18.3",
-    "compression": "^1.7.3",
+    "body-parser": "^1.19.0",
+    "compression": "^1.7.4",
     "cors": "^2.8.5",
-    "express": "^4.16.3",
+    "express": "^4.16.4",
     "morgan": "^1.9.1",
     "serve-index": "^1.9.1",
     "serve-static": "^1.13.2",
     "optimist": "0.6.1",
-    "winston": "3.1.0",
+    "winston": "3.2.1",
     "socket.io": "2.2.0",
     "lcs-reflector": "0.1.0"
   },

+ 2 - 2
public/app.js

@@ -75,8 +75,8 @@ class App {
 
     const dbConnection = new Promise((resolve, reject) => {
 
-      const opt = { peers: this.dbHost, localStorage: false, store: null }
-      opt.store = RindexedDB(opt);
+      const opt = { peers: this.dbHost, localStorage: false}
+      //opt.store = RindexedDB(opt);
       this.db = Gun(opt);
 
       this.user = this.db.user();

+ 181 - 87
public/lib/gundb/axe.js

@@ -1,99 +1,193 @@
 ;(function(){
 
-  /* UNBUILD */
-  var root;
-  if(typeof window !== "undefined"){ root = window }
-  if(typeof global !== "undefined"){ root = global }
-  root = root || {};
-  var console = root.console || {log: function(){}};
-  function USE(arg, req){
-    return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
-      arg(mod = {exports: {}});
-      USE[R(path)] = mod.exports;
-    }
-    function R(p){
-      return p.split('/').slice(-1).toString().replace('.js','');
-    }
-  }
-  if(typeof module !== "undefined"){ var common = module }
-  /* UNBUILD */
+	/* UNBUILD */
+	var root;
+	if(typeof window !== "undefined"){ root = window }
+	if(typeof global !== "undefined"){ root = global }
+	root = root || {};
+	var console = root.console || {log: function(){}};
+	function USE(arg, req){
+		return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
+			arg(mod = {exports: {}});
+			USE[R(path)] = mod.exports;
+		}
+		function R(p){
+			return p.split('/').slice(-1).toString().replace('.js','');
+		}
+	}
+	if(typeof module !== "undefined"){ var common = module }
+	/* UNBUILD */
 
-  ;USE(function(module){
-    if(typeof window !== "undefined"){ module.window = window }
-    var tmp = module.window || module;
-    var AXE = tmp.AXE || function(){};
+	;USE(function(module){
+		if(typeof window !== "undefined"){ module.window = window }
+		var tmp = module.window || module;
+		var AXE = tmp.AXE || function(){};
 
-    if(AXE.window = module.window){ try{
-      AXE.window.AXE = AXE;
-      tmp = document.createEvent('CustomEvent');
-      tmp.initCustomEvent('extension', false, false, {type: "AXE"});
-      (window.dispatchEvent || window.fireEvent)(tmp);
-      window.postMessage({type: "AXE"}, '*');
-    } catch(e){} }
+		if(AXE.window = module.window){ try{
+			AXE.window.AXE = AXE;
+			tmp = document.createEvent('CustomEvent');
+			tmp.initCustomEvent('extension', false, false, {type: "AXE"});
+			(window.dispatchEvent || window.fireEvent)(tmp);
+			window.postMessage({type: "AXE"}, '*');
+		} catch(e){} }
 
-    try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
-    module.exports = AXE;
-  })(USE, './root');
+		try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
+		module.exports = AXE;
+	})(USE, './root');
   
-  ;USE(function(module){
+	;USE(function(module){
 
-    var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
-    (Gun.AXE = AXE).GUN = AXE.Gun = Gun;
+		var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
+		(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
+		Gun.on('opt', function(at){
+			if(!at.axe){
+				at.axe = {};
+				var peers = at.opt.peers, tmp;
+				// 1. If any remembered peers or from last cache or extension
+				// 2. Fallback to use hard coded peers from dApp
+				// 3. Or any offered peers.
+				//if(Gun.obj.empty(p)){
+				//  Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
+				//    p[url] = {url: url, axe: {}};
+				//  });
+				//}
+				// Our current hypothesis is that it is most optimal
+				// to take peers in a common network, and align
+				// them in a line, where you only have left and right
+				// peers, so messages propagate left and right in
+				// a linear manner with reduced overlap, and
+				// with one common superpeer (with ready failovers)
+				// in case the p2p linear latency is high.
+				// Or there could be plenty of other better options.
+				console.log("axe");
 
-    Gun.on('opt', function(at){
-      if(!at.axe){
-        at.axe = {};
-        var p = at.opt.peers, tmp;
-        // 1. If any remembered peers or from last cache or extension
-        // 2. Fallback to use hard coded peers from dApp
-        // 3. Or any offered peers.
-        //if(Gun.obj.empty(p)){
-        //  Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
-        //    p[url] = {url: url, axe: {}};
-        //  });
-        //}
-        // Our current hypothesis is that it is most optimal
-        // to take peers in a common network, and align
-        // them in a line, where you only have left and right
-        // peers, so messages propagate left and right in
-        // a linear manner with reduced overlap, and
-        // with one common superpeer (with ready failovers)
-        // in case the p2p linear latency is high.
-        // Or there could be plenty of other better options.
-        console.log("axe", at.opt);
-        if(at.opt.super){
-          function verify(msg, send, at) {
-            var peers = Object.keys(p), puts = Object.keys(msg.put), i, j, peer;
-            var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
-            for (i=0; i < peers.length; ++i) {
-              peer = p[peers[i]];
-              //if (peer.url) {console.log('AXE do not reject superpeers'); send(msg, peer); continue;} /// always send to superpeers?
-              if (!peer.id) {console.log('AXE peer without id: ', peer); continue;}
-              if (!Gun.subscribe[soul] || !Gun.subscribe[soul][peer.id]) { console.log('AXE SAY reject msg to peer: %s, soul: %s', peer.id, soul); continue; }
-              send(msg, peer);
-            }
-          }
-          AXE.say = function(msg, send, at) {
-            if (!msg.put) { send(msg); return; }
-            console.log('AXE HOOK!! ', msg);
-            verify(msg, send, at);
-          };
-          /// TODO: remove peer from all Gun.subscribe. On `mesh.bye` event?
-        }
-        if(at.opt.super){
-          at.on('in', USE('./lib/super', 1), at);
-        } else {
-          //at.on('in', input, at);
-        }
-      }
-      this.to.next(at); // make sure to call the "next" middleware adapter.
-    });
+				function verify(dht, msg, send, at) {
+					var puts = Object.keys(msg.put);
+					var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
+					var subs = dht(soul);
+// 					console.log('[AXE] VERIFY soul: %s, subs: %s, Peers: %s, msg: ', soul, subs, Object.keys(peers), msg);
+					if (!subs) { return; }
+					var tmp = [];
+					Gun.obj.map(subs.split(','), function(pid) {
+						if (pid in peers) {
+							tmp.push(pid);
+// 							console.log('[AXE] SEND TO >>>>> ', pid, msg.put.bob || msg.put);
+							send(msg, peers[pid]);
+						}
+					});
+					/// Only connected peers in the tmp array.
+					if (at.on.opt.super) {
+						dht(soul, tmp.join(','));
+					}
+				}
 
-    function input(msg){
-      var at = this.as, to = this.to;
-    }
+				var Rad = (Gun.window||{}).Radix || USE('./lib/radix', 1);
+				at.opt.dht = Rad();
+				at.on('in', input/*USE('./lib/super', 1)*/, at);
+// 				at.on('out', function(msg, a) {
+// 					this.to.next(msg);
+// 					console.log('[AXE] out:', msg, a);
+// 				}, at);
+				if(at.opt.super){
+					AXE.say = function(msg, send, at) {
+						if (msg.rtc) {
+// 							console.log('[AXE] MSG WEBRTC: ', msg.rtc);
+							if (msg.rtc.to) {
+								/// Send announce to one peer only if the msg have 'to' attr
+								var peer = (at.on.opt.peers) ? at.on.opt.peers[msg.rtc.to] : null;
+// 								if (peer) { at.on.opt.mesh.say(msg, peer); }
+								if (peer) { send(msg, peer); }
+								return;
+							}
+						}
+						if (!msg.put) { send(msg); return; }
+						//console.log('AXE HOOK!! ', msg);
+						verify(at.on.opt.dht, msg, send, at);
+					};
+				} else {
+					AXE.say = function(msg, send, at) {
+						if (msg.rtc) {
+// 							console.log('[AXE] MSG WEBRTC: ', msg.rtc);
+						}
+						if (!msg.put) { send(msg); return; }
+						verify(at.on.opt.dht, msg, send, at);
+						/// Always send to superpeers?
+						Gun.obj.map(at.on.opt.peers, function(peer) {
+							if (peer.url) {
+// 								console.log('SEND TO SUPERPEER', msg);
+								send(msg, peer);
+							}
+						});
+					};
+					var connections = 0;
+					at.on('hi', function(opt) {
+						this.to.next(opt);
+						//console.log('AXE PEER [HI]', new Date(), opt);
+						connections++;
+						/// The first connection don't need to resubscribe the nodes.
+						if (connections === 1) { return; }
+						/// Resubscribe all nodes.
+						setTimeout(function() {
+							var souls = Object.keys(at.graph);
+							for (var i=0; i < souls.length; ++i) {
+								//at.gun.get(souls[i]).off();
+								at.next[souls[i]].ack = 0;
+								at.gun.get(souls[i]).once(function(){});
+							}
+						//location.reload();
+						}, 500);
+					}, at);
+				}
+			}
+			this.to.next(at); // make sure to call the "next" middleware adapter.
+		});
+		function joindht(dht, soul, pids) {
+			if (!pids || !soul || !dht) { return; }
+			var subs = dht(soul);
+			var tmp = subs ? subs.split(',') : [];
+			Gun.obj.map(pids.split(','), function(pid) {
+				if (pid && tmp.indexOf(pid) === -1) { tmp.push(pid); }
+			});
+			tmp = tmp.join(',');
+			dht(soul, tmp);
+			return tmp;
+		}
+		function input(msg){
+// 			console.log('[AXE] input: ', msg);
+			var at = this.as, to = this.to, peer = (msg._||{}).via;
+			var opt = at.opt;
+			var dht = opt.dht;
+			var get = msg.get, soul, key;
+			if(peer && get){
+				if(soul = get['#']){
+					if(key = get['.']){
 
-    module.exports = AXE;
-  })(USE, './axe');
+					} else {
 
+					}
+					if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);}
+					var pids = joindht(dht, soul, peer.id);
+					if (pids) {
+							var dht = {};
+							dht[soul] = pids;
+							at.opt.mesh.say({dht:dht}, opt.peers[peer.id]);
+					}
+				}
+			}
+			to.next(msg);
+
+			if (opt.rtc && msg.dht) {
+				Gun.obj.map(msg.dht, function(pids, soul) {
+					dht(soul, pids);
+					Gun.obj.map(pids.split(','), function(pid) {
+						/// TODO: here we can put an algorithm of who must connect?
+						if (!pid || pid in opt.peers || pid === opt.pid || opt.announce[pid]) { return; }
+							opt.announce[pid] = true; /// To try only one connection to the same peer.
+							opt.announce(pid);
+					});
+				});
+			}
+		}
+		module.exports = AXE;
+	})(USE, './axe');
 }());

+ 83 - 61
public/lib/gundb/gun.js

@@ -38,28 +38,22 @@
 			while(l > 0){ s += c.charAt(Math.floor(Math.random() * c.length)); l-- }
 			return s;
 		}
-		Type.text.match = function(t, o){ var r = false;
-			t = t || '';
-			o = Type.text.is(o)? {'=': o} : o || {}; // {'~', '=', '*', '<', '>', '+', '-', '?', '!'} // ignore case, exactly equal, anything after, lexically larger, lexically lesser, added in, subtacted from, questionable fuzzy match, and ends with.
-			if(Type.obj.has(o,'~')){ t = t.toLowerCase(); o['='] = (o['='] || o['~']).toLowerCase() }
-			if(Type.obj.has(o,'=')){ return t === o['='] }
-			if(Type.obj.has(o,'*')){ if(t.slice(0, o['*'].length) === o['*']){ r = true; t = t.slice(o['*'].length) } else { return false }}
-			if(Type.obj.has(o,'!')){ if(t.slice(-o['!'].length) === o['!']){ r = true } else { return false }}
-			if(Type.obj.has(o,'+')){
-				if(Type.list.map(Type.list.is(o['+'])? o['+'] : [o['+']], function(m){
-					if(t.indexOf(m) >= 0){ r = true } else { return true }
-				})){ return false }
-			}
-			if(Type.obj.has(o,'-')){
-				if(Type.list.map(Type.list.is(o['-'])? o['-'] : [o['-']], function(m){
-					if(t.indexOf(m) < 0){ r = true } else { return true }
-				})){ return false }
-			}
-			if(Type.obj.has(o,'>')){ if(t > o['>']){ r = true } else { return false }}
-			if(Type.obj.has(o,'<')){ if(t < o['<']){ r = true } else { return false }}
-			function fuzzy(t,f){ var n = -1, i = 0, c; for(;c = f[i++];){ if(!~(n = t.indexOf(c, n+1))){ return false }} return true } // via http://stackoverflow.com/questions/9206013/javascript-fuzzy-search
-			if(Type.obj.has(o,'?')){ if(fuzzy(t, o['?'])){ r = true } else { return false }} // change name!
-			return r;
+		Type.text.match = function(t, o){ var tmp, u;
+			if('string' !== typeof t){ return false }
+			if('string' == typeof o){ o = {'=': o} }
+			o = o || {};
+			tmp = (o['='] || o['*'] || o['>'] || o['<']);
+			if(t === tmp){ return true }
+			if(u !== o['=']){ return false }
+			tmp = (o['*'] || o['>'] || o['<']);
+			if(t.slice(0, (tmp||'').length) === tmp){ return true }
+			if(u !== o['*']){ return false }
+			if(u !== o['>'] && u !== o['<']){
+				return (t >= o['>'] && t <= o['<'])? true : false;
+			}
+			if(u !== o['>'] && t >= o['>']){ return true }
+			if(u !== o['<'] && t <= o['<']){ return true }
+			return false;
 		}
 		Type.list = {is: function(l){ return (l instanceof Array) }}
 		Type.list.slit = Array.prototype.slice;
@@ -807,25 +801,9 @@
 			Gun.on.get = function(msg, gun){
 				var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp;
 				var next = root.next || (root.next = {}), at = next[soul];
-				if(obj_has(soul, '*')){ // TEMPORARY HACK FOR MARTTI, TESTING
-					var graph = {};
-					Gun.obj.map(root.graph, function(node, s){
-						if(Gun.text.match(s, soul)){
-							graph[s] = Gun.obj.copy(node);
-						}
-					});
-					if(!Gun.obj.empty(graph)){
-						root.on('in', {
-							'@': msg['#'],
-							how: '*',
-							put: graph,
-							$: gun
-						});
-					}
-				} // TEMPORARY HACK FOR MARTTI, TESTING
 				if(!node){ return root.on('get', msg) }
 				if(has){
-					if(!obj_has(node, has)){ return root.on('get', msg) }
+					if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) }
 					node = Gun.state.to(node, has);
 					// If we have a key in-memory, do we really need to fetch?
 					// Maybe... in case the in-memory key we have is a local write
@@ -966,6 +944,7 @@
 					at.on('in', at);
 					return;
 				}*/
+				if(at.lex){ msg.get = obj_to(at.lex, msg.get) }
 				if(get['#'] || at.soul){
 					get['#'] = get['#'] || at.soul;
 					msg['#'] || (msg['#'] = text_rand(9));
@@ -979,7 +958,7 @@
 						if(tmp){ return }
 						msg.$ = back.$;
 					} else
-					if(obj_has(back.put, get)){
+					if(obj_has(back.put, get)){ // TODO: support #LEX !
 						put = (back.$.get(get)._);
 						if(!(tmp = put.ack)){ put.ack = -1 }
 						back.on('in', {
@@ -988,6 +967,17 @@
 							get: back.get
 						});
 						if(tmp){ return }
+					} else
+					if('string' != typeof get){
+						var put = {}, meta = (back.put||{})._;
+						Gun.obj.map(back.put, function(v,k){
+							if(!Gun.text.match(k, get)){ return }
+							put[k] = v;
+						})
+						if(!Gun.obj.empty(put)){
+							put._ = meta;
+							back.on('in', {$: back.$, put: put, get: back.get})
+						}
 					}
 					root.ask(ack, msg);
 					return root.on('in', msg);
@@ -1087,7 +1077,6 @@
 			relate(cat, msg, at, rel);
 			echo(cat, msg, eve);
 		}
-		var C = 0;
 
 		function relate(at, msg, from, rel){
 			if(!rel || node_ === at.get){ return }
@@ -1189,21 +1178,23 @@
 			});
 		}
 		function ask(at, soul){
-			var tmp = (at.root.$.get(soul)._);
-			if(at.ack){
-				tmp.on('out', {get: {'#': soul}});
+			var tmp = (at.root.$.get(soul)._), lex = at.lex;
+			if(at.ack || lex){
+				(lex = lex||{})['#'] = soul;
+				tmp.on('out', {get: lex});
 				if(!at.ask){ return } // TODO: PERFORMANCE? More elegant way?
 			}
 			tmp = at.ask; Gun.obj.del(at, 'ask');
 			obj_map(tmp || at.next, function(neat, key){
-				neat.on('out', {get: {'#': soul, '.': key}});
+				var lex = neat.lex || {}; lex['#'] = soul; lex['.'] = lex['.'] || key;
+				neat.on('out', {get: lex});
 			});
 			Gun.obj.del(at, 'ask'); // TODO: PERFORMANCE? More elegant way?
 		}
 		function ack(msg, ev){
 			var as = this.as, get = as.get || empty, at = as.$._, tmp = (msg.put||empty)[get['#']];
 			if(at.ack){ at.ack = (at.ack + 1) || 1; }
-			if(!msg.put || (get['.'] && !obj_has(tmp, at.get))){
+			if(!msg.put || ('string' == typeof get['.'] && !obj_has(tmp, at.get))){
 				if(at.put !== u){ return }
 				at.on('in', {
 					get: at.get,
@@ -1259,12 +1250,18 @@
 			} else
 			if(tmp = rel.is(key)){
 				return this.get(tmp, cb, as);
+			} else
+			if(obj.is(key)){
+				gun = this;
+				if(tmp = ((tmp = key['#'])||empty)['='] || tmp){ gun = gun.get(tmp) }
+				gun._.lex = key;
+				return gun;
 			} else {
 				(as = this.chain())._.err = {err: Gun.log('Invalid get request!', key)}; // CLEAN UP
 				if(cb){ cb.call(as, as._.err) }
 				return as;
 			}
-			if(tmp = cat.stun){ // TODO: Refactor?
+			if(tmp = this._.stun){ // TODO: Refactor?
 				gun._.stun = gun._.stun || tmp;
 			}
 			if(cb && cb instanceof Function){
@@ -1359,7 +1356,18 @@
 			// #soul.has=value>state
 			// ~who#where.where=what>when@was
 			// TODO: BUG! Put probably cannot handle plural chains!
-			var gun = this, at = (gun._), root = at.root.$, tmp;
+			var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp;
+			if(!ctx.puta){ if(tmp = ctx.puts){ if(tmp > M){ // without this, when synchronous, writes to a 'not found' pile up, when 'not found' resolves it recursively calls `put` which incrementally resolves each write. Stack overflow limits can be as low as 10K, so this limit is hardcoded to 1% of 10K.
+				(ctx.stack || (ctx.stack = [])).push([gun, data, cb, as]);
+				if(ctx.puto){ return }
+				ctx.puto = setTimeout(function drain(){
+					var d = ctx.stack.splice(0,M), i = 0, at; ctx.puta = true;
+					while(at = d[i++]){ at[0].put(at[1], at[2], at[3]) } delete ctx.puta;
+					if(ctx.stack.length){ return ctx.puto = setTimeout(drain, 0) }
+					ctx.stack = ctx.puts = ctx.puto = null;
+				}, 0);
+				return gun;
+			} ++ctx.puts } else { ctx.puts = 1 } }
 			as = as || {};
 			as.data = data;
 			as.via = as.$ = as.via || as.$ || gun;
@@ -1392,7 +1400,7 @@
 			}
 			if(Gun.is(data)){
 				data.get(function(soul, o, msg){
-					if(!soul && Gun.val.is(msg.put)){
+					if(!soul){
 						return Gun.log("The reference you are saving is a", typeof msg.put, '"'+ msg.put +'", not a node (object)!');
 					}
 					gun.put(Gun.val.link.ify(soul), cb, as);
@@ -1454,7 +1462,9 @@
 					if(!ack.lack){ this.off() } // One response is good enough for us currently. Later we may want to adjust this.
 					if(!as.ack){ return }
 					as.ack(ack, this);
+					//--C;
 				}, as.opt);
+				//C++;
 				// NOW is a hack to get synchronous replies to correctly call.
 				// and STOP is a hack to get async behavior to correctly call.
 				// neither of these are ideal, need to be fixed without hacks,
@@ -1469,6 +1479,7 @@
 			}, as);
 			if(as.res){ as.res() }
 		} function no(v,k){ if(v){ return true } }
+		//console.debug(999,1); var C = 0; setInterval(function(){ try{ debug.innerHTML = C }catch(e){console.log(e)} }, 500);
 
 		function map(v,k,n, at){ var as = this;
 			var is = Gun.is(v);
@@ -1657,6 +1668,7 @@
 		}
 
 		function val(msg, eve, to){
+			if(!msg.$){ eve.off(); return }
 			var opt = this.as, cat = opt.at, gun = msg.$, at = gun._, data = at.put || msg.put, link, tmp;
 			if(tmp = msg.$$){
 				link = tmp = (msg.$$._);
@@ -1748,8 +1760,9 @@
 		}
 		function each(v,k){
 			if(n_ === k){ return }
-			var msg = this.msg, gun = msg.$, at = this.at, tmp = (gun.get(k)._);
-			(tmp.echo || (tmp.echo = {}))[at.id] = tmp.echo[at.id] || at;
+			var msg = this.msg, gun = msg.$, at = gun._, cat = this.at, tmp = at.lex;
+			if(tmp && !Gun.text.match(k, tmp['.'] || tmp['#'] || tmp)){ return } // review?
+			((tmp = gun.get(k)._).echo || (tmp.echo = {}))[cat.id] = tmp.echo[cat.id] || cat;
 		}
 		var obj_map = Gun.obj.map, noop = function(){}, event = {stun: noop, off: noop}, n_ = Gun.node._, u;
 	})(USE, './map');
@@ -1924,6 +1937,7 @@
 	})(USE, './adapters/localStorage');
 
 	;USE(function(module){
+		var Gun = USE('../index');
 		var Type = USE('../type');
 
 		function Mesh(ctx){
@@ -1945,7 +1959,7 @@
 					return;
 				}
 				// add hook for AXE?
-				//if (Gun.AXE && opt && opt.super) { Gun.AXE.say(msg, mesh.say, this); return; } // rogowski
+				if (Gun.AXE) { Gun.AXE.say(msg, mesh.say, this); return; }
 				mesh.say(msg);
 			}
 
@@ -1959,9 +1973,8 @@
 				if(!raw){ return }
 				var dup = ctx.dup, id, hash, msg, tmp = raw[0];
 				if(opt.pack <= raw.length){ return mesh.say({dam: '!', err: "Message too big!"}, peer) }
-				try{msg = JSON.parse(raw);
-				}catch(e){opt.log('DAM JSON parse error', e)}
 				if('{' === tmp){
+					try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
 					if(!msg){ return }
 					if(dup.check(id = msg['#'])){ return }
 					dup.track(id, true).it = msg; // GUN core also dedups, so `true` is needed.
@@ -1974,7 +1987,7 @@
 					}
 					(msg._ = function(){}).via = peer;
 					if((tmp = msg['><'])){
-						(msg._).to = Type.obj.map(tmp.split(','), function(k,i,m){m(k,true)});
+						(msg._).to = Type.obj.map(tmp.split(','), tomap);
 					}
 					if(msg.dam){
 						if(tmp = mesh.hear[msg.dam]){
@@ -1987,6 +2000,7 @@
 					return;
 				} else
 				if('[' === tmp){
+					try{msg = JSON.parse(raw);}catch(e){opt.log('DAM JSON parse error', e)}
 					if(!msg){ return }
 					var i = 0, m;
 					while(m = msg[i++]){
@@ -1996,6 +2010,7 @@
 					return;
 				}
 			}
+			var tomap = function(k,i,m){m(k,true)};
 
 			;(function(){
 				mesh.say = function(msg, peer, o){
@@ -2046,11 +2061,11 @@
 				function send(raw, peer){
 					var wire = peer.wire;
 					try{
-						if(wire.send){
-							wire.send(raw);
-						} else
 						if(peer.say){
 							peer.say(raw);
+						} else
+						if(wire.send){
+							wire.send(raw);
 						}
 					}catch(e){
 						(peer.queue = peer.queue || []).push(raw);
@@ -2118,6 +2133,7 @@
 					mesh.say({dam: '?'}, opt.peers[tmp] = peer);
 				}
 				if(!tmp.hied){ ctx.on(tmp.hied = 'hi', peer) }
+				// @rogowski I need this here by default for now to fix go1dfish's bug
 				tmp = peer.queue; peer.queue = [];
 				Type.obj.map(tmp, function(msg){
 					mesh.say(msg, peer);
@@ -2127,14 +2143,20 @@
 				Type.obj.del(opt.peers, peer.id); // assume if peer.url then reconnect
 				ctx.on('bye', peer);
 			}
-
 			mesh.hear['!'] = function(msg, peer){ opt.log('Error:', msg.err) }
 			mesh.hear['?'] = function(msg, peer){
-				if(!msg.pid){ return mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer) }
+				if(!msg.pid){
+					mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer);
+					// @rogowski I want to re-enable this AXE logic with some fix/merge later.
+					// var tmp = peer.queue; peer.queue = [];
+					// Type.obj.map(tmp, function(msg){
+					//	mesh.say(msg, peer);
+					// });
+					return;
+				}
 				peer.id = peer.id || msg.pid;
 				mesh.hi(peer);
 			}
-
 			return mesh;
 		}
 

File diff suppressed because it is too large
+ 0 - 0
public/lib/gundb/gun.min.js


+ 2 - 2
public/lib/gundb/lib/bye.js

@@ -5,8 +5,8 @@ Gun.on('opt', function(root){
 	if(root.once){ return }
 	root.on('in', function(msg){
 		//Msg did not have a peer property saved before, so nothing ever went further
-		if(!msg.mesh || !msg.BYE){ return this.to.next(msg) }
-		var peer = msg.mesh.via;
+		if(!msg._ || !msg.BYE){ return this.to.next(msg) }
+		var peer = msg._.via;
 		(peer.bye = peer.bye || []).push(msg.BYE);
 	})
 	root.on('bye', function(peer){

+ 18 - 0
public/lib/gundb/lib/fsrm.js

@@ -0,0 +1,18 @@
+var fs = require('fs');
+var nodePath = require('path');
+
+var dir = __dirname + '/../';
+
+module.exports = function rm(path, full) {
+	path = full || nodePath.join(dir, path);
+  if(!fs.existsSync(path)){ return }
+  fs.readdirSync(path).forEach(function(file,index){
+    var curPath = path + "/" + file;
+    if(fs.lstatSync(curPath).isDirectory()) { // recurse
+      rm(null, curPath);
+    } else { // delete file
+      fs.unlinkSync(curPath);
+    }
+  });
+  fs.rmdirSync(path);
+};

+ 5 - 0
public/lib/gundb/lib/hub.js

@@ -0,0 +1,5 @@
+var fs = require('fs');
+
+fs.watch('.', {persistent: false, recursive: true}, function(eve, name){
+	console.log("changed!", eve, name);
+})

+ 46 - 0
public/lib/gundb/lib/ipfs.js

@@ -0,0 +1,46 @@
+console.log("IPFS PLUGIN NOT OFFICIALLY MAINTAINED! PROBABLY WON'T WORK! USE AT YOUR OWN RISK! PLEASE CONTRIBUTE FIXES!");
+var opt = gun._.opt, u;
+if (u === opt.ipfs.directory) {
+  opt.ipfs.directory = '/gun';
+}
+opt.store = {};
+opt.store.put = function(file, data, cb){
+  var uri = opt.ipfs.directory + '/' + file;
+  opt.ipfs.instance.files.write(uri, Buffer.from(JSON.stringify(data)), {create:true})
+  .then(res => {
+    console.log('File written to IPFS directory', uri, res);
+    return opt.ipfs.instance.files.stat(opt.ipfs.directory, {hash:true});
+  }).then(res => {
+    console.log('Directory hash:', res.hash);
+    return opt.ipfs.instance.name.publish(res.hash);
+    // currently throws "This command must be run in online mode. Try running 'ipfs daemon' first." for some reason, maybe js-ipfs IPNS not ready yet
+  }).then(res => {
+    console.log('IPFS put request successful:', res);
+    cb(undefined, 1);
+  }).catch(error => {
+    console.error('IPFS put request failed', error);
+  });
+}
+opt.store.get = function(file, cb){
+    var uri = opt.ipfs.directory + '/' + file;
+    opt.ipfs.instance.files.read(uri, {})
+    .then(res => {
+      var data = JSON.parse(res.toString());
+      console.log(uri + ' was loaded from ipfs:', data);
+      cb(data);
+    });
+}
+opt.store.list = function(cb){
+    var stream = opt.ipfs.files.lsReadableStream(opt.ipfs.directory);
+
+    stream.on('data', (file) => {
+      console.log('ls', file.name);
+      if (cb(file.name)) {
+        stream.destroy();
+      }
+    });
+
+    stream.on('finish', () => {
+      cb();
+    });
+}

+ 3 - 2
public/lib/gundb/lib/les.js

@@ -71,7 +71,7 @@
 		const gc_enable = root.opt.gc_enable ? root.opt.gc_enable : true;
 		const gc_delay = root.opt.gc_delay ? root.opt.gc_delay : 1000;
 		
-		const gc_info_enable  = root.opt.gc_info_enable  ? root.opt.gc_info_enable  : true;
+		const gc_info_enable  = ("gc_info_enable" in root.opt) ? root.opt.gc_info_enable  : true;
 		const gc_info  = root.opt.gc_info  ? root.opt.gc_info  : 5000;
 		const gc_info_mini = root.opt.gc_info_mini ? root.opt.gc_info_mini : false;
 		
@@ -127,6 +127,7 @@
 		
 		//Executed every time a node gets modified
 		root.on("put", function(e) {
+			this.to.next(e);
 			var ctime = Date.now();
 			var souls = Object.keys(e.put || empty); // get all of the nodes in the update
 			for (var i = 0; i < souls.length; i++) { // iterate over them and add them
@@ -219,4 +220,4 @@
 			return Number(Math.round(value + 'e' + decimals) + 'e-' + decimals);
 		}
 	});
-}());
+}());

+ 25 - 0
public/lib/gundb/lib/match.js

@@ -0,0 +1,25 @@
+var Type = require('../src/type');
+function match(t, o){ var r = false;
+	t = t || '';
+	o = Type.text.is(o)? {'=': o} : o || {}; // {'~', '=', '*', '<', '>', '+', '-', '?', '!'} // ignore case, exactly equal, anything after, lexically larger, lexically lesser, added in, subtacted from, questionable fuzzy match, and ends with.
+	if(Type.obj.has(o,'~')){ t = t.toLowerCase(); o['='] = (o['='] || o['~']).toLowerCase() }
+	if(Type.obj.has(o,'=')){ return t === o['='] }
+	if(Type.obj.has(o,'*')){ if(t.slice(0, o['*'].length) === o['*']){ r = true; t = t.slice(o['*'].length) } else { return false }}
+	if(Type.obj.has(o,'!')){ if(t.slice(-o['!'].length) === o['!']){ r = true } else { return false }}
+	if(Type.obj.has(o,'+')){
+		if(Type.list.map(Type.list.is(o['+'])? o['+'] : [o['+']], function(m){
+			if(t.indexOf(m) >= 0){ r = true } else { return true }
+		})){ return false }
+	}
+	if(Type.obj.has(o,'-')){
+		if(Type.list.map(Type.list.is(o['-'])? o['-'] : [o['-']], function(m){
+			if(t.indexOf(m) < 0){ r = true } else { return true }
+		})){ return false }
+	}
+	if(Type.obj.has(o,'>')){ if(t > o['>']){ r = true } else { return false }}
+	if(Type.obj.has(o,'<')){ if(t < o['<']){ r = true } else { return false }}
+	function fuzzy(t,f){ var n = -1, i = 0, c; for(;c = f[i++];){ if(!~(n = t.indexOf(c, n+1))){ return false }} return true } // via http://stackoverflow.com/questions/9206013/javascript-fuzzy-search
+	if(Type.obj.has(o,'?')){ if(fuzzy(t, o['?'])){ r = true } else { return false }} // change name!
+	return r;
+}
+module.exports = match;

+ 216 - 194
public/lib/gundb/lib/meta.js

@@ -1,73 +1,72 @@
 $(function(){
-	var m = window.meta = {edit:[], os:{}}, ua = '', u;
-	try{ua = navigator.userAgent.toLowerCase()}catch(e){}
-	m.os.is = {
-		win: (ua.search("win") >= 0)? "windows":false,
-		lin: (ua.search("linux") >= 0)? "linux":false,
-		mac: (ua.search("mac") >= 0)? "macintosh":false,
-		and: (ua.search("android") >= 0)? "android":false,
-		ios: (ua.search('ipod') >= 0 
-			|| ua.search('iphone') >= 0 
-			|| ua.search('ipad') >= 0)? "ios":false
-	}
-	var k = m.key = {ctrl: 17, cmd: 91};
-	k.meta = (m.os.is.win||m.os.is.lin||m.os.is.and)? k.ctrl : k.cmd;
+	var noop = function(){}, u;
+	var m = window.meta = {edit:[]};
+	var k = m.key = {};
+	k.meta = {17:17, 91:17, 93:17, 224:17};
 	k.down = function(eve){
-		if($(eve.target).is('input') || eve.repeat){ return }
-		(k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
-		if(!eve.fake && eve.which === k.last){ return }
-		if(k.meta === (k.last = eve.which)){ k.down.meta = m.flip(k.wipe()) || true }
-		if(m.flip.is()){
-			(k.combo || (k.combo = [])).push(eve.which);
-			m.check('on', eve.which, k.at || (k.at = m.edit));
+		if(eve.repeat){ return }
+		var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
+		if(!eve.fake && key === k.last){ return } k.last = key;
+		if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
+			if(k.meta[key]){ k.down.meta = key = -1 }
+			if(!k.down.meta){ return }
+		}
+		(k.combo || (k.combo = [])).push(key);
+		m.check('on', key, k.at || (k.at = m.edit));
+		if(k.meta[key]){
+			m.list(k.at.back || m.edit);
+			if(k.at && !k.at.back){ m.flip() }
 		}
-		if(eve.metaKey && (k.meta !== eve.which)){ k.up(eve) } // on some systems, meta hijacks keyup
 	}
 	k.up = function(eve){ var tmp;
-		if($(eve.target).is('input')){ return }
-		k.eve = m.eve = eve;
+		var key = (k.eve = m.eve = eve).which = eve.which || eve.fake || eve.keyCode;
+		if(!eve.fake && $(eve.target).closest('input, textarea, [contenteditable=true]').length){
+			if(k.meta[key]){
+				k.down.meta = null;
+				key = -1;
+			} else
+			if(!k.down.meta){ return }
+		}
 		k.last = null;
-		eve.which = eve.which || eve.fake || eve.keyCode;
-		if(m.flip.is()){ m.check('up', eve.which) }
-		if(tmp = (k.meta === eve.which)){ k.down.meta = false }
-		if(tmp && k.at === m.edit){ k.wipe() }
-		if(27 === eve.which){ return m.flip(false) }
+		if($(':focus').closest('#meta').length){ return }
+		m.check('up', key);
+		if(-1 === key || 27 === eve.which){ k.wipe() }
 	}
-	m.flip = function(tmp, aid){
-		if(aid){
-			m.flip.aid = true;
-			setTimeout(function(){$(document).one('click',function(eve){m.flip(m.flip.aid = false)})},250); // ugly but important for visual aid.
-		}
+	m.flip = function(tmp){
 		var board = $('#meta .meta-menu');
 		((tmp === false) || (!tmp && board.is(':visible')))? 
 			board.addClass('meta-none')
 		: board.removeClass('meta-none');
 	}
 	m.flip.is = function(){
-		if(m.flip.aid && ((m.eve||{}).fake || k.at !== m.edit)){ m.flip.aid = false }
-		return !m.flip.aid && $('#meta .meta-menu').is(':visible');
+		return $('#meta .meta-menu').is(':visible');
 	}
 	m.flip.wait = 500;
 	m.check = function(how, key, at){
 		at = k.at || m.edit;
-		//m.list(at);
-		var edit = at[key], tmp;
+		var edit = at[key];
 		if(!edit){ return }
-		if(k.eve && k.eve.preventDefault){ k.eve.preventDefault() }
+		var tmp = k.eve || noop;
+		if(tmp.preventDefault){ tmp.preventDefault() }
 		if(edit[how]){
-			edit[how](m.eve);
-			if(k.at !== m.edit && 'up' === how){
-				if(k.down.meta){ m.list(k.at = m.edit) }
-				else { k.wipe() }
+			if(tmp.fake && !edit.fake){
+				m.tap.edit = edit;
+			} else {
+				edit[how](m.eve);
+				/*if(k.at !== m.edit && 'up' === how){
+					if(k.down.meta){ m.list(k.at = m.edit) }
+					else { k.wipe() }
+				}*/
 			}
 		}
 		if('up' != how){ return }
-		edit.back = at;
-		m.list(edit, at);
+		if(at != edit){ edit.back = at }
+		m.list(edit, true);
 	}
-	m.list = function(at){
+	m.list = function(at, opt){
+		if(!at){ return m.flip(false) }
 		var l = [];
-		$.each(at, function(i,k){ 'back' != i && k.combo && l.push(k) });
+		$.each(at, function(i,k){ 'back' != i && k.combo && k.name && l.push(k) });
 		if(!l.length){ return }
 		k.at = at;
 		l = l.sort(function(a,b){
@@ -82,8 +81,10 @@ $(function(){
 		$.each(l, function(i, k){
 			$ul.append($('<li>').text(k.name));
 		});
-		if(!at.back){ return }
-		$ul.append($('<li>').html('&larr;').one('click', function(){ m.list(k.at = at.back) }));
+		if(opt){ m.flip(true) }
+		$ul.append($('<li>').html('&larr;').one('click', function(){
+			m.list(k.at = at.back);
+		}));
 	}
 	m.ask = function(help, cb){
 		var $ul = $('#meta .meta-menu ul').empty();
@@ -96,34 +97,33 @@ $(function(){
 		});
 		var $li = $('<li>').append($form);
 		$ul.append($li);
+		m.flip(true);
 		$put.focus();
 	}
-	k.wipe = function(){
+	k.wipe = function(opt){
+		k.down.meta = false;
 		k.combo = [];
-		m.flip(false);
-		m.flip.aid = false;
+		if(!opt){ m.flip(false) }
 		m.list(k.at = m.edit);
 	};
-	$(document).on('keydown', k.down).on('keyup', k.up);
-	m.tap = {};
-	m.tap.select = function(eve){
-		m.tap.range = null;
-		if(!(m.tap.text()||'').trim()){
-			if(m.tap.was){
-				m.tap.was = null;
-				m.flip(false);
-			}
-			return;
-		}
-		m.flip(m.tap.range = monotype((eve||{}).target), m.tap.was = true);
-	}
-	m.tap.text = function(tmp){
-		return ((tmp = window.getSelection) && tmp().toString()) ||
-			((tmp = document.selection) && tmp.createRange().text) || '';
+	m.tap = function(){
+		var on = $('.meta-on')
+			.or($($(document.querySelectorAll(':hover')).get().reverse()).first())
+			.or($(document.elementFromPoint(meta.tap.x, meta.tap.y)));
+		return on;
 	}
 	$(window).on('blur', k.wipe).on('focus', k.wipe);
-	$(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.tap.select);
-	//.on('keydown', '[contenteditable=true]', function(e){});
+	$(document).on('mousedown mousemove mouseup', function(eve){
+		m.tap.eve = eve;
+		m.tap.x = eve.pageX||0;
+		m.tap.y = eve.pageY||0;
+		m.tap.on = $(eve.target);
+	}).on('mousedown touchstart', function(eve){
+		var tmp = m.tap.edit;
+		if(!tmp || !tmp.on){ return }
+		tmp.on(eve);
+		m.tap.edit = null;
+	});
 	$(document).on('touchstart', '#meta .meta-start', function(eve){ m.tap.stun = true });
 	$(document).on('click', '#meta .meta-menu li', function(eve){
 		if(m.tap.stun){ return m.tap.stun = false }
@@ -132,10 +132,11 @@ $(function(){
 		k.down(eve);
 		k.up(eve);
 	});
+	$(document).on('keydown', k.down).on('keyup', k.up);
 	meta.edit = function(edit){
 		var tmp = edit.combow = [];
 		$.each(edit.combo || (edit.combo = []), function(i,k){
-			if(!k || !k.length){ return }
+			if(!k || !k.length){ if('number' == typeof k){ tmp.push(k) } return }
 			tmp.push(k.toUpperCase().charCodeAt(0));
 		});
 		var at = meta.edit, l = edit.combo.length;
@@ -143,37 +144,7 @@ $(function(){
 		edit.combow = edit.combow.join(',');
 		m.list(meta.edit);
 	}
-	meta.text = {zws: '&#8203;'};
-	meta.text.editor = function(opt, as){ var tmp;
-		if(!opt){ return }
-		opt = (typeof opt == 'string')? {edit: opt} : opt.tag? opt : {tag: opt};
-		var r = opt.range = opt.range || m.tap.range || monotype(), cmd = opt.edit;
-		as = opt.as = opt.as || as;
-		if(cmd && document.execCommand){
-			r.restore();
-			if(document.execCommand(cmd, null, as||null)){ return }
-		}
-		if(!opt.tag){ return }
-		opt.tag = $(opt.tag);
-		opt.name = opt.name || opt.tag.prop('tagName');
-		if((tmp = $(r.get()).closest(opt.name)).length){
-			if(r.s === r.e){
-				tmp.after(meta.text.zws);
-				r = r.select(monotype.next(tmp[0]),1);
-			} else {
-				tmp.contents().unwrap(opt.name);
-			}
-		} else
-		if(r.s === r.e){
-			r.insert(opt.tag);
-			r = r.select(opt.tag);
-		} else {
-			r.wrap(opt.tag);
-		}
-		r.restore();
-		opt.range = null;
-		if(m.tap.range){ m.tap.range = monotype() }
-	}
+	$.fn.or = function(s){ return this.length ? this : $(s||'body') };
 	;(function(){try{
 		/* UI */
 		if(meta.css){ return }
@@ -199,10 +170,12 @@ $(function(){
 				width: '2em',
 				height: '2em',
 				opacity: 0.7,
+				outline: 'none',
 				color: '#000044',
 				overflow: 'visible',
 				transition: 'all 0.2s ease-in'
 			},
+			'#meta *': {outline: 'none'},
 			'#meta .meta-none': {display: 'none'},
 			'#meta span': {'line-height': '2em'},
 			'#meta .meta-menu': {
@@ -263,98 +236,147 @@ $(function(){
 		}
 	}catch(e){}}());
 	;(function(){
-	// on fires when shortcut keydowns or on touch after command selected and then touchdown
-	meta.edit({
-		name: "Bold",
-		combo: ['B'],
-		on: function(e){
-			meta.text.editor('bold');
-		},
-		up: function(){}
-	});
-	meta.edit({
-		name: "Italic",
-		combo: ['I'],
-		on: function(e){
-			meta.text.editor('italic');
-		},
-		up: function(){}
-	});
-	meta.edit({
-		name: "Underline",
-		combo: ['U'],
-		on: function(e){
-			meta.text.editor('underline');
-		},
-		up: function(){}
-	});
-	meta.edit({
-		name: "linK",
-		combo: ['K'],
-		up: function(e){
-			var range = meta.tap.range || monotype();
-			meta.ask('Paste or type link...', function(url){
-				meta.text.editor({tag: $('<a href="'+url+'">link</a>'), edit: url? 'createLink' : 'unlink', as: url, range: range});
-			})
-		},
-		on: function(){}
-	});
-	meta.edit({name: "aliGn", combo: ['G']});
-	meta.edit({
-		name: "Left",
-		combo: ['G','L'],
-		on: function(e){ meta.text.editor('justifyLeft') },
-		up: function(){}
-	});
-	meta.edit({
-		name: "Right",
-		combo: ['G','R'],
-		on: function(e){ meta.text.editor('justifyRight') },
-		up: function(){ }
-	});
-	meta.edit({
-		name: "Middle",
-		combo: ['G','M'],
-		on: function(e){ meta.text.editor('justifyCenter') },
-		up: function(){ }
-	});
-	meta.edit({
-		name: "Justify",
-		combo: ['G','J'],
-		on: function(e){ meta.text.editor('justifyFull') },
-		up: function(){}
-	});
-	// Align Number
-	// Align Points
-	// Align Strike
-	meta.edit({name: "Size", combo: ['S']});
-	meta.edit({
-		name: "Small",
-		combo: ['S','S'],
-		on: function(e){ meta.text.editor('fontSize', 2) },
-		up: function(){ }
-	});
-	meta.edit({
-		name: "Normal",
-		combo: ['S','N'],
-		on: function(e){ meta.text.editor('fontSize', 5) },
-		up: function(){}
-	});
-	meta.edit({
-		name: "Header",
-		combo: ['S','H'],
-		on: function(e){ meta.text.editor('fontSize', 6) },
-		up: function(){}
-	});
-	meta.edit({
-		name: "Title",
-		combo: ['S','T'],
-		on: function(e){ meta.text.editor('fontSize', 7) },
-		up: function(){}
-	});
-	// Size Spacing
-	// Size Super
-	// Size Sub
-	meta.edit({name: "Edit", combo: ['E']});
+		// include basic text editing by default.
+		var monotype = window.monotype || function(){console.log("monotype needed")};
+		var m = meta;
+		m.text = {zws: '&#8203;'};
+		m.text.on = function(eve){ var tmp;
+			if($((eve||{}).target).closest('#meta').length){ return }
+			m.text.range = null;
+			if(!(m.text.copy()||'').trim()){
+				m.flip(false);
+				m.list(m.text.it);
+				return;
+			}
+			m.text.range = monotype((eve||{}).target);
+			m.text.it.on(eve);
+		}
+		m.text.copy = function(tmp){
+			return ((tmp = window.getSelection) && tmp().toString()) ||
+				((tmp = document.selection) && tmp.createRange().text) || '';
+		}
+		$(document).on('select contextmenu keyup mouseup', '[contenteditable=true]', m.text.on);
+		m.text.editor = function(opt, as){ var tmp;
+			if(!opt){ return }
+			opt = (typeof opt == 'string')? {edit: opt} : opt.tag? opt : {tag: opt};
+			var r = opt.range = opt.range || m.text.range || monotype(), cmd = opt.edit;
+			as = opt.as = opt.as || as;
+			if(cmd && document.execCommand){
+				r.restore();
+				if(document.execCommand(cmd, null, as||null)){
+					if(m.text.range){ m.text.range = monotype() }
+					return;
+				}
+			}
+			if(!opt.tag){ return }
+			opt.tag = $(opt.tag);
+			opt.name = opt.name || opt.tag.prop('tagName');
+			if((tmp = $(r.get()).closest(opt.name)).length){
+				if(r.s === r.e){
+					tmp.after(m.text.zws);
+					r = r.select(monotype.next(tmp[0]),1);
+				} else {
+					tmp.contents().unwrap(opt.name);
+				}
+			} else
+			if(r.s === r.e){
+				r.insert(opt.tag);
+				r = r.select(opt.tag);
+			} else {
+				r.wrap(opt.tag);
+			}
+			r.restore();
+			opt.range = null;
+			if(m.text.range){ m.text.range = monotype() }
+		}
+		meta.edit(meta.text.it = {combo: [-1], on: function(){ m.list(this, true) }, back: meta.edit}); // -1 is key for typing.
+		meta.text.it[-1] = meta.text.it;
+		meta.edit({
+			name: "Bold",
+			combo: [-1,'B'], fake: -1,
+			on: function(eve){
+				meta.text.editor('bold');
+			},
+			up: function(){}
+		});
+		meta.edit({
+			name: "Italic",
+			combo: [-1,'I'], fake: -1,
+			on: function(eve){
+				meta.text.editor('italic');
+			},
+			up: function(){}
+		});
+		/*meta.edit({
+			name: "Underline",
+			combo: [-1,'U'], fake: -1,
+			on: function(eve){
+				meta.text.editor('underline');
+			},
+			up: function(){}
+		});*/
+		meta.edit({
+			name: "linK",
+			combo: [-1,'K'], fake: -1,
+			on: function(eve){
+				var range = meta.text.range || monotype();
+				meta.ask('Paste or type link...', function(url){
+					meta.text.editor({tag: $('<a href="'+url+'">link</a>'), edit: url? 'createLink' : 'unlink', as: url, range: range});
+				})
+			}
+		});
+		//meta.edit({name: "aliGn", combo: [-1,'G']}); // MOVE TO ADVANCED MENu!
+		meta.edit({
+			name: "Left",
+			combo: [-1,'G','L'], fake: -1,
+			on: function(eve){ meta.text.editor('justifyLeft') },
+			up: function(){}
+		});
+		meta.edit({
+			name: "Right",
+			combo: [-1,'G','R'], fake: -1,
+			on: function(eve){ meta.text.editor('justifyRight') },
+			up: function(){ }
+		});
+		meta.edit({
+			name: "Middle",
+			combo: [-1,'G','M'], fake: -1,
+			on: function(eve){ meta.text.editor('justifyCenter') },
+			up: function(){ }
+		});
+		meta.edit({
+			name: "Justify",
+			combo: [-1,'G','J'], fake: -1,
+			on: function(eve){ meta.text.editor('justifyFull') },
+			up: function(){}
+		});
+		// Align Number
+		// Align Points
+		// Align Strike
+		meta.edit({name: "Size", combo: [-1,'S'], on: function(){ m.list(this, true) }});
+		meta.edit({
+			name: "Small",
+			combo: [-1,'S','S'], fake: -1,
+			on: function(eve){ meta.text.editor('fontSize', 2) },
+			up: function(){ }
+		});
+		meta.edit({
+			name: "Normal",
+			combo: [-1,'S','N'], fake: -1,
+			on: function(eve){ meta.text.editor('fontSize', 5) },
+			up: function(){}
+		});
+		meta.edit({
+			name: "Header",
+			combo: [-1,'S','H'], fake: -1,
+			on: function(eve){ meta.text.editor('fontSize', 6) },
+			up: function(){}
+		});
+		meta.edit({
+			name: "Title",
+			combo: [-1,'S','T'], fake: -1,
+			on: function(eve){ meta.text.editor('fontSize', 7) },
+			up: function(){}
+		});
 	}());
 });

+ 64 - 0
public/lib/gundb/lib/multicast.js

@@ -0,0 +1,64 @@
+var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
+
+Gun.on('create', function(root){
+	this.to.next(root);
+	var opt = root.opt;
+  if(false === opt.multicast){ return }
+	if(true !== opt.multicast){ return } // disable multicast by default for now.
+
+  var udp = opt.multicast = opt.multicast || {};
+  udp.address = udp.address || '233.255.255.255';
+  udp.pack = udp.pack || 50000; // UDP messages limited to 65KB.
+  udp.port  = udp.port || 23456;
+
+  var noop = function(){}, port;
+
+  var dgram = require("dgram");
+  var socket = dgram.createSocket({type: "udp4", reuseAddr: true});
+  socket.bind(udp.port);
+
+  socket.on("listening", function() {
+    socket.addMembership(udp.address);
+    udp.peer = {url: udp.address + ':' + udp.port, wire: socket};
+
+    udp.peer.say = function(raw){
+      var buf = Buffer.from(raw, 'utf8');
+      if(udp.pack <= buf.length){ // message too big!!!
+        return;
+      }
+      socket.send(buf, 0, buf.length, udp.port, udp.address, noop);
+    }
+    opt.mesh.hi(udp.peer);
+
+    console.log('multicasting on', udp.peer.url);
+    return; // below code only needed for when WebSocket connections desired!
+    setInterval(function broadcast(){
+      port = port || (opt.web && opt.web.address()||{}).port;
+      if(!port){ return }
+      udp.peer.say(JSON.stringify({id: opt.pid || (opt.pid = Math.random().toString(36).slice(2)), port: port}));
+    }, 1000);
+  });
+
+  socket.on("message", function(raw, info) { try {
+    if(!raw){ return }
+    raw = raw.toString('utf8');
+    opt.mesh.hear(raw, udp.peer);
+
+    return; // below code only needed for when WebSocket connections desired!
+    var message;
+    message = JSON.parse(raw.toString('utf8'));
+
+    if(opt.pid === message.id){ return } // ignore self
+
+    var url = 'http://' + info.address + ':' + (port || (opt.web && opt.web.address()||{}).port) + '/gun';
+    if(root.opt.peers[url]){ return }
+  
+    console.log('discovered', url, message, info);
+    root.$.opt(url);
+
+  } catch(e){
+    console.log('multicast error', e, raw);
+    return;
+  } });
+
+});

+ 7 - 1
public/lib/gundb/lib/normalize.js

@@ -29,12 +29,13 @@
     hierarchy: ['div', 'pre', 'ol', 'ul', 'li',
                 'h1', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', // block
                 'b', 'code', 'i', 'span', 's', 'sub', 'sup', 'u',   // inline
-                'br']                                               // empty
+                'br', 'img']                                               // empty
     ,tags: {
       'a': {attrs:{'href':1}, exclude:{'a':1}},
       'b': {exclude:{'b':1,'p':1}},
       'br': {empty: 1},
       'i': {exclude:{'i':1,'p':1}},
+      'img': {attrs:{'src':1}, empty: 1},
       'span': {exclude:{'p':1,'ul':1,'ol':1,'li':1,'br':1}},
       's': {space:1},
       'u': {exclude:{'u':1,'p':1},space:1},
@@ -138,12 +139,17 @@
     return $(($(e)[0]||{})[d]);
   }
 
+  var xssattr = /[^a-z:]/ig, xssjs = /javascript:/ig;
+  // url("javascript: // and all permutations
+  // stylesheets can apparently have XSS?
+
   // create key val attributes object from elements attributes
   function attrsAsObj(e, filterCb){
     var attrObj = {};
     (e = $(e)) && e.length && $(e[0].attributes||[]).each(function(value,name){
       name = name.nodeName||name.name;
       value = e.attr(name);
+      if(value.replace(xssattr,'').match(xssjs)){ e.removeAttr(name); return }
       value = filterCb? filterCb(value,name,e) : value;
       if(value !== undefined && value !== false)
         attrObj[name] = value;

+ 124 - 41
public/lib/gundb/lib/radisk.js

@@ -5,15 +5,21 @@
 		opt = opt || {};
 		opt.log = opt.log || console.log;
 		opt.file = String(opt.file || 'radata');
+		var has = (Radisk.has || (Radisk.has = {}))[opt.file];
+		if(has){ return has }
+
 		opt.pack = opt.pack || (opt.memory? (opt.memory * 1000 * 1000) : 1399000000) * 0.3; // max_old_space_size defaults to 1400 MB.
-		opt.until = opt.until || opt.wait || 9;
-		opt.batch = opt.batch || 10 * 1000;
+		opt.until = opt.until || opt.wait || 250;
+		opt.batch = opt.batch || (10 * 1000);
 		opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
 		opt.code = opt.code || {};
 		opt.code.from = opt.code.from || '!';
+		//opt.jsonify = true; // TODO: REMOVE!!!!
 
 		function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
+		function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
 		var map = Gun.obj.map;
+		var LOG = false;
 
 		if(!opt.store){
 			return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
@@ -36,25 +42,29 @@
 		var r = function(key, val, cb){
 			key = ''+key;
 			if(val instanceof Function){
+				var o = cb || {};
 				cb = val;
 				val = r.batch(key);
 				if(u !== val){
+					cb(u, r.range(val, o), o);
+					if(atomic(val)){ return }
 					// if a node is requested and some of it is cached... the other parts might not be.
-					return cb(u, val);
 				}
 				if(r.thrash.at){
 					val = r.thrash.at(key);
 					if(u !== val){
+						cb(u, r.range(val, o), o);
+						if(atomic(val)){ cb(u, val, o); return }
 						// if a node is requested and some of it is cached... the other parts might not be.
-						return cb(u, val);
 					}
 				}
-				return r.read(key, cb);
+				return r.read(key, cb, o);
 			}
 			r.batch(key, val);
 			if(cb){ r.batch.acks.push(cb) }
 			if(++r.batch.ed >= opt.batch){ return r.thrash() } // (2)
-			clearTimeout(r.batch.to); // (1)
+			if(r.batch.to){ return }
+			//clearTimeout(r.batch.to); // (1) // THIS LINE IS EVIL! NEVER USE IT! ALSO NEVER DELETE THIS SO WE NEVER MAKE THE SAME MISTAKE AGAIN!
 			r.batch.to = setTimeout(r.thrash, opt.until || 1);
 		}
 
@@ -73,9 +83,11 @@
 			r.batch = Radix();
 			r.batch.acks = [];
 			r.batch.ed = 0;
+			//var id = Gun.text.random(2), S = (+new Date); console.log("<<<<<<<<<<<<", id);
 			r.save(batch, function(err, ok){
-				if(++i > 1){ return }
+				if(++i > 1){ opt.log('RAD ERR: Radisk has callbacked multiple times, please report this as a BUG at github.com/amark/gun/issues ! ' + i); return }
 				if(err){ opt.log('err', err) }
+				//console.log(">>>>>>>>>>>>", id, ((+new Date) - S), batch.acks.length);
 				map(batch.acks, function(cb){ cb(err, ok) });
 				thrash.at = null;
 				thrash.ing = false;
@@ -90,7 +102,7 @@
 			4. Read the previous file to that into memory
 			5. Scan through the in memory radix for all values lexically less than the limit.
 			6. Merge and write all of those to the in-memory file and back to disk.
-			7. If file to large, split. More details needed here.
+			7. If file too large, split. More details needed here.
 		*/
 		r.save = function(rad, cb){
 			var s = function Span(){};
@@ -136,28 +148,32 @@
 			Therefore it is unavoidable that a read will have to happen,
 			the question is just how long you delay it.
 		*/
-		r.write = function(file, rad, cb, force){
+		r.write = function(file, rad, cb, o){
+			o = ('object' == typeof o)? o : {force: o};
 			var f = function Fractal(){};
 			f.text = '';
 			f.count = 0;
 			f.file = file;
 			f.each = function(val, key, k, pre){
+				//console.log("RAD:::", JSON.stringify([val, key, k, pre]));
 				if(u !== val){ f.count++ }
 				if(opt.pack <= (val||'').length){ return cb("Record too big!"), true }
 				var enc = Radisk.encode(pre.length) +'#'+ Radisk.encode(k) + (u === val? '' : ':'+ Radisk.encode(val)) +'\n';
-				if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !force){
+				if((opt.chunk < f.text.length + enc.length) && (1 < f.count) && !o.force){
 					f.text = '';
 					f.limit = Math.ceil(f.count/2);
 					f.count = 0;
 					f.sub = Radix();
-					Radix.map(rad, f.slice)
+					Radix.map(rad, f.slice);
 					return true;
 				}
 				f.text += enc;
 			}
 			f.write = function(){
 				var tmp = ename(file);
+				var start; LOG && (start = (+new Date)); // comment this out!
 				opt.store.put(tmp, f.text, function(err){
+					LOG && console.log("wrote JSON in", (+new Date) - start); // comment this out!
 					if(err){ return cb(err) }
 					r.list.add(tmp, cb);
 				});
@@ -168,7 +184,7 @@
 					var name = f.file;
 					f.file = key;
 					f.count = 0;
-					r.write(name, f.sub, f.next, force);
+					r.write(name, f.sub, f.next, o);
 					return true;
 				}
 				f.sub(key, val);
@@ -177,52 +193,94 @@
 				if(err){ return cb(err) }
 				f.sub = Radix();
 				if(!Radix.map(rad, f.slice)){
-					r.write(f.file, f.sub, cb, force);
+					r.write(f.file, f.sub, cb, o);
 				}
 			}
+			if(opt.jsonify){ return r.write.jsonify(f, file, rad, cb, o) } // temporary testing idea
 			if(!Radix.map(rad, f.each, true)){ f.write() }
 		}
 
+		r.write.jsonify = function(f, file, rad, cb, o){
+			var raw;
+			var start; LOG && (start = (+new Date)); // comment this out!
+			try{raw = JSON.stringify(rad.$);
+			}catch(e){ return cb("Record too big!") }
+			LOG && console.log("stringified JSON in", (+new Date) - start); // comment this out!
+			if(opt.chunk < raw.length && !o.force){
+				if(Radix.map(rad, f.each, true)){ return }
+			}
+			f.text = raw;
+			f.write();
+		}
+
+		r.range = function(tree, o){
+			if(!tree || !o){ return }
+			if(u === o.start && u === o.end){ return tree }
+			if(atomic(tree)){ return tree }
+			var sub = Radix();
+			Radix.map(tree, function(v,k){
+				sub(k,v);
+			}, o)
+			return sub('');
+		}
+
 		;(function(){
 			var Q = {};
-			r.read = function(key, cb, next){
-				if(RAD && !next){ // cache
+			r.read = function(key, cb, o){
+				o = o || {};
+				if(RAD && !o.next){ // cache
 					var val = RAD(key);
-					if(u !== val){
+					//if(u !== val){
+						//cb(u, val, o);
+						if(atomic(val)){ cb(u, val, o); return }
 						// if a node is requested and some of it is cached... the other parts might not be.
-						return cb(u, val);
-					}
+					//}
 				}
-				var g = function Get(){}, tmp;
-				g.lex = function(file){
+				o.span = (u !== o.start) || (u !== o.end);
+				var g = function Get(){};
+				g.lex = function(file){ var tmp;
 					file = (u === file)? u : decodeURIComponent(file);
-					if(!file || file > (next || key)){
-						if(next){ g.file = file }
+					tmp = o.next || key || (o.reverse? o.end || '\uffff' : o.start || '');
+					if(!file || (o.reverse? file < tmp : file > tmp)){
+						if(o.next || o.reverse){ g.file = file }
 						if(tmp = Q[g.file]){
-							tmp.push({key: key, ack: cb, file: g.file});
+							tmp.push({key: key, ack: cb, file: g.file, opt: o});
 							return true;
 						}
-						Q[g.file] = [{key: key, ack: cb, file: g.file}];
+						Q[g.file] = [{key: key, ack: cb, file: g.file, opt: o}];
+						if(!g.file){
+							g.it(null, u, {});
+							return true; 
+						}
 						r.parse(g.file, g.it);
 						return true;
 					}
 					g.file = file;
 				}
-				g.it = function(err, disk){
+				g.it = function(err, disk, info){
 					if(g.err = err){ opt.log('err', err) }
+					g.info = info;
 					if(disk){ RAD = g.disk = disk }
 					disk = Q[g.file]; delete Q[g.file];
 					map(disk, g.ack);
 				}
 				g.ack = function(as){
 					if(!as.ack){ return }
-					var tmp = as.key, rad = g.disk || noop, data = rad(tmp), last = rad.last;
-					if(data){ as.ack(g.err, data) }
-					else if(!as.file){ return as.ack(g.err, u) }
-					if(!last || last === tmp){ return as.ack(g.err, u) } // is this correct?
-					if(last > tmp && 0 > last.indexOf(tmp)){ return as.ack(g.err, u) }
-					r.read(tmp, as.ack, as.file);
+					var tmp = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(tmp), o), last = rad.last;
+					o.parsed = (o.parsed || 0) + (info.parsed||0);
+					o.chunks = (o.chunks || 0) + 1;
+					if(!o.some){ o.some = (u !== data) }
+					if(u !== data){ as.ack(g.err, data, o) }
+					else if(!as.file){ !o.some && as.ack(g.err, u, o); return }
+					if(!o.span){
+						if(/*!last || */last === tmp){ !o.some && as.ack(g.err, u, o); return }
+						if(last && last > tmp && 0 != last.indexOf(tmp)){ !o.some && as.ack(g.err, u, o); return }
+					}
+					if(o.some && o.parsed >= o.limit){ return }
+					o.next = as.file;
+					r.read(tmp, as.ack, o);
 				}
+				if(o.reverse){ g.lex.reverse = true }
 				r.list(g.lex);
 			}
 		}());
@@ -236,14 +294,13 @@
 				Then we can work on the harder problem of being multi-process.
 			*/
 			var Q = {}, s = String.fromCharCode(31);
-			r.parse = function(file, cb){ var q;
+			r.parse = function(file, cb, raw){ var q;
 				if(q = Q[file]){ return q.push(cb) } q = Q[file] = [cb];
-				var p = function Parse(){};
+				var p = function Parse(){}, info = {};
 				p.disk = Radix();
 				p.read = function(err, data){ var tmp;
 					delete Q[file];
 					if((p.err = err) || (p.not = !data)){
-						//return cb(err, u);//map(q, p.ack);
 						return map(q, p.ack);
 					}
 					if(typeof data !== 'string'){
@@ -251,12 +308,33 @@
 							if(opt.pack <= data.length){
 								p.err = "Chunk too big!";
 							} else {
-								data = data.toString();
+								data = data.toString(); // If it crashes, it crashes here. How!?? We check size first!
 							}
 						}catch(e){ p.err = e }
 						if(p.err){ return map(q, p.ack) }
 					}
+					info.parsed = data.length;
+
+					var start; LOG && (start = (+new Date)); // keep this commented out in production!
+					if(opt.jsonify){ // temporary testing idea
+						try{
+							var json = JSON.parse(data);
+							p.disk.$ = json;
+							LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production!
+							map(q, p.ack);
+							return;
+						}catch(e){ tmp = e }
+						if('{' === data[0]){
+							p.err = tmp || "JSON error!";
+							return map(q, p.ack);
+						}
+					}
+					var start; LOG && (start = (+new Date)); // keep this commented out in production!
 					var tmp = p.split(data), pre = [], i, k, v;
+					if(!tmp || 0 !== tmp[1]){
+						p.err = "File '"+file+"' does not have root radix! ";
+						return map(q, p.ack);
+					}
 					while(tmp){
 						k = v = u;
 						i = tmp[1];
@@ -274,6 +352,7 @@
 						if(u !== k && u !== v){ p.disk(pre.join(''), v) }
 						tmp = p.split(tmp[2]);
 					}
+					LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production!
 					//cb(err, p.disk);
 					map(q, p.ack);
 				};
@@ -290,9 +369,10 @@
 				}
 				p.ack = function(cb){ 
 					if(!cb){ return }
-					if(p.err || p.not){ return cb(p.err, u) }
-					cb(u, p.disk);
+					if(p.err || p.not){ return cb(p.err, u, info) }
+					cb(u, p.disk, info);
 				}
+				if(raw){ return p.read(null, raw) }
 				opt.store.get(ename(file), p.read);
 			}
 		}());
@@ -301,9 +381,10 @@
 			var dir, q, f = String.fromCharCode(28), ef = ename(f);
 			r.list = function(cb){
 				if(dir){
+					var tmp = {reverse: (cb.reverse)? 1 : 0};
 					Radix.map(dir, function(val, key){
 						return cb(key);
-					}) || cb();
+					}, tmp) || cb();
 					return;
 				}
 				if(q){ return q.push(cb) } q = [cb];
@@ -315,8 +396,11 @@
 					return cb(u, 1);
 				}
 				dir(file, true);
+				cb.listed = (cb.listed || 0) + 1;
 				r.write(f, dir, function(err, ok){
 					if(err){ return cb(err) }
+					cb.listed = (cb.listed || 0) - 1;
+					if(cb.listed !== 0){ return }
 					cb(u, 1);
 				}, true);
 			}
@@ -345,14 +429,13 @@
 				r.list.dir = dir = rad;
 				tmp = q; q = null;
 				Gun.list.map(tmp, function(cb){
-					Radix.map(dir, function(val, key){
-						return cb(key);
-					}) || cb();
+					r.list(cb);
 				});
 			}
 		}());
 
 		var noop = function(){}, RAD, u;
+		Radisk.has[opt.file] = r;
 		return r;
 	}
 

+ 37 - 25
public/lib/gundb/lib/radix.js

@@ -5,9 +5,10 @@
 			key = ''+key;
 			if(!t && u !== val){ 
 				radix.last = (key < radix.last)? radix.last : key;
-				radix.sort = null;
+				delete (radix.$||{})[_];
 			}
-			t = t || radix[_] || (radix[_] = {});
+			t = t || radix.$ || (radix.$ = {});
+			if(!key && Object.keys(t).length){ return t }
 			var i = 0, l = key.length-1, k = key[i], at, tmp;
 			while(!(at = t[k]) && i < l){
 				k += key[++i];
@@ -15,9 +16,9 @@
 			if(!at){
 				if(!map(t, function(r, s){
 					var ii = 0, kk = '';
-					while(s[ii] == key[ii]){
+					if((s||'').length){ while(s[ii] == key[ii]){
 						kk += s[ii++];
-					}
+					} }
 					if(kk){
 						if(u === val){
 							if(ii <= l){ return }
@@ -25,47 +26,58 @@
 						}
 						var __ = {};
 						__[s.slice(ii)] = r;
-						(__[key.slice(ii)] = {})[$] = val;
-						(t[kk] = {})[_] = __;
+						ii = key.slice(ii);
+						('' === ii)? (__[''] = val) : ((__[ii] = {})[''] = val);
+						t[kk] = __;
 						delete t[s];
 						return true;
 					}
 				})){
 					if(u === val){ return; }
-					(t[k] || (t[k] = {}))[$] = val;
+					(t[k] || (t[k] = {}))[''] = val;
 				}
 				if(u === val){
 					return tmp;
 				}
 			} else 
 			if(i == l){
-				if(u === val){ return (u === (tmp = at[$]))? at[_] : tmp }
-				at[$] = val;
+				if(u === val){ return (u === (tmp = at['']))? at : tmp }
+				at[''] = val;
 			} else {
-				if(u !== val){ at.sort = null }
-				return radix(key.slice(++i), val, at[_] || (at[_] = {}));
+				if(u !== val){ delete at[_] }
+				return radix(key.slice(++i), val, at || (at = {}));
 			}
 		}
 		return radix;
 	};
 
 	Radix.map = function map(radix, cb, opt, pre){ pre = pre || [];
-		var t = radix[_] || radix, keys = radix.sort || (radix.sort = Object.keys(t).sort()), i = 0, l = keys.length;
-		for(;i < l; i++){ var key = keys[i], tree = t[key], tmp;
-			if(u !== (tmp = tree[$])){
-				tmp = cb(tmp, pre.join('') + key, key, pre);
+		var t = ('function' == typeof radix)? radix.$ || {} : radix;
+		if(!t){ return }
+		var keys = (t[_]||no).sort || (t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }()).sort;
+		//var keys = Object.keys(t).sort();
+		opt = (true === opt)? {branch: true} : (opt || {});
+		if(opt.reverse){ keys = keys.slice().reverse() }
+		var start = opt.start, end = opt.end;
+		var i = 0, l = keys.length;
+		for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt;
+			if(!tree || '' === key || _ === key){ continue }
+			p = pre.slice(); p.push(key);
+			pt = p.join('');
+			if(u !== start && pt < (start||'').slice(0,pt.length)){ continue }
+			if(u !== end && (end || '\uffff') < pt){ continue }
+			if(u !== (tmp = tree[''])){
+				tmp = cb(tmp, pt, key, pre);
 				if(u !== tmp){ return tmp }
-			} else 
-			if(opt){
-				cb(u, pre.join(''), key, pre);
-			}
-			if(tmp = tree[_]){
-				pre.push(key);
-				tmp = map(tree, cb, opt, pre);
-				//tmp = map(tmp, cb, opt, pre);
+			} else
+			if(opt.branch){
+				tmp = cb(u, pt, key, pre);
 				if(u !== tmp){ return tmp }
-				pre.pop();
 			}
+			pre = p;
+			tmp = map(tree, cb, opt, pre);
+			if(u !== tmp){ return tmp }
+			pre.pop();
 		}
 	};
 
@@ -80,6 +92,6 @@
 	}
 	
 	var map = Gun.obj.map, no = {}, u;
-	var $ = String.fromCharCode(30), _ = String.fromCharCode(29);
+	var _ = String.fromCharCode(24);
 	
 }());

+ 10 - 35
public/lib/gundb/lib/rfs.js

@@ -1,10 +1,15 @@
-var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
-
 function Store(opt){
 	opt = opt || {};
+	opt.log = opt.log || console.log;
 	opt.file = String(opt.file || 'radata');
 	var fs = require('fs'), u;
+
 	var store = function Store(){};
+	if(Store[opt.file]){
+		console.log("Warning: reusing same fs store and options as 1st.");
+		return Store[opt.file];
+	}
+	Store[opt.file] = store;
 
 	store.put = function(file, data, cb){
 		var random = Math.random().toString(36).slice(-3);
@@ -20,18 +25,13 @@ function Store(opt){
 				if('ENOENT' === (err.code||'').toUpperCase()){
 					return cb(null);
 				}
-				Gun.log("ERROR:", err)
+				opt.log("ERROR:", err)
 			}
 			cb(err, data);
 		});
 	};
-	store.list = function(cb, match){
-		fs.readdir(opt.file, function(err, dir){
-			Gun.obj.map(dir, cb) || cb(); // Stream interface requires a final call to know when to be done.
-		});
-	};
+
 	if(!fs.existsSync(opt.file)){ fs.mkdirSync(opt.file) }
-	//store.list(function(){ return true });
 
 	function move(oldPath, newPath, cb) {
 		fs.rename(oldPath, newPath, function (err) {
@@ -60,29 +60,4 @@ function Store(opt){
 	return store;
 }
 
-function Mem(opt){
-  opt = opt || {};
-  opt.file = String(opt.file || 'radata');
-  var storage = Mem.storage || (Mem.storage = {});
-  var store = function Store(){}, u;
-  store.put = function(file, data, cb){
-  	setTimeout(function(){
-      storage[file] = data;
-      cb(null, 1);
-    }, 1);
-  };
-  store.get = function(file, cb){
-    setTimeout(function(){
-      var tmp = storage[file] || u;
-      cb(null, tmp);
-    }, 1);
-  };
-  store.list = function(cb, match){ // supporting this is no longer needed! Optional.
-    setTimeout(function(){
-      Gun.obj.map(Object.keys(storage), cb) || cb();
-    }, 1);
-  };
-  return store;
-}
-
-module.exports = Store;//Gun.TESTING? Mem : Store;
+module.exports = Store;

+ 54 - 114
public/lib/gundb/lib/rindexed.js

@@ -1,129 +1,69 @@
 ;(function(){
-  var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
-
-  Gun.on('create', function(root){
-    this.to.next(root);
-    root.opt.store = root.opt.store || Store(root.opt);
-  });
 
   function Store(opt){
     opt = opt || {};
     opt.file = String(opt.file || 'radata');
-    var db = null;
-
-    opt.indexedDB = opt.indexedDB || window.indexedDB;
-    // Initialize indexedDB. Version 1.
-    var request = opt.indexedDB.open(opt.file, 1)
-
-    // Create schema. onupgradeneeded is called only when DB is first created or when the DB version increases.
-    request.onupgradeneeded = function(event){
-      var db = event.target.result;
-      db.createObjectStore(opt.file);
-    }
+    opt.chunk = opt.chunk || (1024 * 1024); // 1MB
+    var db = null, u;
 
-    // onsuccess is called when the DB is ready.
-    request.onsuccess = function(){
-      db = request.result;
+    try{opt.indexedDB = opt.indexedDB || indexedDB}catch(e){}
+    try{if(!opt.indexedDB || 'file:' == location.protocol){
+      var store = {}, s = {};
+      store.put = function(f, d, cb){ s[f] = d; cb(null, 1) };
+      store.get = function(f, cb){ cb(null, s[f] || u) };
+      console.log('Warning: No indexedDB exists to persist data to!');
+      return store;
+    }}catch(e){}
+    
+    var store = function Store(){};
+    if(Store[opt.file]){
+      console.log("Warning: reusing same IndexedDB store and options as 1st.");
+      return Store[opt.file];
     }
+    Store[opt.file] = store;
 
-    request.onerror = function(event){
-      console.log('ERROR: RAD IndexedDB generic error:', event);
-    };
-
-    var store = function Store(){}, u;
-
-    store.put = function(file, data, cb){
-      cb = cb || function(){};
-      var doPut = function(){
-        // Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action.
-        var transaction = db.transaction([opt.file], 'readwrite');
-
-        // Add or update data.
-        var radStore = transaction.objectStore(opt.file);
-        var putRequest = radStore.put(data, file);
-        putRequest.onsuccess = radStore.onsuccess = transaction.onsuccess = function(){
-          //console.log('RAD IndexedDB put transaction was succesful.');
-          cb(null, 1);
-        };
-        putRequest.onabort = radStore.onabort = transaction.onabort = function(){
-          var es = 'ERROR: RAD IndexedDB put transaction was aborted.';
-          console.log(es);
-          cb(es, undefined);
-        };
-        putRequest.onerror = radStore.onerror = transaction.onerror = function(event){
-          var es = 'ERROR: RAD IndexedDB put transaction was in error: ' + JSON.stringify(event)
-          console.log(es);
-          cb(es, undefined);
-        };
-      }
-      if(!db){
-        waitDbReady(doPut, 100, function(){
-          var es = 'ERROR: Timeout: RAD IndexedDB not ready.';
-          console.log(es);
-          cb(es, undefined);
-        }, 10)
-      } else {
-        doPut();
-      }
-    };
+    store.start = function(){
+      var o = indexedDB.open(opt.file, 1);
+      o.onupgradeneeded = function(eve){ (eve.target.result).createObjectStore(opt.file) }
+      o.onsuccess = function(){ db = o.result }
+      o.onerror = function(eve){ console.log(eve||1); }
+    }; store.start();
 
-    store.get = function(file, cb){
-      cb = cb || function(){};
-      var doGet = function(){
-        // Start a transaction. The transaction will be automaticallt closed when the last success/error handler took no new action.
-        var transaction = db.transaction([opt.file], 'readwrite');
-
-        // Read data.
-        var radStore = transaction.objectStore(opt.file);
-        var getRequest = radStore.get(file);
-        getRequest.onsuccess = function(){
-          //console.log('RAD IndexedDB get transaction was succesful.');
-          cb(null, getRequest.result);
-        };
-        getRequest.onabort = function(){
-          var es = 'ERROR: RAD IndexedDB get transaction was aborted.';
-          console.log(es);
-          cb(es, undefined);
-        };
-        getRequest.onerror = function(event){
-          var es = 'ERROR: RAD IndexedDB get transaction was in error: ' + JSON.stringify(event)
-          console.log(es);
-          cb(es, undefined);
-        };
-      }
-      if(!db){
-        waitDbReady(doGet, 100, function(){
-          var es = 'ERROR: Timeout: RAD IndexedDB not ready.';
-          console.log(es);
-          cb(es, undefined);
-        }, 10)
-      } else {
-        doGet();
-      }
-    };
-
-    var waitDbReady = function(readyFunc, checkInterval, timeoutFunc, timeoutSecs){
-      var startTime = new Date();
-      var checkFunc = function(){
-        if(db){
-          readyFunc(); 
-        } else {
-          if((new Date() - startTime) / 1000 >= timeoutSecs){
-            timeoutFunc();
-          } else {
-            setTimeout(checkFunc, checkInterval);
-          }
-        }
-      };
-      checkFunc();
-    };
+    store.put = function(key, data, cb){
+      if(!db){ setTimeout(function(){ store.put(key, data, cb) },1); return }
+      var tx = db.transaction([opt.file], 'readwrite');
+      var obj = tx.objectStore(opt.file);
+      var req = obj.put(data, ''+key);
+      req.onsuccess = obj.onsuccess = tx.onsuccess = function(){ cb(null, 1) }
+      req.onabort = obj.onabort = tx.onabort = function(eve){ cb(eve||'put.tx.abort') }
+      req.onerror = obj.onerror = tx.onerror = function(eve){ cb(eve||'put.tx.error') }
+    }
 
+    store.get = function(key, cb){
+      if(!db){ setTimeout(function(){ store.get(key, cb) },9); return }
+      var tx = db.transaction([opt.file], 'readonly');
+      var obj = tx.objectStore(opt.file);
+      var req = obj.get(''+key);
+      req.onsuccess = function(){ cb(null, req.result) }
+      req.onabort = function(eve){ cb(eve||4) }
+      req.onerror = function(eve){ cb(eve||5) }
+    }
+    setInterval(function(){ db && db.close(); db = null; store.start() }, 1000 * 15); // reset webkit bug?
     return store;
   }
 
-  if(Gun.window){
-    Gun.window.RindexedDB = Store;
+  if(typeof window !== "undefined"){
+    (Store.window = window).RindexedDB = Store;
   } else {
-    module.exports = Store;
+    try{ module.exports = Store }catch(e){}
   }
-}());
+
+  try{
+    var Gun = Store.window.Gun || require('../gun');
+    Gun.on('create', function(root){
+      this.to.next(root);
+      root.opt.store = root.opt.store || Store(root.opt);
+    });
+  }catch(e){}
+
+}());

+ 29 - 0
public/lib/gundb/lib/rls.js

@@ -0,0 +1,29 @@
+;(function(){
+
+  function Store(opt){
+    opt = opt || {};
+    opt.file = String(opt.file || 'radata');
+    var store = function Store(){};
+
+    var ls = localStorage;
+    store.put = function(key, data, cb){ ls[''+key] = data; cb(null, 1) }
+    store.get = function(key, cb){ cb(null, ls[''+key]) }
+
+    return store;
+  }
+
+  if(typeof window !== "undefined"){
+    (Store.window = window).RlocalStorage = Store;
+  } else {
+    try{ module.exports = Store }catch(e){}
+  }
+
+  try{
+    var Gun = Store.window.Gun || require('../gun');
+    Gun.on('create', function(root){
+      this.to.next(root);
+      root.opt.store = root.opt.store || Store(root.opt);
+    });
+  }catch(e){}
+
+}());

+ 22 - 0
public/lib/gundb/lib/rmem.js

@@ -0,0 +1,22 @@
+function Rmem(){
+  var opt = {}, store = {}, u;
+  opt.put = function(file, data, cb){
+  	//setTimeout(function(){ // make async
+    store[file] = data;
+    cb(null, 1);
+    //}, 1);
+  };
+  opt.get = function(file, cb){
+    //setTimeout(function(){ // make async
+    var tmp = store[file] || u;
+    cb(null, tmp);
+    //}, 1);
+  };
+  return opt;
+}
+
+if(typeof window !== "undefined"){
+  window.Rmem = Rmem;
+} else {
+	try{ module.exports = Rmem }catch(e){}
+}

+ 9 - 3
public/lib/gundb/lib/rs3.js

@@ -8,8 +8,8 @@ Gun.on('create', function(root){
 	this.to.next(root);
 	var opt = root.opt;
 	if(!process.env.AWS_S3_BUCKET){ return }
-	opt.batch = opt.batch || (1000 * 1);
-	opt.until = opt.until || (1000 * 15);
+	opt.batch = opt.batch || (1000 * 10);
+	opt.until = opt.until || (1000 * 3);
 	opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
 
 	try{AWS = require('aws-sdk');
@@ -41,8 +41,14 @@ function Store(opt){
 	opt.file = String(opt.file || 'radata');
 	var opts = opt.s3, s3 = opts.s3;
 	var c = {p: {}, g: {}, l: {}};
-
+	
 	var store = function Store(){};
+	if(Store[opt.file]){
+		console.log("Warning: reusing same S3 store and options as 1st.");
+		return Store[opt.file];
+	}
+	Store[opt.file] = store;
+
 	store.put = function(file, data, cb){
 		var params = {Bucket: opts.bucket, Key: file, Body: data};
 		//console.log("RS3 PUT ---->", (data||"").slice(0,20));

+ 3 - 0
public/lib/gundb/lib/serve.js

@@ -1,8 +1,11 @@
 var fs = require('fs');
 var path = require('path');
+var dot = /\.\.+/g;
+var slash = /\/\/+/g;
 
 function CDN(dir){
 	return function(req, res){
+		req.url = (req.url||'').replace(dot,'').replace(slash,'/');
 		if(serve(req, res)){ return } // filters GUN requests!
 		fs.createReadStream(path.join(dir, req.url)).on('error',function(tmp){ // static files!
 			try{ tmp = fs.readFileSync(path.join(dir, 'index.html')) }catch(e){}

+ 3 - 1
public/lib/gundb/lib/server.js

@@ -16,6 +16,8 @@
 	//try{require('../axe');}catch(e){}
 	require('./file');
 	require('./evict');
+	require('./multicast');
+	require('./stats');
 	if('debug' === process.env.GUN_ENV){ require('./debug') }
 	module.exports = Gun;
-}());
+}());

+ 43 - 0
public/lib/gundb/lib/stats.js

@@ -0,0 +1,43 @@
+var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
+
+Gun.on('opt', function(root){
+	this.to.next(root);
+	if(root.once){ return }
+	if(typeof process === 'undefined'){ return }
+	if(typeof require === 'undefined'){ return }
+	var noop = function(){};
+	var os = require('os') || {};
+	var fs = require('fs') || {};
+	fs.existsSync = fs.existsSync || require('path').existsSync;
+	if(!fs.existsSync){ return }
+	if(!process){ return }
+	process.uptime = process.uptime || noop;
+	process.cpuUsage = process.cpuUsage || noop;
+	process.memoryUsage = process.memoryUsage || noop;
+	os.totalmem = os.totalmem || noop;
+	os.freemem = os.freemem || noop;
+	os.loadavg = os.loadavg || noop;
+	os.cpus = os.cpus || noop;
+	setTimeout(function(){
+		root.stats = Gun.obj.ify((fs.existsSync(__dirname+'/../stats.'+root.opt.file) && fs.readFileSync(__dirname+'/../stats.'+root.opt.file).toString())) || {};
+		root.stats.up = root.stats.up || {};
+		root.stats.up.start = root.stats.up.start || +(new Date);
+		root.stats.up.count = (root.stats.up.count || 0) + 1;
+	},1);
+	setInterval(function(){
+		if(!root.stats){ root.stats = {} }
+		var stats = root.stats, tmp;
+		(stats.up||{}).time = process.uptime();
+		stats.memory = process.memoryUsage() || {};
+		stats.memory.totalmem = os.totalmem();
+		stats.memory.freemem = os.freemem();
+		stats.cpu = process.cpuUsage() || {};
+		stats.cpu.loadavg = os.loadavg();
+		stats.peers = {};
+		stats.peers.count = Object.keys(root.opt.peers||{}).length;
+		stats.node = {};
+		stats.node.count = Object.keys(root.graph||{}).length;
+		fs.writeFile(__dirname+'/../stats.'+root.opt.file, JSON.stringify(stats, null, 2), function(err){});
+	}, 1000 * 15);
+	Object.keys = Object.keys || function(o){ return Gun.obj.map(o, function(v,k,t){t(k)}) }
+});

+ 92 - 55
public/lib/gundb/lib/store.js

@@ -1,58 +1,95 @@
 var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
-
+ 
 Gun.on('create', function(root){
-	this.to.next(root);
-	var opt = root.opt, u;
-	if(false === opt.radisk){ return }
-	var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
-	var Radix = Radisk.Radix;
-
-	opt.store = opt.store || (!Gun.window && require('./rfs')(opt));
-	var rad = Radisk(opt), esc = String.fromCharCode(27);
-
-	root.on('put', function(msg){
-		this.to.next(msg);
-		var id = msg['#'], track = !msg['@'], acks = track? 0 : u; // only ack non-acks.
-		if(msg.rad && !track){ return } // don't save our own acks
-		Gun.graph.is(msg.put, null, function(val, key, node, soul){
-			if(track){ ++acks }
-			val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
-			rad(soul+'.'+key, val, (track? ack : u));
-		});
-		function ack(err, ok){
-			acks--;
-			if(ack.err){ return }
-			if(ack.err = err){
-				root.on('in', {'@': id, err: err});
-				return;
-			}
-			if(acks){ return }
-			root.on('in', {'@': id, ok: 1});
-		}
-	});
-
-	root.on('get', function(msg){
-		this.to.next(msg);
-		var id = msg['#'], soul = msg.get['#'], key = msg.get['.']||'', tmp = soul+'.'+key, node;
-		rad(tmp, function(err, val){
-			if(val){
-				if(val && typeof val !== 'string'){
-					if(key){
-						val = u;
-					} else {
-						Radix.map(val, each) 
-					}
-				}
-				if(!node && val){ each(val, key) }
-			}
-			root.on('in', {'@': id, put: Gun.graph.node(node), err: err? err : u, rad: Radix});
-		});
-		function each(val, key){
-			tmp = val.lastIndexOf('>');
-			var state = Radisk.decode(val.slice(tmp+1), null, esc);
-			val = Radisk.decode(val.slice(0,tmp), null, esc);
-			node = Gun.state.ify(node, key, state, val, soul);
-		}
-	});
-
+    if(Gun.TESTING){ root.opt.file = 'radatatest' }
+    this.to.next(root);
+    var opt = root.opt, u;
+    if(false === opt.radisk){ return }
+    var Radisk = (Gun.window && Gun.window.Radisk) || require('./radisk');
+    var Radix = Radisk.Radix;
+ 
+    opt.store = opt.store || (!Gun.window && require('./rfs')(opt));
+    var rad = Radisk(opt), esc = String.fromCharCode(27);
+ 
+    root.on('put', function(msg){
+        this.to.next(msg);
+        var id = msg['#'] || Gun.text.random(3), track = !msg['@'], acks = track? 0 : u; // only ack non-acks.
+        if(msg.rad && !track){ return } // don't save our own acks
+        Gun.graph.is(msg.put, null, function(val, key, node, soul){
+            if(track){ ++acks }
+            //console.log('put:', soul, key, val);
+            val = Radisk.encode(val, null, esc)+'>'+Radisk.encode(Gun.state.is(node, key), null, esc);
+            rad(soul+esc+key, val, (track? ack : u));
+        });
+        function ack(err, ok){
+            acks--;
+            if(ack.err){ return }
+            if(ack.err = err){
+                root.on('in', {'@': id, err: err});
+                return;
+            }
+            if(acks){ return }
+            //console.log("PAT!", id);
+            root.on('in', {'@': id, ok: 1});
+        }
+    });
+ 
+    root.on('get', function(msg){
+        this.to.next(msg);
+        var id = msg['#'], get = msg.get, soul = msg.get['#'], has = msg.get['.']||'', opt = {}, graph, lex, key, tmp, force;
+        if('string' == typeof soul){
+            key = soul;
+        } else 
+        if(soul){
+            if(u !== (tmp = soul['*'])){ opt.limit = force = 1 }
+            if(u !== soul['>']){ opt.start = soul['>'] }
+            if(u !== soul['<']){ opt.end = soul['<'] }
+            key = force? (''+tmp) : tmp || soul['='];
+            force = null;
+        }
+        if(key && !opt.limit){ // a soul.has must be on a soul, and not during soul*
+            if('string' == typeof has){
+                key = key+esc+(opt.atom = has);
+            } else 
+            if(has){
+                if(u !== has['>']){ opt.start = has['>']; opt.limit = 1 }
+                if(u !== has['<']){ opt.end = has['<']; opt.limit = 1 }
+                if(u !== (tmp = has['*'])){ opt.limit = force = 1 }
+                if(key){ key = key+esc + (force? (''+(tmp||'')) : tmp || (opt.atom = has['='] || '')) }
+            }
+        }
+        if((tmp = get['%']) || opt.limit){
+            opt.limit = (tmp <= (opt.pack || (1000 * 100)))? tmp : 1;
+        }
+        if(has['-'] || (soul||{})['-']){ opt.reverse = true }
+        //console.log("RAD get:", key, opt);
+        //var start = (+new Date); // console.log("GET!", id, JSON.stringify(key));
+        rad(key||'', function(err, data, o){
+            //console.log("RAD gat:", err, data, o);
+            if(data){
+                if(typeof data !== 'string'){
+                    if(opt.atom){
+                        data = u;
+                    } else {
+                        Radix.map(data, each) 
+                    }
+                }
+                if(!graph && data){ each(data, '') }
+            }
+            //console.log("GOT!", id, JSON.stringify(key), ((+new Date) - start));
+            root.on('in', {'@': id, put: graph, err: err? err : u, rad: Radix});
+        }, opt);
+        function each(val, has, a,b){
+            if(!val){ return }
+            has = (key+has).split(esc);
+            var soul = has.slice(0,1)[0];
+            has = has.slice(-1)[0];
+            opt.count = (opt.count || 0) + val.length;
+            tmp = val.lastIndexOf('>');
+            var state = Radisk.decode(val.slice(tmp+1), null, esc);
+            val = Radisk.decode(val.slice(0,tmp), null, esc);
+            (graph = graph || {})[soul] = Gun.state.ify(graph[soul], has, state, val, soul);
+            if(opt.limit && opt.limit <= opt.count){ return true }
+        }
+    });
 });

+ 18 - 10
public/lib/gundb/lib/super.js

@@ -1,28 +1,36 @@
 ;(function(){
 	var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
 	var Rad = (Gun.window||{}).Radix || require('./radix');
+	/// Store the subscribes
+	Gun.subs = Rad();
 	function input(msg){
-		var at = this.as, to = this.to, peer = (msg.mesh||empty).via;
+		var at = this.as, to = this.to, peer = (msg._||empty).via;
 		var get = msg.get, soul, key;
 		if(!peer || !get){ return to.next(msg) }
-		console.log("super", msg);
+		// console.log("super", msg);
 		if(soul = get['#']){
 			if(key = get['.']){
 
 			} else {
 
 			}
-			subscribe(soul, peer, msg);
+			if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);}
+			var subs = Gun.subs(soul) || null;
+			var tmp = subs ? subs.split(',') : [], p = at.opt.peers;
+			if (subs) {
+				Gun.obj.map(subs.split(','), function(peerid) {
+					if (peerid in p) { tmp.push(peerid); }
+				});
+			}
+			if (tmp.indexOf(peer.id) === -1) { tmp.push(peer.id);}
+			tmp = tmp.join(',');
+			Gun.subs(soul, tmp);
+			var dht = {};
+			dht[soul] = tmp;
+			at.opt.mesh.say({dht:dht}, peer);
 		}
 		to.next(msg);
 	}
-	/// Store the subscribes
-	Gun.subscribe = {}; /// TODO: use Rad instead of plain object?
-	function subscribe(soul, peer, msg) {
-		if (!peer.id) { console.log('super jump peer without id: ', peer, msg); return; } /// TODO: this occurs in first subscription. Use peer reference or peer.wire.id?
-		Gun.subscribe[soul] = Gun.subscribe[soul] || {};
-		Gun.subscribe[soul][peer.id] = 1;
-	}
 	var empty = {}, u;
 	if(Gun.window){ return }
 	try{module.exports = input}catch(e){}

+ 1 - 13
public/lib/gundb/lib/unbuild.js

@@ -11,19 +11,7 @@ var write = function(path, data){
 	return fs.writeFileSync(nodePath.join(dir, path), data);
 }
 
-var rm = function(path, full) {
-	path = full || nodePath.join(dir, path);
-  if(!fs.existsSync(path)){ return }
-  fs.readdirSync(path).forEach(function(file,index){
-    var curPath = path + "/" + file;
-    if(fs.lstatSync(curPath).isDirectory()) { // recurse
-      rm(null, curPath);
-    } else { // delete file
-      fs.unlinkSync(curPath);
-    }
-  });
-  fs.rmdirSync(path);
-};
+var rm = require('./fsrm');
 
 var mk = function(path){
 	path = nodePath.join(dir, path);

+ 12 - 6
public/lib/gundb/lib/webrtc.js

@@ -30,12 +30,17 @@
     ]};
     opt.rtc.dataChannel = opt.rtc.dataChannel || {ordered: false, maxRetransmits: 2};
     opt.rtc.sdp = opt.rtc.sdp || {mandatory: {OfferToReceiveAudio: false, OfferToReceiveVideo: false}};
-
+    opt.announce = function(to){
+      //setTimeout(function() {
+	console.log('[WEBRTC] announce ', opt.pid, to); 
+	root.on('out', {rtc: {id: opt.pid, to:to}});
+//      }, 1);
+    }; // announce ourself
 		var mesh = opt.mesh = opt.mesh || Gun.Mesh(root);
-		root.on('create', function(at){
-			this.to.next(at);
-			setTimeout(function(){ root.on('out', {rtc: {id: opt.pid}}) },1); // announce ourself
-		});
+// 		root.on('create', function(at){
+// 			this.to.next(at);
+// 			setTimeout(function(){ root.on('out', {rtc: {id: opt.pid}}) },1); // announce ourself
+// 		});
 		root.on('in', function(msg){
 			if(msg.rtc){ open(msg) }
 			this.to.next(msg);
@@ -44,6 +49,7 @@
 		function open(msg){
 			var rtc = msg.rtc, peer, tmp;
 			if(!rtc || !rtc.id){ return }
+			delete opt.announce[rtc.id]; /// remove after connect
 			if(tmp = rtc.answer){
 				if(!(peer = opt.peers[rtc.id]) || peer.remoteSet){ return }
 				return peer.setRemoteDescription(peer.remoteSet = new opt.RTCSessionDescription(tmp)); 
@@ -101,4 +107,4 @@
 		}
 	});
 	var noop = function(){};
-}());
+}());

+ 1 - 1
public/lib/gundb/sea.js

@@ -510,7 +510,7 @@
         key = pair.epriv || pair;
       }
       var msg = (typeof data == 'string')? data : JSON.stringify(data);
-      var rand = {s: shim.random(8), iv: shim.random(16)};
+      var rand = {s: shim.random(9), iv: shim.random(15)}; // consider making this 9 and 15 or 18 or 12 to reduce == padding.
       var ct = await aeskey(key, rand.s, opt).then((aes) => (/*shim.ossl ||*/ shim.subtle).encrypt({ // Keeping the AES key scope as private as possible...
         name: opt.name || 'AES-GCM', iv: new Uint8Array(rand.iv)
       }, aes, new shim.TextEncoder().encode(msg)));

+ 5 - 1
public/web/locale/en/index.html

@@ -18,9 +18,13 @@
     <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
       <div id="titleText">
         <h1 class="mdc-typography--headline3 mdc-theme--text-secondary-on-background mdc-typography"><a class="mdc-typography link-in-text"
-            style="cursor: pointer;" onclick="window.location.reload(true)"><strong>LiveCoding</strong>.space</a> | Application
+            style="cursor: pointer;" onclick="window.location.reload(true)"><strong>LiveCoding</strong>.space</a> | <span style="font-size: 16pt"> project by <a class="mdc-typography link-in-text"
+              style="cursor: pointer;">Krestianstvo.org</a> </span> 
           <!--<strong>LiveCoding</strong>.space -->
         </h1>
+        <h2>
+          [ <a class="mdc-typography link-in-text mdc-theme--text-hint-on-background" style="cursor:pointer" href="https://www.krestianstvo.org/sdk3">About </a> | <a class="mdc-typography link-in-text mdc-theme--text-hint-on-background" style="cursor:pointer" href="https://www.krestianstvo.org/docs/sdk3">Documentation </a>]
+        </h2>
       </div>
     </div>
   </div>

+ 5 - 1
public/web/locale/ru/index.html

@@ -18,9 +18,13 @@
     <div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
       <div id="titleText">
         <h1 class="mdc-typography--headline3 mdc-theme--text-secondary-on-background mdc-typography"><a class="mdc-typography link-in-text"
-            style="cursor: pointer;" onclick="window.location.reload(true)"><strong>LiveCoding</strong>.пространство</a>
+            style="cursor: pointer;" onclick="window.location.reload(true)"><strong>LiveCoding</strong>.space</a> | <span style="font-size: 16pt"> проект <a class="mdc-typography link-in-text"
+              style="cursor: pointer;">Krestianstvo.org</a> </span> 
           <!--<strong>LiveCoding</strong>.space -->
         </h1>
+        <h2>
+          [ <a class="mdc-typography link-in-text mdc-theme--text-hint-on-background" style="cursor:pointer" href="https://www.krestianstvo.org/ru/sdk3">О проекте </a> | <a class="mdc-typography link-in-text mdc-theme--text-hint-on-background" style="cursor:pointer" href="https://www.krestianstvo.org/docs/ru/sdk3">Документация </a>]
+        </h2>
       </div>
     </div>
   </div>

Some files were not shown because too many files changed in this diff