@mblaney/holster 0.2.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 +246 -121
- package/src/index.js +0 -1
- package/src/sea-utils.js +1 -1
- package/src/sea.js +16 -4
- package/src/store.js +1 -1
- 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 +129 -49
- 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,25 +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
|
-
continue
|
|
72
|
-
}
|
|
66
|
+
// Partial nodes can be read from disk, just ignore them.
|
|
67
|
+
if (!sig || !pub) continue
|
|
73
68
|
|
|
74
|
-
if (!(await SEA.verify({m: node, s:
|
|
69
|
+
if (!(await SEA.verify({m: node, s: sig}, {pub: pub}))) {
|
|
75
70
|
console.log(`error could not verify soul: ${soul}`)
|
|
76
71
|
continue
|
|
77
72
|
}
|
|
@@ -81,11 +76,14 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
81
76
|
if (key === "_") continue
|
|
82
77
|
|
|
83
78
|
const value = node[key]
|
|
84
|
-
const state = node._[">"][key]
|
|
79
|
+
const state = node._ && node._[">"] ? node._[">"][key] : 0
|
|
85
80
|
const currentValue = (graph[soul] || {})[key]
|
|
86
81
|
const currentState = (graph[soul] || {_: {">": {}}})._[">"][key] || 0
|
|
87
82
|
|
|
88
|
-
if (alias && key !== utils.rel.is(value))
|
|
83
|
+
if (alias && key !== utils.rel.is(value)) {
|
|
84
|
+
console.log(`error alias ${alias}: ${key} !== ${utils.rel.is(value)}`)
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
89
87
|
|
|
90
88
|
// Defer the update if ahead of machine time.
|
|
91
89
|
const skew = state - machine
|
|
@@ -96,7 +94,7 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
96
94
|
// Wait the shortest difference before trying the updates again.
|
|
97
95
|
if (wait === 0 || skew < wait) wait = nodeWait = skew
|
|
98
96
|
if (!defer[soul]) {
|
|
99
|
-
defer[soul] = {_: {"#": soul, ">": {}
|
|
97
|
+
defer[soul] = {_: {"#": soul, ">": {}}}
|
|
100
98
|
}
|
|
101
99
|
defer[soul][key] = value
|
|
102
100
|
defer[soul]._[">"][key] = state
|
|
@@ -104,13 +102,13 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
104
102
|
const result = Ham(state, currentState, value, currentValue)
|
|
105
103
|
if (result.incoming) {
|
|
106
104
|
if (!now[soul]) {
|
|
107
|
-
now[soul] = {_: {"#": soul, ">": {}
|
|
105
|
+
now[soul] = {_: {"#": soul, ">": {}}}
|
|
108
106
|
}
|
|
109
107
|
// TODO: graph should not just grow indefintitely in memory.
|
|
110
108
|
// Need to have a max size after which start dropping the oldest state
|
|
111
109
|
// Do something similar to Dup which can handle deletes?
|
|
112
110
|
if (!graph[soul]) {
|
|
113
|
-
graph[soul] = {_: {"#": soul, ">": {}
|
|
111
|
+
graph[soul] = {_: {"#": soul, ">": {}}}
|
|
114
112
|
}
|
|
115
113
|
graph[soul][key] = now[soul][key] = value
|
|
116
114
|
graph[soul]._[">"][key] = now[soul]._[">"][key] = state
|