@mblaney/holster 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/holster.js +3 -3
- package/build/holster.js.map +3 -3
- package/examples/user.js +3 -3
- package/package.json +1 -1
- package/src/get.js +17 -10
- package/src/ham.js +18 -20
- package/src/holster.js +214 -91
- package/src/sea.js +16 -4
- package/src/user.js +21 -22
- package/src/utils.js +13 -6
- package/src/wire.js +21 -15
- package/test/holster.get.test.js +32 -2
- package/test/holster.on.test.js +46 -5
- package/test/holster.put.test.js +19 -2
- package/test/holster.secure.test.js +1 -9
- package/test/holster.user.get.test.js +27 -4
- package/test/holster.user.lex.test.js +2 -2
- package/test/holster.user.on.test.js +58 -4
- package/test/holster.user.put.test.js +121 -59
- package/test/wire.test.js +7 -8
package/src/utils.js
CHANGED
|
@@ -68,19 +68,26 @@ export const rel = {
|
|
|
68
68
|
ify: soul => obj.put({}, "#", soul),
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
export const userSignature = "_holster_user_signature"
|
|
71
72
|
export const userPublicKey = "_holster_user_public_key"
|
|
72
73
|
|
|
73
74
|
// graph converts objects to graph format with updated states,
|
|
74
75
|
// with optional meta data to verify signed data.
|
|
75
76
|
export const graph = (soul, data, sig, pub) => {
|
|
76
|
-
const g = {[soul]: {_: {"#": soul, ">": {}
|
|
77
|
+
const g = {[soul]: {_: {"#": soul, ">": {}}}}
|
|
77
78
|
for (const [key, value] of Object.entries(data)) {
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
if (key !== "_" && key != userPublicKey && key != userSignature) {
|
|
80
|
+
g[soul][key] = value
|
|
81
|
+
g[soul]._[">"][key] = Date.now()
|
|
82
|
+
}
|
|
80
83
|
}
|
|
81
|
-
// If a public key
|
|
82
|
-
// ensure that future updates are only possible with the same
|
|
83
|
-
|
|
84
|
+
// If a signature and public key are provided they also need to be stored on
|
|
85
|
+
// the node to ensure that future updates are only possible with the same
|
|
86
|
+
// public key. The signature is requried because later get requests will
|
|
87
|
+
// broadcast the data as a put, which other devices will need to verify.
|
|
88
|
+
if (sig && pub) {
|
|
89
|
+
g[soul][userSignature] = sig
|
|
90
|
+
g[soul]._[">"][userSignature] = Date.now()
|
|
84
91
|
g[soul][userPublicKey] = pub
|
|
85
92
|
g[soul]._[">"][userPublicKey] = Date.now()
|
|
86
93
|
}
|
package/src/wire.js
CHANGED
|
@@ -37,13 +37,12 @@ const Wire = opt => {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const node = data[soul]
|
|
40
|
-
const pub = node._ ? node._.p : undefined
|
|
41
40
|
const key = utils.userPublicKey
|
|
42
41
|
// If there is no current node then the data is ok to write without
|
|
43
42
|
// matching public keys, as the provided soul also needs a rel on the
|
|
44
43
|
// parent node which then also requires checking. Otherwise publc keys
|
|
45
44
|
// need to match for existing data.
|
|
46
|
-
if (!msg.put || !msg.put[soul] || msg.put[soul][key] ===
|
|
45
|
+
if (!msg.put || !msg.put[soul] || msg.put[soul][key] === node[key]) {
|
|
47
46
|
continue
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -107,32 +106,38 @@ const Wire = opt => {
|
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
const getWithCallback = (lex, cb, send,
|
|
109
|
+
const getWithCallback = (lex, cb, send, _opt) => {
|
|
111
110
|
if (!cb) return
|
|
112
111
|
|
|
113
|
-
if (!utils.obj.is(
|
|
112
|
+
if (!utils.obj.is(_opt)) _opt = {}
|
|
113
|
+
|
|
114
114
|
const ack = Get(lex, graph)
|
|
115
|
+
const track = utils.text.random(9)
|
|
116
|
+
// Request the whole node in secure mode for verification.
|
|
117
|
+
const request = JSON.stringify({
|
|
118
|
+
"#": dup.track(track),
|
|
119
|
+
get: opt.secure ? {"#": lex["#"]} : lex,
|
|
120
|
+
})
|
|
121
|
+
|
|
115
122
|
if (ack) {
|
|
123
|
+
// Also send request on the wire to check for updates.
|
|
124
|
+
send(request)
|
|
116
125
|
cb({put: ack})
|
|
117
126
|
return
|
|
118
127
|
}
|
|
119
128
|
|
|
120
129
|
store.get(lex, (err, ack) => {
|
|
121
130
|
if (ack) {
|
|
131
|
+
// Also send request on the wire to check for updates.
|
|
132
|
+
send(request)
|
|
122
133
|
cb({put: ack, err: err})
|
|
123
134
|
return
|
|
124
135
|
}
|
|
125
136
|
|
|
126
137
|
if (err) console.log(err)
|
|
127
138
|
|
|
128
|
-
const track = utils.text.random(9)
|
|
129
139
|
queue[track] = cb
|
|
130
|
-
send(
|
|
131
|
-
JSON.stringify({
|
|
132
|
-
"#": dup.track(track),
|
|
133
|
-
get: lex,
|
|
134
|
-
}),
|
|
135
|
-
)
|
|
140
|
+
send(request)
|
|
136
141
|
// Respond to callback with null if no response.
|
|
137
142
|
setTimeout(() => {
|
|
138
143
|
const cb = queue[track]
|
|
@@ -143,14 +148,14 @@ const Wire = opt => {
|
|
|
143
148
|
cb({put: ack})
|
|
144
149
|
delete queue[track]
|
|
145
150
|
}
|
|
146
|
-
},
|
|
151
|
+
}, _opt.wait || 100)
|
|
147
152
|
})
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
const api = send => {
|
|
151
156
|
return {
|
|
152
|
-
get: (lex, cb,
|
|
153
|
-
getWithCallback(lex, cb, send,
|
|
157
|
+
get: (lex, cb, _opt) => {
|
|
158
|
+
getWithCallback(lex, cb, send, _opt)
|
|
154
159
|
},
|
|
155
160
|
put: async (data, cb) => {
|
|
156
161
|
// Deferred updates are only stored using wire spec, they're ignored
|
|
@@ -172,7 +177,7 @@ const Wire = opt => {
|
|
|
172
177
|
}),
|
|
173
178
|
)
|
|
174
179
|
},
|
|
175
|
-
on: (lex, cb) => {
|
|
180
|
+
on: (lex, cb, _get, _opt) => {
|
|
176
181
|
const soul = lex && lex["#"]
|
|
177
182
|
if (!soul || !cb) return
|
|
178
183
|
|
|
@@ -181,6 +186,7 @@ const Wire = opt => {
|
|
|
181
186
|
} else {
|
|
182
187
|
listen[soul] = [{".": lex["."], cb: cb}]
|
|
183
188
|
}
|
|
189
|
+
if (_get) getWithCallback(lex, cb, send, _opt)
|
|
184
190
|
},
|
|
185
191
|
off: (lex, cb) => {
|
|
186
192
|
const soul = lex && lex["#"]
|
package/test/holster.get.test.js
CHANGED
|
@@ -36,8 +36,27 @@ describe("holster.get", () => {
|
|
|
36
36
|
})
|
|
37
37
|
})
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
test("get unknown keys in for loop callbacks null", (t, done) => {
|
|
40
|
+
for (let i = 0; i < 5; i++) {
|
|
41
|
+
holster.get("unknown" + i, data => {
|
|
42
|
+
assert.equal(data, null)
|
|
43
|
+
if (i === 4) done()
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test("nested unknown keys both callbacks null", (t, done) => {
|
|
49
|
+
holster.get("unknown", data => {
|
|
50
|
+
assert.equal(data, null)
|
|
51
|
+
|
|
52
|
+
holster.get("unknown", data => {
|
|
53
|
+
assert.equal(data, null)
|
|
54
|
+
done()
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test("get already called callback null", (t, done) => {
|
|
41
60
|
holster.get("chained").get("unknown", data => {
|
|
42
61
|
assert.equal(data, null)
|
|
43
62
|
done()
|
|
@@ -51,6 +70,17 @@ describe("holster.get", () => {
|
|
|
51
70
|
})
|
|
52
71
|
})
|
|
53
72
|
|
|
73
|
+
test("nested next chained unknown keys callback null", (t, done) => {
|
|
74
|
+
holster.get("chained").next("unknown", data => {
|
|
75
|
+
assert.equal(data, null)
|
|
76
|
+
|
|
77
|
+
holster.get("chained").next("unknown", data => {
|
|
78
|
+
assert.equal(data, null)
|
|
79
|
+
done()
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
54
84
|
test("cleanup", (t, done) => {
|
|
55
85
|
fs.rm("test/holster.get", {recursive: true, force: true}, err => {
|
|
56
86
|
assert.equal(err, null)
|
package/test/holster.on.test.js
CHANGED
|
@@ -37,10 +37,12 @@ describe("holster.on", () => {
|
|
|
37
37
|
done() // This done is not called and test would fail if it was.
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
holster.get("key2").put("value2", err => {
|
|
42
|
+
assert.equal(err, null)
|
|
43
|
+
done()
|
|
44
|
+
})
|
|
45
|
+
}, 100)
|
|
44
46
|
})
|
|
45
47
|
|
|
46
48
|
test("on for property on root two updates - two events", (t, done) => {
|
|
@@ -70,6 +72,45 @@ describe("holster.on", () => {
|
|
|
70
72
|
})
|
|
71
73
|
})
|
|
72
74
|
|
|
75
|
+
test("on for properties on root in for loop", (t, done) => {
|
|
76
|
+
for (let i = 0; i < 5; i++) {
|
|
77
|
+
holster.get("for" + i).put(i, err => {
|
|
78
|
+
assert.equal(err, null)
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Need to wait until after all initial puts are done to set on listeners.
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
for (let i = 0; i < 5; i++) {
|
|
85
|
+
holster.get("for" + i).on(data => {
|
|
86
|
+
assert.equal(data, "update" + i)
|
|
87
|
+
if (i === 4) done()
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setTimeout(() => {
|
|
92
|
+
for (let i = 0; i < 5; i++) {
|
|
93
|
+
holster.get("for" + i).put("update" + i, err => {
|
|
94
|
+
assert.equal(err, null)
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
}, 100)
|
|
98
|
+
}, 500)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test("on with get flag set - data returned", (t, done) => {
|
|
102
|
+
holster.get("key4").put("value4", err => {
|
|
103
|
+
assert.equal(err, null)
|
|
104
|
+
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
holster.get("key4").on(data => {
|
|
107
|
+
assert.equal(data, "value4")
|
|
108
|
+
done()
|
|
109
|
+
}, true)
|
|
110
|
+
}, 100)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
73
114
|
test("chained next before on", (t, done) => {
|
|
74
115
|
// The node needs to exist before it can be listend to for updates.
|
|
75
116
|
holster
|
|
@@ -140,7 +181,7 @@ describe("holster.on", () => {
|
|
|
140
181
|
},
|
|
141
182
|
}
|
|
142
183
|
holster.get("nested").on(data => {
|
|
143
|
-
assert.deepEqual(data, update)
|
|
184
|
+
assert.deepEqual(data, {key: nested.key, ...update})
|
|
144
185
|
})
|
|
145
186
|
|
|
146
187
|
holster
|
package/test/holster.put.test.js
CHANGED
|
@@ -63,6 +63,23 @@ describe("holster.put", () => {
|
|
|
63
63
|
})
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
+
test("put and get values in for loop on root", (t, done) => {
|
|
67
|
+
for (let i = 0; i < 5; i++) {
|
|
68
|
+
holster.get("for" + i).put(i, err => {
|
|
69
|
+
assert.equal(err, null)
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setTimeout(() => {
|
|
74
|
+
for (let i = 0; i < 5; i++) {
|
|
75
|
+
holster.get("for" + i, data => {
|
|
76
|
+
assert.equal(data, i)
|
|
77
|
+
if (i === 4) setTimeout(done, 100)
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
}, 200)
|
|
81
|
+
})
|
|
82
|
+
|
|
66
83
|
test("chained get before put", (t, done) => {
|
|
67
84
|
holster
|
|
68
85
|
.get("hello")
|
|
@@ -269,7 +286,7 @@ describe("holster.put", () => {
|
|
|
269
286
|
})
|
|
270
287
|
})
|
|
271
288
|
|
|
272
|
-
test("
|
|
289
|
+
test("put string with set", (t, done) => {
|
|
273
290
|
holster.get("set").put("first", true, err => {
|
|
274
291
|
assert.equal(err, null)
|
|
275
292
|
|
|
@@ -292,7 +309,7 @@ describe("holster.put", () => {
|
|
|
292
309
|
})
|
|
293
310
|
})
|
|
294
311
|
|
|
295
|
-
test("
|
|
312
|
+
test("put object with set", (t, done) => {
|
|
296
313
|
const set1 = {
|
|
297
314
|
key: "value 1",
|
|
298
315
|
child: "child value 1",
|
|
@@ -43,14 +43,6 @@ describe("holster.secure", () => {
|
|
|
43
43
|
})
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
// Chaining get creates a new context for each call.
|
|
47
|
-
test("get chained unknown keys callback null", (t, done) => {
|
|
48
|
-
holster.get("chained").get("unknown", data => {
|
|
49
|
-
assert.equal(data, null)
|
|
50
|
-
done()
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
46
|
test("next chained unknown keys callback null", (t, done) => {
|
|
55
47
|
holster.get("chained").next("unknown", data => {
|
|
56
48
|
assert.equal(data, null)
|
|
@@ -102,7 +94,7 @@ describe("holster.secure", () => {
|
|
|
102
94
|
})
|
|
103
95
|
})
|
|
104
96
|
|
|
105
|
-
test("put and get string", (t, done) => {
|
|
97
|
+
test("user put and get string", (t, done) => {
|
|
106
98
|
user.get("key").put("value", err => {
|
|
107
99
|
assert.equal(err, null)
|
|
108
100
|
|
|
@@ -62,11 +62,23 @@ describe("holster.user.get", () => {
|
|
|
62
62
|
})
|
|
63
63
|
})
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
test("get unknown keys in for loop callbacks null", (t, done) => {
|
|
66
|
+
for (let i = 0; i < 5; i++) {
|
|
67
|
+
user.get("unknown" + i, data => {
|
|
68
|
+
assert.equal(data, null)
|
|
69
|
+
if (i === 4) done()
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("nested get unknown key callback null", (t, done) => {
|
|
75
|
+
user.get("unknown", data => {
|
|
68
76
|
assert.equal(data, null)
|
|
69
|
-
|
|
77
|
+
|
|
78
|
+
user.get("unknown", data => {
|
|
79
|
+
assert.equal(data, null)
|
|
80
|
+
done()
|
|
81
|
+
})
|
|
70
82
|
})
|
|
71
83
|
})
|
|
72
84
|
|
|
@@ -77,6 +89,17 @@ describe("holster.user.get", () => {
|
|
|
77
89
|
})
|
|
78
90
|
})
|
|
79
91
|
|
|
92
|
+
test("nested next chained unknown keys callback null", (t, done) => {
|
|
93
|
+
user.get("chained").next("unknown", data => {
|
|
94
|
+
assert.equal(data, null)
|
|
95
|
+
|
|
96
|
+
user.get("chained").next("unknown", data => {
|
|
97
|
+
assert.equal(data, null)
|
|
98
|
+
done()
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
80
103
|
test("cleanup", (t, done) => {
|
|
81
104
|
fs.rm("test/holster.user.get", {recursive: true, force: true}, err => {
|
|
82
105
|
assert.equal(err, null)
|
|
@@ -12,7 +12,6 @@ describe("holster.user.lex", () => {
|
|
|
12
12
|
maxAge: 100,
|
|
13
13
|
})
|
|
14
14
|
const user = holster.user()
|
|
15
|
-
let alice = ""
|
|
16
15
|
|
|
17
16
|
test("user create", (t, done) => {
|
|
18
17
|
user.create("alice", "password", err => {
|
|
@@ -25,7 +24,6 @@ describe("holster.user.lex", () => {
|
|
|
25
24
|
user.auth("alice", "password", err => {
|
|
26
25
|
assert.equal(err, null)
|
|
27
26
|
assert.equal(user.is.username, "alice")
|
|
28
|
-
alice = user.is.pub
|
|
29
27
|
done()
|
|
30
28
|
})
|
|
31
29
|
})
|
|
@@ -84,6 +82,7 @@ describe("holster.user.lex", () => {
|
|
|
84
82
|
other: "other child value",
|
|
85
83
|
},
|
|
86
84
|
}
|
|
85
|
+
|
|
87
86
|
user.get("nested").put(nested, err => {
|
|
88
87
|
assert.equal(err, null)
|
|
89
88
|
|
|
@@ -123,6 +122,7 @@ describe("holster.user.lex", () => {
|
|
|
123
122
|
has: "child update",
|
|
124
123
|
},
|
|
125
124
|
}
|
|
125
|
+
|
|
126
126
|
user
|
|
127
127
|
.get("on-nested")
|
|
128
128
|
.next("child")
|
|
@@ -69,6 +69,7 @@ describe("holster.user.on", () => {
|
|
|
69
69
|
assert.equal(data, "update1")
|
|
70
70
|
} else {
|
|
71
71
|
assert.equal(data, "update2")
|
|
72
|
+
user.get("key3").off()
|
|
72
73
|
done()
|
|
73
74
|
}
|
|
74
75
|
})
|
|
@@ -86,6 +87,54 @@ describe("holster.user.on", () => {
|
|
|
86
87
|
})
|
|
87
88
|
})
|
|
88
89
|
|
|
90
|
+
test("on for properties in for loop", (t, done) => {
|
|
91
|
+
;(async () => {
|
|
92
|
+
for (let i = 0; i < 5; i++) {
|
|
93
|
+
// Need to be careful putting user properties in a loop as they are
|
|
94
|
+
// verified per node, so wait for each update to return.
|
|
95
|
+
const err = await new Promise(res => {
|
|
96
|
+
user.get("for" + i).put(i, res)
|
|
97
|
+
})
|
|
98
|
+
assert.equal(err, null)
|
|
99
|
+
}
|
|
100
|
+
})()
|
|
101
|
+
|
|
102
|
+
// Need to wait until after all initial puts are done to set on listeners.
|
|
103
|
+
setTimeout(async () => {
|
|
104
|
+
for (let i = 0; i < 5; i++) {
|
|
105
|
+
user.get("for" + i).on(data => {
|
|
106
|
+
assert.equal(data, "update" + i)
|
|
107
|
+
user.get("for" + i).off()
|
|
108
|
+
if (i === 4) done()
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setTimeout(async () => {
|
|
113
|
+
for (let i = 0; i < 5; i++) {
|
|
114
|
+
// Same as above put, wait for each update to return.
|
|
115
|
+
const err = await new Promise(res => {
|
|
116
|
+
user.get("for" + i).put("update" + i, res)
|
|
117
|
+
})
|
|
118
|
+
assert.equal(err, null)
|
|
119
|
+
}
|
|
120
|
+
}, 500)
|
|
121
|
+
}, 1000)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test("on with get flag set - data returned", (t, done) => {
|
|
125
|
+
user.get("key4").put("value4", err => {
|
|
126
|
+
assert.equal(err, null)
|
|
127
|
+
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
user.get("key4").on(data => {
|
|
130
|
+
assert.equal(data, "value4")
|
|
131
|
+
user.get("key4").off()
|
|
132
|
+
done()
|
|
133
|
+
}, true)
|
|
134
|
+
}, 100)
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
89
138
|
test("chained next before on", (t, done) => {
|
|
90
139
|
// The node needs to exist before it can be listend to for updates.
|
|
91
140
|
user
|
|
@@ -119,6 +168,7 @@ describe("holster.user.on", () => {
|
|
|
119
168
|
key: "plain value",
|
|
120
169
|
number: 42,
|
|
121
170
|
}
|
|
171
|
+
|
|
122
172
|
user.get("plain").put(plain, err => {
|
|
123
173
|
assert.equal(err, null)
|
|
124
174
|
|
|
@@ -126,6 +176,7 @@ describe("holster.user.on", () => {
|
|
|
126
176
|
key: "update",
|
|
127
177
|
number: 42,
|
|
128
178
|
}
|
|
179
|
+
|
|
129
180
|
user.get("plain").on(data => {
|
|
130
181
|
assert.deepEqual(data, update)
|
|
131
182
|
done()
|
|
@@ -156,6 +207,7 @@ describe("holster.user.on", () => {
|
|
|
156
207
|
has: "child update",
|
|
157
208
|
},
|
|
158
209
|
}
|
|
210
|
+
|
|
159
211
|
user.get("nested").on(data => {
|
|
160
212
|
assert.deepEqual(data, update)
|
|
161
213
|
})
|
|
@@ -177,9 +229,11 @@ describe("holster.user.on", () => {
|
|
|
177
229
|
})
|
|
178
230
|
|
|
179
231
|
test("cleanup", (t, done) => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
232
|
+
setTimeout(() => {
|
|
233
|
+
fs.rm("test/holster.user.on", {recursive: true, force: true}, err => {
|
|
234
|
+
assert.equal(err, null)
|
|
235
|
+
done()
|
|
236
|
+
})
|
|
237
|
+
}, 100)
|
|
184
238
|
})
|
|
185
239
|
})
|