@mblaney/holster 2.3.0 → 2.3.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": "2.3.0",
3
+ "version": "2.3.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/holster.js CHANGED
@@ -73,11 +73,7 @@ const Holster = opt => {
73
73
  const data = await new Promise(res => {
74
74
  const _ctxid = utils.text.random()
75
75
  allctx.set(_ctxid, {chain: [{item: null, soul: id}], user: ctxUser})
76
- api(_ctxid).next(
77
- null,
78
- res,
79
- retries === 0 ? utils.obj.put(_opt, "fast", true) : _opt,
80
- )
76
+ api(_ctxid).next(null, res, _opt)
81
77
  })
82
78
  if (data !== null || retries >= 5) return data
83
79
  await new Promise(resolve => setTimeout(resolve, 50))
package/src/wire.js CHANGED
@@ -534,12 +534,12 @@ const Wire = opt => {
534
534
  return
535
535
  }
536
536
 
537
- store.get(lex, (err, ack) => {
538
- if (ack) {
537
+ store.get(lex, (err, stored) => {
538
+ if (stored) {
539
539
  // Populate in-memory graph from store data before sending the server
540
540
  // request, so stale server responses with older state are correctly
541
541
  // rejected by ham.mix.
542
- for (const [soul, node] of Object.entries(ack)) {
542
+ for (const [soul, node] of Object.entries(stored)) {
543
543
  if (!node) continue
544
544
  if (!graph[soul]) {
545
545
  graph[soul] = {...node}
@@ -567,40 +567,44 @@ const Wire = opt => {
567
567
  }
568
568
  }
569
569
  // Full-node fetch: the graph is now complete for this soul.
570
- if (!lex["."] && ack[lex["#"]]) hasNode.add(lex["#"])
571
- // Only serve from store if the requested property is present.
572
- const node = ack[lex["#"]]
573
- const hasProperty =
574
- node &&
575
- Object.keys(node).some(
576
- key => key !== "_" && utils.match(lex["."], key),
577
- )
578
- if (hasProperty) {
579
- // Public keys are immutable once written — never need a wire check.
580
- if (!isNode && lex["."] !== utils.userPublicKey) {
581
- if (!lex["."] || typeof lex["."] === "string") {
582
- const checkKey = lex["#"] + (lex["."] ? "." + lex["."] : "")
583
- const now = Date.now()
584
- const lastCheck = recentChecks.get(checkKey)
585
- if (!lastCheck || now - lastCheck > recentCheckTTL) {
586
- recentChecks.set(checkKey, now)
587
- const sendResult = send(request)
588
- if (sendResult && sendResult.err) {
589
- cb({err: sendResult.err})
590
- return
591
- }
592
- }
593
- } else {
570
+ if (!lex["."] && stored[lex["#"]]) hasNode.add(lex["#"])
571
+ }
572
+
573
+ // Answered from the graph rather than the raw store result: the merge
574
+ // above keeps any newer in-memory values, so a local put that store
575
+ // hasn't finished persisting yet is still included, while everything
576
+ // already on disk fills in the rest of the node.
577
+ const ack = {...stored, ...Get(lex, graph, true)}
578
+ // Only serve locally if the requested property is present.
579
+ const node = ack[lex["#"]]
580
+ const hasProperty =
581
+ node &&
582
+ Object.keys(node).some(key => key !== "_" && utils.match(lex["."], key))
583
+ if (hasProperty) {
584
+ // Public keys are immutable once written - never need a wire check.
585
+ if (!isNode && lex["."] !== utils.userPublicKey) {
586
+ if (!lex["."] || typeof lex["."] === "string") {
587
+ const checkKey = lex["#"] + (lex["."] ? "." + lex["."] : "")
588
+ const now = Date.now()
589
+ const lastCheck = recentChecks.get(checkKey)
590
+ if (!lastCheck || now - lastCheck > recentCheckTTL) {
591
+ recentChecks.set(checkKey, now)
594
592
  const sendResult = send(request)
595
593
  if (sendResult && sendResult.err) {
596
594
  cb({err: sendResult.err})
597
595
  return
598
596
  }
599
597
  }
598
+ } else {
599
+ const sendResult = send(request)
600
+ if (sendResult && sendResult.err) {
601
+ cb({err: sendResult.err})
602
+ return
603
+ }
600
604
  }
601
- cb(err ? {put: ack, err: err} : {put: ack})
602
- return
603
605
  }
606
+ cb(err ? {put: ack, err: err} : {put: ack})
607
+ return
604
608
  }
605
609
 
606
610
  // Data wasn't found locally, log errors and call back after a timeout.
@@ -678,17 +682,6 @@ const Wire = opt => {
678
682
  return
679
683
  }
680
684
 
681
- // Ham.mix has already merged this locally-authored update into
682
- // graph[soul], synchronously, before store.put below even starts
683
- // persisting it - mark it as fully known now rather than waiting
684
- // for a future disk fetch to do so, so a read landing in that
685
- // window doesn't get forced through a disk round-trip that could
686
- // itself race ahead of this same write and find nothing, even
687
- // though the correct answer was already sitting in memory.
688
- for (const soul of Object.keys(update.now)) {
689
- hasNode.add(soul)
690
- }
691
-
692
685
  if (opt.userLimit) {
693
686
  for (const [soul, node] of Object.entries(update.now)) {
694
687
  const pub = node[utils.userPublicKey]
package/ts/src/get.ts CHANGED
@@ -59,9 +59,10 @@ const Get = (lex: Lex, graph: Graph, fast?: boolean): Graph | undefined => {
59
59
  // return undefined to ensure we get all matching properties from store.
60
60
  if (!fast) return undefined
61
61
 
62
+ let found = false
62
63
  for (const key of Object.keys(graph[soul]!)) {
63
64
  // Always include userPublicKey for verification, regardless of filter
64
- if (key === userPublicKey || match(lex["."], key)) {
65
+ if (key !== "_" && (key === userPublicKey || match(lex["."], key))) {
65
66
  node[key] = graph[soul]![key]!
66
67
  node._[">"][key] = graph[soul]!._[">"][key]!
67
68
  const state = graph[soul]!._[">"][key]!
@@ -69,8 +70,10 @@ const Get = (lex: Lex, graph: Graph, fast?: boolean): Graph | undefined => {
69
70
  if (sigs && sigs[state]) {
70
71
  signatures[state.toString()] = sigs[state]!
71
72
  }
73
+ found = true
72
74
  }
73
75
  }
76
+ if (!found) return undefined
74
77
  }
75
78
 
76
79
  if (Object.keys(signatures).length > 0) {
package/ts/src/holster.ts CHANGED
@@ -182,11 +182,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
182
182
  const data = await new Promise<unknown>(res => {
183
183
  const _ctxid = utils.text.random()
184
184
  allctx.set(_ctxid, {chain: [{item: null, soul: id}], user: ctxUser})
185
- api(_ctxid).next(
186
- null as never,
187
- res as never,
188
- (retries === 0 ? utils.obj.put(opt, "fast", true) : opt) as never,
189
- )
185
+ api(_ctxid).next(null as never, res as never, opt as never)
190
186
  })
191
187
  if (data !== null || retries >= 5) return data
192
188
  await new Promise(resolve => setTimeout(resolve, 50))
package/ts/src/wire.ts CHANGED
@@ -593,12 +593,12 @@ const Wire = (opt: HolsterOptions): WireAPI => {
593
593
  return
594
594
  }
595
595
 
596
- store.get(lex, (err, ack) => {
597
- if (ack) {
596
+ store.get(lex, (err, stored) => {
597
+ if (stored) {
598
598
  // Populate in-memory graph from store data before sending the server
599
599
  // request, so stale server responses with older state are correctly
600
600
  // rejected by ham.mix.
601
- for (const [soul, node] of Object.entries(ack)) {
601
+ for (const [soul, node] of Object.entries(stored)) {
602
602
  if (!node) continue
603
603
  if (!graph[soul]) {
604
604
  graph[soul] = {...node} as unknown as Graph[string]
@@ -622,40 +622,44 @@ const Wire = (opt: HolsterOptions): WireAPI => {
622
622
  }
623
623
  }
624
624
  // Full-node fetch: the graph is now complete for this soul.
625
- if (!lex["."] && ack[lex["#"]]) hasNode.add(lex["#"])
626
- // Only serve from store if the requested property is present.
627
- const node = ack[lex["#"]]
628
- const hasProperty =
629
- node &&
630
- Object.keys(node).some(
631
- key => key !== "_" && utils.match(lex["."], key),
632
- )
633
- if (hasProperty) {
634
- // Public keys are immutable once written — never need a wire check.
635
- if (!isNode && lex["."] !== utils.userPublicKey) {
636
- if (!lex["."] || typeof lex["."] === "string") {
637
- const checkKey = lex["#"] + (lex["."] ? "." + lex["."] : "")
638
- const now = Date.now()
639
- const lastCheck = recentChecks.get(checkKey)
640
- if (!lastCheck || now - lastCheck > recentCheckTTL) {
641
- recentChecks.set(checkKey, now)
642
- const sendResult = send(request)
643
- if (sendResult && sendResult.err) {
644
- cb({err: sendResult.err})
645
- return
646
- }
647
- }
648
- } else {
625
+ if (!lex["."] && stored[lex["#"]]) hasNode.add(lex["#"])
626
+ }
627
+
628
+ // Answered from the graph rather than the raw store result: the merge
629
+ // above keeps any newer in-memory values, so a local put that store
630
+ // hasn't finished persisting yet is still included, while everything
631
+ // already on disk fills in the rest of the node.
632
+ const ack = {...stored, ...Get(lex, graph, true)}
633
+ // Only serve locally if the requested property is present.
634
+ const node = ack[lex["#"]]
635
+ const hasProperty =
636
+ node &&
637
+ Object.keys(node).some(key => key !== "_" && utils.match(lex["."], key))
638
+ if (hasProperty) {
639
+ // Public keys are immutable once written - never need a wire check.
640
+ if (!isNode && lex["."] !== utils.userPublicKey) {
641
+ if (!lex["."] || typeof lex["."] === "string") {
642
+ const checkKey = lex["#"] + (lex["."] ? "." + lex["."] : "")
643
+ const now = Date.now()
644
+ const lastCheck = recentChecks.get(checkKey)
645
+ if (!lastCheck || now - lastCheck > recentCheckTTL) {
646
+ recentChecks.set(checkKey, now)
649
647
  const sendResult = send(request)
650
648
  if (sendResult && sendResult.err) {
651
649
  cb({err: sendResult.err})
652
650
  return
653
651
  }
654
652
  }
653
+ } else {
654
+ const sendResult = send(request)
655
+ if (sendResult && sendResult.err) {
656
+ cb({err: sendResult.err})
657
+ return
658
+ }
655
659
  }
656
- cb(err ? {put: ack, err: err} : {put: ack})
657
- return
658
660
  }
661
+ cb(err ? {put: ack, err: err} : {put: ack})
662
+ return
659
663
  }
660
664
 
661
665
  if (err) console.log(err)
@@ -725,17 +729,6 @@ const Wire = (opt: HolsterOptions): WireAPI => {
725
729
  return
726
730
  }
727
731
 
728
- // Ham.mix has already merged this locally-authored update into
729
- // graph[soul], synchronously, before store.put below even starts
730
- // persisting it - mark it as fully known now rather than waiting
731
- // for a future disk fetch to do so, so a read landing in that
732
- // window doesn't get forced through a disk round-trip that could
733
- // itself race ahead of this same write and find nothing, even
734
- // though the correct answer was already sitting in memory.
735
- for (const soul of Object.keys(update.now)) {
736
- hasNode.add(soul)
737
- }
738
-
739
732
  if (options.userLimit) {
740
733
  for (const [soul, node] of Object.entries(update.now)) {
741
734
  const pub = node![utils.userPublicKey] as string | undefined