@orkestrel/test 0.0.7 → 0.0.9

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.
@@ -1,9 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let node_buffer = require("node:buffer");
2
3
  let node_fs = require("node:fs");
4
+ let node_http = require("node:http");
5
+ let node_os = require("node:os");
3
6
  let node_path = require("node:path");
4
7
  let node_url = require("node:url");
8
+ let _src_core = require("../core/index.cjs");
5
9
  let node_events = require("node:events");
6
- let node_os = require("node:os");
7
10
  //#region src/server/constants.ts
8
11
  /**
9
12
  * The attempts `removeTree` makes before rethrowing a retryable removal error.
@@ -60,6 +63,30 @@ function isExcluded(key, exclusions) {
60
63
  return exclusions.some((rule) => rule === "" || key === rule || key.startsWith(`${rule}/`));
61
64
  }
62
65
  /**
66
+ * Creates a symbolic link with a directory-junction fallback for hosts that refuse symbolic links.
67
+ *
68
+ * @param path - The path where the link is created.
69
+ * @param source - The destination path the link points at.
70
+ * @throws The original link error when its code is not `EPERM`, or when the source names an
71
+ * existing non-directory; otherwise, any error from inspecting the source or creating the junction.
72
+ * @remarks Only `EPERM` from the first symbolic-link attempt triggers the fallback. The fallback
73
+ * resolves the source against the link's directory. An existing non-directory rethrows the original
74
+ * `EPERM`, while a directory or missing source is passed to a junction attempt. A missing source is
75
+ * accepted to create a dangling junction. Where the host creates a junction, its stored value is the
76
+ * resolved absolute path.
77
+ */
78
+ function createLink(path, source) {
79
+ try {
80
+ (0, node_fs.symlinkSync)(source, path);
81
+ } catch (error) {
82
+ if ((typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : void 0) !== "EPERM") throw error;
83
+ const resolved = (0, node_path.resolve)((0, node_path.dirname)(path), source);
84
+ const status = (0, node_fs.statSync)(resolved, { throwIfNoEntry: false });
85
+ if (status !== void 0 && !status.isDirectory()) throw error;
86
+ (0, node_fs.symlinkSync)(resolved, path, "junction");
87
+ }
88
+ }
89
+ /**
63
90
  * Removes a directory tree, retrying past a transient Windows handle-release race.
64
91
  *
65
92
  * @param path - The absolute directory to remove.
@@ -70,7 +97,8 @@ function isExcluded(key, exclusions) {
70
97
  * `rmSync` `maxRetries`/`retryDelay` options do not cover this error class on that host: probed
71
98
  * against a real held directory, they neither delay nor retry before rethrowing, so the retry
72
99
  * is implemented here with a synchronous sleep instead. Ten attempts 100ms apart bound the wait
73
- * at roughly one second.
100
+ * at roughly one second. A hold that outlasts that second is {@link destroyScratch}'s case, which
101
+ * retries every refusal inside a caller's budget rather than the codes named here.
74
102
  */
75
103
  function removeTree(path) {
76
104
  for (let attempt = 1;; attempt++) try {
@@ -154,6 +182,372 @@ function readInventory(root, targets, options) {
154
182
  }
155
183
  return Object.fromEntries(Array.from(contents).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0));
156
184
  }
185
+ /**
186
+ * Reports whether a process id names a live process.
187
+ *
188
+ * @param pid - The process id to read.
189
+ * @returns True if a process holds that id at the moment of the call; false otherwise, including a
190
+ * pid the host refuses.
191
+ * @throws Nothing. Every host refusal reads as false.
192
+ * @remarks This is an instantaneous observation rather than a claim of ownership. A host reuses a
193
+ * process id after the process holding it exits, so a true answer says some process holds that id now
194
+ * and never says it is the process the caller started. Two host answers are worth knowing. A POSIX
195
+ * host refuses signal `0` to a process another user owns with `EPERM`, and that refusal reads as
196
+ * false here. A pid of `0` names the caller's own process group on POSIX and the system idle process
197
+ * on Windows, so it reads as true on both without naming a process anyone started.
198
+ *
199
+ * A Linux zombie — a process that has exited and whose parent has not reaped it — still accepts
200
+ * signal `0`, so its `/proc` status is read and a `Z` state reads as false.
201
+ */
202
+ function isRunning(pid) {
203
+ try {
204
+ process.kill(pid, 0);
205
+ } catch {
206
+ return false;
207
+ }
208
+ if (process.platform !== "linux") return true;
209
+ try {
210
+ const status = (0, node_fs.readFileSync)(`/proc/${String(pid)}/stat`, "utf8");
211
+ const boundary = status.lastIndexOf(") ");
212
+ return boundary < 0 || status.slice(boundary + 2, boundary + 3) !== "Z";
213
+ } catch {
214
+ return false;
215
+ }
216
+ }
217
+ /**
218
+ * Waits for a socket to close, accepting a peer reset as a forced close.
219
+ *
220
+ * @param socket - The socket to wait on. One that has already closed resolves without listening.
221
+ * @param options - The time bounds and abort signal.
222
+ * @returns A promise that resolves when the socket emits `close`.
223
+ * @throws The socket's own error when its code is not `ECONNRESET`, the abort reason, or an `Error`
224
+ * when a bound is invalid or the socket does not close within the budget.
225
+ * @remarks Default budget: `1000` milliseconds. A reset is the peer forcing the connection down, and
226
+ * the socket still emits `close` afterwards, so `ECONNRESET` is waited past rather than raised while
227
+ * every other error ends the wait. The interval is validated for consistency with the wait family but
228
+ * is not used, because this helper parks on the socket's events. Both listeners are removed on every
229
+ * settlement, so a caller may wait on one socket repeatedly.
230
+ */
231
+ async function waitForSocketClose(socket, options) {
232
+ const budget = options?.budget ?? 1e3;
233
+ const interval = options?.interval ?? 10;
234
+ if (!Number.isFinite(budget) || budget < 0) throw new Error("Socket budget must be finite and non-negative");
235
+ if (!Number.isFinite(interval) || interval < 0) throw new Error("Socket interval must be finite and non-negative");
236
+ const signal = options?.signal;
237
+ signal?.throwIfAborted();
238
+ if (socket.closed) return;
239
+ const closed = Promise.withResolvers();
240
+ const failed = Promise.withResolvers();
241
+ const expiry = Promise.withResolvers();
242
+ const aborted = Promise.withResolvers();
243
+ const subscription = new AbortController();
244
+ socket.on("close", closed.resolve);
245
+ socket.on("error", failed.resolve);
246
+ signal?.addEventListener("abort", () => aborted.reject(signal.reason), {
247
+ once: true,
248
+ signal: subscription.signal
249
+ });
250
+ const timer = setTimeout(() => {
251
+ expiry.reject(/* @__PURE__ */ new Error(`Socket did not close within ${budget}ms`));
252
+ }, budget);
253
+ try {
254
+ const error = await Promise.race([
255
+ closed.promise.then(() => void 0),
256
+ failed.promise,
257
+ expiry.promise,
258
+ aborted.promise
259
+ ]);
260
+ if (error === void 0) return;
261
+ if (error.code !== "ECONNRESET") throw error;
262
+ await Promise.race([
263
+ closed.promise,
264
+ expiry.promise,
265
+ aborted.promise
266
+ ]);
267
+ } finally {
268
+ clearTimeout(timer);
269
+ subscription.abort();
270
+ socket.off("close", closed.resolve);
271
+ socket.off("error", failed.resolve);
272
+ }
273
+ }
274
+ /**
275
+ * Destroys a scratch directory, retrying until the host releases it.
276
+ *
277
+ * @param scratch - The scratch directory to destroy.
278
+ * @param options - The time bounds and abort signal.
279
+ * @returns A promise that resolves once `destroy()` returns without throwing.
280
+ * @throws The abort reason, or an `Error` when a bound is invalid or the budget elapses. The
281
+ * exhaustion error carries the last host refusal as its `cause`.
282
+ * @remarks Default budget: `10000` milliseconds. Default interval: `25` milliseconds. A host holds a
283
+ * directory for a short interval after the process that held it exits, and a just-stopped child's
284
+ * working directory is the case this exists for, so removal is attempted until the host lets go
285
+ * rather than exactly once. {@link ScratchInterface.destroy} stays synchronous and is unchanged; this
286
+ * is the bounded retry around it. A directory nothing releases still fails, with the host's own
287
+ * refusal as the `cause`.
288
+ *
289
+ * Every refusal is retried, deliberately, and that is wider than {@link removeTree}'s policy: that
290
+ * one retries the codes {@link REMOVE_TREE_RETRYABLE_CODES} names and rethrows the rest at once.
291
+ * The hold this waits out is not classifiable across hosts — Windows reports a working-directory
292
+ * hold as `EPERM`, POSIX hosts and network filesystems report their own — so a code list here would
293
+ * be a list of the hosts it had been run on. The residual is the cost of that: a fault no wait can
294
+ * clear, such as a path removed from under the allocation or a permission the process never had,
295
+ * spends the whole budget before it surfaces, and it surfaces wrapped in the exhaustion error with
296
+ * the host's refusal as `cause` rather than by identity. Pass a shorter `budget` or a `signal`
297
+ * wherever a caller must bound that cost.
298
+ */
299
+ async function destroyScratch(scratch, options) {
300
+ const budget = options?.budget ?? 1e4;
301
+ const interval = options?.interval ?? 25;
302
+ if (!Number.isFinite(budget) || budget < 0) throw new Error("Scratch budget must be finite and non-negative");
303
+ if (!Number.isFinite(interval) || interval < 0) throw new Error("Scratch interval must be finite and non-negative");
304
+ const start = performance.now();
305
+ let refusal;
306
+ while (true) {
307
+ options?.signal?.throwIfAborted();
308
+ try {
309
+ scratch.destroy();
310
+ return;
311
+ } catch (error) {
312
+ refusal = error;
313
+ }
314
+ if (performance.now() - start >= budget) throw new Error(`Scratch directory was not destroyed within ${budget}ms`, { cause: refusal });
315
+ await (0, _src_core.waitForDelay)(interval);
316
+ }
317
+ }
318
+ /**
319
+ * Drives a real client upgrade request against a loopback port and reports what the server did.
320
+ *
321
+ * @param port - The port the server listens on at `127.0.0.1`.
322
+ * @param options - Optional request path, offered subprotocols, time bounds, and abort signal.
323
+ * @returns A promise resolving to the server's answer: a claimed upgrade with the protocol it
324
+ * selected, or a refusal with the status it answered.
325
+ * @throws The client's own transport error, such as the `ECONNREFUSED` a closed port answers, the
326
+ * abort reason, or an `Error` when a bound is invalid or the server does not answer within the
327
+ * budget.
328
+ * @remarks Default budget: `1000` milliseconds. The request carries `Connection: Upgrade` and
329
+ * `Upgrade: websocket`, which is what makes a server's `upgrade` handler the one that answers it.
330
+ * The `upgrade`, `response`, and `error` events are mutually exclusive in practice and the promise
331
+ * settles on whichever arrives first, so a second event changes nothing. The client socket is
332
+ * destroyed before every settlement, on the claimed path because an upgraded socket is detached from
333
+ * the request and outlives it otherwise. The request is made with no agent, so no pooled connection
334
+ * survives the call to keep a suite's event loop alive.
335
+ *
336
+ * A server that accepts the connection and answers nothing raises no transport error, so the budget
337
+ * is what ends that call: the rejection names the port and path it was waiting on. The interval is
338
+ * validated for consistency with the wait family but is not used, because this helper parks on the
339
+ * request's events.
340
+ *
341
+ * A `101` is the claimed path's status on the wire and is deliberately not reported: `status` is the
342
+ * refused arm's member, and a claimed upgrade produced no plain answer.
343
+ * @example
344
+ * ```ts
345
+ * const answer = await requestUpgrade(loopback.port, { path: '/socket', protocols: ['chat'] })
346
+ * // { claimed: true, protocol: 'chat' }
347
+ * ```
348
+ */
349
+ async function requestUpgrade(port, options) {
350
+ const budget = options?.budget ?? 1e3;
351
+ const interval = options?.interval ?? 10;
352
+ if (!Number.isFinite(budget) || budget < 0) throw new Error("Upgrade budget must be finite and non-negative");
353
+ if (!Number.isFinite(interval) || interval < 0) throw new Error("Upgrade interval must be finite and non-negative");
354
+ const signal = options?.signal;
355
+ signal?.throwIfAborted();
356
+ const path = options?.path ?? "/";
357
+ const target = `127.0.0.1:${port}${path}`;
358
+ const headers = {
359
+ connection: "Upgrade",
360
+ upgrade: "websocket"
361
+ };
362
+ const protocols = options?.protocols ?? [];
363
+ if (protocols.length > 0) headers["sec-websocket-protocol"] = protocols.join(", ");
364
+ const request = (0, node_http.request)({
365
+ agent: false,
366
+ headers,
367
+ host: "127.0.0.1",
368
+ path,
369
+ port
370
+ });
371
+ const settled = Promise.withResolvers();
372
+ const expiry = Promise.withResolvers();
373
+ const aborted = Promise.withResolvers();
374
+ const subscription = new AbortController();
375
+ request.on("upgrade", (response, socket) => {
376
+ const protocol = response.headers["sec-websocket-protocol"];
377
+ socket.destroy();
378
+ settled.resolve({
379
+ claimed: true,
380
+ protocol
381
+ });
382
+ });
383
+ request.on("response", (response) => {
384
+ const status = response.statusCode;
385
+ response.destroy();
386
+ request.destroy();
387
+ if (status === void 0) {
388
+ settled.reject(/* @__PURE__ */ new Error(`Upgrade request to ${target} was answered without a status`));
389
+ return;
390
+ }
391
+ settled.resolve({
392
+ claimed: false,
393
+ status
394
+ });
395
+ });
396
+ request.on("error", (error) => {
397
+ request.destroy();
398
+ settled.reject(error);
399
+ });
400
+ signal?.addEventListener("abort", () => aborted.reject(signal.reason), {
401
+ once: true,
402
+ signal: subscription.signal
403
+ });
404
+ const timer = setTimeout(() => {
405
+ expiry.reject(/* @__PURE__ */ new Error(`Upgrade request to ${target} was not answered within ${budget}ms`));
406
+ }, budget);
407
+ request.end();
408
+ try {
409
+ return await Promise.race([
410
+ settled.promise,
411
+ expiry.promise,
412
+ aborted.promise
413
+ ]);
414
+ } finally {
415
+ clearTimeout(timer);
416
+ subscription.abort();
417
+ request.destroy();
418
+ }
419
+ }
420
+ /**
421
+ * Checks whether this host links a directory, by creating one link and reading through it.
422
+ *
423
+ * @returns True if the created link reports as a symbolic link, resolves to a directory, and reaches
424
+ * the destination's contents; false otherwise, including every host refusal.
425
+ * @throws Nothing the attempt itself raises. Failing to allocate the probe directory, and failing to
426
+ * remove it afterwards, both propagate.
427
+ * @remarks `symlinkSync(source, target, 'junction')` creates a directory junction on Windows, which
428
+ * needs no privilege, and Node ignores the type argument off Windows, so one call covers both hosts.
429
+ * The answer is false on a filesystem carrying neither reparse points nor symbolic links. Every call
430
+ * probes and cleans up after itself, so a host whose answer changes is read again rather than
431
+ * remembered.
432
+ */
433
+ function supportsDirectoryLinks() {
434
+ const directory = (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "orkestrel-test-directory-links-"));
435
+ try {
436
+ const source = (0, node_path.join)(directory, "source");
437
+ const link = (0, node_path.join)(directory, "link");
438
+ (0, node_fs.mkdirSync)(source);
439
+ (0, node_fs.writeFileSync)((0, node_path.join)(source, "marker.txt"), "marked");
440
+ (0, node_fs.symlinkSync)(source, link, "junction");
441
+ return (0, node_fs.lstatSync)(link).isSymbolicLink() && (0, node_fs.statSync)(link).isDirectory() && (0, node_fs.readFileSync)((0, node_path.join)(link, "marker.txt"), "utf8") === "marked";
442
+ } catch {
443
+ return false;
444
+ } finally {
445
+ removeTree(directory);
446
+ }
447
+ }
448
+ /**
449
+ * Checks whether this host links a file, by creating one link and reading the file through it.
450
+ *
451
+ * @returns True if the file's contents are readable through the link; false otherwise, including
452
+ * every host refusal.
453
+ * @throws Nothing the attempt itself raises. Failing to allocate the probe directory, and failing to
454
+ * remove it afterwards, both propagate.
455
+ * @remarks `symlinkSync(source, target, 'file')` needs the symbolic-link privilege, which Windows
456
+ * grants under Developer Mode or administrator rights and refuses with `EPERM` otherwise, so the
457
+ * answer is true on POSIX and on a privileged Windows host. Where it is false, no mechanism reaches a
458
+ * file through a link and a proof that reads one back cannot run. This is a separate question from
459
+ * {@link supportsDirectoryLinks}, which an unprivileged Windows host answers true through a junction
460
+ * while answering this one false.
461
+ */
462
+ function supportsFileLinks() {
463
+ const directory = (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "orkestrel-test-file-links-"));
464
+ try {
465
+ const source = (0, node_path.join)(directory, "source.txt");
466
+ const link = (0, node_path.join)(directory, "link.txt");
467
+ (0, node_fs.writeFileSync)(source, "linked");
468
+ (0, node_fs.symlinkSync)(source, link, "file");
469
+ return (0, node_fs.readFileSync)(link, "utf8") === "linked";
470
+ } catch {
471
+ return false;
472
+ } finally {
473
+ removeTree(directory);
474
+ }
475
+ }
476
+ /**
477
+ * Checks whether POSIX permission bits round-trip through this host's `chmod` and `stat`.
478
+ *
479
+ * @returns True if a directory created with mode `0o700` reports that mode back; false otherwise,
480
+ * including every host refusal.
481
+ * @throws Nothing the attempt itself raises. Failing to allocate the probe directory, and failing to
482
+ * remove it afterwards, both propagate.
483
+ * @remarks POSIX reports `mode & 0o777 === 0o700` and Windows reports `0o666` regardless, so the
484
+ * answer is true on POSIX and false on Windows. Storing a bit is a narrower question than enforcing
485
+ * it: a POSIX host running as uid `0` stores every bit faithfully and bypasses the access check the
486
+ * bits describe, so a caller that needs a permission to be enforced probes the refusal it needs
487
+ * rather than reading this.
488
+ */
489
+ function supportsMode() {
490
+ const directory = (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "orkestrel-test-mode-"));
491
+ try {
492
+ const path = (0, node_path.join)(directory, "moded");
493
+ (0, node_fs.mkdirSync)(path, { mode: 448 });
494
+ return ((0, node_fs.statSync)(path).mode & 511) === 448;
495
+ } catch {
496
+ return false;
497
+ } finally {
498
+ removeTree(directory);
499
+ }
500
+ }
501
+ /**
502
+ * Checks whether this host treats two names differing only by case as distinct files.
503
+ *
504
+ * @returns True if `A` and `a` hold the contents each was written with; false otherwise, including
505
+ * every host refusal.
506
+ * @throws Nothing the attempt itself raises. Failing to allocate the probe directory, and failing to
507
+ * remove it afterwards, both propagate.
508
+ * @remarks The names `A` and `a` differ by case and by nothing else, which is what makes the reading
509
+ * an answer about case folding rather than an answer about two unrelated files. A case-folding volume
510
+ * routes the second write onto the first entry, so reading the first back returns the second's
511
+ * contents and the answer is false. The answer is true on a typical POSIX host and false on a
512
+ * case-folding Windows or macOS volume.
513
+ */
514
+ function supportsCase() {
515
+ const directory = (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "orkestrel-test-case-"));
516
+ try {
517
+ const upper = (0, node_path.join)(directory, "A");
518
+ const lower = (0, node_path.join)(directory, "a");
519
+ (0, node_fs.writeFileSync)(upper, "upper");
520
+ (0, node_fs.writeFileSync)(lower, "lower");
521
+ return (0, node_fs.readFileSync)(upper, "utf8") === "upper" && (0, node_fs.readFileSync)(lower, "utf8") === "lower";
522
+ } catch {
523
+ return false;
524
+ } finally {
525
+ removeTree(directory);
526
+ }
527
+ }
528
+ /**
529
+ * Checks whether this host accepts a filename carrying a raw byte no UTF-8 decoder resolves.
530
+ *
531
+ * @returns True if a name ending in byte `0x80` is written and read back; false otherwise, including
532
+ * every host refusal.
533
+ * @throws Nothing the attempt itself raises. Failing to allocate the probe directory, and failing to
534
+ * remove it afterwards, both propagate.
535
+ * @remarks Byte `0x80` is an invalid UTF-8 lead byte. POSIX stores the name verbatim and Windows
536
+ * rejects it with `ENOENT`, so the answer is true on POSIX and false on Windows. The path is passed
537
+ * as a `Buffer` because the byte survives no string round trip.
538
+ */
539
+ function supportsBytes() {
540
+ const directory = (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "orkestrel-test-bytes-"));
541
+ try {
542
+ const name = node_buffer.Buffer.concat([node_buffer.Buffer.from(`${directory}${node_path.sep}`), node_buffer.Buffer.from([128])]);
543
+ (0, node_fs.writeFileSync)(name, "raw");
544
+ return (0, node_fs.readFileSync)(name, "utf8") === "raw";
545
+ } catch {
546
+ return false;
547
+ } finally {
548
+ removeTree(directory);
549
+ }
550
+ }
157
551
  //#endregion
158
552
  //#region src/server/factories.ts
159
553
  /**
@@ -244,7 +638,7 @@ function createScratch(options) {
244
638
  if (candidate === void 0) throw new Error(`${outside}: ${target}`);
245
639
  if (!scratch.has(".")) throw new Error("Scratch directory does not exist");
246
640
  (0, node_fs.mkdirSync)((0, node_path.dirname)(candidate), { recursive: true });
247
- (0, node_fs.symlinkSync)(source, candidate);
641
+ createLink(candidate, source);
248
642
  },
249
643
  remove(target) {
250
644
  const candidate = resolveContained(path, target);
@@ -303,16 +697,60 @@ async function createLoopback(server) {
303
697
  }
304
698
  };
305
699
  }
700
+ /**
701
+ * Creates a cookie jar that records a real response's cookies and replays them as one header.
702
+ *
703
+ * @returns The rendered request header, and the members that read and capture cookies.
704
+ * @remarks Selection is by name alone: no `Domain` or `Path` matching, no `Expires` or `Secure`
705
+ * handling, and no persistence beyond the jar. That is what a test driving one origin over one path
706
+ * needs, and a fixture needing a browser's cookie store needs a browser rather than this.
707
+ */
708
+ function createCookieJar() {
709
+ const cookies = /* @__PURE__ */ new Map();
710
+ return {
711
+ get header() {
712
+ const pairs = [...cookies].map(([name, value]) => `${name}=${value}`);
713
+ return pairs.length === 0 ? void 0 : pairs.join("; ");
714
+ },
715
+ read(name) {
716
+ return cookies.get(name);
717
+ },
718
+ capture(response) {
719
+ const fields = response.headers.getSetCookie();
720
+ for (const field of fields) {
721
+ const boundary = field.indexOf(";");
722
+ const pair = boundary < 0 ? field : field.slice(0, boundary);
723
+ const separator = pair.indexOf("=");
724
+ if (separator < 1) continue;
725
+ const name = pair.slice(0, separator);
726
+ if (/;\s*max-age\s*=\s*0\s*(?:;|$)/iu.test(field)) cookies.delete(name);
727
+ else cookies.set(name, pair.slice(separator + 1));
728
+ }
729
+ return fields;
730
+ }
731
+ };
732
+ }
306
733
  //#endregion
307
734
  exports.REMOVE_TREE_MAX_ATTEMPTS = REMOVE_TREE_MAX_ATTEMPTS;
308
735
  exports.REMOVE_TREE_RETRYABLE_CODES = REMOVE_TREE_RETRYABLE_CODES;
309
736
  exports.REMOVE_TREE_RETRY_DELAY_MS = REMOVE_TREE_RETRY_DELAY_MS;
737
+ exports.createCookieJar = createCookieJar;
738
+ exports.createLink = createLink;
310
739
  exports.createLoopback = createLoopback;
311
740
  exports.createScratch = createScratch;
741
+ exports.destroyScratch = destroyScratch;
312
742
  exports.isExcluded = isExcluded;
743
+ exports.isRunning = isRunning;
313
744
  exports.matchesIdentity = matchesIdentity;
314
745
  exports.readInventory = readInventory;
315
746
  exports.removeTree = removeTree;
747
+ exports.requestUpgrade = requestUpgrade;
316
748
  exports.resolveContained = resolveContained;
749
+ exports.supportsBytes = supportsBytes;
750
+ exports.supportsCase = supportsCase;
751
+ exports.supportsDirectoryLinks = supportsDirectoryLinks;
752
+ exports.supportsFileLinks = supportsFileLinks;
753
+ exports.supportsMode = supportsMode;
754
+ exports.waitForSocketClose = waitForSocketClose;
317
755
 
318
756
  //# sourceMappingURL=index.cjs.map