statebus 7.0.13 → 7.0.15

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-library.js CHANGED
@@ -40,10 +40,10 @@
40
40
  return new WebSocket(url + '/' + websocket_prefix + '/websocket')
41
41
  // return new SockJS(url + '/' + websocket_prefix)
42
42
  }
43
- function client_creds (server_url) {
43
+ bus.client_creds = function client_creds () {
44
44
  // This function is only used for websocket connections.
45
45
  // http connections set the cookie on the server.
46
- var me = bus.get('ls/me')
46
+ var me = JSON.parse(localStorage['ls/me'])
47
47
  bus.log('connect: me is', me)
48
48
  if (!me.client) {
49
49
  // Create a client id if we have none yet.
@@ -52,7 +52,7 @@
52
52
  me.client = c || (Math.random().toString(36).substring(2)
53
53
  + Math.random().toString(36).substring(2)
54
54
  + Math.random().toString(36).substring(2))
55
- bus.set(me)
55
+ localStorage['ls/me'] = JSON.stringify(me)
56
56
  }
57
57
 
58
58
  set_cookie('peer', me.client)
@@ -86,30 +86,40 @@
86
86
  send_put(id)
87
87
  }
88
88
  function send_put (id) {
89
+ var the_put = puts.get(id)
89
90
  try {
90
- puts.get(id).status = 'sending'
91
+ console.log('Sending a put!', {
92
+ patches: the_put.patches,
93
+ body: the_put.body
94
+ })
95
+
96
+ the_put.status = 'sending'
91
97
  braid_fetch(
92
- puts.get(id).url,
98
+ the_put.url,
93
99
  {
94
100
  method: 'put',
95
101
  headers: {
96
102
  'content-type': 'application/json',
97
103
  'put-order': id,
104
+ 'peer': bus.client_creds().clientid
98
105
  },
99
- body: puts.get(id).body
106
+ body: the_put.body,
107
+ patches: the_put.patches
100
108
  }
101
109
  ).then(function (res) {
102
110
  if (res.status !== 200)
103
- console.error('Server gave error on PUT:',
104
- e, 'for', puts.get(id).body)
111
+ console.error(
112
+ 'Server gave error on PUT:', e,
113
+ 'for', {body: the_put.body, patches: the_put.patches}
114
+ )
105
115
  puts.delete(id)
106
116
  }).catch(function (e) {
107
- console.error('Error on PUT, waiting...', puts.get(id).url)
108
- puts.get(id).status = 'waiting'
117
+ console.error(e, 'Error on PUT, waiting...', the_put.url)
118
+ the_put.status = 'waiting'
109
119
  })
110
120
  } catch (e) {
111
- console.error('Error on PUT, waiting...', puts.get(id).url)
112
- puts.get(id).status = 'waiting'
121
+ console.error(e, 'Error on PUT, waiting...', the_put.url)
122
+ the_put.status = 'waiting'
113
123
  }
114
124
  }
115
125
  function retry_put (id) {
@@ -127,13 +137,24 @@
127
137
  bus(prefix).setter = function (obj, t) {
128
138
  bus.set.fire(obj)
129
139
 
130
- var put = {
131
- url: url + rem_prefix(obj.key),
132
- body: JSON.stringify(obj.val)
133
- }
140
+ var put = {url: url + rem_prefix(obj.key)}
141
+
142
+ // Set the version and parents
134
143
  if (t.version) put.version = t.version
135
144
  if (t.parents) put.parents = t.parents
136
- if (t.patch) put.patch = t.patch
145
+
146
+ // Do we have patches?
147
+ if (t.patch) {
148
+ put.patches = t.patch.map(patch => {
149
+ var match = patch.match(/(.*) = (.*)/),
150
+ range = match[1],
151
+ content = match[2]
152
+ return {unit: 'json', range, content}
153
+ })
154
+ }
155
+ // Then we just have a simple body.
156
+ else put.body = JSON.stringify(obj.val)
157
+
137
158
  var put_id = put_counter++
138
159
  puts.set(put_id, put)
139
160
  send_put(put_id)
@@ -168,7 +189,20 @@
168
189
 
169
190
  function subscribe (key, t) {
170
191
  var aborter = new AbortController(),
171
- reconnect_attempts = 0
192
+ reconnect_attempts = 0,
193
+ reconnect = (e) => {
194
+ if (subscriptions[key].status === 'aborted') {
195
+ // Then this get is over and done with!
196
+ delete subscriptions[key]
197
+ return
198
+ }
199
+
200
+ // Else, reconnect!
201
+ setTimeout(() => subscribe(key, t),
202
+ reconnect_attempts > 0 ? 5000 : 1500)
203
+ subscriptions[key].status = 'reconnecting'
204
+ reconnect_attempts++
205
+ }
172
206
 
173
207
  // Start the subscription!
174
208
  braid_fetch(
@@ -180,35 +214,28 @@
180
214
  method: 'get',
181
215
  subscribe: true,
182
216
  headers: {accept: 'application/json'},
183
- signal: aborter.signal
184
- }
185
- ).andThen( function (new_version) {
186
- // New update received!
187
- if (subscriptions[key].status === 'connecting') {
188
- console.log('%c[*] opened ' + key,
189
- 'color: blue')
190
- reconnect_attempts = 0
191
- subscriptions[key].status = 'connected'
192
- send_all_puts()
193
- }
194
-
195
- // Return the update
196
- t.return({
197
- key: key,
198
- val: add_prefixes(JSON.parse(new_version.body))
199
- })
200
- }).catch( function (e) {
201
- if (subscriptions[key].status === 'aborted') {
202
- // Then this get is over and done with!
203
- delete subscriptions[key]
204
- return
217
+ signal: aborter.signal,
218
+ // credentials: 'include'
205
219
  }
220
+ ).then(res => res.subscribe(
221
+ new_version => {
222
+ // New update received!
223
+ if (subscriptions[key].status === 'connecting') {
224
+ console.log('%c[*] opened ' + key,
225
+ 'color: blue')
226
+ reconnect_attempts = 0
227
+ subscriptions[key].status = 'connected'
228
+ send_all_puts()
229
+ }
206
230
 
207
- // Reconnect!
208
- setTimeout(function () { subscribe(key, t) },
209
- reconnect_attempts > 0 ? 5000 : 1500)
210
- subscriptions[key].status = 'reconnecting'
211
- })
231
+ // Return the update
232
+ t.return({
233
+ key: key,
234
+ val: add_prefixes(JSON.parse(new_version.body))
235
+ })
236
+ },
237
+ reconnect
238
+ )).catch(reconnect)
212
239
 
213
240
  // Remember this subscription
214
241
  subscriptions[key] = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statebus",
3
- "version": "7.0.13",
3
+ "version": "7.0.15",
4
4
  "description": "Synchronize state.",
5
5
  "main": "statebus.js",
6
6
  "scripts": {
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "dependencies": {
21
21
  "bcrypt-nodejs": "0.0.3",
22
- "braidify": "^0.1.23",
22
+ "braid-http": "^0.1.8",
23
23
  "chokidar": "^3.4.0",
24
24
  "cookie": "^0.3.1",
25
25
  "express": "^4.15.3",
package/server-library.js CHANGED
@@ -1,7 +1,7 @@
1
1
  var fs = require('fs'),
2
2
  util = require('util')
3
3
 
4
- var braidify = require('braidify').http_server
4
+ var braidify = require('braid-http').http_server
5
5
 
6
6
 
7
7
  function default_options (bus) { return {
@@ -142,6 +142,8 @@ function import_server (bus, make_statebus, options)
142
142
  console.log(req.method, req.url, req.headers.subscribe
143
143
  ? 'Subscribe: ' + req.headers.subscribe : '')
144
144
 
145
+ braidify(req, res)
146
+
145
147
  // If this requests knows about Braid then we presume the programmer
146
148
  // knows that any client might make this cross-origin request.
147
149
  if (req.headers.version || req.headers.parents || req.headers.subscribe
@@ -166,12 +168,11 @@ function import_server (bus, make_statebus, options)
166
168
 
167
169
  // Handle the GET or PUT request!
168
170
  if (req.method === 'GET') {
169
- var key = req.path.substr(1)
171
+ var key = req.url.substr(1)
170
172
 
171
173
  // Make a temporary client bus
172
174
  var cbus = bus.bus_for_http_client(req, res)
173
175
 
174
- braidify(req, res)
175
176
  if (req.subscribe)
176
177
  res.startSubscription({ onClose: end_subscription })
177
178
  else
@@ -222,15 +223,21 @@ function import_server (bus, make_statebus, options)
222
223
  }
223
224
 
224
225
  else if (req.method === 'PUT') {
225
- // Maybe the new state is expressed in patches
226
- if (req.headers.patches) {
227
- console.error("We don't actually handle PUT patches yet")
228
226
 
229
- // var patches = await req.patches()
230
- // console.log("...but we see these patches:", patches)
227
+ // Maybe the new state is expressed in patches
228
+ if (req.headers.patches || req.headers['content-range']) {
231
229
 
232
- res.statusCode = 500
233
- res.end()
230
+ var patches = req.patches().then(patches => {
231
+ console.log("...and we see these patches:", patches)
232
+ var statebus_patches = patches.map(patch =>
233
+ patch.range + ' = ' + patch.content
234
+ )
235
+ var cbus = bus.bus_for_http_client(req, res)
236
+ cbus.set(req.url.substr(1), {patch: statebus_patches})
237
+ res.statusCode = 200
238
+ res.end
239
+ console.log('We just processed a patch!')
240
+ })
234
241
  } else {
235
242
  // Otherwise, we assume the body is content-type: json
236
243
  if (typeof req.headers['content-type'] !== 'string'
@@ -516,7 +523,7 @@ function import_server (bus, make_statebus, options)
516
523
  if (message.patch) {
517
524
  var o = bus.cache[message.set] || {key: message.set}
518
525
  try {
519
- message.set = bus.apply_patch(o, message.patch[0])
526
+ message.set = bus.apply_patch(o.val, message.patch[0])
520
527
  } catch (e) {
521
528
  console.error('Received bad sockjs message from '
522
529
  + conn.remoteAddress +': ', message, e)
@@ -2447,7 +2454,7 @@ function import_server (bus, make_statebus, options)
2447
2454
  ['extras/coffee.js', 'extras/sockjs.js',
2448
2455
  'statebus.js', 'client.js'].map((f) => fs.readFileSync('node_modules/statebus/' + f))
2449
2456
  files.unshift(fs.readFileSync(
2450
- 'node_modules/braidify/braidify-client.js'
2457
+ 'node_modules/braid-http/braid-http-client.js'
2451
2458
  ))
2452
2459
  res.send(files.join(';\n'))
2453
2460
  })
@@ -2539,7 +2546,7 @@ function import_server (bus, make_statebus, options)
2539
2546
  function free_the_cors (req, res, next) {
2540
2547
  var free_the_cors = {
2541
2548
  "Access-Control-Allow-Origin": "*",
2542
- "Access-Control-Allow-Methods": "OPTIONS, HEAD, GET, PUT, UNSUBSCRIBE",
2549
+ "Access-Control-Allow-Methods": "OPTIONS, HEAD, GET, PUT, DELETE, UNSUBSCRIBE",
2543
2550
  "Access-Control-Allow-Headers": "subscribe, peer, version, parents, merge-type, content-type, patches, cache-control, put-order"
2544
2551
  }
2545
2552
  Object.entries(free_the_cors).forEach(x => res.setHeader(x[0], x[1]))
package/statebus.js CHANGED
@@ -93,11 +93,24 @@
93
93
 
94
94
  var currently_saving
95
95
  function set (obj, t) {
96
- // First let's handle diffs
96
+ // First let's handle patches. We receive them as:
97
+ //
98
+ // set("foo", {patch: ['.foo.bar = "blah"]})
99
+ //
100
+ // Note the `obj` will actually be a string, set to the key in this case.
101
+ //
97
102
  if (typeof obj === 'string' && t && t.patch) {
98
103
  if (typeof t.patch == 'string') t.patch = [t.patch]
99
104
  // Apply the patch locally
100
- obj = apply_patch(bus.cache[obj] || {key: obj}, t.patch[0])
105
+ console.log('set: applying the patch!', {
106
+ key: obj,
107
+ val: bus.cache[obj],
108
+ patch: t.patch[0]
109
+ })
110
+ obj = {
111
+ key: obj,
112
+ val: apply_patch((bus.cache[obj] && bus.cache[obj].val), t.patch[0])
113
+ }
101
114
  }
102
115
 
103
116
  if (!('key' in obj) || typeof obj.key !== 'string') {
@@ -1272,7 +1285,7 @@
1272
1285
  get_base: Symbol('get_base')
1273
1286
  }
1274
1287
  function make_proxy () {
1275
- function item_proxy (base, o) {
1288
+ function item_proxy (base, path, o) {
1276
1289
 
1277
1290
  // Primitives pass through unscathed
1278
1291
  if (typeof o === 'number'
@@ -1287,7 +1300,7 @@
1287
1300
  // We recursively descend through {key: ...} links
1288
1301
  if (typeof o === 'object' && 'link' in o) {
1289
1302
  var new_base = bus.get(o.link)
1290
- return item_proxy(new_base, new_base.val)
1303
+ return item_proxy(new_base, '', new_base.val)
1291
1304
  }
1292
1305
 
1293
1306
 
@@ -1307,12 +1320,20 @@
1307
1320
  return true
1308
1321
  if (typeof k === 'symbol')
1309
1322
  return undefined
1310
- return item_proxy(base, o[proxied_2_keyed(k)])
1323
+
1324
+ // Compute the new path
1325
+ var new_path = path + '[' + JSON.stringify(k) + ']'
1326
+ return item_proxy(base, new_path, o[proxied_2_keyed(k)])
1311
1327
  },
1312
1328
  set: function set(o, k, v) {
1313
1329
  var value = translate_fields(v, proxied_2_keyed)
1314
1330
  o[proxied_2_keyed(k)] = value
1315
- bus.set(base)
1331
+ var new_path = path + '[' + JSON.stringify(k) + ']'
1332
+ bus.set(
1333
+ base,
1334
+ // Forward the patch too
1335
+ {patch: [new_path + ' = ' + JSON.stringify(v)]}
1336
+ )
1316
1337
  return true
1317
1338
  },
1318
1339
  has: function has(o, k) {
@@ -1320,6 +1341,7 @@
1320
1341
  },
1321
1342
  deleteProperty: function del (o, k) {
1322
1343
  delete o[proxied_2_keyed(k)]
1344
+ return true // Report success to Proxy
1323
1345
  }
1324
1346
  // For function proxies:
1325
1347
  //
@@ -1339,15 +1361,15 @@
1339
1361
  return undefined
1340
1362
  bogus_check(k)
1341
1363
  var base = bus.get(k)
1342
- return item_proxy(base, base.val)
1364
+ return item_proxy(base, '', base.val)
1343
1365
  },
1344
1366
  set: function set(o, key, val) {
1345
- bus.set({key: key,
1346
- val: translate_fields(val, proxied_2_keyed)})
1367
+ bus.set({key: key, val: translate_fields(val, proxied_2_keyed)})
1347
1368
  return true
1348
1369
  },
1349
1370
  deleteProperty: function del (o, k) {
1350
1371
  bus.delete(proxied_2_keyed(k))
1372
+ return true // Report success to Proxy
1351
1373
  },
1352
1374
  // For function proxies:
1353
1375
  // apply: function apply (o, This, args) {
@@ -1552,11 +1574,25 @@
1552
1574
  var t = {version: message.version,
1553
1575
  parents: message.parents,
1554
1576
  patch: message.patch}
1555
- if (t.patch)
1556
- msg.set = apply_patch(bus.cache[msg.set] || {key: msg.set},
1557
- message.patch[0])
1577
+
1578
+ var obj
1579
+
1580
+ // Are we receiving a patch?
1581
+ if (message.patch && typeof message.set === 'string') {
1582
+ // Then message.set is a key, and we are applying a
1583
+ // patch to the data at that key
1584
+ var key = message.set
1585
+ obj = apply_patch(bus.cache[key] && bus.cache[key].val,
1586
+ message.patch[0])
1587
+ }
1588
+
1589
+ // Then we're receiving the full state as an object
1590
+ else
1591
+ obj = message.set
1592
+
1558
1593
  if (!(t.version||t.parents||t.patch))
1559
1594
  t = undefined
1595
+
1560
1596
  bus.set.fire(add_prefixes(message.set), t)
1561
1597
  } catch (err) {
1562
1598
  console.error('Received bad network message from '
@@ -1726,25 +1762,30 @@
1726
1762
  path = x[1],
1727
1763
  new_stuff = JSON.parse(x[2])
1728
1764
 
1729
- var path_segment = /^(\.([^\.\[]+))|(\[((-?\d+):)?(-?\d+)\])/
1765
+ var path_segment = /^(\.?([^\.\[]+))|(\[((-?\d+):)?(-?\d+)\])|\[("(\\"|[^"])*")\]/
1730
1766
  var curr_obj = obj,
1731
1767
  last_obj = null
1732
- function de_neg (x) {
1733
- return x[0] === '-'
1734
- ? curr_obj.length - parseInt(x.substr(1))
1735
- : parseInt(x)
1768
+ function de_neg (numstr) {
1769
+ return numstr[0] === '-'
1770
+ ? curr_obj.length - parseInt(numstr.substr(1))
1771
+ : parseInt(numstr)
1736
1772
  }
1737
1773
 
1774
+ console.log('Getting path', path, 'in obj', obj)
1775
+
1738
1776
  while (true) {
1739
1777
  var match = path_segment.exec(path),
1740
- subpath = match[0],
1741
- field = match[2],
1742
- slice_start = match[5],
1743
- slice_end = match[6]
1778
+ subpath = match ? match[0] : '',
1779
+ field = match && match[2],
1780
+ slice_start = match && match[5],
1781
+ slice_end = match && match[6],
1782
+ quoted_field = match && match[7]
1744
1783
 
1745
1784
  slice_start = slice_start && de_neg(slice_start)
1746
1785
  slice_end = slice_end && de_neg(slice_end)
1747
1786
 
1787
+ if (quoted_field) field = JSON.parse(quoted_field)
1788
+
1748
1789
  // console.log('Descending', {curr_obj, path, subpath, field, slice_start, slice_end, last_obj})
1749
1790
 
1750
1791
  // If it's the final item, set it