@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/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 an item name"
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
- return `error {${key}:${value}} cannot be converted to graph`
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
- return `error ${data} cannot be converted to a graph`
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, opt) => {
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
- opt,
84
+ _opt,
81
85
  )
82
86
  }
83
87
 
84
- const graph = async (soul, data, cb) => {
85
- if (!cb) cb = console.log
86
-
87
- if (user.is) {
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
- const done = data => {
101
- const ctx = allctx.get(ctxid)
102
- if (ctx && typeof ctx.cb !== "undefined") ctx.cb(data)
103
- else if (data) console.log(data)
104
- // A context updated by "on" should only be removed by "off".
105
- if (ctx && !ctx.on) allctx.delete(ctxid)
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
- const get = request && typeof request.get !== "undefined"
110
- const put = request && typeof request.put !== "undefined"
111
- const on = request && typeof request.on !== "undefined"
112
- const off = request && typeof request.off !== "undefined"
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
- wire.get({"#": soul, ".": item}, async msg => {
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
- console.log(`error getting ${item} on ${soul}: ${msg.err}`)
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
- const node = msg.put && msg.put[soul]
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
- // Not sure why the map needs to be set rather than just ctx?
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) api(ctxid).next(null, request.get, cb)
143
- else if (put) api(ctxid).put(request.put, cb)
144
- else if (on) api(ctxid).on(request.on, cb)
145
- else if (off) api(ctxid).off(cb)
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 a property on the current
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
- const rel = {[item]: utils.rel.ify(id)}
154
- const g = await graph(soul, rel, cb)
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
- console.log(`error resolving on for ${item} on ${soul}`)
195
+ // Allow listening to a node that doesn't exist.
168
196
  cb(null)
169
197
  } else if (off) {
170
- console.log(`error resolving off for ${item} on ${soul}`)
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
- cb(`error ${item} not found on ${soul}`)
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
- console.log(`error ${item} not found on ${soul}`)
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, opt) => {
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 && !cb) {
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, the user's public key is used as the root node when
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}, done)
225
- if (soul) get(lex, soul, done, opt)
267
+ const {soul} = resolve({get: lex, _opt: _opt}, _done)
268
+ if (soul) get(lex, soul, _done, _opt)
226
269
  },
227
- next: (key, lex, cb, opt) => {
228
- const ack = data => {
229
- cb ? cb(data) : done(data)
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
- ack(null)
289
+ if (cb) cb(null)
239
290
  return
240
291
  }
241
292
 
293
+ const _ack = ack(ctxid)
242
294
  if (key === "" || key === "_") {
243
- ack(null)
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 && !cb) {
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}, ack)
270
- if (soul) get(lex, soul, ack, opt)
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 = err => {
278
- cb ? cb(err) : done(err)
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
- ack("please provide a key using get(key)")
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
- ack(result)
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}, ack)
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
- wire.get({"#": soul, ".": item}, async msg => {
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
- console.log(`error getting ${soul}: ${msg.err}`)
377
+ _ack(`error getting ${soul}: ${msg.err}`)
317
378
  return
318
379
  }
319
380
 
320
- const current = msg.put && msg.put[soul] && msg.put[soul][item]
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
- const g = await graph(soul, {[item]: data}, ack)
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, ack)
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
- console.log(`error getting ${id}: ${msg.err}`)
397
+ _ack(`error getting ${id}: ${msg.err}`)
334
398
  return
335
399
  }
336
400
 
337
401
  if (!msg.put || !msg.put[id]) {
338
- console.log(`error ${id} not found`)
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 (key === utils.userPublicKey) continue
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
- allctx.set(_ctxid, {chain: [{item: key, soul: id}]})
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
- ack(err)
423
+ _ack(err)
354
424
  return
355
425
  }
356
426
  }
357
- const g = await graph(soul, {[item]: data}, ack)
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, ack)
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
- wire.get({"#": soul, ".": item}, async msg => {
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
- ack(`error getting ${soul}.${item}: ${msg.err}`)
445
+ _ack(`error getting ${soul}.${item}: ${msg.err}`)
371
446
  return
372
447
  }
373
448
 
374
- const current = msg.put && msg.put[soul] && msg.put[soul][item]
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
- const rel = {[item]: utils.rel.ify(utils.text.random())}
379
- const g = await graph(soul, rel, ack)
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
- ack(`error putting ${item} on ${soul}: ${err}`)
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 on here.
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
- let put = false
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
- allctx.set(_ctxid, {chain: [{item: key, soul: id}]})
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 = true
407
- // Group other properties into one update.
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
- ack(err)
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
- wire.put(g, ack)
422
- } else {
423
- // Callback on behalf of nested objects.
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 (!cb) return
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: pub => {
496
- // Return the combined Holster and User APIs. Passing in a public key
497
- // will set the user context to that key, otherwise user.ctx is set when
498
- // a user logs in so that their public key will be used as the root node
499
- // in get(), also put() will sign all graph updates for verification.
500
- if (pub) publicKey = "~" + pub
501
- else publicKey = null
502
- return Object.assign(user, api())
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) msg[k] = signed.m[k]
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
- const msg = utils.parse(data)
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"}