statebus 6.2.3 → 6.2.5

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 (98) hide show
  1. package/client.js +2 -2
  2. package/extras/tests.js +60 -0
  3. package/package.json +9 -2
  4. package/server.js +4 -4
  5. package/statebus.js +39 -7
  6. package/extras/#test_save_delay.js# +0 -11
  7. package/extras/abort-test.html +0 -10
  8. package/extras/abort-test.js +0 -17
  9. package/extras/backups/db.2021-11-13 +0 -12
  10. package/extras/backups/db.2021-11-14 +0 -12
  11. package/extras/backups/db.2021-11-15 +0 -25
  12. package/extras/backups/db.2021-11-16 +0 -25
  13. package/extras/backups/db.2022-01-14 +0 -24
  14. package/extras/backups/db.2022-01-15 +0 -29
  15. package/extras/backups/db.2022-01-16 +0 -29
  16. package/extras/backups/db.2022-01-17 +0 -29
  17. package/extras/backups/db.2022-01-18 +0 -29
  18. package/extras/backups/db.2022-01-19 +0 -29
  19. package/extras/backups/db.2022-01-20 +0 -32
  20. package/extras/backups/db.2022-01-21 +0 -32
  21. package/extras/backups/db.2022-01-22 +0 -47
  22. package/extras/backups/db.2022-01-23 +0 -47
  23. package/extras/backups/db.2022-02-20 +0 -53
  24. package/extras/backups/db.2022-09-08 +0 -48
  25. package/extras/backups/db.2022-09-09 +0 -48
  26. package/extras/backups/db.2022-09-10 +0 -48
  27. package/extras/backups/db.2022-09-11 +0 -48
  28. package/extras/backups/db.2022-09-12 +0 -48
  29. package/extras/backups/db.2022-09-13 +0 -48
  30. package/extras/backups/db.2022-09-14 +0 -48
  31. package/extras/backups/pdb.2017-08-21 +0 -46
  32. package/extras/braid-client.html +0 -12
  33. package/extras/braid-client2.html +0 -9
  34. package/extras/braid-client3.html +0 -0
  35. package/extras/braid-server.js +0 -37
  36. package/extras/braid-server2.js +0 -35
  37. package/extras/chokitest.js +0 -142
  38. package/extras/clientless.js +0 -4
  39. package/extras/css.html +0 -4
  40. package/extras/ct.js +0 -24
  41. package/extras/ctests.js +0 -8
  42. package/extras/db +0 -48
  43. package/extras/ds.html +0 -8
  44. package/extras/ds.js +0 -17
  45. package/extras/email.html +0 -12
  46. package/extras/express-experiment.js +0 -15
  47. package/extras/f.js +0 -21
  48. package/extras/file-test.js +0 -12
  49. package/extras/flash.html +0 -8
  50. package/extras/flash.js +0 -7
  51. package/extras/fullnode-test.html +0 -4
  52. package/extras/fullnode-test.js +0 -26
  53. package/extras/hello-react/index.html +0 -24
  54. package/extras/hello-react.html +0 -4
  55. package/extras/hello.html +0 -4
  56. package/extras/k.js +0 -12
  57. package/extras/load-from-file.html +0 -4
  58. package/extras/multi-chat/1.html +0 -39
  59. package/extras/multicriteria +0 -52054
  60. package/extras/nested +0 -425965
  61. package/extras/new-react.html +0 -20
  62. package/extras/pdb +0 -46
  63. package/extras/perf.js +0 -68
  64. package/extras/perf2.js +0 -69
  65. package/extras/perf3.js +0 -14
  66. package/extras/pgt.js +0 -61
  67. package/extras/pointers-test.js +0 -17
  68. package/extras/pointers-test2.js +0 -83
  69. package/extras/ptest.js +0 -19
  70. package/extras/react-example.html +0 -32
  71. package/extras/react-example.jsx.html +0 -41
  72. package/extras/react.12.js +0 -18116
  73. package/extras/react.12.min.js +0 -16
  74. package/extras/react.15.min.js +0 -28
  75. package/extras/react.3.min.js +0 -19
  76. package/extras/remote-hello.html +0 -9
  77. package/extras/reset_pass.coffee +0 -101
  78. package/extras/reset_pass.html +0 -116
  79. package/extras/rocks-test.js +0 -6
  80. package/extras/s4.js +0 -2
  81. package/extras/sbdb +0 -4
  82. package/extras/slideboard +0 -4
  83. package/extras/smaller-file.js +0 -8
  84. package/extras/sqls-test.js +0 -6
  85. package/extras/stateb.us.coffee +0 -92
  86. package/extras/syncie.html +0 -31
  87. package/extras/syncie.js +0 -15
  88. package/extras/test2.js +0 -48
  89. package/extras/test_client.html +0 -8
  90. package/extras/test_client.js +0 -19
  91. package/extras/test_save_delay.js +0 -11
  92. package/extras/test_timeouts.js +0 -10
  93. package/extras/tests-val.js +0 -1572
  94. package/extras/tests.html +0 -5
  95. package/extras/uni.js +0 -7
  96. package/extras/usage_test.html +0 -3
  97. package/extras/usage_test.js +0 -32
  98. package/extras/vtest.js +0 -17
package/client.js CHANGED
@@ -461,7 +461,7 @@
461
461
  re_rendering = false
462
462
  function schedule_re_render() {
463
463
  if (!re_render_scheduled) {
464
- requestAnimationFrame(function () {
464
+ requestAnimationFrame(bus.extend_reaction(function () {
465
465
  re_render_scheduled = false
466
466
 
467
467
  // Re-renders dirty components
@@ -476,7 +476,7 @@
476
476
  re_rendering = false
477
477
  }
478
478
  }
479
- })
479
+ }))
480
480
  re_render_scheduled = true
481
481
  }
482
482
  }
package/extras/tests.js CHANGED
@@ -1644,6 +1644,66 @@ test(function common_time (done) {
1644
1644
  delay(50, done)
1645
1645
  })
1646
1646
 
1647
+ test(function closed_net_mount_stops_retrying (done) {
1648
+ // Nothing listens on port 1, so the socket never opens, and this fetch
1649
+ // waits in the outbox with a retry timer running
1650
+ var c = require('../statebus')()
1651
+ var mount = c.net_mount('/*', 'statei://localhost:1')
1652
+ c.fetch('/anything')
1653
+ mount.close()
1654
+
1655
+ // Count the retries over the next second
1656
+ var retries = 0
1657
+ var real_setTimeout = setTimeout
1658
+ setTimeout = function (f) {
1659
+ if (f.name == 'flush_outbox') retries++
1660
+ return real_setTimeout.apply(this, arguments)
1661
+ }
1662
+ real_setTimeout(function () {
1663
+ setTimeout = real_setTimeout
1664
+ assert(retries == 0, 'flush_outbox retried ' + retries + ' times after close()')
1665
+ done()
1666
+ }, 1000)
1667
+ })
1668
+
1669
+ test(function extend_reaction (done) {
1670
+ var forgotten = []
1671
+ function record_forget (key) { forgotten.push(key) }
1672
+ bus('extended/*').to_forget = record_forget
1673
+
1674
+ // Reads two keys, and reacts to a change by leaving its re-run for
1675
+ // later, the way client.js waits for an animation frame. The re-run
1676
+ // reads only one of the keys.
1677
+ var rerun, reading_b = true
1678
+ var funk = bus.reactive(function () {
1679
+ if (funk.called_directly) {
1680
+ bus.fetch('extended/a')
1681
+ if (reading_b) bus.fetch('extended/b')
1682
+ } else
1683
+ rerun = bus.extend_reaction(function () {
1684
+ reading_b = false
1685
+ funk()
1686
+ })
1687
+ })
1688
+ funk()
1689
+ bus.save({key: 'extended/a', n: 1})
1690
+
1691
+ // While the re-run waits, nothing is forgotten
1692
+ setTimeout(function () {
1693
+ assert(rerun, 'The change did not start a reaction')
1694
+ assert(forgotten.length == 0, 'Forgot ' + forgotten + ' before the re-run')
1695
+ rerun()
1696
+
1697
+ // Within a second after it, the key it stopped reading is forgotten
1698
+ setTimeout(function () {
1699
+ assert.deepEqual(forgotten, ['extended/b'])
1700
+ funk.forget()
1701
+ bus('extended/*').to_forget.delete(record_forget)
1702
+ done()
1703
+ }, 1100)
1704
+ }, 400)
1705
+ })
1706
+
1647
1707
 
1648
1708
  // The following tests are unfinished
1649
1709
  if (false) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statebus",
3
- "version": "6.2.3",
3
+ "version": "6.2.5",
4
4
  "description": "Synchronize state.",
5
5
  "main": "statebus.js",
6
6
  "scripts": {
@@ -11,7 +11,14 @@
11
11
  "statebus.js",
12
12
  "client.js",
13
13
  "server.js",
14
- "extras/",
14
+ "extras/coffee.js",
15
+ "extras/dash.coffee",
16
+ "extras/diffsync.js",
17
+ "extras/getCaretCoordinates.js",
18
+ "extras/react.js",
19
+ "extras/sockjs.js",
20
+ "extras/tcp-bind.js",
21
+ "extras/tests.js",
15
22
  "readme.md"
16
23
  ],
17
24
  "repository": "invisible-college/statebus",
package/server.js CHANGED
@@ -981,7 +981,8 @@ function import_server (bus, options)
981
981
  // https://github.com/JoshuaWise/better-sqlite3/blob/master/docs/api.md#backupdestination-options---promise
982
982
  function backup_db() {
983
983
  if (opts.backups === false) return
984
- var backup_dir = opts.backup_dir || 'backups'
984
+ var path = require('path')
985
+ var backup_dir = opts.backup_dir || path.join(path.dirname(opts.filename), 'backups')
985
986
  if (fs.existsSync && !fs.existsSync(backup_dir))
986
987
  fs.mkdirSync(backup_dir)
987
988
 
@@ -995,7 +996,7 @@ function import_server (bus, options)
995
996
 
996
997
  require('child_process').execFile(
997
998
  'sqlite3',
998
- [opts.filename, '.backup '+"'"+backup_dir+'/'+opts.filename+'.'+date+"'"])
999
+ [opts.filename, '.backup '+"'"+path.join(backup_dir, path.basename(opts.filename)+'.'+date)+"'"])
999
1000
  },
1000
1001
  1000 * 60 // Every minute
1001
1002
  )
@@ -2010,7 +2011,7 @@ function import_server (bus, options)
2010
2011
  // Validate that the login/name is not changed to something clobberish
2011
2012
  var old_login = u.login || u.name
2012
2013
  if (old_login.toLowerCase() !== login.toLowerCase()
2013
- && userpass.hasOwnProperty(login)) {
2014
+ && userpass.hasOwnProperty(login.toLowerCase())) {
2014
2015
  client.log('The login', login, 'is already taken. Aborting.')
2015
2016
  client.save.abort(o) // Abort
2016
2017
 
@@ -2364,7 +2365,6 @@ function import_server (bus, options)
2364
2365
 
2365
2366
  textbus.fetch(req.path) // So that textbus never clears the cache
2366
2367
  textbus.fetch(req.path, function cb (o) {
2367
- res.setHeader('Cache-Control', 'public')
2368
2368
  res.setHeader('Cache-Control', 'public, max-age=' + (60 * 60 * 6)) // 6 hours
2369
2369
  res.setHeader('ETag', o.etag)
2370
2370
  res.setHeader('Access-Control-Allow-Origin', '*')
package/statebus.js CHANGED
@@ -3,7 +3,7 @@
3
3
  if (typeof module != 'undefined') module.exports = definition()
4
4
  else if (typeof define == 'function' && typeof define.amd == 'object') define(definition)
5
5
  else this[name] = definition()
6
- }('statebus', function() {statelog_indent = 0; var busses = {}, executing_funk, global_funk, funks = {}, clean_timer, symbols, nodejs = typeof window === 'undefined'; function make_bus (options) {
6
+ }('statebus', function() {statelog_indent = 0; var busses = {}, executing_funk, global_funk, funks = {}, clean_timer, unfinished_extended_reactions = 0, symbols, nodejs = typeof window === 'undefined'; function make_bus (options) {
7
7
 
8
8
 
9
9
  // ****************
@@ -407,7 +407,14 @@
407
407
  // .on_fetch handler.
408
408
  if (!fetches_in.has_any(key)) {
409
409
  clearTimeout(to_be_forgotten[key])
410
- to_be_forgotten[key] = setTimeout(function () {
410
+ to_be_forgotten[key] = setTimeout(function run_forgetters () {
411
+ // Wait for any unfinished extended reaction, which may fetch
412
+ // the key again
413
+ if (unfinished_extended_reactions) {
414
+ to_be_forgotten[key] = setTimeout(run_forgetters, 1000)
415
+ return
416
+ }
417
+
411
418
  // Send a forget upstream
412
419
  bus.route(key, 'to_forget', key, t)
413
420
 
@@ -671,9 +678,9 @@
671
678
  param_names.push(param.slice(1))
672
679
  return prefix + '([^/]+)'
673
680
  }
674
- var regex = new RegExp('^'
675
- + pattern.replace(/(^|\/)(:[^/()]+)|(\*)/g, replace_param)
676
- + '$')
681
+ var regex = new RegExp(
682
+ '^' + pattern.replace(/(^|\/)(:[^/()]+)|(\*)/g, replace_param) + '$'
683
+ )
677
684
 
678
685
  return function (path) {
679
686
  var match = path.match(regex)
@@ -1190,6 +1197,31 @@
1190
1197
  // Tells you whether the currently executing funk is loading
1191
1198
  function loading () { return executing_funk.loading() }
1192
1199
 
1200
+ // A reaction normally ends when its reactive function returns. If it
1201
+ // leaves work for code that runs later, such as on the next animation
1202
+ // frame, wrap that code:
1203
+ //
1204
+ // requestAnimationFrame(bus.extend_reaction(f))
1205
+ //
1206
+ // The reaction then lasts until f has run, and the state it read stays
1207
+ // loaded until then. f must eventually run, and if it leaves work for
1208
+ // later too, wrap that as well. Fetches inside f don't make it reactive,
1209
+ // so read state in a reactive function that f calls.
1210
+ function extend_reaction (f) {
1211
+ unfinished_extended_reactions++
1212
+ var finished = false
1213
+ return function () {
1214
+ try {
1215
+ return f.apply(this, arguments)
1216
+ } finally {
1217
+ if (!finished) {
1218
+ finished = true
1219
+ unfinished_extended_reactions--
1220
+ }
1221
+ }
1222
+ }
1223
+ }
1224
+
1193
1225
  bus.default = function () {
1194
1226
  bus.deep_map(arguments, function (o) {
1195
1227
  if (o.key && !(bus.cache.hasOwnProperty(o.key)))
@@ -1809,7 +1841,7 @@
1809
1841
  // But normally we just send the message immediately
1810
1842
  else
1811
1843
  sock.send(outbox.shift())
1812
- else
1844
+ else if (!done)
1813
1845
  setTimeout(flush_outbox, 400)
1814
1846
  }
1815
1847
  function add_prefix (key) {
@@ -2488,7 +2520,7 @@
2488
2520
 
2489
2521
  // Make these private methods accessible
2490
2522
  var api = ['cache backup_cache fetch save forget del fire dirty fetch_once',
2491
- 'subspace bindings run_handler bind unbind reactive uncallback',
2523
+ 'subspace bindings run_handler bind unbind reactive extend_reaction uncallback',
2492
2524
  'versions new_version',
2493
2525
  'make_proxy braid_proxy state sb link',
2494
2526
  'funk_key funk_name funks key_id key_name id',
@@ -1,11 +0,0 @@
1
- bus = require('../server.js')({
2
- file_store: {save_delay: 1700},
3
- full_node: true
4
- })
5
- //bus.file_store('*', {save_delay: 1700})
6
- bus.honk = true
7
-
8
- setTimeout(function () {console.log('100 ms happened')}, 100)
9
- setTimeout(function () {console.log('500 ms happened')}, 500)
10
- setTimeout(function () {console.log('1000 ms happened')}, 1000)
11
- bus.save({key: 'foo', 3: 4})
@@ -1,10 +0,0 @@
1
- <script type=coffee>
2
- dom.BODY = -> 'hello!'
3
- bus.honk = 1
4
-
5
- fetch('/foo')
6
-
7
-
8
-
9
-
10
- </script><script src=https://stateb.us/client6.js server="ws://localhost:9999"></script>
@@ -1,17 +0,0 @@
1
- bus = require('../statebus').serve({
2
- port: 9999,
3
- // client: (client) => {
4
- // client('foo').to_save = (obj, t) => {
5
- // client.fetch(obj.key)
6
- // t.abort()
7
- // }
8
- // }
9
- })
10
-
11
- bus.honk = true
12
-
13
- bus('foo').to_save = (obj, t) => {
14
- delete obj.body
15
- t.done()
16
- t.abort()
17
- }
@@ -1,12 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "3": 4,
7
- "key": "foo"
8
- },
9
- "users/passwords": {
10
- "key": "users/passwords"
11
- }
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "3": 4,
7
- "key": "foo"
8
- },
9
- "users/passwords": {
10
- "key": "users/passwords"
11
- }
12
- }
@@ -1,25 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "3": 4,
7
- "key": "foo"
8
- },
9
- "users/passwords": {
10
- "key": "users/passwords"
11
- },
12
- "some_text": {
13
- "key": "some_text",
14
- "_": "Hello! This is the text."
15
- },
16
- "stuff": {
17
- "key": "stuff",
18
- "stuff": {
19
- "Yeah": "man",
20
- "this_is": {
21
- "the": "stuff!"
22
- }
23
- }
24
- }
25
- }
@@ -1,25 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "3": 4,
7
- "key": "foo"
8
- },
9
- "users/passwords": {
10
- "key": "users/passwords"
11
- },
12
- "some_text": {
13
- "key": "some_text",
14
- "_": "Hello! This is the text."
15
- },
16
- "stuff": {
17
- "key": "stuff",
18
- "stuff": {
19
- "Yeah": "man",
20
- "this_is": {
21
- "the": "stuff!"
22
- }
23
- }
24
- }
25
- }
@@ -1,24 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "key": "foo",
7
- "val": {
8
- "foo": 9
9
- }
10
- },
11
- "users/passwords": {
12
- "key": "users/passwords"
13
- },
14
- "some_text": {
15
- "key": "some_text",
16
- "val": "Hello! This is the text."
17
- },
18
- "stuff": {
19
- "key": "stuff",
20
- "val": {
21
- "hey man": "hell yeah stuff!"
22
- }
23
- }
24
- }
@@ -1,29 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "key": "foo",
7
- "val": {
8
- "foo": 9
9
- }
10
- },
11
- "users/passwords": {
12
- "key": "users/passwords"
13
- },
14
- "some_text": {
15
- "key": "some_text",
16
- "val": "Hello! This is the text."
17
- },
18
- "stuff": {
19
- "key": "stuff",
20
- "val": {
21
- "stuff": {
22
- "Yeah": "man",
23
- "this_is": {
24
- "the": "stuff!"
25
- }
26
- }
27
- }
28
- }
29
- }
@@ -1,29 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "key": "foo",
7
- "val": {
8
- "foo": 9
9
- }
10
- },
11
- "users/passwords": {
12
- "key": "users/passwords"
13
- },
14
- "some_text": {
15
- "key": "some_text",
16
- "val": "Hello! This is the text."
17
- },
18
- "stuff": {
19
- "key": "stuff",
20
- "val": {
21
- "stuff": {
22
- "Yeah": "man",
23
- "this_is": {
24
- "the": "stuff!"
25
- }
26
- }
27
- }
28
- }
29
- }
@@ -1,29 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "key": "foo",
7
- "val": {
8
- "foo": 9
9
- }
10
- },
11
- "users/passwords": {
12
- "key": "users/passwords"
13
- },
14
- "some_text": {
15
- "key": "some_text",
16
- "val": "Hello! This is the text."
17
- },
18
- "stuff": {
19
- "key": "stuff",
20
- "val": {
21
- "stuff": {
22
- "Yeah": "man",
23
- "this_is": {
24
- "the": "stuff!"
25
- }
26
- }
27
- }
28
- }
29
- }
@@ -1,29 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "key": "foo",
7
- "val": {
8
- "foo": 9
9
- }
10
- },
11
- "users/passwords": {
12
- "key": "users/passwords"
13
- },
14
- "some_text": {
15
- "key": "some_text",
16
- "val": "Hello! This is the text."
17
- },
18
- "stuff": {
19
- "key": "stuff",
20
- "val": {
21
- "stuff": {
22
- "Yeah": "man",
23
- "this_is": {
24
- "the": "stuff!"
25
- }
26
- }
27
- }
28
- }
29
- }
@@ -1,29 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections"
4
- },
5
- "foo": {
6
- "key": "foo",
7
- "val": {
8
- "foo": 9
9
- }
10
- },
11
- "users/passwords": {
12
- "key": "users/passwords"
13
- },
14
- "some_text": {
15
- "key": "some_text",
16
- "val": "Hello! This is the text."
17
- },
18
- "stuff": {
19
- "key": "stuff",
20
- "val": {
21
- "stuff": {
22
- "Yeah": "man",
23
- "this_is": {
24
- "the": "stuff!"
25
- }
26
- }
27
- }
28
- }
29
- }
@@ -1,32 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections",
4
- "6fab8674-ac94-4ea8-a055-3906d0cf0efd": {
5
- "client": "6fab8674-ac94-4ea8-a055-3906d0cf0efd",
6
- "id": "6fab8674-ac94-4ea8-a055-3906d0cf0efd"
7
- }
8
- },
9
- "foo": {
10
- "key": "foo",
11
- "val": 13
12
- },
13
- "users/passwords": {
14
- "key": "users/passwords"
15
- },
16
- "some_text": {
17
- "key": "some_text",
18
- "val": "Hello! This is the text."
19
- },
20
- "stuff": {
21
- "key": "stuff",
22
- "val": {
23
- "stuff": {
24
- "Yeah": "man",
25
- "this_is": {
26
- "the": "stuff!"
27
- }
28
- },
29
- "fluff": true
30
- }
31
- }
32
- }
@@ -1,32 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections",
4
- "c1eb2211-a43a-4efb-a26f-bf66d3f3f289": {
5
- "client": "c1eb2211-a43a-4efb-a26f-bf66d3f3f289",
6
- "id": "c1eb2211-a43a-4efb-a26f-bf66d3f3f289"
7
- }
8
- },
9
- "foo": {
10
- "key": "foo",
11
- "val": 3
12
- },
13
- "users/passwords": {
14
- "key": "users/passwords"
15
- },
16
- "some_text": {
17
- "key": "some_text",
18
- "val": "Hello! This is the text."
19
- },
20
- "stuff": {
21
- "key": "stuff",
22
- "val": {
23
- "stuff": {
24
- "Yeah": "man",
25
- "this_is": {
26
- "the": "stuff!"
27
- }
28
- },
29
- "fluff": true
30
- }
31
- }
32
- }
@@ -1,47 +0,0 @@
1
- {
2
- "connections": {
3
- "key": "connections",
4
- "780834b9-8898-407f-aca8-2c16a5b648d9": {
5
- "client": "780834b9-8898-407f-aca8-2c16a5b648d9",
6
- "id": "780834b9-8898-407f-aca8-2c16a5b648d9"
7
- }
8
- },
9
- "foo": {
10
- "key": "foo",
11
- "val": 3
12
- },
13
- "users/passwords": {
14
- "key": "users/passwords"
15
- },
16
- "some_text": {
17
- "key": "some_text",
18
- "val": "Hello! This is the text."
19
- },
20
- "stuff": {
21
- "key": "stuff",
22
- "val": {
23
- "stuff": {
24
- "Yeah": "man",
25
- "this_is": {
26
- "the": "stuff!"
27
- }
28
- },
29
- "fluff": true
30
- }
31
- },
32
- "bar": {
33
- "key": "bar",
34
- "val": {
35
- "me": "you"
36
- }
37
- },
38
- "link": {
39
- "key": "link",
40
- "val": {
41
- "key": "bar",
42
- "val": {
43
- "me": "you"
44
- }
45
- }
46
- }
47
- }