statebus 6.1.7 → 6.1.11

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.
Files changed (3) hide show
  1. package/package.json +2 -1
  2. package/server.js +76 -14
  3. package/statebus.js +379 -312
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statebus",
3
- "version": "6.1.7",
3
+ "version": "6.1.11",
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 their routes in here
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
- // Add a fallback that goes to state
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
- if (braidify)
132
- res.sendVersion({body: bus.to_http_body(o)})
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(bus.to_http_body(o))
147
+ res.send(body)
135
148
 
136
- if (!braidify || !req.subscribe)
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.to_http_body = (o) => {
143
- var unwrap = (Object.keys(o).length === 2
144
- && '_' in o
145
- && typeof o._ === 'string')
146
- // To do: translate pointers as keys
147
- return unwrap ? o._ : JSON.stringify(o)
148
- }
149
- bus.from_http_body = (o) => {}
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
- var sb = (function sb () {
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
- k = encode_field(k)
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[encode_field(k)] = v
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(encode_field(k))
1584
+ return o.hasOwnProperty(underscore_escape(k))
1227
1585
  },
1228
1586
  deleteProperty: function del (o, k) {
1229
- delete o[encode_field(k)]
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
- var raw = bus.fetch(k),
1242
- obj = raw
1243
- while (typeof obj == 'object' && '_' in obj) obj = obj._
1244
- return item_proxy(raw, obj)
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, k, v) {
1247
- if (typeof v === 'number'
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(encode_field(k))
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 encode_field(k) {
1848
+ function underscore_escape(k) {
1783
1849
  return k.replace(/(_(keys?|time)?$|^key$)/, '$1_')
1784
1850
  }
1785
- function decode_field (k) {
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.colored_honk === false)
2209
- color = ''
2210
2274
  if (honking_at(key))
2211
- indented_log(color + icon + ' ' + message + normal)
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
- 'encode_field decode_field translate_keys apply_patch',
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(' ')