@mblaney/holster 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mblaney/holster",
3
- "version": "1.0.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/ham.js CHANGED
@@ -63,11 +63,10 @@ Ham.mix = async (change, graph, secure, listen) => {
63
63
  }
64
64
  }
65
65
  if (verify) {
66
- // Partial nodes can be read from disk, just ignore them.
67
- if (!sig || !pub) continue
68
-
69
- if (!(await SEA.verify({m: node, s: sig}, {pub: pub}))) {
70
- 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}))) {
71
70
  continue
72
71
  }
73
72
  }
package/src/holster.js CHANGED
@@ -100,17 +100,21 @@ const Holster = opt => {
100
100
  return utils.graph(soul, data)
101
101
  }
102
102
 
103
- const done = data => {
104
- const ctx = allctx.get(ctxid)
105
- if (ctx && typeof ctx.cb !== "undefined") {
106
- // Use a timeout so that the context can be removed before data is
107
- // returned to the callback (allows nested get calls).
108
- setTimeout(() => ctx.cb(data), 1)
109
- } else if (data) {
110
- console.log("error no callback for data", data, "ctx", ctx)
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)
111
117
  }
112
- // A context updated by "on" should only be removed by "off".
113
- if (ctx && !ctx.on) allctx.delete(ctxid)
114
118
  }
115
119
 
116
120
  const resolve = (request, cb) => {
@@ -258,13 +262,22 @@ const Holster = opt => {
258
262
  allctx.set(ctxid, {chain: [{item: key, soul: "root"}], cb: cb})
259
263
  if (!cb) return api(ctxid)
260
264
 
265
+ const _done = done(ctxid)
261
266
  // When there's a callback need to resolve the context first.
262
- const {soul} = resolve({get: lex, _opt: _opt}, done)
263
- if (soul) get(lex, soul, done, _opt)
267
+ const {soul} = resolve({get: lex, _opt: _opt}, _done)
268
+ if (soul) get(lex, soul, _done, _opt)
264
269
  },
265
270
  next: (key, lex, cb, _opt) => {
266
- const ack = data => {
267
- cb ? cb(data) : done(data)
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
+ }
268
281
  }
269
282
  if (typeof lex === "function") {
270
283
  _opt = cb
@@ -273,12 +286,13 @@ const Holster = opt => {
273
286
  }
274
287
  if (!ctxid) {
275
288
  console.log("error please provide a key using get(key)")
276
- ack(null)
289
+ if (cb) cb(null)
277
290
  return
278
291
  }
279
292
 
293
+ const _ack = ack(ctxid)
280
294
  if (key === "" || key === "_") {
281
- ack(null)
295
+ _ack(null)
282
296
  return
283
297
  }
284
298
 
@@ -304,20 +318,25 @@ const Holster = opt => {
304
318
  if (!ctx.cb) return api(ctxid)
305
319
 
306
320
  // When there's a callback need to resolve the context first.
307
- const {soul} = resolve({get: lex, _opt: _opt}, ack)
308
- if (soul) get(lex, soul, ack, _opt)
321
+ const {soul} = resolve({get: lex, _opt: _opt}, _ack)
322
+ if (soul) get(lex, soul, _ack, _opt)
309
323
  },
310
324
  put: (data, set, cb) => {
311
325
  if (typeof set === "function") {
312
326
  cb = set
313
327
  set = false
314
328
  }
315
- const ack = err => {
316
- 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
+ }
317
337
  }
318
-
319
338
  if (!ctxid) {
320
- ack("please provide a key using get(key)")
339
+ if (cb) cb("error please provide a key using get(key)")
321
340
  return
322
341
  }
323
342
 
@@ -334,15 +353,16 @@ const Holster = opt => {
334
353
  }
335
354
  if (set) data = {[utils.text.random()]: data}
336
355
 
356
+ const _ack = ack(ctxid)
337
357
  const result = check(data)
338
358
  if (typeof result === "string") {
339
359
  // All strings returned from check are errors, cannot continue.
340
- ack(result)
360
+ _ack(result)
341
361
  return
342
362
  }
343
363
 
344
364
  // Resolve the current context before putting data.
345
- const {item, soul} = resolve({put: data}, ack)
365
+ const {item, soul} = resolve({put: data}, _ack)
346
366
  if (!soul) return
347
367
 
348
368
  if (result === true) {
@@ -354,7 +374,7 @@ const Holster = opt => {
354
374
  ctx.user || opt.secure ? {"#": soul} : {"#": soul, ".": item}
355
375
  wire.get(lex, async msg => {
356
376
  if (msg.err) {
357
- ack(`error getting ${soul}: ${msg.err}`)
377
+ _ack(`error getting ${soul}: ${msg.err}`)
358
378
  return
359
379
  }
360
380
 
@@ -365,21 +385,21 @@ const Holster = opt => {
365
385
  // Not a rel, can just put the data.
366
386
  if (!node) node = {}
367
387
  node[item] = data
368
- const g = await graph(soul, node, ctx.user, ack)
388
+ const g = await graph(soul, node, ctx.user, _ack)
369
389
  if (g === null) return
370
390
 
371
- wire.put(g, ack)
391
+ wire.put(g, _ack)
372
392
  return
373
393
  }
374
394
 
375
395
  wire.get({"#": id}, async msg => {
376
396
  if (msg.err) {
377
- ack(`error getting ${id}: ${msg.err}`)
397
+ _ack(`error getting ${id}: ${msg.err}`)
378
398
  return
379
399
  }
380
400
 
381
401
  if (!msg.put || !msg.put[id]) {
382
- ack(`error ${id} not found`)
402
+ _ack(`error ${id} not found`)
383
403
  return
384
404
  }
385
405
 
@@ -400,16 +420,16 @@ const Holster = opt => {
400
420
  api(_ctxid).put(null, res)
401
421
  })
402
422
  if (err) {
403
- ack(err)
423
+ _ack(err)
404
424
  return
405
425
  }
406
426
  }
407
427
  if (!node) node = {}
408
428
  node[item] = data
409
- const g = await graph(soul, node, ctx.user, ack)
429
+ const g = await graph(soul, node, ctx.user, _ack)
410
430
  if (g === null) return
411
431
 
412
- wire.put(g, ack)
432
+ wire.put(g, _ack)
413
433
  })
414
434
  })
415
435
  return
@@ -422,7 +442,7 @@ const Holster = opt => {
422
442
  ctx.user || opt.secure ? {"#": soul} : {"#": soul, ".": item}
423
443
  wire.get(lex, async msg => {
424
444
  if (msg.err) {
425
- ack(`error getting ${soul}.${item}: ${msg.err}`)
445
+ _ack(`error getting ${soul}.${item}: ${msg.err}`)
426
446
  return
427
447
  }
428
448
 
@@ -433,12 +453,12 @@ const Holster = opt => {
433
453
  // The current rel doesn't exist, so add it first.
434
454
  if (!node) node = {}
435
455
  node[item] = utils.rel.ify(utils.text.random())
436
- const g = await graph(soul, node, ctx.user, ack)
456
+ const g = await graph(soul, node, ctx.user, _ack)
437
457
  if (g === null) return
438
458
 
439
459
  wire.put(g, err => {
440
460
  if (err) {
441
- ack(`error putting ${item} on ${soul}: ${err}`)
461
+ _ack(`error putting ${item} on ${soul}: ${err}`)
442
462
  } else {
443
463
  const _ctxid = utils.text.random()
444
464
  const chain = [{item: item, soul: soul}]
@@ -466,13 +486,13 @@ const Holster = opt => {
466
486
  }
467
487
  })
468
488
  if (err) {
469
- ack(err)
489
+ _ack(err, ctxid)
470
490
  return
471
491
  }
472
492
  }
473
493
 
474
494
  if (update.length === 0) {
475
- ack(null)
495
+ _ack(null)
476
496
  return
477
497
  }
478
498
 
@@ -480,7 +500,7 @@ const Holster = opt => {
480
500
  // object, so fetch the node so the rest of the updates can be added.
481
501
  wire.get({"#": id}, async msg => {
482
502
  if (msg.err) {
483
- ack(`error getting ${id}: ${msg.err}`)
503
+ _ack(`error getting ${id}: ${msg.err}`)
484
504
  return
485
505
  }
486
506
 
@@ -489,10 +509,10 @@ const Holster = opt => {
489
509
  update.forEach(key => {
490
510
  node[key] = data[key]
491
511
  })
492
- const g = await graph(id, node, ctx.user, ack)
512
+ const g = await graph(id, node, ctx.user, _ack)
493
513
  if (g === null) return
494
514
 
495
- wire.put(g, ack)
515
+ wire.put(g, _ack)
496
516
  })
497
517
  })
498
518
  },
@@ -618,8 +638,9 @@ const Holster = opt => {
618
638
  if (!cb) return api(ctxid)
619
639
 
620
640
  // When there's a callback need to resolve the context first.
621
- const {soul} = resolve({get: lex}, done)
622
- if (soul) get(lex, soul, done, _opt)
641
+ const _done = done(ctxid)
642
+ const {soul} = resolve({get: lex}, _done)
643
+ if (soul) get(lex, soul, _done, _opt)
623
644
  }
624
645
  }
625
646
  return user
@@ -37,12 +37,16 @@ describe("holster.get", () => {
37
37
  })
38
38
 
39
39
  test("get unknown keys in for loop callbacks null", (t, done) => {
40
+ let count = 0
40
41
  for (let i = 0; i < 5; i++) {
41
42
  holster.get("unknown" + i, data => {
42
43
  assert.equal(data, null)
43
- if (i === 4) done()
44
+ count++
44
45
  })
45
46
  }
47
+ setTimeout(() => {
48
+ if (count === 5) done()
49
+ }, 200)
46
50
  })
47
51
 
48
52
  test("nested unknown keys both callbacks null", (t, done) => {
@@ -71,12 +71,16 @@ describe("holster.put", () => {
71
71
  }
72
72
 
73
73
  setTimeout(() => {
74
+ let count = 0
74
75
  for (let i = 0; i < 5; i++) {
75
76
  holster.get("for" + i, data => {
76
77
  assert.equal(data, i)
77
- if (i === 4) setTimeout(done, 100)
78
+ count++
78
79
  })
79
80
  }
81
+ setTimeout(() => {
82
+ if (count === 5) done()
83
+ }, 200)
80
84
  }, 200)
81
85
  })
82
86
 
@@ -63,12 +63,16 @@ describe("holster.user.get", () => {
63
63
  })
64
64
 
65
65
  test("get unknown keys in for loop callbacks null", (t, done) => {
66
+ let count = 0
66
67
  for (let i = 0; i < 5; i++) {
67
68
  user.get("unknown" + i, data => {
68
69
  assert.equal(data, null)
69
- if (i === 4) done()
70
+ count++
70
71
  })
71
72
  }
73
+ setTimeout(() => {
74
+ if (count === 5) done()
75
+ }, 200)
72
76
  })
73
77
 
74
78
  test("nested get unknown key callback null", (t, done) => {
@@ -101,11 +101,12 @@ describe("holster.user.on", () => {
101
101
 
102
102
  // Need to wait until after all initial puts are done to set on listeners.
103
103
  setTimeout(async () => {
104
+ let count = 0
104
105
  for (let i = 0; i < 5; i++) {
105
106
  user.get("for" + i).on(data => {
106
107
  assert.equal(data, "update" + i)
107
108
  user.get("for" + i).off()
108
- if (i === 4) done()
109
+ count++
109
110
  })
110
111
  }
111
112
 
@@ -117,6 +118,9 @@ describe("holster.user.on", () => {
117
118
  })
118
119
  assert.equal(err, null)
119
120
  }
121
+ setTimeout(() => {
122
+ if (count === 5) done()
123
+ }, 500)
120
124
  }, 500)
121
125
  }, 1000)
122
126
  })
@@ -98,12 +98,16 @@ describe("holster.user.put", () => {
98
98
  })()
99
99
 
100
100
  setTimeout(async () => {
101
+ let count = 0
101
102
  for (let i = 0; i < 5; i++) {
102
103
  user.get("for" + i, data => {
103
104
  assert.equal(data, i)
104
- if (i === 4) setTimeout(done, 100)
105
+ count++
105
106
  })
106
107
  }
108
+ setTimeout(() => {
109
+ if (count === 5) done()
110
+ }, 200)
107
111
  }, 500)
108
112
  })
109
113