diff --git a/graph/index.html b/graph/index.html
new file mode 100644
index 0000000..b6c13ce
--- /dev/null
+++ b/graph/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Document
+
+
+
+
+
\ No newline at end of file
diff --git a/graph/storage.js b/graph/storage.js
new file mode 100644
index 0000000..3d3624b
--- /dev/null
+++ b/graph/storage.js
@@ -0,0 +1,69 @@
+/** @typedef {{Save:(key:string, data:object)=>void, Load:(key:string)=>object, ID:string, List:Record, Dump:()=>void}} SaveLoad */
+
+/** @type {Record} */
+const Master = {};
+const Prefix = "SL:";
+const Delimiter = "_"
+/** @type {(keyOuter:string)=>SaveLoad} */
+function SaveLoad(keyOuter)
+{
+ /** @type {SaveLoad} */
+ const sl = {
+ ID: keyOuter,
+ Save(key, data)
+ {
+ localStorage.setItem(Prefix+keyOuter+Delimiter+key, JSON.stringify(data));
+ },
+ Load(key)
+ {
+ const obj = JSON.parse( localStorage.getItem(Prefix+keyOuter+Delimiter+key) || "{}" );
+ this.List[key] = obj;
+ return obj;
+ },
+ List:{},
+ Dump(){this.List = {};}
+ };
+
+ Master[keyOuter] = sl;
+ return sl;
+}
+
+/** @type {()=>void} */
+function MassLoad()
+{
+ let loaded = 0;
+ let scanned = 0;
+ for (let i = 0; i < localStorage.length; i++) {
+
+ scanned++;
+
+ const fullKey = localStorage.key(i) || "";
+
+ if(fullKey.substring(0, Prefix.length) == Prefix)
+ {
+ const delim = fullKey.indexOf(Delimiter);
+ const typeKey = fullKey.substring(Prefix.length, delim);
+ const type = Master[typeKey];
+ if(type)
+ {
+ const actualKey = fullKey.substring(delim+1);
+ type.Load(actualKey) && loaded++;
+ }
+ }
+ }
+ console.log("mass load complete. loaded:", loaded, "scanned:", scanned);
+}
+
+const Node = SaveLoad("Node");
+const Link = SaveLoad("Link");
+MassLoad();
+console.log(Node.List);
+console.log(Link.List);
+
+
+
+Node.Save("n1", {name:"n1"});
+Node.Save("n2", {name:"n2"});
+Link.Save("l1", ["n1", "n2", {time:123}]);
+
+Node.Save("n1", {name:"n1", updated:true});
diff --git a/walker.js b/graph/walker.js
similarity index 100%
rename from walker.js
rename to graph/walker.js