braid-blob 0.0.24 → 0.0.25
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 +70 -42
- package/package.json +1 -1
- package/test/tests.js +219 -8
package/index.js
CHANGED
|
@@ -40,15 +40,10 @@ function create_braid_blob() {
|
|
|
40
40
|
braid_blob.put = async (key, body, options = {}) => {
|
|
41
41
|
// Handle URL case - make a remote PUT request
|
|
42
42
|
if (key instanceof URL) {
|
|
43
|
-
options.my_abort = new AbortController()
|
|
44
|
-
if (options.signal) {
|
|
45
|
-
options.signal.addEventListener('abort', () =>
|
|
46
|
-
options.my_abort.abort())
|
|
47
|
-
}
|
|
48
43
|
|
|
49
44
|
var params = {
|
|
50
45
|
method: 'PUT',
|
|
51
|
-
signal: options.
|
|
46
|
+
signal: options.signal,
|
|
52
47
|
retry: () => true,
|
|
53
48
|
body: body
|
|
54
49
|
}
|
|
@@ -62,6 +57,7 @@ function create_braid_blob() {
|
|
|
62
57
|
}
|
|
63
58
|
|
|
64
59
|
await braid_blob.init()
|
|
60
|
+
if (options.signal?.aborted) return
|
|
65
61
|
|
|
66
62
|
// Read the meta data using new meta API
|
|
67
63
|
var meta = braid_blob.db.get_meta(key) || {}
|
|
@@ -83,6 +79,7 @@ function create_braid_blob() {
|
|
|
83
79
|
// Write the file using url-file-db (unless skip_write is set)
|
|
84
80
|
if (!options.skip_write)
|
|
85
81
|
await braid_blob.db.write(key, body)
|
|
82
|
+
if (options.signal?.aborted) return
|
|
86
83
|
|
|
87
84
|
// Update only the fields we want to change in metadata
|
|
88
85
|
var meta_updates = { event: their_e }
|
|
@@ -90,6 +87,7 @@ function create_braid_blob() {
|
|
|
90
87
|
meta_updates.content_type = options.content_type
|
|
91
88
|
|
|
92
89
|
await braid_blob.db.update_meta(key, meta_updates)
|
|
90
|
+
if (options.signal?.aborted) return
|
|
93
91
|
|
|
94
92
|
// Notify all subscriptions of the update
|
|
95
93
|
// (except the peer which made the PUT request itself)
|
|
@@ -109,21 +107,21 @@ function create_braid_blob() {
|
|
|
109
107
|
braid_blob.get = async (key, options = {}) => {
|
|
110
108
|
// Handle URL case - make a remote GET request
|
|
111
109
|
if (key instanceof URL) {
|
|
112
|
-
options.my_abort = new AbortController()
|
|
113
|
-
|
|
114
110
|
var params = {
|
|
115
|
-
signal: options.
|
|
111
|
+
signal: options.signal,
|
|
116
112
|
subscribe: !!options.subscribe,
|
|
117
113
|
heartbeats: 120,
|
|
118
114
|
}
|
|
119
115
|
if (!options.dont_retry) {
|
|
120
|
-
params.retry = () =>
|
|
116
|
+
params.retry = (res) => res.status !== 404
|
|
121
117
|
}
|
|
122
118
|
for (var x of ['headers', 'parents', 'version', 'peer'])
|
|
123
119
|
if (options[x] != null) params[x] = options[x]
|
|
124
120
|
|
|
125
121
|
var res = await braid_fetch(key.href, params)
|
|
126
122
|
|
|
123
|
+
if (res.status === 404) return null
|
|
124
|
+
|
|
127
125
|
if (options.subscribe) {
|
|
128
126
|
if (options.dont_retry) {
|
|
129
127
|
var error_happened
|
|
@@ -154,6 +152,7 @@ function create_braid_blob() {
|
|
|
154
152
|
content_type: meta.content_type
|
|
155
153
|
}
|
|
156
154
|
if (options.header_cb) await options.header_cb(result)
|
|
155
|
+
if (options.signal?.aborted) return
|
|
157
156
|
// Check if requested version/parents is newer than what we have - if so, we don't have it
|
|
158
157
|
if (options.version && options.version.length && compare_events(options.version[0], meta.event) > 0)
|
|
159
158
|
throw new Error('unkown version: ' + options.version)
|
|
@@ -164,7 +163,8 @@ function create_braid_blob() {
|
|
|
164
163
|
if (options.subscribe) {
|
|
165
164
|
var subscribe_chain = Promise.resolve()
|
|
166
165
|
options.my_subscribe = (x) => subscribe_chain =
|
|
167
|
-
subscribe_chain.then(() =>
|
|
166
|
+
subscribe_chain.then(() =>
|
|
167
|
+
!options.signal?.aborted && options.subscribe(x))
|
|
168
168
|
|
|
169
169
|
// Start a subscription for future updates
|
|
170
170
|
if (!braid_blob.key_to_subs[key])
|
|
@@ -181,14 +181,14 @@ function create_braid_blob() {
|
|
|
181
181
|
}
|
|
182
182
|
})
|
|
183
183
|
|
|
184
|
-
|
|
185
|
-
result.unsubscribe = () => {
|
|
184
|
+
options.signal?.addEventListener('abort', () => {
|
|
186
185
|
braid_blob.key_to_subs[key].delete(peer)
|
|
187
186
|
if (!braid_blob.key_to_subs[key].size)
|
|
188
187
|
delete braid_blob.key_to_subs[key]
|
|
189
|
-
}
|
|
188
|
+
})
|
|
190
189
|
|
|
191
190
|
if (options.before_send_cb) await options.before_send_cb(result)
|
|
191
|
+
if (options.signal?.aborted) return
|
|
192
192
|
|
|
193
193
|
// Send an immediate update if needed
|
|
194
194
|
if (!options.parents ||
|
|
@@ -212,15 +212,10 @@ function create_braid_blob() {
|
|
|
212
212
|
braid_blob.delete = async (key, options = {}) => {
|
|
213
213
|
// Handle URL case - make a remote DELETE request
|
|
214
214
|
if (key instanceof URL) {
|
|
215
|
-
options.my_abort = new AbortController()
|
|
216
|
-
if (options.signal) {
|
|
217
|
-
options.signal.addEventListener('abort', () =>
|
|
218
|
-
options.my_abort.abort())
|
|
219
|
-
}
|
|
220
215
|
|
|
221
216
|
var params = {
|
|
222
217
|
method: 'DELETE',
|
|
223
|
-
signal: options.
|
|
218
|
+
signal: options.signal
|
|
224
219
|
}
|
|
225
220
|
for (var x of ['headers', 'peer'])
|
|
226
221
|
if (options[x] != null) params[x] = options[x]
|
|
@@ -229,6 +224,7 @@ function create_braid_blob() {
|
|
|
229
224
|
}
|
|
230
225
|
|
|
231
226
|
await braid_blob.init()
|
|
227
|
+
if (options.signal?.aborted) return
|
|
232
228
|
|
|
233
229
|
// Delete the file from the database
|
|
234
230
|
await braid_blob.db.delete(key)
|
|
@@ -329,27 +325,40 @@ function create_braid_blob() {
|
|
|
329
325
|
})
|
|
330
326
|
}
|
|
331
327
|
|
|
332
|
-
braid_blob.sync =
|
|
333
|
-
var unsync_cbs = []
|
|
334
|
-
options.my_unsync = () => unsync_cbs.forEach(cb => cb())
|
|
335
|
-
|
|
328
|
+
braid_blob.sync = (a, b, options = {}) => {
|
|
336
329
|
if ((a instanceof URL) === (b instanceof URL)) {
|
|
337
330
|
// Both are URLs or both are local keys
|
|
331
|
+
var a_first_put, b_first_put
|
|
332
|
+
var a_first_put_promise = new Promise(done => a_first_put = done)
|
|
333
|
+
var b_first_put_promise = new Promise(done => b_first_put = done)
|
|
334
|
+
|
|
338
335
|
var a_ops = {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
336
|
+
signal: options.signal,
|
|
337
|
+
subscribe: update => {
|
|
338
|
+
braid_blob.put(b, update.body, {
|
|
339
|
+
signal: options.signal,
|
|
340
|
+
version: update.version,
|
|
341
|
+
content_type: update.headers?.['content-type']
|
|
342
|
+
}).then(a_first_put)
|
|
343
|
+
}
|
|
343
344
|
}
|
|
344
|
-
braid_blob.get(a, a_ops)
|
|
345
|
+
braid_blob.get(a, a_ops).then(x =>
|
|
346
|
+
x || b_first_put_promise.then(() =>
|
|
347
|
+
braid_blob.get(a, a_ops)))
|
|
345
348
|
|
|
346
349
|
var b_ops = {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
350
|
+
signal: options.signal,
|
|
351
|
+
subscribe: update => {
|
|
352
|
+
braid_blob.put(a, update.body, {
|
|
353
|
+
signal: options.signal,
|
|
354
|
+
version: update.version,
|
|
355
|
+
content_type: update.headers?.['content-type']
|
|
356
|
+
}).then(b_first_put)
|
|
357
|
+
}
|
|
351
358
|
}
|
|
352
|
-
braid_blob.get(b, b_ops)
|
|
359
|
+
braid_blob.get(b, b_ops).then(x =>
|
|
360
|
+
x || a_first_put_promise.then(() =>
|
|
361
|
+
braid_blob.get(b, b_ops)))
|
|
353
362
|
} else {
|
|
354
363
|
// One is local, one is remote - make a=local and b=remote (swap if not)
|
|
355
364
|
if (a instanceof URL) {
|
|
@@ -357,17 +366,23 @@ function create_braid_blob() {
|
|
|
357
366
|
}
|
|
358
367
|
|
|
359
368
|
var closed = false
|
|
360
|
-
options.my_unsync = () => { closed = true; disconnect() }
|
|
361
|
-
|
|
362
369
|
var disconnect = () => { }
|
|
370
|
+
options.signal?.addEventListener('abort', () =>
|
|
371
|
+
{ closed = true; disconnect() })
|
|
372
|
+
|
|
373
|
+
var local_first_put, remote_first_put
|
|
374
|
+
var local_first_put_promise = new Promise(done => local_first_put = done)
|
|
375
|
+
var remote_first_put_promise = new Promise(done => remote_first_put = done)
|
|
376
|
+
|
|
363
377
|
async function connect() {
|
|
364
378
|
var ac = new AbortController()
|
|
365
|
-
|
|
366
|
-
disconnect = () => disconnect_cbs.forEach(cb => cb())
|
|
379
|
+
disconnect = () => ac.abort()
|
|
367
380
|
|
|
368
381
|
try {
|
|
369
382
|
// Check if remote has our current version (simple fork-point check)
|
|
370
|
-
var local_result = await braid_blob.get(a
|
|
383
|
+
var local_result = await braid_blob.get(a, {
|
|
384
|
+
signal: ac.signal
|
|
385
|
+
})
|
|
371
386
|
var local_version = local_result ? local_result.version : null
|
|
372
387
|
var server_has_our_version = false
|
|
373
388
|
|
|
@@ -383,12 +398,13 @@ function create_braid_blob() {
|
|
|
383
398
|
|
|
384
399
|
// Local -> remote: subscribe to future local changes
|
|
385
400
|
var a_ops = {
|
|
401
|
+
signal: ac.signal,
|
|
386
402
|
subscribe: update => {
|
|
387
|
-
update.signal = ac.signal
|
|
388
403
|
braid_blob.put(b, update.body, {
|
|
404
|
+
signal: ac.signal,
|
|
389
405
|
version: update.version,
|
|
390
406
|
content_type: update.content_type
|
|
391
|
-
}).catch(e => {
|
|
407
|
+
}).then(local_first_put).catch(e => {
|
|
392
408
|
if (e.name === 'AbortError') {
|
|
393
409
|
// ignore
|
|
394
410
|
} else throw e
|
|
@@ -400,24 +416,36 @@ function create_braid_blob() {
|
|
|
400
416
|
if (server_has_our_version) {
|
|
401
417
|
a_ops.parents = local_version
|
|
402
418
|
}
|
|
403
|
-
braid_blob.get(a, a_ops)
|
|
404
419
|
|
|
405
420
|
// Remote -> local: subscribe to remote updates
|
|
406
421
|
var b_ops = {
|
|
422
|
+
signal: ac.signal,
|
|
407
423
|
dont_retry: true,
|
|
408
424
|
subscribe: async update => {
|
|
409
425
|
await braid_blob.put(a, update.body, {
|
|
410
426
|
version: update.version,
|
|
411
427
|
content_type: update.headers?.['content-type']
|
|
412
428
|
})
|
|
429
|
+
remote_first_put()
|
|
413
430
|
},
|
|
414
431
|
}
|
|
415
432
|
// Use fork-point (parents) to avoid receiving data we already have
|
|
416
433
|
if (local_version) {
|
|
417
434
|
b_ops.parents = local_version
|
|
418
435
|
}
|
|
436
|
+
|
|
437
|
+
// Set up both subscriptions, handling cases where one doesn't exist yet
|
|
438
|
+
braid_blob.get(a, a_ops).then(x =>
|
|
439
|
+
x || remote_first_put_promise.then(() =>
|
|
440
|
+
braid_blob.get(a, a_ops)))
|
|
441
|
+
|
|
419
442
|
// NOTE: this should not return, but it might throw
|
|
420
443
|
await braid_blob.get(b, b_ops)
|
|
444
|
+
|
|
445
|
+
// this will only return if it couldn't find the key
|
|
446
|
+
await local_first_put_promise
|
|
447
|
+
disconnect()
|
|
448
|
+
connect()
|
|
421
449
|
} catch (e) {
|
|
422
450
|
if (closed) {
|
|
423
451
|
return
|
package/package.json
CHANGED
package/test/tests.js
CHANGED
|
@@ -1162,12 +1162,14 @@ runTest(
|
|
|
1162
1162
|
// Use an invalid/unreachable URL to trigger an error
|
|
1163
1163
|
var remote_url = new URL('http://localhost:9999/${remote_key}')
|
|
1164
1164
|
|
|
1165
|
-
//
|
|
1166
|
-
var
|
|
1167
|
-
|
|
1165
|
+
// Create an AbortController to stop the sync
|
|
1166
|
+
var ac = new AbortController()
|
|
1167
|
+
|
|
1168
|
+
// Start sync with signal
|
|
1169
|
+
braid_blob.sync('${local_key}', remote_url, { signal: ac.signal })
|
|
1168
1170
|
|
|
1169
1171
|
// Close the sync immediately to trigger the closed path when error occurs
|
|
1170
|
-
|
|
1172
|
+
ac.abort()
|
|
1171
1173
|
|
|
1172
1174
|
res.end('sync started and closed')
|
|
1173
1175
|
} catch (e) {
|
|
@@ -1201,15 +1203,17 @@ runTest(
|
|
|
1201
1203
|
// Use an invalid/unreachable URL to trigger an error
|
|
1202
1204
|
var remote_url = new URL('http://localhost:9999/${remote_key}')
|
|
1203
1205
|
|
|
1204
|
-
//
|
|
1205
|
-
var
|
|
1206
|
-
|
|
1206
|
+
// Create an AbortController to stop the sync
|
|
1207
|
+
var ac = new AbortController()
|
|
1208
|
+
|
|
1209
|
+
// Start sync with signal - should trigger retry on error
|
|
1210
|
+
braid_blob.sync('${local_key}', remote_url, { signal: ac.signal })
|
|
1207
1211
|
|
|
1208
1212
|
// Wait a bit for the error to occur and retry message to print
|
|
1209
1213
|
await new Promise(done => setTimeout(done, 200))
|
|
1210
1214
|
|
|
1211
1215
|
// Now close it to stop retrying
|
|
1212
|
-
|
|
1216
|
+
ac.abort()
|
|
1213
1217
|
|
|
1214
1218
|
res.end('sync error occurred')
|
|
1215
1219
|
} catch (e) {
|
|
@@ -1494,6 +1498,213 @@ runTest(
|
|
|
1494
1498
|
'true'
|
|
1495
1499
|
)
|
|
1496
1500
|
|
|
1501
|
+
runTest(
|
|
1502
|
+
"test get with URL returns null on 404",
|
|
1503
|
+
async () => {
|
|
1504
|
+
var key = 'test-url-get-404-' + Math.random().toString(36).slice(2)
|
|
1505
|
+
|
|
1506
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
1507
|
+
method: 'POST',
|
|
1508
|
+
body: `void (async () => {
|
|
1509
|
+
var braid_blob = require(\`\${__dirname}/../index.js\`)
|
|
1510
|
+
var url = new URL('http://localhost:' + req.socket.localPort + '/${key}')
|
|
1511
|
+
var result = await braid_blob.get(url)
|
|
1512
|
+
res.end(result === null ? 'null' : 'not null: ' + JSON.stringify(result))
|
|
1513
|
+
})()`
|
|
1514
|
+
})
|
|
1515
|
+
|
|
1516
|
+
return await r1.text()
|
|
1517
|
+
},
|
|
1518
|
+
'null'
|
|
1519
|
+
)
|
|
1520
|
+
|
|
1521
|
+
runTest(
|
|
1522
|
+
"test signal abort stops local put operation",
|
|
1523
|
+
async () => {
|
|
1524
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
1525
|
+
method: 'POST',
|
|
1526
|
+
body: `void (async () => {
|
|
1527
|
+
var fs = require('fs').promises
|
|
1528
|
+
var test_id = 'test-abort-put-' + Math.random().toString(36).slice(2)
|
|
1529
|
+
var db_folder = __dirname + '/' + test_id + '-db'
|
|
1530
|
+
var meta_folder = __dirname + '/' + test_id + '-meta'
|
|
1531
|
+
|
|
1532
|
+
try {
|
|
1533
|
+
var bb = braid_blob.create_braid_blob()
|
|
1534
|
+
bb.db_folder = db_folder
|
|
1535
|
+
bb.meta_folder = meta_folder
|
|
1536
|
+
|
|
1537
|
+
// Create an already-aborted signal
|
|
1538
|
+
var ac = new AbortController()
|
|
1539
|
+
ac.abort()
|
|
1540
|
+
|
|
1541
|
+
// Try to put with aborted signal
|
|
1542
|
+
var result = await bb.put('/test-file', Buffer.from('hello'), {
|
|
1543
|
+
signal: ac.signal
|
|
1544
|
+
})
|
|
1545
|
+
|
|
1546
|
+
// Result should be undefined since operation was aborted
|
|
1547
|
+
res.end(result === undefined ? 'aborted' : 'not aborted: ' + result)
|
|
1548
|
+
} catch (e) {
|
|
1549
|
+
res.end('error: ' + e.message)
|
|
1550
|
+
} finally {
|
|
1551
|
+
await fs.rm(db_folder, { recursive: true, force: true })
|
|
1552
|
+
await fs.rm(meta_folder, { recursive: true, force: true })
|
|
1553
|
+
}
|
|
1554
|
+
})()`
|
|
1555
|
+
})
|
|
1556
|
+
return await r1.text()
|
|
1557
|
+
},
|
|
1558
|
+
'aborted'
|
|
1559
|
+
)
|
|
1560
|
+
|
|
1561
|
+
runTest(
|
|
1562
|
+
"test signal abort stops local get operation",
|
|
1563
|
+
async () => {
|
|
1564
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
1565
|
+
method: 'POST',
|
|
1566
|
+
body: `void (async () => {
|
|
1567
|
+
var fs = require('fs').promises
|
|
1568
|
+
var test_id = 'test-abort-get-' + Math.random().toString(36).slice(2)
|
|
1569
|
+
var db_folder = __dirname + '/' + test_id + '-db'
|
|
1570
|
+
var meta_folder = __dirname + '/' + test_id + '-meta'
|
|
1571
|
+
|
|
1572
|
+
try {
|
|
1573
|
+
var bb = braid_blob.create_braid_blob()
|
|
1574
|
+
bb.db_folder = db_folder
|
|
1575
|
+
bb.meta_folder = meta_folder
|
|
1576
|
+
|
|
1577
|
+
// Put a file first
|
|
1578
|
+
await bb.put('/test-file', Buffer.from('hello'), { version: ['1'] })
|
|
1579
|
+
|
|
1580
|
+
// Create an already-aborted signal
|
|
1581
|
+
var ac = new AbortController()
|
|
1582
|
+
ac.abort()
|
|
1583
|
+
|
|
1584
|
+
// Try to get with aborted signal (after header_cb)
|
|
1585
|
+
var header_called = false
|
|
1586
|
+
var result = await bb.get('/test-file', {
|
|
1587
|
+
signal: ac.signal,
|
|
1588
|
+
header_cb: () => { header_called = true }
|
|
1589
|
+
})
|
|
1590
|
+
|
|
1591
|
+
// Result should be undefined since operation was aborted after header_cb
|
|
1592
|
+
res.end(header_called && result === undefined ? 'aborted' : 'not aborted: header=' + header_called + ' result=' + JSON.stringify(result))
|
|
1593
|
+
} catch (e) {
|
|
1594
|
+
res.end('error: ' + e.message)
|
|
1595
|
+
} finally {
|
|
1596
|
+
await fs.rm(db_folder, { recursive: true, force: true })
|
|
1597
|
+
await fs.rm(meta_folder, { recursive: true, force: true })
|
|
1598
|
+
}
|
|
1599
|
+
})()`
|
|
1600
|
+
})
|
|
1601
|
+
return await r1.text()
|
|
1602
|
+
},
|
|
1603
|
+
'aborted'
|
|
1604
|
+
)
|
|
1605
|
+
|
|
1606
|
+
runTest(
|
|
1607
|
+
"test signal abort stops local delete operation",
|
|
1608
|
+
async () => {
|
|
1609
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
1610
|
+
method: 'POST',
|
|
1611
|
+
body: `void (async () => {
|
|
1612
|
+
var fs = require('fs').promises
|
|
1613
|
+
var test_id = 'test-abort-delete-' + Math.random().toString(36).slice(2)
|
|
1614
|
+
var db_folder = __dirname + '/' + test_id + '-db'
|
|
1615
|
+
var meta_folder = __dirname + '/' + test_id + '-meta'
|
|
1616
|
+
|
|
1617
|
+
try {
|
|
1618
|
+
var bb = braid_blob.create_braid_blob()
|
|
1619
|
+
bb.db_folder = db_folder
|
|
1620
|
+
bb.meta_folder = meta_folder
|
|
1621
|
+
|
|
1622
|
+
// Put a file first
|
|
1623
|
+
await bb.put('/test-file', Buffer.from('hello'), { version: ['1'] })
|
|
1624
|
+
|
|
1625
|
+
// Create an already-aborted signal
|
|
1626
|
+
var ac = new AbortController()
|
|
1627
|
+
ac.abort()
|
|
1628
|
+
|
|
1629
|
+
// Try to delete with aborted signal
|
|
1630
|
+
await bb.delete('/test-file', { signal: ac.signal })
|
|
1631
|
+
|
|
1632
|
+
// File should still exist since delete was aborted
|
|
1633
|
+
var result = await bb.get('/test-file')
|
|
1634
|
+
res.end(result && result.body ? 'still exists' : 'deleted')
|
|
1635
|
+
} catch (e) {
|
|
1636
|
+
res.end('error: ' + e.message)
|
|
1637
|
+
} finally {
|
|
1638
|
+
await fs.rm(db_folder, { recursive: true, force: true })
|
|
1639
|
+
await fs.rm(meta_folder, { recursive: true, force: true })
|
|
1640
|
+
}
|
|
1641
|
+
})()`
|
|
1642
|
+
})
|
|
1643
|
+
return await r1.text()
|
|
1644
|
+
},
|
|
1645
|
+
'still exists'
|
|
1646
|
+
)
|
|
1647
|
+
|
|
1648
|
+
runTest(
|
|
1649
|
+
"test signal abort stops subscription updates",
|
|
1650
|
+
async () => {
|
|
1651
|
+
var r1 = await braid_fetch(`/eval`, {
|
|
1652
|
+
method: 'POST',
|
|
1653
|
+
body: `void (async () => {
|
|
1654
|
+
var fs = require('fs').promises
|
|
1655
|
+
var test_id = 'test-abort-sub-' + Math.random().toString(36).slice(2)
|
|
1656
|
+
var db_folder = __dirname + '/' + test_id + '-db'
|
|
1657
|
+
var meta_folder = __dirname + '/' + test_id + '-meta'
|
|
1658
|
+
|
|
1659
|
+
try {
|
|
1660
|
+
var bb = braid_blob.create_braid_blob()
|
|
1661
|
+
bb.db_folder = db_folder
|
|
1662
|
+
bb.meta_folder = meta_folder
|
|
1663
|
+
|
|
1664
|
+
// Put a file first
|
|
1665
|
+
await bb.put('/test-file', Buffer.from('v1'), { version: ['1'] })
|
|
1666
|
+
|
|
1667
|
+
// Subscribe with an AbortController
|
|
1668
|
+
var ac = new AbortController()
|
|
1669
|
+
var updates = []
|
|
1670
|
+
|
|
1671
|
+
await bb.get('/test-file', {
|
|
1672
|
+
signal: ac.signal,
|
|
1673
|
+
subscribe: (update) => {
|
|
1674
|
+
updates.push(update.body.toString())
|
|
1675
|
+
}
|
|
1676
|
+
})
|
|
1677
|
+
|
|
1678
|
+
// Should have received initial update
|
|
1679
|
+
if (updates.length !== 1 || updates[0] !== 'v1') {
|
|
1680
|
+
res.end('initial update wrong: ' + JSON.stringify(updates))
|
|
1681
|
+
return
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
// Abort the subscription
|
|
1685
|
+
ac.abort()
|
|
1686
|
+
|
|
1687
|
+
// Put another update
|
|
1688
|
+
await bb.put('/test-file', Buffer.from('v2'), { version: ['2'] })
|
|
1689
|
+
|
|
1690
|
+
// Wait a bit for any updates to propagate
|
|
1691
|
+
await new Promise(done => setTimeout(done, 50))
|
|
1692
|
+
|
|
1693
|
+
// Should still only have the initial update
|
|
1694
|
+
res.end(updates.length === 1 ? 'stopped' : 'got extra: ' + JSON.stringify(updates))
|
|
1695
|
+
} catch (e) {
|
|
1696
|
+
res.end('error: ' + e.message)
|
|
1697
|
+
} finally {
|
|
1698
|
+
await fs.rm(db_folder, { recursive: true, force: true })
|
|
1699
|
+
await fs.rm(meta_folder, { recursive: true, force: true })
|
|
1700
|
+
}
|
|
1701
|
+
})()`
|
|
1702
|
+
})
|
|
1703
|
+
return await r1.text()
|
|
1704
|
+
},
|
|
1705
|
+
'stopped'
|
|
1706
|
+
)
|
|
1707
|
+
|
|
1497
1708
|
}
|
|
1498
1709
|
|
|
1499
1710
|
// Export for Node.js (CommonJS)
|