braid-blob 0.0.49 → 0.0.51

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.
Files changed (3) hide show
  1. package/index.js +321 -355
  2. package/package.json +1 -1
  3. package/test/tests.js +6 -17
package/index.js CHANGED
@@ -14,153 +14,214 @@ function create_braid_blob() {
14
14
 
15
15
  var temp_folder = null // will be set in init
16
16
 
17
- braid_blob.init = async () => {
18
- // We only want to initialize once
19
- var init_p = real_init()
20
- braid_blob.init = () => init_p
21
- await braid_blob.init()
17
+ braid_blob.sync = (a, b, options = {}) => {
18
+ options = normalize_options(options)
19
+ if (!options.peer) options.peer = Math.random().toString(36).slice(2)
22
20
 
23
- async function real_init() {
24
- // Ensure our meta folder exists
25
- await require('fs').promises.mkdir(braid_blob.meta_folder, { recursive: true })
21
+ // Support for same-type params removed for now,
22
+ // since it is unused, unoptimized,
23
+ // and not as well battle tested
24
+ if ((a instanceof URL) === (b instanceof URL))
25
+ throw new Error(`one parameter should be local string key, and the other a remote URL object`)
26
26
 
27
- // Create a temp folder inside the meta folder for writing temp files,
28
- // for atomic writing.
29
- // The temp folder is called "temp",
30
- // And this is guaranteed not to conflict with any other files,
31
- // because other files are the result of encode_filename,
32
- // which always ends with a ".XX" (for handling insensitive filesystems)
33
- temp_folder = `${braid_blob.meta_folder}/temp`
34
- await require('fs').promises.mkdir(temp_folder, { recursive: true })
27
+ // One is local, one is remote - make a=local and b=remote (swap if not)
28
+ if (a instanceof URL) {
29
+ let swap = a; a = b; b = swap
30
+ }
35
31
 
36
- // Set up db - either use provided object or create file-based storage
37
- if (typeof braid_blob.db_folder === 'string') {
38
- await require('fs').promises.mkdir(braid_blob.db_folder, { recursive: true })
39
- braid_blob.db = {
40
- read: async (key) => {
41
- var file_path = `${braid_blob.db_folder}/${encode_filename(key)}`
42
- try {
43
- return await require('fs').promises.readFile(file_path)
44
- } catch (e) {
45
- if (e.code === 'ENOENT') return null
46
- throw e
47
- }
48
- },
49
- write: async (key, data) => {
50
- var file_path = `${braid_blob.db_folder}/${encode_filename(key)}`
51
- await atomic_write(file_path, data, temp_folder)
52
- },
53
- delete: async (key) => {
54
- var file_path = `${braid_blob.db_folder}/${encode_filename(key)}`
32
+ var ac = null
33
+ options.signal?.addEventListener('abort', () => ac?.abort())
34
+
35
+ function handle_error(e) {
36
+ if (options.signal?.aborted) return
37
+ console.log(`disconnected from ${b.href}, retrying in ${braid_blob.reconnect_delay_ms ?? 1000}ms`)
38
+ setTimeout(connect, braid_blob.reconnect_delay_ms ?? 1000)
39
+ }
40
+
41
+ async function connect() {
42
+ if (options.signal?.aborted) return
43
+ if (options.on_pre_connect) await options.on_pre_connect()
44
+
45
+ // Abort stuff in the previous connect
46
+ ac?.abort()
47
+ ac = new AbortController()
48
+
49
+ try {
50
+ // Check if remote has our current version (simple fork-point check)
51
+ var server_has_our_version = false
52
+ var local_version = (await braid_blob.get(a, {
53
+ ...options,
54
+ signal: ac.signal,
55
+ head: true
56
+ }))?.version
57
+ if (local_version) {
58
+ var r = await braid_blob.get(b, {
59
+ ...options,
60
+ signal: ac.signal,
61
+ head: true,
62
+ dont_retry: true,
63
+ version: local_version,
64
+ })
65
+ server_has_our_version = !!r
66
+ }
67
+
68
+ // Local -> remote
69
+ await braid_blob.get(a, {
70
+ ...options,
71
+ signal: ac.signal,
72
+ parents: server_has_our_version ? local_version : null,
73
+ subscribe: async update => {
55
74
  try {
56
- await require('fs').promises.unlink(file_path)
75
+ if (update.delete) {
76
+ var x = await braid_blob.delete(b, {
77
+ ...options,
78
+ signal: ac.signal,
79
+ dont_retry: true,
80
+ content_type: update.content_type,
81
+ })
82
+ if (!x.ok) handle_error(new Error('failed to delete'))
83
+ } else {
84
+ var x = await braid_blob.put(b, update.body, {
85
+ ...options,
86
+ signal: ac.signal,
87
+ dont_retry: true,
88
+ version: update.version,
89
+ content_type: update.content_type,
90
+ })
91
+ if ((x.status === 401 || x.status === 403) && options.on_unauthorized) {
92
+ await options.on_unauthorized?.()
93
+ } else if (!x.ok) handle_error(new Error('failed to PUT: ' + x.status))
94
+ }
57
95
  } catch (e) {
58
- if (e.code !== 'ENOENT') throw e
96
+ if (e.name !== 'AbortError')
97
+ handle_error(e)
59
98
  }
60
99
  }
61
- }
62
- } else {
63
- // db_folder is already an object with read/write/delete
64
- braid_blob.db = braid_blob.db_folder
65
- }
66
-
67
- // establish a peer id if not already set
68
- if (!braid_blob.peer)
69
- braid_blob.peer = Math.random().toString(36).slice(2)
70
- }
71
- }
100
+ })
72
101
 
73
- async function get_meta(key) {
74
- if (!braid_blob.meta_cache[key]) {
75
- try {
76
- braid_blob.meta_cache[key] = JSON.parse(
77
- await require('fs').promises.readFile(
78
- `${braid_blob.meta_folder}/${encode_filename(key)}`, 'utf8'))
102
+ // Remote -> local
103
+ var remote_res = await braid_blob.get(b, {
104
+ ...options,
105
+ signal: ac.signal,
106
+ dont_retry: true,
107
+ parents: local_version,
108
+ subscribe: async update => {
109
+ if (update.delete) await braid_blob.delete(a, {
110
+ ...options,
111
+ signal: ac.signal,
112
+ content_type: update.content_type,
113
+ })
114
+ else await braid_blob.put(a, update.body, {
115
+ ...options,
116
+ signal: ac.signal,
117
+ version: update.version,
118
+ content_type: update.content_type,
119
+ })
120
+ },
121
+ on_error: e => {
122
+ options.on_disconnect?.()
123
+ handle_error(e)
124
+ }
125
+ })
126
+ options.on_res?.(remote_res)
79
127
  } catch (e) {
80
- if (e.code === 'ENOENT')
81
- braid_blob.meta_cache[key] = {}
82
- else throw e
128
+ handle_error(e)
83
129
  }
84
130
  }
85
- return braid_blob.meta_cache[key]
86
- }
87
-
88
- async function save_meta(key) {
89
- await atomic_write(`${braid_blob.meta_folder}/${encode_filename(key)}`,
90
- JSON.stringify(braid_blob.meta_cache[key]), temp_folder)
91
- }
92
-
93
- async function delete_meta(key) {
94
- delete braid_blob.meta_cache[key]
95
- try {
96
- await require('fs').promises.unlink(
97
- `${braid_blob.meta_folder}/${encode_filename(key)}`)
98
- } catch (e) {
99
- if (e.code !== 'ENOENT') throw e
100
- }
131
+ connect()
101
132
  }
102
133
 
103
- braid_blob.put = async (key, body, options = {}) => {
104
- options = normalize_options(options)
105
-
106
- // Handle URL case - make a remote PUT request
107
- if (key instanceof URL) {
108
- var params = {
109
- method: 'PUT',
110
- signal: options.signal,
111
- body
112
- }
113
- if (!options.dont_retry)
114
- params.retry = () => true
115
- for (var x of ['headers', 'version', 'peer'])
116
- if (options[x] != null) params[x] = options[x]
117
- if (options.content_type)
118
- params.headers = { ...params.headers,
119
- 'Content-Type': options.content_type }
134
+ braid_blob.serve = async (req, res, options = {}) => {
135
+ await braid_blob.init()
120
136
 
121
- return await braid_fetch(key.href, params)
137
+ if (!options.key) {
138
+ var url = new URL(req.url, 'http://localhost')
139
+ options.key = url.pathname
122
140
  }
123
141
 
124
- await braid_blob.init()
125
- if (options.signal?.aborted) return
126
-
127
- return await within_fiber(key, async () => {
128
- var meta = await get_meta(key)
129
- if (options.signal?.aborted) return
142
+ braidify(req, res)
143
+ if (res.is_multiplexer) return
130
144
 
131
- var their_e = options.version ? options.version[0] :
132
- // we'll give them a event id in this case
133
- `${braid_blob.peer}-${max_seq('' + Date.now(),
134
- meta.event ? increment_seq(get_event_seq(meta.event)) : '')}`
145
+ // Handle OPTIONS request
146
+ if (req.method === 'OPTIONS') return res.end()
135
147
 
136
- if (compare_events(their_e, meta.event) > 0) {
137
- meta.event = their_e
148
+ // consume PUT body
149
+ var body = req.method === 'PUT' && await slurp(req)
138
150
 
139
- if (!options.skip_write)
140
- await (options.db || braid_blob.db).write(key, body)
141
- if (options.signal?.aborted) return
151
+ if (req.method === 'GET' || req.method === 'HEAD') {
152
+ if (!res.hasHeader("editable")) res.setHeader("Editable", "true")
153
+ if (!req.subscribe) res.setHeader("Accept-Subscribe", "true")
154
+ res.setHeader("Merge-Type", "aww")
142
155
 
143
- if (options.content_type)
144
- meta.content_type = options.content_type
156
+ try {
157
+ var result = await braid_blob.get(options.key, {
158
+ peer: req.peer,
159
+ head: req.method === "HEAD",
160
+ version: req.version,
161
+ parents: req.parents,
162
+ header_cb: (result) => {
163
+ res.setHeader((req.subscribe ? "Current-" : "") +
164
+ "Version", version_to_header(result.version))
165
+ if (result.content_type)
166
+ res.setHeader('Content-Type', result.content_type)
167
+ },
168
+ before_send_cb: () => res.startSubscription(),
169
+ subscribe: req.subscribe ? (update) => {
170
+ if (update.delete) {
171
+ update.status = 404
172
+ delete update.delete
173
+ }
174
+ if (update.content_type) {
175
+ update['Content-Type'] = update.content_type
176
+ delete update.content_type
177
+ }
178
+ update['Merge-Type'] = 'aww'
179
+ res.sendUpdate(update)
180
+ } : null
181
+ })
182
+ } catch (e) {
183
+ if (e.message && e.message.startsWith('unknown version')) {
184
+ // Server doesn't have this version
185
+ res.statusCode = 309
186
+ res.statusMessage = 'Version Unknown Here'
187
+ return res.end('')
188
+ } else throw e
189
+ }
145
190
 
146
- await save_meta(key)
147
- if (options.signal?.aborted) return
191
+ if (!result) {
192
+ res.statusCode = 404
193
+ return res.end('File Not Found')
194
+ }
148
195
 
149
- // Notify all subscriptions of the update
150
- // (except the peer which made the PUT request itself)
151
- var update = {
152
- version: [meta.event],
153
- content_type: meta.content_type,
154
- body
155
- }
156
- if (braid_blob.key_to_subs[key])
157
- for (var [peer, sub] of braid_blob.key_to_subs[key].entries())
158
- if (!options.peer || options.peer !== peer)
159
- await sub.sendUpdate(update)
196
+ if (result.content_type && req.headers.accept &&
197
+ !isAcceptable(result.content_type, req.headers.accept)) {
198
+ res.statusCode = 406
199
+ return res.end(`Content-Type of ${result.content_type} not in Accept: ${req.headers.accept}`)
160
200
  }
161
201
 
162
- return meta.event
163
- })
202
+ if (req.method == "HEAD") return res.end('')
203
+ else if (!req.subscribe) return res.end(result.body)
204
+ else {
205
+ // If no immediate update was sent,
206
+ // get the node http code to send headers
207
+ if (!result.sent) res.write('\n\n')
208
+ }
209
+ } else if (req.method === 'PUT') {
210
+ // Handle PUT request to update binary files
211
+ var event = await braid_blob.put(options.key, body, {
212
+ version: req.version,
213
+ content_type: req.headers['content-type'],
214
+ peer: req.peer
215
+ })
216
+ res.setHeader("Version", version_to_header(event != null ? [event] : []))
217
+ res.end('')
218
+ } else if (req.method === 'DELETE') {
219
+ await braid_blob.delete(options.key, {
220
+ content_type: req.headers['content-type'],
221
+ peer: req.peer
222
+ })
223
+ res.end('')
224
+ }
164
225
  }
165
226
 
166
227
  braid_blob.get = async (key, options = {}) => {
@@ -277,6 +338,69 @@ function create_braid_blob() {
277
338
  })
278
339
  }
279
340
 
341
+ braid_blob.put = async (key, body, options = {}) => {
342
+ options = normalize_options(options)
343
+
344
+ // Handle URL case - make a remote PUT request
345
+ if (key instanceof URL) {
346
+ var params = {
347
+ method: 'PUT',
348
+ signal: options.signal,
349
+ body
350
+ }
351
+ if (!options.dont_retry)
352
+ params.retry = () => true
353
+ for (var x of ['headers', 'version', 'peer'])
354
+ if (options[x] != null) params[x] = options[x]
355
+ if (options.content_type)
356
+ params.headers = { ...params.headers,
357
+ 'Content-Type': options.content_type }
358
+
359
+ return await braid_fetch(key.href, params)
360
+ }
361
+
362
+ await braid_blob.init()
363
+ if (options.signal?.aborted) return
364
+
365
+ return await within_fiber(key, async () => {
366
+ var meta = await get_meta(key)
367
+ if (options.signal?.aborted) return
368
+
369
+ var their_e = options.version ? options.version[0] :
370
+ // we'll give them a event id in this case
371
+ `${braid_blob.peer}-${max_seq('' + Date.now(),
372
+ meta.event ? increment_seq(get_event_seq(meta.event)) : '')}`
373
+
374
+ if (compare_events(their_e, meta.event) > 0) {
375
+ meta.event = their_e
376
+
377
+ if (!options.skip_write)
378
+ await (options.db || braid_blob.db).write(key, body)
379
+ if (options.signal?.aborted) return
380
+
381
+ if (options.content_type)
382
+ meta.content_type = options.content_type
383
+
384
+ await save_meta(key)
385
+ if (options.signal?.aborted) return
386
+
387
+ // Notify all subscriptions of the update
388
+ // (except the peer which made the PUT request itself)
389
+ var update = {
390
+ version: [meta.event],
391
+ content_type: meta.content_type,
392
+ body
393
+ }
394
+ if (braid_blob.key_to_subs[key])
395
+ for (var [peer, sub] of braid_blob.key_to_subs[key].entries())
396
+ if (!options.peer || options.peer !== peer)
397
+ await sub.sendUpdate(update)
398
+ }
399
+
400
+ return meta.event
401
+ })
402
+ }
403
+
280
404
  braid_blob.delete = async (key, options = {}) => {
281
405
  options = normalize_options(options)
282
406
 
@@ -321,241 +445,96 @@ function create_braid_blob() {
321
445
  })
322
446
  }
323
447
 
324
- braid_blob.serve = async (req, res, options = {}) => {
448
+ braid_blob.init = async () => {
449
+ // We only want to initialize once
450
+ var init_p = real_init()
451
+ braid_blob.init = () => init_p
325
452
  await braid_blob.init()
326
453
 
327
- if (!options.key) {
328
- var url = new URL(req.url, 'http://localhost')
329
- options.key = url.pathname
330
- }
331
-
332
- braidify(req, res)
333
- if (res.is_multiplexer) return
334
-
335
- // Handle OPTIONS request
336
- if (req.method === 'OPTIONS') return res.end()
337
-
338
- // consume PUT body
339
- var body = req.method === 'PUT' && await slurp(req)
454
+ async function real_init() {
455
+ // Ensure our meta folder exists
456
+ await require('fs').promises.mkdir(braid_blob.meta_folder, { recursive: true })
340
457
 
341
- if (req.method === 'GET' || req.method === 'HEAD') {
342
- if (!res.hasHeader("editable")) res.setHeader("Editable", "true")
343
- if (!req.subscribe) res.setHeader("Accept-Subscribe", "true")
344
- res.setHeader("Merge-Type", "aww")
458
+ // Create a temp folder inside the meta folder for writing temp files,
459
+ // for atomic writing.
460
+ // The temp folder is called "temp",
461
+ // And this is guaranteed not to conflict with any other files,
462
+ // because other files are the result of encode_filename,
463
+ // which always ends with a ".XX" (for handling insensitive filesystems)
464
+ temp_folder = `${braid_blob.meta_folder}/temp`
465
+ await require('fs').promises.mkdir(temp_folder, { recursive: true })
345
466
 
346
- try {
347
- var result = await braid_blob.get(options.key, {
348
- peer: req.peer,
349
- head: req.method === "HEAD",
350
- version: req.version,
351
- parents: req.parents,
352
- header_cb: (result) => {
353
- res.setHeader((req.subscribe ? "Current-" : "") +
354
- "Version", version_to_header(result.version))
355
- if (result.content_type)
356
- res.setHeader('Content-Type', result.content_type)
357
- },
358
- before_send_cb: () => res.startSubscription(),
359
- subscribe: req.subscribe ? (update) => {
360
- if (update.delete) {
361
- update.status = 404
362
- delete update.delete
467
+ // Set up db - either use provided object or create file-based storage
468
+ if (typeof braid_blob.db_folder === 'string') {
469
+ await require('fs').promises.mkdir(braid_blob.db_folder, { recursive: true })
470
+ braid_blob.db = {
471
+ read: async (key) => {
472
+ var file_path = `${braid_blob.db_folder}/${encode_filename(key)}`
473
+ try {
474
+ return await require('fs').promises.readFile(file_path)
475
+ } catch (e) {
476
+ if (e.code === 'ENOENT') return null
477
+ throw e
363
478
  }
364
- if (update.content_type) {
365
- update['Content-Type'] = update.content_type
366
- delete update.content_type
479
+ },
480
+ write: async (key, data) => {
481
+ var file_path = `${braid_blob.db_folder}/${encode_filename(key)}`
482
+ await atomic_write(file_path, data, temp_folder)
483
+ },
484
+ delete: async (key) => {
485
+ var file_path = `${braid_blob.db_folder}/${encode_filename(key)}`
486
+ try {
487
+ await require('fs').promises.unlink(file_path)
488
+ } catch (e) {
489
+ if (e.code !== 'ENOENT') throw e
367
490
  }
368
- update['Merge-Type'] = 'aww'
369
- res.sendUpdate(update)
370
- } : null
371
- })
372
- } catch (e) {
373
- if (e.message && e.message.startsWith('unknown version')) {
374
- // Server doesn't have this version
375
- res.statusCode = 309
376
- res.statusMessage = 'Version Unknown Here'
377
- return res.end('')
378
- } else throw e
379
- }
380
-
381
- if (!result) {
382
- res.statusCode = 404
383
- return res.end('File Not Found')
384
- }
385
-
386
- if (result.content_type && req.headers.accept &&
387
- !isAcceptable(result.content_type, req.headers.accept)) {
388
- res.statusCode = 406
389
- return res.end(`Content-Type of ${result.content_type} not in Accept: ${req.headers.accept}`)
491
+ }
492
+ }
493
+ } else {
494
+ // db_folder is already an object with read/write/delete
495
+ braid_blob.db = braid_blob.db_folder
390
496
  }
391
497
 
392
- if (req.method == "HEAD") return res.end('')
393
- else if (!req.subscribe) return res.end(result.body)
394
- else {
395
- // If no immediate update was sent,
396
- // get the node http code to send headers
397
- if (!result.sent) res.write('\n\n')
398
- }
399
- } else if (req.method === 'PUT') {
400
- // Handle PUT request to update binary files
401
- var event = await braid_blob.put(options.key, body, {
402
- version: req.version,
403
- content_type: req.headers['content-type'],
404
- peer: req.peer
405
- })
406
- res.setHeader("Version", version_to_header(event != null ? [event] : []))
407
- res.end('')
408
- } else if (req.method === 'DELETE') {
409
- await braid_blob.delete(options.key, {
410
- content_type: req.headers['content-type'],
411
- peer: req.peer
412
- })
413
- res.end('')
498
+ // establish a peer id if not already set
499
+ if (!braid_blob.peer)
500
+ braid_blob.peer = Math.random().toString(36).slice(2)
414
501
  }
415
502
  }
416
503
 
417
- braid_blob.sync = (a, b, options = {}) => {
418
- options = normalize_options(options)
419
- if (!options.peer) options.peer = Math.random().toString(36).slice(2)
420
-
421
- if ((a instanceof URL) === (b instanceof URL)) {
422
- braid_blob.get(a, {
423
- ...options,
424
- subscribe: async update => {
425
- if (update.delete) await braid_blob.delete(b, {
426
- ...options,
427
- content_type: update.content_type,
428
- })
429
- else await braid_blob.put(b, update.body, {
430
- ...options,
431
- version: update.version,
432
- content_type: update.content_type,
433
- })
434
- }
435
- })
436
- braid_blob.get(b, {
437
- ...options,
438
- subscribe: async update => {
439
- if (update.delete) await braid_blob.delete(a, {
440
- ...options,
441
- content_type: update.content_type,
442
- })
443
- else await braid_blob.put(a, update.body, {
444
- ...options,
445
- version: update.version,
446
- content_type: update.content_type,
447
- })
448
- }
449
- })
450
- } else {
451
- // One is local, one is remote - make a=local and b=remote (swap if not)
452
- if (a instanceof URL) {
453
- let swap = a; a = b; b = swap
454
- }
455
-
456
- var ac = null
457
- options.signal?.addEventListener('abort', () => ac?.abort())
458
-
459
- function handle_error(e) {
460
- if (options.signal?.aborted) return
461
- console.log(`disconnected from ${b.href}, retrying in ${braid_blob.reconnect_delay_ms ?? 1000}ms`)
462
- setTimeout(connect, braid_blob.reconnect_delay_ms ?? 1000)
504
+ async function get_meta(key) {
505
+ if (!braid_blob.meta_cache[key]) {
506
+ try {
507
+ braid_blob.meta_cache[key] = JSON.parse(
508
+ await require('fs').promises.readFile(
509
+ `${braid_blob.meta_folder}/${encode_filename(key)}`, 'utf8'))
510
+ } catch (e) {
511
+ if (e.code === 'ENOENT')
512
+ braid_blob.meta_cache[key] = {}
513
+ else throw e
463
514
  }
515
+ }
516
+ return braid_blob.meta_cache[key]
517
+ }
464
518
 
465
- async function connect() {
466
- if (options.signal?.aborted) return
467
- if (options.on_pre_connect) await options.on_pre_connect()
468
-
469
- // Abort stuff in the previous connect
470
- ac?.abort()
471
- ac = new AbortController()
472
-
473
- try {
474
- // Check if remote has our current version (simple fork-point check)
475
- var server_has_our_version = false
476
- var local_version = (await braid_blob.get(a, {
477
- ...options,
478
- signal: ac.signal,
479
- head: true
480
- }))?.version
481
- if (local_version) {
482
- var r = await braid_blob.get(b, {
483
- ...options,
484
- signal: ac.signal,
485
- head: true,
486
- dont_retry: true,
487
- version: local_version,
488
- })
489
- server_has_our_version = !!r
490
- }
491
-
492
- // Local -> remote
493
- await braid_blob.get(a, {
494
- ...options,
495
- signal: ac.signal,
496
- parents: server_has_our_version ? local_version : null,
497
- subscribe: async update => {
498
- try {
499
- if (update.delete) {
500
- var x = await braid_blob.delete(b, {
501
- ...options,
502
- signal: ac.signal,
503
- dont_retry: true,
504
- content_type: update.content_type,
505
- })
506
- if (!x.ok) handle_error(new Error('failed to delete'))
507
- } else {
508
- var x = await braid_blob.put(b, update.body, {
509
- ...options,
510
- signal: ac.signal,
511
- dont_retry: true,
512
- version: update.version,
513
- content_type: update.content_type,
514
- })
515
- if ((x.status === 401 || x.status === 403) && options.on_unauthorized) {
516
- await options.on_unauthorized?.()
517
- } else if (!x.ok) handle_error(new Error('failed to PUT: ' + x.status))
518
- }
519
- } catch (e) {
520
- if (e.name !== 'AbortError')
521
- handle_error(e)
522
- }
523
- }
524
- })
519
+ async function save_meta(key) {
520
+ await atomic_write(`${braid_blob.meta_folder}/${encode_filename(key)}`,
521
+ JSON.stringify(braid_blob.meta_cache[key]), temp_folder)
522
+ }
525
523
 
526
- // Remote -> local
527
- var remote_res = await braid_blob.get(b, {
528
- ...options,
529
- signal: ac.signal,
530
- dont_retry: true,
531
- parents: local_version,
532
- subscribe: async update => {
533
- if (update.delete) await braid_blob.delete(a, {
534
- ...options,
535
- signal: ac.signal,
536
- content_type: update.content_type,
537
- })
538
- else await braid_blob.put(a, update.body, {
539
- ...options,
540
- signal: ac.signal,
541
- version: update.version,
542
- content_type: update.content_type,
543
- })
544
- },
545
- on_error: e => {
546
- options.on_disconnect?.()
547
- handle_error(e)
548
- }
549
- })
550
- options.on_res?.(remote_res)
551
- } catch (e) {
552
- handle_error(e)
553
- }
554
- }
555
- connect()
524
+ async function delete_meta(key) {
525
+ delete braid_blob.meta_cache[key]
526
+ try {
527
+ await require('fs').promises.unlink(
528
+ `${braid_blob.meta_folder}/${encode_filename(key)}`)
529
+ } catch (e) {
530
+ if (e.code !== 'ENOENT') throw e
556
531
  }
557
532
  }
558
533
 
534
+ //////////////////////////////////////////////////////////////////
535
+ //////////////////////////////////////////////////////////////////
536
+ //////////////////////////////////////////////////////////////////
537
+
559
538
  function compare_events(a, b) {
560
539
  if (!a) a = ''
561
540
  if (!b) b = ''
@@ -629,7 +608,7 @@ function create_braid_blob() {
629
608
  })
630
609
  return within_fiber.chains[id] = curr
631
610
  }
632
-
611
+
633
612
  async function slurp(req) {
634
613
  return await new Promise(done => {
635
614
  var chunks = []
@@ -695,19 +674,6 @@ function create_braid_blob() {
695
674
  }
696
675
  }
697
676
 
698
- function get_header(headers, key) {
699
- if (!headers) return
700
-
701
- // optimization..
702
- if (headers.hasOwnProperty(key))
703
- return headers[key]
704
-
705
- var lowerKey = key.toLowerCase()
706
- for (var headerKey of Object.keys(headers))
707
- if (headerKey.toLowerCase() === lowerKey)
708
- return headers[headerKey]
709
- }
710
-
711
677
  function normalize_options(options = {}) {
712
678
  if (!normalize_options.special) {
713
679
  normalize_options.special = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-blob",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "Library for collaborative blobs over http using braid.",
5
5
  "author": "Braid Working Group",
6
6
  "repository": "braid-org/braid-blob",
package/test/tests.js CHANGED
@@ -996,7 +996,7 @@ runTest(
996
996
  )
997
997
 
998
998
  runTest(
999
- "test sync two local keys",
999
+ "test sync two local keys throws error",
1000
1000
  async () => {
1001
1001
  var key1 = '/test-sync-local1-' + Math.random().toString(36).slice(2)
1002
1002
  var key2 = '/test-sync-local2-' + Math.random().toString(36).slice(2)
@@ -1007,29 +1007,18 @@ runTest(
1007
1007
  try {
1008
1008
  var braid_blob = require(\`\${__dirname}/../index.js\`)
1009
1009
 
1010
- // Put something to first key
1011
- await braid_blob.put('${key1}', Buffer.from('sync local content'), { version: ['700'] })
1012
-
1013
- // Start sync between two local keys
1010
+ // Try to sync between two local keys - should throw
1014
1011
  braid_blob.sync('${key1}', '${key2}')
1015
1012
 
1016
- res.end('syncing')
1013
+ res.end('no error thrown')
1017
1014
  } catch (e) {
1018
- res.end('error: ' + e.message + ' ' + e.stack)
1015
+ res.end('error thrown')
1019
1016
  }
1020
1017
  })()`
1021
1018
  })
1022
- var result = await r1.text()
1023
- if (result.startsWith('error:')) return result
1024
-
1025
- // Wait a bit for sync to happen
1026
- await new Promise(done => setTimeout(done, 100))
1027
-
1028
- // Check second key has the content
1029
- var r = await braid_fetch(`${key2}`)
1030
- return await r.text()
1019
+ return await r1.text()
1031
1020
  },
1032
- 'sync local content'
1021
+ 'error thrown'
1033
1022
  )
1034
1023
 
1035
1024
  runTest(