@mblaney/holster 2.2.1 → 2.2.2

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.2.1",
3
+ "version": "2.2.2",
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
@@ -167,7 +167,7 @@ const Holster = opt => {
167
167
  const node = msg.put && msg.put[soul]
168
168
  const id = utils.rel.is(node && node[item])
169
169
  if (!id) {
170
- // If it's a plain value (not a rel) and this is a get, deliver it.
170
+ // If it's a plain value (not a rel) and using get, deliver it.
171
171
  // node._ guards against timeout null-acks which lack metadata.
172
172
  if (!on && node && node._ && typeof node[item] !== "undefined") {
173
173
  if (timer) clearTimeout(timer)
@@ -189,7 +189,9 @@ const Holster = opt => {
189
189
  {...request._opt, secure: !!(ctx.user || opt.secure)},
190
190
  )
191
191
  }
192
- wire.on({"#": soul, ".": item}, handler, false, request._opt)
192
+ // The get parameter is set to true to call handler immediately for
193
+ // listeners set on nested properties.
194
+ wire.on({"#": soul, ".": item}, handler, true, request._opt)
193
195
  // Time out after the same total duration as the on() retry loop
194
196
  // (1+2+4+8+16 = 31s) so callers are not blocked forever if the node
195
197
  // genuinely doesn't exist.
@@ -325,7 +327,7 @@ const Holster = opt => {
325
327
  api(ctxid).put(request.put, cb)
326
328
  })
327
329
  } else if (on) {
328
- // Item is not a rel yet — watch the parent soul for when it
330
+ // Item is not a rel yet - watch the parent soul for when it
329
331
  // becomes one, then retry chain resolution.
330
332
  watchForRel(soul, item, ctx, i, request, cb)
331
333
  if (request._get) cb(null)
@@ -346,7 +348,7 @@ const Holster = opt => {
346
348
  })
347
349
  } else {
348
350
  if (on) {
349
- // Node doesn't exist yet — watch for it to appear.
351
+ // Node doesn't exist yet - watch for it to appear.
350
352
  watchForRel(soul, item, ctx, i, request, cb)
351
353
  if (request._get) cb(null)
352
354
  } else if (get && node) {
@@ -356,14 +358,14 @@ const Holster = opt => {
356
358
  Object.keys(sv).length > 0 &&
357
359
  typeof sv[item] === "undefined"
358
360
  ) {
359
- // State vector has other properties but not this one — never written.
361
+ // State vector has other properties but not this one.
360
362
  if (cb) cb(null)
361
363
  } else {
362
- // Empty state vector or item is tracked — may arrive via push.
364
+ // Empty state vector or item is tracked, may arrive via push.
363
365
  watchForRel(soul, item, ctx, i, request, cb, false)
364
366
  }
365
367
  } else {
366
- // Soul doesn't exist or no get — return null.
368
+ // Soul doesn't exist or no get - return null.
367
369
  if (cb) cb(null)
368
370
  }
369
371
  }
@@ -698,7 +700,7 @@ const Holster = opt => {
698
700
  if (_opt && _opt.nested) {
699
701
  // attachNested calls this once for the top level and again for
700
702
  // every discovered child, each needing its own independent
701
- // context - a single shared _ctxid would accumulate .next()
703
+ // context. A single shared _ctxid would accumulate .next()
702
704
  // calls onto the same path instead of giving sibling paths.
703
705
  const factory = () => {
704
706
  const _ctxid = utils.text.random()
@@ -724,37 +726,36 @@ const Holster = opt => {
724
726
  const maxRetries = 5
725
727
  const retryDelay = 1000 // Start with 1 second
726
728
  let retryTimer = null
727
- // Which soul the persistent wire listener is currently attached to
728
- // - starts on the parent (soul/item), and moves to a rel's target
729
- // soul once item turns out to be one, since further updates land
730
- // on the target's own soul, not the parent's. Tracked (rather than
731
- // switching unconditionally) so the two places that can discover a
732
- // rel - the initial check below and a later resolveValue retry -
733
- // don't both re-register the same listener. Scoped to this one
734
- // on() call's closure, so concurrent subscriptions (even on the
735
- // same key) each track their own independently.
729
+ // Which soul the persistent wire listener is currently attached to.
730
+ // Starts on the parent (soul/item), and moves to a rel's target soul
731
+ // once item is created, since further updates land on the target's own
732
+ // soul, not the parent's. Tracked rather than switching unconditionally
733
+ // so that the two places that can discover a rel - the initial check
734
+ // below and a later resolveValue retry - don't both re-register the
735
+ // same listener. Scoped to this one on() call's closure, so concurrent
736
+ // subscriptions (even on the same key) can each track their own.
736
737
  let listenedSoul = soul
737
738
 
738
739
  map.set(cb, () => {
739
- // Bail out if off() has already cleaned up this context — the mapped
740
+ // Bail out if off() has already cleaned up this context. The mapped
740
741
  // callback can fire twice (once from wire.on and once from the _get
741
742
  // immediate-read path) and the second call after async context
742
743
  // cleanup would crash in resolve(). Matches the guard in watchForRel.
743
744
  if (!allctx.has(ctxid)) return
744
- // Cancel any pending retry — the persistent listener firing means we
745
+
746
+ // Cancel any pending retry. The persistent listener firing means we
745
747
  // have an update and should not call cb twice.
746
748
  clearTimeout(retryTimer)
747
749
  retryTimer = null
748
750
 
749
751
  // Resolves {soul, item} to a genuine value, following a rel to its
750
- // target if the property turns out to be one - checked fresh on
751
- // every attempt, not just the first, since a property can still
752
- // be a plain undefined/null on an early check and only become a
753
- // rel once a later retry catches up (e.g. a nested rel-creating
754
- // put both adds "item" as a rel on this node AND populates its
755
- // target, and either half can still be mid-flight when the
756
- // listener first fires). wire.get() can't reliably tell
757
- // "genuinely null" apart from "hasn't landed yet" either - its
752
+ // target if the property turns out to be one. This is checked on
753
+ // every attempt, since a property can still be a plain undefined/null
754
+ // on an early check and only become a rel once a later retry catches
755
+ // up (e.g. a nested rel-creating put both adds "item" as a rel on
756
+ // this node AND populates its target, and either half can still be
757
+ // mid-flight when the listener first fires). Also wire.get() can't
758
+ // reliably tell "genuinely null" apart from "hasn't landed yet", its
758
759
  // own internal timeout fallback explicitly answers null when
759
760
  // nothing comes back in time - so a null/missing value is
760
761
  // retried a few times before being trusted as final, the same
@@ -848,7 +849,7 @@ const Holster = opt => {
848
849
  const current = msg.put && msg.put[soul] && msg.put[soul][item]
849
850
  const id = utils.rel.is(current)
850
851
  if (id) {
851
- // It's a rel, need to switch listener to the related node -
852
+ // It's a rel, need to switch listener to the related node,
852
853
  // unless resolveValue (triggered by the initial listener
853
854
  // firing) already beat this check to it.
854
855
  if (listenedSoul !== id) {
@@ -878,7 +879,7 @@ const Holster = opt => {
878
879
  return
879
880
  }
880
881
 
881
- // Resolve the current context before removing event listener.
882
+ // Resolve the current context before removing the event listener.
882
883
  const {item, soul} = resolve({off: true}, cb)
883
884
  if (!soul) return
884
885
 
package/ts/src/holster.ts CHANGED
@@ -291,7 +291,9 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
291
291
  },
292
292
  )
293
293
  }
294
- wire.on({"#": soul, ".": item}, handler, false, request._opt)
294
+ // The get parameter is set to true to call handler immediately for
295
+ // listeners set on nested properties.
296
+ wire.on({"#": soul, ".": item}, handler, true, request._opt)
295
297
  // Time out after the same total duration as the on() retry loop
296
298
  // (1+2+4+8+16 = 31s) so callers are not blocked forever if the node
297
299
  // genuinely doesn't exist.
@@ -69,7 +69,7 @@ const page = users.slice(offset, offset + limit)
69
69
  console.log(` Total users: ${users.length}`)
70
70
  if (page.length > 0) {
71
71
  console.log(
72
- ` Showing full public keys for ${page.length} user${page.length !== 1 ? "s" : ""} (offset: ${offset}, limit: ${limit})`,
72
+ ` Showing public keys for ${page.length} user${page.length !== 1 ? "s" : ""} (query: --offset ${offset} --limit ${limit})`,
73
73
  )
74
74
  }
75
75
 
@@ -78,10 +78,10 @@ for (const {pub, total} of page) {
78
78
  const pubLimit = data[pub]
79
79
  const limitStr =
80
80
  typeof pubLimit !== "number"
81
- ? "no override (see server logs for default)"
81
+ ? "server default"
82
82
  : pubLimit === 0
83
- ? "0 B (blocked)"
83
+ ? "blocked"
84
84
  : formatBytes(pubLimit * 1048576)
85
85
  console.log(` ${pub}`)
86
- console.log(` used: ${formatBytes(total)} / limit: ${limitStr}`)
86
+ console.log(` used: ${formatBytes(total)} / ${limitStr}`)
87
87
  }