@muhkoo/connect 0.13.0-alpha.0 → 0.13.2-alpha.0

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.
@@ -5327,7 +5327,7 @@ function base64UrlDecode(s) {
5327
5327
  * expect (and isn't serving a stale cached bundle). Keep this in sync with
5328
5328
  * `package.json` `version`.
5329
5329
  */
5330
- const VERSION = "0.13.0-alpha.0";
5330
+ const VERSION = "0.13.2-alpha.0";
5331
5331
 
5332
5332
  /**
5333
5333
  * `HttpClient` — the one place the unified {@link Client} talks to the
@@ -12183,6 +12183,32 @@ function escapeLiteral(s) {
12183
12183
  * repeat.
12184
12184
  */
12185
12185
  const MAX_DEPTH = 64;
12186
+ /**
12187
+ * How many directories `walk` lists at once.
12188
+ *
12189
+ * Enough to hide per-request latency on a wide tree; low enough that a big
12190
+ * project does not open a request per directory. Browsers cap concurrency per
12191
+ * origin anyway, so a larger number mostly moves the queue.
12192
+ */
12193
+ const WALK_CONCURRENCY = 8;
12194
+ /**
12195
+ * `Promise.all` with a ceiling, preserving input order.
12196
+ *
12197
+ * Results are written back BY INDEX rather than pushed as they settle, so the
12198
+ * output order matches the input regardless of which finishes first — which is
12199
+ * what lets `walk` parallelise without changing what it returns.
12200
+ */
12201
+ async function mapLimit(items, limit, fn) {
12202
+ if (items.length <= 1)
12203
+ return items.length ? [await fn(items[0])] : [];
12204
+ const out = new Array(items.length);
12205
+ let next = 0;
12206
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, async () => {
12207
+ for (let i = next++; i < items.length; i = next++)
12208
+ out[i] = await fn(items[i]);
12209
+ }));
12210
+ return out;
12211
+ }
12186
12212
  const DEFAULT_HISTORY_LIMIT = 20;
12187
12213
  class VfsNamespace {
12188
12214
  deps;
@@ -12341,34 +12367,40 @@ class VfsNamespace {
12341
12367
  * one fetch per directory. Fine for a project, wrong as a default.
12342
12368
  */
12343
12369
  async walk(path = ".") {
12344
- const out = [];
12345
- // Guard against a tree that is not one.
12346
- //
12347
- // `visit` recurses on a PATH, re-resolved from the root each time, so a
12348
- // record whose entry points back at an ancestor - or, before names were
12349
- // validated, an entry simply named ".." - recurses forever and grows
12350
- // `out` without bound. Nothing in the format prevents that, and `mount`
12351
- // calls this on every sync pass.
12370
+ /**
12371
+ * Sibling directories are listed CONCURRENTLY.
12372
+ *
12373
+ * Each `list` is a round trip for one directory record, and walking them
12374
+ * one after another cost the sum of those latencies: measured against a
12375
+ * real project, the walk alone was 1–2.1s before a single file had been
12376
+ * read. Directories are independent, so there was never a reason to
12377
+ * queue them.
12378
+ *
12379
+ * Order is preserved exactly. Each entry maps to its own slice of the
12380
+ * result and the slices are concatenated in listing order, so the output
12381
+ * is identical to the depth-first version — callers and tests that
12382
+ * depend on it are unaffected, and only the timing changes.
12383
+ */
12352
12384
  const seen = new Set();
12353
12385
  const visit = async (dirPath, depth) => {
12354
12386
  if (depth > MAX_DEPTH) {
12355
12387
  this.warn(`stopping at ${dirPath}: deeper than ${MAX_DEPTH} levels`);
12356
- return;
12388
+ return [];
12357
12389
  }
12390
+ // Claimed BEFORE the first await, so two concurrent visits to the
12391
+ // same directory cannot both get past this. A record whose entry
12392
+ // points back at an ancestor would otherwise recurse forever, and
12393
+ // nothing in the format prevents one.
12358
12394
  if (seen.has(dirPath)) {
12359
12395
  this.warn(`stopping at ${dirPath}: already visited (the tree contains a cycle)`);
12360
- return;
12396
+ return [];
12361
12397
  }
12362
12398
  seen.add(dirPath);
12363
- for (const entry of await this.list(dirPath)) {
12364
- if (entry.kind === "dir")
12365
- await visit(entry.path, depth + 1);
12366
- else
12367
- out.push(entry.path);
12368
- }
12399
+ const entries = await this.list(dirPath);
12400
+ const slices = await mapLimit(entries, WALK_CONCURRENCY, async (entry) => entry.kind === "dir" ? visit(entry.path, depth + 1) : [entry.path]);
12401
+ return slices.flat();
12369
12402
  };
12370
- await visit(this.resolve(path), 0);
12371
- return out;
12403
+ return visit(this.resolve(path), 0);
12372
12404
  }
12373
12405
  async readFile(path) {
12374
12406
  const entry = await this.expectFile(path);