instrumentality 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.
- package/README.md +3 -5
- package/dist/base.d.ts +11 -12
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +23 -39
- package/dist/dom.d.ts +1 -6
- package/dist/dom.d.ts.map +1 -1
- package/dist/dom.js +1 -6
- package/dist/road.d.ts +121 -103
- package/dist/road.d.ts.map +1 -1
- package/dist/road.js +401 -388
- package/package.json +5 -3
- package/src/base.ts +22 -43
- package/src/dom.ts +1 -10
- package/src/road.ts +352 -426
package/dist/road.js
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as cr from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import { constants as fsc } from "node:fs";
|
|
4
4
|
import * as fp from "node:fs/promises";
|
|
5
5
|
import * as ph from "node:path";
|
|
6
6
|
import * as os from "node:os";
|
|
7
|
-
import * as cr from "node:crypto";
|
|
8
7
|
import { on } from "node:events";
|
|
9
|
-
import
|
|
10
|
-
/** Subclass of {@link
|
|
11
|
-
export class
|
|
8
|
+
import { InsErr } from "./base.js";
|
|
9
|
+
/** Subclass of {@link InsErr} that represents an error thrown from this specific module of the library */
|
|
10
|
+
export class Err extends InsErr {
|
|
12
11
|
name = "Instrumentality-Road-Error";
|
|
13
12
|
}
|
|
14
|
-
export {
|
|
13
|
+
export { Err as RoadError, Err as RdErr };
|
|
15
14
|
/**
|
|
16
15
|
* Returns the constructor function corresponding to the file mode (statmode).
|
|
17
16
|
*
|
|
18
|
-
* @param statmode_ The file mode to check.
|
|
17
|
+
* @param statmode_ The file mode or {@link fs.Dirent} to check.
|
|
19
18
|
* @returns The constructor function corresponding to the road type (e.g., {@link File}, {@link Folder}, etc.).
|
|
20
|
-
* @throws If the file mode is unknown, throws a {@link
|
|
19
|
+
* @throws If the file mode is unknown, throws a {@link Err}.
|
|
21
20
|
*/
|
|
22
|
-
export function
|
|
21
|
+
export function resolveStat(statmode_) {
|
|
23
22
|
switch (statmode_ & fsc.S_IFMT) {
|
|
24
23
|
case fsc.S_IFREG: return File;
|
|
25
24
|
case fsc.S_IFDIR: return Folder;
|
|
@@ -28,9 +27,36 @@ export function resolveMode(statmode_) {
|
|
|
28
27
|
case fsc.S_IFLNK: return SymbolicLink;
|
|
29
28
|
case fsc.S_IFIFO: return Fifo;
|
|
30
29
|
case fsc.S_IFSOCK: return Socket;
|
|
31
|
-
default: throw new
|
|
30
|
+
default: throw new Err(`Unknown mode type ${statmode_} (statmode is most likely corrupted)`);
|
|
32
31
|
}
|
|
33
32
|
}
|
|
33
|
+
export { resolveStat as resStat };
|
|
34
|
+
/**
|
|
35
|
+
* Returns the constructor function corresponding to the type of the given {@link fs.Dirent}.
|
|
36
|
+
*
|
|
37
|
+
* @param dirent The directory entry to check.
|
|
38
|
+
* @returns The constructor function corresponding to the road type (e.g., {@link File}, {@link Folder}, etc.).
|
|
39
|
+
* @throws If the directory entry type is unknown, throws a {@link Err}.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveDirent(dirent) {
|
|
42
|
+
// Order by likelihood: files/dicts are most common, followed by symbolic links
|
|
43
|
+
if (dirent.isFile())
|
|
44
|
+
return File;
|
|
45
|
+
if (dirent.isDirectory())
|
|
46
|
+
return Folder;
|
|
47
|
+
if (dirent.isSymbolicLink())
|
|
48
|
+
return SymbolicLink;
|
|
49
|
+
if (dirent.isBlockDevice())
|
|
50
|
+
return BlockDevice;
|
|
51
|
+
if (dirent.isCharacterDevice())
|
|
52
|
+
return CharacterDevice;
|
|
53
|
+
if (dirent.isFIFO())
|
|
54
|
+
return Fifo;
|
|
55
|
+
if (dirent.isSocket())
|
|
56
|
+
return Socket;
|
|
57
|
+
throw new Err(`Unknown dirent type for ${dirent.name}`);
|
|
58
|
+
}
|
|
59
|
+
export { resolveDirent as resDirent };
|
|
34
60
|
/**
|
|
35
61
|
* Creates the appropriate subclass of {@link Road} based on the file mode of the specified path.
|
|
36
62
|
*
|
|
@@ -39,16 +65,18 @@ export function resolveMode(statmode_) {
|
|
|
39
65
|
* @throws If {@link fp.lstat}/{@link fs.lstatSync} fails to retrieved the status of {@link path_}.
|
|
40
66
|
*/
|
|
41
67
|
export async function factory(path_) {
|
|
42
|
-
return new (
|
|
68
|
+
return new (resolveStat((await fp.lstat(path_)).mode))(path_, false);
|
|
43
69
|
}
|
|
70
|
+
export { factory as fac, factory as mk };
|
|
44
71
|
/** Sync version of {@link factory}. */
|
|
45
72
|
export function factorySync(path_) {
|
|
46
|
-
return new (
|
|
73
|
+
return new (resolveStat(fs.lstatSync(path_).mode))(path_, false);
|
|
47
74
|
}
|
|
75
|
+
export { factorySync as facSync, factorySync as mkSync };
|
|
48
76
|
/**
|
|
49
77
|
* A map that keeps track of locked roads to prevent concurrent modifications.
|
|
50
78
|
*
|
|
51
|
-
* @key The absolute path of the road that is currently locked.
|
|
79
|
+
* @key The **absolute** and **normalized** (**resolved**) path of the road that is currently locked.
|
|
52
80
|
* @value A promise that resolves when the lock on the road is released.
|
|
53
81
|
*
|
|
54
82
|
* @remarks The map is initialized lazily when the first lock is created to minimize import-time side effects.
|
|
@@ -84,63 +112,12 @@ export class Road {
|
|
|
84
112
|
* @param path_ Any path-like string that can be resolved to an absolute path. It will be resolved to an absolute path using {@link ph.resolve}.
|
|
85
113
|
* @param typeCheck_ If true, the constructor will check if the path corresponds to the expected type of road (e.g., file, folder, etc.) and throw an error if it doesn't. If false, no type checking will be performed.
|
|
86
114
|
*
|
|
87
|
-
* @throws If {@link typeCheck_} is true and the path does not correspond to the expected type of road, a {@link
|
|
115
|
+
* @throws If {@link typeCheck_} is true and the path does not correspond to the expected type of road, a {@link Err} will be thrown.
|
|
88
116
|
*/
|
|
89
117
|
constructor(path_, typeCheck_) {
|
|
90
118
|
this.pointsTo = ph.resolve(path_);
|
|
91
119
|
if (typeCheck_ && !this.checkSync())
|
|
92
|
-
throw new
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Aquires a lock for the path represented by this Road instance, preventing concurrent modifications from this and other Road instances pointing to the same path.
|
|
96
|
-
*
|
|
97
|
-
* @param cb_ A callback function that will be called when the lock is released. This is used internally to manage the lock state.
|
|
98
|
-
* @returns An object with a dispose method that releases the lock when called.
|
|
99
|
-
* @throws If the road is immutable, a {@link RdErr} will be thrown.
|
|
100
|
-
*
|
|
101
|
-
* @remarks This method MUST be used with a `using` statement to ensure that the lock is released properly. Failing to do so may result in deadlocks or other concurrency issues.
|
|
102
|
-
* Non-deterministic release is unfortunately not possible due to the nature of JavaScript's garbage collection, thus the lock must be released deterministically by the user (to which the `using` statement is a convenient way to do so).
|
|
103
|
-
*/
|
|
104
|
-
async lock(cb_ = () => { }) {
|
|
105
|
-
if (!this.mutable)
|
|
106
|
-
throw new RdErr(`Road to '${this.isAt}' is immutable.`);
|
|
107
|
-
lockedRoads ??= new Map();
|
|
108
|
-
const isAt = this.isAt;
|
|
109
|
-
await lockedRoads.get(isAt); // will skip if `undefined` (no lock)
|
|
110
|
-
lockedRoads.set(isAt, new Promise(res => cb_ = res));
|
|
111
|
-
return {
|
|
112
|
-
[Symbol.dispose]() {
|
|
113
|
-
cb_();
|
|
114
|
-
lockedRoads.delete(isAt);
|
|
115
|
-
},
|
|
116
|
-
async [Symbol.asyncDispose]() {
|
|
117
|
-
cb_();
|
|
118
|
-
lockedRoads.delete(isAt);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Sync version of {@link lock}.
|
|
124
|
-
* @throws Also throws a {@link RdErr} if the road is currently locked by another operation as it cannot wait for the lock to be released in a synchronous context.
|
|
125
|
-
*/
|
|
126
|
-
lockSync(cb_ = () => { }) {
|
|
127
|
-
if (!this.mutable)
|
|
128
|
-
throw new RdErr(`Road to '${this.isAt}' is immutable.`);
|
|
129
|
-
lockedRoads ??= new Map();
|
|
130
|
-
const isAt = this.isAt;
|
|
131
|
-
if (lockedRoads.has(isAt))
|
|
132
|
-
throw new RdErr(`Road to '${this.isAt}' is currently locked by another operation.`);
|
|
133
|
-
lockedRoads.set(isAt, new Promise(res => cb_ = res));
|
|
134
|
-
return {
|
|
135
|
-
[Symbol.dispose]() {
|
|
136
|
-
lockedRoads.delete(isAt);
|
|
137
|
-
cb_();
|
|
138
|
-
},
|
|
139
|
-
async [Symbol.asyncDispose]() {
|
|
140
|
-
lockedRoads.delete(isAt);
|
|
141
|
-
cb_();
|
|
142
|
-
}
|
|
143
|
-
};
|
|
120
|
+
throw new Err(`Type mismatch: '${this.isAt}'`);
|
|
144
121
|
}
|
|
145
122
|
/** @returns An instance of {@link Folder} representing the parent directory of the current road. */
|
|
146
123
|
parent() { return new Folder(ph.dirname(this.isAt), false); }
|
|
@@ -160,6 +137,36 @@ export class Road {
|
|
|
160
137
|
}
|
|
161
138
|
/** @returns An array of {@link Folder} instances representing the ancestors of the current road. */
|
|
162
139
|
ancestors() { return [...this.ancestorsIt()]; }
|
|
140
|
+
reserveLock(allowConcurrent) {
|
|
141
|
+
if (!this.mutable)
|
|
142
|
+
throw new Err(`Road to '${this.isAt}' is immutable.`);
|
|
143
|
+
lockedRoads ??= new Map();
|
|
144
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
145
|
+
const isAt = this.isAt;
|
|
146
|
+
const previous = lockedRoads.get(this.isAt);
|
|
147
|
+
if (!allowConcurrent && previous)
|
|
148
|
+
throw new Err(`Road to '${this.isAt}' is already locked.`);
|
|
149
|
+
lockedRoads.set(this.isAt, promise);
|
|
150
|
+
return {
|
|
151
|
+
[Symbol.dispose]: () => {
|
|
152
|
+
resolve();
|
|
153
|
+
if (lockedRoads.get(isAt) === promise)
|
|
154
|
+
lockedRoads.delete(isAt);
|
|
155
|
+
},
|
|
156
|
+
previous,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
async lock() {
|
|
160
|
+
const l = this.reserveLock(true);
|
|
161
|
+
try {
|
|
162
|
+
await l.previous;
|
|
163
|
+
}
|
|
164
|
+
catch { }
|
|
165
|
+
return l;
|
|
166
|
+
}
|
|
167
|
+
lockSync() {
|
|
168
|
+
return this.reserveLock(false);
|
|
169
|
+
}
|
|
163
170
|
/**
|
|
164
171
|
* Watches the current entry for changes and resolves when the entry becomes accessible (i.e., exists and can be accessed).
|
|
165
172
|
*
|
|
@@ -215,10 +222,50 @@ export class Road {
|
|
|
215
222
|
lstat() { return fp.lstat(this.isAt); }
|
|
216
223
|
/** @returns The result of {@link fs.lstatSync} for the current entry. */
|
|
217
224
|
lstatSync() { return fs.lstatSync(this.isAt); }
|
|
218
|
-
/**
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
225
|
+
/** Wrapper around {@link fp.rm} with locking. */
|
|
226
|
+
async delete() {
|
|
227
|
+
using _ = await this.lock();
|
|
228
|
+
await fp.rm(this.isAt, { recursive: true, force: true });
|
|
229
|
+
}
|
|
230
|
+
/** Wrapper around {@link fs.rmSync} with locking. */
|
|
231
|
+
deleteSync() {
|
|
232
|
+
using _ = this.lockSync();
|
|
233
|
+
fs.rmSync(this.isAt, { recursive: true, force: true });
|
|
234
|
+
}
|
|
235
|
+
async copy(into_) {
|
|
236
|
+
const newPath = into_.join(this.name);
|
|
237
|
+
await fp.cp(this.isAt, newPath, { recursive: true, force: true });
|
|
238
|
+
return new this.constructor(newPath, false);
|
|
239
|
+
}
|
|
240
|
+
copySync(into_) {
|
|
241
|
+
const newPath = into_.join(this.name);
|
|
242
|
+
fs.cpSync(this.isAt, newPath, { recursive: true, force: true });
|
|
243
|
+
return new this.constructor(newPath, false);
|
|
244
|
+
}
|
|
245
|
+
async move(into_) {
|
|
246
|
+
using _ = await this.lock();
|
|
247
|
+
const newPath = into_.join(this.name);
|
|
248
|
+
await fp.rename(this.isAt, newPath);
|
|
249
|
+
this.pointsTo = newPath;
|
|
250
|
+
}
|
|
251
|
+
moveSync(into_) {
|
|
252
|
+
using _ = this.lockSync();
|
|
253
|
+
const newPath = into_.join(this.name);
|
|
254
|
+
fs.renameSync(this.isAt, newPath);
|
|
255
|
+
this.pointsTo = newPath;
|
|
256
|
+
}
|
|
257
|
+
async rename(newName_) {
|
|
258
|
+
using _ = await this.lock();
|
|
259
|
+
const newPath = this.parent().join(newName_);
|
|
260
|
+
await fp.rename(this.isAt, newPath);
|
|
261
|
+
this.pointsTo = newPath;
|
|
262
|
+
}
|
|
263
|
+
renameSync(newName_) {
|
|
264
|
+
using _ = this.lockSync();
|
|
265
|
+
const newPath = this.parent().join(newName_);
|
|
266
|
+
fs.renameSync(this.isAt, newPath);
|
|
267
|
+
this.pointsTo = newPath;
|
|
268
|
+
}
|
|
222
269
|
/** Type narrowing for {@link File} (similar to `instanceof` without unnecessary runtime checks). */
|
|
223
270
|
isFile() { return false; }
|
|
224
271
|
/** Type narrowing for {@link Folder} (similar to `instanceof` without unnecessary runtime checks). */
|
|
@@ -227,10 +274,6 @@ export class Road {
|
|
|
227
274
|
isFolder() { return false; }
|
|
228
275
|
/** Type narrowing for {@link Folder} (similar to `instanceof` without unnecessary runtime checks). */
|
|
229
276
|
isDirectory() { return false; }
|
|
230
|
-
/** Type narrowing for {@link Folder} (similar to `instanceof` without unnecessary runtime checks). */
|
|
231
|
-
isDict() { return false; }
|
|
232
|
-
/** Type narrowing for {@link Folder} (similar to `instanceof` without unnecessary runtime checks). */
|
|
233
|
-
isDictionary() { return false; }
|
|
234
277
|
/** Type narrowing for {@link SymbolicLink} (similar to `instanceof` without unnecessary runtime checks). */
|
|
235
278
|
isSymlink() { return false; }
|
|
236
279
|
/** Type narrowing for {@link SymbolicLink} (similar to `instanceof` without unnecessary runtime checks). */
|
|
@@ -248,54 +291,55 @@ export class Road {
|
|
|
248
291
|
}
|
|
249
292
|
/** Subclass of {@link Road} that represents a file. */
|
|
250
293
|
export class File extends Road {
|
|
251
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Creates a new file at the specified path if it does not already exist.
|
|
296
|
+
*
|
|
297
|
+
* @param at_ The path at which to create the file.
|
|
298
|
+
* @returns A promise that resolves to the newly created `File` instance.
|
|
299
|
+
* @throws if {@link fp.writeFile} throws.
|
|
300
|
+
*/
|
|
301
|
+
static async create(at_, data = "") {
|
|
252
302
|
try {
|
|
253
303
|
await fp.access(at_, fsc.W_OK);
|
|
254
304
|
}
|
|
255
305
|
catch {
|
|
256
|
-
await fp.writeFile(at_,
|
|
306
|
+
await fp.writeFile(at_, data);
|
|
257
307
|
}
|
|
258
308
|
return new File(at_, true);
|
|
259
309
|
}
|
|
310
|
+
/** Alias for {@link File.create}. */
|
|
260
311
|
static mk = File.create;
|
|
261
|
-
|
|
312
|
+
/** Synchronous version of {@link File.create}. */
|
|
313
|
+
static createSync(at_, data = "") {
|
|
262
314
|
try {
|
|
263
315
|
fs.accessSync(at_, fs.constants.W_OK);
|
|
264
316
|
}
|
|
265
317
|
catch {
|
|
266
|
-
fs.writeFileSync(at_,
|
|
318
|
+
fs.writeFileSync(at_, data);
|
|
267
319
|
}
|
|
268
320
|
return new File(at_, true);
|
|
269
321
|
}
|
|
322
|
+
/** Alias for {@link File.createSync}. */
|
|
270
323
|
static mkSync = File.createSync;
|
|
324
|
+
/** The file extension of this file, including the leading dot. */
|
|
271
325
|
get ext() { return ph.extname(this.isAt); }
|
|
326
|
+
/** The file name without its extension. */
|
|
272
327
|
get noExt() { return ph.basename(this.isAt, this.ext); }
|
|
273
|
-
async read(
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return fs.readFileSync(this.isAt, { encoding: encoding_, flag: flag_ });
|
|
282
|
-
else
|
|
283
|
-
return fs.readFileSync(this.isAt);
|
|
284
|
-
}
|
|
285
|
-
async *itBuff(chunkSize_ = 64 * 1024, flags_ = 'r', mode_) {
|
|
286
|
-
const fd = await fp.open(this.isAt, flags_, mode_);
|
|
328
|
+
async read(options_) {
|
|
329
|
+
return fp.readFile(this.isAt, options_);
|
|
330
|
+
}
|
|
331
|
+
readSync(options_) {
|
|
332
|
+
return fs.readFileSync(this.isAt, options_);
|
|
333
|
+
}
|
|
334
|
+
async *itBuff(options_) {
|
|
335
|
+
const stream = fs.createReadStream(this.isAt, { ...options_, encoding: undefined });
|
|
287
336
|
try {
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
do {
|
|
291
|
-
const readResult = await fd.read(buffer, 0, chunkSize_, null);
|
|
292
|
-
bytesRead = readResult.bytesRead;
|
|
293
|
-
if (bytesRead > 0)
|
|
294
|
-
yield buffer.subarray(0, bytesRead);
|
|
295
|
-
} while (bytesRead === chunkSize_);
|
|
337
|
+
for await (const chunk of stream)
|
|
338
|
+
yield chunk;
|
|
296
339
|
}
|
|
297
340
|
finally {
|
|
298
|
-
|
|
341
|
+
if (!stream.destroyed)
|
|
342
|
+
stream.destroy();
|
|
299
343
|
}
|
|
300
344
|
}
|
|
301
345
|
*itBuffSync(chunkSize_ = 64 * 1024, flags_ = 'r', mode_) {
|
|
@@ -306,145 +350,130 @@ export class File extends Road {
|
|
|
306
350
|
do {
|
|
307
351
|
bytesRead = fs.readSync(fd, buffer, 0, chunkSize_, null);
|
|
308
352
|
if (bytesRead > 0)
|
|
309
|
-
yield buffer.subarray(0, bytesRead);
|
|
353
|
+
yield Buffer.from(buffer.subarray(0, bytesRead));
|
|
310
354
|
} while (bytesRead === chunkSize_);
|
|
311
355
|
}
|
|
312
356
|
finally {
|
|
313
357
|
fs.closeSync(fd);
|
|
314
358
|
}
|
|
315
359
|
}
|
|
316
|
-
async
|
|
317
|
-
|
|
318
|
-
|
|
360
|
+
async sameAs(other_) {
|
|
361
|
+
if (this.isAt === other_.isAt)
|
|
362
|
+
return true;
|
|
363
|
+
const [s1, s2] = await Promise.all([this.size(), other_.size()]);
|
|
364
|
+
if (s1 !== s2)
|
|
365
|
+
return false;
|
|
366
|
+
if (s1 === 0)
|
|
367
|
+
return true;
|
|
368
|
+
const iter1 = this.itBuff();
|
|
369
|
+
const iter2 = other_.itBuff();
|
|
370
|
+
try {
|
|
371
|
+
while (true) {
|
|
372
|
+
const [a, b] = await Promise.all([iter1.next(), iter2.next()]);
|
|
373
|
+
if (a.done && b.done)
|
|
374
|
+
return true;
|
|
375
|
+
if (a.done !== b.done)
|
|
376
|
+
return false;
|
|
377
|
+
if (!a.value.equals(b.value))
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
finally {
|
|
382
|
+
await Promise.all([iter1.return?.(undefined), iter2.return?.(undefined)]);
|
|
383
|
+
}
|
|
319
384
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
385
|
+
sameAsSync(other_) {
|
|
386
|
+
if (this.isAt === other_.isAt)
|
|
387
|
+
return true;
|
|
388
|
+
const [s1, s2] = [this.sizeSync(), other_.sizeSync()];
|
|
389
|
+
if (s1 !== s2)
|
|
390
|
+
return false;
|
|
391
|
+
if (s1 === 0)
|
|
392
|
+
return true;
|
|
393
|
+
const iter1 = this.itBuffSync();
|
|
394
|
+
const iter2 = other_.itBuffSync();
|
|
395
|
+
try {
|
|
396
|
+
while (true) {
|
|
397
|
+
const a = iter1.next();
|
|
398
|
+
const b = iter2.next();
|
|
399
|
+
if (a.done && b.done)
|
|
400
|
+
return true;
|
|
401
|
+
if (a.done !== b.done)
|
|
402
|
+
return false;
|
|
403
|
+
if (!a.value.equals(b.value))
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
finally {
|
|
408
|
+
try {
|
|
409
|
+
iter1.return?.(undefined);
|
|
410
|
+
}
|
|
411
|
+
catch { }
|
|
412
|
+
try {
|
|
413
|
+
iter2.return?.(undefined);
|
|
414
|
+
}
|
|
415
|
+
catch { }
|
|
416
|
+
}
|
|
323
417
|
}
|
|
324
|
-
async
|
|
325
|
-
|
|
326
|
-
await
|
|
418
|
+
async hash(algorithm_ = "sha256", options_, encoding_) {
|
|
419
|
+
const hash = cr.createHash(algorithm_, options_);
|
|
420
|
+
for await (const chunk of this.itBuff())
|
|
421
|
+
hash.update(chunk);
|
|
422
|
+
return encoding_ ? hash.digest(encoding_) : hash.digest();
|
|
327
423
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
424
|
+
hashSync(algorithm_ = "sha256", options_, encoding_) {
|
|
425
|
+
const hash = cr.createHash(algorithm_, options_);
|
|
426
|
+
for (const chunk of this.itBuffSync())
|
|
427
|
+
hash.update(chunk);
|
|
428
|
+
return encoding_ ? hash.digest(encoding_) : hash.digest();
|
|
331
429
|
}
|
|
332
|
-
async
|
|
430
|
+
async size() { return (await this.lstat()).size; }
|
|
431
|
+
sizeSync() { return this.lstatSync().size; }
|
|
432
|
+
async writeAtomic(data_, options_) {
|
|
333
433
|
using _ = await this.lock();
|
|
334
|
-
await
|
|
434
|
+
await this.parent().borrow(File, async (tmp) => {
|
|
435
|
+
using _ = await tmp.lock();
|
|
436
|
+
await fp.writeFile(tmp.isAt, data_, options_);
|
|
437
|
+
await fp.rename(tmp.isAt, this.isAt);
|
|
438
|
+
});
|
|
335
439
|
}
|
|
336
|
-
|
|
440
|
+
writeAtomicSync(data_, options_) {
|
|
337
441
|
using _ = this.lockSync();
|
|
338
|
-
|
|
442
|
+
this.parent().borrowSync(File, tmp => {
|
|
443
|
+
using _ = tmp.lockSync();
|
|
444
|
+
fs.writeFileSync(tmp.isAt, data_, options_);
|
|
445
|
+
fs.renameSync(tmp.isAt, this.isAt);
|
|
446
|
+
});
|
|
339
447
|
}
|
|
340
|
-
async
|
|
448
|
+
async write(data_, options_) {
|
|
341
449
|
using _ = await this.lock();
|
|
342
|
-
|
|
343
|
-
await fp.rename(this.isAt, newPath);
|
|
344
|
-
this.pointsTo = newPath;
|
|
450
|
+
await fp.writeFile(this.isAt, data_, options_);
|
|
345
451
|
}
|
|
346
|
-
|
|
452
|
+
writeSync(data_, options_) {
|
|
347
453
|
using _ = this.lockSync();
|
|
348
|
-
|
|
349
|
-
fs.renameSync(this.isAt, newPath);
|
|
350
|
-
this.pointsTo = newPath;
|
|
351
|
-
}
|
|
352
|
-
async copy(into_) {
|
|
353
|
-
const newPath = into_.join(this.name);
|
|
354
|
-
await fp.copyFile(this.isAt, newPath);
|
|
355
|
-
return new File(newPath, false);
|
|
356
|
-
}
|
|
357
|
-
copySync(into_) {
|
|
358
|
-
const newPath = into_.join(this.name);
|
|
359
|
-
fs.copyFileSync(this.isAt, newPath);
|
|
360
|
-
return new File(newPath, false);
|
|
454
|
+
fs.writeFileSync(this.isAt, data_, options_);
|
|
361
455
|
}
|
|
362
|
-
async
|
|
456
|
+
async append(data_, options_) {
|
|
363
457
|
using _ = await this.lock();
|
|
364
|
-
|
|
365
|
-
await fp.rename(this.isAt, newPath);
|
|
366
|
-
this.pointsTo = newPath;
|
|
458
|
+
await fp.appendFile(this.isAt, data_, options_);
|
|
367
459
|
}
|
|
368
|
-
|
|
460
|
+
appendSync(data_, options_) {
|
|
369
461
|
using _ = this.lockSync();
|
|
370
|
-
|
|
371
|
-
fs.renameSync(this.isAt, newPath);
|
|
372
|
-
this.pointsTo = newPath;
|
|
373
|
-
}
|
|
374
|
-
async check() { return (await fp.lstat(this.isAt)).isFile(); }
|
|
375
|
-
checkSync() { return fs.lstatSync(this.isAt).isFile(); }
|
|
376
|
-
isFile() { return true; }
|
|
377
|
-
}
|
|
378
|
-
export async function hash(f_, algorithm_ = "sha256", options_, encoding_) {
|
|
379
|
-
const hash = cr.createHash(algorithm_, options_);
|
|
380
|
-
for await (const chunk of f_.itBuff())
|
|
381
|
-
hash.update(chunk);
|
|
382
|
-
return encoding_ ? hash.digest(encoding_) : hash.digest();
|
|
383
|
-
}
|
|
384
|
-
export function hashSync(f_, algorithm_ = "sha256", options_, encoding_) {
|
|
385
|
-
const hash = cr.createHash(algorithm_, options_);
|
|
386
|
-
for (const chunk of f_.itBuffSync())
|
|
387
|
-
hash.update(chunk);
|
|
388
|
-
return encoding_ ? hash.digest(encoding_) : hash.digest();
|
|
389
|
-
}
|
|
390
|
-
export async function streamHash(f_, algorithm_ = "sha256", options_, encoding_) {
|
|
391
|
-
const hash = cr.createHash(algorithm_, options_);
|
|
392
|
-
for await (const chunk of f_.itBuff())
|
|
393
|
-
hash.update(chunk);
|
|
394
|
-
return encoding_ ? hash.digest(encoding_) : hash.digest();
|
|
395
|
-
}
|
|
396
|
-
export function streamHashSync(f_, algorithm_ = "sha256", options_, encoding_) {
|
|
397
|
-
const hash = cr.createHash(algorithm_, options_);
|
|
398
|
-
for (const chunk of f_.itBuffSync())
|
|
399
|
-
hash.update(chunk);
|
|
400
|
-
return encoding_ ? hash.digest(encoding_) : hash.digest();
|
|
401
|
-
}
|
|
402
|
-
export async function* itLines(f_, options_ = { encoding: 'utf-8' }) {
|
|
403
|
-
const readStream = fs.createReadStream(f_.isAt, options_);
|
|
404
|
-
const rlInterface = rl.createInterface({ input: readStream, crlfDelay: Infinity });
|
|
405
|
-
try {
|
|
406
|
-
for await (const line of rlInterface)
|
|
407
|
-
yield line;
|
|
462
|
+
fs.appendFileSync(this.isAt, data_, options_);
|
|
408
463
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
readStream.destroy();
|
|
464
|
+
async check() { try {
|
|
465
|
+
return (await fp.lstat(this.isAt)).isFile();
|
|
412
466
|
}
|
|
413
|
-
|
|
414
|
-
export async function fileSameAs(f1_, f2_) {
|
|
415
|
-
if (f1_.isAt === f2_.isAt)
|
|
416
|
-
return true;
|
|
417
|
-
else if ((await fp.lstat(f1_.isAt)).size !== (await fp.lstat(f2_.isAt)).size)
|
|
467
|
+
catch {
|
|
418
468
|
return false;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const [a, b] = await Promise.all([iter1.next(), iter2.next()]);
|
|
423
|
-
if (a.done && b.done)
|
|
424
|
-
return true;
|
|
425
|
-
if (a.done !== b.done)
|
|
426
|
-
return false;
|
|
427
|
-
if (!a.value.equals(b.value))
|
|
428
|
-
return false;
|
|
469
|
+
} }
|
|
470
|
+
checkSync() { try {
|
|
471
|
+
return fs.lstatSync(this.isAt).isFile();
|
|
429
472
|
}
|
|
430
|
-
|
|
431
|
-
export function fileSameAsSync(f1_, f2_) {
|
|
432
|
-
if (f1_.isAt === f2_.isAt)
|
|
433
|
-
return true;
|
|
434
|
-
else if (fs.statSync(f1_.isAt).size !== fs.statSync(f2_.isAt).size)
|
|
473
|
+
catch {
|
|
435
474
|
return false;
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
while (true) {
|
|
439
|
-
const a = iter1.next();
|
|
440
|
-
const b = iter2.next();
|
|
441
|
-
if (a.done && b.done)
|
|
442
|
-
return true;
|
|
443
|
-
if (a.done !== b.done)
|
|
444
|
-
return false;
|
|
445
|
-
if (!a.value.equals(b.value))
|
|
446
|
-
return false;
|
|
447
|
-
}
|
|
475
|
+
} }
|
|
476
|
+
isFile() { return true; }
|
|
448
477
|
}
|
|
449
478
|
export class Folder extends Road {
|
|
450
479
|
static async create(at_) {
|
|
@@ -470,40 +499,50 @@ export class Folder extends Road {
|
|
|
470
499
|
join(...paths_) {
|
|
471
500
|
return ph.join(this.isAt, ...paths_);
|
|
472
501
|
}
|
|
473
|
-
async *it(
|
|
474
|
-
for (const
|
|
475
|
-
const road =
|
|
476
|
-
if (!
|
|
502
|
+
async *it(filter_) {
|
|
503
|
+
for (const entry of await fp.readdir(this.isAt, { withFileTypes: true })) {
|
|
504
|
+
const road = new (resolveDirent(entry))(this.join(entry.name), false);
|
|
505
|
+
if (!filter_ || filter_(road))
|
|
477
506
|
yield road;
|
|
478
507
|
}
|
|
479
508
|
}
|
|
480
|
-
*itSync(
|
|
481
|
-
for (const entry of fs.readdirSync(this.isAt)) {
|
|
482
|
-
const road =
|
|
483
|
-
if (!
|
|
509
|
+
*itSync(filter_) {
|
|
510
|
+
for (const entry of fs.readdirSync(this.isAt, { withFileTypes: true })) {
|
|
511
|
+
const road = new (resolveDirent(entry))(this.join(entry.name), false);
|
|
512
|
+
if (!filter_ || filter_(road))
|
|
484
513
|
yield road;
|
|
485
514
|
}
|
|
486
515
|
}
|
|
487
|
-
async list(
|
|
488
|
-
const entries = (await fp.readdir(this.isAt)).map(
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
516
|
+
async list(filter_) {
|
|
517
|
+
const entries = (await fp.readdir(this.isAt, { withFileTypes: true })).map(e => new (resolveDirent(e))(this.join(e.name), false));
|
|
518
|
+
return filter_ ? entries.filter(entry => filter_(entry)) : entries;
|
|
519
|
+
}
|
|
520
|
+
listSync(filter_) {
|
|
521
|
+
const entries = fs.readdirSync(this.isAt, { withFileTypes: true }).map(e => new (resolveDirent(e))(this.join(e.name), false));
|
|
522
|
+
return filter_ ? entries.filter(entry => filter_(entry)) : entries;
|
|
523
|
+
}
|
|
524
|
+
async *walk(filter_) {
|
|
525
|
+
for await (const entry of this.it()) {
|
|
526
|
+
if (!filter_ || filter_(entry))
|
|
527
|
+
yield entry;
|
|
528
|
+
if (entry.isDir())
|
|
529
|
+
yield* entry.walk(filter_);
|
|
530
|
+
}
|
|
493
531
|
}
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
532
|
+
*walkSync(filter_) {
|
|
533
|
+
for (const entry of this.itSync()) {
|
|
534
|
+
if (!filter_ || filter_(entry))
|
|
535
|
+
yield entry;
|
|
536
|
+
if (entry.isDir())
|
|
537
|
+
yield* entry.walkSync(filter_);
|
|
538
|
+
}
|
|
499
539
|
}
|
|
500
|
-
async find(name_,
|
|
540
|
+
async find(name_, expect_) {
|
|
501
541
|
try {
|
|
502
|
-
await fp.access(this.join(name_), fs.constants.F_OK);
|
|
503
542
|
const found = await factory(this.join(name_));
|
|
504
|
-
if (!
|
|
543
|
+
if (!expect_)
|
|
505
544
|
return found;
|
|
506
|
-
if (found instanceof
|
|
545
|
+
if (found instanceof expect_)
|
|
507
546
|
return found;
|
|
508
547
|
return null;
|
|
509
548
|
}
|
|
@@ -511,78 +550,74 @@ export class Folder extends Road {
|
|
|
511
550
|
return null;
|
|
512
551
|
}
|
|
513
552
|
}
|
|
514
|
-
findSync(name_,
|
|
553
|
+
findSync(name_, expect_) {
|
|
515
554
|
try {
|
|
516
555
|
const found = factorySync(this.join(name_));
|
|
517
|
-
if (!
|
|
556
|
+
if (!expect_)
|
|
518
557
|
return found;
|
|
519
|
-
if (found instanceof
|
|
558
|
+
if (found instanceof expect_)
|
|
520
559
|
return found;
|
|
521
560
|
return null;
|
|
522
561
|
}
|
|
523
|
-
catch {
|
|
562
|
+
catch (e) {
|
|
524
563
|
return null;
|
|
525
564
|
}
|
|
526
565
|
}
|
|
527
566
|
async add(name_, createable_) {
|
|
528
567
|
const newPath = this.join(name_);
|
|
529
|
-
await createable_.
|
|
568
|
+
await createable_.mk(newPath);
|
|
530
569
|
return (await factory(newPath));
|
|
531
570
|
}
|
|
532
571
|
addSync(name_, createable_) {
|
|
533
572
|
const newPath = this.join(name_);
|
|
534
|
-
createable_.
|
|
573
|
+
createable_.mkSync(newPath);
|
|
535
574
|
return factorySync(newPath);
|
|
536
575
|
}
|
|
537
|
-
async
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
async move(into_) {
|
|
546
|
-
using _ = await this.lock();
|
|
547
|
-
const newPath = into_.join(this.name);
|
|
548
|
-
await fp.rename(this.isAt, newPath);
|
|
549
|
-
this.pointsTo = newPath;
|
|
576
|
+
async borrow(createable_, cb_) {
|
|
577
|
+
const path = this.join(`instrumentality@${crypto.randomUUID()}`);
|
|
578
|
+
try {
|
|
579
|
+
await cb_(await createable_.mk(path));
|
|
580
|
+
}
|
|
581
|
+
finally {
|
|
582
|
+
await fp.rm(path, { recursive: true, force: true });
|
|
583
|
+
}
|
|
550
584
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
585
|
+
borrowSync(createable_, cb_) {
|
|
586
|
+
const path = this.join(`instrumentality@${crypto.randomUUID()}`);
|
|
587
|
+
try {
|
|
588
|
+
cb_(createable_.mkSync(path));
|
|
589
|
+
}
|
|
590
|
+
finally {
|
|
591
|
+
fs.rmSync(path, { recursive: true, force: true });
|
|
592
|
+
}
|
|
556
593
|
}
|
|
557
|
-
async
|
|
558
|
-
|
|
559
|
-
await
|
|
560
|
-
|
|
594
|
+
async size() {
|
|
595
|
+
let size = 0;
|
|
596
|
+
for await (const entry of this.it())
|
|
597
|
+
size += await entry.size();
|
|
598
|
+
return size;
|
|
561
599
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
600
|
+
sizeSync() {
|
|
601
|
+
let size = 0;
|
|
602
|
+
for (const entry of this.itSync())
|
|
603
|
+
size += entry.sizeSync();
|
|
604
|
+
return size;
|
|
566
605
|
}
|
|
567
|
-
async
|
|
568
|
-
|
|
569
|
-
const newPath = this.parent().join(to_);
|
|
570
|
-
await fp.rename(this.isAt, newPath);
|
|
571
|
-
this.pointsTo = newPath;
|
|
606
|
+
async check() { try {
|
|
607
|
+
return (await fp.lstat(this.isAt)).isDirectory();
|
|
572
608
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
this.
|
|
609
|
+
catch {
|
|
610
|
+
return false;
|
|
611
|
+
} }
|
|
612
|
+
checkSync() { try {
|
|
613
|
+
return fs.lstatSync(this.isAt).isDirectory();
|
|
578
614
|
}
|
|
579
|
-
|
|
580
|
-
|
|
615
|
+
catch {
|
|
616
|
+
return false;
|
|
617
|
+
} }
|
|
581
618
|
isFolder() { return true; }
|
|
582
619
|
isDir() { return true; }
|
|
583
620
|
isDirectory() { return true; }
|
|
584
|
-
isDict() { return true; }
|
|
585
|
-
isDictionary() { return true; }
|
|
586
621
|
}
|
|
587
622
|
export function sysRoot() { return new Folder(ph.parse(process.cwd()).root, false); }
|
|
588
623
|
export function home() { return new Folder(os.homedir(), false); }
|
|
@@ -617,13 +652,17 @@ export class SymbolicLink extends Road {
|
|
|
617
652
|
return factorySync(ph.resolve(ph.dirname(this.isAt), fs.readlinkSync(this.isAt)));
|
|
618
653
|
}
|
|
619
654
|
async retarget(to_) {
|
|
620
|
-
await this.
|
|
621
|
-
|
|
655
|
+
using _ = await this.lock();
|
|
656
|
+
await fp.unlink(this.isAt);
|
|
657
|
+
await fp.symlink(to_.isAt, this.isAt);
|
|
622
658
|
}
|
|
623
659
|
retargetSync(to_) {
|
|
624
|
-
this.
|
|
660
|
+
using _ = this.lockSync();
|
|
661
|
+
fs.unlinkSync(this.isAt);
|
|
625
662
|
fs.symlinkSync(to_.isAt, this.isAt);
|
|
626
663
|
}
|
|
664
|
+
async size() { return (await this.lstat()).size; }
|
|
665
|
+
sizeSync() { return this.lstatSync().size; }
|
|
627
666
|
async delete() {
|
|
628
667
|
using _ = await this.lock();
|
|
629
668
|
await fp.unlink(this.isAt);
|
|
@@ -632,132 +671,106 @@ export class SymbolicLink extends Road {
|
|
|
632
671
|
using _ = this.lockSync();
|
|
633
672
|
fs.unlinkSync(this.isAt);
|
|
634
673
|
}
|
|
635
|
-
async
|
|
636
|
-
|
|
637
|
-
const newPath = into_.join(this.name);
|
|
638
|
-
await fp.rename(this.isAt, newPath);
|
|
639
|
-
this.pointsTo = newPath;
|
|
640
|
-
}
|
|
641
|
-
moveSync(into_) {
|
|
642
|
-
using _ = this.lockSync();
|
|
643
|
-
const newPath = into_.join(this.name);
|
|
644
|
-
fs.renameSync(this.isAt, newPath);
|
|
645
|
-
this.pointsTo = newPath;
|
|
646
|
-
}
|
|
647
|
-
async copy(into_) {
|
|
648
|
-
const newPath = into_.join(this.name);
|
|
649
|
-
const target = await this.target();
|
|
650
|
-
await fp.symlink(target.isAt, newPath);
|
|
651
|
-
return new SymbolicLink(newPath, false);
|
|
652
|
-
}
|
|
653
|
-
copySync(into_) {
|
|
654
|
-
const newPath = into_.join(this.name);
|
|
655
|
-
const target = this.targetSync();
|
|
656
|
-
fs.symlinkSync(target.isAt, newPath);
|
|
657
|
-
return new SymbolicLink(newPath, false);
|
|
658
|
-
}
|
|
659
|
-
async rename(to_) {
|
|
660
|
-
using _ = await this.lock();
|
|
661
|
-
const newPath = this.parent().join(to_);
|
|
662
|
-
await fp.rename(this.isAt, newPath);
|
|
663
|
-
this.pointsTo = newPath;
|
|
674
|
+
async check() { try {
|
|
675
|
+
return (await fp.lstat(this.isAt)).isSymbolicLink();
|
|
664
676
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
this.
|
|
677
|
+
catch {
|
|
678
|
+
return false;
|
|
679
|
+
} }
|
|
680
|
+
checkSync() { try {
|
|
681
|
+
return fs.lstatSync(this.isAt).isSymbolicLink();
|
|
670
682
|
}
|
|
671
|
-
|
|
672
|
-
|
|
683
|
+
catch {
|
|
684
|
+
return false;
|
|
685
|
+
} }
|
|
673
686
|
isSymlink() { return true; }
|
|
674
687
|
isSymbolicLink() { return true; }
|
|
675
688
|
}
|
|
676
689
|
export { SymbolicLink as Symlink };
|
|
677
690
|
export class UnusableRoad extends Road {
|
|
678
691
|
mutable = false; // Modification will cause system issues (e.g. deleting a device file)
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
692
|
+
async size() { return 0; }
|
|
693
|
+
sizeSync() { return 0; }
|
|
694
|
+
error() { throw new Err(`${this.constructor.name} at '${this.isAt}' is a system-level resource thus not subject to modification.`); }
|
|
695
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be locked */
|
|
696
|
+
lock() { return this.error(); }
|
|
697
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be locked */
|
|
685
698
|
lockSync() { return this.error(); }
|
|
686
|
-
|
|
699
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be deleted */
|
|
700
|
+
delete() { return this.error(); }
|
|
701
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be deleted */
|
|
687
702
|
deleteSync() { return this.error(); }
|
|
688
|
-
|
|
703
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be moved */
|
|
704
|
+
move() { return this.error(); }
|
|
705
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be moved */
|
|
689
706
|
moveSync() { return this.error(); }
|
|
690
|
-
|
|
707
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be copied */
|
|
708
|
+
copy() { return this.error(); }
|
|
709
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be copied */
|
|
691
710
|
copySync() { return this.error(); }
|
|
692
|
-
|
|
711
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be renamed */
|
|
712
|
+
rename() { return this.error(); }
|
|
713
|
+
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be renamed */
|
|
693
714
|
renameSync() { return this.error(); }
|
|
694
715
|
isUnusable() { return true; }
|
|
695
716
|
}
|
|
696
717
|
export class BlockDevice extends UnusableRoad {
|
|
697
|
-
async check() {
|
|
698
|
-
|
|
718
|
+
async check() { try {
|
|
719
|
+
return (await fp.lstat(this.isAt)).isBlockDevice();
|
|
720
|
+
}
|
|
721
|
+
catch {
|
|
722
|
+
return false;
|
|
723
|
+
} }
|
|
724
|
+
checkSync() { try {
|
|
725
|
+
return fs.lstatSync(this.isAt).isBlockDevice();
|
|
726
|
+
}
|
|
727
|
+
catch {
|
|
728
|
+
return false;
|
|
729
|
+
} }
|
|
699
730
|
isBlockDevice() { return true; }
|
|
700
731
|
}
|
|
701
732
|
export class CharacterDevice extends UnusableRoad {
|
|
702
|
-
async check() {
|
|
703
|
-
|
|
733
|
+
async check() { try {
|
|
734
|
+
return (await fp.lstat(this.isAt)).isCharacterDevice();
|
|
735
|
+
}
|
|
736
|
+
catch {
|
|
737
|
+
return false;
|
|
738
|
+
} }
|
|
739
|
+
checkSync() { try {
|
|
740
|
+
return fs.lstatSync(this.isAt).isCharacterDevice();
|
|
741
|
+
}
|
|
742
|
+
catch {
|
|
743
|
+
return false;
|
|
744
|
+
} }
|
|
704
745
|
isCharacterDevice() { return true; }
|
|
705
746
|
}
|
|
706
747
|
export class Fifo extends UnusableRoad {
|
|
707
|
-
async check() {
|
|
708
|
-
|
|
748
|
+
async check() { try {
|
|
749
|
+
return (await fp.lstat(this.isAt)).isFIFO();
|
|
750
|
+
}
|
|
751
|
+
catch {
|
|
752
|
+
return false;
|
|
753
|
+
} }
|
|
754
|
+
checkSync() { try {
|
|
755
|
+
return fs.lstatSync(this.isAt).isFIFO();
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
return false;
|
|
759
|
+
} }
|
|
709
760
|
isFifo() { return true; }
|
|
710
761
|
}
|
|
711
762
|
export class Socket extends UnusableRoad {
|
|
712
|
-
async check() {
|
|
713
|
-
|
|
763
|
+
async check() { try {
|
|
764
|
+
return (await fp.lstat(this.isAt)).isSocket();
|
|
765
|
+
}
|
|
766
|
+
catch {
|
|
767
|
+
return false;
|
|
768
|
+
} }
|
|
769
|
+
checkSync() { try {
|
|
770
|
+
return fs.lstatSync(this.isAt).isSocket();
|
|
771
|
+
}
|
|
772
|
+
catch {
|
|
773
|
+
return false;
|
|
774
|
+
} }
|
|
714
775
|
isSocket() { return true; }
|
|
715
776
|
}
|
|
716
|
-
let finalizer = null;
|
|
717
|
-
let toDelete = null;
|
|
718
|
-
let exitHandlerRegistered = null;
|
|
719
|
-
/**
|
|
720
|
-
* Forcefully cleans up all files and folders registered for cleanup on exit.
|
|
721
|
-
*/
|
|
722
|
-
function forceCleanupToDelete() {
|
|
723
|
-
for (const path of toDelete ?? [])
|
|
724
|
-
try {
|
|
725
|
-
fs.rmSync(path, { force: true, recursive: true });
|
|
726
|
-
}
|
|
727
|
-
catch { }
|
|
728
|
-
toDelete?.clear();
|
|
729
|
-
toDelete = null;
|
|
730
|
-
finalizer = null;
|
|
731
|
-
if (exitHandlerRegistered)
|
|
732
|
-
process.off('exit', forceCleanupToDelete);
|
|
733
|
-
exitHandlerRegistered = false;
|
|
734
|
-
}
|
|
735
|
-
export function registerToCleanup(self_) {
|
|
736
|
-
finalizer ??= new FinalizationRegistry(p => { try {
|
|
737
|
-
fs.rmSync(p, { force: true, recursive: true });
|
|
738
|
-
}
|
|
739
|
-
catch { } ; toDelete?.delete(p); });
|
|
740
|
-
toDelete ??= new Set();
|
|
741
|
-
if (!exitHandlerRegistered) {
|
|
742
|
-
process.once('exit', forceCleanupToDelete);
|
|
743
|
-
exitHandlerRegistered = true;
|
|
744
|
-
}
|
|
745
|
-
toDelete.add(self_.isAt);
|
|
746
|
-
finalizer.register(self_, self_.isAt, self_);
|
|
747
|
-
}
|
|
748
|
-
export function Temp(createable_, autoCleanup_) {
|
|
749
|
-
let t = createable_.createSync(tmp().join(`instrumentality@${cr.randomUUID()}`));
|
|
750
|
-
if (autoCleanup_)
|
|
751
|
-
registerToCleanup(t);
|
|
752
|
-
return Object.freeze(Object.assign(t, {
|
|
753
|
-
[Symbol.dispose]() { try {
|
|
754
|
-
t.deleteSync();
|
|
755
|
-
}
|
|
756
|
-
catch { } toDelete?.delete(t.isAt); finalizer?.unregister(t); },
|
|
757
|
-
async [Symbol.asyncDispose]() { try {
|
|
758
|
-
await t.delete();
|
|
759
|
-
}
|
|
760
|
-
catch { } toDelete?.delete(t.isAt); finalizer?.unregister(t); }
|
|
761
|
-
}));
|
|
762
|
-
}
|
|
763
|
-
11;
|