statebus 6.1.8 → 6.1.12
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 +75 -11
- package/package.json +2 -1
- package/server.js +76 -14
- package/statebus.js +379 -312
package/client.js
CHANGED
|
@@ -57,6 +57,61 @@
|
|
|
57
57
|
return {clientid: me.client}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function braid_http_mount (prefix, url) {
|
|
61
|
+
var preprefix = prefix.slice(0,-1)
|
|
62
|
+
var has_prefix = new RegExp('^' + preprefix)
|
|
63
|
+
var client_fetched_keys = new bus.Set()
|
|
64
|
+
if (url[url.length-1]=='/') url = url.substr(0,url.length-1)
|
|
65
|
+
function add_prefix (key) {
|
|
66
|
+
return is_absolute.test(key) ? key : preprefix + key }
|
|
67
|
+
function rem_prefix (key) {
|
|
68
|
+
return has_prefix.test(key) ? key.substr(preprefix.length) : key }
|
|
69
|
+
function add_prefixes (obj) {
|
|
70
|
+
return bus.translate_keys(bus.clone(obj), add_prefix) }
|
|
71
|
+
function rem_prefixes (obj) {
|
|
72
|
+
return bus.translate_keys(bus.clone(obj), rem_prefix) }
|
|
73
|
+
|
|
74
|
+
bus(prefix).to_save = function (obj, t) {
|
|
75
|
+
console.log('saving', prefix, obj)
|
|
76
|
+
bus.save.fire(obj)
|
|
77
|
+
|
|
78
|
+
// var x = {save: obj}
|
|
79
|
+
// if (t.version) x.version = t.version
|
|
80
|
+
// if (t.parents) x.parents = t.parents
|
|
81
|
+
// if (t.patch) x.patch = t.patch
|
|
82
|
+
// if (t.patch) x.save = rem_prefix(x.save.key)
|
|
83
|
+
|
|
84
|
+
braid_fetch(url + obj.key,
|
|
85
|
+
{
|
|
86
|
+
method: 'put',
|
|
87
|
+
body: JSON.stringify(obj.val)
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
bus(prefix).to_fetch = function (key, t) {
|
|
92
|
+
try {
|
|
93
|
+
braid_fetch(url + rem_prefix(key),
|
|
94
|
+
{
|
|
95
|
+
method: 'get',
|
|
96
|
+
subscribe: true
|
|
97
|
+
}
|
|
98
|
+
).andThen( x => {
|
|
99
|
+
t.return({
|
|
100
|
+
key,
|
|
101
|
+
val: add_prefixes(JSON.parse(x.body))
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.error('Braid-HTTP network error:', e)
|
|
106
|
+
}
|
|
107
|
+
client_fetched_keys.add(key)
|
|
108
|
+
}
|
|
109
|
+
bus(prefix).to_forget = function (key) {
|
|
110
|
+
// send({forget: key}),
|
|
111
|
+
client_fetched_keys.delete(key)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
60
115
|
|
|
61
116
|
// ****************
|
|
62
117
|
// Manipulate Localstorage
|
|
@@ -382,16 +437,22 @@
|
|
|
382
437
|
react_render = React.version >= '0.14.' ? ReactDOM.render : React.render
|
|
383
438
|
make_client_statebus_maker()
|
|
384
439
|
window.bus = window.statebus()
|
|
385
|
-
|
|
386
|
-
|
|
440
|
+
bus.label = 'bus'
|
|
441
|
+
if (clientjs_option('braid_mode_test'))
|
|
442
|
+
bus.state = bus.braid_proxy()
|
|
443
|
+
else
|
|
444
|
+
window.sb = bus.sb
|
|
387
445
|
statebus.widget = React_View
|
|
388
446
|
statebus.create_react_class = React_View
|
|
389
447
|
statebus.createReactClass = React_View
|
|
390
448
|
|
|
391
449
|
improve_react()
|
|
392
450
|
window.ignore_flashbacks = false
|
|
393
|
-
if (statebus_server !== 'none')
|
|
451
|
+
if (statebus_server !== 'none') {
|
|
394
452
|
bus.net_mount ('/*', statebus_server)
|
|
453
|
+
if (clientjs_option('braid_mode_test'))
|
|
454
|
+
braid_http_mount ('http/*', statebus_server)
|
|
455
|
+
}
|
|
395
456
|
|
|
396
457
|
if (window.statebus_backdoor) {
|
|
397
458
|
window.master = statebus()
|
|
@@ -399,16 +460,19 @@
|
|
|
399
460
|
}
|
|
400
461
|
bus.net_automount()
|
|
401
462
|
|
|
402
|
-
//
|
|
403
|
-
|
|
404
|
-
|
|
463
|
+
// This /new/* code is deprecated
|
|
464
|
+
if (!clientjs_option('braid_mode_test')) {
|
|
465
|
+
bus('/new/*').to_save = function (o) {
|
|
466
|
+
if (o.key.split('/').length > 3) return
|
|
405
467
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
468
|
+
var old_key = o.key
|
|
469
|
+
o.key = old_key + '/' + Math.random().toString(36).substring(2,12)
|
|
470
|
+
statebus.cache[o.key] = o
|
|
471
|
+
delete statebus.cache[old_key]
|
|
472
|
+
bus.save(o)
|
|
473
|
+
}
|
|
411
474
|
}
|
|
475
|
+
|
|
412
476
|
load_coffee()
|
|
413
477
|
|
|
414
478
|
statebus.compile_coffee = compile_coffee
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "statebus",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.12",
|
|
4
4
|
"description": "Synchronize state.",
|
|
5
5
|
"main": "statebus.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"repository": "invisible-college/statebus",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"bcrypt-nodejs": "0.0.3",
|
|
13
|
+
"braidify": "^0.1.18",
|
|
13
14
|
"chokidar": "^3.4.0",
|
|
14
15
|
"coffeescript": "^2.3.1",
|
|
15
16
|
"cookie": "^0.3.1",
|
package/server.js
CHANGED
|
@@ -96,7 +96,7 @@ function import_server (bus, options)
|
|
|
96
96
|
bus.sync_files(x.state_path, x.fs_path)
|
|
97
97
|
})
|
|
98
98
|
|
|
99
|
-
// User will put
|
|
99
|
+
// User will put his routes in here
|
|
100
100
|
bus.express.use('/', bus.http)
|
|
101
101
|
|
|
102
102
|
// Use braidify if it's available
|
|
@@ -107,7 +107,7 @@ function import_server (bus, options)
|
|
|
107
107
|
var braidify = undefined
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
//
|
|
110
|
+
// Mirror state over HTTP
|
|
111
111
|
bus.express.get('*', function (req, res) {
|
|
112
112
|
// Make a temporary client bus
|
|
113
113
|
var cbus = bus.bus_for_http_client(req, res)
|
|
@@ -115,6 +115,7 @@ function import_server (bus, options)
|
|
|
115
115
|
function end_it_all () {
|
|
116
116
|
cbus.forget(key, cb)
|
|
117
117
|
cbus.delete_bus()
|
|
118
|
+
res.end()
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
braidify && braidify(req, res)
|
|
@@ -128,25 +129,86 @@ function import_server (bus, options)
|
|
|
128
129
|
cbus.honk = 'statelog'
|
|
129
130
|
var key = req.path.substr(1)
|
|
130
131
|
cbus.fetch(key, cb = (o) => {
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
var body = bus.to_http_body(o)
|
|
133
|
+
|
|
134
|
+
// Return 404 if the value is undefined
|
|
135
|
+
if (!body) {
|
|
136
|
+
console.log('no body! time to 404')
|
|
137
|
+
res.statusCode = 404
|
|
138
|
+
res.end()
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Or if we're braid, send via subscription
|
|
142
|
+
else if (braidify)
|
|
143
|
+
res.sendVersion({body})
|
|
144
|
+
|
|
145
|
+
// Or just return the current version
|
|
133
146
|
else
|
|
134
|
-
res.send(
|
|
147
|
+
res.send(body)
|
|
135
148
|
|
|
136
|
-
if
|
|
149
|
+
// And shut down the connection if there's nothing left to do
|
|
150
|
+
if (!braidify || !req.subscribe || !body)
|
|
137
151
|
end_it_all()
|
|
138
152
|
})
|
|
139
153
|
})
|
|
140
154
|
|
|
155
|
+
bus.express.put('*', function (req, res) {
|
|
156
|
+
console.log('Got a put with body', req.body)
|
|
157
|
+
|
|
158
|
+
// Make a temporary client bus
|
|
159
|
+
var cbus = bus.bus_for_http_client(req, res)
|
|
160
|
+
|
|
161
|
+
// Maybe the new state is expressed in patches
|
|
162
|
+
if (req.headers.patches) {
|
|
163
|
+
console.error("We don't actually handle PUT patches yet")
|
|
164
|
+
|
|
165
|
+
// var patches = await req.patches()
|
|
166
|
+
// console.log("...but we see these patches:", patches)
|
|
167
|
+
|
|
168
|
+
res.statusCode = 500
|
|
169
|
+
res.end()
|
|
170
|
+
} else {
|
|
171
|
+
// Otherwise, we assume the body is content-type: json
|
|
172
|
+
if (typeof req.headers['content-type'] !== 'string'
|
|
173
|
+
|| req.headers['content-type'].toLowerCase() !== 'application/json')
|
|
174
|
+
console.error('Error: PUT content-type is not application/json')
|
|
175
|
+
var body = ''
|
|
176
|
+
req.on('data', chunk => {body += chunk.toString()})
|
|
177
|
+
req.on('end', () => {
|
|
178
|
+
try {
|
|
179
|
+
console.log('gonna parse', body)
|
|
180
|
+
var path = req.url.substr(1)
|
|
181
|
+
var obj = bus.from_http_body(path, body)
|
|
182
|
+
} catch (e) {
|
|
183
|
+
console.error('Error: PUT body was not valid json', e)
|
|
184
|
+
res.statusCode = 500
|
|
185
|
+
res.end()
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
cbus.save(obj)
|
|
189
|
+
res.statusCode = 200
|
|
190
|
+
res.end()
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
141
195
|
// Set up default linked json converters
|
|
142
|
-
bus.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
196
|
+
if (bus.options.braid_mode_test) {
|
|
197
|
+
bus.to_http_body = (o) => JSON.stringify(o.val)
|
|
198
|
+
bus.state = bus.braid_proxy()
|
|
199
|
+
} else
|
|
200
|
+
bus.to_http_body = (o) => {
|
|
201
|
+
var unwrap = (Object.keys(o).length === 2
|
|
202
|
+
&& '_' in o
|
|
203
|
+
&& typeof o._ === 'string')
|
|
204
|
+
// To do: translate pointers as keys
|
|
205
|
+
return unwrap ? o._ : JSON.stringify(o)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
bus.from_http_body = (key, body) => ({
|
|
209
|
+
key,
|
|
210
|
+
val: JSON.parse(body)
|
|
211
|
+
})
|
|
150
212
|
|
|
151
213
|
// Serve Client Coffee
|
|
152
214
|
bus.serve_client_coffee()
|
package/statebus.js
CHANGED
|
@@ -1197,7 +1197,366 @@
|
|
|
1197
1197
|
})
|
|
1198
1198
|
}
|
|
1199
1199
|
|
|
1200
|
-
|
|
1200
|
+
|
|
1201
|
+
// Old Proxy Prototype
|
|
1202
|
+
if (true) {
|
|
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 = underscore_escape(k)
|
|
1221
|
+
return item_proxy(base, o[k])
|
|
1222
|
+
},
|
|
1223
|
+
set: function set(o, k, v) {
|
|
1224
|
+
var result = o[underscore_escape(k)] = v
|
|
1225
|
+
bus.save(base)
|
|
1226
|
+
return result
|
|
1227
|
+
},
|
|
1228
|
+
has: function has(o, k) {
|
|
1229
|
+
return o.hasOwnProperty(underscore_escape(k))
|
|
1230
|
+
},
|
|
1231
|
+
deleteProperty: function del (o, k) {
|
|
1232
|
+
delete o[underscore_escape(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(underscore_escape(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[underscore_escape(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[underscore_unescape(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, underscore_escape(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[underscore_escape(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
|
+
}
|
|
1513
|
+
|
|
1514
|
+
var newbase = (encoded_v && encoded_v.key) ? encoded_v : base
|
|
1515
|
+
return true
|
|
1516
|
+
},
|
|
1517
|
+
has: function has(O, k) {
|
|
1518
|
+
// XXX QUESTIONS:
|
|
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(underscore_escape(k))
|
|
1527
|
+
},
|
|
1528
|
+
deleteProperty: function del (O, k) {
|
|
1529
|
+
if (base) {
|
|
1530
|
+
// console.log(' deleting:', underscore_escape(k), 'of', o)
|
|
1531
|
+
delete o[underscore_escape(k)] // Deleting innards
|
|
1532
|
+
if (Object.keys(o).length === 1 && o.key)
|
|
1533
|
+
o._ = {}
|
|
1534
|
+
bus.save(base)
|
|
1535
|
+
}
|
|
1536
|
+
else
|
|
1537
|
+
bus.delete(underscore_escape(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
|
+
return x[symbols.is_proxy] &&
|
|
1550
|
+
['span', {style: 'background-color: #feb; padding: 3px;'},
|
|
1551
|
+
JSON.stringify(proxy_decode_json(x()))]
|
|
1552
|
+
},
|
|
1553
|
+
hasBody: function (x) {return false}
|
|
1554
|
+
}]
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// ******************
|
|
1558
|
+
// Braid Test Mode Proxy
|
|
1559
|
+
function braid_proxy () {
|
|
1201
1560
|
// I have the cache behind the scenes
|
|
1202
1561
|
// Each proxy has a target object -- the raw data on cache
|
|
1203
1562
|
// If we're proxying a {_: ...} singleton then ...
|
|
@@ -1214,19 +1573,18 @@
|
|
|
1214
1573
|
get: function get(o, k) {
|
|
1215
1574
|
if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
|
|
1216
1575
|
return undefined
|
|
1217
|
-
|
|
1218
|
-
return item_proxy(base, o[k])
|
|
1576
|
+
return item_proxy(base, o[underscore_escape(k)])
|
|
1219
1577
|
},
|
|
1220
1578
|
set: function set(o, k, v) {
|
|
1221
|
-
var result = o[
|
|
1579
|
+
var result = o[underscore_escape(k)] = v
|
|
1222
1580
|
bus.save(base)
|
|
1223
1581
|
return result
|
|
1224
1582
|
},
|
|
1225
1583
|
has: function has(o, k) {
|
|
1226
|
-
return o.hasOwnProperty(
|
|
1584
|
+
return o.hasOwnProperty(underscore_escape(k))
|
|
1227
1585
|
},
|
|
1228
1586
|
deleteProperty: function del (o, k) {
|
|
1229
|
-
delete o[
|
|
1587
|
+
delete o[underscore_escape(k)]
|
|
1230
1588
|
},
|
|
1231
1589
|
apply: function apply (o, This, args) {
|
|
1232
1590
|
return o
|
|
@@ -1235,27 +1593,15 @@
|
|
|
1235
1593
|
|
|
1236
1594
|
return new Proxy(cache, {
|
|
1237
1595
|
get: function get(o, k) {
|
|
1238
|
-
if (k in bogus_keys) return o[k]
|
|
1239
1596
|
if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
|
|
1240
1597
|
return undefined
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
return item_proxy(
|
|
1598
|
+
if (k in bogus_keys)
|
|
1599
|
+
return o[k]
|
|
1600
|
+
var base = bus.fetch(k)
|
|
1601
|
+
return item_proxy(base, base.val)
|
|
1245
1602
|
},
|
|
1246
|
-
set: function set(o,
|
|
1247
|
-
|
|
1248
|
-
|| typeof v === 'string'
|
|
1249
|
-
|| typeof v === 'boolean'
|
|
1250
|
-
|| v === undefined
|
|
1251
|
-
|| v === null
|
|
1252
|
-
|| typeof v === 'function'
|
|
1253
|
-
|| Array.isArray(v))
|
|
1254
|
-
v = {_:v}
|
|
1255
|
-
else
|
|
1256
|
-
v = bus.clone(v)
|
|
1257
|
-
v.key = k
|
|
1258
|
-
bus.save(v)
|
|
1603
|
+
set: function set(o, key, val) {
|
|
1604
|
+
bus.save({key, val})
|
|
1259
1605
|
},
|
|
1260
1606
|
// In future, this might check if there's a .to_fetch function OR
|
|
1261
1607
|
// something in the cache:
|
|
@@ -1265,290 +1611,10 @@
|
|
|
1265
1611
|
// },
|
|
1266
1612
|
// ... but I haven't had a need yet.
|
|
1267
1613
|
deleteProperty: function del (o, k) {
|
|
1268
|
-
bus.delete(
|
|
1269
|
-
}
|
|
1270
|
-
})
|
|
1271
|
-
})()
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
// ******** State (Proxy) API *********
|
|
1275
|
-
//
|
|
1276
|
-
// The top-level state[..] object translates pointers of the
|
|
1277
|
-
// special form:
|
|
1278
|
-
//
|
|
1279
|
-
// {key: <s>, _: *}
|
|
1280
|
-
//
|
|
1281
|
-
// Examples:
|
|
1282
|
-
//
|
|
1283
|
-
// state['uninitialized']
|
|
1284
|
-
// >> undefined
|
|
1285
|
-
//
|
|
1286
|
-
// state['uninitialized'] = {}
|
|
1287
|
-
// >> {key: 'uninitialized'}
|
|
1288
|
-
//
|
|
1289
|
-
// state['uninitialized'] = {a: 3}
|
|
1290
|
-
// >> {key: 'uninitialized', a: 3}
|
|
1291
|
-
//
|
|
1292
|
-
// state['uninitialized'] = []
|
|
1293
|
-
// >> {key: 'uninitialized', _: []}
|
|
1294
|
-
//
|
|
1295
|
-
// delete state['uninitialized']
|
|
1296
|
-
// state['uninitialized']
|
|
1297
|
-
// >> undefined
|
|
1298
|
-
//
|
|
1299
|
-
// ** Rules
|
|
1300
|
-
// Setting:
|
|
1301
|
-
// - Escape-translate each field recursively
|
|
1302
|
-
// - If setting an object, put each field directly on it
|
|
1303
|
-
// - If setting anything else, put it into ._
|
|
1304
|
-
//
|
|
1305
|
-
// Getting:
|
|
1306
|
-
// - If it has unescaped fields other than ._, return object with them
|
|
1307
|
-
// - Otherwise, return ._
|
|
1308
|
-
// - Unescape all fields
|
|
1309
|
-
|
|
1310
|
-
var strict_mode = (function () {return !this})()
|
|
1311
|
-
function pget (base, o, k) {
|
|
1312
|
-
// console.log('pget:', {base, o, k})
|
|
1313
|
-
|
|
1314
|
-
if (base) {
|
|
1315
|
-
o = o[k]
|
|
1316
|
-
|
|
1317
|
-
// If new base, update and subscribe
|
|
1318
|
-
if (typeof o == 'object' && 'key' in o) {
|
|
1319
|
-
base = o
|
|
1320
|
-
bus.fetch(o.key)
|
|
1321
|
-
}
|
|
1322
|
-
} else {
|
|
1323
|
-
// We are getting from the Root
|
|
1324
|
-
o = bus.fetch(k)
|
|
1325
|
-
// console.log('pget: fetched', k, 'and got', o)
|
|
1326
|
-
base = o
|
|
1327
|
-
if (bus.validate(o, {key: '*', '?_': '*'})) {
|
|
1328
|
-
// console.log('pget: jumping into the _')
|
|
1329
|
-
o = o._
|
|
1614
|
+
bus.delete(underscore_escape(k))
|
|
1330
1615
|
}
|
|
1331
|
-
if (typeof o === 'object' && o.key)
|
|
1332
|
-
base = o
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
// Follow symlinks
|
|
1336
|
-
if (typeof o == 'object' && 'key' in o && '_' in o) {
|
|
1337
|
-
// Note: I don't actually need this recursion here, because
|
|
1338
|
-
// recursively linked state cannot be created by the proxy API.
|
|
1339
|
-
// So it can be undefined behavior.
|
|
1340
|
-
var tmp = pget(base, o, '_')
|
|
1341
|
-
base = tmp[0]
|
|
1342
|
-
o = tmp[1]
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
return [base, o]
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
function proxy_encode_val (x) {
|
|
1349
|
-
// Arrays
|
|
1350
|
-
if (Array.isArray(x)) {
|
|
1351
|
-
var result = []
|
|
1352
|
-
for (var i=0; i < x.length; i++)
|
|
1353
|
-
result[i] = proxy_encode_val(x[i])
|
|
1354
|
-
return result
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
// Objects
|
|
1358
|
-
else if (typeof x === 'object') {
|
|
1359
|
-
// Actual objects need their keys translated
|
|
1360
|
-
var result = {}
|
|
1361
|
-
for (var k in x)
|
|
1362
|
-
result[encode_field(k)] = proxy_encode_val(x[k])
|
|
1363
|
-
return result
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
// Proxieds: already have JSON, stored inside. Return it.
|
|
1367
|
-
else if (typeof x === 'function' && x[symbols.is_proxy]) {
|
|
1368
|
-
return x()
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
// Everything else return
|
|
1372
|
-
return x
|
|
1373
|
-
}
|
|
1374
|
-
function proxy_decode_json (json) {
|
|
1375
|
-
// Returns data for proxies
|
|
1376
|
-
// - Remove keys
|
|
1377
|
-
// - Translate
|
|
1378
|
-
|
|
1379
|
-
// Root objects of special form
|
|
1380
|
-
if (bus.validate(json, {key: '*', '_': '*'}))
|
|
1381
|
-
return proxy_decode_json(json._)
|
|
1382
|
-
|
|
1383
|
-
// Arrays
|
|
1384
|
-
if (Array.isArray(json)) {
|
|
1385
|
-
var arr = json.slice()
|
|
1386
|
-
for (var i=0; i<arr.length; i++)
|
|
1387
|
-
arr[i] = proxy_decode_json(arr[i])
|
|
1388
|
-
return arr
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
// Objects
|
|
1392
|
-
if (typeof json === 'object' && json !== null) {
|
|
1393
|
-
var obj = {}
|
|
1394
|
-
for (var k in json)
|
|
1395
|
-
if (k !== 'key')
|
|
1396
|
-
obj[decode_field(k)] = proxy_decode_json(json[k])
|
|
1397
|
-
return obj
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
// Other primitives just return
|
|
1401
|
-
return json
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
if (nodejs) var util = require('util')
|
|
1405
|
-
function make_proxy (base, o) {
|
|
1406
|
-
if (!symbols)
|
|
1407
|
-
symbols = {is_proxy: Symbol('is_proxy'),
|
|
1408
|
-
get_json: Symbol('get_json'),
|
|
1409
|
-
get_base: Symbol('get_base')}
|
|
1410
|
-
|
|
1411
|
-
if (typeof o !== 'object' || o === null) return o
|
|
1412
|
-
|
|
1413
|
-
function get_json() {
|
|
1414
|
-
// Pop up to parent if this is a singleton array.
|
|
1415
|
-
// We know it's a singleton array if base._ === x(), and base is
|
|
1416
|
-
// of the form {key: *, _: x}
|
|
1417
|
-
if (base && base._ && Object.keys(base).length === 2
|
|
1418
|
-
&& base._ === o)
|
|
1419
|
-
return base
|
|
1420
|
-
|
|
1421
|
-
// Otherwise return x's JSON.
|
|
1422
|
-
return o
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
// Javascript won't let us function call a proxy unless the "target"
|
|
1426
|
-
// is a function. So we make a dummy target, and don't use it.
|
|
1427
|
-
var dummy_obj = function () {}
|
|
1428
|
-
return new Proxy(dummy_obj, {
|
|
1429
|
-
get: function (dummy_obj, k) {
|
|
1430
|
-
// console.log('get:', k, '::'+typeof k, 'on', o)
|
|
1431
|
-
|
|
1432
|
-
// Print something nice for Node console inspector
|
|
1433
|
-
if (nodejs && k === util.inspect.custom) {
|
|
1434
|
-
if (o == bus.cache)
|
|
1435
|
-
return function () {return 'state'+bus.toString().substr(3)}
|
|
1436
|
-
return function () {return 'p: '+util.format(proxy_decode_json(o))}
|
|
1437
|
-
}
|
|
1438
|
-
if (k in bogus_keys) return o[k]
|
|
1439
|
-
// Proxies distinguish themselves via proxy.is_proxy == true
|
|
1440
|
-
if (k === symbols.is_proxy) return true
|
|
1441
|
-
if (k === symbols.get_json) return get_json()
|
|
1442
|
-
if (k === symbols.get_base) return base
|
|
1443
|
-
if (k === Symbol.isConcatSpreadable) return Array.isArray(o)
|
|
1444
|
-
if (k === Symbol.toPrimitive) return function () {
|
|
1445
|
-
return JSON.stringify(proxy_decode_json(o))
|
|
1446
|
-
}
|
|
1447
|
-
if (typeof k === 'symbol') {
|
|
1448
|
-
console.warn('Got request for weird symbol', k)
|
|
1449
|
-
return undefined
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
var tmp2 = pget(base, o, encode_field(k))
|
|
1453
|
-
var base2 = tmp2[0]
|
|
1454
|
-
var o2 = tmp2[1]
|
|
1455
|
-
|
|
1456
|
-
// console.log('returning proxy on', base2, o2)
|
|
1457
|
-
return make_proxy(base2, o2)
|
|
1458
|
-
},
|
|
1459
|
-
set: function (dummy_obj, k, v) {
|
|
1460
|
-
// console.log('set:', {base, o, k, v})
|
|
1461
|
-
|
|
1462
|
-
if (base) {
|
|
1463
|
-
var encoded_v = o[encode_field(k)] = proxy_encode_val(v)
|
|
1464
|
-
// console.log(' set: saving', encoded_v, 'into', base)
|
|
1465
|
-
|
|
1466
|
-
// Collapse state of the form:
|
|
1467
|
-
// {key: '*', _: {foo: bar, ...}}
|
|
1468
|
-
// down to:
|
|
1469
|
-
// {key: '*', foo: bar}
|
|
1470
|
-
if (base._
|
|
1471
|
-
&& Object.keys(base).length === 2
|
|
1472
|
-
&& typeof base._ === 'object'
|
|
1473
|
-
&& base._ !== null
|
|
1474
|
-
&& !Array.isArray(base._)
|
|
1475
|
-
&& !base._.key
|
|
1476
|
-
&& Object.keys(base._).length !== 0) {
|
|
1477
|
-
// console.log('Collapsing', JSON.stringify(base))
|
|
1478
|
-
for (var k2 in base._)
|
|
1479
|
-
base[k2] = base._[k2]
|
|
1480
|
-
delete base._
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
bus.save(base)
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
// Saving into top-level state
|
|
1487
|
-
else {
|
|
1488
|
-
var encoded_v = proxy_encode_val(v)
|
|
1489
|
-
// console.log(' set top-level:', {v, encoded_v})
|
|
1490
|
-
|
|
1491
|
-
// Setting a top-level object to undefined wipes it out
|
|
1492
|
-
if (v === undefined)
|
|
1493
|
-
encoded_v = {key: k}
|
|
1494
|
-
|
|
1495
|
-
// Prefix with _: anything that is:
|
|
1496
|
-
else if (// A proxy to another state
|
|
1497
|
-
(typeof v === 'object' && v[symbols.is_proxy])
|
|
1498
|
-
// An empty {} object
|
|
1499
|
-
|| (typeof v === 'object' && Object.keys(v).length === 0)
|
|
1500
|
-
// A number, bool, string, function, etc
|
|
1501
|
-
|| typeof v !== 'object' || v === null
|
|
1502
|
-
// An array
|
|
1503
|
-
|| Array.isArray(v))
|
|
1504
|
-
encoded_v = {_: encoded_v}
|
|
1505
|
-
encoded_v.key = k
|
|
1506
|
-
|
|
1507
|
-
// console.log(' set top-level: now encoded_v is', encoded_v)
|
|
1508
|
-
bus.save(encoded_v)
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
var newbase = (encoded_v && encoded_v.key) ? encoded_v : base
|
|
1512
|
-
return true
|
|
1513
|
-
},
|
|
1514
|
-
has: function has(O, k) {
|
|
1515
|
-
// XXX QUESTIONS:
|
|
1516
|
-
//
|
|
1517
|
-
// - Do I want this to return true if there's a .to_fetch()
|
|
1518
|
-
// function for this o, k?
|
|
1519
|
-
//
|
|
1520
|
-
// - Does this need to do a fetch as well?
|
|
1521
|
-
//
|
|
1522
|
-
// - For a keyed object, should this do a loading() check?
|
|
1523
|
-
return o.hasOwnProperty(encode_field(k))
|
|
1524
|
-
},
|
|
1525
|
-
deleteProperty: function del (O, k) {
|
|
1526
|
-
if (base) {
|
|
1527
|
-
// console.log(' deleting:', encode_field(k), 'of', o)
|
|
1528
|
-
delete o[encode_field(k)] // Deleting innards
|
|
1529
|
-
if (Object.keys(o).length === 1 && o.key)
|
|
1530
|
-
o._ = {}
|
|
1531
|
-
bus.save(base)
|
|
1532
|
-
}
|
|
1533
|
-
else
|
|
1534
|
-
bus.delete(encode_field(k)) // Deleting top-level
|
|
1535
|
-
},
|
|
1536
|
-
apply: function apply (f, This, args) { return get_json() }
|
|
1537
1616
|
})
|
|
1538
1617
|
}
|
|
1539
|
-
if (nodejs || window.Proxy)
|
|
1540
|
-
var state = make_proxy(null, cache)
|
|
1541
|
-
|
|
1542
|
-
// So chrome can print out proxy objects decently
|
|
1543
|
-
if (!nodejs)
|
|
1544
|
-
window.devtoolsFormatters = [{
|
|
1545
|
-
header: function (x) {
|
|
1546
|
-
return x[symbols.is_proxy] &&
|
|
1547
|
-
['span', {style: 'background-color: #feb; padding: 3px;'},
|
|
1548
|
-
JSON.stringify(proxy_decode_json(x()))]
|
|
1549
|
-
},
|
|
1550
|
-
hasBody: function (x) {return false}
|
|
1551
|
-
}]
|
|
1552
1618
|
|
|
1553
1619
|
// ******************
|
|
1554
1620
|
// Network client
|
|
@@ -1779,10 +1845,10 @@
|
|
|
1779
1845
|
}
|
|
1780
1846
|
return obj
|
|
1781
1847
|
}
|
|
1782
|
-
function
|
|
1848
|
+
function underscore_escape(k) {
|
|
1783
1849
|
return k.replace(/(_(keys?|time)?$|^key$)/, '$1_')
|
|
1784
1850
|
}
|
|
1785
|
-
function
|
|
1851
|
+
function underscore_unescape (k) {
|
|
1786
1852
|
return k.replace(/(_$)/, '')
|
|
1787
1853
|
}
|
|
1788
1854
|
|
|
@@ -2205,10 +2271,11 @@
|
|
|
2205
2271
|
console.log.apply(console, arguments)
|
|
2206
2272
|
}
|
|
2207
2273
|
function statelog (key, color, icon, message) {
|
|
2208
|
-
if (bus.honking_colors === false)
|
|
2209
|
-
color = ''
|
|
2210
2274
|
if (honking_at(key))
|
|
2211
|
-
|
|
2275
|
+
if (bus.honking_colors === false)
|
|
2276
|
+
indented_log(icon + ' ' + message)
|
|
2277
|
+
else
|
|
2278
|
+
indented_log(color + icon + ' ' + message + normal)
|
|
2212
2279
|
}
|
|
2213
2280
|
function honking_at (key) {
|
|
2214
2281
|
return (bus.honk instanceof RegExp
|
|
@@ -2232,11 +2299,11 @@
|
|
|
2232
2299
|
var api = ['cache backup_cache fetch save forget del fire dirty fetch_once',
|
|
2233
2300
|
'subspace bindings run_handler bind unbind reactive uncallback',
|
|
2234
2301
|
'versions new_version',
|
|
2235
|
-
'make_proxy state sb',
|
|
2302
|
+
'make_proxy braid_proxy state sb',
|
|
2236
2303
|
'funk_key funk_name funks key_id key_name id',
|
|
2237
2304
|
'pending_fetches fetches_in fetches_out loading_keys loading once',
|
|
2238
2305
|
'global_funk busses rerunnable_funks',
|
|
2239
|
-
'
|
|
2306
|
+
'underscore_escape underscore_unescape translate_keys apply_patch',
|
|
2240
2307
|
'net_mount net_automount message_method',
|
|
2241
2308
|
'parse Set One_To_Many clone extend deep_map deep_equals prune validate sorta_diff log deps'
|
|
2242
2309
|
].join(' ').split(' ')
|