braid-http 1.3.106 → 1.3.107

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/README.md CHANGED
@@ -82,7 +82,39 @@ fetch('https://braid.org/chat', {subscribe: true}).then(
82
82
  )
83
83
  ```
84
84
 
85
- If you want automatic reconnections, this library add a `{retry: true}` option to `fetch()`.
85
+ ### Example Subscription with Async/Await
86
+
87
+ ```javascript
88
+ (await fetch('/chat', {subscribe: true, retry: true})).subscribe(
89
+ (update) => {
90
+ // We got a new update!
91
+ })
92
+ ```
93
+
94
+ ### Example Subscription with `for await`
95
+
96
+ ```javascript
97
+ var subscription_iterator = (await fetch('/chat',
98
+ {subscribe: true, retry: true})).subscription
99
+ for await (var update of subscription_iterator) {
100
+ // Updates might come in the form of patches:
101
+ if (update.patches)
102
+ chat = apply_patches(update.patches, chat)
103
+
104
+ // Or complete snapshots:
105
+ else
106
+ // Beware the server doesn't send these yet.
107
+ chat = JSON.parse(update.body_text)
108
+
109
+ render_stuff()
110
+ }
111
+ ```
112
+
113
+ ### Advanced client features
114
+
115
+ #### Automatic reconection
116
+
117
+ Pass a `{retry: true}` option to `fetch()` to automatically reconnect:
86
118
 
87
119
  ```javascript
88
120
  fetch('https://braid.org/chat', {subscribe: true, retry: true}).then(
@@ -95,12 +127,16 @@ fetch('https://braid.org/chat', {subscribe: true, retry: true}).then(
95
127
  )
96
128
  ```
97
129
 
98
- For use in conjunction with `{retry: true}`, it's possible to make the `parents` param equal to a function, which will be called to get the current parents each time the fetch establishes a new connection.
130
+
131
+ To update the parent version that you reconnect from, set the `parents`
132
+ paramter to a function rather than an array of strings:
99
133
 
100
134
  ```javascript
101
- fetch('https://braid.org/chat', {subscribe: true, retry: true, parents: () => {
102
- return current_parents
103
- }}).then(
135
+ fetch('https://braid.org/chat', {
136
+ subscribe: true,
137
+ retry: true,
138
+ parents: () => current_parents
139
+ }).then(
104
140
  res => res.subscribe(
105
141
  (update) => {
106
142
  console.log('We got a new update!', update)
@@ -110,7 +146,13 @@ fetch('https://braid.org/chat', {subscribe: true, retry: true, parents: () => {
110
146
  )
111
147
  ```
112
148
 
113
- You can monitor the subscription's connection status with `onSubscriptionStatus`:
149
+ It will call the `parents` function each time it reconnects to learn the
150
+ current parents to connection from.
151
+
152
+ #### Monitor the connection status
153
+
154
+ The `onSubscriptionStatus(status)` callback informs you when the connection
155
+ goes online and offline:
114
156
 
115
157
  ```javascript
116
158
  fetch('https://braid.org/chat', {
@@ -133,33 +175,7 @@ The callback receives an object with only the fields relevant to the event:
133
175
  - `{online: true}` — the subscription is connected
134
176
  - `{online: false, error}` — the subscription went offline, with the error/reason for disconnection
135
177
 
136
- ### Example Subscription with Async/Await
137
-
138
- ```javascript
139
- (await fetch('/chat', {subscribe: true, retry: true})).subscribe(
140
- (update) => {
141
- // We got a new update!
142
- })
143
- ```
144
-
145
- ### Example Subscription with `for await`
146
178
 
147
- ```javascript
148
- var subscription_iterator = (await fetch('/chat',
149
- {subscribe: true, retry: true})).subscription
150
- for await (var update of subscription_iterator) {
151
- // Updates might come in the form of patches:
152
- if (update.patches)
153
- chat = apply_patches(update.patches, chat)
154
-
155
- // Or complete snapshots:
156
- else
157
- // Beware the server doesn't send these yet.
158
- chat = JSON.parse(update.body_text)
159
-
160
- render_stuff()
161
- }
162
- ```
163
179
 
164
180
  ## Using it in Nodejs
165
181
 
@@ -159,20 +159,21 @@ async function braid_fetch (url, params = {}) {
159
159
  params.headers.set('version', params.version.map(JSON.stringify).map(ascii_ify).join(', '))
160
160
  if (Array.isArray(params.parents))
161
161
  params.headers.set('parents', params.parents.map(JSON.stringify).map(ascii_ify).join(', '))
162
- if (params.subscribe)
162
+ if (params.subscribe) {
163
163
  params.headers.set('subscribe', 'true')
164
+ // Prevent this response from being cached
165
+ params.cache = 'no-store'
166
+ }
167
+
164
168
  if (params.peer)
165
169
  params.headers.set('peer', params.peer)
166
-
170
+
167
171
  if (params.heartbeats)
168
172
  params.headers.set('heartbeats',
169
173
  typeof params.heartbeats === 'number'
170
174
  ? `${params.heartbeats}s`
171
175
  : params.heartbeats)
172
176
 
173
- // Prevent browsers from going to disk cache
174
- params.cache = 'no-cache'
175
-
176
177
  // Prepare patches
177
178
  if (params.patches) {
178
179
  console.assert(!params.body, 'Cannot send both patches and body')
@@ -311,9 +312,32 @@ async function braid_fetch (url, params = {}) {
311
312
  if (parents) params.headers.set('parents', parents.map(JSON.stringify).join(', '))
312
313
  }
313
314
 
314
- // undocumented feature used by braid-chrome
315
- // to see the fetch args as they are right before it is actually called,
316
- // to display them for the user in the dev panel
315
+ // Work around Chrome bug where when you restore a closed tab,
316
+ // Chrome overrides our {cache:'no-store'} parameter and instead sets
317
+ // SKIP_CACHE_VALIDATION, which overrides our subscription response
318
+ // with ... a static entry from its cache! That sucks.
319
+ //
320
+ // See https://issues.chromium.org/issues/490673934
321
+ //
322
+ // Our workaround is to detect if we are in chrome, and subscribing,
323
+ // and are within a page restoration... and then...
324
+ if (!is_nodejs && params.headers.has('subscribe')) {
325
+ var nav_entry = performance?.getEntriesByType?.('navigation')?.[0]
326
+ if (nav_entry?.type === 'back_forward'
327
+ && document.readyState !== 'complete')
328
+
329
+ // ...we just wait until the page has loaded to send this fetch
330
+ await new Promise(r => window.addEventListener('load', r))
331
+
332
+ // Because the SKIP_CACHE_VALIDATION policy in chrome goes away
333
+ // as soon as the page loads.
334
+ }
335
+
336
+ // Braid-Chrome needs the following special (undocumented)
337
+ // `onFetch` feature. It's a callback with the params as they
338
+ // are being sent to the underlying fetch(). The Braid
339
+ // devtools wants to be able to display these in its devtools
340
+ // panel.
317
341
  params.onFetch?.(url, params, underlying_aborter)
318
342
 
319
343
  // Now we run the original fetch....
@@ -332,12 +332,22 @@ function braidify (req, res, next) {
332
332
  if ((subscribe === '' || subscribe)
333
333
  // And this is a GET, because `Subscribe:` is only
334
334
  // specified for GET thus far...
335
- && req.method === 'GET')
335
+ && req.method === 'GET') {
336
336
  // Then let's set 'subscribe' on. We default to "true", but if the
337
337
  // client actually specified a value other than empty string '', let's
338
338
  // use that rich value.
339
339
  subscribe = subscribe || true
340
340
 
341
+ // Great. Now we also need to set the response body's content-type, so
342
+ // that FireFox doesn't try to sniff the content-type on a stream and
343
+ // hang forever waiting for 512 bytes (see firefox issue
344
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1544313)
345
+ res.setHeader('Content-Type', 'message/http-sequence')
346
+
347
+ // And we don't want any caches trying to store these stream bodies.
348
+ res.setHeader('Cache-Control', 'no-store')
349
+ }
350
+
341
351
  // Define convenience variables
342
352
  req.version = version
343
353
  req.parents = parents
@@ -848,10 +858,15 @@ async function send_update(res, data, url, peer) {
848
858
  // See also https://github.com/braid-org/braid-spec/issues/73
849
859
  if (res.isSubscription) {
850
860
  var extra_newlines = 1
851
- if (res.is_firefox)
852
- // Work around Firefox network buffering bug
853
- // See https://github.com/braid-org/braidjs/issues/15
854
- extra_newlines = 240
861
+
862
+ // Note: this firefox workaround was replaced with a content-type fix
863
+ // above. We realized that content-type fixes the issue when we found
864
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1544313
865
+ //
866
+ // if (res.is_firefox)
867
+ // // Work around Firefox network buffering bug
868
+ // // See https://github.com/braid-org/braidjs/issues/15
869
+ // extra_newlines = 240
855
870
 
856
871
  for (var i = 0; i < 1 + extra_newlines; i++)
857
872
  res.write("\r\n")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-http",
3
- "version": "1.3.106",
3
+ "version": "1.3.107",
4
4
  "description": "An implementation of Braid-HTTP for Node.js and Browsers",
5
5
  "scripts": {
6
6
  "test": "node test/test.js",