@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/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
- holster.user(bob.pub).get("shared", enc => {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mblaney/holster",
3
- "version": "0.3.0",
3
+ "version": "1.0.1",
4
4
  "description": "Holster is a service that allows synchronising data between devices using Node, Deno, Bun or the browser.",
5
5
  "type": "module",
6
6
  "main": "index.js",
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
- const key = typeof lex["."] === "string" ? lex["."] : false
4
- var node = graph[soul]
5
+ if (!graph[soul]) return
5
6
 
6
- // Can only return a node if a key is provided, because the graph may not
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
- let value = node[key]
12
- if (!value) return
9
+ if (typeof lex["."] === "string") {
10
+ const key = lex["."]
11
+ if (typeof graph[soul][key] === "undefined") return
13
12
 
14
- node = {_: node._, [key]: value}
15
- node._[">"] = {[key]: node._[">"][key]}
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
- var machine = Date.now()
30
- var now = {}
31
- var defer = {}
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 (node._.s && node._.p) verify = true
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 (node._.p && soul != "~" + node._.p) {
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
- if (!node._.s || !node._.p) {
70
- console.log("error signature and public key required to verify data")
71
- continue
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)) continue
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, ">": {}, s: node._.s, p: node._.p}}
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, ">": {}, s: node._.s, p: node._.p}}
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, ">": {}, s: node._.s, p: node._.p}}
110
+ graph[soul] = {_: {"#": soul, ">": {}}}
114
111
  }
115
112
  graph[soul][key] = now[soul][key] = value
116
113
  graph[soul]._[">"][key] = now[soul]._[">"][key] = state