statebus 7.2.5 → 7.2.7

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
@@ -13,7 +13,8 @@
13
13
  // ****************
14
14
  // Connecting over the Network
15
15
  function set_cookie (key, val) {
16
- document.cookie = key + '=' + val + '; Expires=21 Oct 2025 00:0:00 GMT;'
16
+ // max-age is 400 days, the max allowed in chrome now
17
+ document.cookie = key + '=' + val + '; max-age=34560000;'
17
18
  }
18
19
  function get_cookie (key) {
19
20
  var c = document.cookie.match('(^|;)\\s*' + key + '\\s*=\\s*([^;]+)');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statebus",
3
- "version": "7.2.5",
3
+ "version": "7.2.7",
4
4
  "description": "Synchronize state.",
5
5
  "main": "statebus.js",
6
6
  "scripts": {
package/server-library.js CHANGED
@@ -167,10 +167,9 @@ function import_server (bus, make_statebus, options)
167
167
  + Math.random().toString(36).substring(2))
168
168
 
169
169
  // Add peer to response cookie, so client knows we've named it
170
- var expires = new Date()
171
- expires.setFullYear(expires.getFullYear() + 20)
170
+ // (max-age is 400 days, the max allowed in chrome now)
172
171
  res.setHeader('Set-Cookie', 'peer=' + client_id
173
- + '; Expires=' + expires.toUTCString() + ';')
172
+ + '; max-age=34560000;')
174
173
 
175
174
  // Decode the key
176
175
  var key = decodeURIComponent(req.url.substr(1))
@@ -190,10 +189,10 @@ function import_server (bus, make_statebus, options)
190
189
 
191
190
  function send_update (obj, t) {
192
191
  var body = to_http_body(obj)
193
- // console.log('http_in: Sending update of', body)
192
+ console.log('http_in: Sending update of', body, 'for', req.url)
194
193
 
195
194
  res.sendUpdate({
196
- body,
195
+ body: body || '',
197
196
  // If body === undefined, send 404
198
197
  status: body === undefined ? 404 : 200
199
198
  })
@@ -1033,154 +1032,6 @@ function import_server (bus, make_statebus, options)
1033
1032
  }
1034
1033
  },
1035
1034
 
1036
- setup_usage_log (opts) {
1037
- bus.serve_time()
1038
- opts = opts || {}
1039
- opts.filename = opts.filename || 'db.sqlite'
1040
- var db = new (require('better-sqlite3'))(opts.filename)
1041
- bus.usage_log_db = db
1042
- //db.pragma('journal_mode = WAL')
1043
- db.prepare('create table if not exists usage (date integer, event text, details text)').run()
1044
- db.prepare('create index if not exists date_index on usage (date)').run()
1045
- var refresh_interval = 1000*60
1046
-
1047
- var nots = ["details not like '%facebookexternalhit%'",
1048
- "details not like '%/apple-touch-icon%'",
1049
- "details not like '%Googlebot%'",
1050
- "details not like '%AdsBot-Google%'",
1051
- "details not like '%Google-Adwords-Instant%'",
1052
- "details not like '%Apache-HttpClient%'",
1053
- "details not like '%SafeDNSBot%'",
1054
- "details not like '%RevueBot%'",
1055
- "details not like '%MetaURI API%'",
1056
- "details not like '%redback/v%'",
1057
- "details not like '%Slackbot%'",
1058
- "details not like '%HTTP_Request2/%'",
1059
- "details not like '%python-requests/%'",
1060
- "details not like '%LightspeedSystemsCrawler/%'",
1061
- "details not like '%CipaCrawler/%'",
1062
- "details not like '%Twitterbot/%'",
1063
- "details not like '%Go-http-client/%'",
1064
- "details not like '%/cheese_service%'"
1065
- ].join(' and ')
1066
- bus.usage_log_nots = nots
1067
-
1068
- // Aggregate all accesses by day, to get daily active users
1069
- bus('usage').getter = () => {
1070
- bus.get('time/' + refresh_interval)
1071
- var days = []
1072
- var last_day
1073
- for (var row of db.prepare('select * from usage where '
1074
- + nots + ' order by date').iterate()) {
1075
- row.details = JSON.parse(row.details)
1076
- if (row.details.agent && row.details.agent.match(/bot/)) continue
1077
-
1078
- var d = new Date(row.date * 1000)
1079
- var day = d.getFullYear() + '-' + (d.getMonth()+1) + '-' + d.getDate()
1080
- if (last_day !== day)
1081
- days.push({day: day, // Init
1082
- clients: new Set(),
1083
- ips: new Set(),
1084
- client_socket_opens: new Set(),
1085
- ip_socket_opens: new Set()
1086
- })
1087
- last_day = day
1088
-
1089
- if (row.event === 'socket open') {
1090
- days[days.length-1].client_socket_opens.add(row.details.client)
1091
- days[days.length-1].ip_socket_opens.add(row.details.ip)
1092
- }
1093
- days[days.length-1].clients.add(row.details.client)
1094
- days[days.length-1].ips.add(row.details.ip)
1095
- }
1096
-
1097
- for (var i=0; i<days.length; i++)
1098
- days[i] = {day: days[i].day,
1099
- ip_hits: days[i].ips.size,
1100
- client_hits: days[i].clients.size,
1101
- client_socket_opens: days[i].client_socket_opens.size,
1102
- ip_socket_opens: days[i].ip_socket_opens.size
1103
- }
1104
-
1105
- return {_: days}
1106
- }
1107
-
1108
- bus('recent_hits/*').getter = (rest) => {
1109
- bus.get('time/' + refresh_interval)
1110
- var result = []
1111
- for (var row of db.prepare('select * from usage where '
1112
- + nots + ' order by date desc limit ?').iterate(
1113
- [parseInt(rest)])) {
1114
-
1115
- row.details = JSON.parse(row.details)
1116
- if (row.details.agent && row.details.agent.match(/bot/)) continue
1117
-
1118
- result.push({url: row.details.url, ip: row.details.ip, date: row.date})
1119
- }
1120
-
1121
- return {_: result}
1122
- }
1123
-
1124
- bus('recent_referers/*').getter = (rest) => {
1125
- bus.get('time/' + refresh_interval)
1126
- var result = []
1127
- for (var row of db.prepare('select * from usage where '
1128
- + nots + ' order by date desc limit ?').iterate(
1129
- [parseInt(rest)])) {
1130
-
1131
- row.details = JSON.parse(row.details)
1132
- if (row.details.agent && row.details.agent.match(/bot/)) continue
1133
-
1134
- if (row.details.referer && !row.details.referer.match(/^https:\/\/cheeseburgertherapy.com/))
1135
- result.push({url: row.details.url, referer: row.details.referer,
1136
- date: row.date})
1137
- }
1138
-
1139
- return {_: result}
1140
- }
1141
-
1142
-
1143
- function sock_open_time (sock_event) {
1144
- var client = JSON.parse(sock_event.details).client
1145
- if (!client) return null
1146
-
1147
- var http_req = db.prepare('select * from usage where event = "http request" and date < ? and '
1148
- + ' details like ? and '
1149
- + nots + ' order by date desc limit 1').get([sock_event.date,
1150
- '%'+client+'%'])
1151
- if (!http_req) return null
1152
-
1153
- var delay = sock_event.date - http_req.date
1154
- var res = !delay || delay > 300 ? 'fail' : delay
1155
- if (delay && delay < 300
1156
- && JSON.parse(sock_event.details).ip
1157
- !== JSON.parse(http_req.details).ip)
1158
- console.error('Yuck!', delay, sock_event, http_req)
1159
- return [res, JSON.parse(http_req.details).url]
1160
- }
1161
- function sock_open_times () {
1162
- var opens = db.prepare('select * from usage where event = "socket open" and '
1163
- + nots + ' order by date desc limit ?').all(500)
1164
- var times = []
1165
- for (var i=0; i<opens.length; i++) {
1166
- times.push(sock_open_time(opens[i]))
1167
- // Get the most recent http hit before this open, from the same client id
1168
- // subract the times
1169
- }
1170
- return times
1171
- }
1172
-
1173
- bus('socket_load_times').getter = () => {
1174
- return {_: sock_open_times()}
1175
- }
1176
- },
1177
- log_usage(event, details) {
1178
- bus.usage_log_db.prepare('insert into usage (date, event, details) values (?, ?, ?)')
1179
- .run([new Date().getTime()/1000,
1180
- event,
1181
- JSON.stringify(details)])
1182
- },
1183
-
1184
1035
  time () {
1185
1036
  if (bus('time*').getter.length > 0)
1186
1037
  // Then it's already installed
@@ -2371,32 +2222,41 @@ function import_server (bus, make_statebus, options)
2371
2222
  // Installs a GET handler at route that gets state from a getter function
2372
2223
  // Note: Makes too many textbusses. Should re-use one.
2373
2224
  http_serve: function http_serve (route, getter) {
2374
- var textbus = make_statebus()
2375
- textbus.label = 'textbus'
2376
- var watched = new Set()
2377
- textbus('*').getter = (filename, old) => {
2378
- return {etag: Math.random() + '',
2379
- _: getter(filename)}
2380
- }
2225
+ // if (!this.filebus) {
2226
+ // this.filebus = make_statebus()
2227
+ // this.filebus.label = 'filebus'
2228
+ // }
2229
+ // var filebus = this.filebus
2230
+
2231
+ var cache = this.compiled_coffee = this.compiled_coffee || {}
2232
+
2233
+ // filebus('*').getter = (filename, old) => ({
2234
+ // etag: Math.random() + '',
2235
+ // contents: getter(filename)
2236
+ // })
2381
2237
  bus.http.get(route, (req, res) => {
2238
+ console.log('Getting', route, req.path, 'from',
2239
+ Object.values(cache).map(x => JSON.stringify(x).substr(0, 100)))
2382
2240
  var path = req.path
2383
- var etag = textbus.cache[path] && textbus.cache[path].etag
2241
+ var etag = cache[path] && cache[path].etag
2384
2242
  if (etag && req.get('If-None-Match') === etag) {
2385
2243
  res.status(304).end()
2386
2244
  return
2387
2245
  }
2388
2246
 
2389
- textbus.get(req.path) // So that textbus never clears the cache
2390
- textbus.get(req.path, function cb (o) {
2391
- res.setHeader('Cache-Control', 'public')
2392
- // res.setHeader('Cache-Control', 'public, max-age='
2393
- // + (60 * 60 * 24 * 30)) // 1 month
2394
- res.setHeader('ETag', o.etag)
2395
- res.setHeader('Access-Control-Allow-Origin', '*')
2396
- res.setHeader('Content-Type', 'application/javascript')
2397
- res.send(o._)
2398
- textbus.forget(o.key, cb) // But we do want to forget the cb
2399
- })
2247
+ if (!cache[path])
2248
+ cache[path] = {
2249
+ etag: Math.random() + '',
2250
+ contents: getter(path)
2251
+ }
2252
+
2253
+ res.setHeader('Cache-Control', 'public')
2254
+ // res.setHeader('Cache-Control', 'public, max-age='
2255
+ // + (60 * 60 * 24 * 30)) // 1 month
2256
+ res.setHeader('ETag', cache[path].etag)
2257
+ res.setHeader('Access-Control-Allow-Origin', '*')
2258
+ res.setHeader('Content-Type', 'application/javascript')
2259
+ res.send(cache[path].contents)
2400
2260
  })
2401
2261
  },
2402
2262
 
@@ -2404,7 +2264,11 @@ function import_server (bus, make_statebus, options)
2404
2264
  bus.http_serve('/client/:filename', (filename) => {
2405
2265
  filename = /\/client\/(.*)/.exec(filename)[0]
2406
2266
  var source_filename = filename.substr(1)
2407
- var source = bus.read_file(source_filename)
2267
+ var source = fs.readFileSync(source_filename) + ''
2268
+ // var source = bus.read_file(source_filename)
2269
+
2270
+ // console.log('Source is', source)
2271
+
2408
2272
  if (bus.loading()) throw 'loading'
2409
2273
  if (filename.match(/\.coffee$/))
2410
2274
  return bus.compile_coffee(source, source_filename)
@@ -2420,7 +2284,8 @@ function import_server (bus, make_statebus, options)
2420
2284
  sourceMap: true})
2421
2285
  } catch (e) {
2422
2286
  console.error('Could not compile ' + e.toString())
2423
- return 'console.error(' + JSON.stringify(e.toString()) + ')'
2287
+ return 'console.error(' + JSON.stringify("When compiling " + filename + ": "
2288
+ + e.toString()) + ')'
2424
2289
  }
2425
2290
 
2426
2291
  var source_map = JSON.parse(compiled.v3SourceMap)
package/statebus.js CHANGED
@@ -119,7 +119,7 @@
119
119
 
120
120
  if (!('key' in obj) || typeof obj.key !== 'string') {
121
121
  console.error('Error: set(obj) called on object without a key: ', obj)
122
- console.trace('Bad set(obj)')
122
+ //console.trace('Bad set(obj)')
123
123
  }
124
124
  bogus_check(obj.key)
125
125
 
@@ -963,45 +963,11 @@
963
963
  // We're currently calling that (param1,param2:val2) part "function params", and presuming
964
964
  // that it exists in the () part of the pattern like in /foo/bar*().
965
965
  function func_args () {
966
- // This code is copied from Raphael Walker's parser.coffee
967
- function split_once (str, char) {
968
- var i = str.indexOf(char)
969
- return i === -1 ? [str, ""] : [str.slice(0, i), str.slice(i + 1)]
970
- }
971
-
972
- // Function Strings -- an alternative to query strings
973
- //
974
- function parse_function_string (str) {
975
- // Coffeescript doesn't have object comprehensions :(
976
- var ret = {}
977
-
978
- // Now process the string:
979
- str
980
- // Pull out the parentheses
981
- .slice(1, -1)
982
- // Split by commas. TODO: Allow spaces after commas?
983
- .split(",")
984
- // Delete empty parts (this ensures that empty strings
985
- // will properly result in empty func_string, and allows trailing
986
- // commas)
987
- .filter(part => part.length)
988
- .forEach(part => {
989
- // If the part has a comma, its a key:value, otherwise
990
- // it's just a singleton
991
- var [k, v] = split_once(part, ":")
992
-
993
- if (v.length === 0) ret[k] = true
994
- // If the value is itself a func_string params object, parse it recursively
995
- else if (v.startsWith("(")) ret[k] = parse_function_string(v)
996
- else ret[k] = v
997
- })
998
- return ret
999
- }
1000
966
  // console.log('Getting func_args from', func.statebus_binding.args)
1001
967
  if (!(func.statebus_binding.args
1002
968
  && func.statebus_binding.args.func_args))
1003
969
  return undefined
1004
- return parse_function_string(func.statebus_binding.args.func_args)
970
+ return funcarg_parse(func.statebus_binding.args.func_args)
1005
971
  }
1006
972
  var f = reactive(function () {
1007
973
  // Initialize transaction
@@ -1447,6 +1413,62 @@
1447
1413
  bus.get_once(key, (o) => resolve(o)))
1448
1414
  }
1449
1415
 
1416
+ // ******************
1417
+ // Function Args -- an alternative to Query Params
1418
+
1419
+ // Parse a funcarg string.
1420
+
1421
+ function funcarg_parse (str) {
1422
+ // Coffeescript doesn't have object comprehensions :(
1423
+ var ret = {}
1424
+
1425
+ function split_once (str, char) {
1426
+ var i = str.indexOf(char)
1427
+ return i === -1 ? [str, ""] : [str.slice(0, i), str.slice(i + 1)]
1428
+ }
1429
+
1430
+ // Now process the string:
1431
+ str
1432
+ // Pull out the parentheses
1433
+ .slice(1, -1)
1434
+ // Split by commas. TODO: Allow spaces after commas?
1435
+ .split(",")
1436
+ // Delete empty parts (this ensures that empty strings
1437
+ // will properly result in empty func_string, and allows trailing
1438
+ // commas)
1439
+ .filter(part => part.length)
1440
+ .forEach(part => {
1441
+ // If the part has a comma, its a key:value, otherwise
1442
+ // it's just a singleton
1443
+ var [k, v] = split_once(part, ":")
1444
+
1445
+ if (v.length === 0) ret[k] = true
1446
+ // If the value is itself a func_string params object, parse it recursively
1447
+ else if (v.startsWith("(")) ret[k] = funcarg_parse(v)
1448
+ else ret[k] = v
1449
+ })
1450
+ return ret
1451
+ }
1452
+
1453
+ // Generate a funcarg string
1454
+ function funcarg_stringify (obj) {
1455
+ var inner = Object.entries(obj)
1456
+ // Allowed values in KSON are: object, string, true
1457
+ // Arrays are in fact objects, and we don't need to treat them differently.
1458
+ // Their order won't change!
1459
+ .filter(([k, v]) => v)
1460
+ .sort()
1461
+ .map(([k, v]) => {
1462
+ if (typeof v === "boolean") return k
1463
+ if (typeof v === "string") return `${k}:${v}`
1464
+ if (typeof v === "object") return `${k}:${funcarg_string(v)}`
1465
+ return ""
1466
+ })
1467
+ .join(",")
1468
+ return inner.length ? `(${inner})` : ""
1469
+ }
1470
+
1471
+
1450
1472
  // ******************
1451
1473
  // Proxy
1452
1474
 
@@ -2717,6 +2739,7 @@
2717
2739
  'old_subspace new_subspace bindings run_handler bind unbind reactive uncallback',
2718
2740
  'versions new_version',
2719
2741
  'aget client_bus_for',
2742
+ 'funcarg_parse funcarg_stringify',
2720
2743
  'funk_key funk_name funks key_id key_name id',
2721
2744
  'pending_gets gets_in gets_out loading_keys loading once',
2722
2745
  'global_funk busses rerunnable_funks',