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 +28 -0
- package/package.json +2 -2
- package/server-library.js +2 -2
- package/statebus.js +112 -34
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
300
|
-
// for (var i=0; i<
|
|
301
|
-
// console.log('Forgetting', funk_name(
|
|
302
|
-
//
|
|
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, '
|
|
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, '
|
|
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, '
|
|
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
|
|
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 (
|
|
715
|
-
bogus_check(
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
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 + ' ' +
|
|
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 (
|
|
727
|
-
bogus_check(
|
|
728
|
-
if (
|
|
729
|
-
// Delete
|
|
730
|
-
for (var i=0; i<
|
|
731
|
-
var handler =
|
|
732
|
-
if (handler.
|
|
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
|
-
|
|
737
|
-
i--
|
|
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 + ' ' +
|
|
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
|
|
768
|
-
for (var i=0; i <
|
|
769
|
-
handler =
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
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
|
-
|
|
776
|
-
|
|
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
|
|