@mblaney/holster 2.0.2 → 2.1.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/README.md +43 -6
- package/build/holster.js +3 -3
- package/build/holster.js.map +3 -3
- package/dist/holster.js +3 -3
- package/dist/holster.js.map +3 -3
- package/package.json +2 -2
- package/src/ham.js +10 -3
- package/src/holster.js +36 -25
- package/src/radisk.js +26 -12
- package/src/user.js +92 -101
- package/src/wire.js +300 -104
- package/ts/src/array.ts +10 -13
- package/ts/src/buffer.ts +40 -20
- package/ts/src/get.ts +5 -6
- package/ts/src/ham.ts +47 -28
- package/ts/src/holster.ts +277 -132
- package/ts/src/index.ts +14 -15
- package/ts/src/radisk.ts +122 -37
- package/ts/src/radix.ts +50 -34
- package/ts/src/schemas.ts +44 -24
- package/ts/src/sea-utils.ts +7 -11
- package/ts/src/sea.ts +51 -54
- package/ts/src/store.ts +48 -42
- package/ts/src/user.ts +136 -113
- package/ts/src/utils.ts +36 -11
- package/ts/src/wire.ts +455 -232
package/ts/src/user.ts
CHANGED
|
@@ -6,37 +6,57 @@
|
|
|
6
6
|
import * as utils from "./utils.ts"
|
|
7
7
|
import Wire from "./wire.ts"
|
|
8
8
|
import SEA from "./sea.ts"
|
|
9
|
-
import type {
|
|
10
|
-
|
|
9
|
+
import type {
|
|
10
|
+
UserIdentity,
|
|
11
|
+
HolsterOptions,
|
|
12
|
+
LexFilter,
|
|
13
|
+
WireOptions,
|
|
14
|
+
} from "./schemas.ts"
|
|
15
|
+
import type {HolsterAPI} from "./holster.ts"
|
|
11
16
|
|
|
12
17
|
type WireInterface = ReturnType<typeof Wire>
|
|
13
18
|
|
|
14
19
|
export interface UserInterface {
|
|
15
20
|
is?: UserIdentity | null
|
|
16
|
-
create: (
|
|
17
|
-
|
|
21
|
+
create: (
|
|
22
|
+
username: string,
|
|
23
|
+
password: string,
|
|
24
|
+
cb?: (err?: string | null) => void,
|
|
25
|
+
) => void
|
|
26
|
+
auth: (
|
|
27
|
+
username: string,
|
|
28
|
+
password: string,
|
|
29
|
+
cb?: (err?: string | null) => void,
|
|
30
|
+
) => void
|
|
18
31
|
change: (
|
|
19
32
|
username: string,
|
|
20
33
|
password: string,
|
|
21
34
|
newPassword: string,
|
|
22
|
-
cb?: (err?: string | null) => void
|
|
35
|
+
cb?: (err?: string | null) => void,
|
|
23
36
|
) => void
|
|
24
37
|
store: (localStorage?: boolean) => void
|
|
25
38
|
recall: () => void
|
|
26
39
|
leave: () => void
|
|
27
|
-
delete: (
|
|
40
|
+
delete: (
|
|
41
|
+
username: string,
|
|
42
|
+
password: string,
|
|
43
|
+
cb?: (err?: string | null) => void,
|
|
44
|
+
) => void
|
|
28
45
|
get?(
|
|
29
46
|
keys: string | string[],
|
|
30
47
|
lex?: LexFilter | ((data: unknown) => void),
|
|
31
48
|
cb?: ((data: unknown) => void) | WireOptions,
|
|
32
|
-
_opt?: WireOptions
|
|
49
|
+
_opt?: WireOptions,
|
|
33
50
|
): HolsterAPI
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
/**
|
|
37
54
|
* Create User management interface
|
|
38
55
|
*/
|
|
39
|
-
const User = (
|
|
56
|
+
const User = (
|
|
57
|
+
opt: HolsterOptions | undefined,
|
|
58
|
+
wire?: WireInterface,
|
|
59
|
+
): UserInterface => {
|
|
40
60
|
if (!wire) wire = Wire(opt as HolsterOptions)
|
|
41
61
|
let pubs: string[] = []
|
|
42
62
|
let creating = false
|
|
@@ -47,7 +67,7 @@ const User = (opt: HolsterOptions | undefined, wire?: WireInterface): UserInterf
|
|
|
47
67
|
username: string,
|
|
48
68
|
password: string,
|
|
49
69
|
newPassword: string,
|
|
50
|
-
ack: (err?: string | null) => void
|
|
70
|
+
ack: (err?: string | null) => void,
|
|
51
71
|
): void => {
|
|
52
72
|
const retry = (username: string): void => {
|
|
53
73
|
retries++
|
|
@@ -66,57 +86,61 @@ const User = (opt: HolsterOptions | undefined, wire?: WireInterface): UserInterf
|
|
|
66
86
|
}
|
|
67
87
|
|
|
68
88
|
const pub = pubs.shift()!
|
|
69
|
-
wire.get(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return
|
|
75
|
-
}
|
|
89
|
+
wire.get({"#": pub, ".": ["auth", "pub", "epub"]}, async msg => {
|
|
90
|
+
if (msg.err) {
|
|
91
|
+
done(`error getting ${pub}: ${msg.err}`)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
76
94
|
|
|
77
|
-
|
|
78
|
-
|
|
95
|
+
const data = msg.put && msg.put[pub]
|
|
96
|
+
if (!data || !data.auth) return next()
|
|
79
97
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
const authData = JSON.parse(data.auth as string) as {
|
|
99
|
+
enc: {ct: string; iv: string; s: string}
|
|
100
|
+
salt: string
|
|
101
|
+
}
|
|
102
|
+
const work = await SEA.work(password, authData.salt)
|
|
103
|
+
const dec = (await SEA.decrypt(authData.enc, work)) as {
|
|
104
|
+
priv: string
|
|
105
|
+
epriv: string
|
|
106
|
+
} | null
|
|
107
|
+
if (!dec) return next()
|
|
108
|
+
|
|
109
|
+
user.is = {
|
|
110
|
+
username: username,
|
|
111
|
+
pub: data.pub as string,
|
|
112
|
+
epub: data.epub as string,
|
|
113
|
+
priv: dec.priv,
|
|
114
|
+
epriv: dec.epriv,
|
|
115
|
+
}
|
|
116
|
+
if (newPassword !== "") {
|
|
117
|
+
const salt = utils.text.random(64)
|
|
118
|
+
const newWork = await SEA.work(newPassword, salt)
|
|
119
|
+
const enc = await SEA.encrypt(dec, newWork)
|
|
120
|
+
const update = {
|
|
121
|
+
auth: JSON.stringify({enc: enc, salt: salt}),
|
|
96
122
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
const timestamp = Date.now()
|
|
124
|
+
const sig = await SEA.signTimestamp(timestamp, user.is)
|
|
125
|
+
const graph = utils.graph(
|
|
126
|
+
pub,
|
|
127
|
+
update,
|
|
128
|
+
sig!,
|
|
129
|
+
data.pub as string,
|
|
130
|
+
timestamp,
|
|
131
|
+
)
|
|
132
|
+
wire.put(graph, err => {
|
|
133
|
+
if (err) {
|
|
134
|
+
done(`error putting ${JSON.stringify(update)} on ${pub}: ${err}`)
|
|
135
|
+
} else {
|
|
136
|
+
done(null)
|
|
103
137
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
wire.put(graph, err => {
|
|
108
|
-
if (err) {
|
|
109
|
-
done(`error putting ${JSON.stringify(update)} on ${pub}: ${err}`)
|
|
110
|
-
} else {
|
|
111
|
-
done(null)
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
return
|
|
115
|
-
}
|
|
138
|
+
})
|
|
139
|
+
return
|
|
140
|
+
}
|
|
116
141
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
142
|
+
done(null)
|
|
143
|
+
})
|
|
120
144
|
}
|
|
121
145
|
|
|
122
146
|
if (retries > 9) {
|
|
@@ -125,22 +149,19 @@ const User = (opt: HolsterOptions | undefined, wire?: WireInterface): UserInterf
|
|
|
125
149
|
}
|
|
126
150
|
|
|
127
151
|
const soul = "~@" + username
|
|
128
|
-
wire.get(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return
|
|
134
|
-
}
|
|
152
|
+
wire.get({"#": soul}, async msg => {
|
|
153
|
+
if (msg.err) {
|
|
154
|
+
done(`error getting ${soul}: ${msg.err}`)
|
|
155
|
+
return
|
|
156
|
+
}
|
|
135
157
|
|
|
136
|
-
|
|
137
|
-
|
|
158
|
+
const data = msg.put && msg.put[soul]
|
|
159
|
+
if (!data) return retry(username)
|
|
138
160
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)
|
|
161
|
+
delete (data as {_?: unknown})._
|
|
162
|
+
pubs = Object.keys(data)
|
|
163
|
+
next()
|
|
164
|
+
})
|
|
144
165
|
}
|
|
145
166
|
|
|
146
167
|
const user: UserInterface = {
|
|
@@ -169,56 +190,53 @@ const User = (opt: HolsterOptions | undefined, wire?: WireInterface): UserInterf
|
|
|
169
190
|
creating = true
|
|
170
191
|
|
|
171
192
|
const soul = "~@" + username
|
|
172
|
-
wire.get(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
return
|
|
179
|
-
}
|
|
193
|
+
wire.get({"#": soul}, async msg => {
|
|
194
|
+
if (msg.err) {
|
|
195
|
+
creating = false
|
|
196
|
+
ack(`error getting ${soul}: ${msg.err}`)
|
|
197
|
+
return
|
|
198
|
+
}
|
|
180
199
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
200
|
+
if (msg.put && msg.put[soul]) {
|
|
201
|
+
creating = false
|
|
202
|
+
ack("Username already exists")
|
|
203
|
+
return
|
|
204
|
+
}
|
|
186
205
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
const salt = utils.text.random(64)
|
|
207
|
+
const work = await SEA.work(password, salt)
|
|
208
|
+
const pair = await SEA.pair()
|
|
209
|
+
const priv = {priv: pair.priv, epriv: pair.epriv}
|
|
210
|
+
const enc = await SEA.encrypt(priv, work)
|
|
211
|
+
const data = {
|
|
212
|
+
username: username,
|
|
213
|
+
pub: pair.pub,
|
|
214
|
+
epub: pair.epub,
|
|
215
|
+
auth: JSON.stringify({enc: enc, salt: salt}),
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const pub = "~" + pair.pub
|
|
219
|
+
const timestamp = Date.now()
|
|
220
|
+
const sig = await SEA.signTimestamp(timestamp, pair)
|
|
221
|
+
const graph = utils.graph(pub, data, sig!, pair.pub, timestamp)
|
|
222
|
+
wire.put(graph, err => {
|
|
223
|
+
creating = false
|
|
224
|
+
if (err) {
|
|
225
|
+
ack(`error putting ${JSON.stringify(data)} on ${pub}: ${err}`)
|
|
226
|
+
return
|
|
197
227
|
}
|
|
198
228
|
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
const sig = await SEA.signTimestamp(timestamp, pair)
|
|
202
|
-
const graph = utils.graph(pub, data, sig!, pair.pub, timestamp)
|
|
203
|
-
wire.put(graph, err => {
|
|
204
|
-
creating = false
|
|
229
|
+
const rel = {[pub]: {"#": pub}}
|
|
230
|
+
wire.put(utils.graph(soul, rel as never), err => {
|
|
205
231
|
if (err) {
|
|
206
|
-
ack(`error putting ${JSON.stringify(
|
|
232
|
+
ack(`error putting ${JSON.stringify(rel)} on ${soul}: ${err}`)
|
|
207
233
|
return
|
|
208
234
|
}
|
|
209
235
|
|
|
210
|
-
|
|
211
|
-
wire.put(utils.graph(soul, rel as never), err => {
|
|
212
|
-
if (err) {
|
|
213
|
-
ack(`error putting ${JSON.stringify(rel)} on ${soul}: ${err}`)
|
|
214
|
-
return
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (cb) cb(null)
|
|
218
|
-
})
|
|
236
|
+
if (cb) cb(null)
|
|
219
237
|
})
|
|
220
|
-
}
|
|
221
|
-
)
|
|
238
|
+
})
|
|
239
|
+
})
|
|
222
240
|
},
|
|
223
241
|
auth: (username, password, cb) => {
|
|
224
242
|
const ack = (err?: string | null): void => {
|
|
@@ -353,11 +371,17 @@ const User = (opt: HolsterOptions | undefined, wire?: WireInterface): UserInterf
|
|
|
353
371
|
return
|
|
354
372
|
}
|
|
355
373
|
|
|
356
|
-
const data = {
|
|
374
|
+
const data = {username: null, pub: null, epub: null, auth: null}
|
|
357
375
|
const timestamp = Date.now()
|
|
358
376
|
const sig = await SEA.signTimestamp(timestamp, user.is!)
|
|
359
377
|
const pub = "~" + user.is!.pub
|
|
360
|
-
const graph = utils.graph(
|
|
378
|
+
const graph = utils.graph(
|
|
379
|
+
pub,
|
|
380
|
+
data as never,
|
|
381
|
+
sig!,
|
|
382
|
+
user.is!.pub,
|
|
383
|
+
timestamp,
|
|
384
|
+
)
|
|
361
385
|
wire.put(graph, err => {
|
|
362
386
|
if (err) {
|
|
363
387
|
ack(`error putting null on ${pub}: ${err}`)
|
|
@@ -374,4 +398,3 @@ const User = (opt: HolsterOptions | undefined, wire?: WireInterface): UserInterf
|
|
|
374
398
|
}
|
|
375
399
|
|
|
376
400
|
export default User
|
|
377
|
-
|
package/ts/src/utils.ts
CHANGED
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
* Provides helpers for type checking, graph manipulation, and pattern matching
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
Soul,
|
|
8
|
+
Relation,
|
|
9
|
+
Graph,
|
|
10
|
+
GraphNode,
|
|
11
|
+
LexFilter,
|
|
12
|
+
GraphValue,
|
|
13
|
+
} from "./schemas.ts"
|
|
7
14
|
|
|
8
15
|
// ============================================================================
|
|
9
16
|
// Number Utilities
|
|
@@ -50,7 +57,7 @@ export const obj = {
|
|
|
50
57
|
map: <T, R>(
|
|
51
58
|
list: Record<string, T>,
|
|
52
59
|
cb: (value: T, key: string, o?: Record<string, unknown>) => R | undefined,
|
|
53
|
-
o?: Record<string, unknown
|
|
60
|
+
o?: Record<string, unknown>,
|
|
54
61
|
): R | undefined => {
|
|
55
62
|
const keys = Object.keys(list)
|
|
56
63
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -64,7 +71,11 @@ export const obj = {
|
|
|
64
71
|
/**
|
|
65
72
|
* Set a property on an object
|
|
66
73
|
*/
|
|
67
|
-
put: (
|
|
74
|
+
put: (
|
|
75
|
+
o: Record<string, unknown> | undefined,
|
|
76
|
+
key: string,
|
|
77
|
+
value: unknown,
|
|
78
|
+
): Record<string, unknown> => {
|
|
68
79
|
const target = o || {}
|
|
69
80
|
target[key] = value
|
|
70
81
|
return target
|
|
@@ -73,7 +84,10 @@ export const obj = {
|
|
|
73
84
|
/**
|
|
74
85
|
* Delete a property from an object
|
|
75
86
|
*/
|
|
76
|
-
del: <T>(
|
|
87
|
+
del: <T>(
|
|
88
|
+
o: Record<string, T> | undefined,
|
|
89
|
+
key: string,
|
|
90
|
+
): Record<string, T> | undefined => {
|
|
77
91
|
if (!o) return undefined
|
|
78
92
|
o[key] = null as T
|
|
79
93
|
delete o[key]
|
|
@@ -85,7 +99,11 @@ export const obj = {
|
|
|
85
99
|
// Relation Utilities
|
|
86
100
|
// ============================================================================
|
|
87
101
|
|
|
88
|
-
const map_soul = (
|
|
102
|
+
const map_soul = (
|
|
103
|
+
soul: unknown,
|
|
104
|
+
key: string,
|
|
105
|
+
o: {id: string | false},
|
|
106
|
+
): void => {
|
|
89
107
|
// If id is already defined AND we're still looping through the object,
|
|
90
108
|
// then it is considered invalid
|
|
91
109
|
if (o.id) {
|
|
@@ -115,8 +133,16 @@ export const rel = {
|
|
|
115
133
|
!("_" in value) &&
|
|
116
134
|
obj.is(value)
|
|
117
135
|
) {
|
|
118
|
-
const o: {
|
|
119
|
-
obj.map(
|
|
136
|
+
const o: {id: string | false} = {id: ""}
|
|
137
|
+
obj.map(
|
|
138
|
+
value as Record<string, unknown>,
|
|
139
|
+
map_soul as (
|
|
140
|
+
value: unknown,
|
|
141
|
+
key: string,
|
|
142
|
+
o?: Record<string, unknown>,
|
|
143
|
+
) => void,
|
|
144
|
+
o,
|
|
145
|
+
)
|
|
120
146
|
if (o.id) return o.id
|
|
121
147
|
}
|
|
122
148
|
return false
|
|
@@ -147,12 +173,12 @@ export const graph = (
|
|
|
147
173
|
data: Record<string, GraphValue>,
|
|
148
174
|
sig?: string,
|
|
149
175
|
pub?: string,
|
|
150
|
-
timestamp?: number
|
|
176
|
+
timestamp?: number,
|
|
151
177
|
): Graph => {
|
|
152
178
|
const ts = timestamp || Date.now()
|
|
153
179
|
const stateVector: Record<string, number> = {}
|
|
154
180
|
const node: GraphNode = {
|
|
155
|
-
_: {
|
|
181
|
+
_: {"#": soul, ">": stateVector},
|
|
156
182
|
}
|
|
157
183
|
|
|
158
184
|
for (const [key, value] of Object.entries(data)) {
|
|
@@ -171,7 +197,7 @@ export const graph = (
|
|
|
171
197
|
stateVector[userPublicKey] = timestamp
|
|
172
198
|
}
|
|
173
199
|
|
|
174
|
-
return {
|
|
200
|
+
return {[soul]: node}
|
|
175
201
|
}
|
|
176
202
|
|
|
177
203
|
// ============================================================================
|
|
@@ -229,4 +255,3 @@ export const text = {
|
|
|
229
255
|
return s
|
|
230
256
|
},
|
|
231
257
|
}
|
|
232
|
-
|