braid-http 0.3.1 → 0.3.3

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.
@@ -48,25 +48,26 @@ function braidify_http (http) {
48
48
  // Always add the `peer` header
49
49
  options.headers.peer = options.headers.peer || peer
50
50
 
51
- // Wrap the callback to provide our new .on('version', ...) feature
51
+ // Wrap the callback to provide our new .on('update', ...) feature
52
52
  // on nodejs servers
53
- var on_version,
53
+ var on_update,
54
54
  on_error,
55
55
  orig_cb = cb
56
56
  cb = (res) => {
57
57
  res.orig_on = res.on
58
58
  res.on = (key, f) => {
59
59
 
60
- // Define .on('version', cb)
61
- if (key === 'version') {
60
+ // Define .on('update', cb)
61
+ if (key === 'update'
62
+ || key === 'version' /* Deprecated API calls it 'version' */ ) {
62
63
 
63
- // If we have an 'version' handler, let's remember it
64
- on_version = f
64
+ // If we have an 'update' handler, let's remember it
65
+ on_update = f
65
66
 
66
67
  // And set up a subscription parser
67
- var parser = subscription_parser((version, error) => {
68
+ var parser = subscription_parser((update, error) => {
68
69
  if (!error)
69
- on_version && on_version(version)
70
+ on_update && on_update(update)
70
71
  else
71
72
  on_error && on_error(error)
72
73
  })
@@ -149,13 +150,21 @@ async function braid_fetch (url, params = {}) {
149
150
  else
150
151
  params.headers = new Headers(params.headers)
151
152
 
153
+ // Sanity check inputs
154
+ if (params.version)
155
+ console.assert(Array.isArray(params.version),
156
+ 'fetch(): `version` must be an array')
157
+ if (params.parents)
158
+ console.assert(Array.isArray(params.parents),
159
+ 'fetch(): `parents` must be an array')
160
+
152
161
  // Always set the peer
153
162
  params.headers.set('peer', peer)
154
163
 
155
164
  // We provide some shortcuts for Braid params
156
165
  if (params.version)
157
166
  params.headers.set('version', params.version.map(JSON.stringify).join(', '))
158
- if (params.parents?.length)
167
+ if (params.parents)
159
168
  params.headers.set('parents', params.parents.map(JSON.stringify).join(', '))
160
169
  if (params.subscribe)
161
170
  params.headers.set('subscribe', 'true')
@@ -342,7 +351,6 @@ async function handle_fetch_stream (stream, cb) {
342
351
  // Braid-HTTP Subscription Parser
343
352
  // ****************************
344
353
 
345
-
346
354
  var subscription_parser = (cb) => ({
347
355
  // A parser keeps some parse state
348
356
  state: {input: ''},
@@ -359,23 +367,18 @@ var subscription_parser = (cb) => ({
359
367
 
360
368
  // Now loop through the input and parse until we hit a dead end
361
369
  while (this.state.input.trim() !== '') {
362
- this.state = parse_version (this.state)
370
+ this.state = parse_update (this.state)
363
371
 
364
372
  // Maybe we parsed a version! That's cool!
365
373
  if (this.state.result === 'success') {
366
- let ignore_headers = {
367
- version: true,
368
- parents: true,
369
- patches: true,
370
- 'content-length': true,
371
- 'content-range': true,
372
- }
373
374
  this.cb({
374
375
  version: this.state.version,
375
376
  parents: this.state.parents,
376
377
  body: this.state.body,
377
378
  patches: this.state.patches,
378
- ...((x => Object.keys(x).length ? {extra_headers: x} : {})(Object.fromEntries(Object.entries(this.state.headers).filter(([k, v]) => !ignore_headers[k]))))
379
+
380
+ // Output extra_headers if there are some
381
+ extra_headers: extra_headers(this.state.headers)
379
382
  })
380
383
 
381
384
  // Reset the parser for the next version!
@@ -410,7 +413,7 @@ var subscription_parser = (cb) => ({
410
413
  // => {result: 'error', ...} If there is a syntax error in the input
411
414
 
412
415
 
413
- function parse_version (state) {
416
+ function parse_update (state) {
414
417
  // If we don't have headers yet, let's try to parse some
415
418
  if (!state.headers) {
416
419
  var parsed = parse_headers(state.input)
@@ -488,7 +491,8 @@ function parse_headers (input) {
488
491
  // Success! Let's parse special headers
489
492
  if ('version' in headers)
490
493
  headers.version = JSON.parse('['+headers.version+']')
491
- headers.parents = JSON.parse('['+(headers.parents ?? '')+']')
494
+ if ('parents' in headers)
495
+ headers.parents = JSON.parse('['+headers.parents+']')
492
496
  if ('patches' in headers)
493
497
  headers.patches = JSON.parse(headers.patches)
494
498
 
@@ -632,13 +636,8 @@ function parse_body (state) {
632
636
  last_patch.unit = match.unit
633
637
  last_patch.range = match.range
634
638
  last_patch.content = state.input.substr(0, content_length)
635
-
636
- // instead of headers, we'll create an "extra_headers" field that ignore the headers we've used
637
- last_patch.extra_headers = last_patch.headers
638
- delete last_patch.headers
639
- delete last_patch.extra_headers['content-length']
640
- delete last_patch.extra_headers['content-range']
641
- if (!Object.keys(last_patch.extra_headers).length) delete last_patch.extra_headers
639
+ last_patch.extra_headers = extra_headers(last_patch.headers)
640
+ delete last_patch.headers // We only keep the extra headers ^^
642
641
 
643
642
  // Consume the parsed input
644
643
  state.input = state.input.substring(content_length)
@@ -655,6 +654,28 @@ function parse_body (state) {
655
654
  }
656
655
  }
657
656
 
657
+ // The "extra_headers" field is returned to the client on any *update* or
658
+ // *patch* to include any headers that we've received, but don't have braid
659
+ // semantics for.
660
+ //
661
+ // This function creates that hash from a headers object, by filtering out all
662
+ // known headers.
663
+ function extra_headers (headers) {
664
+ // Clone headers
665
+ var result = Object.assign({}, headers)
666
+
667
+ // Remove the non-extra parts
668
+ var known_headers = ['version', 'parents', 'patches',
669
+ 'content-length', 'content-range']
670
+ for (var i = 0; i < known_headers.length; i++)
671
+ delete result[known_headers[i]]
672
+
673
+ // Return undefined if we deleted them all
674
+ if (Object.keys(result).length === 0)
675
+ return undefined
676
+
677
+ return result
678
+ }
658
679
 
659
680
  // ****************************
660
681
  // Exports
@@ -665,7 +686,7 @@ if (typeof module !== 'undefined' && module.exports)
665
686
  fetch: braid_fetch,
666
687
  http: braidify_http,
667
688
  subscription_parser,
668
- parse_version,
689
+ parse_update,
669
690
  parse_headers,
670
691
  parse_body
671
692
  }
@@ -195,8 +195,8 @@ function braidify (req, res, next) {
195
195
  res.setHeader('Range-Request-Allow-Units', 'json')
196
196
 
197
197
  // Extract braid info from headers
198
- var version = JSON.parse('['+(req.headers.version ?? '')+']'),
199
- parents = JSON.parse('['+(req.headers.parents ?? '')+']'),
198
+ var version = req.headers.version && JSON.parse('['+req.headers.version+']'),
199
+ parents = req.headers.parents && JSON.parse('['+req.headers.parents+']'),
200
200
  peer = req.headers['peer'],
201
201
  url = req.url.substr(1)
202
202
 
@@ -211,8 +211,8 @@ function braidify (req, res, next) {
211
211
  req.subscribe = subscribe
212
212
 
213
213
  // Add the braidly request/response helper methods
214
- res.sendVersion = (stuff) => send_version(res, stuff, req.url, peer)
215
- res.sendUpdate = res.sendVersion
214
+ res.sendUpdate = (stuff) => send_update(res, stuff, req.url, peer)
215
+ res.sendVersion = res.sendUpdate
216
216
  req.patches = () => new Promise(
217
217
  (done, err) => parse_patches(req, (patches) => done(patches))
218
218
  )
@@ -273,7 +273,7 @@ function braidify (req, res, next) {
273
273
  next && next()
274
274
  }
275
275
 
276
- function send_version(res, data, url, peer) {
276
+ function send_update(res, data, url, peer) {
277
277
  var {version, parents, patches, patch, body} = data
278
278
 
279
279
  function set_header (key, val) {
@@ -332,13 +332,13 @@ function send_version(res, data, url, peer) {
332
332
  for (var [header, value] of Object.entries(data)) {
333
333
  header = header.toLowerCase()
334
334
 
335
- // Version and Parents get output in the Structured Headers format
335
+ // Version and Parents get output in the Structured Headers format,
336
+ // so we convert `value` from array to comma-separated strings.
336
337
  if (header === 'version') {
337
338
  header = 'Version' // Capitalize for prettiness
338
339
  value = value.map(JSON.stringify).join(", ")
339
340
  } else if (header === 'parents') {
340
341
  header = 'Parents' // Capitalize for prettiness
341
- if (value.length == 0) continue // we express no parents as not having the field at all
342
342
  value = value.map(JSON.stringify).join(", ")
343
343
  }
344
344
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-http",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "An implementation of Braid-HTTP for Node.js and Browsers",
5
5
  "scripts": {
6
6
  "test": "node test/server.js"
package/readme.md CHANGED
@@ -38,7 +38,7 @@ import {fetch, http_client, http_server} from 'braid-http'
38
38
  This library adds a `{subscribe: true}` option to `fetch()`, and lets you
39
39
  access the result of a subscription with two new fields on the fetch response:
40
40
 
41
- - `response.subscribe( new_version => ... )`
41
+ - `response.subscribe( update => ... )`
42
42
  - `response.subscription`: an iterator that can be used with `for await`
43
43
 
44
44
  ### Example Subscription with Promises
@@ -48,16 +48,16 @@ Here is an example of subscribing to a Braid resource using promises:
48
48
  ```javascript
49
49
  fetch('https://braid.org/chat', {subscribe: true}).then(
50
50
  res => res.subscribe(
51
- (new_version) => {
52
- console.log('We got a new version!', new_version)
51
+ (update) => {
52
+ console.log('We got a new update!', update)
53
53
  // {
54
- // version: "me",
54
+ // version: ["me"],
55
55
  // parents: ["mom", "dad"],
56
56
  // patches: [{unit: "json", range: ".foo", content: "3"}]
57
57
  // body: "3"
58
58
  // }
59
59
  //
60
- // Note that new_version will contain either patches *or* body
60
+ // Note that `update` will contain either patches *or* body
61
61
  }
62
62
  )
63
63
  )
@@ -69,9 +69,9 @@ If you want automatic reconnections, add two error handlers like this:
69
69
  function connect() {
70
70
  fetch('https://braid.org/chat', {subscribe: true}).then(
71
71
  res => res.subscribe(
72
- (new_version) => {
73
- console.log('We got a new version!', new_version)
74
- // Do something with the new_version
72
+ (update) => {
73
+ console.log('We got a new update!', update)
74
+ // Do something with the update
75
75
  },
76
76
  e => setTimeout(connect, 1000)
77
77
  )
@@ -86,8 +86,8 @@ connect()
86
86
  async function connect () {
87
87
  try {
88
88
  (await fetch('/chat', {subscribe: true})).subscribe(
89
- (new_version) => {
90
- // We got a new version!
89
+ (update) => {
90
+ // We got a new update!
91
91
  },
92
92
  () => setTimeout(connect, 1000)
93
93
  )
@@ -108,7 +108,7 @@ async function connect () {
108
108
  if (v.patches)
109
109
  chat = apply_patches(v.patches, chat)
110
110
 
111
- // Or complete versions:
111
+ // Or complete snapshots:
112
112
  else
113
113
  // Beware the server doesn't send these yet.
114
114
  chat = JSON.parse(v.body)
@@ -153,7 +153,7 @@ require('http').createServer(
153
153
 
154
154
  // Send the current version
155
155
  res.sendUpdate({
156
- version: 'greg',
156
+ version: ['greg'],
157
157
  body: JSON.stringify({greg: 'greg'})
158
158
  })
159
159
  }
@@ -184,14 +184,14 @@ app.get('/', (req, res) => {
184
184
 
185
185
  // Send the current version
186
186
  res.sendUpdate({
187
- version: 'greg',
187
+ version: ['greg'],
188
188
  parents: ['gr','eg'],
189
189
  body: JSON.stringify({greg: 'greg'})
190
190
  })
191
191
 
192
192
  // Or you can send patches like this:
193
193
  // res.sendUpdate({
194
- // version: 'greg',
194
+ // version: ['greg'],
195
195
  // parents: ['gr','eg'],
196
196
  // patches: [{range: '.greg', unit: 'json', content: '"greg"'}]
197
197
  // })
@@ -217,8 +217,8 @@ https.get(
217
217
  'https://braid.org/chat',
218
218
  {subscribe: true},
219
219
  (res) => {
220
- res.on('version', (version) => {
221
- console.log('well we got one', version)
220
+ res.on('update', (update) => {
221
+ console.log('well we got one', update)
222
222
  })
223
223
  }
224
224
  )
@@ -232,15 +232,15 @@ function connect () {
232
232
  'https://braid.org/chat',
233
233
  {subscribe: true},
234
234
  (res) => {
235
- res.on('version', (version) => {
235
+ res.on('update', (update) => {
236
236
  // {
237
- // version: "me",
237
+ // version: ["me"],
238
238
  // parents: ["mom", "dad"],
239
239
  // patches: [{unit: "json", range: ".foo", content: "3"}]
240
240
  // body: "3"
241
241
  // }
242
- // // Version will contain either patches *or* body, but not both
243
- console.log('We got a new version!', version)
242
+ // // Update will contain either patches *or* body, but not both
243
+ console.log('We got a new update!', update)
244
244
  })
245
245
 
246
246
  res.on('end', e => setTimeout(connect, 1000))
@@ -272,5 +272,3 @@ automatically reconnect. (See
272
272
  [issue #980](https://github.com/node-fetch/node-fetch/issues/980) and
273
273
  [#753](https://github.com/node-fetch/node-fetch/issues/753).) We recommend
274
274
  using the `http` library (below) for requests on nodejs instead.
275
-
276
-