statebus 6.1.12 → 6.1.13

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 CHANGED
@@ -60,6 +60,7 @@
60
60
  function braid_http_mount (prefix, url) {
61
61
  var preprefix = prefix.slice(0,-1)
62
62
  var has_prefix = new RegExp('^' + preprefix)
63
+ var is_absolute = /^https?:\/\//
63
64
  var client_fetched_keys = new bus.Set()
64
65
  if (url[url.length-1]=='/') url = url.substr(0,url.length-1)
65
66
  function add_prefix (key) {
package/extras/tests.js CHANGED
@@ -294,6 +294,43 @@ test(function url_translation (done) {
294
294
  done()
295
295
  })
296
296
 
297
+ test(function translate_fields (done) {
298
+ // Translate Statebus -> Proxy format
299
+ var tests1 = [
300
+ [{key: 'foo', link: 'boo'}, {key: 'foo', link: 'boo'}],
301
+ [{_key: 'foo', link: 'boo'}, {key: 'foo', link: 'boo'}],
302
+ [{__key: 'foo', link: 'boo'}, {_key: 'foo', link: 'boo'}],
303
+ [[{_key: 'foo', link: 'boo'}], [{key: 'foo', link: 'boo'}]],
304
+ [{a: [{_key: 'foo'}]}, {a: [{key: 'foo'}]}]
305
+ ]
306
+
307
+ // Translate Proxy -> Statebus format
308
+ var tests2 = [
309
+ [{key: 'foo', link: 'boo'}, {_key: 'foo', link: 'boo'}],
310
+ [{_key: 'foo', link: 'boo'}, {__key: 'foo', link: 'boo'}],
311
+ [[{_key: 'foo', link: ['boo']}], [{__key: 'foo', link: ['boo']}]]
312
+ ]
313
+
314
+ // Test 1
315
+ var tests = tests1
316
+ for (var i=0; i<tests.length; i++) {
317
+ var trans = bus.translate_fields(tests[i][0], bus.keyed_2_proxied)
318
+ assert(bus.deep_equals(trans, tests[i][1]),
319
+ 'Bad translation1: ' + i + ' ' + JSON.stringify(trans))
320
+ }
321
+
322
+ // Test 2
323
+ tests = tests2
324
+ for (var i=0; i<tests2.length; i++) {
325
+ var trans = bus.translate_fields(tests[i][0], bus.proxied_2_keyed)
326
+ assert(bus.deep_equals(trans, tests[i][1]),
327
+ 'Bad translation2: ' + i + ' ' + JSON.stringify(trans))
328
+ }
329
+
330
+ log('Passed translate_fields')
331
+ done()
332
+ })
333
+
297
334
  test(function basics (done) {
298
335
  bus('basic wait').to_fetch = function () {
299
336
  setTimeout(function () {bus.save.fire({key:'basic wait', a:1})},
@@ -834,6 +871,102 @@ test(function proxies2 (done) {
834
871
  // Getting a normal property should do a fetch
835
872
  })
836
873
 
874
+ test(function braid_proxies (done) {
875
+ var bus = require('../statebus').serve({braid_mode_test: true,
876
+ file_store: false})
877
+ var state = bus.state
878
+
879
+ assert(state.array === undefined)
880
+ assert(state.bar === undefined)
881
+
882
+ state.array = []
883
+ log('array:', state.array)
884
+ assert(state.array.length === 0)
885
+ state.array[0] = 1
886
+ log('array:', state.array)
887
+ assert(state.array.length === 1)
888
+ state.bar = {}
889
+ log('bar:', state.bar)
890
+ state.bar = {a: 1}
891
+ log('bar:', state.bar)
892
+ assert(state.bar.a === 1)
893
+ state.bar.a = state.array
894
+ log('bar:', state.bar)
895
+ state.array[1] = 2
896
+ log('array:', state.array)
897
+ log('bar:', state.bar)
898
+ assert(state.bar.a[1] !== 2,
899
+ "Array ref linked.\n\t"
900
+ + JSON.stringify(bus.cache.bar) + '\n\t'
901
+ + JSON.stringify(bus.cache.array))
902
+
903
+ state.undefining = undefined
904
+ assert(state.undefining === undefined)
905
+ state.undefining = {a: undefined}
906
+ log('state.undefining =', state.undefining)
907
+ // assert(state.undefining.a === undefined)
908
+ // TODO: fix https://github.com/invisible-college/statebus/issues/34
909
+ state.undefining = {}
910
+ assert(!('a' in state.undefining))
911
+ state.undefining.a = undefined
912
+ assert('a' in state.undefining)
913
+ assert(state.undefining.a === undefined)
914
+
915
+ state.b = {a: undefined}
916
+ assert('a' in state.b)
917
+ assert(state.b.a === undefined)
918
+ delete state.b.a
919
+ // TODO: https://github.com/invisible-college/statebus/issues/34
920
+ assert(!('a' in state.b))
921
+ assert(state.b.a === undefined)
922
+
923
+ return done()
924
+
925
+ /*
926
+ Things I want to test:
927
+
928
+ - Setting nested items
929
+ - Escaping their fields
930
+ - Calling fetch on them
931
+ - Converting state[..] to keyed objects internally
932
+ - has() potentially doing a fetch, or loading()
933
+ - set() returning a proxy object
934
+ - console output
935
+ - node AND chrome
936
+ - having nice colors and distinctions and shit
937
+ */
938
+
939
+ state.foo = 3
940
+ // This should set into _
941
+ console.assert(bus.validate(bus.cache.foo,
942
+ {key: 'foo', _: 3}))
943
+
944
+
945
+ state.foo = {a: 5}
946
+ // This should set directly on it
947
+ console.assert(bus.validate(bus.cache.foo,
948
+ {key: 'foo', a: 5}))
949
+ state.foo.b = 6
950
+ console.assert(bus.validate(bus.cache.foo,
951
+ {key: 'foo', b: 6}))
952
+
953
+ state.bar = [3]
954
+ state.foo = {a: 3, bar: state.bar}
955
+
956
+ bus(() => {
957
+ state.foo // foo triggers re-render
958
+ state.foo.bar // bar triggers re-render
959
+ state.foo.a // triggers re-render too
960
+ })
961
+
962
+ // Getting a linked item should do a fetch
963
+ bus(() => {
964
+ state.bar
965
+ })
966
+
967
+ // Getting a normal property should do a fetch
968
+ })
969
+
837
970
  test(function only_one (done) {
838
971
  bus('only_one/*').to_fetch = function (k) {
839
972
  var id = k[k.length-1]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statebus",
3
- "version": "6.1.12",
3
+ "version": "6.1.13",
4
4
  "description": "Synchronize state.",
5
5
  "main": "statebus.js",
6
6
  "scripts": {
package/statebus.js CHANGED
@@ -1217,19 +1217,19 @@
1217
1217
  get: function get(o, k) {
1218
1218
  if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
1219
1219
  return undefined
1220
- k = underscore_escape(k)
1220
+ k = escape_keys(k)
1221
1221
  return item_proxy(base, o[k])
1222
1222
  },
1223
1223
  set: function set(o, k, v) {
1224
- var result = o[underscore_escape(k)] = v
1224
+ var result = o[escape_keys(k)] = v
1225
1225
  bus.save(base)
1226
1226
  return result
1227
1227
  },
1228
1228
  has: function has(o, k) {
1229
- return o.hasOwnProperty(underscore_escape(k))
1229
+ return o.hasOwnProperty(escape_keys(k))
1230
1230
  },
1231
1231
  deleteProperty: function del (o, k) {
1232
- delete o[underscore_escape(k)]
1232
+ delete o[escape_keys(k)]
1233
1233
  },
1234
1234
  apply: function apply (o, This, args) {
1235
1235
  return o
@@ -1268,7 +1268,7 @@
1268
1268
  // },
1269
1269
  // ... but I haven't had a need yet.
1270
1270
  deleteProperty: function del (o, k) {
1271
- bus.delete(underscore_escape(k))
1271
+ bus.delete(escape_keys(k))
1272
1272
  }
1273
1273
  })
1274
1274
  })()
@@ -1362,7 +1362,7 @@
1362
1362
  // Actual objects need their keys translated
1363
1363
  var result = {}
1364
1364
  for (var k in x)
1365
- result[underscore_escape(k)] = proxy_encode_val(x[k])
1365
+ result[escape_keys(k)] = proxy_encode_val(x[k])
1366
1366
  return result
1367
1367
  }
1368
1368
 
@@ -1396,7 +1396,7 @@
1396
1396
  var obj = {}
1397
1397
  for (var k in json)
1398
1398
  if (k !== 'key')
1399
- obj[underscore_unescape(k)] = proxy_decode_json(json[k])
1399
+ obj[unescape_keys(k)] = proxy_decode_json(json[k])
1400
1400
  return obj
1401
1401
  }
1402
1402
 
@@ -1452,7 +1452,7 @@
1452
1452
  return undefined
1453
1453
  }
1454
1454
 
1455
- var tmp2 = pget(base, o, underscore_escape(k))
1455
+ var tmp2 = pget(base, o, escape_keys(k))
1456
1456
  var base2 = tmp2[0]
1457
1457
  var o2 = tmp2[1]
1458
1458
 
@@ -1463,7 +1463,7 @@
1463
1463
  // console.log('set:', {base, o, k, v})
1464
1464
 
1465
1465
  if (base) {
1466
- var encoded_v = o[underscore_escape(k)] = proxy_encode_val(v)
1466
+ var encoded_v = o[escape_keys(k)] = proxy_encode_val(v)
1467
1467
  // console.log(' set: saving', encoded_v, 'into', base)
1468
1468
 
1469
1469
  // Collapse state of the form:
@@ -1523,18 +1523,18 @@
1523
1523
  // - Does this need to do a fetch as well?
1524
1524
  //
1525
1525
  // - For a keyed object, should this do a loading() check?
1526
- return o.hasOwnProperty(underscore_escape(k))
1526
+ return o.hasOwnProperty(escape_keys(k))
1527
1527
  },
1528
1528
  deleteProperty: function del (O, k) {
1529
1529
  if (base) {
1530
- // console.log(' deleting:', underscore_escape(k), 'of', o)
1531
- delete o[underscore_escape(k)] // Deleting innards
1530
+ // console.log(' deleting:', escape_keys(k), 'of', o)
1531
+ delete o[escape_keys(k)] // Deleting innards
1532
1532
  if (Object.keys(o).length === 1 && o.key)
1533
1533
  o._ = {}
1534
1534
  bus.save(base)
1535
1535
  }
1536
1536
  else
1537
- bus.delete(underscore_escape(k)) // Deleting top-level
1537
+ bus.delete(escape_keys(k)) // Deleting top-level
1538
1538
  },
1539
1539
  apply: function apply (f, This, args) { return get_json() }
1540
1540
  })
@@ -1556,41 +1556,56 @@
1556
1556
 
1557
1557
  // ******************
1558
1558
  // Braid Test Mode Proxy
1559
- function braid_proxy () {
1560
- // I have the cache behind the scenes
1561
- // Each proxy has a target object -- the raw data on cache
1562
- // If we're proxying a {_: ...} singleton then ...
1563
1559
 
1560
+ var symbols = {is_proxy: Symbol('is_proxy'),
1561
+ is_link: Symbol('is_link'),
1562
+ get_json: Symbol('get_json'),
1563
+ get_base: Symbol('get_base')}
1564
+ function braid_proxy () {
1564
1565
  function item_proxy (base, o) {
1566
+
1567
+ // We only create a proxy for objects.
1565
1568
  if (typeof o === 'number'
1566
1569
  || typeof o === 'string'
1567
1570
  || typeof o === 'boolean'
1568
1571
  || o === undefined
1569
1572
  || o === null
1570
- || typeof o === 'function') return o
1573
+ || typeof o === 'function')
1574
+ // Everything else passes through unscathed
1575
+ return o
1576
+
1577
+ // We recursively descend through {key: ...} links
1578
+ if (typeof o === 'object' && 'key' in o) {
1579
+ var new_base = bus.fetch(o.key)
1580
+ return item_proxy(new_base, new_base.val)
1581
+ }
1571
1582
 
1572
1583
  return new Proxy(o, {
1573
1584
  get: function get(o, k) {
1574
- if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
1585
+ if (k === 'inspect'
1586
+ || k === 'valueOf' || typeof k === 'symbol')
1575
1587
  return undefined
1576
- return item_proxy(base, o[underscore_escape(k)])
1588
+ return item_proxy(base, o[proxied_2_keyed(k)])
1577
1589
  },
1578
1590
  set: function set(o, k, v) {
1579
- var result = o[underscore_escape(k)] = v
1591
+ var value = translate_fields(v, proxied_2_keyed)
1592
+ o[proxied_2_keyed(k)] = value
1580
1593
  bus.save(base)
1581
- return result
1594
+ return value
1582
1595
  },
1583
1596
  has: function has(o, k) {
1584
- return o.hasOwnProperty(underscore_escape(k))
1597
+ return o.hasOwnProperty(proxied_2_keyed(k))
1585
1598
  },
1586
1599
  deleteProperty: function del (o, k) {
1587
- delete o[underscore_escape(k)]
1600
+ delete o[proxied_2_keyed(k)]
1588
1601
  },
1589
1602
  apply: function apply (o, This, args) {
1590
1603
  return o
1591
1604
  }
1592
1605
  })}
1593
1606
 
1607
+
1608
+ // The top-level Proxy object holds HTTP resources
1594
1609
  return new Proxy(cache, {
1595
1610
  get: function get(o, k) {
1596
1611
  if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
@@ -1601,21 +1616,25 @@
1601
1616
  return item_proxy(base, base.val)
1602
1617
  },
1603
1618
  set: function set(o, key, val) {
1604
- bus.save({key, val})
1619
+ bus.save({key: key, val: val})
1605
1620
  },
1606
- // In future, this might check if there's a .to_fetch function OR
1607
- // something in the cache:
1608
- //
1609
- // has: function has(o, k) {
1610
- // return k in o
1611
- // },
1612
- // ... but I haven't had a need yet.
1613
1621
  deleteProperty: function del (o, k) {
1614
- bus.delete(underscore_escape(k))
1622
+ bus.delete(proxied_2_keyed(k))
1615
1623
  }
1616
1624
  })
1617
1625
  }
1618
1626
 
1627
+ function link (url) {
1628
+ var result = fetch(url)
1629
+ result[symbols.is_link] = true
1630
+ return result
1631
+ }
1632
+
1633
+ if ((nodejs && bus.options && bus.options.braid_mode_test)
1634
+ || (!nodejs && window.Proxy && clientjs_option('braid_mode_test')))
1635
+ state = braid_proxy()
1636
+
1637
+
1619
1638
  // ******************
1620
1639
  // Network client
1621
1640
  function get_domain (key) { // Returns e.g. "state://foo.com"
@@ -1822,8 +1841,8 @@
1822
1841
  }
1823
1842
 
1824
1843
 
1825
- // ******************
1826
- // Key translation
1844
+ // ************************************************
1845
+ // Translating the URLs under keys of state
1827
1846
  function translate_keys (obj, f) {
1828
1847
  // Recurse through each element in arrays
1829
1848
  if (Array.isArray(obj))
@@ -1845,14 +1864,60 @@
1845
1864
  }
1846
1865
  return obj
1847
1866
  }
1848
- function underscore_escape(k) {
1867
+ function escape_keys(k) {
1849
1868
  return k.replace(/(_(keys?|time)?$|^key$)/, '$1_')
1850
1869
  }
1851
- function underscore_unescape (k) {
1870
+ function unescape_keys (k) {
1852
1871
  return k.replace(/(_$)/, '')
1853
1872
  }
1854
1873
 
1855
1874
 
1875
+ // ************************************************
1876
+ // Translating fields of objects
1877
+
1878
+ // Recurse through JSON and swap all object fields with other field names,
1879
+ // according to the function f(field, object). Returns a copy.
1880
+ //
1881
+ // f(field, object) takes a string `field` and returns the new field name
1882
+ // for `object`.
1883
+ function translate_fields (input, f) {
1884
+ var result
1885
+
1886
+ // Recurse through each element in arrays
1887
+ if (Array.isArray(input)) {
1888
+ var new_array = input.slice()
1889
+ for (var i=0; i < input.length; i++)
1890
+ new_array[i] = translate_fields(input[i], f)
1891
+ result = new_array
1892
+ }
1893
+
1894
+ // Recurse through each property on objects
1895
+ else if (typeof input === 'object') {
1896
+ var new_obj = {}
1897
+ for (var k in input)
1898
+ new_obj[f(k, input)] = translate_fields(input[k], f)
1899
+ result = new_obj
1900
+ }
1901
+
1902
+ // Everything else passes through unscathed
1903
+ else
1904
+ result = input
1905
+
1906
+ return result
1907
+ }
1908
+ // Remove underscore from _key
1909
+ function keyed_2_proxied (k, object) {
1910
+ if (object[symbols.is_link])
1911
+ return k
1912
+ else
1913
+ return k.replace(/^(_*)_key$/, '$1key')
1914
+ }
1915
+ // Add underscore to key
1916
+ function proxied_2_keyed (k) {
1917
+ return k.replace(/^(_*)key$/, '$1_key')
1918
+ }
1919
+
1920
+
1856
1921
  // ******************
1857
1922
  // JSON encoding
1858
1923
  // - Does both escape/unescape keys, and wraps/unwraps pointers
@@ -2299,11 +2364,12 @@
2299
2364
  var api = ['cache backup_cache fetch save forget del fire dirty fetch_once',
2300
2365
  'subspace bindings run_handler bind unbind reactive uncallback',
2301
2366
  'versions new_version',
2302
- 'make_proxy braid_proxy state sb',
2367
+ 'make_proxy braid_proxy state sb link',
2303
2368
  'funk_key funk_name funks key_id key_name id',
2304
2369
  'pending_fetches fetches_in fetches_out loading_keys loading once',
2305
2370
  'global_funk busses rerunnable_funks',
2306
- 'underscore_escape underscore_unescape translate_keys apply_patch',
2371
+ 'escape_keys unescape_keys translate_keys apply_patch',
2372
+ 'keyed_2_proxied proxied_2_keyed translate_fields',
2307
2373
  'net_mount net_automount message_method',
2308
2374
  'parse Set One_To_Many clone extend deep_map deep_equals prune validate sorta_diff log deps'
2309
2375
  ].join(' ').split(' ')