statebus 6.1.20 → 7.0.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.js → clients.js} +257 -141
- package/extras/tests.js +292 -412
- package/package.json +9 -3
- package/{server.js → servers.js} +288 -333
- package/statebus.js +299 -620
- package/extras/coffee.js +0 -12
- package/extras/dash.coffee +0 -302
- package/extras/diffsync.js +0 -1462
- package/extras/getCaretCoordinates.js +0 -141
- package/extras/react.js +0 -18147
- package/extras/sockjs.js +0 -27
package/statebus.js
CHANGED
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
if (typeof module != 'undefined') module.exports = definition()
|
|
4
4
|
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition)
|
|
5
5
|
else this[name] = definition()
|
|
6
|
-
}('statebus', function() {statelog_indent = 0; var busses = {}, executing_funk, global_funk, funks = {}, clean_timer, symbols, nodejs = typeof window === 'undefined'; function make_bus (options) {
|
|
6
|
+
}('statebus', function() {statelog_indent = 0; var busses = {}, bus_count = 0, executing_funk, global_funk, funks = {}, clean_timer, symbols, nodejs = typeof window === 'undefined'; function make_bus (options) {
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
// ****************
|
|
10
|
-
//
|
|
10
|
+
// Get, Set, Forget, Delete
|
|
11
11
|
|
|
12
|
-
function
|
|
12
|
+
function get (key, callback) {
|
|
13
|
+
if (typeof key !== 'string'
|
|
14
|
+
&& !(typeof key === 'object' && typeof key.key === 'string'))
|
|
15
|
+
throw ('Error: get(key) called with key = '
|
|
16
|
+
+ JSON.stringify(key))
|
|
13
17
|
key = key.key || key // You can pass in an object instead of key
|
|
14
18
|
// We should probably disable this in future
|
|
15
|
-
if (typeof key !== 'string')
|
|
16
|
-
throw ('Error: fetch(key) called with a non-string key: '+key)
|
|
17
19
|
bogus_check(key)
|
|
18
20
|
|
|
19
21
|
var called_from_reactive_funk = !callback
|
|
@@ -22,7 +24,7 @@
|
|
|
22
24
|
// Initialize callback
|
|
23
25
|
if (callback) {
|
|
24
26
|
(callback.defined = callback.defined || []
|
|
25
|
-
).push({as:'
|
|
27
|
+
).push({as:'get callback', key:key});
|
|
26
28
|
callback.has_seen = callback.has_seen || function (bus, key, version) {
|
|
27
29
|
callback.seen_keys = callback.seen_keys || {}
|
|
28
30
|
var bus_key = JSON.stringify([bus.id, key])
|
|
@@ -35,27 +37,27 @@
|
|
|
35
37
|
|
|
36
38
|
// ** Subscribe the calling funk **
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
gets_in.add(key, funk_key(funk))
|
|
39
41
|
if (to_be_forgotten[key]) {
|
|
40
42
|
clearTimeout(to_be_forgotten[key])
|
|
41
43
|
delete to_be_forgotten[key]
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
bind(key, '
|
|
46
|
+
bind(key, 'on_set', funk)
|
|
45
47
|
|
|
46
|
-
// ** Call
|
|
48
|
+
// ** Call getters upstream **
|
|
47
49
|
|
|
48
|
-
// TODO: checking
|
|
50
|
+
// TODO: checking gets_out[] doesn't count keys that we got which
|
|
49
51
|
// arrived nested within a bigger object, because we never explicity
|
|
50
|
-
//
|
|
52
|
+
// got those keys. But we don't need to get them now cause we
|
|
51
53
|
// already have them.
|
|
52
|
-
var
|
|
53
|
-
if (!
|
|
54
|
-
|
|
54
|
+
var to_getters = 0
|
|
55
|
+
if (!gets_out[key])
|
|
56
|
+
to_getters = bus.route(key, 'to_get', key)
|
|
55
57
|
|
|
56
58
|
// Now there might be a new value pubbed onto this bus.
|
|
57
|
-
// Or there might be a pending
|
|
58
|
-
// ... or there weren't any
|
|
59
|
+
// Or there might be a pending get.
|
|
60
|
+
// ... or there weren't any getters upstream.
|
|
59
61
|
|
|
60
62
|
|
|
61
63
|
// ** Return a value **
|
|
@@ -68,29 +70,29 @@
|
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
// Otherwise, we want to make sure that a pub gets called on the
|
|
71
|
-
// handler. If there's a pending
|
|
72
|
-
// If there was a
|
|
73
|
+
// handler. If there's a pending get, then it'll get called later.
|
|
74
|
+
// If there was a to_get, then it already got called. Otherwise,
|
|
73
75
|
// let's call it now.
|
|
74
|
-
else if (!
|
|
76
|
+
else if (!pending_gets[key] && to_getters === 0) {
|
|
75
77
|
// TODO: my intuition suggests that we might prefer to delay this
|
|
76
|
-
// .
|
|
77
|
-
// with other calls to .
|
|
78
|
+
// .on_set getting called in a setTimeout(f,0), to be consistent
|
|
79
|
+
// with other calls to .on_set.
|
|
78
80
|
backup_cache[key] = backup_cache[key] || {key: key}
|
|
79
|
-
run_handler(funk, '
|
|
81
|
+
run_handler(funk, 'on_set', cache[key] = cache[key] || {key: key})
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
|
-
function
|
|
84
|
+
function get_once (key, cb) {
|
|
83
85
|
function cb2 (o) { cb(o); forget(key, cb2) }
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
+
// get(key) // This prevents key from being forgotten
|
|
87
|
+
get(key, cb2)
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
var
|
|
89
|
-
var
|
|
90
|
-
var
|
|
89
|
+
get.once = get_once
|
|
90
|
+
var pending_gets = {}
|
|
91
|
+
var gets_out = {} // Maps `key' to `func' iff we've got `key'
|
|
92
|
+
var gets_in = new One_To_Many() // Maps `key' to `pub_funcs' subscribed to our key
|
|
91
93
|
|
|
92
94
|
var currently_saving
|
|
93
|
-
function
|
|
95
|
+
function set (obj, t) {
|
|
94
96
|
// First let's handle diffs
|
|
95
97
|
if (typeof obj === 'string' && t && t.patch) {
|
|
96
98
|
if (typeof t.patch == 'string') t.patch = [t.patch]
|
|
@@ -99,8 +101,8 @@
|
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
if (!('key' in obj) || typeof obj.key !== 'string') {
|
|
102
|
-
console.error('Error:
|
|
103
|
-
console.trace('Bad
|
|
104
|
+
console.error('Error: set(obj) called on object without a key: ', obj)
|
|
105
|
+
console.trace('Bad set(obj)')
|
|
104
106
|
}
|
|
105
107
|
bogus_check(obj.key)
|
|
106
108
|
|
|
@@ -114,7 +116,7 @@
|
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
if (honking_at(obj.key))
|
|
117
|
-
var message =
|
|
119
|
+
var message = set_msg(obj, t, 'set')
|
|
118
120
|
|
|
119
121
|
// Ignore if nothing happened
|
|
120
122
|
if (obj.key && !changed(obj)) {
|
|
@@ -128,11 +130,11 @@
|
|
|
128
130
|
var was_saving = currently_saving
|
|
129
131
|
currently_saving = obj.key
|
|
130
132
|
|
|
131
|
-
// Call the
|
|
132
|
-
var num_handlers = bus.route(obj.key, '
|
|
133
|
+
// Call the to_set() handlers!
|
|
134
|
+
var num_handlers = bus.route(obj.key, 'to_set', obj, t)
|
|
133
135
|
if (num_handlers === 0) {
|
|
134
136
|
// And fire if there weren't any!
|
|
135
|
-
|
|
137
|
+
set.fire(obj, t)
|
|
136
138
|
bus.route(obj.key, 'on_set_sync', obj, t)
|
|
137
139
|
}
|
|
138
140
|
}
|
|
@@ -141,13 +143,13 @@
|
|
|
141
143
|
currently_saving = was_saving
|
|
142
144
|
}
|
|
143
145
|
// TODO: Here's an alternative. Instead of counting the handlers and
|
|
144
|
-
// seeing if there are zero, I could just make a
|
|
146
|
+
// seeing if there are zero, I could just make a to_set handler that
|
|
145
147
|
// is shadowed by other handlers if I can get later handlers to shadow
|
|
146
148
|
// earlier ones.
|
|
147
149
|
}
|
|
148
150
|
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
+
// set.sync() will set with the version of the current executing reaction
|
|
152
|
+
set.sync = function set_sync (obj, t) {
|
|
151
153
|
t = bus.clone(t || {})
|
|
152
154
|
// t.version: executing_funk?.transaction?.version
|
|
153
155
|
// || executing_funk?.latest_reaction_at
|
|
@@ -156,13 +158,13 @@
|
|
|
156
158
|
&& executing_funk.transaction.version)
|
|
157
159
|
|| (executing_funk
|
|
158
160
|
&& executing_funk.latest_reaction_at))
|
|
159
|
-
|
|
161
|
+
set(obj, t)
|
|
160
162
|
}
|
|
161
163
|
|
|
162
|
-
// We might eventually want a
|
|
164
|
+
// We might eventually want a set.fire.sync() too, which defaults the
|
|
163
165
|
// version to `executing_funk?.transaction?.version`
|
|
164
166
|
|
|
165
|
-
|
|
167
|
+
set.fire = fire
|
|
166
168
|
function fire (obj, t) {
|
|
167
169
|
t = t || {}
|
|
168
170
|
|
|
@@ -176,7 +178,7 @@
|
|
|
176
178
|
// Warning: Changes to *nested* objects will *not* be printed out!
|
|
177
179
|
// In the future, we'll remove the recursion from fire() so that
|
|
178
180
|
// nested objects aren't even changed.
|
|
179
|
-
var message =
|
|
181
|
+
var message = set_msg(obj, t, 'set.fire')
|
|
180
182
|
var color, icon
|
|
181
183
|
if (currently_saving === obj.key &&
|
|
182
184
|
!(obj.key && !changed(obj))) {
|
|
@@ -189,18 +191,18 @@
|
|
|
189
191
|
if (obj.key && !changed(obj)) {
|
|
190
192
|
color = grey
|
|
191
193
|
icon = 'x'
|
|
192
|
-
if (t.
|
|
193
|
-
message = (t.m) || '
|
|
194
|
+
if (t.to_get)
|
|
195
|
+
message = (t.m) || 'Got ' + bus + "('"+obj.key+"')"
|
|
194
196
|
if (t.version) message += ' [' + t.version + ']'
|
|
195
197
|
statelog(obj.key, color, icon, message)
|
|
196
198
|
return
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
color = red, icon = '•'
|
|
200
|
-
if (t.
|
|
202
|
+
if (t.to_get || pending_gets[obj.key]) {
|
|
201
203
|
color = green
|
|
202
204
|
icon = '^'
|
|
203
|
-
message = add_diff_msg((t.m)||'
|
|
205
|
+
message = add_diff_msg((t.m)||'Got '+bus+"('"+obj.key+"')",
|
|
204
206
|
obj)
|
|
205
207
|
if (t.version) message += ' [' + t.version + ']'
|
|
206
208
|
}
|
|
@@ -213,7 +215,7 @@
|
|
|
213
215
|
// Recursively add all of obj, and its sub-objects, into the cache
|
|
214
216
|
var modified_keys = update_cache(obj, cache)
|
|
215
217
|
|
|
216
|
-
delete
|
|
218
|
+
delete pending_gets[obj.key]
|
|
217
219
|
|
|
218
220
|
if ((executing_funk !== global_funk) && executing_funk.loading()) {
|
|
219
221
|
abort_changes(modified_keys)
|
|
@@ -234,7 +236,7 @@
|
|
|
234
236
|
}
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
|
|
239
|
+
set.abort = function (obj, t) {
|
|
238
240
|
if (!obj) console.error('No obj', obj)
|
|
239
241
|
abort_changes([obj.key])
|
|
240
242
|
statelog(obj.key, yellow, '<', 'Aborting ' + obj.key)
|
|
@@ -247,19 +249,19 @@
|
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
// Now create the statebus object
|
|
250
|
-
function bus (
|
|
252
|
+
function bus (arg1, arg2) {
|
|
251
253
|
// Called with a function to react to
|
|
252
|
-
if (typeof
|
|
253
|
-
var f = reactive(
|
|
254
|
+
if (typeof arg1 === 'function') {
|
|
255
|
+
var f = reactive(arg1)
|
|
254
256
|
f()
|
|
255
257
|
return f
|
|
256
258
|
}
|
|
257
259
|
|
|
258
260
|
// Called with a key to produce a subspace
|
|
259
|
-
else return subspace(
|
|
261
|
+
else return subspace(arg1, arg2)
|
|
260
262
|
}
|
|
261
|
-
var id = 'bus
|
|
262
|
-
bus.toString = function () { return bus.label || id }
|
|
263
|
+
var id = 'bus-' + Math.random().toString(36).substring(7)
|
|
264
|
+
bus.toString = function () { return bus.label || 'bus'+this_bus_num || id }
|
|
263
265
|
bus.delete_bus = function () {
|
|
264
266
|
// // Forget all wildcard handlers
|
|
265
267
|
// for (var i=0; i<wildcard_handlers.length; i++) {
|
|
@@ -295,7 +297,7 @@
|
|
|
295
297
|
// time they are pubbed into the cache. After that, we can
|
|
296
298
|
// trust that they aren't referenced elsewhere. (We make it
|
|
297
299
|
// the programmer's responsibility to clone data if necessary
|
|
298
|
-
// on
|
|
300
|
+
// on get, but not when on pub.)
|
|
299
301
|
//
|
|
300
302
|
// We'll optimize this once we have history. We can look at
|
|
301
303
|
// the old version to see if an object/array existed already
|
|
@@ -303,7 +305,7 @@
|
|
|
303
305
|
//
|
|
304
306
|
// 2. Don't go infinitely deep.
|
|
305
307
|
//
|
|
306
|
-
// Eventually, each
|
|
308
|
+
// Eventually, each set/pub will be limited to the scope
|
|
307
309
|
// underneath nested keyed objects. Right now I'm just
|
|
308
310
|
// recursing infinitely on the whole data structure with each
|
|
309
311
|
// pub.
|
|
@@ -363,7 +365,7 @@
|
|
|
363
365
|
}
|
|
364
366
|
|
|
365
367
|
function changed (object) {
|
|
366
|
-
return
|
|
368
|
+
return pending_gets[object.key]
|
|
367
369
|
|| ! cache.hasOwnProperty(object.key)
|
|
368
370
|
|| !backup_cache.hasOwnProperty(object.key)
|
|
369
371
|
|| !(deep_equals(object, backup_cache[object.key]))
|
|
@@ -374,7 +376,7 @@
|
|
|
374
376
|
}
|
|
375
377
|
|
|
376
378
|
|
|
377
|
-
function forget (key,
|
|
379
|
+
function forget (key, set_handler, t) {
|
|
378
380
|
if (arguments.length === 0) {
|
|
379
381
|
// Then we're forgetting the executing funk
|
|
380
382
|
console.assert(executing_funk !== global_funk,
|
|
@@ -384,26 +386,26 @@
|
|
|
384
386
|
}
|
|
385
387
|
bogus_check(key)
|
|
386
388
|
|
|
387
|
-
//log('forget:', key, funk_name(
|
|
388
|
-
|
|
389
|
-
var fkey = funk_key(
|
|
390
|
-
//console.log('
|
|
391
|
-
if (!
|
|
389
|
+
//log('forget:', key, funk_name(set_handler), funk_name(executing_funk))
|
|
390
|
+
set_handler = set_handler || executing_funk
|
|
391
|
+
var fkey = funk_key(set_handler)
|
|
392
|
+
//console.log('Gets in is', gets_in.hash)
|
|
393
|
+
if (!gets_in.has(key, fkey)) {
|
|
392
394
|
console.error("***\n****\nTrying to forget lost key", key,
|
|
393
|
-
'from', funk_name(
|
|
394
|
-
"that hasn't
|
|
395
|
+
'from', funk_name(set_handler), fkey,
|
|
396
|
+
"that hasn't got that key.")
|
|
395
397
|
console.trace()
|
|
396
398
|
return
|
|
397
399
|
// throw Error('asdfalsdkfajsdf')
|
|
398
400
|
}
|
|
399
401
|
|
|
400
|
-
|
|
401
|
-
unbind(key, '
|
|
402
|
+
gets_in.delete(key, fkey)
|
|
403
|
+
unbind(key, 'on_set', set_handler)
|
|
402
404
|
|
|
403
405
|
// If this is the last handler listening to this key, then we can
|
|
404
406
|
// delete the cache entry, send a forget upstream, and de-activate the
|
|
405
|
-
// .
|
|
406
|
-
if (!
|
|
407
|
+
// .on_get handler.
|
|
408
|
+
if (!gets_in.has_any(key)) {
|
|
407
409
|
clearTimeout(to_be_forgotten[key])
|
|
408
410
|
to_be_forgotten[key] = setTimeout(function () {
|
|
409
411
|
// Send a forget upstream
|
|
@@ -411,7 +413,7 @@
|
|
|
411
413
|
|
|
412
414
|
// Delete the cache entry...?
|
|
413
415
|
// delete cache[key]
|
|
414
|
-
delete
|
|
416
|
+
delete gets_out[key]
|
|
415
417
|
delete to_be_forgotten[key]
|
|
416
418
|
}, 200)
|
|
417
419
|
}
|
|
@@ -443,7 +445,7 @@
|
|
|
443
445
|
// etc.
|
|
444
446
|
// - NOTE: I did a crappy implementation of abort just now above!
|
|
445
447
|
// But it doesn't work if called after the to_delete handler returns.
|
|
446
|
-
// - Generalize the code across
|
|
448
|
+
// - Generalize the code across set and del with a "mutate"
|
|
447
449
|
// operation
|
|
448
450
|
//
|
|
449
451
|
// - Right now we fire the to_delete handlers right here.
|
|
@@ -458,18 +460,18 @@
|
|
|
458
460
|
}
|
|
459
461
|
|
|
460
462
|
var changed_keys = new Set()
|
|
461
|
-
var
|
|
463
|
+
var dirty_getters = {} // Maps funk_key => version dirtied at
|
|
462
464
|
function dirty (key, t) {
|
|
463
465
|
statelog(key, brown, '*', bus + ".dirty('"+key+"')")
|
|
464
466
|
bogus_check(key)
|
|
465
467
|
|
|
466
468
|
var version = (t && t.version) || 'dirty-' + new_version()
|
|
467
469
|
|
|
468
|
-
// Find any .
|
|
470
|
+
// Find any .to_get, and mark as dirty so that it re-runs
|
|
469
471
|
var found = false
|
|
470
|
-
if (
|
|
471
|
-
for (var i=0; i<
|
|
472
|
-
|
|
472
|
+
if (gets_out.hasOwnProperty(key))
|
|
473
|
+
for (var i=0; i<gets_out[key].length; i++) {
|
|
474
|
+
dirty_getters[funk_key(gets_out[key][i])] = version
|
|
473
475
|
found = true
|
|
474
476
|
}
|
|
475
477
|
clean_timer = clean_timer || setTimeout(clean)
|
|
@@ -486,7 +488,7 @@
|
|
|
486
488
|
}
|
|
487
489
|
|
|
488
490
|
function clean () {
|
|
489
|
-
// 1. Collect all functions for all keys and dirtied
|
|
491
|
+
// 1. Collect all functions for all keys and dirtied getters
|
|
490
492
|
var dirty_funks = {}
|
|
491
493
|
for (var b in busses) {
|
|
492
494
|
var fs = busses[b].rerunnable_funks()
|
|
@@ -495,7 +497,7 @@
|
|
|
495
497
|
}
|
|
496
498
|
clean_timer = null
|
|
497
499
|
|
|
498
|
-
// 2. Run any priority function first (e.g. file_store's
|
|
500
|
+
// 2. Run any priority function first (e.g. file_store's on_set)
|
|
499
501
|
log(bus.label, 'Cleaning up', Object.keys(dirty_funks).length, 'funks')
|
|
500
502
|
for (var k in dirty_funks) {
|
|
501
503
|
var funk = funks[k],
|
|
@@ -535,19 +537,19 @@
|
|
|
535
537
|
var result = []
|
|
536
538
|
var keys = changed_keys.values()
|
|
537
539
|
|
|
538
|
-
//log(bus+' Cleaning up!', keys, 'keys, and',
|
|
540
|
+
//log(bus+' Cleaning up!', keys, 'keys, and', getters.length, 'getters')
|
|
539
541
|
for (var i=0; i<keys.length; i++) { // Collect all keys
|
|
540
542
|
// if (to_be_forgotten[keys[i]])
|
|
541
543
|
// // Ignore changes to keys that have been forgotten, but not
|
|
542
544
|
// // processed yet
|
|
543
545
|
// continue
|
|
544
|
-
var fs = bindings(keys[i], '
|
|
546
|
+
var fs = bindings(keys[i], 'on_set')
|
|
545
547
|
for (var j=0; j<fs.length; j++) {
|
|
546
548
|
var f = fs[j].func
|
|
547
549
|
if (f.react) {
|
|
548
550
|
// Skip if it's already up to date
|
|
549
|
-
var v = f.
|
|
550
|
-
//log('re-run:', keys[i], f.statebus_id, f.
|
|
551
|
+
var v = f.getted_keys[JSON.stringify([this.id, keys[i]])]
|
|
552
|
+
//log('re-run:', keys[i], f.statebus_id, f.getted_keys)
|
|
551
553
|
if (v && v.indexOf(versions[keys[i]]) !== -1) {
|
|
552
554
|
log('skipping', funk_name(f), 'already at version', versions[keys[i]], 'proof:', v)
|
|
553
555
|
continue
|
|
@@ -564,19 +566,19 @@
|
|
|
564
566
|
continue
|
|
565
567
|
}
|
|
566
568
|
autodetect_args(f)
|
|
567
|
-
f = run_handler(f, '
|
|
568
|
-
|
|
569
|
+
f = run_handler(f, 'on_set', cache[keys[i]], {dont_run: true,
|
|
570
|
+
binding: keys[i]})
|
|
569
571
|
}
|
|
570
572
|
result.push({funk_key: funk_key(f),
|
|
571
573
|
at_version: versions[keys[i]]})
|
|
572
574
|
}
|
|
573
575
|
}
|
|
574
|
-
for (var k in
|
|
576
|
+
for (var k in dirty_getters) // Collect all getters
|
|
575
577
|
result.push({funk_key: k,
|
|
576
|
-
at_version:
|
|
578
|
+
at_version: dirty_getters[k]})
|
|
577
579
|
|
|
578
580
|
changed_keys.clear()
|
|
579
|
-
|
|
581
|
+
dirty_getters = {}
|
|
580
582
|
|
|
581
583
|
//log('found', result.length, 'funks to re run')
|
|
582
584
|
|
|
@@ -585,28 +587,43 @@
|
|
|
585
587
|
|
|
586
588
|
// ****************
|
|
587
589
|
// Connections
|
|
588
|
-
function subspace (key) {
|
|
589
|
-
var
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
590
|
+
function subspace (key, params) {
|
|
591
|
+
var methods = {to_get:null, to_set:null, on_set:null, on_set_sync:null,
|
|
592
|
+
on_delete:null, to_delete:null, to_forget:null}
|
|
593
|
+
|
|
594
|
+
if (params) {
|
|
595
|
+
for (var method in params) {
|
|
596
|
+
console.assert(method in methods)
|
|
597
|
+
var func = params[method]
|
|
598
|
+
autodetect_args(func)
|
|
599
|
+
func.defined = func.defined || []
|
|
600
|
+
func.defined.push({bus, method, key, as: 'handler'})
|
|
601
|
+
func.use_linked_json = true
|
|
602
|
+
bind(key, method, func, 'allow_wildcards')
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
var result = {}
|
|
607
|
+
for (var method in methods)
|
|
608
|
+
(function (method) {
|
|
609
|
+
Object.defineProperty(result, method, {
|
|
610
|
+
set: function (func) {
|
|
611
|
+
autodetect_args(func)
|
|
612
|
+
func.defined = func.defined || []
|
|
613
|
+
func.defined.push(
|
|
614
|
+
{as:'handler', bus:bus, method:method, key:key})
|
|
615
|
+
bind(key, method, func, 'allow_wildcards')
|
|
616
|
+
},
|
|
617
|
+
get: function () {
|
|
618
|
+
var result = bindings(key, method)
|
|
619
|
+
for (var i=0; i<result.length; i++) result[i] = result[i].func
|
|
620
|
+
result.delete = function (func) { unbind (key, method, func, 'allow_wildcards') }
|
|
621
|
+
return result
|
|
622
|
+
}
|
|
623
|
+
})
|
|
624
|
+
})(method)
|
|
625
|
+
return result
|
|
626
|
+
}
|
|
610
627
|
}
|
|
611
628
|
|
|
612
629
|
function autodetect_args (handler) {
|
|
@@ -630,6 +647,7 @@
|
|
|
630
647
|
case 'star':
|
|
631
648
|
case 'rest':
|
|
632
649
|
handler.args['rest'] = i; break
|
|
650
|
+
case 'cb':
|
|
633
651
|
case 't':
|
|
634
652
|
case 'transaction':
|
|
635
653
|
handler.args['t'] = i; break
|
|
@@ -644,7 +662,7 @@
|
|
|
644
662
|
}
|
|
645
663
|
}
|
|
646
664
|
|
|
647
|
-
// The funks attached to each key, maps e.g. '
|
|
665
|
+
// The funks attached to each key, maps e.g. 'get /point/3' to '/30'
|
|
648
666
|
var handlers = new One_To_Many()
|
|
649
667
|
var wildcard_handlers = [] // An array of {prefix, method, funk}
|
|
650
668
|
|
|
@@ -659,7 +677,7 @@
|
|
|
659
677
|
else
|
|
660
678
|
handlers.add(method + ' ' + key, funk_key(func))
|
|
661
679
|
|
|
662
|
-
// Now check if the method is a
|
|
680
|
+
// Now check if the method is a get and there's a getted
|
|
663
681
|
// key in this space, and if so call the handler.
|
|
664
682
|
}
|
|
665
683
|
function unbind (key, method, funk, allow_wildcards) {
|
|
@@ -726,9 +744,9 @@
|
|
|
726
744
|
just_make_it = options.dont_run,
|
|
727
745
|
binding = options.binding
|
|
728
746
|
|
|
729
|
-
// When we first run a handler (e.g. a
|
|
730
|
-
// reactive() funk that calls it with its arg. Then if it
|
|
731
|
-
//
|
|
747
|
+
// When we first run a handler (e.g. a get or set), we wrap it in a
|
|
748
|
+
// reactive() funk that calls it with its arg. Then if it gets or
|
|
749
|
+
// sets, it'll register a .on_set handler with this funk.
|
|
732
750
|
|
|
733
751
|
// Is it reactive already? Let's distinguish it.
|
|
734
752
|
var funk = funck.react && funck, // Funky! So reactive!
|
|
@@ -738,7 +756,7 @@
|
|
|
738
756
|
|
|
739
757
|
if (false && !funck.global_funk) {
|
|
740
758
|
// \u26A1
|
|
741
|
-
var event = {'
|
|
759
|
+
var event = {'to_set':'set','on_set':'set.fire','to_get':'get',
|
|
742
760
|
'to_delete':'delete','to_forget':'forget'}[method],
|
|
743
761
|
triggering = funk ? 're-running' : 'initiating'
|
|
744
762
|
console.log(' > a', bus+'.'+event + "('" + (arg.key||arg) + "') is " + triggering
|
|
@@ -746,43 +764,50 @@
|
|
|
746
764
|
}
|
|
747
765
|
|
|
748
766
|
if (funk) {
|
|
749
|
-
// Then this is an
|
|
767
|
+
// Then this is an on_set event re-triggering an already-wrapped
|
|
750
768
|
// funk. It has its own arg internally that it's calling itself
|
|
751
769
|
// with. Let's tell it to re-trigger itself with that arg.
|
|
752
770
|
|
|
753
|
-
if (method !== '
|
|
754
|
-
console.error(method === '
|
|
771
|
+
if (method !== 'on_set') {
|
|
772
|
+
console.error(method === 'on_set', 'Funk is being re-triggered, but isn\'t on_set. It is: "' + method + '", oh and funk: ' + funk_name(funk))
|
|
755
773
|
return
|
|
756
774
|
}
|
|
757
775
|
return funk.react()
|
|
758
776
|
|
|
759
777
|
// Hmm... does this do the right thing? Example:
|
|
760
778
|
//
|
|
761
|
-
// bus('foo').
|
|
762
|
-
//
|
|
763
|
-
//
|
|
764
|
-
//
|
|
779
|
+
// bus('foo').on_set = function (o) {...}
|
|
780
|
+
// set({key: 'foo'})
|
|
781
|
+
// set({key: 'foo'})
|
|
782
|
+
// set({key: 'foo'})
|
|
765
783
|
//
|
|
766
784
|
// Does this spin up 3 reactive functions? Hmm...
|
|
767
785
|
// Well, I think it does, but they all get forgotten once
|
|
768
786
|
// they run once, and then are garbage collected.
|
|
769
787
|
//
|
|
770
|
-
// bus('foo*').
|
|
771
|
-
//
|
|
772
|
-
//
|
|
773
|
-
//
|
|
774
|
-
//
|
|
788
|
+
// bus('foo*').on_set = function (o) {...}
|
|
789
|
+
// set({key: 'foo1'})
|
|
790
|
+
// set({key: 'foo2'})
|
|
791
|
+
// set({key: 'foo1'})
|
|
792
|
+
// set({key: 'foo3'})
|
|
775
793
|
//
|
|
776
794
|
// Does this work ok? Yeah, I think so.
|
|
777
795
|
}
|
|
778
796
|
|
|
779
797
|
// Alright then. Let's wrap this func with some funk.
|
|
780
798
|
|
|
781
|
-
// Fresh
|
|
799
|
+
// Fresh get/set/forget/delete handlers will just be regular
|
|
782
800
|
// functions. We'll store their arg and transaction and let them
|
|
783
801
|
// re-run until they are done re-running.
|
|
784
802
|
function key_arg () { return ((typeof arg.key) == 'string') ? arg.key : arg }
|
|
785
803
|
function rest_arg () { return (key_arg()).substr(binding.length-1) }
|
|
804
|
+
function val_arg () {
|
|
805
|
+
console.assert(method === 'to_set' || method === 'on_set' || method === 'on_set_sync')
|
|
806
|
+
// Is there a time I am supposed to return bus.cache[arg]? It used to say:
|
|
807
|
+
// arg.key ? arg : bus.cache[arg]
|
|
808
|
+
|
|
809
|
+
return arg.key ? (func.use_linked_json ? arg.val : arg) : undefined
|
|
810
|
+
}
|
|
786
811
|
function vars_arg () {
|
|
787
812
|
var r = rest_arg()
|
|
788
813
|
try {
|
|
@@ -792,49 +817,50 @@
|
|
|
792
817
|
}
|
|
793
818
|
}
|
|
794
819
|
var f = reactive(function () {
|
|
795
|
-
|
|
796
820
|
// Initialize transaction
|
|
797
821
|
t = clone(t || {})
|
|
798
822
|
|
|
799
823
|
// Add .abort() method
|
|
800
|
-
if (method === '
|
|
824
|
+
if (method === 'to_set' || method === 'to_delete')
|
|
801
825
|
t.abort = function () {
|
|
802
|
-
var key = method === '
|
|
826
|
+
var key = method === 'to_set' ? arg.key : arg
|
|
803
827
|
if (f.loading()) return
|
|
804
828
|
bus.cache[key] = bus.cache[key] || {key: key}
|
|
805
829
|
bus.backup_cache[key] = bus.backup_cache[key] || {key: key}
|
|
806
|
-
bus.
|
|
830
|
+
bus.set.abort(bus.cache[key])
|
|
807
831
|
}
|
|
808
832
|
|
|
809
833
|
// Add .done() method
|
|
810
834
|
if (method !== 'to_forget')
|
|
811
835
|
t.done = function (o) {
|
|
812
|
-
var key = method === '
|
|
836
|
+
var key = method === 'to_set' ? arg.key : arg
|
|
837
|
+
if (func.use_linked_json)
|
|
838
|
+
o = {key, val: o}
|
|
813
839
|
bus.log('We are DONE()ing', method, key, o||arg)
|
|
814
840
|
|
|
815
841
|
// We use a simple (and crappy?) heuristic to know if the
|
|
816
|
-
//
|
|
842
|
+
// to_set handler has changed the state: whether the
|
|
817
843
|
// programmer passed (o) to the t.done(o) handler. If
|
|
818
844
|
// not, we assume it hasn't changed. If so, we assume it
|
|
819
845
|
// *has* changed, and thus we change the version of the
|
|
820
846
|
// state. I imagine it would be more accurate to diff
|
|
821
|
-
// from before the
|
|
847
|
+
// from before the to_set handler began with when
|
|
822
848
|
// t.done(o) ran.
|
|
823
849
|
//
|
|
824
850
|
// Note: We will likely solve this in the future by
|
|
825
|
-
// preventing .
|
|
851
|
+
// preventing .to_set() from changing the incoming state,
|
|
826
852
|
// except through an explicit .revise() function.
|
|
827
853
|
if (o) t.version = new_version()
|
|
828
854
|
|
|
829
855
|
if (method === 'to_delete')
|
|
830
856
|
delete bus.cache[key]
|
|
831
|
-
else if (method === '
|
|
832
|
-
bus.
|
|
857
|
+
else if (method === 'to_set') {
|
|
858
|
+
bus.set.fire(o || arg, t)
|
|
833
859
|
bus.route(key, 'on_set_sync', o || arg, t)
|
|
834
860
|
} else {
|
|
835
|
-
// Now method === '
|
|
861
|
+
// Now method === 'to_get'
|
|
836
862
|
o.key = key
|
|
837
|
-
bus.
|
|
863
|
+
bus.set.fire(o, t)
|
|
838
864
|
// And now reset the version cause it could get called again
|
|
839
865
|
delete t.version
|
|
840
866
|
}
|
|
@@ -843,14 +869,14 @@
|
|
|
843
869
|
// Alias .return() to .done(), in case that feels better to you
|
|
844
870
|
t.return = t.done
|
|
845
871
|
|
|
846
|
-
// Alias t.
|
|
847
|
-
if (method === '
|
|
848
|
-
t.
|
|
872
|
+
// Alias t.reget() to bus.dirty()
|
|
873
|
+
if (method === 'to_set')
|
|
874
|
+
t.reget = function () { bus.dirty(arg.key) }
|
|
849
875
|
|
|
850
876
|
// Now to call the handler, let's line up the function's special
|
|
851
877
|
// named arguemnts like key, o, t, rest, vars, etc.
|
|
852
878
|
var args = []
|
|
853
|
-
args[0] = arg
|
|
879
|
+
args[0] = (method in {to_set:1, on_set:1, on_set_sync:1}) ? val_arg() : arg
|
|
854
880
|
args[1] = t
|
|
855
881
|
for (var k in (func.args||{})) {
|
|
856
882
|
switch (k) {
|
|
@@ -864,7 +890,7 @@
|
|
|
864
890
|
case 't':
|
|
865
891
|
args[func.args[k]] = t; break
|
|
866
892
|
case 'obj':
|
|
867
|
-
args[func.args[k]] =
|
|
893
|
+
args[func.args[k]] = val_arg(); break
|
|
868
894
|
case 'old':
|
|
869
895
|
var key = key_arg()
|
|
870
896
|
args[func.args[k]] = bus.cache[key] || (bus.cache[key] = {key:key})
|
|
@@ -881,35 +907,45 @@
|
|
|
881
907
|
// arr[func.args[k]] = <compute_blah(k)>
|
|
882
908
|
|
|
883
909
|
// Trigger done() or abort() by return value
|
|
884
|
-
console.assert(!(result === '
|
|
910
|
+
console.assert(!(result === 'to_get' &&
|
|
885
911
|
(result === 'done' || result === 'abort')),
|
|
886
|
-
'Returning "done" or "abort" is not allowed from
|
|
912
|
+
'Returning "done" or "abort" is not allowed from to_get handlers')
|
|
887
913
|
if (result === 'done') t.done()
|
|
888
914
|
if (result === 'abort') t.abort()
|
|
889
915
|
|
|
890
|
-
// For
|
|
891
|
-
if (
|
|
892
|
-
&& !f.loading()
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
916
|
+
// For get
|
|
917
|
+
if (func.use_linked_json) {
|
|
918
|
+
if (method === 'to_get' && result !== undefined && !f.loading()) {
|
|
919
|
+
var obj = {key: arg, val: result}
|
|
920
|
+
var new_t = clone(t || {})
|
|
921
|
+
new_t.to_get = true
|
|
922
|
+
set.fire(obj, new_t)
|
|
923
|
+
return result
|
|
924
|
+
}
|
|
925
|
+
} else {
|
|
926
|
+
if (method === 'to_get' && result instanceof Object
|
|
927
|
+
&& !f.loading() // Experimental.
|
|
928
|
+
) {
|
|
929
|
+
result.key = arg
|
|
930
|
+
var new_t = clone(t || {})
|
|
931
|
+
new_t.to_get = true
|
|
932
|
+
set.fire(result, new_t)
|
|
933
|
+
return result
|
|
934
|
+
}
|
|
899
935
|
}
|
|
900
936
|
|
|
901
|
-
//
|
|
937
|
+
// Set, forget and delete handlers stop re-running once they've
|
|
902
938
|
// completed without anything loading.
|
|
903
939
|
// ... with f.forget()
|
|
904
|
-
if (method !== '
|
|
940
|
+
if (method !== 'to_get' && !f.loading())
|
|
905
941
|
f.forget()
|
|
906
942
|
})
|
|
907
943
|
f.proxies_for = func
|
|
908
944
|
f.arg = arg
|
|
909
945
|
f.transaction = t || {}
|
|
910
946
|
|
|
911
|
-
//
|
|
912
|
-
if (method === '
|
|
947
|
+
// to_get handlers stop re-running when the key is forgotten
|
|
948
|
+
if (method === 'to_get') {
|
|
913
949
|
var key = arg
|
|
914
950
|
function handler_done () {
|
|
915
951
|
f.forget()
|
|
@@ -918,13 +954,13 @@
|
|
|
918
954
|
bind(key, 'to_forget', handler_done)
|
|
919
955
|
|
|
920
956
|
// // Check if it's doubled-up
|
|
921
|
-
// if (
|
|
922
|
-
// console.error('Two .
|
|
923
|
-
// key+'!', funk_name(funck), funk_name(
|
|
957
|
+
// if (gets_out[key])
|
|
958
|
+
// console.error('Two .to_get functions are running on the same key',
|
|
959
|
+
// key+'!', funk_name(funck), funk_name(gets_out[key]))
|
|
924
960
|
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
961
|
+
gets_out[key] = gets_out[key] || []
|
|
962
|
+
gets_out[key].push(f) // Record active to_get handler
|
|
963
|
+
pending_gets[key] = f // Record that the get is pending
|
|
928
964
|
}
|
|
929
965
|
|
|
930
966
|
if (just_make_it)
|
|
@@ -943,9 +979,9 @@
|
|
|
943
979
|
for (var i=0; i<handlers.length; i++)
|
|
944
980
|
bus.run_handler(handlers[i].func, method, arg, {t: t, binding: handlers[i].key})
|
|
945
981
|
|
|
946
|
-
// if (method === '
|
|
982
|
+
// if (method === 'to_get')
|
|
947
983
|
// console.assert(handlers.length<2,
|
|
948
|
-
// 'Two
|
|
984
|
+
// 'Two to_get functions are registered for the same key '+key,
|
|
949
985
|
// handlers)
|
|
950
986
|
return handlers.length
|
|
951
987
|
}
|
|
@@ -955,7 +991,7 @@
|
|
|
955
991
|
// Reactive functions
|
|
956
992
|
//
|
|
957
993
|
// We wrap any function with a reactive wrapper that re-calls it whenever
|
|
958
|
-
// state it's
|
|
994
|
+
// state it's got changes.
|
|
959
995
|
|
|
960
996
|
if (!global_funk) {
|
|
961
997
|
global_funk = reactive(function global_funk () {})
|
|
@@ -970,7 +1006,7 @@
|
|
|
970
1006
|
// f = reactive(func)
|
|
971
1007
|
// f(arg1, arg2)
|
|
972
1008
|
//
|
|
973
|
-
// This will remember every
|
|
1009
|
+
// This will remember every get it depends on, and make it re-call
|
|
974
1010
|
// itself whenever that state changes. It will remember arg1 and arg2
|
|
975
1011
|
// and use those again. You can also trigger a re-action manually
|
|
976
1012
|
// with:
|
|
@@ -1029,14 +1065,14 @@
|
|
|
1029
1065
|
|
|
1030
1066
|
funk.func = func // just for debugging
|
|
1031
1067
|
funk.called_directly = true
|
|
1032
|
-
funk.
|
|
1068
|
+
funk.getted_keys = {} // maps [bus,key] to version
|
|
1033
1069
|
// version will be undefined until loaded
|
|
1034
1070
|
funk.abortable_keys = []
|
|
1035
1071
|
funk.has_seen = function (bus, key, version) {
|
|
1036
1072
|
//console.log('depend:', bus, key, versions[key])
|
|
1037
1073
|
var bus_key = JSON.stringify([bus.id, key])
|
|
1038
1074
|
var seen_versions =
|
|
1039
|
-
this.
|
|
1075
|
+
this.getted_keys[bus_key] = this.getted_keys[bus_key] || []
|
|
1040
1076
|
seen_versions.push(version)
|
|
1041
1077
|
if (seen_versions.length > 50) seen_versions.shift()
|
|
1042
1078
|
}
|
|
@@ -1051,29 +1087,29 @@
|
|
|
1051
1087
|
return result
|
|
1052
1088
|
}
|
|
1053
1089
|
funk.forget = function () {
|
|
1054
|
-
// Todo: This will bug out if an .
|
|
1055
|
-
//
|
|
1056
|
-
// when it
|
|
1057
|
-
//
|
|
1058
|
-
// re-run, and doesn't
|
|
1059
|
-
// defined as an .
|
|
1090
|
+
// Todo: This will bug out if an .on_set handler for a key also
|
|
1091
|
+
// gets that key once, and then doesn't get it again, because
|
|
1092
|
+
// when it gets the key, that key will end up being a
|
|
1093
|
+
// getted_key, and will then be forgotten as soon as the funk is
|
|
1094
|
+
// re-run, and doesn't get it again, and the fact that it is
|
|
1095
|
+
// defined as an .on_set .on_set handler won't matter anymore.
|
|
1060
1096
|
|
|
1061
1097
|
if (funk.statebus_id === 'global funk') return
|
|
1062
1098
|
|
|
1063
|
-
for (var hash in funk.
|
|
1099
|
+
for (var hash in funk.getted_keys) {
|
|
1064
1100
|
var tmp = JSON.parse(hash),
|
|
1065
1101
|
bus = busses[tmp[0]], key = tmp[1]
|
|
1066
1102
|
if (bus) // Cause it might have been deleted
|
|
1067
1103
|
bus.forget(key, funk)
|
|
1068
1104
|
}
|
|
1069
|
-
funk.
|
|
1105
|
+
funk.getted_keys = {}
|
|
1070
1106
|
}
|
|
1071
1107
|
funk.loading = function () {
|
|
1072
|
-
for (var hash in funk.
|
|
1108
|
+
for (var hash in funk.getted_keys) {
|
|
1073
1109
|
var tmp = JSON.parse(hash),
|
|
1074
1110
|
bus = busses[tmp[0]], key = tmp[1]
|
|
1075
1111
|
if (bus // Cause it might have been deleted
|
|
1076
|
-
&& bus.
|
|
1112
|
+
&& bus.pending_gets[key])
|
|
1077
1113
|
return true
|
|
1078
1114
|
}
|
|
1079
1115
|
return false
|
|
@@ -1087,9 +1123,9 @@
|
|
|
1087
1123
|
|
|
1088
1124
|
function loading_keys (keys) {
|
|
1089
1125
|
// Do any of these keys have outstanding gets?
|
|
1090
|
-
//console.log('Loading: pending_keys is',
|
|
1126
|
+
//console.log('Loading: pending_keys is', pending_gets)
|
|
1091
1127
|
for (var i=0; i<keys.length; i++)
|
|
1092
|
-
if (
|
|
1128
|
+
if (pending_gets[keys[i]]) return true
|
|
1093
1129
|
return false
|
|
1094
1130
|
}
|
|
1095
1131
|
|
|
@@ -1123,7 +1159,8 @@
|
|
|
1123
1159
|
var red = '', normal = '', grey = '',
|
|
1124
1160
|
green = '', brown = ''
|
|
1125
1161
|
function add_diff_msg (message, obj) {
|
|
1126
|
-
var diff = sorta_diff(backup_cache[obj.key]
|
|
1162
|
+
var diff = sorta_diff(backup_cache[obj.key] && backup_cache[obj.key].val,
|
|
1163
|
+
obj && obj.val)
|
|
1127
1164
|
if (diff) {
|
|
1128
1165
|
var end_col = message.length + 2 + statelog_indent * 3
|
|
1129
1166
|
for (var i=0; i<40-end_col; i++) message += ' '
|
|
@@ -1132,7 +1169,7 @@
|
|
|
1132
1169
|
else message += ' <no diff>'
|
|
1133
1170
|
return message
|
|
1134
1171
|
}
|
|
1135
|
-
function
|
|
1172
|
+
function set_msg (obj, t, meth) {
|
|
1136
1173
|
if (!honking_at(obj.key)) return
|
|
1137
1174
|
var message = (t && t.m) || bus + "."+meth+"('"+obj.key+"')"
|
|
1138
1175
|
message = add_diff_msg(message, obj)
|
|
@@ -1150,14 +1187,14 @@
|
|
|
1150
1187
|
if (!name) throw 'Uncallback function needs a name'
|
|
1151
1188
|
var watching = {}
|
|
1152
1189
|
var prefix = 'uncallback/' + name
|
|
1153
|
-
bus(prefix + '/*').
|
|
1190
|
+
bus(prefix + '/*').to_get = function (key, json) {
|
|
1154
1191
|
var args = json
|
|
1155
1192
|
function cb (err, result) {
|
|
1156
1193
|
if (err) {
|
|
1157
1194
|
console.trace('have err:', err, 'and result is', JSON.stringify(result))
|
|
1158
1195
|
throw err
|
|
1159
1196
|
} else
|
|
1160
|
-
bus.
|
|
1197
|
+
bus.set.fire({key: key, _: result})
|
|
1161
1198
|
}
|
|
1162
1199
|
|
|
1163
1200
|
// Inject the callback into the right place
|
|
@@ -1184,7 +1221,7 @@
|
|
|
1184
1221
|
}
|
|
1185
1222
|
return function () {
|
|
1186
1223
|
var args = [].slice.call(arguments)
|
|
1187
|
-
return bus.
|
|
1224
|
+
return bus.get(prefix + '/' + JSON.stringify(args))._
|
|
1188
1225
|
}
|
|
1189
1226
|
}
|
|
1190
1227
|
|
|
@@ -1198,392 +1235,31 @@
|
|
|
1198
1235
|
}
|
|
1199
1236
|
|
|
1200
1237
|
|
|
1201
|
-
//
|
|
1202
|
-
|
|
1203
|
-
var sb = (function sb () {
|
|
1204
|
-
// I have the cache behind the scenes
|
|
1205
|
-
// Each proxy has a target object -- the raw data on cache
|
|
1206
|
-
// If we're proxying a {_: ...} singleton then ...
|
|
1207
|
-
|
|
1208
|
-
function item_proxy (base, o) {
|
|
1209
|
-
if (typeof o === 'number'
|
|
1210
|
-
|| typeof o === 'string'
|
|
1211
|
-
|| typeof o === 'boolean'
|
|
1212
|
-
|| o === undefined
|
|
1213
|
-
|| o === null
|
|
1214
|
-
|| typeof o === 'function') return o
|
|
1215
|
-
|
|
1216
|
-
return new Proxy(o, {
|
|
1217
|
-
get: function get(o, k) {
|
|
1218
|
-
if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
|
|
1219
|
-
return undefined
|
|
1220
|
-
k = escape_keys(k)
|
|
1221
|
-
return item_proxy(base, o[k])
|
|
1222
|
-
},
|
|
1223
|
-
set: function set(o, k, v) {
|
|
1224
|
-
var result = o[escape_keys(k)] = v
|
|
1225
|
-
bus.save(base)
|
|
1226
|
-
return result
|
|
1227
|
-
},
|
|
1228
|
-
has: function has(o, k) {
|
|
1229
|
-
return o.hasOwnProperty(escape_keys(k))
|
|
1230
|
-
},
|
|
1231
|
-
deleteProperty: function del (o, k) {
|
|
1232
|
-
delete o[escape_keys(k)]
|
|
1233
|
-
},
|
|
1234
|
-
apply: function apply (o, This, args) {
|
|
1235
|
-
return o
|
|
1236
|
-
}
|
|
1237
|
-
})}
|
|
1238
|
-
|
|
1239
|
-
return new Proxy(cache, {
|
|
1240
|
-
get: function get(o, k) {
|
|
1241
|
-
if (k in bogus_keys) return o[k]
|
|
1242
|
-
if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
|
|
1243
|
-
return undefined
|
|
1244
|
-
var raw = bus.fetch(k),
|
|
1245
|
-
obj = raw
|
|
1246
|
-
while (typeof obj == 'object' && '_' in obj) obj = obj._
|
|
1247
|
-
return item_proxy(raw, obj)
|
|
1248
|
-
},
|
|
1249
|
-
set: function set(o, k, v) {
|
|
1250
|
-
if (typeof v === 'number'
|
|
1251
|
-
|| typeof v === 'string'
|
|
1252
|
-
|| typeof v === 'boolean'
|
|
1253
|
-
|| v === undefined
|
|
1254
|
-
|| v === null
|
|
1255
|
-
|| typeof v === 'function'
|
|
1256
|
-
|| Array.isArray(v))
|
|
1257
|
-
v = {_:v}
|
|
1258
|
-
else
|
|
1259
|
-
v = bus.clone(v)
|
|
1260
|
-
v.key = k
|
|
1261
|
-
bus.save(v)
|
|
1262
|
-
},
|
|
1263
|
-
// In future, this might check if there's a .to_fetch function OR
|
|
1264
|
-
// something in the cache:
|
|
1265
|
-
//
|
|
1266
|
-
// has: function has(o, k) {
|
|
1267
|
-
// return k in o
|
|
1268
|
-
// },
|
|
1269
|
-
// ... but I haven't had a need yet.
|
|
1270
|
-
deleteProperty: function del (o, k) {
|
|
1271
|
-
bus.delete(escape_keys(k))
|
|
1272
|
-
}
|
|
1273
|
-
})
|
|
1274
|
-
})()
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
// ******** State (Proxy) API *********
|
|
1278
|
-
//
|
|
1279
|
-
// The top-level state[..] object translates pointers of the
|
|
1280
|
-
// special form:
|
|
1281
|
-
//
|
|
1282
|
-
// {key: <s>, _: *}
|
|
1283
|
-
//
|
|
1284
|
-
// Examples:
|
|
1285
|
-
//
|
|
1286
|
-
// state['uninitialized']
|
|
1287
|
-
// >> undefined
|
|
1288
|
-
//
|
|
1289
|
-
// state['uninitialized'] = {}
|
|
1290
|
-
// >> {key: 'uninitialized'}
|
|
1291
|
-
//
|
|
1292
|
-
// state['uninitialized'] = {a: 3}
|
|
1293
|
-
// >> {key: 'uninitialized', a: 3}
|
|
1294
|
-
//
|
|
1295
|
-
// state['uninitialized'] = []
|
|
1296
|
-
// >> {key: 'uninitialized', _: []}
|
|
1297
|
-
//
|
|
1298
|
-
// delete state['uninitialized']
|
|
1299
|
-
// state['uninitialized']
|
|
1300
|
-
// >> undefined
|
|
1301
|
-
//
|
|
1302
|
-
// ** Rules
|
|
1303
|
-
// Setting:
|
|
1304
|
-
// - Escape-translate each field recursively
|
|
1305
|
-
// - If setting an object, put each field directly on it
|
|
1306
|
-
// - If setting anything else, put it into ._
|
|
1307
|
-
//
|
|
1308
|
-
// Getting:
|
|
1309
|
-
// - If it has unescaped fields other than ._, return object with them
|
|
1310
|
-
// - Otherwise, return ._
|
|
1311
|
-
// - Unescape all fields
|
|
1312
|
-
|
|
1313
|
-
var strict_mode = (function () {return !this})()
|
|
1314
|
-
function pget (base, o, k) {
|
|
1315
|
-
// console.log('pget:', {base, o, k})
|
|
1316
|
-
|
|
1317
|
-
if (base) {
|
|
1318
|
-
o = o[k]
|
|
1319
|
-
|
|
1320
|
-
// If new base, update and subscribe
|
|
1321
|
-
if (typeof o == 'object' && 'key' in o) {
|
|
1322
|
-
base = o
|
|
1323
|
-
bus.fetch(o.key)
|
|
1324
|
-
}
|
|
1325
|
-
} else {
|
|
1326
|
-
// We are getting from the Root
|
|
1327
|
-
o = bus.fetch(k)
|
|
1328
|
-
// console.log('pget: fetched', k, 'and got', o)
|
|
1329
|
-
base = o
|
|
1330
|
-
if (bus.validate(o, {key: '*', '?_': '*'})) {
|
|
1331
|
-
// console.log('pget: jumping into the _')
|
|
1332
|
-
o = o._
|
|
1333
|
-
}
|
|
1334
|
-
if (typeof o === 'object' && o.key)
|
|
1335
|
-
base = o
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
// Follow symlinks
|
|
1339
|
-
if (typeof o == 'object' && 'key' in o && '_' in o) {
|
|
1340
|
-
// Note: I don't actually need this recursion here, because
|
|
1341
|
-
// recursively linked state cannot be created by the proxy API.
|
|
1342
|
-
// So it can be undefined behavior.
|
|
1343
|
-
var tmp = pget(base, o, '_')
|
|
1344
|
-
base = tmp[0]
|
|
1345
|
-
o = tmp[1]
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
return [base, o]
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
function proxy_encode_val (x) {
|
|
1352
|
-
// Arrays
|
|
1353
|
-
if (Array.isArray(x)) {
|
|
1354
|
-
var result = []
|
|
1355
|
-
for (var i=0; i < x.length; i++)
|
|
1356
|
-
result[i] = proxy_encode_val(x[i])
|
|
1357
|
-
return result
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
// Objects
|
|
1361
|
-
else if (typeof x === 'object') {
|
|
1362
|
-
// Actual objects need their keys translated
|
|
1363
|
-
var result = {}
|
|
1364
|
-
for (var k in x)
|
|
1365
|
-
result[escape_keys(k)] = proxy_encode_val(x[k])
|
|
1366
|
-
return result
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
// Proxieds: already have JSON, stored inside. Return it.
|
|
1370
|
-
else if (typeof x === 'function' && x[symbols.is_proxy]) {
|
|
1371
|
-
return x()
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
// Everything else return
|
|
1375
|
-
return x
|
|
1376
|
-
}
|
|
1377
|
-
function proxy_decode_json (json) {
|
|
1378
|
-
// Returns data for proxies
|
|
1379
|
-
// - Remove keys
|
|
1380
|
-
// - Translate
|
|
1381
|
-
|
|
1382
|
-
// Root objects of special form
|
|
1383
|
-
if (bus.validate(json, {key: '*', '_': '*'}))
|
|
1384
|
-
return proxy_decode_json(json._)
|
|
1385
|
-
|
|
1386
|
-
// Arrays
|
|
1387
|
-
if (Array.isArray(json)) {
|
|
1388
|
-
var arr = json.slice()
|
|
1389
|
-
for (var i=0; i<arr.length; i++)
|
|
1390
|
-
arr[i] = proxy_decode_json(arr[i])
|
|
1391
|
-
return arr
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
// Objects
|
|
1395
|
-
if (typeof json === 'object' && json !== null) {
|
|
1396
|
-
var obj = {}
|
|
1397
|
-
for (var k in json)
|
|
1398
|
-
if (k !== 'key')
|
|
1399
|
-
obj[unescape_keys(k)] = proxy_decode_json(json[k])
|
|
1400
|
-
return obj
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
// Other primitives just return
|
|
1404
|
-
return json
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
if (nodejs) var util = require('util')
|
|
1408
|
-
function make_proxy (base, o) {
|
|
1409
|
-
if (!symbols)
|
|
1410
|
-
symbols = {is_proxy: Symbol('is_proxy'),
|
|
1411
|
-
get_json: Symbol('get_json'),
|
|
1412
|
-
get_base: Symbol('get_base')}
|
|
1413
|
-
|
|
1414
|
-
if (typeof o !== 'object' || o === null) return o
|
|
1415
|
-
|
|
1416
|
-
function get_json() {
|
|
1417
|
-
// Pop up to parent if this is a singleton array.
|
|
1418
|
-
// We know it's a singleton array if base._ === x(), and base is
|
|
1419
|
-
// of the form {key: *, _: x}
|
|
1420
|
-
if (base && base._ && Object.keys(base).length === 2
|
|
1421
|
-
&& base._ === o)
|
|
1422
|
-
return base
|
|
1423
|
-
|
|
1424
|
-
// Otherwise return x's JSON.
|
|
1425
|
-
return o
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
// Javascript won't let us function call a proxy unless the "target"
|
|
1429
|
-
// is a function. So we make a dummy target, and don't use it.
|
|
1430
|
-
var dummy_obj = function () {}
|
|
1431
|
-
return new Proxy(dummy_obj, {
|
|
1432
|
-
get: function (dummy_obj, k) {
|
|
1433
|
-
// console.log('get:', k, '::'+typeof k, 'on', o)
|
|
1434
|
-
|
|
1435
|
-
// Print something nice for Node console inspector
|
|
1436
|
-
if (nodejs && k === util.inspect.custom) {
|
|
1437
|
-
if (o == bus.cache)
|
|
1438
|
-
return function () {return 'state'+bus.toString().substr(3)}
|
|
1439
|
-
return function () {return 'p: '+util.format(proxy_decode_json(o))}
|
|
1440
|
-
}
|
|
1441
|
-
if (k in bogus_keys) return o[k]
|
|
1442
|
-
// Proxies distinguish themselves via proxy.is_proxy == true
|
|
1443
|
-
if (k === symbols.is_proxy) return true
|
|
1444
|
-
if (k === symbols.get_json) return get_json()
|
|
1445
|
-
if (k === symbols.get_base) return base
|
|
1446
|
-
if (k === Symbol.isConcatSpreadable) return Array.isArray(o)
|
|
1447
|
-
if (k === Symbol.toPrimitive) return function () {
|
|
1448
|
-
return JSON.stringify(proxy_decode_json(o))
|
|
1449
|
-
}
|
|
1450
|
-
if (typeof k === 'symbol') {
|
|
1451
|
-
console.warn('Got request for weird symbol', k)
|
|
1452
|
-
return undefined
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
var tmp2 = pget(base, o, escape_keys(k))
|
|
1456
|
-
var base2 = tmp2[0]
|
|
1457
|
-
var o2 = tmp2[1]
|
|
1458
|
-
|
|
1459
|
-
// console.log('returning proxy on', base2, o2)
|
|
1460
|
-
return make_proxy(base2, o2)
|
|
1461
|
-
},
|
|
1462
|
-
set: function (dummy_obj, k, v) {
|
|
1463
|
-
// console.log('set:', {base, o, k, v})
|
|
1464
|
-
|
|
1465
|
-
if (base) {
|
|
1466
|
-
var encoded_v = o[escape_keys(k)] = proxy_encode_val(v)
|
|
1467
|
-
// console.log(' set: saving', encoded_v, 'into', base)
|
|
1468
|
-
|
|
1469
|
-
// Collapse state of the form:
|
|
1470
|
-
// {key: '*', _: {foo: bar, ...}}
|
|
1471
|
-
// down to:
|
|
1472
|
-
// {key: '*', foo: bar}
|
|
1473
|
-
if (base._
|
|
1474
|
-
&& Object.keys(base).length === 2
|
|
1475
|
-
&& typeof base._ === 'object'
|
|
1476
|
-
&& base._ !== null
|
|
1477
|
-
&& !Array.isArray(base._)
|
|
1478
|
-
&& !base._.key
|
|
1479
|
-
&& Object.keys(base._).length !== 0) {
|
|
1480
|
-
// console.log('Collapsing', JSON.stringify(base))
|
|
1481
|
-
for (var k2 in base._)
|
|
1482
|
-
base[k2] = base._[k2]
|
|
1483
|
-
delete base._
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
bus.save(base)
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
// Saving into top-level state
|
|
1490
|
-
else {
|
|
1491
|
-
var encoded_v = proxy_encode_val(v)
|
|
1492
|
-
// console.log(' set top-level:', {v, encoded_v})
|
|
1493
|
-
|
|
1494
|
-
// Setting a top-level object to undefined wipes it out
|
|
1495
|
-
if (v === undefined)
|
|
1496
|
-
encoded_v = {key: k}
|
|
1497
|
-
|
|
1498
|
-
// Prefix with _: anything that is:
|
|
1499
|
-
else if (// A proxy to another state
|
|
1500
|
-
(typeof v === 'object' && v[symbols.is_proxy])
|
|
1501
|
-
// An empty {} object
|
|
1502
|
-
|| (typeof v === 'object' && Object.keys(v).length === 0)
|
|
1503
|
-
// A number, bool, string, function, etc
|
|
1504
|
-
|| typeof v !== 'object' || v === null
|
|
1505
|
-
// An array
|
|
1506
|
-
|| Array.isArray(v))
|
|
1507
|
-
encoded_v = {_: encoded_v}
|
|
1508
|
-
encoded_v.key = k
|
|
1509
|
-
|
|
1510
|
-
// console.log(' set top-level: now encoded_v is', encoded_v)
|
|
1511
|
-
bus.save(encoded_v)
|
|
1512
|
-
}
|
|
1238
|
+
// ******************
|
|
1239
|
+
// Proxy
|
|
1513
1240
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
//
|
|
1520
|
-
// - Do I want this to return true if there's a .to_fetch()
|
|
1521
|
-
// function for this o, k?
|
|
1522
|
-
//
|
|
1523
|
-
// - Does this need to do a fetch as well?
|
|
1524
|
-
//
|
|
1525
|
-
// - For a keyed object, should this do a loading() check?
|
|
1526
|
-
return o.hasOwnProperty(escape_keys(k))
|
|
1527
|
-
},
|
|
1528
|
-
deleteProperty: function del (O, k) {
|
|
1529
|
-
if (base) {
|
|
1530
|
-
// console.log(' deleting:', escape_keys(k), 'of', o)
|
|
1531
|
-
delete o[escape_keys(k)] // Deleting innards
|
|
1532
|
-
if (Object.keys(o).length === 1 && o.key)
|
|
1533
|
-
o._ = {}
|
|
1534
|
-
bus.save(base)
|
|
1535
|
-
}
|
|
1536
|
-
else
|
|
1537
|
-
bus.delete(escape_keys(k)) // Deleting top-level
|
|
1538
|
-
},
|
|
1539
|
-
apply: function apply (f, This, args) { return get_json() }
|
|
1540
|
-
})
|
|
1541
|
-
}
|
|
1542
|
-
if (nodejs || window.Proxy)
|
|
1543
|
-
var state = make_proxy(null, cache)
|
|
1544
|
-
|
|
1545
|
-
// So chrome can print out proxy objects decently
|
|
1546
|
-
if (!nodejs)
|
|
1547
|
-
window.devtoolsFormatters = [{
|
|
1548
|
-
header: function (x) {
|
|
1549
|
-
if (x[symbols.is_proxy])
|
|
1550
|
-
return ['span', {style: 'background-color: #feb; padding: 3px;'},
|
|
1551
|
-
JSON.stringify(proxy_decode_json(x()))]
|
|
1552
|
-
else if (x[symbols.is_braid_proxy])
|
|
1553
|
-
return ['span', {style: 'background-color: #fffbe5; padding: 3px;'},
|
|
1554
|
-
JSON.stringify(x)]
|
|
1555
|
-
// For function proxies:
|
|
1556
|
-
// JSON.stringify(x(), null, 2)]
|
|
1557
|
-
},
|
|
1558
|
-
hasBody: function (x) {return false}
|
|
1559
|
-
}]
|
|
1241
|
+
var symbols = {
|
|
1242
|
+
is_proxy: Symbol('is_proxy'),
|
|
1243
|
+
is_link: Symbol('is_link'),
|
|
1244
|
+
get_json: Symbol('get_json'),
|
|
1245
|
+
get_base: Symbol('get_base')
|
|
1560
1246
|
}
|
|
1561
|
-
|
|
1562
|
-
// ******************
|
|
1563
|
-
// Braid Test Mode Proxy
|
|
1564
|
-
|
|
1565
|
-
var symbols = {is_braid_proxy: Symbol('is_braid_proxy'),
|
|
1566
|
-
is_proxy: Symbol('is_proxy'),
|
|
1567
|
-
is_link: Symbol('is_link'),
|
|
1568
|
-
get_json: Symbol('get_json'),
|
|
1569
|
-
get_base: Symbol('get_base')
|
|
1570
|
-
}
|
|
1571
|
-
function braid_proxy () {
|
|
1247
|
+
function make_proxy () {
|
|
1572
1248
|
function item_proxy (base, o) {
|
|
1573
1249
|
|
|
1574
|
-
//
|
|
1250
|
+
// Primitives pass through unscathed
|
|
1575
1251
|
if (typeof o === 'number'
|
|
1576
1252
|
|| typeof o === 'string'
|
|
1577
1253
|
|| typeof o === 'boolean'
|
|
1578
1254
|
|| o === undefined
|
|
1579
1255
|
|| o === null
|
|
1580
1256
|
|| typeof o === 'function')
|
|
1581
|
-
|
|
1257
|
+
|
|
1582
1258
|
return o
|
|
1583
1259
|
|
|
1584
1260
|
// We recursively descend through {key: ...} links
|
|
1585
1261
|
if (typeof o === 'object' && 'key' in o) {
|
|
1586
|
-
var new_base = bus.
|
|
1262
|
+
var new_base = bus.get(o.key)
|
|
1587
1263
|
return item_proxy(new_base, new_base.val)
|
|
1588
1264
|
}
|
|
1589
1265
|
|
|
@@ -1600,7 +1276,7 @@
|
|
|
1600
1276
|
get: function get(o, k) {
|
|
1601
1277
|
if (k === 'inspect' || k === 'valueOf')
|
|
1602
1278
|
return undefined
|
|
1603
|
-
if (k === symbols.
|
|
1279
|
+
if (k === symbols.is_proxy)
|
|
1604
1280
|
return true
|
|
1605
1281
|
if (typeof k === 'symbol')
|
|
1606
1282
|
return undefined
|
|
@@ -1609,7 +1285,7 @@
|
|
|
1609
1285
|
set: function set(o, k, v) {
|
|
1610
1286
|
var value = translate_fields(v, proxied_2_keyed)
|
|
1611
1287
|
o[proxied_2_keyed(k)] = value
|
|
1612
|
-
bus.
|
|
1288
|
+
bus.set(base)
|
|
1613
1289
|
return true
|
|
1614
1290
|
},
|
|
1615
1291
|
has: function has(o, k) {
|
|
@@ -1635,11 +1311,11 @@
|
|
|
1635
1311
|
if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
|
|
1636
1312
|
return undefined
|
|
1637
1313
|
bogus_check(k)
|
|
1638
|
-
var base = bus.
|
|
1314
|
+
var base = bus.get(k)
|
|
1639
1315
|
return item_proxy(base, base.val)
|
|
1640
1316
|
},
|
|
1641
1317
|
set: function set(o, key, val) {
|
|
1642
|
-
bus.
|
|
1318
|
+
bus.set({key: key,
|
|
1643
1319
|
val: translate_fields(val, proxied_2_keyed)})
|
|
1644
1320
|
return true
|
|
1645
1321
|
},
|
|
@@ -1652,17 +1328,27 @@
|
|
|
1652
1328
|
// }
|
|
1653
1329
|
})
|
|
1654
1330
|
}
|
|
1331
|
+
bus.state = make_proxy()
|
|
1655
1332
|
|
|
1656
1333
|
function link (url) {
|
|
1657
|
-
var result =
|
|
1334
|
+
var result = bus.get(url)
|
|
1658
1335
|
result[symbols.is_link] = true
|
|
1659
1336
|
return result
|
|
1660
1337
|
}
|
|
1661
1338
|
|
|
1662
|
-
if ((nodejs && bus.options && bus.options.braid_mode)
|
|
1663
|
-
|| (!nodejs && window.Proxy && clientjs_option('braid_mode')))
|
|
1664
|
-
state = braid_proxy()
|
|
1665
1339
|
|
|
1340
|
+
// So chrome can print out proxy objects decently
|
|
1341
|
+
if (!nodejs)
|
|
1342
|
+
window.devtoolsFormatters = [{
|
|
1343
|
+
header: function (x) {
|
|
1344
|
+
if (x[symbols.is_proxy])
|
|
1345
|
+
return ['span', {style: 'background-color: #fffbe5; padding: 3px;'},
|
|
1346
|
+
JSON.stringify(x)]
|
|
1347
|
+
// For function proxies:
|
|
1348
|
+
// JSON.stringify(x(), null, 2)]
|
|
1349
|
+
},
|
|
1350
|
+
hasBody: function (x) {return false}
|
|
1351
|
+
}]
|
|
1666
1352
|
|
|
1667
1353
|
// ******************
|
|
1668
1354
|
// Network client
|
|
@@ -1671,13 +1357,13 @@
|
|
|
1671
1357
|
return m && m[0]
|
|
1672
1358
|
}
|
|
1673
1359
|
function message_method (m) {
|
|
1674
|
-
return (m.
|
|
1675
|
-
|| (m.
|
|
1360
|
+
return (m.get && 'get')
|
|
1361
|
+
|| (m.set && 'set')
|
|
1676
1362
|
|| (m['delete'] && 'delete')
|
|
1677
1363
|
|| (m.forget && 'forget')
|
|
1678
1364
|
}
|
|
1679
1365
|
|
|
1680
|
-
function
|
|
1366
|
+
function ws_mount (prefix, url, client_creds) {
|
|
1681
1367
|
// Local: state://foo.com/* or /*
|
|
1682
1368
|
var preprefix = prefix.slice(0,-1)
|
|
1683
1369
|
var is_absolute = /^i?statei?:\/\//
|
|
@@ -1686,7 +1372,7 @@
|
|
|
1686
1372
|
var sock
|
|
1687
1373
|
var attempts = 0
|
|
1688
1374
|
var outbox = []
|
|
1689
|
-
var
|
|
1375
|
+
var client_getted_keys = new bus.Set()
|
|
1690
1376
|
var heartbeat
|
|
1691
1377
|
if (url[url.length-1]=='/') url = url.substr(0,url.length-1)
|
|
1692
1378
|
function nlog (s) {
|
|
@@ -1696,9 +1382,9 @@
|
|
|
1696
1382
|
pushpop = pushpop || 'push'
|
|
1697
1383
|
o = rem_prefixes(o)
|
|
1698
1384
|
var m = message_method(o)
|
|
1699
|
-
if (m == '
|
|
1385
|
+
if (m == 'get' || m == 'delete' || m == 'forget')
|
|
1700
1386
|
o[m] = rem_prefix(o[m])
|
|
1701
|
-
bus.log('
|
|
1387
|
+
bus.log('ws_mount.send:', JSON.stringify(o))
|
|
1702
1388
|
outbox[pushpop](JSON.stringify(o))
|
|
1703
1389
|
flush_outbox()
|
|
1704
1390
|
}
|
|
@@ -1727,19 +1413,19 @@
|
|
|
1727
1413
|
function rem_prefixes (obj) {
|
|
1728
1414
|
return bus.translate_keys(bus.clone(obj), rem_prefix) }
|
|
1729
1415
|
|
|
1730
|
-
bus(prefix).
|
|
1731
|
-
bus.
|
|
1732
|
-
var x = {
|
|
1416
|
+
bus(prefix).to_set = function (obj, t) {
|
|
1417
|
+
bus.set.fire(obj)
|
|
1418
|
+
var x = {set: obj}
|
|
1733
1419
|
if (t.version) x.version = t.version
|
|
1734
1420
|
if (t.parents) x.parents = t.parents
|
|
1735
1421
|
if (t.patch) x.patch = t.patch
|
|
1736
|
-
if (t.patch) x.
|
|
1422
|
+
if (t.patch) x.set = rem_prefix(x.set.key)
|
|
1737
1423
|
send(x)
|
|
1738
1424
|
}
|
|
1739
|
-
bus(prefix).
|
|
1740
|
-
|
|
1425
|
+
bus(prefix).to_get = function (key) { send({get: key}),
|
|
1426
|
+
client_getted_keys.add(key) }
|
|
1741
1427
|
bus(prefix).to_forget = function (key) { send({forget: key}),
|
|
1742
|
-
|
|
1428
|
+
client_getted_keys.delete(key) }
|
|
1743
1429
|
bus(prefix).to_delete = function (key) { send({'delete': key}) }
|
|
1744
1430
|
|
|
1745
1431
|
function connect () {
|
|
@@ -1749,16 +1435,16 @@
|
|
|
1749
1435
|
nlog('[*] opened ' + url)
|
|
1750
1436
|
|
|
1751
1437
|
// Update state
|
|
1752
|
-
var peers = bus.
|
|
1438
|
+
var peers = bus.get('peers')
|
|
1753
1439
|
peers[url] = peers[url] || {}
|
|
1754
1440
|
peers[url].connected = true
|
|
1755
|
-
|
|
1441
|
+
set(peers)
|
|
1756
1442
|
|
|
1757
1443
|
// Login
|
|
1758
1444
|
var creds = client_creds || (bus.client_creds && bus.client_creds(url))
|
|
1759
1445
|
if (creds) {
|
|
1760
1446
|
var i = []
|
|
1761
|
-
function intro (o) {i.push(JSON.stringify({
|
|
1447
|
+
function intro (o) {i.push(JSON.stringify({set: o}))}
|
|
1762
1448
|
if (creds.clientid)
|
|
1763
1449
|
intro({key: 'current_user', client: creds.clientid})
|
|
1764
1450
|
if (creds.name && creds.pass)
|
|
@@ -1777,11 +1463,11 @@
|
|
|
1777
1463
|
|
|
1778
1464
|
// Reconnect
|
|
1779
1465
|
if (attempts > 0) {
|
|
1780
|
-
// Then we need to
|
|
1466
|
+
// Then we need to reget everything, cause it
|
|
1781
1467
|
// might have changed
|
|
1782
|
-
var keys =
|
|
1468
|
+
var keys = client_getted_keys.values()
|
|
1783
1469
|
for (var i=0; i<keys.length; i++)
|
|
1784
|
-
send({
|
|
1470
|
+
send({get: keys[i]})
|
|
1785
1471
|
}
|
|
1786
1472
|
|
|
1787
1473
|
attempts = 0
|
|
@@ -1797,14 +1483,14 @@
|
|
|
1797
1483
|
setTimeout(connect, attempts++ < 3 ? 1500 : 5000)
|
|
1798
1484
|
|
|
1799
1485
|
// Update state
|
|
1800
|
-
var peers = bus.
|
|
1486
|
+
var peers = bus.get('peers')
|
|
1801
1487
|
peers[url] = peers[url] || {}
|
|
1802
1488
|
peers[url].connected = false
|
|
1803
|
-
|
|
1489
|
+
set(peers)
|
|
1804
1490
|
|
|
1805
|
-
// Remove all
|
|
1491
|
+
// Remove all gets and forgets from queue
|
|
1806
1492
|
var new_outbox = []
|
|
1807
|
-
var bad = {'
|
|
1493
|
+
var bad = {'get':1, 'forget':1}
|
|
1808
1494
|
for (var i=0; i<outbox.length; i++)
|
|
1809
1495
|
if (!bad[JSON.parse(outbox[i]).method])
|
|
1810
1496
|
new_outbox.push(outbox[i])
|
|
@@ -1826,18 +1512,18 @@
|
|
|
1826
1512
|
var message = JSON.parse(event.data)
|
|
1827
1513
|
var method = message_method(message)
|
|
1828
1514
|
|
|
1829
|
-
// We only take
|
|
1830
|
-
if (method !== '
|
|
1515
|
+
// We only take sets from the server for now
|
|
1516
|
+
if (method !== 'set' && method !== 'pong') throw 'barf'
|
|
1831
1517
|
bus.log('net client received', message)
|
|
1832
1518
|
var t = {version: message.version,
|
|
1833
1519
|
parents: message.parents,
|
|
1834
1520
|
patch: message.patch}
|
|
1835
1521
|
if (t.patch)
|
|
1836
|
-
msg.
|
|
1522
|
+
msg.set = apply_patch(bus.cache[msg.set] || {key: msg.set},
|
|
1837
1523
|
message.patch[0])
|
|
1838
1524
|
if (!(t.version||t.parents||t.patch))
|
|
1839
1525
|
t = undefined
|
|
1840
|
-
bus.
|
|
1526
|
+
bus.set.fire(add_prefixes(message.set), t)
|
|
1841
1527
|
} catch (err) {
|
|
1842
1528
|
console.error('Received bad network message from '
|
|
1843
1529
|
+url+': ', event.data, err)
|
|
@@ -1861,7 +1547,7 @@
|
|
|
1861
1547
|
bus.route = function (key, method, arg, t) {
|
|
1862
1548
|
var d = get_domain(key)
|
|
1863
1549
|
if (d && !connections[d]) {
|
|
1864
|
-
bus.
|
|
1550
|
+
bus.ws_mount(d + '/*', d)
|
|
1865
1551
|
connections[d] = true
|
|
1866
1552
|
}
|
|
1867
1553
|
|
|
@@ -2210,8 +1896,8 @@
|
|
|
2210
1896
|
}
|
|
2211
1897
|
|
|
2212
1898
|
// This prune() function is a temporary workaround for dealing with nested
|
|
2213
|
-
// objects in
|
|
2214
|
-
// now, it calls .
|
|
1899
|
+
// objects in set() handlers, until we change statebus' behavior. Right
|
|
1900
|
+
// now, it calls .to_set only on the top-level state. But if that state
|
|
2215
1901
|
// validates, it calls fire() on *every* level of state. This means that
|
|
2216
1902
|
// state changes can sneak inside. Prune() will take out any changes from
|
|
2217
1903
|
// the nested levels of state in a new object -- replacing them with the
|
|
@@ -2228,7 +1914,7 @@
|
|
|
2228
1914
|
// Recurse through each property on objects
|
|
2229
1915
|
else if (typeof(o) === 'object')
|
|
2230
1916
|
if (o !== null && o.key)
|
|
2231
|
-
return bus.
|
|
1917
|
+
return bus.get(o.key)
|
|
2232
1918
|
else
|
|
2233
1919
|
for (var k in o)
|
|
2234
1920
|
o[k] = recurse(o[k])
|
|
@@ -2334,8 +2020,8 @@
|
|
|
2334
2020
|
switch (def.as) {
|
|
2335
2021
|
case 'handler':
|
|
2336
2022
|
return def.bus+"('"+def.key+"')."+def.method+' = '+f_string
|
|
2337
|
-
case '
|
|
2338
|
-
return '
|
|
2023
|
+
case 'get callback':
|
|
2024
|
+
return 'get('+def.key+', '+f_string+')'
|
|
2339
2025
|
case 'reactive':
|
|
2340
2026
|
return "reactive('"+f_string+"')"
|
|
2341
2027
|
default:
|
|
@@ -2346,7 +2032,7 @@
|
|
|
2346
2032
|
function deps (key) {
|
|
2347
2033
|
// First print out everything waiting for it to pub
|
|
2348
2034
|
var result = 'Deps: ('+key+') fires into:'
|
|
2349
|
-
var pubbers = bindings(key, '
|
|
2035
|
+
var pubbers = bindings(key, 'on_set')
|
|
2350
2036
|
if (pubbers.length === 0) result += ' nothing'
|
|
2351
2037
|
for (var i=0; i<pubbers.length; i++)
|
|
2352
2038
|
result += '\n ' + funk_name(pubbers[i].func)
|
|
@@ -2390,16 +2076,16 @@
|
|
|
2390
2076
|
}
|
|
2391
2077
|
|
|
2392
2078
|
// Make these private methods accessible
|
|
2393
|
-
var api = ['cache backup_cache
|
|
2079
|
+
var api = ['cache backup_cache get set forget del fire dirty get_once',
|
|
2394
2080
|
'subspace bindings run_handler bind unbind reactive uncallback',
|
|
2395
2081
|
'versions new_version',
|
|
2396
|
-
'
|
|
2082
|
+
'link',
|
|
2397
2083
|
'funk_key funk_name funks key_id key_name id',
|
|
2398
|
-
'
|
|
2084
|
+
'pending_gets gets_in gets_out loading_keys loading once',
|
|
2399
2085
|
'global_funk busses rerunnable_funks',
|
|
2400
2086
|
'escape_keys unescape_keys translate_keys apply_patch',
|
|
2401
2087
|
'keyed_2_proxied proxied_2_keyed translate_fields',
|
|
2402
|
-
'
|
|
2088
|
+
'ws_mount net_automount message_method',
|
|
2403
2089
|
'parse Set One_To_Many clone extend deep_map deep_equals prune validate sorta_diff log deps symbols'
|
|
2404
2090
|
].join(' ').split(' ')
|
|
2405
2091
|
for (var i=0; i<api.length; i++)
|
|
@@ -2417,27 +2103,20 @@
|
|
|
2417
2103
|
document.querySelector('script[src^="client"][src$=".js"]'))
|
|
2418
2104
|
return script_elem && script_elem.getAttribute(option_name)
|
|
2419
2105
|
}
|
|
2420
|
-
if (nodejs || clientjs_option('globals') !== 'false') {
|
|
2421
|
-
var globals = ['loading', 'clone', 'forget']
|
|
2422
|
-
var client_globals = ['fetch', 'save', 'del', 'state']
|
|
2423
|
-
if (!nodejs && Object.keys(busses).length == 0)
|
|
2424
|
-
globals = globals.concat(client_globals)
|
|
2425
|
-
|
|
2426
|
-
this.og_fetch = this.fetch // Cause fetch() is actually a browser API now
|
|
2427
|
-
for (var i=0; i<globals.length; i++)
|
|
2428
|
-
this[globals[i]] = eval(globals[i])
|
|
2429
|
-
}
|
|
2430
2106
|
|
|
2431
2107
|
busses[bus.id] = bus
|
|
2108
|
+
var this_bus_num = bus_count++
|
|
2109
|
+
|
|
2110
|
+
bus.libs = {}
|
|
2432
2111
|
|
|
2433
2112
|
if (nodejs)
|
|
2434
|
-
require('./
|
|
2113
|
+
require('./servers').import_server(bus, options)
|
|
2435
2114
|
|
|
2436
2115
|
bus.render_when_loading = true
|
|
2437
2116
|
return bus
|
|
2438
2117
|
}
|
|
2439
2118
|
|
|
2440
|
-
if (nodejs) require('./
|
|
2119
|
+
if (nodejs) require('./servers').import_module(make_bus)
|
|
2441
2120
|
|
|
2442
2121
|
return make_bus
|
|
2443
2122
|
}))
|