statebus 6.1.12 → 6.1.16
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/server.js +119 -76
- package/statebus.js +119 -43
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/server.js
CHANGED
|
@@ -2,6 +2,15 @@ var fs = require('fs'),
|
|
|
2
2
|
util = require('util')
|
|
3
3
|
var unique_sockjs_string = '_connect_to_statebus_'
|
|
4
4
|
|
|
5
|
+
// Import braidify if it's available
|
|
6
|
+
try {
|
|
7
|
+
var braidify = require('braidify').http_server
|
|
8
|
+
console.log('Found braidify library. Braid-HTTP enabled!')
|
|
9
|
+
} catch (e) {
|
|
10
|
+
var braidify = undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
5
14
|
function default_options (bus) { return {
|
|
6
15
|
port: 'auto',
|
|
7
16
|
backdoor: null,
|
|
@@ -68,6 +77,7 @@ function import_server (bus, options)
|
|
|
68
77
|
bus.express = express()
|
|
69
78
|
bus.http = express.Router()
|
|
70
79
|
bus.install_express(bus.express)
|
|
80
|
+
bus.initialize_braid_http()
|
|
71
81
|
|
|
72
82
|
// use gzip compression if available
|
|
73
83
|
try { bus.http.use(require('compression')())
|
|
@@ -99,16 +109,94 @@ function import_server (bus, options)
|
|
|
99
109
|
// User will put his routes in here
|
|
100
110
|
bus.express.use('/', bus.http)
|
|
101
111
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
112
|
+
// Free the CORS for Braid requests!
|
|
113
|
+
bus.express.use((req, res, next) => {
|
|
114
|
+
// If this requests knows about Braid then we presume the
|
|
115
|
+
// programmer knows that any client might make this cross-origin
|
|
116
|
+
// request.
|
|
117
|
+
if (req.version || req.parents || req.subscribe
|
|
118
|
+
|| req.headers.subscribe
|
|
119
|
+
|| req.method === 'OPTIONS')
|
|
120
|
+
free_the_cors(req, res, next)
|
|
121
|
+
else
|
|
122
|
+
next()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Connect bus to the HTTP server
|
|
126
|
+
bus.express.use(bus.http_handlers)
|
|
127
|
+
|
|
128
|
+
// Serve Client Coffee
|
|
129
|
+
bus.serve_client_coffee()
|
|
130
|
+
|
|
131
|
+
// Custom router
|
|
132
|
+
bus.route = function server_route (key, method, arg, t) {
|
|
133
|
+
var handlers = bus.bindings(key, method)
|
|
134
|
+
var count = handlers.length
|
|
135
|
+
if (count)
|
|
136
|
+
bus.log('route:', bus+'("'+key+'").'+method+'['+handlers.length+'](key:"'+(arg.key||arg)+'")')
|
|
137
|
+
|
|
138
|
+
// Call all handlers!
|
|
139
|
+
for (var i=0; i<handlers.length; i++)
|
|
140
|
+
bus.run_handler(handlers[i].func, method, arg, {t: t, binding: handlers[i].key})
|
|
141
|
+
|
|
142
|
+
// Now handle backup handlers
|
|
143
|
+
if (count === 0) {
|
|
144
|
+
|
|
145
|
+
// A save to master without a to_save defined should be
|
|
146
|
+
// automatically reflected to all clients.
|
|
147
|
+
if (method === 'to_save') {
|
|
148
|
+
bus.save.fire(arg, t)
|
|
149
|
+
bus.route(arg.key, 'on_set_sync', arg, t)
|
|
150
|
+
count++
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// A fetch to master without a to_fetch defined should go to
|
|
154
|
+
// the database, if we have one.
|
|
155
|
+
if (method === 'to_fetch') {
|
|
156
|
+
bus.db_fetch(key)
|
|
157
|
+
// This basically renders the count useless-- you probably
|
|
158
|
+
// don't want to wrap this route() with another route().
|
|
159
|
+
count++
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return handlers.length
|
|
108
163
|
}
|
|
109
164
|
|
|
110
|
-
//
|
|
111
|
-
bus.
|
|
165
|
+
// Back door to the control room
|
|
166
|
+
if (bus.options.backdoor) {
|
|
167
|
+
bus.make_http_server({
|
|
168
|
+
port: bus.options.backdoor,
|
|
169
|
+
name: 'backdoor_http_server',
|
|
170
|
+
use_ssl: use_ssl
|
|
171
|
+
})
|
|
172
|
+
bus.sockjs_server(this.backdoor_http_server)
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
initialize_braid_http: function () {
|
|
177
|
+
// Set up default linked json converters
|
|
178
|
+
if (bus.options.braid_mode_test) {
|
|
179
|
+
bus.to_http_body = (o) => JSON.stringify(o.val)
|
|
180
|
+
bus.state = bus.braid_proxy()
|
|
181
|
+
} else
|
|
182
|
+
bus.to_http_body = (o) => {
|
|
183
|
+
var unwrap = (Object.keys(o).length === 2
|
|
184
|
+
&& '_' in o
|
|
185
|
+
&& typeof o._ === 'string')
|
|
186
|
+
// To do: translate pointers as keys
|
|
187
|
+
return unwrap ? o._ : JSON.stringify(o)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
bus.from_http_body = (key, body) => ({
|
|
191
|
+
key,
|
|
192
|
+
val: JSON.parse(body)
|
|
193
|
+
})
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
// Connect HTTP GET and PUT to our Fetch and Save
|
|
197
|
+
http_handlers: function (req, res, next) {
|
|
198
|
+
if (req.method === 'GET') {
|
|
199
|
+
|
|
112
200
|
// Make a temporary client bus
|
|
113
201
|
var cbus = bus.bus_for_http_client(req, res)
|
|
114
202
|
var cb
|
|
@@ -146,13 +234,13 @@ function import_server (bus, options)
|
|
|
146
234
|
else
|
|
147
235
|
res.send(body)
|
|
148
236
|
|
|
149
|
-
// And shut down the connection if
|
|
237
|
+
// And shut down the connection if nothing's left to do
|
|
150
238
|
if (!braidify || !req.subscribe || !body)
|
|
151
239
|
end_it_all()
|
|
152
240
|
})
|
|
153
|
-
}
|
|
241
|
+
}
|
|
154
242
|
|
|
155
|
-
|
|
243
|
+
else if (req.method === 'PUT') {
|
|
156
244
|
console.log('Got a put with body', req.body)
|
|
157
245
|
|
|
158
246
|
// Make a temporary client bus
|
|
@@ -190,72 +278,9 @@ function import_server (bus, options)
|
|
|
190
278
|
res.end()
|
|
191
279
|
})
|
|
192
280
|
}
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
// Set up default linked json converters
|
|
196
|
-
if (bus.options.braid_mode_test) {
|
|
197
|
-
bus.to_http_body = (o) => JSON.stringify(o.val)
|
|
198
|
-
bus.state = bus.braid_proxy()
|
|
199
|
-
} else
|
|
200
|
-
bus.to_http_body = (o) => {
|
|
201
|
-
var unwrap = (Object.keys(o).length === 2
|
|
202
|
-
&& '_' in o
|
|
203
|
-
&& typeof o._ === 'string')
|
|
204
|
-
// To do: translate pointers as keys
|
|
205
|
-
return unwrap ? o._ : JSON.stringify(o)
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
bus.from_http_body = (key, body) => ({
|
|
209
|
-
key,
|
|
210
|
-
val: JSON.parse(body)
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
// Serve Client Coffee
|
|
214
|
-
bus.serve_client_coffee()
|
|
215
|
-
|
|
216
|
-
// Custom router
|
|
217
|
-
bus.route = function server_route (key, method, arg, t) {
|
|
218
|
-
var handlers = bus.bindings(key, method)
|
|
219
|
-
var count = handlers.length
|
|
220
|
-
if (count)
|
|
221
|
-
bus.log('route:', bus+'("'+key+'").'+method+'['+handlers.length+'](key:"'+(arg.key||arg)+'")')
|
|
222
|
-
|
|
223
|
-
// Call all handlers!
|
|
224
|
-
for (var i=0; i<handlers.length; i++)
|
|
225
|
-
bus.run_handler(handlers[i].func, method, arg, {t: t, binding: handlers[i].key})
|
|
226
|
-
|
|
227
|
-
// Now handle backup handlers
|
|
228
|
-
if (count === 0) {
|
|
229
|
-
|
|
230
|
-
// A save to master without a to_save defined should be
|
|
231
|
-
// automatically reflected to all clients.
|
|
232
|
-
if (method === 'to_save') {
|
|
233
|
-
bus.save.fire(arg, t)
|
|
234
|
-
bus.route(arg.key, 'on_set_sync', arg, t)
|
|
235
|
-
count++
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// A fetch to master without a to_fetch defined should go to
|
|
239
|
-
// the database, if we have one.
|
|
240
|
-
if (method === 'to_fetch') {
|
|
241
|
-
bus.db_fetch(key)
|
|
242
|
-
// This basically renders the count useless-- you probably
|
|
243
|
-
// don't want to wrap this route() with another route().
|
|
244
|
-
count++
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
return handlers.length
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Back door to the control room
|
|
251
|
-
if (bus.options.backdoor) {
|
|
252
|
-
bus.make_http_server({
|
|
253
|
-
port: bus.options.backdoor,
|
|
254
|
-
name: 'backdoor_http_server',
|
|
255
|
-
use_ssl: use_ssl
|
|
256
|
-
})
|
|
257
|
-
bus.sockjs_server(this.backdoor_http_server)
|
|
258
281
|
}
|
|
282
|
+
else
|
|
283
|
+
next()
|
|
259
284
|
},
|
|
260
285
|
|
|
261
286
|
bus_for_http_client: function (req, res) {
|
|
@@ -2450,6 +2475,24 @@ function import_server (bus, options)
|
|
|
2450
2475
|
}
|
|
2451
2476
|
|
|
2452
2477
|
|
|
2478
|
+
// Disables CORS in HTTP servers
|
|
2479
|
+
function free_the_cors (req, res, next) {
|
|
2480
|
+
console.log('free the cors!', req.method, req.url)
|
|
2481
|
+
|
|
2482
|
+
var free_the_cors = {
|
|
2483
|
+
"Access-Control-Allow-Origin": "*",
|
|
2484
|
+
"Access-Control-Allow-Methods": "OPTIONS, HEAD, GET, PUT, UNSUBSCRIBE",
|
|
2485
|
+
"Access-Control-Allow-Headers": "subscribe, peer, version, parents, merge-type, content-type, patches, cache-control"
|
|
2486
|
+
}
|
|
2487
|
+
Object.entries(free_the_cors).forEach(x => res.setHeader(x[0], x[1]))
|
|
2488
|
+
if (req.method === 'OPTIONS') {
|
|
2489
|
+
res.writeHead(200)
|
|
2490
|
+
res.end()
|
|
2491
|
+
} else
|
|
2492
|
+
next()
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
|
|
2453
2496
|
// Handy functions for writing tests on nodejs
|
|
2454
2497
|
var tests = []
|
|
2455
2498
|
function test (f) {tests.push(f)}
|
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
|
})
|
|
@@ -1546,9 +1546,12 @@
|
|
|
1546
1546
|
if (!nodejs)
|
|
1547
1547
|
window.devtoolsFormatters = [{
|
|
1548
1548
|
header: function (x) {
|
|
1549
|
-
|
|
1550
|
-
['span', {style: 'background-color: #feb; padding: 3px;'},
|
|
1551
|
-
|
|
1549
|
+
if (x[symbols.is_proxy])
|
|
1550
|
+
return ['span', {style: 'background-color: #feb; padding: 3px;'},
|
|
1551
|
+
JSON.stringify(proxy_decode_json(x()))]
|
|
1552
|
+
else if (x[symbols.is_braid_proxy])
|
|
1553
|
+
return ['span', {style: 'background-color: #fffbe5; padding: 3px;'},
|
|
1554
|
+
JSON.stringify(x)]
|
|
1552
1555
|
},
|
|
1553
1556
|
hasBody: function (x) {return false}
|
|
1554
1557
|
}]
|
|
@@ -1556,41 +1559,61 @@
|
|
|
1556
1559
|
|
|
1557
1560
|
// ******************
|
|
1558
1561
|
// 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
1562
|
|
|
1563
|
+
var symbols = {is_braid_proxy: Symbol('is_braid_proxy'),
|
|
1564
|
+
is_proxy: Symbol('is_proxy'),
|
|
1565
|
+
is_link: Symbol('is_link'),
|
|
1566
|
+
get_json: Symbol('get_json'),
|
|
1567
|
+
get_base: Symbol('get_base')
|
|
1568
|
+
}
|
|
1569
|
+
function braid_proxy () {
|
|
1564
1570
|
function item_proxy (base, o) {
|
|
1571
|
+
|
|
1572
|
+
// We only create a proxy for objects.
|
|
1565
1573
|
if (typeof o === 'number'
|
|
1566
1574
|
|| typeof o === 'string'
|
|
1567
1575
|
|| typeof o === 'boolean'
|
|
1568
1576
|
|| o === undefined
|
|
1569
1577
|
|| o === null
|
|
1570
|
-
|| typeof o === 'function')
|
|
1578
|
+
|| typeof o === 'function')
|
|
1579
|
+
// Everything else passes through unscathed
|
|
1580
|
+
return o
|
|
1581
|
+
|
|
1582
|
+
// We recursively descend through {key: ...} links
|
|
1583
|
+
if (typeof o === 'object' && 'key' in o) {
|
|
1584
|
+
var new_base = bus.fetch(o.key)
|
|
1585
|
+
return item_proxy(new_base, new_base.val)
|
|
1586
|
+
}
|
|
1571
1587
|
|
|
1572
1588
|
return new Proxy(o, {
|
|
1573
1589
|
get: function get(o, k) {
|
|
1574
|
-
if (k === 'inspect' || k === 'valueOf'
|
|
1590
|
+
if (k === 'inspect' || k === 'valueOf')
|
|
1575
1591
|
return undefined
|
|
1576
|
-
|
|
1592
|
+
if (k === symbols.is_braid_proxy)
|
|
1593
|
+
return true
|
|
1594
|
+
if (typeof k === 'symbol')
|
|
1595
|
+
return undefined
|
|
1596
|
+
return item_proxy(base, o[proxied_2_keyed(k)])
|
|
1577
1597
|
},
|
|
1578
1598
|
set: function set(o, k, v) {
|
|
1579
|
-
var
|
|
1599
|
+
var value = translate_fields(v, proxied_2_keyed)
|
|
1600
|
+
o[proxied_2_keyed(k)] = value
|
|
1580
1601
|
bus.save(base)
|
|
1581
|
-
return
|
|
1602
|
+
return true
|
|
1582
1603
|
},
|
|
1583
1604
|
has: function has(o, k) {
|
|
1584
|
-
return o.hasOwnProperty(
|
|
1605
|
+
return o.hasOwnProperty(proxied_2_keyed(k))
|
|
1585
1606
|
},
|
|
1586
1607
|
deleteProperty: function del (o, k) {
|
|
1587
|
-
delete o[
|
|
1608
|
+
delete o[proxied_2_keyed(k)]
|
|
1588
1609
|
},
|
|
1589
1610
|
apply: function apply (o, This, args) {
|
|
1590
1611
|
return o
|
|
1591
1612
|
}
|
|
1592
1613
|
})}
|
|
1593
1614
|
|
|
1615
|
+
|
|
1616
|
+
// The top-level Proxy object holds HTTP resources
|
|
1594
1617
|
return new Proxy(cache, {
|
|
1595
1618
|
get: function get(o, k) {
|
|
1596
1619
|
if (k === 'inspect' || k === 'valueOf' || typeof k === 'symbol')
|
|
@@ -1601,21 +1624,27 @@
|
|
|
1601
1624
|
return item_proxy(base, base.val)
|
|
1602
1625
|
},
|
|
1603
1626
|
set: function set(o, key, val) {
|
|
1604
|
-
bus.save({key,
|
|
1627
|
+
bus.save({key: key,
|
|
1628
|
+
val: translate_fields(val, proxied_2_keyed)})
|
|
1629
|
+
return true
|
|
1605
1630
|
},
|
|
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
1631
|
deleteProperty: function del (o, k) {
|
|
1614
|
-
bus.delete(
|
|
1632
|
+
bus.delete(proxied_2_keyed(k))
|
|
1615
1633
|
}
|
|
1616
1634
|
})
|
|
1617
1635
|
}
|
|
1618
1636
|
|
|
1637
|
+
function link (url) {
|
|
1638
|
+
var result = fetch(url)
|
|
1639
|
+
result[symbols.is_link] = true
|
|
1640
|
+
return result
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
if ((nodejs && bus.options && bus.options.braid_mode_test)
|
|
1644
|
+
|| (!nodejs && window.Proxy && clientjs_option('braid_mode_test')))
|
|
1645
|
+
state = braid_proxy()
|
|
1646
|
+
|
|
1647
|
+
|
|
1619
1648
|
// ******************
|
|
1620
1649
|
// Network client
|
|
1621
1650
|
function get_domain (key) { // Returns e.g. "state://foo.com"
|
|
@@ -1822,8 +1851,8 @@
|
|
|
1822
1851
|
}
|
|
1823
1852
|
|
|
1824
1853
|
|
|
1825
|
-
//
|
|
1826
|
-
//
|
|
1854
|
+
// ************************************************
|
|
1855
|
+
// Translating the URLs under keys of state
|
|
1827
1856
|
function translate_keys (obj, f) {
|
|
1828
1857
|
// Recurse through each element in arrays
|
|
1829
1858
|
if (Array.isArray(obj))
|
|
@@ -1845,14 +1874,60 @@
|
|
|
1845
1874
|
}
|
|
1846
1875
|
return obj
|
|
1847
1876
|
}
|
|
1848
|
-
function
|
|
1877
|
+
function escape_keys(k) {
|
|
1849
1878
|
return k.replace(/(_(keys?|time)?$|^key$)/, '$1_')
|
|
1850
1879
|
}
|
|
1851
|
-
function
|
|
1880
|
+
function unescape_keys (k) {
|
|
1852
1881
|
return k.replace(/(_$)/, '')
|
|
1853
1882
|
}
|
|
1854
1883
|
|
|
1855
1884
|
|
|
1885
|
+
// ************************************************
|
|
1886
|
+
// Translating fields of objects
|
|
1887
|
+
|
|
1888
|
+
// Recurse through JSON and swap all object fields with other field names,
|
|
1889
|
+
// according to the function f(field, object). Returns a copy.
|
|
1890
|
+
//
|
|
1891
|
+
// f(field, object) takes a string `field` and returns the new field name
|
|
1892
|
+
// for `object`.
|
|
1893
|
+
function translate_fields (input, f) {
|
|
1894
|
+
var result
|
|
1895
|
+
|
|
1896
|
+
// Recurse through each element in arrays
|
|
1897
|
+
if (Array.isArray(input)) {
|
|
1898
|
+
var new_array = input.slice()
|
|
1899
|
+
for (var i=0; i < input.length; i++)
|
|
1900
|
+
new_array[i] = translate_fields(input[i], f)
|
|
1901
|
+
result = new_array
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
// Recurse through each property on objects
|
|
1905
|
+
else if (typeof input === 'object') {
|
|
1906
|
+
var new_obj = {}
|
|
1907
|
+
for (var k in input)
|
|
1908
|
+
new_obj[f(k, input)] = translate_fields(input[k], f)
|
|
1909
|
+
result = new_obj
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// Everything else passes through unscathed
|
|
1913
|
+
else
|
|
1914
|
+
result = input
|
|
1915
|
+
|
|
1916
|
+
return result
|
|
1917
|
+
}
|
|
1918
|
+
// Remove underscore from _key
|
|
1919
|
+
function keyed_2_proxied (k, object) {
|
|
1920
|
+
if (object[symbols.is_link])
|
|
1921
|
+
return k
|
|
1922
|
+
else
|
|
1923
|
+
return k.replace(/^(_*)_key$/, '$1key')
|
|
1924
|
+
}
|
|
1925
|
+
// Add underscore to key
|
|
1926
|
+
function proxied_2_keyed (k) {
|
|
1927
|
+
return k.replace(/^(_*)key$/, '$1_key')
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
|
|
1856
1931
|
// ******************
|
|
1857
1932
|
// JSON encoding
|
|
1858
1933
|
// - Does both escape/unescape keys, and wraps/unwraps pointers
|
|
@@ -2299,13 +2374,14 @@
|
|
|
2299
2374
|
var api = ['cache backup_cache fetch save forget del fire dirty fetch_once',
|
|
2300
2375
|
'subspace bindings run_handler bind unbind reactive uncallback',
|
|
2301
2376
|
'versions new_version',
|
|
2302
|
-
'make_proxy braid_proxy state sb',
|
|
2377
|
+
'make_proxy braid_proxy state sb link',
|
|
2303
2378
|
'funk_key funk_name funks key_id key_name id',
|
|
2304
2379
|
'pending_fetches fetches_in fetches_out loading_keys loading once',
|
|
2305
2380
|
'global_funk busses rerunnable_funks',
|
|
2306
|
-
'
|
|
2381
|
+
'escape_keys unescape_keys translate_keys apply_patch',
|
|
2382
|
+
'keyed_2_proxied proxied_2_keyed translate_fields',
|
|
2307
2383
|
'net_mount net_automount message_method',
|
|
2308
|
-
'parse Set One_To_Many clone extend deep_map deep_equals prune validate sorta_diff log deps'
|
|
2384
|
+
'parse Set One_To_Many clone extend deep_map deep_equals prune validate sorta_diff log deps symbols'
|
|
2309
2385
|
].join(' ').split(' ')
|
|
2310
2386
|
for (var i=0; i<api.length; i++)
|
|
2311
2387
|
bus[api[i]] = eval(api[i])
|