statebus 7.0.32 → 7.0.33

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/extras/tests.js CHANGED
@@ -870,6 +870,34 @@ test(function proxies (done) {
870
870
  // Getting a normal property should do a get
871
871
  })
872
872
 
873
+ test(function key_patterns (done) {
874
+ bus.honk = 3
875
+ bus('path/:pattern/stuff*').getter = function (key, key_parts, t) {
876
+ t.done({ key: key, val: key_parts })
877
+ }
878
+
879
+ bus.get_once('path/foo/stuff(yes:maam,okay)', (o) => {
880
+ assert(o.val.pattern === 'foo')
881
+ done()
882
+ })
883
+ })
884
+
885
+ test(function kson (done) {
886
+ bus('kson_stuff/foo/stuff*').getter = function (key, kson, t) {
887
+ t.done({
888
+ key: key,
889
+ val: {kson: kson}
890
+ })
891
+ }
892
+
893
+ bus.get_once('kson_stuff/foo/stuff(yes:maam,okay)', (o) => {
894
+ console.log(o)
895
+ assert(o.val.kson.yes === 'maam')
896
+ assert(o.val.kson.okay)
897
+ done()
898
+ })
899
+ })
900
+
873
901
  test(function only_one (done) {
874
902
  bus('only_one/*').getter = function (k) {
875
903
  var id = k[k.length-1]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statebus",
3
- "version": "7.0.32",
3
+ "version": "7.0.33",
4
4
  "description": "Synchronize state.",
5
5
  "main": "statebus.js",
6
6
  "scripts": {
@@ -20,7 +20,7 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "bcrypt-nodejs": "0.0.3",
23
- "braid-http": "^1.0.7",
23
+ "braid-http": "^1.3.4",
24
24
  "chokidar": "^3.4.0",
25
25
  "cookie": "^0.3.1",
26
26
  "express": "^4.15.3",
package/server-library.js CHANGED
@@ -184,13 +184,13 @@ function import_server (bus, make_statebus, options)
184
184
  // console.log('http_in: doing cbus.get(', key, ')')
185
185
 
186
186
  var send_to_client = (o, t) => {
187
- // console.trace('http_in: Sending update of', key)
187
+ console.log('http_in: Sending update of', {key, o, res})
188
188
  var body = to_http_body(o)
189
189
 
190
190
  // Note: if body === undefined, we need to send the
191
191
  // equivalent of a 404. This is missing in braid spec:
192
192
  // https://github.com/braid-org/braid-spec/issues/110
193
- res.sendVersion({body: body || 'null'})
193
+ res.sendUpdate({body: body || 'null'})
194
194
 
195
195
  // And shut down the connection if there's no subscription
196
196
  if (!req.subscribe)
package/statebus.js CHANGED
@@ -296,10 +296,10 @@
296
296
  var id = 'bus-' + Math.random().toString(36).substring(7)
297
297
  bus.toString = function () { return bus.label || 'bus'+this_bus_num || id }
298
298
  bus.delete_bus = function () {
299
- // // Forget all wildcard handlers
300
- // for (var i=0; i<wildcard_handlers.length; i++) {
301
- // console.log('Forgetting', funk_name(wildcard_handlers[i].funk))
302
- // wildcard_handlers[i].funk.forget()
299
+ // // Forget all pattern handlers
300
+ // for (var i=0; i<pattern_handlers.length; i++) {
301
+ // console.log('Forgetting', funk_name(pattern_handlers[i].funk))
302
+ // pattern_handlers[i].funk.forget()
303
303
  // }
304
304
 
305
305
  // // Forget all handlers
@@ -642,7 +642,7 @@
642
642
  func.defined = func.defined || []
643
643
  func.defined.push({bus, method, key, as: 'handler'})
644
644
  func.use_linked_json = true
645
- bind(key, method, func, 'allow_wildcards')
645
+ bind(key, method, func, 'pattern')
646
646
  }
647
647
  }
648
648
  else {
@@ -655,12 +655,12 @@
655
655
  func.defined = func.defined || []
656
656
  func.defined.push(
657
657
  {as:'handler', bus:bus, method:method, key:key})
658
- bind(key, method, func, 'allow_wildcards')
658
+ bind(key, method, func, 'pattern')
659
659
  },
660
660
  get: function () {
661
661
  var result = bindings(key, method)
662
662
  for (var i=0; i<result.length; i++) result[i] = result[i].func
663
- result.delete = function (func) { unbind (key, method, func, 'allow_wildcards') }
663
+ result.delete = function (func) { unbind (key, method, func, 'pattern') }
664
664
  return result
665
665
  }
666
666
  })
@@ -684,6 +684,8 @@
684
684
  case 'key':
685
685
  case 'k':
686
686
  handler.args['key'] = i; break
687
+ case 'key_parts':
688
+ handler.args['key_parts'] = i; break
687
689
  case 'json':
688
690
  case 'vars':
689
691
  handler.args['vars'] = i; break
@@ -702,44 +704,71 @@
702
704
  handler.args['obj'] = i; break
703
705
  case 'old':
704
706
  handler.args['old'] = i; break
707
+ case 'kson':
708
+ handler.args['kson'] = i; break
705
709
  }
706
710
  }
707
711
 
708
712
  // The funks attached to each key, maps e.g. 'get /point/3' to '/30'
709
713
  var handlers = new One_To_Many()
710
- var wildcard_handlers = [] // An array of {prefix, method, funk}
714
+ var pattern_handlers = [] // An array of {pattern, method, funk}
711
715
 
712
716
  // A set of timers, for keys to send forgets on
713
717
  var to_be_forgotten = {}
714
- function bind (key, method, func, allow_wildcards) {
715
- bogus_check(key)
716
- if (allow_wildcards && key[key.length-1] === '*')
717
- wildcard_handlers.push({prefix: key,
718
- method: method,
719
- funk: func})
718
+ function bind (pattern, method, func, type) {
719
+ bogus_check(pattern)
720
+
721
+ function pattern_matcher (pattern) {
722
+ var param_names = []
723
+ var regex = new RegExp('^' + pattern.replace(/(?<=^|\/):[^/()]+|\*/g, match => {
724
+ // Replace * with .*
725
+ if (match === '*') return '.*'
726
+
727
+ // Replace :foo with ([^/]+), and remember the "foo" name
728
+ param_names.push(match.slice(1))
729
+ return '([^/]+)'
730
+ }) + '$')
731
+
732
+ return path => {
733
+ var match = path.match(regex)
734
+ if (!match) return null
735
+ var params = {}
736
+ match.slice(1).forEach((val, i) => params[param_names[i]] = val)
737
+ return params
738
+ }
739
+ }
740
+
741
+ // Add patterns to the list, not the hash
742
+ if (type === 'pattern' && /[:*]/.test(pattern)) // Is it a pattern?
743
+ pattern_handlers.push({pattern: pattern,
744
+ matcher: pattern_matcher(pattern),
745
+ method: method,
746
+ funk: func})
747
+
748
+ // Add simple keys to the hash
720
749
  else
721
- handlers.add(method + ' ' + key, funk_key(func))
750
+ handlers.add(method + ' ' + pattern, funk_key(func))
722
751
 
723
752
  // Now check if the method is a get and there's a getted
724
753
  // key in this space, and if so call the handler.
725
754
  }
726
- function unbind (key, method, funk, allow_wildcards) {
727
- bogus_check(key)
728
- if (allow_wildcards && key[key.length-1] === '*')
729
- // Delete wildcard connection
730
- for (var i=0; i<wildcard_handlers.length; i++) {
731
- var handler = wildcard_handlers[i]
732
- if (handler.prefix === key
755
+ function unbind (pattern, method, funk, type) {
756
+ bogus_check(pattern)
757
+ if (type === 'pattern' && /[:*]/.test(pattern)) // Is it a pattern?
758
+ // Delete pattern connection
759
+ for (var i=0; i < pattern_handlers.length; i++) {
760
+ var handler = pattern_handlers[i]
761
+ if (handler.pattern === pattern
733
762
  && handler.method === method
734
763
  && handler.funk === funk) {
735
764
 
736
- wildcard_handlers.splice(i,1) // Splice this element out of the array
737
- i-- // And decrement the counter while we're looping
765
+ pattern_handlers.splice(i,1) // Splice this element out of the array
766
+ i-- // And decrement the counter while we're looping
738
767
  }
739
768
  }
740
769
  else
741
770
  // Delete direct connection
742
- handlers.delete(method + ' ' + key, funk_key(funk))
771
+ handlers.delete(method + ' ' + pattern, funk_key(funk))
743
772
  }
744
773
 
745
774
  function bindings(key, method) {
@@ -764,16 +793,20 @@
764
793
  }
765
794
  }
766
795
 
767
- // Now iterate through prefixes
768
- for (var i=0; i < wildcard_handlers.length; i++) {
769
- handler = wildcard_handlers[i]
770
-
771
- var prefix = handler.prefix.slice(0, -1) // Cut off the *
772
- if (prefix === key.substr(0,prefix.length) // If the prefix matches
773
- && method === handler.method // And it has the right method
796
+ // Now iterate through patterns
797
+ for (var i=0; i < pattern_handlers.length; i++) {
798
+ handler = pattern_handlers[i]
799
+ var matches = handler.matcher(key)
800
+ if (matches // If the pattern matches
801
+ && method === handler.method // And it has the right method
774
802
  && !seen[funk_key(handler.funk)]) {
775
- handler.funk.statebus_binding = {key:handler.prefix, method:method}
776
- result.push({method:method, key:handler.prefix, func:handler.funk})
803
+
804
+ handler.funk.statebus_binding = {
805
+ key: handler.pattern,
806
+ method: method,
807
+ params: matches
808
+ }
809
+ result.push({method, key:handler.pattern, func:handler.funk})
777
810
  seen[funk_key(handler.funk)] = true
778
811
  }
779
812
  }
@@ -860,6 +893,46 @@
860
893
  return 'Bad JSON "' + r + '" for key ' + key_arg()
861
894
  }
862
895
  }
896
+ // Parse out query params for URLs in the style /foo/bar(param1,param2:val2).
897
+ // We're currently calling that (param1,param2:val2) part "kson", and presuming
898
+ // that it exists in the * part of the pattern like in /foo/bar*.
899
+ function kson_args () {
900
+ // This code is copied from Raphael Walker's parser.coffee
901
+ function split_once (str, char) {
902
+ var i = str.indexOf(char)
903
+ return i === -1 ? [str, ""] : [str.slice(0, i), str.slice(i + 1)]
904
+ }
905
+
906
+ // KSON = Kinda Simple Object Notation
907
+ // but it rhymes with JSON
908
+ function parse_kson (str) {
909
+ // Coffeescript doesn't have object comprehensions :(
910
+ var ret = {}
911
+
912
+ // Now process the string:
913
+ str
914
+ // Pull out the parentheses
915
+ .slice(1, -1)
916
+ // Split by commas. TODO: Allow spaces after commas?
917
+ .split(",")
918
+ // Delete empty parts (this ensures that empty strings
919
+ // will properly result in empty KSON, and allows trailing
920
+ // commas)
921
+ .filter(part => part.length)
922
+ .forEach(part => {
923
+ // If the part has a comma, its a key:value, otherwise
924
+ // it's just a singleton
925
+ var [k, v] = split_once(part, ":")
926
+
927
+ if (v.length === 0) ret[k] = true
928
+ // If the value is itself a KSON object, parse it recursively
929
+ else if (v.startsWith("(")) ret[k] = parse_kson(v)
930
+ else ret[k] = v
931
+ })
932
+ return ret
933
+ }
934
+ return parse_kson(rest_arg())
935
+ }
863
936
  var f = reactive(function () {
864
937
  // Initialize transaction
865
938
  t = clone(t || {})
@@ -934,6 +1007,9 @@
934
1007
  switch (k) {
935
1008
  case 'key':
936
1009
  args[func.args[k]] = key_arg(); break
1010
+ case 'key_parts':
1011
+ args[func.args[k]] = func.statebus_binding && func.statebus_binding.params
1012
+ break
937
1013
  case 'rest':
938
1014
  args[func.args[k]] = rest_arg(); break
939
1015
  case 'vars':
@@ -947,6 +1023,8 @@
947
1023
  var key = key_arg()
948
1024
  args[func.args[k]] = bus.cache[key] || (bus.cache[key] = {key:key})
949
1025
  break
1026
+ case 'kson':
1027
+ args[func.args[k]] = kson_args(rest_arg()); break
950
1028
  }
951
1029
  }
952
1030