braidfs 0.0.142 → 0.0.144

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 (2) hide show
  1. package/index.js +36 -111
  2. package/package.json +4 -3
package/index.js CHANGED
@@ -379,7 +379,10 @@ async function scan_files() {
379
379
  var timestamp = new Date().toLocaleTimeString(
380
380
  'en-US', {minute: '2-digit', second: '2-digit', hour: '2-digit'}
381
381
  )
382
- console.log(`scan files.. ${timestamp}. ${Date.now() - st}ms`)
382
+ var mem = process.memoryUsage()
383
+ var heapMB = (mem.heapUsed / 1024 / 1024).toFixed(1)
384
+ var rssMB = (mem.rss / 1024 / 1024).toFixed(1)
385
+ console.log(`scan files.. ${timestamp}. ${Date.now() - st}ms [${heapMB} MB heap, ${rssMB} MB RSS]`)
383
386
  }
384
387
  scan_files.running = false
385
388
 
@@ -1184,121 +1187,43 @@ function ReconnectRateLimiter(wait_time) {
1184
1187
  return self
1185
1188
  }
1186
1189
 
1190
+ // Undici-based fetch with HTTP/2 support for HTTPS,
1191
+ // falls back to built-in fetch for HTTP (faster, no Agent overhead)
1192
+ // Timeout defaults: connect.timeout=10s,
1193
+ // headersTimeout=5min, bodyTimeout=5min,
1194
+ // keepAliveTimeout=4s, keepAliveMaxTimeout=10min
1187
1195
  async function fetch_http2(url, options = {}) {
1188
- if (!fetch_http2.sessions) {
1189
- fetch_http2.sessions = new Map()
1190
- process.on("exit", () => fetch_http2.sessions.forEach(s => s.session.close()))
1191
- }
1192
-
1193
- var u = new URL(url)
1194
- if (u.protocol !== "https:") return fetch(url, options)
1195
-
1196
- try {
1197
- var sessionInfo = fetch_http2.sessions.get(u.origin)
1198
- if (!sessionInfo || sessionInfo.session.closed) {
1199
- var session = require("http2").connect(u.origin, {
1200
- rejectUnauthorized: options.rejectUnauthorized !== false,
1201
- })
1202
- sessionInfo = { session, pendingRejects: new Set() }
1203
-
1204
- session.on("error", (e) => {
1205
- for (var f of sessionInfo.pendingRejects) f(e)
1206
- fetch_http2.sessions.delete(u.origin)
1207
- })
1208
- session.on("close", () => {
1209
- var e = new Error('Session closed')
1210
- for (var f of sessionInfo.pendingRejects) f(e)
1211
- fetch_http2.sessions.delete(u.origin)
1212
- })
1213
- fetch_http2.sessions.set(u.origin, sessionInfo)
1214
- }
1215
-
1216
- var session = sessionInfo.session
1217
-
1218
- return await new Promise((resolve, reject) => {
1219
- sessionInfo.pendingRejects.add(reject)
1220
-
1221
- var responseTimeout = setTimeout(() => {
1222
- stream.destroy(new Error('Response timeout'))
1223
- }, options.responseTimeout || 10000)
1224
-
1225
- var stream = session.request({
1226
- ":method": options.method || "GET",
1227
- ":path": u.pathname + u.search,
1228
- ":scheme": "https",
1229
- ":authority": u.host,
1230
- ...Object.fromEntries(options.headers || []),
1231
- })
1232
-
1233
- options.signal?.addEventListener("abort",
1234
- () => stream.destroy(new Error("Request aborted")),
1235
- { once: true })
1236
-
1237
- stream.on("response", headers => {
1238
- clearTimeout(responseTimeout)
1239
- sessionInfo.pendingRejects.delete(reject)
1240
- var status = +headers[":status"]
1241
- resolve({
1242
- ok: status >= 200 && status < 300,
1243
- status,
1244
- statusText: "",
1245
- headers: new Headers(Object.fromEntries(
1246
- Object.entries(headers).filter(([k]) =>
1247
- typeof k === "string" && !k.startsWith(":")))),
1248
- body: new ReadableStream({
1249
- start(ctrl) {
1250
- stream.on("data", x => ctrl.enqueue(new Uint8Array(x)))
1251
- stream.on("end", () => ctrl.close())
1252
- stream.on("error", err => ctrl.error(err))
1253
- },
1254
- cancel() { stream.destroy() },
1255
- }),
1256
- bodyUsed: false,
1257
- async _consumeBody() {
1258
- this.bodyUsed = true
1259
- var chunks = []
1260
- var reader = this.body.getReader()
1261
-
1262
- while (true) {
1263
- var { done, value } = await reader.read()
1264
- if (done) break
1265
- chunks.push(value)
1266
- }
1267
- return Buffer.concat(chunks.map((c) => (Buffer.isBuffer(c) ? c : Buffer.from(c))))
1268
- },
1269
- async text() { return (await this._consumeBody()).toString() },
1270
- async json() { return JSON.parse(await this.text()) },
1271
- async arrayBuffer() {
1272
- var b = await this._consumeBody()
1273
- return b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength)
1274
- },
1275
- })
1276
- })
1277
-
1278
- stream.on("error", (err) => {
1279
- clearTimeout(responseTimeout)
1280
- sessionInfo.pendingRejects.delete(reject)
1281
- reject(err)
1282
- })
1283
-
1284
- var body = options.body
1285
- if (!body) return stream.end()
1286
-
1287
- if (body instanceof Uint8Array || Buffer.isBuffer(body)) stream.end(body)
1288
- else if (body instanceof Blob) body.arrayBuffer()
1289
- .then((b) => stream.end(Buffer.from(b)))
1290
- .catch(reject)
1291
- else stream.end(typeof body === "string" ? body : JSON.stringify(body))
1196
+ // Use built-in fetch for HTTP (faster, no need for HTTP/2)
1197
+ if (new URL(url).protocol !== 'https:')
1198
+ return fetch(url, options)
1199
+
1200
+ if (!fetch_http2.undici) {
1201
+ fetch_http2.undici = require('undici')
1202
+ var makeAgent = (insecure) => new fetch_http2.undici.Agent({
1203
+ allowH2: true,
1204
+ connect: { rejectUnauthorized: !insecure },
1292
1205
  })
1293
- } catch (err) {
1294
- if (err.code?.includes("HTTP2") || err.message?.includes("HTTP/2")) {
1295
- // console.log("HTTP/2 failed, falling back to HTTP/1.1:", err.message)
1296
- return fetch(url, options)
1297
- }
1298
- throw err
1206
+ fetch_http2.agent = makeAgent(false)
1207
+ fetch_http2.insecureAgent = makeAgent(true)
1299
1208
  }
1209
+
1210
+ // Workaround: undici HTTP/2 can hang with empty body on some servers
1211
+ // See https://github.com/nodejs/undici/issues/2589
1212
+ var body = options.body
1213
+ if (body?.length === 0) body = undefined
1214
+
1215
+ return fetch_http2.undici.fetch(url, {
1216
+ method: options.method || 'GET',
1217
+ headers: options.headers,
1218
+ body,
1219
+ signal: options.signal,
1220
+ dispatcher: options.rejectUnauthorized === false
1221
+ ? fetch_http2.insecureAgent
1222
+ : fetch_http2.agent,
1223
+ })
1300
1224
  }
1301
1225
 
1226
+
1302
1227
  ////////////////////////////////
1303
1228
 
1304
1229
  function normalize_url(url) {
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "braidfs",
3
- "version": "0.0.142",
3
+ "version": "0.0.144",
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": "~1.3.86",
10
- "braid-text": "~0.2.103",
10
+ "braid-text": "~0.2.105",
11
11
  "braid-blob": "~0.0.49",
12
- "chokidar": "^4.0.3"
12
+ "chokidar": "^4.0.3",
13
+ "undici": "^7.18.2"
13
14
  },
14
15
  "bin": {
15
16
  "braidfs": "./index.sh"