statebus 7.0.32 → 7.1.0
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/client-library.js +7 -3
- package/extras/tests.js +161 -27
- package/package.json +2 -2
- package/server-library.js +222 -232
- package/statebus.js +425 -115
package/statebus.js
CHANGED
|
@@ -92,6 +92,10 @@
|
|
|
92
92
|
var gets_in = new One_To_Many() // Maps `key' to `pub_funcs' subscribed to our key
|
|
93
93
|
|
|
94
94
|
var currently_saving
|
|
95
|
+
|
|
96
|
+
// Two forms:
|
|
97
|
+
// - set(obj, t) with snapshot
|
|
98
|
+
// - set(key, t) with patches
|
|
95
99
|
function set (obj, t) {
|
|
96
100
|
// First let's handle patches. We receive them as:
|
|
97
101
|
//
|
|
@@ -105,11 +109,11 @@
|
|
|
105
109
|
var key = obj,
|
|
106
110
|
obj = bus.cache[key]
|
|
107
111
|
|
|
108
|
-
console.log('set: applying the patches!', {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
})
|
|
112
|
+
// console.log('set: applying the patches!', {
|
|
113
|
+
// key: key,
|
|
114
|
+
// obj: obj,
|
|
115
|
+
// patches: t.patches[0]
|
|
116
|
+
// })
|
|
113
117
|
obj.val = apply_patch(obj.val, t.patches[0])
|
|
114
118
|
}
|
|
115
119
|
|
|
@@ -290,16 +294,25 @@
|
|
|
290
294
|
return f
|
|
291
295
|
}
|
|
292
296
|
|
|
293
|
-
// Called with a key to produce a subspace
|
|
294
|
-
|
|
297
|
+
// Called with a key to produce a subspace.
|
|
298
|
+
// We currently have two forms:
|
|
299
|
+
//
|
|
300
|
+
// old_subspace('foo').to_get = ... // etc.
|
|
301
|
+
//
|
|
302
|
+
// new_subspace('foo', {get: ...}) // etc
|
|
303
|
+
//
|
|
304
|
+
else if (arg2 === undefined)
|
|
305
|
+
return old_subspace(arg1)
|
|
306
|
+
else
|
|
307
|
+
return new_subspace(arg1, arg2)
|
|
295
308
|
}
|
|
296
309
|
var id = 'bus-' + Math.random().toString(36).substring(7)
|
|
297
310
|
bus.toString = function () { return bus.label || 'bus'+this_bus_num || id }
|
|
298
311
|
bus.delete_bus = function () {
|
|
299
|
-
// // Forget all
|
|
300
|
-
// for (var i=0; i<
|
|
301
|
-
// console.log('Forgetting', funk_name(
|
|
302
|
-
//
|
|
312
|
+
// // Forget all pattern handlers
|
|
313
|
+
// for (var i=0; i<pattern_handlers.length; i++) {
|
|
314
|
+
// console.log('Forgetting', funk_name(pattern_handlers[i].funk))
|
|
315
|
+
// pattern_handlers[i].funk.forget()
|
|
303
316
|
// }
|
|
304
317
|
|
|
305
318
|
// // Forget all handlers
|
|
@@ -393,6 +406,7 @@
|
|
|
393
406
|
|
|
394
407
|
return obj
|
|
395
408
|
}
|
|
409
|
+
|
|
396
410
|
deep_map(object, update_object)
|
|
397
411
|
return modified_keys.values()
|
|
398
412
|
}
|
|
@@ -449,6 +463,12 @@
|
|
|
449
463
|
// delete backup_cache[key]
|
|
450
464
|
delete gets_out[key]
|
|
451
465
|
delete to_be_forgotten[key]
|
|
466
|
+
|
|
467
|
+
if (bus.auto_delete && gets_in.empty()) {
|
|
468
|
+
bus.auto_delete()
|
|
469
|
+
bus.delete_bus()
|
|
470
|
+
}
|
|
471
|
+
|
|
452
472
|
}, 200)
|
|
453
473
|
}
|
|
454
474
|
}
|
|
@@ -622,50 +642,49 @@
|
|
|
622
642
|
|
|
623
643
|
// ****************
|
|
624
644
|
// Connections
|
|
625
|
-
function
|
|
645
|
+
function old_subspace (key, params) {
|
|
626
646
|
var methods = {getter:null, setter:null, on_set:null, on_set_sync:null,
|
|
627
647
|
on_delete:null, deleter:null, forgetter:null}
|
|
628
648
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
return result
|
|
649
|
+
var result = {}
|
|
650
|
+
for (var method in methods)
|
|
651
|
+
(function (method) {
|
|
652
|
+
Object.defineProperty(result, method, {
|
|
653
|
+
set: function (func) {
|
|
654
|
+
autodetect_args(func)
|
|
655
|
+
func.defined = func.defined || []
|
|
656
|
+
func.defined.push(
|
|
657
|
+
{as:'handler', bus:bus, method:method, key:key})
|
|
658
|
+
bind(key, method, func, 'pattern')
|
|
659
|
+
},
|
|
660
|
+
get: function () {
|
|
661
|
+
var result = bindings(key, method)
|
|
662
|
+
for (var i=0; i<result.length; i++) result[i] = result[i].func
|
|
663
|
+
result.delete = function (func) { unbind (key, method, func, 'pattern') }
|
|
664
|
+
return result
|
|
665
|
+
}
|
|
666
|
+
})
|
|
667
|
+
})(method)
|
|
668
|
+
return result
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function new_subspace (key, params) {
|
|
672
|
+
for (var method in params) {
|
|
673
|
+
var func = params[method]
|
|
674
|
+
var param_names = {get:'getter', set:'setter',
|
|
675
|
+
delete: 'deleter', forget: 'forgetter',
|
|
676
|
+
on_set:'on_set', on_set_sync:'on_set_sync'}
|
|
677
|
+
|
|
678
|
+
// Map it from the API's name to our internal name
|
|
679
|
+
console.assert(param_names[method],
|
|
680
|
+
'Method "' + method + '" is invalid')
|
|
681
|
+
method = param_names[method]
|
|
682
|
+
|
|
683
|
+
autodetect_args(func)
|
|
684
|
+
func.call_with_proxies = true
|
|
685
|
+
func.defined = func.defined || []
|
|
686
|
+
func.defined.push({bus, method, key, as: 'handler'})
|
|
687
|
+
bind(key, method, func, 'pattern')
|
|
669
688
|
}
|
|
670
689
|
}
|
|
671
690
|
|
|
@@ -684,6 +703,8 @@
|
|
|
684
703
|
case 'key':
|
|
685
704
|
case 'k':
|
|
686
705
|
handler.args['key'] = i; break
|
|
706
|
+
case 'path':
|
|
707
|
+
handler.args['path'] = i; break
|
|
687
708
|
case 'json':
|
|
688
709
|
case 'vars':
|
|
689
710
|
handler.args['vars'] = i; break
|
|
@@ -702,44 +723,96 @@
|
|
|
702
723
|
handler.args['obj'] = i; break
|
|
703
724
|
case 'old':
|
|
704
725
|
handler.args['old'] = i; break
|
|
726
|
+
case 'func_args':
|
|
727
|
+
handler.args['func_args'] = i; break
|
|
705
728
|
}
|
|
706
729
|
}
|
|
707
730
|
|
|
708
731
|
// The funks attached to each key, maps e.g. 'get /point/3' to '/30'
|
|
709
732
|
var handlers = new One_To_Many()
|
|
710
|
-
var
|
|
733
|
+
var pattern_handlers = [] // An array of {pattern, method, funk}
|
|
711
734
|
|
|
712
735
|
// A set of timers, for keys to send forgets on
|
|
713
736
|
var to_be_forgotten = {}
|
|
714
|
-
function bind (
|
|
715
|
-
bogus_check(
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
737
|
+
function bind (pattern, method, func, type) {
|
|
738
|
+
bogus_check(pattern)
|
|
739
|
+
|
|
740
|
+
function pattern_matcher (pattern) {
|
|
741
|
+
var param_names = []
|
|
742
|
+
|
|
743
|
+
// console.log('Creating pattern for', pattern)
|
|
744
|
+
|
|
745
|
+
// First, convert the pattern into a regexp.
|
|
746
|
+
|
|
747
|
+
// 1. Handle :foo by swapping it with '([^/*()]+)'
|
|
748
|
+
pattern = pattern.replace(/(?<=^|\/):[^/*()]+/g, match => {
|
|
749
|
+
// Replace :foo with ([^/]+), and remember the "foo" name
|
|
750
|
+
param_names.push(match.slice(1))
|
|
751
|
+
return '([^/*()]+)'
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
// Now look for * and () at the end and replace with regexps
|
|
755
|
+
if (pattern.slice(-3) === '*()')
|
|
756
|
+
pattern = pattern.slice(0, -3) + '(?<star>[^()]*)(?<fstring>\\(.*\\))?'
|
|
757
|
+
else if (pattern.slice(-1) === '*')
|
|
758
|
+
pattern = pattern.slice(0, -1) + '(?<star>.*)'
|
|
759
|
+
else if (pattern.slice(-2) === '()')
|
|
760
|
+
pattern = pattern.slice(0, -2) + '(?<fstring>\\(.*\\))?'
|
|
761
|
+
|
|
762
|
+
// Construct the regexp
|
|
763
|
+
var regex = new RegExp('^' + pattern + '$')
|
|
764
|
+
|
|
765
|
+
// console.log('Pattern became ', regex)
|
|
766
|
+
|
|
767
|
+
// Return a matcher function that runs it
|
|
768
|
+
return path => {
|
|
769
|
+
var match = path.match(regex)
|
|
770
|
+
if (!match) return null
|
|
771
|
+
var params = {}
|
|
772
|
+
params.func_args = match.groups.fstring
|
|
773
|
+
params.star = match.groups.star
|
|
774
|
+
|
|
775
|
+
// Add to params.path all the path parts
|
|
776
|
+
params.path = {}
|
|
777
|
+
match.slice(1, param_names.length + 1).forEach((val, i) =>
|
|
778
|
+
params.path[param_names[i]] = val
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
return params
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Add patterns to the list, not the hash
|
|
786
|
+
if (type === 'pattern' && /[:*]/.test(pattern)) // Is it a pattern?
|
|
787
|
+
pattern_handlers.push({pattern: pattern,
|
|
788
|
+
matcher: pattern_matcher(pattern),
|
|
789
|
+
method: method,
|
|
790
|
+
funk: func})
|
|
791
|
+
|
|
792
|
+
// Add simple keys to the hash
|
|
720
793
|
else
|
|
721
|
-
handlers.add(method + ' ' +
|
|
794
|
+
handlers.add(method + ' ' + pattern, funk_key(func))
|
|
722
795
|
|
|
723
796
|
// Now check if the method is a get and there's a getted
|
|
724
797
|
// key in this space, and if so call the handler.
|
|
725
798
|
}
|
|
726
|
-
function unbind (
|
|
727
|
-
bogus_check(
|
|
728
|
-
if (
|
|
729
|
-
// Delete
|
|
730
|
-
for (var i=0; i<
|
|
731
|
-
var handler =
|
|
732
|
-
if (handler.
|
|
799
|
+
function unbind (pattern, method, funk, type) {
|
|
800
|
+
bogus_check(pattern)
|
|
801
|
+
if (type === 'pattern' && /[:*]/.test(pattern)) // Is it a pattern?
|
|
802
|
+
// Delete pattern connection
|
|
803
|
+
for (var i=0; i < pattern_handlers.length; i++) {
|
|
804
|
+
var handler = pattern_handlers[i]
|
|
805
|
+
if (handler.pattern === pattern
|
|
733
806
|
&& handler.method === method
|
|
734
807
|
&& handler.funk === funk) {
|
|
735
808
|
|
|
736
|
-
|
|
737
|
-
i--
|
|
809
|
+
pattern_handlers.splice(i,1) // Splice this element out of the array
|
|
810
|
+
i-- // And decrement the counter while we're looping
|
|
738
811
|
}
|
|
739
812
|
}
|
|
740
813
|
else
|
|
741
814
|
// Delete direct connection
|
|
742
|
-
handlers.delete(method + ' ' +
|
|
815
|
+
handlers.delete(method + ' ' + pattern, funk_key(funk))
|
|
743
816
|
}
|
|
744
817
|
|
|
745
818
|
function bindings(key, method) {
|
|
@@ -764,16 +837,22 @@
|
|
|
764
837
|
}
|
|
765
838
|
}
|
|
766
839
|
|
|
767
|
-
// Now iterate through
|
|
768
|
-
for (var i=0; i <
|
|
769
|
-
handler =
|
|
840
|
+
// Now iterate through patterns
|
|
841
|
+
for (var i=0; i < pattern_handlers.length; i++) {
|
|
842
|
+
handler = pattern_handlers[i]
|
|
843
|
+
var matches = handler.matcher(key)
|
|
844
|
+
// console.log('The matching of', pattern_handlers[i], 'to', key, 'is', matches)
|
|
770
845
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
&& method === handler.method // And it has the right method
|
|
846
|
+
if (matches // If the pattern matches
|
|
847
|
+
&& method === handler.method // And it has the right method
|
|
774
848
|
&& !seen[funk_key(handler.funk)]) {
|
|
775
|
-
|
|
776
|
-
|
|
849
|
+
|
|
850
|
+
handler.funk.statebus_binding = {
|
|
851
|
+
key: handler.pattern,
|
|
852
|
+
method: method,
|
|
853
|
+
args: matches
|
|
854
|
+
}
|
|
855
|
+
result.push({method, key:handler.pattern, func:handler.funk})
|
|
777
856
|
seen[funk_key(handler.funk)] = true
|
|
778
857
|
}
|
|
779
858
|
}
|
|
@@ -782,6 +861,11 @@
|
|
|
782
861
|
}
|
|
783
862
|
|
|
784
863
|
function run_handler(funck, method, arg, options) {
|
|
864
|
+
// If method == "set", then arg is an object {key: "foobar", ...}
|
|
865
|
+
// If method == "get", then arg is a key "foobar"
|
|
866
|
+
// If method == "forget", then arg is a key "foobar"
|
|
867
|
+
// If method == "delete", then arg is a key "foobar"
|
|
868
|
+
|
|
785
869
|
options = options || {}
|
|
786
870
|
var t = options.t,
|
|
787
871
|
just_make_it = options.dont_run,
|
|
@@ -842,15 +926,30 @@
|
|
|
842
926
|
// Fresh get/set/forget/delete handlers will just be regular
|
|
843
927
|
// functions. We'll store their arg and transaction and let them
|
|
844
928
|
// re-run until they are done re-running.
|
|
845
|
-
function key_arg () { return ((typeof arg.key)
|
|
846
|
-
function rest_arg () {
|
|
929
|
+
function key_arg () { return ((typeof arg.key) === 'string') ? arg.key : arg }
|
|
930
|
+
function rest_arg () {
|
|
931
|
+
return func.statebus_binding && func.statebus_binding.args.star
|
|
932
|
+
// (key_arg()).substr(binding.length-1)
|
|
933
|
+
}
|
|
847
934
|
function val_arg () {
|
|
848
935
|
console.assert(method === 'setter' || method === 'on_set' || method === 'on_set_sync',
|
|
849
936
|
'Bad method for val_arg()')
|
|
850
|
-
// Is there a time I am supposed to return bus.cache[arg]? It used to say:
|
|
851
|
-
// arg.key ? arg : bus.cache[arg]
|
|
852
937
|
|
|
853
|
-
|
|
938
|
+
var value = (typeof arg === 'string'
|
|
939
|
+
// If arg is a key, then semantics of val is "the
|
|
940
|
+
// current value in the cache". I'm doing this
|
|
941
|
+
// because it *might* be useful in a getter ... but
|
|
942
|
+
// let's see if we actually have good examples of use
|
|
943
|
+
// for this feature.
|
|
944
|
+
? bus.cache[arg]
|
|
945
|
+
// Otherwise, arg must be the actual object
|
|
946
|
+
: arg)
|
|
947
|
+
|
|
948
|
+
// The Proxy API unwraps the .val for us
|
|
949
|
+
if (func.call_with_proxies)
|
|
950
|
+
value = value.val
|
|
951
|
+
|
|
952
|
+
return value
|
|
854
953
|
}
|
|
855
954
|
function vars_arg () {
|
|
856
955
|
var r = rest_arg()
|
|
@@ -860,6 +959,50 @@
|
|
|
860
959
|
return 'Bad JSON "' + r + '" for key ' + key_arg()
|
|
861
960
|
}
|
|
862
961
|
}
|
|
962
|
+
// Parse out function params for URLs in the style /foo/bar(param1,param2:val2).
|
|
963
|
+
// We're currently calling that (param1,param2:val2) part "function params", and presuming
|
|
964
|
+
// that it exists in the () part of the pattern like in /foo/bar*().
|
|
965
|
+
function func_args () {
|
|
966
|
+
// This code is copied from Raphael Walker's parser.coffee
|
|
967
|
+
function split_once (str, char) {
|
|
968
|
+
var i = str.indexOf(char)
|
|
969
|
+
return i === -1 ? [str, ""] : [str.slice(0, i), str.slice(i + 1)]
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// Function Strings -- an alternative to query strings
|
|
973
|
+
//
|
|
974
|
+
function parse_function_string (str) {
|
|
975
|
+
// Coffeescript doesn't have object comprehensions :(
|
|
976
|
+
var ret = {}
|
|
977
|
+
|
|
978
|
+
// Now process the string:
|
|
979
|
+
str
|
|
980
|
+
// Pull out the parentheses
|
|
981
|
+
.slice(1, -1)
|
|
982
|
+
// Split by commas. TODO: Allow spaces after commas?
|
|
983
|
+
.split(",")
|
|
984
|
+
// Delete empty parts (this ensures that empty strings
|
|
985
|
+
// will properly result in empty func_string, and allows trailing
|
|
986
|
+
// commas)
|
|
987
|
+
.filter(part => part.length)
|
|
988
|
+
.forEach(part => {
|
|
989
|
+
// If the part has a comma, its a key:value, otherwise
|
|
990
|
+
// it's just a singleton
|
|
991
|
+
var [k, v] = split_once(part, ":")
|
|
992
|
+
|
|
993
|
+
if (v.length === 0) ret[k] = true
|
|
994
|
+
// If the value is itself a func_string params object, parse it recursively
|
|
995
|
+
else if (v.startsWith("(")) ret[k] = parse_function_string(v)
|
|
996
|
+
else ret[k] = v
|
|
997
|
+
})
|
|
998
|
+
return ret
|
|
999
|
+
}
|
|
1000
|
+
// console.log('Getting func_args from', func.statebus_binding.args)
|
|
1001
|
+
if (!(func.statebus_binding.args
|
|
1002
|
+
&& func.statebus_binding.args.func_args))
|
|
1003
|
+
return undefined
|
|
1004
|
+
return parse_function_string(func.statebus_binding.args.func_args)
|
|
1005
|
+
}
|
|
863
1006
|
var f = reactive(function () {
|
|
864
1007
|
// Initialize transaction
|
|
865
1008
|
t = clone(t || {})
|
|
@@ -878,8 +1021,8 @@
|
|
|
878
1021
|
if (method !== 'forgetter')
|
|
879
1022
|
t.done = function (o) {
|
|
880
1023
|
var key = method === 'setter' ? arg.key : arg
|
|
881
|
-
if (func.
|
|
882
|
-
o = {key, val: o}
|
|
1024
|
+
if (func.call_with_proxies)
|
|
1025
|
+
o = {key, val: raw(o)}
|
|
883
1026
|
bus.log('We are DONE()ing', method, key, o||arg)
|
|
884
1027
|
|
|
885
1028
|
// We use a simple (and crappy?) heuristic to know if the
|
|
@@ -934,6 +1077,11 @@
|
|
|
934
1077
|
switch (k) {
|
|
935
1078
|
case 'key':
|
|
936
1079
|
args[func.args[k]] = key_arg(); break
|
|
1080
|
+
case 'path':
|
|
1081
|
+
args[func.args[k]] = (func.statebus_binding
|
|
1082
|
+
&& func.statebus_binding.args
|
|
1083
|
+
&& func.statebus_binding.args.path)
|
|
1084
|
+
break
|
|
937
1085
|
case 'rest':
|
|
938
1086
|
args[func.args[k]] = rest_arg(); break
|
|
939
1087
|
case 'vars':
|
|
@@ -946,7 +1094,11 @@
|
|
|
946
1094
|
case 'old':
|
|
947
1095
|
var key = key_arg()
|
|
948
1096
|
args[func.args[k]] = bus.cache[key] || (bus.cache[key] = {key:key})
|
|
1097
|
+
if (func.call_with_proxies)
|
|
1098
|
+
args[func.args[k]] = args[func.args[k]].val
|
|
949
1099
|
break
|
|
1100
|
+
case 'func_args':
|
|
1101
|
+
args[func.args[k]] = func_args(); break
|
|
950
1102
|
}
|
|
951
1103
|
}
|
|
952
1104
|
|
|
@@ -967,9 +1119,9 @@
|
|
|
967
1119
|
if (result === 'abort') t.abort()
|
|
968
1120
|
|
|
969
1121
|
// For get
|
|
970
|
-
if (func.
|
|
1122
|
+
if (func.call_with_proxies) {
|
|
971
1123
|
if (method === 'getter' && result !== undefined && !f.loading()) {
|
|
972
|
-
var obj = {key: arg, val: result}
|
|
1124
|
+
var obj = {key: arg, val: raw(result)}
|
|
973
1125
|
var new_t = clone(t || {})
|
|
974
1126
|
new_t.getter = true
|
|
975
1127
|
set.fire(obj, new_t)
|
|
@@ -1300,9 +1452,8 @@
|
|
|
1300
1452
|
|
|
1301
1453
|
var symbols = {
|
|
1302
1454
|
is_proxy: Symbol('is_proxy'),
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
// get_base: Symbol('get_base')
|
|
1455
|
+
raw: Symbol('raw'),
|
|
1456
|
+
link: Symbol('link')
|
|
1306
1457
|
}
|
|
1307
1458
|
function make_proxy () {
|
|
1308
1459
|
// var custom_inspect_symbol = nodejs && require('util').inspect.custom,
|
|
@@ -1323,8 +1474,9 @@
|
|
|
1323
1474
|
|
|
1324
1475
|
// We recursively descend through {key: ...} links
|
|
1325
1476
|
if (typeof o === 'object' && 'link' in o) {
|
|
1326
|
-
var new_base = bus.get(o.link)
|
|
1327
|
-
return item_proxy(new_base, '', new_base.val)
|
|
1477
|
+
// var new_base = bus.get(o.link)
|
|
1478
|
+
// return item_proxy(new_base, '', new_base.val)
|
|
1479
|
+
return link_proxy(o.link)
|
|
1328
1480
|
}
|
|
1329
1481
|
|
|
1330
1482
|
|
|
@@ -1344,18 +1496,20 @@
|
|
|
1344
1496
|
// return custom_inspect
|
|
1345
1497
|
if (k === symbols.is_proxy)
|
|
1346
1498
|
return true
|
|
1499
|
+
if (k === symbols.raw)
|
|
1500
|
+
return o
|
|
1347
1501
|
if (typeof k === 'symbol')
|
|
1348
1502
|
return undefined
|
|
1349
1503
|
|
|
1350
1504
|
// Compute the new path
|
|
1351
1505
|
var new_path = path + '[' + JSON.stringify(k) + ']'
|
|
1352
|
-
return item_proxy(base, new_path, o[
|
|
1506
|
+
return item_proxy(base, new_path, o[escape_field_json_to_bus(k)])
|
|
1353
1507
|
},
|
|
1354
1508
|
set: function (o, k, v) {
|
|
1355
|
-
var value =
|
|
1356
|
-
o[
|
|
1509
|
+
var value = escape_json_to_bus(v)
|
|
1510
|
+
o[escape_field_json_to_bus(k)] = value
|
|
1357
1511
|
var new_path = path + '[' + JSON.stringify(k) + ']'
|
|
1358
|
-
bus.set(
|
|
1512
|
+
bus.set.sync(
|
|
1359
1513
|
base,
|
|
1360
1514
|
// Forward the patches too
|
|
1361
1515
|
{patches: [{unit: 'json',
|
|
@@ -1367,17 +1521,17 @@
|
|
|
1367
1521
|
has: function (o, k) {
|
|
1368
1522
|
// if (custom_inspect && k === custom_inspect)
|
|
1369
1523
|
// return true
|
|
1370
|
-
return o.hasOwnProperty(
|
|
1524
|
+
return o.hasOwnProperty(escape_field_json_to_bus(k))
|
|
1371
1525
|
},
|
|
1372
1526
|
ownKeys: function () {
|
|
1373
|
-
return Object.keys(o).map(
|
|
1527
|
+
return Object.keys(o).map(unescape_field_bus_to_json)
|
|
1374
1528
|
},
|
|
1375
1529
|
getOwnPropertyDescriptor: function (target, key) {
|
|
1376
1530
|
return { enumerable: true, configurable: true, value: this.get(o, key) }
|
|
1377
1531
|
},
|
|
1378
1532
|
deleteProperty: function del (o, k) {
|
|
1379
1533
|
var new_path = path + '[' + JSON.stringify(k) + ']'
|
|
1380
|
-
delete o[
|
|
1534
|
+
delete o[escape_field_json_to_bus(k)]
|
|
1381
1535
|
bus.set(
|
|
1382
1536
|
base,
|
|
1383
1537
|
// Forward the patches too
|
|
@@ -1394,15 +1548,84 @@
|
|
|
1394
1548
|
// }
|
|
1395
1549
|
})}
|
|
1396
1550
|
|
|
1551
|
+
function link_proxy (url) {
|
|
1552
|
+
return new Proxy(function follow_link () {}, {
|
|
1553
|
+
get: function (o, k) {
|
|
1554
|
+
if (k === 'inspect' || k === 'valueOf')
|
|
1555
|
+
return undefined
|
|
1556
|
+
// if (custom_inspect && k === custom_inspect)
|
|
1557
|
+
// return custom_inspect
|
|
1558
|
+
if (k === symbols.is_proxy)
|
|
1559
|
+
return true
|
|
1560
|
+
if (k === symbols.raw)
|
|
1561
|
+
return {link: url}
|
|
1562
|
+
if (typeof k === 'symbol')
|
|
1563
|
+
return undefined
|
|
1564
|
+
|
|
1565
|
+
if (k === 'link')
|
|
1566
|
+
return url
|
|
1567
|
+
|
|
1568
|
+
return undefined
|
|
1569
|
+
},
|
|
1570
|
+
set: function (o, k, v) {
|
|
1571
|
+
if (k === 'link') {
|
|
1572
|
+
url = v
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
console.assert(false, "Have not fully implemented setting links yet")
|
|
1576
|
+
|
|
1577
|
+
// Todo: update the containing proxy object with bus.set.sync
|
|
1578
|
+
// else return
|
|
1579
|
+
// var value = escape_json_to_bus(v)
|
|
1580
|
+
// o[escape_field_json_to_bus(k)] = value
|
|
1581
|
+
// var new_path = path + '[' + JSON.stringify(k) + ']'
|
|
1582
|
+
// bus.set.sync(
|
|
1583
|
+
// base,
|
|
1584
|
+
// // Forward the patches too
|
|
1585
|
+
// {patches: [{unit: 'json',
|
|
1586
|
+
// range: new_path,
|
|
1587
|
+
// content: JSON.stringify(v)}]}
|
|
1588
|
+
// )
|
|
1589
|
+
return true
|
|
1590
|
+
},
|
|
1591
|
+
has: function (o, k) {
|
|
1592
|
+
return k === 'link'
|
|
1593
|
+
},
|
|
1594
|
+
ownKeys: function () {
|
|
1595
|
+
return ['link']
|
|
1596
|
+
},
|
|
1597
|
+
// getOwnPropertyDescriptor: function (target, key) {
|
|
1598
|
+
// return { enumerable: true, configurable: true, value: this.get(o, key) }
|
|
1599
|
+
// },
|
|
1600
|
+
// deleteProperty: function del (o, k) {
|
|
1601
|
+
// var new_path = path + '[' + JSON.stringify(k) + ']'
|
|
1602
|
+
// delete o[escape_field_json_to_bus(k)]
|
|
1603
|
+
// bus.set(
|
|
1604
|
+
// base,
|
|
1605
|
+
// // Forward the patches too
|
|
1606
|
+
// {patches: [{unit: 'json',
|
|
1607
|
+
// range: new_path,
|
|
1608
|
+
// content: undefined}]}
|
|
1609
|
+
// )
|
|
1610
|
+
// return true // Report success to Proxy
|
|
1611
|
+
// }
|
|
1612
|
+
|
|
1613
|
+
// For function proxy:
|
|
1614
|
+
apply: function apply (o, This, args) {
|
|
1615
|
+
// console.log('Descending into the link', url, '!')
|
|
1616
|
+
return top_level_proxy[url]
|
|
1617
|
+
}
|
|
1618
|
+
})
|
|
1619
|
+
}
|
|
1397
1620
|
|
|
1398
|
-
// For function proxies:
|
|
1399
|
-
// var dummy = function () {}
|
|
1400
1621
|
|
|
1401
1622
|
// The top-level Proxy object holds HTTP resources
|
|
1402
|
-
|
|
1623
|
+
var top_level_proxy = new Proxy(cache, {
|
|
1403
1624
|
get: function get(o, k) {
|
|
1404
1625
|
if (k === symbols.is_proxy)
|
|
1405
1626
|
return true
|
|
1627
|
+
if (k === symbols.raw)
|
|
1628
|
+
return raw_proxy()
|
|
1406
1629
|
if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
|
|
1407
1630
|
return undefined
|
|
1408
1631
|
bogus_check(k)
|
|
@@ -1412,7 +1635,7 @@
|
|
|
1412
1635
|
set: function set(o, key, val) {
|
|
1413
1636
|
bus.set({
|
|
1414
1637
|
key: key,
|
|
1415
|
-
val:
|
|
1638
|
+
val: escape_json_to_bus(val)
|
|
1416
1639
|
})
|
|
1417
1640
|
return true
|
|
1418
1641
|
},
|
|
@@ -1425,15 +1648,35 @@
|
|
|
1425
1648
|
// return 'fluffy'
|
|
1426
1649
|
// }
|
|
1427
1650
|
})
|
|
1651
|
+
|
|
1652
|
+
return top_level_proxy
|
|
1428
1653
|
}
|
|
1429
1654
|
bus.state = make_proxy()
|
|
1430
1655
|
|
|
1431
|
-
//
|
|
1432
|
-
//
|
|
1433
|
-
//
|
|
1434
|
-
|
|
1435
|
-
|
|
1656
|
+
// This is temporary code to wrap the cache as a flat key/valuel store
|
|
1657
|
+
// that returns raw objects, dereferencing .val. We can remove it once we
|
|
1658
|
+
// remove the internal .val stuff from statebus.
|
|
1659
|
+
function raw_proxy () {
|
|
1660
|
+
return new Proxy(cache, {
|
|
1661
|
+
get: function get(o, k) { return bus.cache[k].val },
|
|
1662
|
+
set: function set(o, key, val) {
|
|
1663
|
+
return false
|
|
1664
|
+
},
|
|
1665
|
+
deleteProperty: function del (o, k) {
|
|
1666
|
+
return false
|
|
1667
|
+
}
|
|
1668
|
+
})
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
function link (url) {
|
|
1672
|
+
return {[symbols.link]: url}
|
|
1673
|
+
}
|
|
1674
|
+
function raw (proxy) {
|
|
1675
|
+
if (!(typeof proxy === 'object' && proxy[symbols.is_proxy]))
|
|
1676
|
+
return proxy
|
|
1436
1677
|
|
|
1678
|
+
return proxy[symbols.raw]
|
|
1679
|
+
}
|
|
1437
1680
|
|
|
1438
1681
|
// So chrome can print out proxy objects decently
|
|
1439
1682
|
if (!nodejs)
|
|
@@ -1449,7 +1692,55 @@
|
|
|
1449
1692
|
hasBody: function (x) {return false}
|
|
1450
1693
|
}]
|
|
1451
1694
|
|
|
1452
|
-
|
|
1695
|
+
|
|
1696
|
+
// ************************************************
|
|
1697
|
+
//
|
|
1698
|
+
// Every dialogue we have with a peer gets an ID.
|
|
1699
|
+
// This `bus.dialogues` variable maps:
|
|
1700
|
+
//
|
|
1701
|
+
// dialogue ID -> send_function
|
|
1702
|
+
//
|
|
1703
|
+
// for each peer we are in dialogue with.
|
|
1704
|
+
//
|
|
1705
|
+
// One can use this to make sure we don't send an update
|
|
1706
|
+
// back to the same peer that sent it to us.
|
|
1707
|
+
|
|
1708
|
+
bus.dialogues = {}
|
|
1709
|
+
|
|
1710
|
+
|
|
1711
|
+
// ************************************************
|
|
1712
|
+
// Custom clients
|
|
1713
|
+
var client_counter = 0
|
|
1714
|
+
var client_busses = {}
|
|
1715
|
+
function client_bus_for (client_id) {
|
|
1716
|
+
if (!bus.custom_clients) return bus
|
|
1717
|
+
|
|
1718
|
+
// Do we have a bus for this client yet?
|
|
1719
|
+
if (client_busses[client_id])
|
|
1720
|
+
|
|
1721
|
+
// Return existing bus
|
|
1722
|
+
return client_busses[client_id]
|
|
1723
|
+
|
|
1724
|
+
else {
|
|
1725
|
+
|
|
1726
|
+
// Make a new bus
|
|
1727
|
+
var client_bus = make_bus()
|
|
1728
|
+
client_bus.label = 'client-' + client_counter++
|
|
1729
|
+
client_bus.master = bus
|
|
1730
|
+
|
|
1731
|
+
// Initialize the client-specific handlers
|
|
1732
|
+
bus.custom_clients(client_bus, client_id)
|
|
1733
|
+
|
|
1734
|
+
// Remember it, and auto-delete it when it's no longer being used
|
|
1735
|
+
client_busses[client_id] = client_bus
|
|
1736
|
+
client_bus.auto_delete = () => delete client_busses[client_id]
|
|
1737
|
+
|
|
1738
|
+
return client_bus
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
|
|
1743
|
+
// ************************************************
|
|
1453
1744
|
// Network client
|
|
1454
1745
|
function get_domain (key) { // Returns e.g. "state://foo.com"
|
|
1455
1746
|
var m = key.match(/^i?statei?\:\/\/(([^:\/?#]*)(?:\:([0-9]+))?)/)
|
|
@@ -1465,9 +1756,10 @@
|
|
|
1465
1756
|
function ws_mount (prefix, url, client_creds) {
|
|
1466
1757
|
// Local: state://foo.com/* or /*
|
|
1467
1758
|
var preprefix = prefix.slice(0,-1)
|
|
1759
|
+
var bus = this
|
|
1468
1760
|
var is_absolute = /^i?statei?:\/\//
|
|
1761
|
+
var creds = client_creds || (bus.client_creds && bus.client_creds(url))
|
|
1469
1762
|
var has_prefix = new RegExp('^' + preprefix)
|
|
1470
|
-
var bus = this
|
|
1471
1763
|
var sock
|
|
1472
1764
|
var attempts = 0
|
|
1473
1765
|
var outbox = []
|
|
@@ -1547,7 +1839,6 @@
|
|
|
1547
1839
|
set(peers)
|
|
1548
1840
|
|
|
1549
1841
|
// Login
|
|
1550
|
-
var creds = client_creds || (bus.client_creds && bus.client_creds(url))
|
|
1551
1842
|
if (creds) {
|
|
1552
1843
|
var i = []
|
|
1553
1844
|
function intro (o) {i.push(JSON.stringify({set: o}))}
|
|
@@ -1753,8 +2044,10 @@
|
|
|
1753
2044
|
}
|
|
1754
2045
|
|
|
1755
2046
|
// Recurse through each property on objects
|
|
1756
|
-
else if (typeof json === 'object' && json !== null) {
|
|
2047
|
+
else if (typeof json === 'object' && json !== null && !(symbols.link in json)) {
|
|
1757
2048
|
var new_obj = {}
|
|
2049
|
+
|
|
2050
|
+
// Regular objects get their fields translated
|
|
1758
2051
|
for (var k in json)
|
|
1759
2052
|
if (typeof k === 'string')
|
|
1760
2053
|
new_obj[translate(k, json)] = translate_fields(json[k], translate)
|
|
@@ -1785,6 +2078,21 @@
|
|
|
1785
2078
|
escape_to_nelson = (obj) => translate_fields(obj, escape_field_to_nelson)
|
|
1786
2079
|
unescape_from_nelson = (obj) => translate_fields(obj, unescape_field_from_nelson)
|
|
1787
2080
|
|
|
2081
|
+
var escape_field_json_to_bus = (field) =>
|
|
2082
|
+
escape_field_to_bus(escape_field_to_nelson(field))
|
|
2083
|
+
var unescape_field_bus_to_json = (field) =>
|
|
2084
|
+
unescape_field_from_nelson(unescape_field_from_bus(field))
|
|
2085
|
+
|
|
2086
|
+
var escape_json_to_bus = (obj) => {
|
|
2087
|
+
obj = translate_fields(obj, escape_field_json_to_bus)
|
|
2088
|
+
return deep_map(obj, o => (typeof o === 'object' && symbols.link in o
|
|
2089
|
+
? {link: o[symbols.link]}
|
|
2090
|
+
: o))
|
|
2091
|
+
}
|
|
2092
|
+
var unescape_bus_to_json = (obj) =>
|
|
2093
|
+
translate_fields(obj, field => unescape_field_bus_to_jsob(field))
|
|
2094
|
+
|
|
2095
|
+
|
|
1788
2096
|
var field_replace = (field, pattern, replacement) => (typeof field === 'string'
|
|
1789
2097
|
? field.replace(pattern, replacement)
|
|
1790
2098
|
: field)
|
|
@@ -1893,6 +2201,7 @@
|
|
|
1893
2201
|
this.has = function (k, v) { return hash[k] && hash[k][v] }
|
|
1894
2202
|
this.has_any = function (k) { return counts[k] }
|
|
1895
2203
|
this.del = this.delete // for compatibility; remove this soon
|
|
2204
|
+
this.empty = function () { return !Object.values(counts).some(count => count !== 0) }
|
|
1896
2205
|
}
|
|
1897
2206
|
function Set () {
|
|
1898
2207
|
var hash = {}
|
|
@@ -2234,14 +2543,15 @@
|
|
|
2234
2543
|
|
|
2235
2544
|
// Make these private methods accessible
|
|
2236
2545
|
var api = ['cache backup_cache get set forget del fire dirty get_once',
|
|
2237
|
-
'
|
|
2546
|
+
'old_subspace new_subspace bindings run_handler bind unbind reactive uncallback',
|
|
2238
2547
|
'versions new_version',
|
|
2239
|
-
'aget',
|
|
2548
|
+
'aget client_bus_for',
|
|
2240
2549
|
'funk_key funk_name funks key_id key_name id',
|
|
2241
2550
|
'pending_gets gets_in gets_out loading_keys loading once',
|
|
2242
2551
|
'global_funk busses rerunnable_funks',
|
|
2243
|
-
'translate_keys translate_links apply_patch',
|
|
2552
|
+
'link raw translate_keys translate_links apply_patch',
|
|
2244
2553
|
'translate_fields escape_to_bus unescape_from_bus escape_to_nelson unescape_from_nelson',
|
|
2554
|
+
'escape_field_json_to_bus unescape_field_bus_to_json escape_json_to_bus unescape_bus_to_json',
|
|
2245
2555
|
'ws_mount net_automount message_method',
|
|
2246
2556
|
'parse Set One_To_Many clone extend deep_map deep_equals prune validate sorta_diff log deps symbols'
|
|
2247
2557
|
].join(' ').split(' ')
|