@mblaney/holster 0.3.0 → 1.0.1
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 +19 -22
- package/src/holster.js +256 -112
- 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 +36 -2
- package/test/holster.on.test.js +46 -5
- package/test/holster.put.test.js +23 -2
- package/test/holster.secure.test.js +1 -9
- package/test/holster.user.get.test.js +31 -4
- package/test/holster.user.lex.test.js +2 -2
- package/test/holster.user.on.test.js +62 -4
- package/test/holster.user.put.test.js +125 -59
- package/test/wire.test.js +7 -8
package/examples/user.js
CHANGED
|
@@ -10,7 +10,7 @@ let alice, bob
|
|
|
10
10
|
// Wait for the websocket to connect.
|
|
11
11
|
setTimeout(() => {
|
|
12
12
|
user.create("alice", "password", err => {
|
|
13
|
-
if (err !== "Username already exists") {
|
|
13
|
+
if (err && err !== "Username already exists") {
|
|
14
14
|
console.log(err)
|
|
15
15
|
return
|
|
16
16
|
}
|
|
@@ -40,7 +40,7 @@ setTimeout(() => {
|
|
|
40
40
|
|
|
41
41
|
setTimeout(() => {
|
|
42
42
|
user.create("bob", "stronger password", err => {
|
|
43
|
-
if (err !== "Username already exists") {
|
|
43
|
+
if (err && err !== "Username already exists") {
|
|
44
44
|
console.log(err)
|
|
45
45
|
return
|
|
46
46
|
}
|
|
@@ -77,7 +77,7 @@ setTimeout(() => {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// Use Bob's public key to fetch shared data under his account.
|
|
80
|
-
|
|
80
|
+
user.get([bob.pub, "shared"], enc => {
|
|
81
81
|
user.auth("alice", "password", async err => {
|
|
82
82
|
if (err) {
|
|
83
83
|
console.log(err)
|
package/package.json
CHANGED
package/src/get.js
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
|
+
import {match} from "./utils.js"
|
|
2
|
+
|
|
1
3
|
const Get = (lex, graph) => {
|
|
2
4
|
const soul = lex["#"]
|
|
3
|
-
|
|
4
|
-
var node = graph[soul]
|
|
5
|
+
if (!graph[soul]) return
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
// have all the keys populated for a given soul. This is because Ham.mix
|
|
8
|
-
// only adds incoming changes to the graph.
|
|
9
|
-
if (!node || !key) return
|
|
7
|
+
const node = {_: {"#": soul, ">": {}}}
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
if (typeof lex["."] === "string") {
|
|
10
|
+
const key = lex["."]
|
|
11
|
+
if (typeof graph[soul][key] === "undefined") return
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
node[key] = graph[soul][key]
|
|
14
|
+
node._[">"][key] = graph[soul]._[">"][key]
|
|
15
|
+
} else {
|
|
16
|
+
for (const key of Object.keys(graph[soul])) {
|
|
17
|
+
if (match(lex["."], key)) {
|
|
18
|
+
node[key] = graph[soul][key]
|
|
19
|
+
node._[">"][key] = graph[soul]._[">"][key]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
16
23
|
return {[soul]: node}
|
|
17
24
|
}
|
|
18
25
|
|
package/src/ham.js
CHANGED
|
@@ -26,9 +26,9 @@ const Ham = (state, currentState, value, currentValue) => {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
Ham.mix = async (change, graph, secure, listen) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const machine = Date.now()
|
|
30
|
+
const now = {}
|
|
31
|
+
const defer = {}
|
|
32
32
|
let wait = 0
|
|
33
33
|
|
|
34
34
|
for (const soul of Object.keys(change)) {
|
|
@@ -36,13 +36,14 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
36
36
|
let updated = false
|
|
37
37
|
let alias = false
|
|
38
38
|
let nodeWait = 0
|
|
39
|
-
let pub = ""
|
|
40
39
|
let verify = secure
|
|
41
40
|
|
|
42
41
|
if (!node || !node._) continue
|
|
43
42
|
|
|
43
|
+
const sig = node[utils.userSignature]
|
|
44
|
+
const pub = node[utils.userPublicKey]
|
|
44
45
|
// If a signature and public key are provided then always verify.
|
|
45
|
-
if (
|
|
46
|
+
if (sig && pub) verify = true
|
|
46
47
|
|
|
47
48
|
// Special case if soul starts with "~". Node must be system data ie,
|
|
48
49
|
// ~@alias or ~publickey. For aliases, key and value must be a self
|
|
@@ -53,26 +54,19 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
53
54
|
alias = true
|
|
54
55
|
verify = false
|
|
55
56
|
} else {
|
|
56
|
-
if (
|
|
57
|
+
if (pub && soul != "~" + pub) {
|
|
57
58
|
console.log(`error public key does not match for soul: ${soul}`)
|
|
58
59
|
continue
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
// Need to be able to create public key data before a user is
|
|
62
|
-
// authenticated, in this case the soul is the public key which
|
|
63
|
-
// should've been used to sign the data.
|
|
64
|
-
node._.p = soul.slice(1)
|
|
65
62
|
verify = true
|
|
66
63
|
}
|
|
67
64
|
}
|
|
68
65
|
if (verify) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (!(await SEA.verify({m: node, s: node._.s}, {pub: node._.p}))) {
|
|
75
|
-
console.log(`error could not verify soul: ${soul}`)
|
|
66
|
+
// Partial nodes can be read from disk, which are ignored. This could be
|
|
67
|
+
// a missing signature or public key, or missing properties on the node
|
|
68
|
+
// which means verify won't work either.
|
|
69
|
+
if (!sig || !pub || !(await SEA.verify({m: node, s: sig}, {pub: pub}))) {
|
|
76
70
|
continue
|
|
77
71
|
}
|
|
78
72
|
}
|
|
@@ -81,11 +75,14 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
81
75
|
if (key === "_") continue
|
|
82
76
|
|
|
83
77
|
const value = node[key]
|
|
84
|
-
const state = node._[">"][key]
|
|
78
|
+
const state = node._ && node._[">"] ? node._[">"][key] : 0
|
|
85
79
|
const currentValue = (graph[soul] || {})[key]
|
|
86
80
|
const currentState = (graph[soul] || {_: {">": {}}})._[">"][key] || 0
|
|
87
81
|
|
|
88
|
-
if (alias && key !== utils.rel.is(value))
|
|
82
|
+
if (alias && key !== utils.rel.is(value)) {
|
|
83
|
+
console.log(`error alias ${alias}: ${key} !== ${utils.rel.is(value)}`)
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
89
86
|
|
|
90
87
|
// Defer the update if ahead of machine time.
|
|
91
88
|
const skew = state - machine
|
|
@@ -96,7 +93,7 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
96
93
|
// Wait the shortest difference before trying the updates again.
|
|
97
94
|
if (wait === 0 || skew < wait) wait = nodeWait = skew
|
|
98
95
|
if (!defer[soul]) {
|
|
99
|
-
defer[soul] = {_: {"#": soul, ">": {}
|
|
96
|
+
defer[soul] = {_: {"#": soul, ">": {}}}
|
|
100
97
|
}
|
|
101
98
|
defer[soul][key] = value
|
|
102
99
|
defer[soul]._[">"][key] = state
|
|
@@ -104,13 +101,13 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
104
101
|
const result = Ham(state, currentState, value, currentValue)
|
|
105
102
|
if (result.incoming) {
|
|
106
103
|
if (!now[soul]) {
|
|
107
|
-
now[soul] = {_: {"#": soul, ">": {}
|
|
104
|
+
now[soul] = {_: {"#": soul, ">": {}}}
|
|
108
105
|
}
|
|
109
106
|
// TODO: graph should not just grow indefintitely in memory.
|
|
110
107
|
// Need to have a max size after which start dropping the oldest state
|
|
111
108
|
// Do something similar to Dup which can handle deletes?
|
|
112
109
|
if (!graph[soul]) {
|
|
113
|
-
graph[soul] = {_: {"#": soul, ">": {}
|
|
110
|
+
graph[soul] = {_: {"#": soul, ">": {}}}
|
|
114
111
|
}
|
|
115
112
|
graph[soul][key] = now[soul][key] = value
|
|
116
113
|
graph[soul]._[">"][key] = now[soul]._[">"][key] = state
|