braid-text 0.2.84 → 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
@@ -108,7 +108,7 @@ function create_braid_text() {
108
108
 
109
109
  connect()
110
110
  async function connect() {
111
- if (options.on_connect) options.on_connect()
111
+ if (options.on_pre_connect) await options.on_pre_connect()
112
112
 
113
113
  if (closed) return
114
114
 
@@ -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
  }
@@ -187,7 +192,10 @@ function create_braid_text() {
187
192
  await braid_text.put(a, update)
188
193
  extend_fork_point(update)
189
194
  },
190
- on_error: handle_error
195
+ on_error: e => {
196
+ options.on_disconnect?.()
197
+ handle_error(e)
198
+ }
191
199
  }
192
200
  // Handle case where remote doesn't exist yet - wait for local to create it
193
201
  var remote_result = await braid_text.get(b, b_ops)
@@ -196,6 +204,7 @@ function create_braid_text() {
196
204
  await local_first_put_promise
197
205
  disconnect()
198
206
  connect()
207
+ return
199
208
  }
200
209
  options.on_res?.(remote_result)
201
210
  // on_error will call handle_error when connection drops
@@ -702,8 +711,9 @@ function create_braid_text() {
702
711
  var params = {
703
712
  method: 'PUT',
704
713
  signal: options.signal,
705
- retry: () => true,
706
714
  }
715
+ if (!options.dont_retry)
716
+ params.retry = () => true
707
717
  for (var x of ['headers', 'parents', 'version', 'peer', 'body', 'patches'])
708
718
  if (options[x] != null) params[x] = options[x]
709
719
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-text",
3
- "version": "0.2.84",
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
@@ -46,7 +46,7 @@ runTest(
46
46
  var ac = new AbortController()
47
47
  braid_text.sync('/${key}', new URL('http://localhost:8889/have_error'), {
48
48
  signal: ac.signal,
49
- on_connect: () => {
49
+ on_pre_connect: () => {
50
50
  count++
51
51
  if (count === 2) {
52
52
  res.end('it reconnected!')
@@ -198,7 +198,7 @@ runTest(
198
198
  var ac = new AbortController()
199
199
  braid_text.sync('/${key_a}', new URL('http://localhost:8889/have_error'), {
200
200
  signal: ac.signal,
201
- on_connect: () => {
201
+ on_pre_connect: () => {
202
202
  count++
203
203
  if (count === 2) {
204
204
  res.end('it reconnected!')
@@ -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 () => {