braidfs 0.0.25 → 0.0.26
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 +207 -174
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -157,33 +157,28 @@ server.listen(config.port, () => {
|
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
async function proxy_url(url) {
|
|
160
|
-
let chain = proxy_url.chain || (proxy_url.chain = Promise.resolve())
|
|
161
|
-
|
|
162
160
|
async function ensure_path(path) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
let
|
|
169
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
170
|
-
let partial = require("path").join(...parts.slice(0, i))
|
|
161
|
+
try {
|
|
162
|
+
await require("fs").promises.mkdir(path, { recursive: true })
|
|
163
|
+
} catch (e) {
|
|
164
|
+
let parts = path.split(require("path").sep).slice(1)
|
|
165
|
+
for (let i = 1; i <= parts.length; i++) {
|
|
166
|
+
let partial = require("path").sep + require("path").join(...parts.slice(0, i))
|
|
171
167
|
|
|
172
|
-
|
|
173
|
-
|
|
168
|
+
if (!(await is_dir(partial))) {
|
|
169
|
+
let save = await require("fs").promises.readFile(partial)
|
|
174
170
|
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
await require("fs").promises.unlink(partial)
|
|
172
|
+
await require("fs").promises.mkdir(path, { recursive: true })
|
|
177
173
|
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
while (await is_dir(partial))
|
|
175
|
+
partial = require("path").join(partial, 'index')
|
|
180
176
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
177
|
+
await require("fs").promises.writeFile(partial, save)
|
|
178
|
+
break
|
|
184
179
|
}
|
|
185
180
|
}
|
|
186
|
-
}
|
|
181
|
+
}
|
|
187
182
|
}
|
|
188
183
|
|
|
189
184
|
// normalize url by removing any trailing /index/index/
|
|
@@ -192,197 +187,235 @@ async function proxy_url(url) {
|
|
|
192
187
|
url = normalized_url
|
|
193
188
|
|
|
194
189
|
if (!proxy_url.cache) proxy_url.cache = {}
|
|
195
|
-
if (proxy_url.
|
|
196
|
-
|
|
197
|
-
|
|
190
|
+
if (!proxy_url.chain) proxy_url.chain = Promise.resolve()
|
|
191
|
+
if (!proxy_url.cache[url]) proxy_url.cache[url] = proxy_url.chain = proxy_url.chain.then(async () => {
|
|
192
|
+
let self = {}
|
|
198
193
|
|
|
199
|
-
|
|
194
|
+
console.log(`proxy_url: ${url}`)
|
|
200
195
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
196
|
+
let is_external_link = url.match(/^https?:\/\//)
|
|
197
|
+
let path = is_external_link ? url.replace(/^https?:\/\//, '') : `localhost/${url}`
|
|
198
|
+
let fullpath = require("path").join(config.proxy_base, path)
|
|
204
199
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
if (is_external_link) {
|
|
201
|
+
let u = new URL(url)
|
|
202
|
+
host_to_protocol[u.host] = u.protocol
|
|
203
|
+
}
|
|
209
204
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
205
|
+
// if we're accessing /blah/index, it will be normalized to /blah,
|
|
206
|
+
// but we still want to create a directory out of blah in this case
|
|
207
|
+
if (wasnt_normal && !(await is_dir(fullpath))) await ensure_path(fullpath)
|
|
213
208
|
|
|
214
|
-
|
|
209
|
+
await ensure_path(require("path").dirname(fullpath))
|
|
215
210
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
211
|
+
async function get_fullpath() {
|
|
212
|
+
let p = fullpath
|
|
213
|
+
while (await is_dir(p)) p = require("path").join(p, 'index')
|
|
214
|
+
return p
|
|
215
|
+
}
|
|
221
216
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
217
|
+
let peer = Math.random().toString(36).slice(2)
|
|
218
|
+
var char_counter = -1
|
|
219
|
+
let file_last_version = null
|
|
220
|
+
let file_last_text = null
|
|
221
|
+
self.file_read_only = null
|
|
222
|
+
let file_needs_reading = true
|
|
223
|
+
let file_needs_writing = null
|
|
224
|
+
let file_loop_pump_lock = 0
|
|
225
|
+
|
|
226
|
+
function signal_file_needs_reading() {
|
|
227
|
+
file_needs_reading = true
|
|
228
|
+
file_loop_pump()
|
|
229
|
+
}
|
|
235
230
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
231
|
+
function signal_file_needs_writing() {
|
|
232
|
+
file_needs_writing = true
|
|
233
|
+
file_loop_pump()
|
|
234
|
+
}
|
|
240
235
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
236
|
+
async function send_out(stuff) {
|
|
237
|
+
if (is_external_link) await braid_fetch_wrapper(url, {
|
|
238
|
+
headers: {
|
|
239
|
+
"Merge-Type": "dt",
|
|
240
|
+
"Content-Type": 'text/plain',
|
|
241
|
+
...config?.domains?.[(new URL(url)).hostname]?.auth_headers,
|
|
242
|
+
},
|
|
243
|
+
method: "PUT",
|
|
244
|
+
retry: true,
|
|
245
|
+
...stuff
|
|
246
|
+
})
|
|
247
|
+
}
|
|
253
248
|
|
|
254
|
-
|
|
255
|
-
async function file_loop_pump() {
|
|
256
|
-
if (file_loop_pump_lock) return
|
|
257
|
-
file_loop_pump_lock++
|
|
249
|
+
path_to_func[path] = signal_file_needs_reading
|
|
258
250
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
251
|
+
file_loop_pump()
|
|
252
|
+
async function file_loop_pump() {
|
|
253
|
+
if (file_loop_pump_lock) return
|
|
254
|
+
file_loop_pump_lock++
|
|
255
|
+
|
|
256
|
+
if (file_last_version === null) {
|
|
257
|
+
try {
|
|
258
|
+
file_last_version = JSON.parse(await require('fs').promises.readFile(require('path').join(config.proxy_base_last_versions, braid_text.encode_filename(url)), { encoding: 'utf8' }))
|
|
259
|
+
file_last_text = (await braid_text.get(url, { version: file_last_version })).body
|
|
260
|
+
file_needs_writing = !v_eq(file_last_version, (await braid_text.get(url, {})).version)
|
|
261
|
+
} catch (e) {
|
|
262
|
+
file_last_text = ''
|
|
263
|
+
file_needs_writing = true
|
|
264
|
+
}
|
|
267
265
|
}
|
|
268
|
-
}
|
|
269
266
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
267
|
+
while (file_needs_reading || file_needs_writing) {
|
|
268
|
+
if (file_needs_reading) {
|
|
269
|
+
file_needs_reading = false
|
|
273
270
|
|
|
274
|
-
|
|
271
|
+
if (self.file_read_only === null) try { self.file_read_only = await is_read_only(await get_fullpath()) } catch (e) { }
|
|
275
272
|
|
|
276
|
-
|
|
277
|
-
|
|
273
|
+
let text = ''
|
|
274
|
+
try { text = await require('fs').promises.readFile(await get_fullpath(), { encoding: 'utf8' }) } catch (e) { }
|
|
278
275
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
276
|
+
var patches = diff(file_last_text, text)
|
|
277
|
+
if (patches.length) {
|
|
278
|
+
// convert from js-indicies to code-points
|
|
279
|
+
char_counter += patches_to_code_points(patches, file_last_text)
|
|
283
280
|
|
|
284
|
-
|
|
281
|
+
file_last_text = text
|
|
285
282
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
283
|
+
var version = [peer + "-" + char_counter]
|
|
284
|
+
var parents = file_last_version
|
|
285
|
+
file_last_version = version
|
|
289
286
|
|
|
290
|
-
|
|
287
|
+
send_out({ version, parents, patches, peer })
|
|
291
288
|
|
|
292
|
-
|
|
289
|
+
await braid_text.put(url, { version, parents, patches, peer })
|
|
293
290
|
|
|
294
|
-
|
|
291
|
+
await require('fs').promises.writeFile(require('path').join(config.proxy_base_last_versions, braid_text.encode_filename(url)), JSON.stringify(file_last_version))
|
|
292
|
+
}
|
|
295
293
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (!v_eq(version, file_last_version)) {
|
|
294
|
+
if (file_needs_writing) {
|
|
295
|
+
file_needs_writing = false
|
|
296
|
+
let { version, body } = await braid_text.get(url, {})
|
|
297
|
+
if (!v_eq(version, file_last_version)) {
|
|
301
298
|
|
|
302
|
-
|
|
299
|
+
console.log(`writing file ${await get_fullpath()}`)
|
|
303
300
|
|
|
304
|
-
|
|
301
|
+
try { if (await is_read_only(await get_fullpath())) await set_read_only(await get_fullpath(), false) } catch (e) { }
|
|
305
302
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
303
|
+
file_last_version = version
|
|
304
|
+
file_last_text = body
|
|
305
|
+
await require('fs').promises.writeFile(await get_fullpath(), file_last_text)
|
|
306
|
+
await require('fs').promises.writeFile(require('path').join(config.proxy_base_last_versions, braid_text.encode_filename(url)), JSON.stringify(file_last_version))
|
|
307
|
+
}
|
|
311
308
|
|
|
312
|
-
|
|
309
|
+
if (await is_read_only(await get_fullpath()) !== self.file_read_only) await set_read_only(await get_fullpath(), self.file_read_only)
|
|
310
|
+
}
|
|
313
311
|
}
|
|
312
|
+
file_loop_pump_lock--
|
|
314
313
|
}
|
|
315
|
-
file_loop_pump_lock--
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (is_external_link) braid_fetch_wrapper(url, {
|
|
319
|
-
headers: {
|
|
320
|
-
"Merge-Type": "dt",
|
|
321
|
-
Accept: 'text/plain',
|
|
322
|
-
...config?.domains?.[(new URL(url)).hostname]?.auth_headers,
|
|
323
|
-
},
|
|
324
|
-
subscribe: true,
|
|
325
|
-
retry: true,
|
|
326
|
-
parents: async () => {
|
|
327
|
-
let cur = await braid_text.get(url, {})
|
|
328
|
-
if (cur.version.length) return cur.version
|
|
329
|
-
},
|
|
330
|
-
peer
|
|
331
|
-
}, (res) => {
|
|
332
|
-
self.file_read_only = res.headers.get('editable') === 'false'
|
|
333
|
-
signal_file_needs_writing()
|
|
334
|
-
}).then(x => {
|
|
335
|
-
x.subscribe(async update => {
|
|
336
|
-
// console.log(`update: ${JSON.stringify(update, null, 4)}`)
|
|
337
|
-
if (update.version.length == 0) return;
|
|
338
|
-
|
|
339
|
-
await braid_text.put(url, { ...update, peer, merge_type: 'dt' })
|
|
340
|
-
|
|
341
|
-
signal_file_needs_writing()
|
|
342
|
-
})
|
|
343
|
-
})
|
|
344
314
|
|
|
345
|
-
|
|
315
|
+
// try a HEAD without subscribe to get the version
|
|
316
|
+
let parents = null
|
|
317
|
+
if (is_external_link) {
|
|
318
|
+
try {
|
|
319
|
+
let head_res = await braid_fetch_wrapper(url, {
|
|
320
|
+
method: 'HEAD',
|
|
321
|
+
headers: {
|
|
322
|
+
Accept: 'text/plain',
|
|
323
|
+
...config?.domains?.[(new URL(url)).hostname]?.auth_headers,
|
|
324
|
+
},
|
|
325
|
+
retry: true,
|
|
326
|
+
})
|
|
327
|
+
parents = head_res.headers.get('version') ?
|
|
328
|
+
JSON.parse(`[${head_res.headers.get('version')}]`) :
|
|
329
|
+
null
|
|
330
|
+
self.file_read_only = head_res.headers.get('editable') === 'false'
|
|
331
|
+
signal_file_needs_writing()
|
|
332
|
+
} catch (e) {
|
|
333
|
+
console.log('HEAD failed: ', e)
|
|
334
|
+
}
|
|
346
335
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
336
|
+
// work here
|
|
337
|
+
console.log(`waiting_for_versions: ${parents}`)
|
|
338
|
+
|
|
339
|
+
let waiting_for_versions = Object.fromEntries(parents?.map(x => [x, true]) ?? [])
|
|
340
|
+
await new Promise(done => {
|
|
341
|
+
braid_fetch_wrapper(url, {
|
|
342
|
+
headers: {
|
|
343
|
+
"Merge-Type": "dt",
|
|
344
|
+
Accept: 'text/plain',
|
|
345
|
+
...config?.domains?.[(new URL(url)).hostname]?.auth_headers,
|
|
346
|
+
},
|
|
347
|
+
subscribe: true,
|
|
348
|
+
retry: true,
|
|
349
|
+
parents: async () => {
|
|
350
|
+
let cur = await braid_text.get(url, {})
|
|
351
|
+
if (cur.version.length) {
|
|
352
|
+
waiting_for_versions = Object.fromEntries(Object.keys(waiting_for_versions).map(x => {
|
|
353
|
+
let [a, seq] = x.split('-')
|
|
354
|
+
return [a, seq]
|
|
355
|
+
}))
|
|
356
|
+
for (let v of cur.version) {
|
|
357
|
+
let [a, seq] = v.split('-')
|
|
358
|
+
if (waiting_for_versions[a] <= seq) delete waiting_for_versions[a]
|
|
359
|
+
}
|
|
360
|
+
waiting_for_versions = Object.fromEntries(Object.entries(waiting_for_versions).map(x => [`${x[0]}-${x[1]}`, true]))
|
|
361
|
+
|
|
362
|
+
if (done) {
|
|
363
|
+
if (!Object.keys(waiting_for_versions).length) {
|
|
364
|
+
console.log('got everything we were waiting for..')
|
|
365
|
+
done()
|
|
366
|
+
done = null
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return cur.version
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
peer
|
|
374
|
+
}, (res) => {
|
|
375
|
+
self.file_read_only = res.headers.get('editable') === 'false'
|
|
376
|
+
signal_file_needs_writing()
|
|
377
|
+
}).then(x => {
|
|
378
|
+
x.subscribe(async update => {
|
|
379
|
+
// console.log(`update: ${JSON.stringify(update, null, 4)}`)
|
|
380
|
+
if (update.version.length == 0) return;
|
|
381
|
+
if (update.version.length != 1) throw 'unexpected';
|
|
382
|
+
|
|
383
|
+
await braid_text.put(url, { ...update, peer, merge_type: 'dt' })
|
|
384
|
+
|
|
385
|
+
if (done) {
|
|
386
|
+
delete waiting_for_versions[update.version[0]]
|
|
387
|
+
if (!Object.keys(waiting_for_versions).length) {
|
|
388
|
+
console.log('got everything we were waiting for..')
|
|
389
|
+
done()
|
|
390
|
+
done = null
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
signal_file_needs_writing()
|
|
395
|
+
})
|
|
396
|
+
})
|
|
358
397
|
})
|
|
359
|
-
parents = head_res.headers.get('version') ?
|
|
360
|
-
JSON.parse(`[${head_res.headers.get('version')}]`) :
|
|
361
|
-
null
|
|
362
|
-
self.file_read_only = head_res.headers.get('editable') === 'false'
|
|
363
|
-
signal_file_needs_writing()
|
|
364
|
-
} catch (e) {
|
|
365
|
-
console.log('HEAD failed: ', e)
|
|
366
398
|
}
|
|
367
|
-
}
|
|
368
399
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
400
|
+
// now get everything since then, and send it back..
|
|
401
|
+
braid_text.get(url, {
|
|
402
|
+
parents,
|
|
403
|
+
merge_type: 'dt',
|
|
404
|
+
peer,
|
|
405
|
+
subscribe: async ({ version, parents, body, patches }) => {
|
|
406
|
+
if (version.length == 0) return;
|
|
376
407
|
|
|
377
|
-
|
|
408
|
+
// console.log(`local got: ${JSON.stringify({ version, parents, body, patches }, null, 4)}`)
|
|
378
409
|
|
|
379
|
-
|
|
410
|
+
signal_file_needs_writing()
|
|
380
411
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
412
|
+
send_out({ version, parents, body, patches, peer })
|
|
413
|
+
},
|
|
414
|
+
})
|
|
384
415
|
|
|
385
|
-
|
|
416
|
+
return self
|
|
417
|
+
})
|
|
418
|
+
return await proxy_url.cache[url]
|
|
386
419
|
}
|
|
387
420
|
|
|
388
421
|
////////////////////////////////
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "braidfs",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
4
4
|
"description": "braid technology synchronizing files and webpages",
|
|
5
5
|
"author": "Braid Working Group",
|
|
6
6
|
"repository": "braid-org/braidfs",
|
|
7
7
|
"homepage": "https://braid.org",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"braid-http": "^0.3.20",
|
|
10
|
-
"braid-text": "^0.0.
|
|
10
|
+
"braid-text": "^0.0.30",
|
|
11
11
|
"chokidar": "^3.6.0"
|
|
12
12
|
},
|
|
13
13
|
"bin": {
|