braid-text 0.2.85 → 0.2.86

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/index.js CHANGED
@@ -165,13 +165,18 @@ function create_braid_text() {
165
165
  signal: ac.signal,
166
166
  subscribe: update => {
167
167
  update.signal = ac.signal
168
+ update.dont_retry = true
168
169
  braid_text.put(b, update).then((x) => {
169
- local_first_put()
170
- extend_fork_point(update)
170
+ if (x.ok) {
171
+ local_first_put()
172
+ extend_fork_point(update)
173
+ } else if (x.status === 401 || x.status === 403) {
174
+ options.on_unauthorized?.()
175
+ } else throw new Error('failed to PUT: ' + x.status)
171
176
  }).catch(e => {
172
177
  if (e.name === 'AbortError') {
173
178
  // ignore
174
- } else throw e
179
+ } else handle_error(e)
175
180
  })
176
181
  }
177
182
  }
@@ -199,6 +204,7 @@ function create_braid_text() {
199
204
  await local_first_put_promise
200
205
  disconnect()
201
206
  connect()
207
+ return
202
208
  }
203
209
  options.on_res?.(remote_result)
204
210
  // on_error will call handle_error when connection drops
@@ -705,8 +711,9 @@ function create_braid_text() {
705
711
  var params = {
706
712
  method: 'PUT',
707
713
  signal: options.signal,
708
- retry: () => true,
709
714
  }
715
+ if (!options.dont_retry)
716
+ params.retry = () => true
710
717
  for (var x of ['headers', 'parents', 'version', 'peer', 'body', 'patches'])
711
718
  if (options[x] != null) params[x] = options[x]
712
719
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.2.85",
3
+ "version": "0.2.86",
4
4
  "description": "Library for collaborative text over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braid-text",
package/test/test.js CHANGED
@@ -69,6 +69,21 @@ function createTestServer(options = {}) {
69
69
  return res.end('error')
70
70
  }
71
71
 
72
+ if (req.url.startsWith('/unauthorized') && req.method === 'PUT') {
73
+ res.statusCode = 401
74
+ return res.end('Unauthorized')
75
+ }
76
+
77
+ if (req.url.startsWith('/forbidden') && req.method === 'PUT') {
78
+ res.statusCode = 403
79
+ return res.end('Forbidden')
80
+ }
81
+
82
+ if (req.url.startsWith('/server_error') && req.method === 'PUT') {
83
+ res.statusCode = 500
84
+ return res.end('Internal Server Error')
85
+ }
86
+
72
87
  if (req.url.startsWith('/404')) {
73
88
  res.statusCode = 404
74
89
  return res.end('Not Found')
package/test/tests.js CHANGED
@@ -2066,6 +2066,140 @@ runTest(
2066
2066
  'on_res called'
2067
2067
  )
2068
2068
 
2069
+ runTest(
2070
+ "test braid_text.sync reconnects when inner put fails with non-200 status",
2071
+ async () => {
2072
+ var key = 'test-' + Math.random().toString(36).slice(2)
2073
+
2074
+ // Create a local resource with content
2075
+ var r = await braid_fetch(`/${key}`, {
2076
+ method: 'PUT',
2077
+ body: 'initial'
2078
+ })
2079
+ if (!r.ok) return 'initial put failed: ' + r.status
2080
+
2081
+ var r = await braid_fetch(`/eval`, {
2082
+ method: 'PUT',
2083
+ body: `void (async () => {
2084
+ var connect_count = 0
2085
+ var ac = new AbortController()
2086
+
2087
+ braid_text.sync('/${key}', new URL('http://localhost:8889/server_error'), {
2088
+ signal: ac.signal,
2089
+ on_pre_connect: () => {
2090
+ connect_count++
2091
+ if (connect_count >= 2) {
2092
+ ac.abort()
2093
+ res.end('reconnected after put failure')
2094
+ }
2095
+ }
2096
+ })
2097
+
2098
+ // Trigger a local put which will fail when synced to the error endpoint
2099
+ await new Promise(done => setTimeout(done, 100))
2100
+ await braid_text.put('/${key}', { body: 'trigger sync' })
2101
+
2102
+ // Wait for reconnect attempt
2103
+ await new Promise(done => setTimeout(done, 2000))
2104
+ ac.abort()
2105
+ res.end('did not reconnect')
2106
+ })()`
2107
+ })
2108
+ if (!r.ok) return 'eval failed: ' + r.status
2109
+
2110
+ return await r.text()
2111
+ },
2112
+ 'reconnected after put failure'
2113
+ )
2114
+
2115
+ runTest(
2116
+ "test braid_text.sync on_unauthorized callback for 401",
2117
+ async () => {
2118
+ var key = 'test-' + Math.random().toString(36).slice(2)
2119
+
2120
+ // Create a local resource with content
2121
+ var r = await braid_fetch(`/${key}`, {
2122
+ method: 'PUT',
2123
+ body: 'initial'
2124
+ })
2125
+ if (!r.ok) return 'initial put failed: ' + r.status
2126
+
2127
+ var r = await braid_fetch(`/eval`, {
2128
+ method: 'PUT',
2129
+ body: `void (async () => {
2130
+ var unauthorized_called = false
2131
+ var ac = new AbortController()
2132
+
2133
+ braid_text.sync('/${key}', new URL('http://localhost:8889/unauthorized'), {
2134
+ signal: ac.signal,
2135
+ on_unauthorized: () => {
2136
+ unauthorized_called = true
2137
+ ac.abort()
2138
+ res.end('on_unauthorized called')
2139
+ }
2140
+ })
2141
+
2142
+ // Trigger a local put which will get 401 when synced
2143
+ await new Promise(done => setTimeout(done, 100))
2144
+ await braid_text.put('/${key}', { body: 'trigger sync' })
2145
+
2146
+ // Wait for callback
2147
+ await new Promise(done => setTimeout(done, 2000))
2148
+ ac.abort()
2149
+ res.end('on_unauthorized not called')
2150
+ })()`
2151
+ })
2152
+ if (!r.ok) return 'eval failed: ' + r.status
2153
+
2154
+ return await r.text()
2155
+ },
2156
+ 'on_unauthorized called'
2157
+ )
2158
+
2159
+ runTest(
2160
+ "test braid_text.sync on_unauthorized callback for 403",
2161
+ async () => {
2162
+ var key = 'test-' + Math.random().toString(36).slice(2)
2163
+
2164
+ // Create a local resource with content
2165
+ var r = await braid_fetch(`/${key}`, {
2166
+ method: 'PUT',
2167
+ body: 'initial'
2168
+ })
2169
+ if (!r.ok) return 'initial put failed: ' + r.status
2170
+
2171
+ var r = await braid_fetch(`/eval`, {
2172
+ method: 'PUT',
2173
+ body: `void (async () => {
2174
+ var unauthorized_called = false
2175
+ var ac = new AbortController()
2176
+
2177
+ braid_text.sync('/${key}', new URL('http://localhost:8889/forbidden'), {
2178
+ signal: ac.signal,
2179
+ on_unauthorized: () => {
2180
+ unauthorized_called = true
2181
+ ac.abort()
2182
+ res.end('on_unauthorized called')
2183
+ }
2184
+ })
2185
+
2186
+ // Trigger a local put which will get 403 when synced
2187
+ await new Promise(done => setTimeout(done, 100))
2188
+ await braid_text.put('/${key}', { body: 'trigger sync' })
2189
+
2190
+ // Wait for callback
2191
+ await new Promise(done => setTimeout(done, 2000))
2192
+ ac.abort()
2193
+ res.end('on_unauthorized not called')
2194
+ })()`
2195
+ })
2196
+ if (!r.ok) return 'eval failed: ' + r.status
2197
+
2198
+ return await r.text()
2199
+ },
2200
+ 'on_unauthorized called'
2201
+ )
2202
+
2069
2203
  runTest(
2070
2204
  "test getting a binary update from a subscription",
2071
2205
  async () => {