statebus 7.0.14 → 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 +72 -45
- package/package.json +2 -2
- package/server-library.js +19 -12
- package/statebus.js +62 -21
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 (
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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(
|
|
104
|
-
|
|
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...',
|
|
108
|
-
|
|
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...',
|
|
112
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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.
|
|
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
|
-
"
|
|
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('
|
|
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
|
|
@@ -171,7 +173,6 @@ function import_server (bus, make_statebus, options)
|
|
|
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
|
-
|
|
230
|
-
|
|
227
|
+
// Maybe the new state is expressed in patches
|
|
228
|
+
if (req.headers.patches || req.headers['content-range']) {
|
|
231
229
|
|
|
232
|
-
|
|
233
|
-
|
|
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/
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
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 = /^(
|
|
1765
|
+
var path_segment = /^(\.?([^\.\[]+))|(\[((-?\d+):)?(-?\d+)\])|\[("(\\"|[^"])*")\]/
|
|
1730
1766
|
var curr_obj = obj,
|
|
1731
1767
|
last_obj = null
|
|
1732
|
-
function de_neg (
|
|
1733
|
-
return
|
|
1734
|
-
? curr_obj.length - parseInt(
|
|
1735
|
-
: parseInt(
|
|
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
|