braid-text 0.2.77 → 0.2.78
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 +52 -48
- package/package.json +1 -1
- package/test/fuzz-test.js +12 -8
- package/test/tests.js +75 -16
package/index.js
CHANGED
|
@@ -17,25 +17,37 @@ function create_braid_text() {
|
|
|
17
17
|
let max_encoded_key_size = 240
|
|
18
18
|
|
|
19
19
|
braid_text.sync = async (a, b, options = {}) => {
|
|
20
|
-
var unsync_cbs = []
|
|
21
|
-
options.my_unsync = () => unsync_cbs.forEach(cb => cb())
|
|
22
|
-
|
|
23
20
|
if (!options.merge_type) options.merge_type = 'dt'
|
|
24
21
|
|
|
25
22
|
if ((a instanceof URL) === (b instanceof URL)) {
|
|
23
|
+
// Both are URLs or both are local keys
|
|
24
|
+
var a_first_put, b_first_put
|
|
25
|
+
var a_first_put_promise = new Promise(done => a_first_put = done)
|
|
26
|
+
var b_first_put_promise = new Promise(done => b_first_put = done)
|
|
27
|
+
|
|
26
28
|
var a_ops = {
|
|
27
|
-
|
|
29
|
+
signal: options.signal,
|
|
30
|
+
subscribe: update => {
|
|
31
|
+
update.signal = options.signal
|
|
32
|
+
braid_text.put(b, update).then(a_first_put)
|
|
33
|
+
},
|
|
28
34
|
merge_type: options.merge_type,
|
|
29
35
|
}
|
|
30
|
-
braid_text.get(a, a_ops)
|
|
31
|
-
|
|
36
|
+
braid_text.get(a, a_ops).then(x =>
|
|
37
|
+
x || b_first_put_promise.then(() =>
|
|
38
|
+
braid_text.get(a, a_ops)))
|
|
32
39
|
|
|
33
40
|
var b_ops = {
|
|
34
|
-
|
|
41
|
+
signal: options.signal,
|
|
42
|
+
subscribe: update => {
|
|
43
|
+
update.signal = options.signal
|
|
44
|
+
braid_text.put(a, update).then(b_first_put)
|
|
45
|
+
},
|
|
35
46
|
merge_type: options.merge_type,
|
|
36
47
|
}
|
|
37
|
-
braid_text.get(b, b_ops)
|
|
38
|
-
|
|
48
|
+
braid_text.get(b, b_ops).then(x =>
|
|
49
|
+
x || a_first_put_promise.then(() =>
|
|
50
|
+
braid_text.get(b, b_ops)))
|
|
39
51
|
} else {
|
|
40
52
|
// make a=local and b=remote (swap if not)
|
|
41
53
|
if (a instanceof URL) { let swap = a; a = b; b = swap }
|
|
@@ -78,12 +90,15 @@ function create_braid_text() {
|
|
|
78
90
|
}
|
|
79
91
|
|
|
80
92
|
var closed
|
|
81
|
-
var disconnect
|
|
82
|
-
|
|
93
|
+
var disconnect = () => {}
|
|
94
|
+
options.signal?.addEventListener('abort', () => {
|
|
83
95
|
closed = true
|
|
84
96
|
disconnect()
|
|
85
97
|
})
|
|
86
98
|
|
|
99
|
+
var local_first_put
|
|
100
|
+
var local_first_put_promise = new Promise(done => local_first_put = done)
|
|
101
|
+
|
|
87
102
|
connect()
|
|
88
103
|
async function connect() {
|
|
89
104
|
if (options.on_connect) options.on_connect()
|
|
@@ -91,9 +106,7 @@ function create_braid_text() {
|
|
|
91
106
|
if (closed) return
|
|
92
107
|
|
|
93
108
|
var ac = new AbortController()
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
disconnect = () => disconnect_cbs.forEach(cb => cb())
|
|
109
|
+
disconnect = () => ac.abort()
|
|
97
110
|
|
|
98
111
|
try {
|
|
99
112
|
// fork-point
|
|
@@ -142,9 +155,11 @@ function create_braid_text() {
|
|
|
142
155
|
|
|
143
156
|
// local -> remote
|
|
144
157
|
var a_ops = {
|
|
158
|
+
signal: ac.signal,
|
|
145
159
|
subscribe: update => {
|
|
146
160
|
update.signal = ac.signal
|
|
147
161
|
braid_text.put(b, update).then((x) => {
|
|
162
|
+
local_first_put()
|
|
148
163
|
extend_fork_point(update)
|
|
149
164
|
}).catch(e => {
|
|
150
165
|
if (e.name === 'AbortError') {
|
|
@@ -155,20 +170,26 @@ function create_braid_text() {
|
|
|
155
170
|
}
|
|
156
171
|
if (resource.meta.fork_point)
|
|
157
172
|
a_ops.parents = resource.meta.fork_point
|
|
158
|
-
disconnect_cbs.push(() => braid_text.forget(a, a_ops))
|
|
159
173
|
braid_text.get(a, a_ops)
|
|
160
174
|
|
|
161
175
|
// remote -> local
|
|
162
176
|
var b_ops = {
|
|
177
|
+
signal: ac.signal,
|
|
163
178
|
dont_retry: true,
|
|
164
179
|
subscribe: async update => {
|
|
165
180
|
await braid_text.put(a, update)
|
|
166
181
|
extend_fork_point(update)
|
|
167
182
|
},
|
|
168
183
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
184
|
+
// Handle case where remote doesn't exist yet - wait for local to create it
|
|
185
|
+
var remote_result = await braid_text.get(b, b_ops)
|
|
186
|
+
if (remote_result === null) {
|
|
187
|
+
// Remote doesn't exist yet, wait for local to put something
|
|
188
|
+
await local_first_put_promise
|
|
189
|
+
disconnect()
|
|
190
|
+
connect()
|
|
191
|
+
}
|
|
192
|
+
// NOTE: if remote exists, this should not return, but it might throw
|
|
172
193
|
} catch (e) {
|
|
173
194
|
if (closed) return
|
|
174
195
|
|
|
@@ -176,7 +197,7 @@ function create_braid_text() {
|
|
|
176
197
|
console.log(`disconnected, retrying in 1 second`)
|
|
177
198
|
setTimeout(connect, 1000)
|
|
178
199
|
}
|
|
179
|
-
}
|
|
200
|
+
}
|
|
180
201
|
}
|
|
181
202
|
}
|
|
182
203
|
|
|
@@ -468,15 +489,9 @@ function create_braid_text() {
|
|
|
468
489
|
|
|
469
490
|
// Handle URL - make a DELETE request
|
|
470
491
|
if (key instanceof URL) {
|
|
471
|
-
options.my_abort = new AbortController()
|
|
472
|
-
if (options.signal)
|
|
473
|
-
options.signal.addEventListener('abort', () =>
|
|
474
|
-
options.my_abort.abort())
|
|
475
|
-
|
|
476
492
|
var params = {
|
|
477
493
|
method: 'DELETE',
|
|
478
|
-
signal: options.
|
|
479
|
-
retry: () => true,
|
|
494
|
+
signal: options.signal,
|
|
480
495
|
}
|
|
481
496
|
for (var x of ['headers', 'peer'])
|
|
482
497
|
if (options[x] != null) params[x] = options[x]
|
|
@@ -502,19 +517,21 @@ function create_braid_text() {
|
|
|
502
517
|
if (key instanceof URL) {
|
|
503
518
|
if (!options) options = {}
|
|
504
519
|
|
|
505
|
-
options.my_abort = new AbortController()
|
|
506
|
-
|
|
507
520
|
var params = {
|
|
508
|
-
signal: options.
|
|
521
|
+
signal: options.signal,
|
|
509
522
|
subscribe: !!options.subscribe,
|
|
510
523
|
heartbeats: 120,
|
|
511
524
|
}
|
|
512
|
-
if (!options.dont_retry)
|
|
525
|
+
if (!options.dont_retry) {
|
|
526
|
+
params.retry = (res) => res.status !== 404
|
|
527
|
+
}
|
|
513
528
|
for (var x of ['headers', 'parents', 'version', 'peer'])
|
|
514
529
|
if (options[x] != null) params[x] = options[x]
|
|
515
530
|
|
|
516
531
|
var res = await braid_fetch(key.href, params)
|
|
517
532
|
|
|
533
|
+
if (res.status === 404) return null
|
|
534
|
+
|
|
518
535
|
if (options.subscribe) {
|
|
519
536
|
if (options.dont_retry) {
|
|
520
537
|
var error_happened
|
|
@@ -603,6 +620,8 @@ function create_braid_text() {
|
|
|
603
620
|
|
|
604
621
|
options.my_last_sent_version = x.version
|
|
605
622
|
resource.simpleton_clients.add(options)
|
|
623
|
+
options.signal?.addEventListener('abort', () =>
|
|
624
|
+
resource.simpleton_clients.delete(options))
|
|
606
625
|
} else {
|
|
607
626
|
|
|
608
627
|
if (options.accept_encoding?.match(/updates\s*\((.*)\)/)?.[1].split(',').map(x=>x.trim()).includes('dt')) {
|
|
@@ -650,22 +669,12 @@ function create_braid_text() {
|
|
|
650
669
|
}
|
|
651
670
|
|
|
652
671
|
resource.clients.add(options)
|
|
672
|
+
options.signal?.addEventListener('abort', () =>
|
|
673
|
+
resource.clients.delete(options))
|
|
653
674
|
}
|
|
654
675
|
}
|
|
655
676
|
}
|
|
656
677
|
|
|
657
|
-
braid_text.forget = async (key, options) => {
|
|
658
|
-
if (!options) throw new Error('options is required')
|
|
659
|
-
|
|
660
|
-
if (key instanceof URL) return options.my_abort.abort()
|
|
661
|
-
|
|
662
|
-
let resource = (typeof key == 'string') ? await get_resource(key) : key
|
|
663
|
-
|
|
664
|
-
if (options.merge_type != "dt")
|
|
665
|
-
resource.simpleton_clients.delete(options)
|
|
666
|
-
else resource.clients.delete(options)
|
|
667
|
-
}
|
|
668
|
-
|
|
669
678
|
braid_text.put = async (key, options) => {
|
|
670
679
|
if (options.version) {
|
|
671
680
|
validate_version_array(options.version)
|
|
@@ -677,14 +686,9 @@ function create_braid_text() {
|
|
|
677
686
|
}
|
|
678
687
|
|
|
679
688
|
if (key instanceof URL) {
|
|
680
|
-
options.my_abort = new AbortController()
|
|
681
|
-
if (options.signal)
|
|
682
|
-
options.signal.addEventListener('abort', () =>
|
|
683
|
-
options.my_abort.abort())
|
|
684
|
-
|
|
685
689
|
var params = {
|
|
686
690
|
method: 'PUT',
|
|
687
|
-
signal: options.
|
|
691
|
+
signal: options.signal,
|
|
688
692
|
retry: () => true,
|
|
689
693
|
}
|
|
690
694
|
for (var x of ['headers', 'parents', 'version', 'peer', 'body', 'patches'])
|
package/package.json
CHANGED
package/test/fuzz-test.js
CHANGED
|
@@ -270,16 +270,20 @@ async function main() {
|
|
|
270
270
|
|
|
271
271
|
// try getting updates from middle_doc to doc
|
|
272
272
|
if (!v_eq(middle_v, doc_v)) {
|
|
273
|
-
var
|
|
273
|
+
var ac = new AbortController()
|
|
274
274
|
await new Promise(async done => {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
275
|
+
braid_text.get('doc', {
|
|
276
|
+
signal: ac.signal,
|
|
277
|
+
merge_type: 'dt',
|
|
278
|
+
parents: middle_v,
|
|
279
|
+
subscribe: async update => {
|
|
280
|
+
await braid_text.put('middle_doc', update)
|
|
281
|
+
middle_v = (await braid_text.get_resource('middle_doc')).version
|
|
282
|
+
if (v_eq(doc_v, middle_v)) done()
|
|
283
|
+
}
|
|
284
|
+
})
|
|
281
285
|
})
|
|
282
|
-
|
|
286
|
+
ac.abort()
|
|
283
287
|
}
|
|
284
288
|
|
|
285
289
|
if (await braid_text.get('middle_doc') != await braid_text.get('doc')) throw new Error('bad')
|
package/test/tests.js
CHANGED
|
@@ -50,13 +50,14 @@ runTest(
|
|
|
50
50
|
method: 'PUT',
|
|
51
51
|
body: `void (async () => {
|
|
52
52
|
var count = 0
|
|
53
|
-
var
|
|
54
|
-
braid_text.sync('/${key}', new URL('http://localhost:8889/have_error'),
|
|
53
|
+
var ac = new AbortController()
|
|
54
|
+
braid_text.sync('/${key}', new URL('http://localhost:8889/have_error'), {
|
|
55
|
+
signal: ac.signal,
|
|
55
56
|
on_connect: () => {
|
|
56
57
|
count++
|
|
57
58
|
if (count === 2) {
|
|
58
59
|
res.end('it reconnected!')
|
|
59
|
-
|
|
60
|
+
ac.abort()
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
})
|
|
@@ -201,13 +202,14 @@ runTest(
|
|
|
201
202
|
method: 'PUT',
|
|
202
203
|
body: `void (async () => {
|
|
203
204
|
var count = 0
|
|
204
|
-
var
|
|
205
|
-
braid_text.sync('/${key_a}', new URL('http://localhost:8889/have_error'),
|
|
205
|
+
var ac = new AbortController()
|
|
206
|
+
braid_text.sync('/${key_a}', new URL('http://localhost:8889/have_error'), {
|
|
207
|
+
signal: ac.signal,
|
|
206
208
|
on_connect: () => {
|
|
207
209
|
count++
|
|
208
210
|
if (count === 2) {
|
|
209
211
|
res.end('it reconnected!')
|
|
210
|
-
|
|
212
|
+
ac.abort()
|
|
211
213
|
}
|
|
212
214
|
}
|
|
213
215
|
})
|
|
@@ -265,10 +267,10 @@ runTest(
|
|
|
265
267
|
var r = await braid_fetch(`/eval`, {
|
|
266
268
|
method: 'PUT',
|
|
267
269
|
body: `void (async () => {
|
|
268
|
-
var
|
|
269
|
-
braid_text.sync('/${key_a}', '/${key_b}',
|
|
270
|
+
var ac = new AbortController()
|
|
271
|
+
braid_text.sync('/${key_a}', '/${key_b}', {signal: ac.signal})
|
|
270
272
|
await new Promise(done => setTimeout(done, 100))
|
|
271
|
-
|
|
273
|
+
ac.abort()
|
|
272
274
|
res.end('')
|
|
273
275
|
})()`
|
|
274
276
|
})
|
|
@@ -283,10 +285,10 @@ runTest(
|
|
|
283
285
|
var r = await braid_fetch(`/eval`, {
|
|
284
286
|
method: 'PUT',
|
|
285
287
|
body: `void (async () => {
|
|
286
|
-
var
|
|
287
|
-
braid_text.sync('/${key_a}', new URL('http://localhost:8889/${key_b}'),
|
|
288
|
+
var ac = new AbortController()
|
|
289
|
+
braid_text.sync('/${key_a}', new URL('http://localhost:8889/${key_b}'), {signal: ac.signal})
|
|
288
290
|
await new Promise(done => setTimeout(done, 100))
|
|
289
|
-
|
|
291
|
+
ac.abort()
|
|
290
292
|
res.end('')
|
|
291
293
|
})()`
|
|
292
294
|
})
|
|
@@ -456,14 +458,15 @@ runTest(
|
|
|
456
458
|
method: 'PUT',
|
|
457
459
|
body: `void (async () => {
|
|
458
460
|
var url = new URL('http://localhost:8889/${key}')
|
|
461
|
+
var ac = new AbortController()
|
|
459
462
|
var update = await new Promise(done => {
|
|
460
|
-
|
|
463
|
+
braid_text.get(url, {
|
|
464
|
+
signal: ac.signal,
|
|
461
465
|
subscribe: update => {
|
|
462
|
-
|
|
466
|
+
ac.abort()
|
|
463
467
|
done(update)
|
|
464
468
|
}
|
|
465
|
-
}
|
|
466
|
-
braid_text.get(url, o)
|
|
469
|
+
})
|
|
467
470
|
})
|
|
468
471
|
res.end(update.body)
|
|
469
472
|
})()`
|
|
@@ -1929,6 +1932,62 @@ runTest(
|
|
|
1929
1932
|
'got: '
|
|
1930
1933
|
)
|
|
1931
1934
|
|
|
1935
|
+
runTest(
|
|
1936
|
+
"test braid_text.get(url) returns null for 404",
|
|
1937
|
+
async () => {
|
|
1938
|
+
// Use the /404 endpoint that always returns 404
|
|
1939
|
+
var r = await braid_fetch(`/eval`, {
|
|
1940
|
+
method: 'PUT',
|
|
1941
|
+
body: `void (async () => {
|
|
1942
|
+
var result = await braid_text.get(new URL('http://localhost:8889/404'))
|
|
1943
|
+
res.end(result === null ? 'null' : 'not null: ' + result)
|
|
1944
|
+
})()`
|
|
1945
|
+
})
|
|
1946
|
+
return await r.text()
|
|
1947
|
+
},
|
|
1948
|
+
'null'
|
|
1949
|
+
)
|
|
1950
|
+
|
|
1951
|
+
runTest(
|
|
1952
|
+
"test braid_text.sync handles remote not existing yet",
|
|
1953
|
+
async () => {
|
|
1954
|
+
var local_key = 'test-local-' + Math.random().toString(36).slice(2)
|
|
1955
|
+
var remote_key = 'test-remote-' + Math.random().toString(36).slice(2)
|
|
1956
|
+
|
|
1957
|
+
// Start sync between a local key and a remote URL that doesn't exist yet
|
|
1958
|
+
// The sync should wait for local to create something, then push to remote
|
|
1959
|
+
var r = await braid_fetch(`/eval`, {
|
|
1960
|
+
method: 'PUT',
|
|
1961
|
+
body: `void (async () => {
|
|
1962
|
+
var ac = new AbortController()
|
|
1963
|
+
|
|
1964
|
+
// Start sync - remote doesn't exist yet
|
|
1965
|
+
braid_text.sync('/${local_key}', new URL('http://localhost:8889/${remote_key}'), {
|
|
1966
|
+
signal: ac.signal
|
|
1967
|
+
})
|
|
1968
|
+
|
|
1969
|
+
// Wait a bit then put something locally
|
|
1970
|
+
await new Promise(done => setTimeout(done, 100))
|
|
1971
|
+
await braid_text.put('/${local_key}', { body: 'created locally' })
|
|
1972
|
+
|
|
1973
|
+
// Wait for sync to propagate
|
|
1974
|
+
await new Promise(done => setTimeout(done, 200))
|
|
1975
|
+
|
|
1976
|
+
// Stop sync
|
|
1977
|
+
ac.abort()
|
|
1978
|
+
|
|
1979
|
+
res.end('done')
|
|
1980
|
+
})()`
|
|
1981
|
+
})
|
|
1982
|
+
if (!r.ok) return 'eval failed: ' + r.status
|
|
1983
|
+
|
|
1984
|
+
// Check that remote now has the content
|
|
1985
|
+
var r2 = await braid_fetch(`/${remote_key}`)
|
|
1986
|
+
return await r2.text()
|
|
1987
|
+
},
|
|
1988
|
+
'created locally'
|
|
1989
|
+
)
|
|
1990
|
+
|
|
1932
1991
|
runTest(
|
|
1933
1992
|
"test getting a binary update from a subscription",
|
|
1934
1993
|
async () => {
|