@mblaney/holster 2.3.1 → 2.3.3

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.1",
3
+ "version": "2.3.3",
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
@@ -416,10 +416,10 @@ const Holster = opt => {
416
416
  return
417
417
  }
418
418
 
419
- // lex requires a callback as it's not included in the chain below.
419
+ // lex isn't part of the chain below, so without a callback the
420
+ // result is returned as a promise instead.
420
421
  if (lex && typeof cb !== "function") {
421
- console.log("error lex requires a callback function")
422
- return
422
+ return new Promise(resolve => api(ctxid).get(key, lex, resolve, _opt))
423
423
  }
424
424
 
425
425
  // Top level keys are added to a root node so their values don't need
@@ -462,10 +462,12 @@ const Holster = opt => {
462
462
  return
463
463
  }
464
464
 
465
- // lex requires a callback as it's not included in the chain below.
465
+ // lex isn't part of the chain below, so without a callback the
466
+ // result is returned as a promise instead.
466
467
  if (lex && typeof cb !== "function") {
467
- console.log("error lex requires a callback function")
468
- return
468
+ return new Promise(resolve =>
469
+ api(ctxid).next(key, lex, resolve, _opt),
470
+ )
469
471
  }
470
472
 
471
473
  const ctx = allctx.get(ctxid)
@@ -769,6 +771,9 @@ const Holster = opt => {
769
771
  // same listener. Scoped to this one on() call's closure, so concurrent
770
772
  // subscriptions (even on the same key) can each track their own.
771
773
  let listenedSoul = soul
774
+ // Each update starts its own async read, and an earlier one can finish
775
+ // last - only the newest is delivered, so stale data can't follow it.
776
+ let latestRun = 0
772
777
 
773
778
  map.set(cb, () => {
774
779
  // Bail out if off() has already cleaned up this context. The mapped
@@ -781,6 +786,12 @@ const Holster = opt => {
781
786
  // have an update and should not call cb twice.
782
787
  clearTimeout(retryTimer)
783
788
  retryTimer = null
789
+ const run = ++latestRun
790
+ const deliver = data => {
791
+ if (run !== latestRun) return
792
+ retryTimer = null
793
+ cb(data)
794
+ }
784
795
 
785
796
  // Resolves {soul, item} to a genuine value, following a rel to its
786
797
  // target if the property turns out to be one. This is checked on
@@ -796,6 +807,7 @@ const Holster = opt => {
796
807
  // way the rel branch below already retries on a null target
797
808
  // read.
798
809
  const resolveValue = (retries, node, value) => {
810
+ if (run !== latestRun) return
799
811
  const id = utils.rel.is(value)
800
812
  if (id) {
801
813
  if (listenedSoul !== id) {
@@ -803,21 +815,16 @@ const Holster = opt => {
803
815
  wire.on({"#": id, ".": null}, map.get(cb), false, _opt)
804
816
  listenedSoul = id
805
817
  }
806
- readRelTarget(id, ctx ? ctx.user : null, _opt).then(data => {
807
- retryTimer = null
808
- cb(data)
809
- })
818
+ readRelTarget(id, ctx ? ctx.user : null, _opt).then(deliver)
810
819
  return
811
820
  }
812
821
 
813
822
  if (value !== undefined && value !== null) {
814
- retryTimer = null
815
- cb(value)
823
+ deliver(value)
816
824
  return
817
825
  }
818
826
  if (retries >= maxRetries) {
819
- retryTimer = null
820
- cb(value !== undefined ? value : null)
827
+ deliver(value !== undefined ? value : null)
821
828
  return
822
829
  }
823
830
  // Fast once the parent node already exists (a value is likely
@@ -977,10 +984,10 @@ const Holster = opt => {
977
984
  return
978
985
  }
979
986
 
980
- // lex requires a callback as it's not included in the chain below.
987
+ // lex isn't part of the chain below, so without a callback the
988
+ // result is returned as a promise instead.
981
989
  if (lex && !cb) {
982
- console.log("error lex requires a callback function")
983
- return
990
+ return new Promise(resolve => user.get(keys, lex, resolve, _opt))
984
991
  }
985
992
 
986
993
  ctxid = utils.text.random()
package/ts/src/holster.ts CHANGED
@@ -37,11 +37,8 @@ export interface HolsterAPI {
37
37
  cb: (data: unknown) => void,
38
38
  _opt?: WireOptions,
39
39
  ): HolsterAPI
40
- (
41
- key: string | null,
42
- lex?: LexWithDot,
43
- _opt?: WireOptions,
44
- ): AwaitableHolsterAPI
40
+ (key: string | null, lex: LexWithDot, _opt?: WireOptions): Promise<unknown>
41
+ (key: string | null, lex?: null, _opt?: WireOptions): AwaitableHolsterAPI
45
42
  }
46
43
  next: {
47
44
  (
@@ -55,11 +52,8 @@ export interface HolsterAPI {
55
52
  cb: (data: unknown) => void,
56
53
  _opt?: WireOptions,
57
54
  ): HolsterAPI
58
- (
59
- key: string | null,
60
- lex?: LexWithDot,
61
- _opt?: WireOptions,
62
- ): AwaitableHolsterAPI
55
+ (key: string | null, lex: LexWithDot, _opt?: WireOptions): Promise<unknown>
56
+ (key: string | null, lex?: null, _opt?: WireOptions): AwaitableHolsterAPI
63
57
  }
64
58
  put: {
65
59
  (
@@ -601,9 +595,12 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
601
595
  return this
602
596
  }
603
597
 
598
+ // lex isn't part of the chain below, so without a callback the
599
+ // result is returned as a promise instead.
604
600
  if (lexFilter && typeof callback !== "function") {
605
- console.log("error lex requires a callback function")
606
- return this
601
+ return new Promise(resolve =>
602
+ api(ctxid).get(key, lexFilter, resolve, opts),
603
+ ) as never
607
604
  }
608
605
 
609
606
  ctxid = utils.text.random()
@@ -662,9 +659,12 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
662
659
  return this
663
660
  }
664
661
 
662
+ // lex isn't part of the chain below, so without a callback the
663
+ // result is returned as a promise instead.
665
664
  if (lexFilter && typeof callback !== "function") {
666
- console.log("error lex requires a callback function")
667
- return this
665
+ return new Promise(resolve =>
666
+ api(ctxid).next(key, lexFilter, resolve, opts),
667
+ ) as never
668
668
  }
669
669
 
670
670
  const ctx = allctx.get(ctxid)
@@ -1020,6 +1020,9 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
1020
1020
  // on() call's closure, so concurrent subscriptions (even on the
1021
1021
  // same key) each track their own independently.
1022
1022
  let listenedSoul = soul
1023
+ // Each update starts its own async read, and an earlier one can finish
1024
+ // last - only the newest is delivered, so stale data can't follow it.
1025
+ let latestRun = 0
1023
1026
 
1024
1027
  map.set(callback, () => {
1025
1028
  // Bail out if off() has already cleaned up this context — the mapped
@@ -1031,6 +1034,12 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
1031
1034
  // have an update and should not call callback twice.
1032
1035
  clearTimeout(retryTimer!)
1033
1036
  retryTimer = null
1037
+ const run = ++latestRun
1038
+ const deliver = (data: unknown) => {
1039
+ if (run !== latestRun) return
1040
+ retryTimer = null
1041
+ callback(data)
1042
+ }
1034
1043
 
1035
1044
  // Resolves {soul, item} to a genuine value, following a rel to its
1036
1045
  // target if the property turns out to be one - checked fresh on
@@ -1051,6 +1060,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
1051
1060
  node: Record<string, GraphValue> | null | undefined,
1052
1061
  value: unknown,
1053
1062
  ): void => {
1063
+ if (run !== latestRun) return
1054
1064
  const id = utils.rel.is(value as GraphValue)
1055
1065
  if (id) {
1056
1066
  if (listenedSoul !== id) {
@@ -1063,21 +1073,16 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
1063
1073
  )
1064
1074
  listenedSoul = id
1065
1075
  }
1066
- readRelTarget(id, ctx ? ctx.user : null, opts).then(data => {
1067
- retryTimer = null
1068
- callback(data)
1069
- })
1076
+ readRelTarget(id, ctx ? ctx.user : null, opts).then(deliver)
1070
1077
  return
1071
1078
  }
1072
1079
 
1073
1080
  if (value !== undefined && value !== null) {
1074
- retryTimer = null
1075
- callback(value)
1081
+ deliver(value)
1076
1082
  return
1077
1083
  }
1078
1084
  if (retries >= maxRetries) {
1079
- retryTimer = null
1080
- callback(value !== undefined ? value : null)
1085
+ deliver(value !== undefined ? value : null)
1081
1086
  return
1082
1087
  }
1083
1088
  // Fast once the parent node already exists (a value is likely
@@ -1261,9 +1266,12 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
1261
1266
  return this
1262
1267
  }
1263
1268
 
1269
+ // lex isn't part of the chain below, so without a callback the
1270
+ // result is returned as a promise instead.
1264
1271
  if (lexFilter && !callback) {
1265
- console.log("error lex requires a callback function")
1266
- return this
1272
+ return new Promise(resolve =>
1273
+ user.get!(keys, lexFilter, resolve, opts),
1274
+ ) as never
1267
1275
  }
1268
1276
 
1269
1277
  ctxid = utils.text.random()
package/ts/src/user.ts CHANGED
@@ -9,7 +9,7 @@ import SEA from "./sea.ts"
9
9
  import type {
10
10
  UserIdentity,
11
11
  HolsterOptions,
12
- LexFilter,
12
+ LexWithDot,
13
13
  WireOptions,
14
14
  } from "./schemas.ts"
15
15
  import type {HolsterAPI, AwaitableHolsterAPI} from "./holster.ts"
@@ -45,7 +45,7 @@ export interface UserInterface {
45
45
  get?: {
46
46
  (
47
47
  keys: string | string[],
48
- lex: LexFilter,
48
+ lex: LexWithDot,
49
49
  cb: (data: unknown) => void,
50
50
  _opt?: WireOptions,
51
51
  ): HolsterAPI
@@ -56,7 +56,12 @@ export interface UserInterface {
56
56
  ): HolsterAPI
57
57
  (
58
58
  keys: string | string[],
59
- lex?: LexFilter,
59
+ lex: LexWithDot,
60
+ _opt?: WireOptions,
61
+ ): Promise<unknown>
62
+ (
63
+ keys: string | string[],
64
+ lex?: null,
60
65
  _opt?: WireOptions,
61
66
  ): AwaitableHolsterAPI
62
67
  }