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 +1 -0
- package/extras/tests.js +133 -0
- package/package.json +1 -1
- package/statebus.js +105 -39
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
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 =
|
|
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[
|
|
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(
|
|
1229
|
+
return o.hasOwnProperty(escape_keys(k))
|
|
1230
1230
|
},
|
|
1231
1231
|
deleteProperty: function del (o, k) {
|
|
1232
|
-
delete o[
|
|
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(
|
|
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[
|
|
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[
|
|
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,
|
|
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[
|
|
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(
|
|
1526
|
+
return o.hasOwnProperty(escape_keys(k))
|
|
1527
1527
|
},
|
|
1528
1528
|
deleteProperty: function del (O, k) {
|
|
1529
1529
|
if (base) {
|
|
1530
|
-
// console.log(' deleting:',
|
|
1531
|
-
delete o[
|
|
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(
|
|
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')
|
|
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'
|
|
1585
|
+
if (k === 'inspect'
|
|
1586
|
+
|| k === 'valueOf' || typeof k === 'symbol')
|
|
1575
1587
|
return undefined
|
|
1576
|
-
return item_proxy(base, o[
|
|
1588
|
+
return item_proxy(base, o[proxied_2_keyed(k)])
|
|
1577
1589
|
},
|
|
1578
1590
|
set: function set(o, k, v) {
|
|
1579
|
-
var
|
|
1591
|
+
var value = translate_fields(v, proxied_2_keyed)
|
|
1592
|
+
o[proxied_2_keyed(k)] = value
|
|
1580
1593
|
bus.save(base)
|
|
1581
|
-
return
|
|
1594
|
+
return value
|
|
1582
1595
|
},
|
|
1583
1596
|
has: function has(o, k) {
|
|
1584
|
-
return o.hasOwnProperty(
|
|
1597
|
+
return o.hasOwnProperty(proxied_2_keyed(k))
|
|
1585
1598
|
},
|
|
1586
1599
|
deleteProperty: function del (o, k) {
|
|
1587
|
-
delete o[
|
|
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(
|
|
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
|
-
//
|
|
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
|
|
1867
|
+
function escape_keys(k) {
|
|
1849
1868
|
return k.replace(/(_(keys?|time)?$|^key$)/, '$1_')
|
|
1850
1869
|
}
|
|
1851
|
-
function
|
|
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
|
-
'
|
|
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(' ')
|