statebus 7.0.33 → 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 +146 -40
- package/package.json +2 -2
- package/server-library.js +222 -232
- package/statebus.js +344 -112
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,8 +294,17 @@
|
|
|
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 }
|
|
@@ -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,8 +703,8 @@
|
|
|
684
703
|
case 'key':
|
|
685
704
|
case 'k':
|
|
686
705
|
handler.args['key'] = i; break
|
|
687
|
-
case '
|
|
688
|
-
handler.args['
|
|
706
|
+
case 'path':
|
|
707
|
+
handler.args['path'] = i; break
|
|
689
708
|
case 'json':
|
|
690
709
|
case 'vars':
|
|
691
710
|
handler.args['vars'] = i; break
|
|
@@ -704,8 +723,8 @@
|
|
|
704
723
|
handler.args['obj'] = i; break
|
|
705
724
|
case 'old':
|
|
706
725
|
handler.args['old'] = i; break
|
|
707
|
-
case '
|
|
708
|
-
handler.args['
|
|
726
|
+
case 'func_args':
|
|
727
|
+
handler.args['func_args'] = i; break
|
|
709
728
|
}
|
|
710
729
|
}
|
|
711
730
|
|
|
@@ -720,20 +739,45 @@
|
|
|
720
739
|
|
|
721
740
|
function pattern_matcher (pattern) {
|
|
722
741
|
var param_names = []
|
|
723
|
-
var regex = new RegExp('^' + pattern.replace(/(?<=^|\/):[^/()]+|\*/g, match => {
|
|
724
|
-
// Replace * with .*
|
|
725
|
-
if (match === '*') return '.*'
|
|
726
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 => {
|
|
727
749
|
// Replace :foo with ([^/]+), and remember the "foo" name
|
|
728
750
|
param_names.push(match.slice(1))
|
|
729
|
-
return '([
|
|
730
|
-
})
|
|
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 + '$')
|
|
731
764
|
|
|
765
|
+
// console.log('Pattern became ', regex)
|
|
766
|
+
|
|
767
|
+
// Return a matcher function that runs it
|
|
732
768
|
return path => {
|
|
733
769
|
var match = path.match(regex)
|
|
734
770
|
if (!match) return null
|
|
735
771
|
var params = {}
|
|
736
|
-
|
|
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
|
+
|
|
737
781
|
return params
|
|
738
782
|
}
|
|
739
783
|
}
|
|
@@ -797,14 +841,16 @@
|
|
|
797
841
|
for (var i=0; i < pattern_handlers.length; i++) {
|
|
798
842
|
handler = pattern_handlers[i]
|
|
799
843
|
var matches = handler.matcher(key)
|
|
844
|
+
// console.log('The matching of', pattern_handlers[i], 'to', key, 'is', matches)
|
|
845
|
+
|
|
800
846
|
if (matches // If the pattern matches
|
|
801
847
|
&& method === handler.method // And it has the right method
|
|
802
848
|
&& !seen[funk_key(handler.funk)]) {
|
|
803
849
|
|
|
804
850
|
handler.funk.statebus_binding = {
|
|
805
|
-
key:
|
|
851
|
+
key: handler.pattern,
|
|
806
852
|
method: method,
|
|
807
|
-
|
|
853
|
+
args: matches
|
|
808
854
|
}
|
|
809
855
|
result.push({method, key:handler.pattern, func:handler.funk})
|
|
810
856
|
seen[funk_key(handler.funk)] = true
|
|
@@ -815,6 +861,11 @@
|
|
|
815
861
|
}
|
|
816
862
|
|
|
817
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
|
+
|
|
818
869
|
options = options || {}
|
|
819
870
|
var t = options.t,
|
|
820
871
|
just_make_it = options.dont_run,
|
|
@@ -875,15 +926,30 @@
|
|
|
875
926
|
// Fresh get/set/forget/delete handlers will just be regular
|
|
876
927
|
// functions. We'll store their arg and transaction and let them
|
|
877
928
|
// re-run until they are done re-running.
|
|
878
|
-
function key_arg () { return ((typeof arg.key)
|
|
879
|
-
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
|
+
}
|
|
880
934
|
function val_arg () {
|
|
881
935
|
console.assert(method === 'setter' || method === 'on_set' || method === 'on_set_sync',
|
|
882
936
|
'Bad method for val_arg()')
|
|
883
|
-
// Is there a time I am supposed to return bus.cache[arg]? It used to say:
|
|
884
|
-
// arg.key ? arg : bus.cache[arg]
|
|
885
937
|
|
|
886
|
-
|
|
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
|
|
887
953
|
}
|
|
888
954
|
function vars_arg () {
|
|
889
955
|
var r = rest_arg()
|
|
@@ -893,19 +959,19 @@
|
|
|
893
959
|
return 'Bad JSON "' + r + '" for key ' + key_arg()
|
|
894
960
|
}
|
|
895
961
|
}
|
|
896
|
-
// Parse out
|
|
897
|
-
// We're currently calling that (param1,param2:val2) part "
|
|
898
|
-
// that it exists in the
|
|
899
|
-
function
|
|
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 () {
|
|
900
966
|
// This code is copied from Raphael Walker's parser.coffee
|
|
901
967
|
function split_once (str, char) {
|
|
902
968
|
var i = str.indexOf(char)
|
|
903
969
|
return i === -1 ? [str, ""] : [str.slice(0, i), str.slice(i + 1)]
|
|
904
970
|
}
|
|
905
971
|
|
|
906
|
-
//
|
|
907
|
-
//
|
|
908
|
-
function
|
|
972
|
+
// Function Strings -- an alternative to query strings
|
|
973
|
+
//
|
|
974
|
+
function parse_function_string (str) {
|
|
909
975
|
// Coffeescript doesn't have object comprehensions :(
|
|
910
976
|
var ret = {}
|
|
911
977
|
|
|
@@ -916,7 +982,7 @@
|
|
|
916
982
|
// Split by commas. TODO: Allow spaces after commas?
|
|
917
983
|
.split(",")
|
|
918
984
|
// Delete empty parts (this ensures that empty strings
|
|
919
|
-
// will properly result in empty
|
|
985
|
+
// will properly result in empty func_string, and allows trailing
|
|
920
986
|
// commas)
|
|
921
987
|
.filter(part => part.length)
|
|
922
988
|
.forEach(part => {
|
|
@@ -925,13 +991,17 @@
|
|
|
925
991
|
var [k, v] = split_once(part, ":")
|
|
926
992
|
|
|
927
993
|
if (v.length === 0) ret[k] = true
|
|
928
|
-
// If the value is itself a
|
|
929
|
-
else if (v.startsWith("(")) ret[k] =
|
|
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)
|
|
930
996
|
else ret[k] = v
|
|
931
997
|
})
|
|
932
998
|
return ret
|
|
933
999
|
}
|
|
934
|
-
|
|
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)
|
|
935
1005
|
}
|
|
936
1006
|
var f = reactive(function () {
|
|
937
1007
|
// Initialize transaction
|
|
@@ -951,8 +1021,8 @@
|
|
|
951
1021
|
if (method !== 'forgetter')
|
|
952
1022
|
t.done = function (o) {
|
|
953
1023
|
var key = method === 'setter' ? arg.key : arg
|
|
954
|
-
if (func.
|
|
955
|
-
o = {key, val: o}
|
|
1024
|
+
if (func.call_with_proxies)
|
|
1025
|
+
o = {key, val: raw(o)}
|
|
956
1026
|
bus.log('We are DONE()ing', method, key, o||arg)
|
|
957
1027
|
|
|
958
1028
|
// We use a simple (and crappy?) heuristic to know if the
|
|
@@ -1007,8 +1077,10 @@
|
|
|
1007
1077
|
switch (k) {
|
|
1008
1078
|
case 'key':
|
|
1009
1079
|
args[func.args[k]] = key_arg(); break
|
|
1010
|
-
case '
|
|
1011
|
-
args[func.args[k]] = func.statebus_binding
|
|
1080
|
+
case 'path':
|
|
1081
|
+
args[func.args[k]] = (func.statebus_binding
|
|
1082
|
+
&& func.statebus_binding.args
|
|
1083
|
+
&& func.statebus_binding.args.path)
|
|
1012
1084
|
break
|
|
1013
1085
|
case 'rest':
|
|
1014
1086
|
args[func.args[k]] = rest_arg(); break
|
|
@@ -1022,9 +1094,11 @@
|
|
|
1022
1094
|
case 'old':
|
|
1023
1095
|
var key = key_arg()
|
|
1024
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
|
|
1025
1099
|
break
|
|
1026
|
-
case '
|
|
1027
|
-
args[func.args[k]] =
|
|
1100
|
+
case 'func_args':
|
|
1101
|
+
args[func.args[k]] = func_args(); break
|
|
1028
1102
|
}
|
|
1029
1103
|
}
|
|
1030
1104
|
|
|
@@ -1045,9 +1119,9 @@
|
|
|
1045
1119
|
if (result === 'abort') t.abort()
|
|
1046
1120
|
|
|
1047
1121
|
// For get
|
|
1048
|
-
if (func.
|
|
1122
|
+
if (func.call_with_proxies) {
|
|
1049
1123
|
if (method === 'getter' && result !== undefined && !f.loading()) {
|
|
1050
|
-
var obj = {key: arg, val: result}
|
|
1124
|
+
var obj = {key: arg, val: raw(result)}
|
|
1051
1125
|
var new_t = clone(t || {})
|
|
1052
1126
|
new_t.getter = true
|
|
1053
1127
|
set.fire(obj, new_t)
|
|
@@ -1378,9 +1452,8 @@
|
|
|
1378
1452
|
|
|
1379
1453
|
var symbols = {
|
|
1380
1454
|
is_proxy: Symbol('is_proxy'),
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
// get_base: Symbol('get_base')
|
|
1455
|
+
raw: Symbol('raw'),
|
|
1456
|
+
link: Symbol('link')
|
|
1384
1457
|
}
|
|
1385
1458
|
function make_proxy () {
|
|
1386
1459
|
// var custom_inspect_symbol = nodejs && require('util').inspect.custom,
|
|
@@ -1401,8 +1474,9 @@
|
|
|
1401
1474
|
|
|
1402
1475
|
// We recursively descend through {key: ...} links
|
|
1403
1476
|
if (typeof o === 'object' && 'link' in o) {
|
|
1404
|
-
var new_base = bus.get(o.link)
|
|
1405
|
-
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)
|
|
1406
1480
|
}
|
|
1407
1481
|
|
|
1408
1482
|
|
|
@@ -1422,18 +1496,20 @@
|
|
|
1422
1496
|
// return custom_inspect
|
|
1423
1497
|
if (k === symbols.is_proxy)
|
|
1424
1498
|
return true
|
|
1499
|
+
if (k === symbols.raw)
|
|
1500
|
+
return o
|
|
1425
1501
|
if (typeof k === 'symbol')
|
|
1426
1502
|
return undefined
|
|
1427
1503
|
|
|
1428
1504
|
// Compute the new path
|
|
1429
1505
|
var new_path = path + '[' + JSON.stringify(k) + ']'
|
|
1430
|
-
return item_proxy(base, new_path, o[
|
|
1506
|
+
return item_proxy(base, new_path, o[escape_field_json_to_bus(k)])
|
|
1431
1507
|
},
|
|
1432
1508
|
set: function (o, k, v) {
|
|
1433
|
-
var value =
|
|
1434
|
-
o[
|
|
1509
|
+
var value = escape_json_to_bus(v)
|
|
1510
|
+
o[escape_field_json_to_bus(k)] = value
|
|
1435
1511
|
var new_path = path + '[' + JSON.stringify(k) + ']'
|
|
1436
|
-
bus.set(
|
|
1512
|
+
bus.set.sync(
|
|
1437
1513
|
base,
|
|
1438
1514
|
// Forward the patches too
|
|
1439
1515
|
{patches: [{unit: 'json',
|
|
@@ -1445,17 +1521,17 @@
|
|
|
1445
1521
|
has: function (o, k) {
|
|
1446
1522
|
// if (custom_inspect && k === custom_inspect)
|
|
1447
1523
|
// return true
|
|
1448
|
-
return o.hasOwnProperty(
|
|
1524
|
+
return o.hasOwnProperty(escape_field_json_to_bus(k))
|
|
1449
1525
|
},
|
|
1450
1526
|
ownKeys: function () {
|
|
1451
|
-
return Object.keys(o).map(
|
|
1527
|
+
return Object.keys(o).map(unescape_field_bus_to_json)
|
|
1452
1528
|
},
|
|
1453
1529
|
getOwnPropertyDescriptor: function (target, key) {
|
|
1454
1530
|
return { enumerable: true, configurable: true, value: this.get(o, key) }
|
|
1455
1531
|
},
|
|
1456
1532
|
deleteProperty: function del (o, k) {
|
|
1457
1533
|
var new_path = path + '[' + JSON.stringify(k) + ']'
|
|
1458
|
-
delete o[
|
|
1534
|
+
delete o[escape_field_json_to_bus(k)]
|
|
1459
1535
|
bus.set(
|
|
1460
1536
|
base,
|
|
1461
1537
|
// Forward the patches too
|
|
@@ -1472,15 +1548,84 @@
|
|
|
1472
1548
|
// }
|
|
1473
1549
|
})}
|
|
1474
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
|
+
}
|
|
1475
1620
|
|
|
1476
|
-
// For function proxies:
|
|
1477
|
-
// var dummy = function () {}
|
|
1478
1621
|
|
|
1479
1622
|
// The top-level Proxy object holds HTTP resources
|
|
1480
|
-
|
|
1623
|
+
var top_level_proxy = new Proxy(cache, {
|
|
1481
1624
|
get: function get(o, k) {
|
|
1482
1625
|
if (k === symbols.is_proxy)
|
|
1483
1626
|
return true
|
|
1627
|
+
if (k === symbols.raw)
|
|
1628
|
+
return raw_proxy()
|
|
1484
1629
|
if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
|
|
1485
1630
|
return undefined
|
|
1486
1631
|
bogus_check(k)
|
|
@@ -1490,7 +1635,7 @@
|
|
|
1490
1635
|
set: function set(o, key, val) {
|
|
1491
1636
|
bus.set({
|
|
1492
1637
|
key: key,
|
|
1493
|
-
val:
|
|
1638
|
+
val: escape_json_to_bus(val)
|
|
1494
1639
|
})
|
|
1495
1640
|
return true
|
|
1496
1641
|
},
|
|
@@ -1503,15 +1648,35 @@
|
|
|
1503
1648
|
// return 'fluffy'
|
|
1504
1649
|
// }
|
|
1505
1650
|
})
|
|
1651
|
+
|
|
1652
|
+
return top_level_proxy
|
|
1506
1653
|
}
|
|
1507
1654
|
bus.state = make_proxy()
|
|
1508
1655
|
|
|
1509
|
-
//
|
|
1510
|
-
//
|
|
1511
|
-
//
|
|
1512
|
-
|
|
1513
|
-
|
|
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
|
+
}
|
|
1514
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
|
|
1677
|
+
|
|
1678
|
+
return proxy[symbols.raw]
|
|
1679
|
+
}
|
|
1515
1680
|
|
|
1516
1681
|
// So chrome can print out proxy objects decently
|
|
1517
1682
|
if (!nodejs)
|
|
@@ -1527,7 +1692,55 @@
|
|
|
1527
1692
|
hasBody: function (x) {return false}
|
|
1528
1693
|
}]
|
|
1529
1694
|
|
|
1530
|
-
|
|
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
|
+
// ************************************************
|
|
1531
1744
|
// Network client
|
|
1532
1745
|
function get_domain (key) { // Returns e.g. "state://foo.com"
|
|
1533
1746
|
var m = key.match(/^i?statei?\:\/\/(([^:\/?#]*)(?:\:([0-9]+))?)/)
|
|
@@ -1543,9 +1756,10 @@
|
|
|
1543
1756
|
function ws_mount (prefix, url, client_creds) {
|
|
1544
1757
|
// Local: state://foo.com/* or /*
|
|
1545
1758
|
var preprefix = prefix.slice(0,-1)
|
|
1759
|
+
var bus = this
|
|
1546
1760
|
var is_absolute = /^i?statei?:\/\//
|
|
1761
|
+
var creds = client_creds || (bus.client_creds && bus.client_creds(url))
|
|
1547
1762
|
var has_prefix = new RegExp('^' + preprefix)
|
|
1548
|
-
var bus = this
|
|
1549
1763
|
var sock
|
|
1550
1764
|
var attempts = 0
|
|
1551
1765
|
var outbox = []
|
|
@@ -1625,7 +1839,6 @@
|
|
|
1625
1839
|
set(peers)
|
|
1626
1840
|
|
|
1627
1841
|
// Login
|
|
1628
|
-
var creds = client_creds || (bus.client_creds && bus.client_creds(url))
|
|
1629
1842
|
if (creds) {
|
|
1630
1843
|
var i = []
|
|
1631
1844
|
function intro (o) {i.push(JSON.stringify({set: o}))}
|
|
@@ -1831,8 +2044,10 @@
|
|
|
1831
2044
|
}
|
|
1832
2045
|
|
|
1833
2046
|
// Recurse through each property on objects
|
|
1834
|
-
else if (typeof json === 'object' && json !== null) {
|
|
2047
|
+
else if (typeof json === 'object' && json !== null && !(symbols.link in json)) {
|
|
1835
2048
|
var new_obj = {}
|
|
2049
|
+
|
|
2050
|
+
// Regular objects get their fields translated
|
|
1836
2051
|
for (var k in json)
|
|
1837
2052
|
if (typeof k === 'string')
|
|
1838
2053
|
new_obj[translate(k, json)] = translate_fields(json[k], translate)
|
|
@@ -1863,6 +2078,21 @@
|
|
|
1863
2078
|
escape_to_nelson = (obj) => translate_fields(obj, escape_field_to_nelson)
|
|
1864
2079
|
unescape_from_nelson = (obj) => translate_fields(obj, unescape_field_from_nelson)
|
|
1865
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
|
+
|
|
1866
2096
|
var field_replace = (field, pattern, replacement) => (typeof field === 'string'
|
|
1867
2097
|
? field.replace(pattern, replacement)
|
|
1868
2098
|
: field)
|
|
@@ -1971,6 +2201,7 @@
|
|
|
1971
2201
|
this.has = function (k, v) { return hash[k] && hash[k][v] }
|
|
1972
2202
|
this.has_any = function (k) { return counts[k] }
|
|
1973
2203
|
this.del = this.delete // for compatibility; remove this soon
|
|
2204
|
+
this.empty = function () { return !Object.values(counts).some(count => count !== 0) }
|
|
1974
2205
|
}
|
|
1975
2206
|
function Set () {
|
|
1976
2207
|
var hash = {}
|
|
@@ -2312,14 +2543,15 @@
|
|
|
2312
2543
|
|
|
2313
2544
|
// Make these private methods accessible
|
|
2314
2545
|
var api = ['cache backup_cache get set forget del fire dirty get_once',
|
|
2315
|
-
'
|
|
2546
|
+
'old_subspace new_subspace bindings run_handler bind unbind reactive uncallback',
|
|
2316
2547
|
'versions new_version',
|
|
2317
|
-
'aget',
|
|
2548
|
+
'aget client_bus_for',
|
|
2318
2549
|
'funk_key funk_name funks key_id key_name id',
|
|
2319
2550
|
'pending_gets gets_in gets_out loading_keys loading once',
|
|
2320
2551
|
'global_funk busses rerunnable_funks',
|
|
2321
|
-
'translate_keys translate_links apply_patch',
|
|
2552
|
+
'link raw translate_keys translate_links apply_patch',
|
|
2322
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',
|
|
2323
2555
|
'ws_mount net_automount message_method',
|
|
2324
2556
|
'parse Set One_To_Many clone extend deep_map deep_equals prune validate sorta_diff log deps symbols'
|
|
2325
2557
|
].join(' ').split(' ')
|