@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/src/holster.js
CHANGED
|
@@ -14,8 +14,6 @@ const Holster = opt => {
|
|
|
14
14
|
const map = new Map()
|
|
15
15
|
// Allow concurrent calls to the api by storing each context.
|
|
16
16
|
const allctx = new Map()
|
|
17
|
-
// Allow user queries by public key
|
|
18
|
-
let publicKey
|
|
19
17
|
|
|
20
18
|
const ok = data => {
|
|
21
19
|
return (
|
|
@@ -37,21 +35,26 @@ const Holster = opt => {
|
|
|
37
35
|
const keys = []
|
|
38
36
|
for (const [key, value] of Object.entries(data)) {
|
|
39
37
|
if (key === "_") {
|
|
40
|
-
return "error underscore cannot be used as
|
|
38
|
+
return "error underscore cannot be used as a property name"
|
|
41
39
|
}
|
|
42
40
|
if (utils.obj.is(value) || ok(value)) {
|
|
43
41
|
keys.push(key)
|
|
44
42
|
continue
|
|
45
43
|
}
|
|
46
|
-
|
|
44
|
+
if (typeof value === "undefined") {
|
|
45
|
+
return `error undefined ${key} cannot be converted to a graph`
|
|
46
|
+
}
|
|
47
|
+
const error = JSON.stringify({[key]: value})
|
|
48
|
+
return `error ${error} cannot be converted to a graph`
|
|
47
49
|
}
|
|
48
50
|
if (keys.length !== 0) return keys
|
|
49
51
|
}
|
|
50
|
-
|
|
52
|
+
const error = JSON.stringify(data)
|
|
53
|
+
return `error ${error} cannot be converted to a graph`
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
const api = ctxid => {
|
|
54
|
-
const get = (lex, soul, ack,
|
|
57
|
+
const get = (lex, soul, ack, _opt) => {
|
|
55
58
|
wire.get(
|
|
56
59
|
utils.obj.put(lex, "#", soul),
|
|
57
60
|
async msg => {
|
|
@@ -59,6 +62,7 @@ const Holster = opt => {
|
|
|
59
62
|
if (msg.put && msg.put[soul]) {
|
|
60
63
|
delete msg.put[soul]._
|
|
61
64
|
delete msg.put[soul][utils.userPublicKey]
|
|
65
|
+
delete msg.put[soul][utils.userSignature]
|
|
62
66
|
// Resolve any rels on the node before returning to the user.
|
|
63
67
|
for (const key of Object.keys(msg.put[soul])) {
|
|
64
68
|
const id = utils.rel.is(msg.put[soul][key])
|
|
@@ -77,19 +81,18 @@ const Holster = opt => {
|
|
|
77
81
|
ack(null)
|
|
78
82
|
}
|
|
79
83
|
},
|
|
80
|
-
|
|
84
|
+
_opt,
|
|
81
85
|
)
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
const graph = async (soul, data, cb) => {
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const signed = await SEA.sign(data, user.is)
|
|
89
|
-
return utils.graph(soul, signed.m, signed.s, user.is.pub)
|
|
88
|
+
const graph = async (soul, data, userctx, cb) => {
|
|
89
|
+
if (userctx) {
|
|
90
|
+
const signed = await SEA.sign(data, userctx)
|
|
91
|
+
return utils.graph(soul, signed.m, signed.s, userctx.pub)
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
if (opt.secure) {
|
|
95
|
+
if (!cb) cb = console.log
|
|
93
96
|
cb(`error putting data on ${soul}: user required in secure mode`)
|
|
94
97
|
return null
|
|
95
98
|
}
|
|
@@ -97,19 +100,33 @@ const Holster = opt => {
|
|
|
97
100
|
return utils.graph(soul, data)
|
|
98
101
|
}
|
|
99
102
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
// done takes a context id and returns a new callback function so that the
|
|
104
|
+
// callback is not overwritten by simultaneous requests.
|
|
105
|
+
const done = ctxid => {
|
|
106
|
+
return data => {
|
|
107
|
+
const ctx = allctx.get(ctxid)
|
|
108
|
+
if (ctx && typeof ctx.cb !== "undefined") {
|
|
109
|
+
// Use a timeout so that the context can be removed before data is
|
|
110
|
+
// returned to the callback (allows nested get calls).
|
|
111
|
+
setTimeout(() => ctx.cb(data), 1)
|
|
112
|
+
} else if (data) {
|
|
113
|
+
console.log("error no callback for data", data, "ctx", ctx)
|
|
114
|
+
}
|
|
115
|
+
// A context updated by "on" should only be removed by "off".
|
|
116
|
+
if (ctx && !ctx.on) allctx.delete(ctxid)
|
|
117
|
+
}
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
const resolve = (request, cb) => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
if (!request) {
|
|
122
|
+
console.log("error resolve request parameter required")
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const get = typeof request.get !== "undefined"
|
|
127
|
+
const put = typeof request.put !== "undefined"
|
|
128
|
+
const on = typeof request.on !== "undefined"
|
|
129
|
+
const off = typeof request.off !== "undefined"
|
|
113
130
|
|
|
114
131
|
let found = false
|
|
115
132
|
const ctx = allctx.get(ctxid)
|
|
@@ -124,34 +141,45 @@ const Holster = opt => {
|
|
|
124
141
|
// Found a soul that needs resolving, need the previous context
|
|
125
142
|
// (ie the parent node) to find a soul relation for it.
|
|
126
143
|
const {item, soul} = ctx.chain[i - 1]
|
|
127
|
-
|
|
144
|
+
// If this is a user context or using secure mode then need to get the
|
|
145
|
+
// whole node for verification.
|
|
146
|
+
const lex =
|
|
147
|
+
ctx.user || opt.secure ? {"#": soul} : {"#": soul, ".": item}
|
|
148
|
+
wire.get(lex, async msg => {
|
|
128
149
|
if (msg.err) {
|
|
129
|
-
|
|
150
|
+
if (ctx.user || opt.secure) {
|
|
151
|
+
console.log(`error getting ${soul}: ${msg.err}`)
|
|
152
|
+
} else {
|
|
153
|
+
console.log(`error getting ${item} on ${soul}: ${msg.err}`)
|
|
154
|
+
}
|
|
130
155
|
if (cb) cb(null)
|
|
131
156
|
return
|
|
132
157
|
}
|
|
133
158
|
|
|
134
|
-
|
|
159
|
+
let node = msg.put && msg.put[soul]
|
|
135
160
|
if (node && typeof node[item] !== "undefined") {
|
|
136
161
|
let id = utils.rel.is(node[item])
|
|
137
162
|
if (id) {
|
|
138
163
|
ctx.chain[i].soul = id
|
|
139
|
-
|
|
140
|
-
allctx.set(ctxid, {chain: ctx.chain, cb: ctx.cb})
|
|
164
|
+
allctx.set(ctxid, {...ctx})
|
|
141
165
|
// Call api again using the updated context.
|
|
142
|
-
if (get)
|
|
143
|
-
|
|
144
|
-
else if (
|
|
145
|
-
|
|
166
|
+
if (get) {
|
|
167
|
+
api(ctxid).next(null, request.get, cb, request._opt)
|
|
168
|
+
} else if (put) {
|
|
169
|
+
api(ctxid).put(request.put, cb)
|
|
170
|
+
} else if (on) {
|
|
171
|
+
api(ctxid).on(request.on, cb, request._get, request._opt)
|
|
172
|
+
} else if (off) {
|
|
173
|
+
api(ctxid).off(cb)
|
|
174
|
+
}
|
|
146
175
|
} else if (get) {
|
|
147
|
-
// Request was not for a node, return
|
|
148
|
-
// soul.
|
|
176
|
+
// Request was not for a node, return property on current soul.
|
|
149
177
|
cb(node[item])
|
|
150
178
|
} else if (put) {
|
|
151
179
|
// Request was chained before put, so rel doesn't exist yet.
|
|
152
180
|
id = utils.text.random()
|
|
153
|
-
|
|
154
|
-
const g = await graph(soul,
|
|
181
|
+
node[item] = utils.rel.ify(id)
|
|
182
|
+
const g = await graph(soul, node, ctx.user, cb)
|
|
155
183
|
if (g === null) return
|
|
156
184
|
|
|
157
185
|
wire.put(g, err => {
|
|
@@ -164,16 +192,31 @@ const Holster = opt => {
|
|
|
164
192
|
api(ctxid).put(request.put, cb)
|
|
165
193
|
})
|
|
166
194
|
} else if (on) {
|
|
167
|
-
|
|
195
|
+
// Allow listening to a node that doesn't exist.
|
|
168
196
|
cb(null)
|
|
169
197
|
} else if (off) {
|
|
170
|
-
|
|
198
|
+
// Allow stop listening to a node that doesn't exist.
|
|
171
199
|
if (cb) cb(null)
|
|
172
200
|
}
|
|
173
201
|
} else if (put) {
|
|
174
|
-
|
|
202
|
+
// Request was chained before put, so rel doesn't exist yet.
|
|
203
|
+
const id = utils.text.random()
|
|
204
|
+
if (!node) node = {}
|
|
205
|
+
node[item] = utils.rel.ify(id)
|
|
206
|
+
const g = await graph(soul, node, ctx.user, cb)
|
|
207
|
+
if (g === null) return
|
|
208
|
+
|
|
209
|
+
wire.put(g, err => {
|
|
210
|
+
if (err) {
|
|
211
|
+
cb(`error putting ${item} on ${soul}: ${err}`)
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
ctx.chain[i].soul = id
|
|
216
|
+
api(ctxid).put(request.put, cb)
|
|
217
|
+
})
|
|
175
218
|
} else {
|
|
176
|
-
|
|
219
|
+
// Allow querying a node that doesn't exist.
|
|
177
220
|
if (cb) cb(null)
|
|
178
221
|
}
|
|
179
222
|
})
|
|
@@ -195,57 +238,66 @@ const Holster = opt => {
|
|
|
195
238
|
}
|
|
196
239
|
|
|
197
240
|
return {
|
|
198
|
-
get: (key, lex, cb,
|
|
241
|
+
get: (key, lex, cb, _opt) => {
|
|
199
242
|
if (typeof lex === "function") {
|
|
243
|
+
_opt = cb
|
|
200
244
|
cb = lex
|
|
201
245
|
lex = null
|
|
202
246
|
}
|
|
203
247
|
if (key === null || key === "" || key === "_") {
|
|
248
|
+
console.log("error please provide a key")
|
|
204
249
|
if (cb) cb(null)
|
|
205
250
|
return
|
|
206
251
|
}
|
|
207
252
|
|
|
208
253
|
// lex requires a callback as it's not included in the chain below.
|
|
209
|
-
if (lex &&
|
|
254
|
+
if (lex && typeof cb !== "function") {
|
|
210
255
|
console.log("error lex requires a callback function")
|
|
211
256
|
return
|
|
212
257
|
}
|
|
213
258
|
|
|
214
259
|
// Top level keys are added to a root node so their values don't need
|
|
215
|
-
// to be objects
|
|
216
|
-
// publicKey or user.ctx is set.
|
|
217
|
-
const root = publicKey ? publicKey : user.ctx ? user.ctx : "root"
|
|
218
|
-
|
|
260
|
+
// to be objects.
|
|
219
261
|
ctxid = utils.text.random()
|
|
220
|
-
allctx.set(ctxid, {chain: [{item: key, soul: root}], cb: cb})
|
|
262
|
+
allctx.set(ctxid, {chain: [{item: key, soul: "root"}], cb: cb})
|
|
221
263
|
if (!cb) return api(ctxid)
|
|
222
264
|
|
|
265
|
+
const _done = done(ctxid)
|
|
223
266
|
// When there's a callback need to resolve the context first.
|
|
224
|
-
const {soul} = resolve({get: lex},
|
|
225
|
-
if (soul) get(lex, soul,
|
|
267
|
+
const {soul} = resolve({get: lex, _opt: _opt}, _done)
|
|
268
|
+
if (soul) get(lex, soul, _done, _opt)
|
|
226
269
|
},
|
|
227
|
-
next: (key, lex, cb,
|
|
228
|
-
|
|
229
|
-
|
|
270
|
+
next: (key, lex, cb, _opt) => {
|
|
271
|
+
// ack needs to work the same as done, pass it the context id and then
|
|
272
|
+
// return a new function for the actual callback.
|
|
273
|
+
const ack = ctxid => {
|
|
274
|
+
return data => {
|
|
275
|
+
if (cb) {
|
|
276
|
+
cb(data)
|
|
277
|
+
} else {
|
|
278
|
+
done(ctxid)(data)
|
|
279
|
+
}
|
|
280
|
+
}
|
|
230
281
|
}
|
|
231
|
-
|
|
232
282
|
if (typeof lex === "function") {
|
|
283
|
+
_opt = cb
|
|
233
284
|
cb = lex
|
|
234
285
|
lex = null
|
|
235
286
|
}
|
|
236
287
|
if (!ctxid) {
|
|
237
288
|
console.log("error please provide a key using get(key)")
|
|
238
|
-
|
|
289
|
+
if (cb) cb(null)
|
|
239
290
|
return
|
|
240
291
|
}
|
|
241
292
|
|
|
293
|
+
const _ack = ack(ctxid)
|
|
242
294
|
if (key === "" || key === "_") {
|
|
243
|
-
|
|
295
|
+
_ack(null)
|
|
244
296
|
return
|
|
245
297
|
}
|
|
246
298
|
|
|
247
299
|
// lex requires a callback as it's not included in the chain below.
|
|
248
|
-
if (lex &&
|
|
300
|
+
if (lex && typeof cb !== "function") {
|
|
249
301
|
console.log("error lex requires a callback function")
|
|
250
302
|
return
|
|
251
303
|
}
|
|
@@ -266,20 +318,25 @@ const Holster = opt => {
|
|
|
266
318
|
if (!ctx.cb) return api(ctxid)
|
|
267
319
|
|
|
268
320
|
// When there's a callback need to resolve the context first.
|
|
269
|
-
const {soul} = resolve({get: lex},
|
|
270
|
-
if (soul) get(lex, soul,
|
|
321
|
+
const {soul} = resolve({get: lex, _opt: _opt}, _ack)
|
|
322
|
+
if (soul) get(lex, soul, _ack, _opt)
|
|
271
323
|
},
|
|
272
324
|
put: (data, set, cb) => {
|
|
273
325
|
if (typeof set === "function") {
|
|
274
326
|
cb = set
|
|
275
327
|
set = false
|
|
276
328
|
}
|
|
277
|
-
const ack =
|
|
278
|
-
|
|
329
|
+
const ack = ctxid => {
|
|
330
|
+
return data => {
|
|
331
|
+
if (cb) {
|
|
332
|
+
cb(data)
|
|
333
|
+
} else {
|
|
334
|
+
done(ctxid)(data)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
279
337
|
}
|
|
280
|
-
|
|
281
338
|
if (!ctxid) {
|
|
282
|
-
|
|
339
|
+
if (cb) cb("error please provide a key using get(key)")
|
|
283
340
|
return
|
|
284
341
|
}
|
|
285
342
|
|
|
@@ -296,68 +353,83 @@ const Holster = opt => {
|
|
|
296
353
|
}
|
|
297
354
|
if (set) data = {[utils.text.random()]: data}
|
|
298
355
|
|
|
356
|
+
const _ack = ack(ctxid)
|
|
299
357
|
const result = check(data)
|
|
300
358
|
if (typeof result === "string") {
|
|
301
359
|
// All strings returned from check are errors, cannot continue.
|
|
302
|
-
|
|
360
|
+
_ack(result)
|
|
303
361
|
return
|
|
304
362
|
}
|
|
305
363
|
|
|
306
364
|
// Resolve the current context before putting data.
|
|
307
|
-
const {item, soul} = resolve({put: data},
|
|
365
|
+
const {item, soul} = resolve({put: data}, _ack)
|
|
308
366
|
if (!soul) return
|
|
309
367
|
|
|
310
368
|
if (result === true) {
|
|
311
369
|
// When result is true data is a property to put on the current soul.
|
|
312
370
|
// Need to check if item is a rel and also set the node to null. (This
|
|
313
371
|
// applies for any update from a rel to a property, not just null.)
|
|
314
|
-
|
|
372
|
+
// If using secure mode need to get the whole node for verification.
|
|
373
|
+
const lex =
|
|
374
|
+
ctx.user || opt.secure ? {"#": soul} : {"#": soul, ".": item}
|
|
375
|
+
wire.get(lex, async msg => {
|
|
315
376
|
if (msg.err) {
|
|
316
|
-
|
|
377
|
+
_ack(`error getting ${soul}: ${msg.err}`)
|
|
317
378
|
return
|
|
318
379
|
}
|
|
319
380
|
|
|
320
|
-
|
|
381
|
+
let node = msg.put && msg.put[soul]
|
|
382
|
+
const current = node && node[item]
|
|
321
383
|
const id = utils.rel.is(current)
|
|
322
384
|
if (!id) {
|
|
323
385
|
// Not a rel, can just put the data.
|
|
324
|
-
|
|
386
|
+
if (!node) node = {}
|
|
387
|
+
node[item] = data
|
|
388
|
+
const g = await graph(soul, node, ctx.user, _ack)
|
|
325
389
|
if (g === null) return
|
|
326
390
|
|
|
327
|
-
wire.put(g,
|
|
391
|
+
wire.put(g, _ack)
|
|
328
392
|
return
|
|
329
393
|
}
|
|
330
394
|
|
|
331
395
|
wire.get({"#": id}, async msg => {
|
|
332
396
|
if (msg.err) {
|
|
333
|
-
|
|
397
|
+
_ack(`error getting ${id}: ${msg.err}`)
|
|
334
398
|
return
|
|
335
399
|
}
|
|
336
400
|
|
|
337
401
|
if (!msg.put || !msg.put[id]) {
|
|
338
|
-
|
|
402
|
+
_ack(`error ${id} not found`)
|
|
339
403
|
return
|
|
340
404
|
}
|
|
341
405
|
|
|
342
|
-
delete msg.put[id]._
|
|
343
406
|
// null each of the properties on the node before putting data.
|
|
344
407
|
for (const key of Object.keys(msg.put[id])) {
|
|
345
|
-
if (
|
|
408
|
+
if (
|
|
409
|
+
key === "_" ||
|
|
410
|
+
key === utils.userPublicKey ||
|
|
411
|
+
key === utils.userSignature
|
|
412
|
+
) {
|
|
413
|
+
continue
|
|
414
|
+
}
|
|
346
415
|
|
|
347
416
|
const err = await new Promise(res => {
|
|
348
417
|
const _ctxid = utils.text.random()
|
|
349
|
-
|
|
418
|
+
const chain = [{item: key, soul: id}]
|
|
419
|
+
allctx.set(_ctxid, {chain: chain, user: ctx.user})
|
|
350
420
|
api(_ctxid).put(null, res)
|
|
351
421
|
})
|
|
352
422
|
if (err) {
|
|
353
|
-
|
|
423
|
+
_ack(err)
|
|
354
424
|
return
|
|
355
425
|
}
|
|
356
426
|
}
|
|
357
|
-
|
|
427
|
+
if (!node) node = {}
|
|
428
|
+
node[item] = data
|
|
429
|
+
const g = await graph(soul, node, ctx.user, _ack)
|
|
358
430
|
if (g === null) return
|
|
359
431
|
|
|
360
|
-
wire.put(g,
|
|
432
|
+
wire.put(g, _ack)
|
|
361
433
|
})
|
|
362
434
|
})
|
|
363
435
|
return
|
|
@@ -365,72 +437,96 @@ const Holster = opt => {
|
|
|
365
437
|
|
|
366
438
|
// Otherwise put the data using the keys returned in result.
|
|
367
439
|
// Need to check if a rel has already been added on the current node.
|
|
368
|
-
|
|
440
|
+
// If using secure mode need to get the whole node for verification.
|
|
441
|
+
const lex =
|
|
442
|
+
ctx.user || opt.secure ? {"#": soul} : {"#": soul, ".": item}
|
|
443
|
+
wire.get(lex, async msg => {
|
|
369
444
|
if (msg.err) {
|
|
370
|
-
|
|
445
|
+
_ack(`error getting ${soul}.${item}: ${msg.err}`)
|
|
371
446
|
return
|
|
372
447
|
}
|
|
373
448
|
|
|
374
|
-
|
|
449
|
+
let node = msg.put && msg.put[soul]
|
|
450
|
+
const current = node && node[item]
|
|
375
451
|
const id = utils.rel.is(current)
|
|
376
452
|
if (!id) {
|
|
377
453
|
// The current rel doesn't exist, so add it first.
|
|
378
|
-
|
|
379
|
-
|
|
454
|
+
if (!node) node = {}
|
|
455
|
+
node[item] = utils.rel.ify(utils.text.random())
|
|
456
|
+
const g = await graph(soul, node, ctx.user, _ack)
|
|
380
457
|
if (g === null) return
|
|
381
458
|
|
|
382
459
|
wire.put(g, err => {
|
|
383
460
|
if (err) {
|
|
384
|
-
|
|
461
|
+
_ack(`error putting ${item} on ${soul}: ${err}`)
|
|
385
462
|
} else {
|
|
386
463
|
const _ctxid = utils.text.random()
|
|
387
464
|
const chain = [{item: item, soul: soul}]
|
|
388
|
-
// Pass the previous context's callback
|
|
389
|
-
allctx.set(_ctxid, {chain: chain, cb: ctx.cb})
|
|
465
|
+
// Pass on the previous context's callback and user flag here.
|
|
466
|
+
allctx.set(_ctxid, {chain: chain, user: ctx.user, cb: ctx.cb})
|
|
390
467
|
api(_ctxid).put(data)
|
|
391
468
|
}
|
|
392
469
|
})
|
|
393
470
|
return
|
|
394
471
|
}
|
|
395
472
|
|
|
396
|
-
|
|
397
|
-
const update = {}
|
|
473
|
+
const update = []
|
|
398
474
|
for (const key of result) {
|
|
399
475
|
const err = await new Promise(res => {
|
|
400
|
-
if (utils.obj.is(data[key])) {
|
|
476
|
+
if (utils.obj.is(data[key]) && !utils.rel.is(data[key])) {
|
|
401
477
|
// Use the current rel as the context for nested objects.
|
|
402
478
|
const _ctxid = utils.text.random()
|
|
403
|
-
|
|
479
|
+
const chain = [{item: key, soul: id}]
|
|
480
|
+
allctx.set(_ctxid, {chain: chain, user: ctx.user})
|
|
404
481
|
api(_ctxid).put(data[key], res)
|
|
405
482
|
} else {
|
|
406
|
-
put
|
|
407
|
-
|
|
408
|
-
update[key] = data[key]
|
|
483
|
+
// Group the rest of the updates for put below.
|
|
484
|
+
update.push(key)
|
|
409
485
|
res(null)
|
|
410
486
|
}
|
|
411
487
|
})
|
|
412
488
|
if (err) {
|
|
413
|
-
|
|
489
|
+
_ack(err, ctxid)
|
|
414
490
|
return
|
|
415
491
|
}
|
|
416
492
|
}
|
|
417
|
-
if (put) {
|
|
418
|
-
const g = await graph(id, update, ack)
|
|
419
|
-
if (g === null) return
|
|
420
493
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
ack(null)
|
|
494
|
+
if (update.length === 0) {
|
|
495
|
+
_ack(null)
|
|
496
|
+
return
|
|
425
497
|
}
|
|
498
|
+
|
|
499
|
+
// The nested objects created above will also have rels on the parent
|
|
500
|
+
// object, so fetch the node so the rest of the updates can be added.
|
|
501
|
+
wire.get({"#": id}, async msg => {
|
|
502
|
+
if (msg.err) {
|
|
503
|
+
_ack(`error getting ${id}: ${msg.err}`)
|
|
504
|
+
return
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
let node = msg.put && msg.put[id]
|
|
508
|
+
if (!node) node = {}
|
|
509
|
+
update.forEach(key => {
|
|
510
|
+
node[key] = data[key]
|
|
511
|
+
})
|
|
512
|
+
const g = await graph(id, node, ctx.user, _ack)
|
|
513
|
+
if (g === null) return
|
|
514
|
+
|
|
515
|
+
wire.put(g, _ack)
|
|
516
|
+
})
|
|
426
517
|
})
|
|
427
518
|
},
|
|
428
|
-
on: (lex, cb) => {
|
|
519
|
+
on: (lex, cb, _get, _opt) => {
|
|
429
520
|
if (typeof lex === "function") {
|
|
521
|
+
_opt = _get
|
|
522
|
+
_get = cb
|
|
430
523
|
cb = lex
|
|
431
524
|
lex = null
|
|
432
525
|
}
|
|
433
|
-
if (
|
|
526
|
+
if (typeof cb !== "function") {
|
|
527
|
+
console.log("error on() requires a callback function")
|
|
528
|
+
return
|
|
529
|
+
}
|
|
434
530
|
|
|
435
531
|
if (!ctxid) {
|
|
436
532
|
console.log("error please provide a key using get(key)")
|
|
@@ -439,7 +535,7 @@ const Holster = opt => {
|
|
|
439
535
|
}
|
|
440
536
|
|
|
441
537
|
// Resolve the current context before adding event listener.
|
|
442
|
-
const {item, soul} = resolve({on: lex}, cb)
|
|
538
|
+
const {item, soul} = resolve({on: lex, _get: _get, _opt: _opt}, cb)
|
|
443
539
|
if (!soul) return
|
|
444
540
|
|
|
445
541
|
// Flag that this context is set from on and shouldn't be removed.
|
|
@@ -463,7 +559,7 @@ const Holster = opt => {
|
|
|
463
559
|
if (lex) lex = utils.obj.put(lex, "#", soul)
|
|
464
560
|
else lex = {"#": soul, ".": item}
|
|
465
561
|
}
|
|
466
|
-
wire.on(lex, map.get(cb))
|
|
562
|
+
wire.on(lex, map.get(cb), _get, _opt)
|
|
467
563
|
})
|
|
468
564
|
},
|
|
469
565
|
off: cb => {
|
|
@@ -492,14 +588,62 @@ const Holster = opt => {
|
|
|
492
588
|
allctx.delete(ctxid)
|
|
493
589
|
})
|
|
494
590
|
},
|
|
495
|
-
user:
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
591
|
+
user: () => {
|
|
592
|
+
if (!user.get) {
|
|
593
|
+
// Return the combined Holster and User APIs.
|
|
594
|
+
Object.assign(user, api())
|
|
595
|
+
// Need to provide a user specific get() function to know if user
|
|
596
|
+
// context should be checked.
|
|
597
|
+
user.get = (keys, lex, cb, _opt) => {
|
|
598
|
+
if (typeof lex === "function") {
|
|
599
|
+
_opt = cb
|
|
600
|
+
cb = lex
|
|
601
|
+
lex = null
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
let pub = null
|
|
605
|
+
let key = null
|
|
606
|
+
if (user.is) pub = user.is.pub
|
|
607
|
+
if (typeof keys === "string") {
|
|
608
|
+
key = keys
|
|
609
|
+
} else if (keys instanceof Array) {
|
|
610
|
+
if (keys.length === 2) {
|
|
611
|
+
pub = keys[0]
|
|
612
|
+
key = keys[1]
|
|
613
|
+
} else if (keys.length === 1) {
|
|
614
|
+
key = keys[0]
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (!pub) {
|
|
618
|
+
console.log("error please log in or provide a public key")
|
|
619
|
+
if (cb) cb(null)
|
|
620
|
+
return
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (key === null || key === "" || key === "_") {
|
|
624
|
+
console.log("error please provide a key")
|
|
625
|
+
if (cb) cb(null)
|
|
626
|
+
return
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// lex requires a callback as it's not included in the chain below.
|
|
630
|
+
if (lex && !cb) {
|
|
631
|
+
console.log("error lex requires a callback function")
|
|
632
|
+
return
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
ctxid = utils.text.random()
|
|
636
|
+
const chain = [{item: key, soul: "~" + pub}]
|
|
637
|
+
allctx.set(ctxid, {chain: chain, user: user.is, cb: cb})
|
|
638
|
+
if (!cb) return api(ctxid)
|
|
639
|
+
|
|
640
|
+
// When there's a callback need to resolve the context first.
|
|
641
|
+
const _done = done(ctxid)
|
|
642
|
+
const {soul} = resolve({get: lex}, _done)
|
|
643
|
+
if (soul) get(lex, soul, _done, _opt)
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return user
|
|
503
647
|
},
|
|
504
648
|
// Allow the wire spec to be used via holster.
|
|
505
649
|
wire: wire,
|
package/src/sea.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {userPublicKey} from "./utils.js"
|
|
1
|
+
import {userPublicKey, userSignature} from "./utils.js"
|
|
2
2
|
import * as utils from "./sea-utils.js"
|
|
3
3
|
import SafeBuffer from "./buffer.js"
|
|
4
4
|
|
|
@@ -117,8 +117,10 @@ const SEA = {
|
|
|
117
117
|
} else {
|
|
118
118
|
// Allow data to be passed in with graph meta data,
|
|
119
119
|
// (which should not be part of signature, so not verified).
|
|
120
|
-
for (const k of Object.keys(signed.m)) {
|
|
121
|
-
if (k !== "_" && k != userPublicKey
|
|
120
|
+
for (const k of Object.keys(signed.m).sort()) {
|
|
121
|
+
if (k !== "_" && k != userPublicKey && k != userSignature) {
|
|
122
|
+
msg[k] = signed.m[k]
|
|
123
|
+
}
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
const hash = await utils.sha256(msg)
|
|
@@ -139,7 +141,17 @@ const SEA = {
|
|
|
139
141
|
return null
|
|
140
142
|
}
|
|
141
143
|
|
|
142
|
-
|
|
144
|
+
let msg = {}
|
|
145
|
+
if (typeof data === "string") {
|
|
146
|
+
msg = data
|
|
147
|
+
} else {
|
|
148
|
+
const check = utils.parse(data)
|
|
149
|
+
for (const k of Object.keys(check).sort()) {
|
|
150
|
+
if (k !== "_" && k != userPublicKey && k != userSignature) {
|
|
151
|
+
msg[k] = check[k]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
143
155
|
const hash = await utils.sha256(msg)
|
|
144
156
|
const jwk = utils.jwk(pair.pub, pair.priv)
|
|
145
157
|
const alg = {name: "ECDSA", namedCurve: "P-256"}
|