instrumentality 0.0.3 → 0.0.4

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/dist/road.js CHANGED
@@ -5,24 +5,22 @@ import * as fp from "node:fs/promises";
5
5
  import * as ph from "node:path";
6
6
  import * as os from "node:os";
7
7
  import * as cr from "node:crypto";
8
- import * as sp from "node:stream/promises";
9
8
  import { on } from "node:events";
10
9
  import * as bs from "./base.js";
11
- /**
12
- * Subclass of {@link bs.InsErr} that represents an error thrown from this specific module of the library
13
- */
10
+ /** Subclass of {@link bs.InsErr} that represents an error thrown from this specific module of the library */
14
11
  export class RdErr extends bs.InsErr {
15
12
  name = "Instrumentality-Road-Error";
16
13
  }
14
+ export { RdErr as RoadError };
17
15
  /**
18
- * Returns the constructor function corresponding to the file mode.
16
+ * Returns the constructor function corresponding to the file mode (statmode).
19
17
  *
20
- * @param statmode The file mode to check.
21
- * @returns The constructor function corresponding to the file mode.
18
+ * @param statmode_ The file mode to check.
19
+ * @returns The constructor function corresponding to the road type (e.g., {@link File}, {@link Folder}, etc.).
22
20
  * @throws If the file mode is unknown, throws a {@link RdErr}.
23
21
  */
24
- export function resolveMode(statmode) {
25
- switch (statmode & fsc.S_IFMT) {
22
+ export function resolveMode(statmode_) {
23
+ switch (statmode_ & fsc.S_IFMT) {
26
24
  case fsc.S_IFREG: return File;
27
25
  case fsc.S_IFDIR: return Folder;
28
26
  case fsc.S_IFBLK: return BlockDevice;
@@ -30,41 +28,40 @@ export function resolveMode(statmode) {
30
28
  case fsc.S_IFLNK: return SymbolicLink;
31
29
  case fsc.S_IFIFO: return Fifo;
32
30
  case fsc.S_IFSOCK: return Socket;
33
- default: throw new RdErr(`Unknown mode type ${statmode} (statmode is most likely corrupted)`);
31
+ default: throw new RdErr(`Unknown mode type ${statmode_} (statmode is most likely corrupted)`);
34
32
  }
35
33
  }
36
34
  /**
37
- * Creates a new instance of the appropriate subclass of {@link Road} based on the file mode of the specified path.
35
+ * Creates the appropriate subclass of {@link Road} based on the file mode of the specified path.
38
36
  *
39
- * @param lookFor The path to check.
40
- * @returns A new instance of the appropriate subclass of {@link Road}.
41
- * @throws If the path does not exist, throws a fs {@link Error}.
37
+ * @param path_ The path to follow.
38
+ * @returns A new instance of {@link Road}.
39
+ * @throws If {@link fp.lstat}/{@link fs.lstatSync} fails to retrieved the status of {@link path_}.
42
40
  */
43
- export function factorySync(lookFor) {
44
- fs.accessSync(lookFor, fsc.F_OK);
45
- return new (resolveMode(fs.lstatSync(lookFor).mode))(lookFor, false);
41
+ export async function factory(path_) {
42
+ return new (resolveMode((await fp.lstat(path_)).mode))(path_, false);
46
43
  }
47
- /** Async version of {@link factorySync}. */
48
- export async function factory(lookFor) {
49
- await fp.access(lookFor, fsc.F_OK);
50
- return new (resolveMode((await fp.lstat(lookFor)).mode))(lookFor, false);
44
+ /** Sync version of {@link factory}. */
45
+ export function factorySync(path_) {
46
+ return new (resolveMode(fs.lstatSync(path_).mode))(path_, false);
51
47
  }
52
48
  /**
53
- * A map that keeps track of locked roads to prevent concurrent modifications. The keys are the absolute paths of the roads, and the values are promises that resolve when the lock is released.
49
+ * A map that keeps track of locked roads to prevent concurrent modifications.
50
+ *
51
+ * @key The absolute path of the road that is currently locked.
52
+ * @value A promise that resolves when the lock on the road is released.
54
53
  *
55
- * @remarks Don't manually modify this map. Use the {@link Road.initChange} and {@link Road.initChangeSync} methods to acquire and release locks on roads.
56
- * For read-only purposes, you should use the {@link lockFor} function to await the optional lock on a road.
54
+ * @remarks The map is initialized lazily when the first lock is created to minimize import-time side effects.
55
+ * It's generally meant for read-only purposes. It's not advised to modify this map directly if there are built-in mechanisms that do the job for you as well.
57
56
  */
58
- let lockedRoads = null;
57
+ export let lockedRoads = null;
59
58
  /**
60
- * Getter for the locked roads map.
59
+ * Road is an OOP, pointer-like representation of an entry in the local file system. It wraps around the Node.js fs module and provides a more convenient access to it.
60
+ * It's meant to help the user mentally model an entry and help them reason about it, as well as provide a more convenient API and guardrails for common operations.
61
61
  *
62
- * @param roadOrPath - A {@link Road} instance or a string representing the absolute path of the road to check.
63
- * @returns The promise associated with the locked road, or `undefined` if the road is not currently locked.
62
+ * @remarks This is by no means a one-to-one mapping of the underlying file system (after initialization).
63
+ * It is more like a memory representation, similar to a pointer in low-level programming languages; other processes might mess with the underlying entry. There are methods to check for consistency, but they are not guaranteed to be foolproof.
64
64
  */
65
- export function lockFor(roadOrPath) {
66
- return lockedRoads?.get(roadOrPath.toString());
67
- }
68
65
  export class Road {
69
66
  /** The absolute path to the file or directory that this Road instance represents.
70
67
  * @remarks Intentionally made protected to prevent external modification, as changing this value could lead to inconsistencies and unexpected behavior. */
@@ -73,138 +70,85 @@ export class Road {
73
70
  * Changing this value does not affect the actual file system permissions, but rather serves as a safeguard within the application to prevent accidental modifications. */
74
71
  mutable = true;
75
72
  // Quick accessors
76
- /** Accessor for the absolute path to the file or directory that this Road instance represents. */
73
+ /** Copy of the absolute path. */
77
74
  get isAt() { return this.pointsTo; }
78
- /** Accessor for the name of the file or directory that this Road instance represents. */
75
+ /** Name of the road without the path (including extensions). */
79
76
  get name() { return this.isAt.slice(this.isAt.lastIndexOf(ph.sep) + 1); }
80
- /** Same as {@link isAt} but for compatibility with external libraries that try to convert the object to a string. */
77
+ /** The amount of path segments in the absolute path to the file or directory represented by this Road instance, minus one (i.e., the depth of the path in the file system hierarchy). */
78
+ get depth() { return this.isAt.split(ph.sep).length - 1; }
79
+ /** Same as {@link isAt} but for compatibility with external APIs. */
81
80
  toString() { return this.isAt; }
82
- /** Returns the OS file type of the file or directory.
83
- * Return value (OS type) and the type of this instance are not guaranteed to be the same, as the file system may have changed since this instance was created. */
84
- typeSync() { return (resolveMode(fs.lstatSync(this.isAt).mode)); }
85
- /** Async version of {@link typeSync}. */
86
- async type() { return (resolveMode((await fp.lstat(this.isAt)).mode)); }
87
81
  /**
88
- * Constructs a new Road instance representing the file or directory at the specified path.
82
+ * Creates a new instance of the Road class.
89
83
  *
90
- * @param lookFor The path to the file or directory that this Road instance will represent.
91
- * @param typeCheck Whether to check if the type of the file or directory at the specified path matches the type of this instance.
92
- * This can be skipped for performance reasons if the type is known to be correct, but it is recommended to keep it enabled for safety.
93
- * @throws If the specified path does not exist, throws a fs.{@link Error}.
94
- * @throws If the type of the file or directory at the specified path does not match the type of this instance, throws a {@link RdErr}. Useful for subclasses.
95
- */
96
- constructor(lookFor, typeCheck) {
97
- this.pointsTo = ph.resolve(lookFor);
98
- if (typeCheck && !(this instanceof this.typeSync())) // `this` directly refers to the subclass
99
- throw new RdErr(`Type missmatch: Path '${this.isAt}' is not of constructed type ${this.constructor.name}.`);
100
- }
101
- /**
102
- * Verifies that the file or directory represented by this Road instance exists, is of the same type as this instance, and (optionally) is writable.
84
+ * @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
+ * @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.
103
86
  *
104
- * @param expectMode The expected access mode for this Road other than visibility by this process.
105
- * @returns Result of the verification.
87
+ * @throws If {@link typeCheck_} is true and the path does not correspond to the expected type of road, a {@link RdErr} will be thrown.
106
88
  */
107
- async verify(expectMode, typeCheck) {
108
- try {
109
- await fp.access(this.isAt, fsc.F_OK | expectMode);
110
- return typeCheck || this instanceof (await this.type());
111
- }
112
- catch {
113
- return false;
114
- }
115
- }
116
- /** Sync version of {@link verify}. */
117
- verifySync(expectMode, typeCheck) {
118
- try {
119
- fs.accessSync(this.isAt, fsc.F_OK | expectMode);
120
- return typeCheck || this instanceof this.typeSync();
121
- }
122
- catch {
123
- return false;
124
- }
89
+ constructor(path_, typeCheck_) {
90
+ this.pointsTo = ph.resolve(path_);
91
+ if (typeCheck_ && !this.checkSync())
92
+ throw new RdErr(`Type mismatch: '${this.isAt}'`);
125
93
  }
126
94
  /**
127
- * Creates a disposable lock for the file or directory represented by this Road instance, preventing concurrent modifications.
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.
128
100
  *
129
- * @returns An object with a `dispose` method that releases the lock when called. The lock is automatically released when the object is garbage collected.
130
- * @remarks Please use this method with the `await using` statement to ensure that the lock is properly released after the operation is complete. This method is intended for internal use and shouldn't be called directly in most cases. (that's why it's protected)
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).
131
103
  */
132
- async initChange() {
133
- if (!await this.verify(fsc.R_OK | fsc.W_OK, true))
134
- throw new RdErr(`Road to '${this.isAt}' (${this.constructor.name}) isn't the same as during construction, can't modify (OS type: ${fs.existsSync(this.isAt) ? this.typeSync().name : 'nonexistent'})`);
104
+ async lock(cb_ = () => { }) {
135
105
  if (!this.mutable)
136
- throw new RdErr(`Attempting to modify road to '${this.isAt}' of type ${this.constructor.name} which's marked as immutable (unrelated to the actual OS file permissions)`);
137
- if (!lockedRoads)
138
- lockedRoads = new Map();
139
- const lockedPath = this.isAt;
140
- let releaseLock = () => { };
141
- await lockFor(lockedPath);
142
- lockedRoads.set(lockedPath, new Promise(res => releaseLock = res));
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));
143
111
  return {
144
112
  [Symbol.dispose]() {
145
- releaseLock();
146
- releaseLock = () => { throw new RdErr("Lock already released, can't dispose"); };
147
- lockedRoads.delete(lockedPath);
113
+ cb_();
114
+ lockedRoads.delete(isAt);
148
115
  },
149
116
  async [Symbol.asyncDispose]() {
150
- releaseLock();
151
- releaseLock = () => { throw new RdErr("Lock already released, can't dispose"); };
152
- lockedRoads.delete(lockedPath);
117
+ cb_();
118
+ lockedRoads.delete(isAt);
153
119
  }
154
120
  };
155
121
  }
156
122
  /**
157
- * Sync version of {@link initChangeSync}.
158
- *
159
- * @remarks This sync version will throw if the road is already locked by another operation.
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.
160
125
  */
161
- initChangeSync() {
162
- if (!this.verifySync(fsc.R_OK | fsc.W_OK, true))
163
- throw new RdErr(`Road to '${this.isAt}' (${this.constructor.name}) isn't the same as during construction, can't modify (OS type: ${fs.existsSync(this.isAt) ? this.typeSync().name : 'nonexistent'})`);
126
+ lockSync(cb_ = () => { }) {
164
127
  if (!this.mutable)
165
- throw new RdErr(`Attempting to modify road to '${this.isAt}' of type ${this.constructor.name} which's marked as immutable (unrelated to the actual OS file permissions)`);
166
- if (!lockedRoads)
167
- lockedRoads = new Map();
168
- const lockedPath = this.isAt;
169
- if (lockedRoads.has(lockedPath))
170
- throw new RdErr(`Road to '${this.isAt}' is currently locked by another operation, can't modify synchronously`);
171
- let releaseLock = () => { };
172
- lockedRoads.set(lockedPath, new Promise(res => releaseLock = res));
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));
173
134
  return {
174
135
  [Symbol.dispose]() {
175
- lockedRoads.delete(lockedPath);
176
- releaseLock();
177
- releaseLock = () => { throw new RdErr("Lock already released, can't dispose"); };
136
+ lockedRoads.delete(isAt);
137
+ cb_();
178
138
  },
179
139
  async [Symbol.asyncDispose]() {
180
- lockedRoads.delete(lockedPath);
181
- releaseLock();
182
- releaseLock = () => { throw new RdErr("Lock already released, can't dispose"); };
140
+ lockedRoads.delete(isAt);
141
+ cb_();
183
142
  }
184
143
  };
185
144
  }
145
+ /** @returns An instance of {@link Folder} representing the parent directory of the current road. */
146
+ parent() { return new Folder(ph.dirname(this.isAt), false); }
186
147
  /**
187
- * Checks if the file or directory represented by this Road is both visible and of the same type as expected.
188
- */
189
- async exists() { return this.verify(fsc.F_OK, true); }
190
- existsSync() { return this.verifySync(fsc.F_OK, true); }
191
- /**
192
- * @returns The file system stats for the file or directory.
193
- * @see {@link fs.lstat}
194
- */
195
- async stats() { return fp.lstat(this.isAt); }
196
- /**
197
- * @returns The file system stats for the file or directory.
198
- * @see {@link fs.lstatSync}
199
- */
200
- statsSync() { return fs.lstatSync(this.isAt); }
201
- /**
202
- * @returns The amount of path segments in the absolute path to the file or directory represented by this Road instance, minus one (i.e., the depth of the file or directory in the file system hierarchy).
203
- * @remarks As subclasses of {@link Road} require all paths to be absolute/normalized and valid, this method is guaranteed to return a non-negative integer.
148
+ * An async generator that yields the ancestors of the current road, starting from its parent and moving up the directory tree until it reaches the root.
149
+ *
150
+ * @yields Each ancestor folder as a {@link Folder} instance.
204
151
  */
205
- depth() { return this.isAt.split(ph.sep).length - 1; }
206
- /** @returns The parent folder of the file or directory represented by this Road instance. */
207
- parent() { return new Folder(ph.dirname(this.isAt), false); }
208
152
  *ancestorsIt() {
209
153
  let current = this.parent();
210
154
  let parent = current.parent();
@@ -214,28 +158,50 @@ export class Road {
214
158
  parent = current.parent();
215
159
  }
216
160
  }
161
+ /** @returns An array of {@link Folder} instances representing the ancestors of the current road. */
217
162
  ancestors() { return [...this.ancestorsIt()]; }
218
- async untilAccessible(mode = fsc.F_OK, abs, onEachAttempt) {
163
+ /**
164
+ * Watches the current entry for changes and resolves when the entry becomes accessible (i.e., exists and can be accessed).
165
+ *
166
+ * @param abs An {@link AbortSignal} that stops the watching process when aborted.
167
+ * @param expectMode The expected access mode for the entry, defaults to {@link fsc.F_OK} (existence check).
168
+ * @param cb_ An optional callback function that will be called with any errors encountered while checking for accessibility.
169
+ * @see {@link fp.access} on how the check for accessibility is performed.
170
+ * @see {@link fs.watch} for more information on how the watching process works.
171
+ * @see {@link on} for more information on how the event listener is set up.
172
+ */
173
+ async untilAccessible(abs, expectMode = fsc.F_OK, cb_) {
219
174
  const watcher = fs.watch(this.isAt);
220
175
  try {
221
- if (await this.verify(mode, true))
222
- return;
223
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
224
- for await (let _ of on(watcher, 'change', { signal: abs }))
225
- if (await this.verify(mode, true))
176
+ for await (let _ of on(watcher, 'change', { signal: abs })) {
177
+ try {
178
+ await fp.access(this.isAt, expectMode);
226
179
  return;
227
- else
228
- await onEachAttempt?.();
180
+ }
181
+ catch (err) {
182
+ await cb_?.(err);
183
+ }
184
+ }
185
+ }
186
+ catch (e) {
187
+ throw e;
229
188
  }
230
189
  finally {
231
190
  watcher.close();
232
191
  }
233
192
  }
234
- async onChange(abs, cb) {
193
+ /**
194
+ * Watches the current entry for changes and resolves when the entry is changed.
195
+ *
196
+ * @param abs_ An {@link AbortSignal} that stops the watching process when aborted.
197
+ * @param cb_ An optional callback function that will be called when the entry is changed.
198
+ * @returns The return value of the callback function, or null if no callback is provided.
199
+ */
200
+ async onChange(abs_, cb_) {
235
201
  const watcher = fs.watch(this.isAt);
236
202
  try {
237
- for await (let _ of on(watcher, 'change', { signal: abs }))
238
- return await cb?.() || null;
203
+ for await (let _ of on(watcher, 'change', { signal: abs_ }))
204
+ return await cb_?.() ?? null;
239
205
  return null;
240
206
  }
241
207
  catch (e) {
@@ -245,285 +211,295 @@ export class Road {
245
211
  watcher.close();
246
212
  }
247
213
  }
248
- metaSync(suffixID = "tsInstrumentalityMeta") {
249
- if (os.platform() === "win32")
250
- return fs.readFileSync(`${this.isAt}:${suffixID}`);
251
- else
252
- throw new RdErr("Extended attributes are not supported on this platform");
253
- }
254
- async meta(suffixID = "tsInstrumentalityMeta") {
255
- if (os.platform() === "win32")
256
- return JSON.parse(await fp.readFile(`${this.isAt}:${suffixID}`, 'utf-8'));
257
- else
258
- throw new RdErr("Extended attributes are not supported on this platform");
259
- }
260
- setMetaSync(meta, suffixID = "tsInstrumentalityMeta") {
261
- using _ = this.initChangeSync();
262
- if (os.platform() === "win32")
263
- fs.writeFileSync(`${this.isAt}:${suffixID}`, JSON.stringify(meta), 'utf-8');
264
- else
265
- throw new RdErr("Extended attributes are not supported on this platform");
266
- }
267
- async setMeta(meta, suffixID = "tsInstrumentalityMeta") {
268
- using _ = await this.initChange();
269
- if (os.platform() === "win32")
270
- await fp.writeFile(`${this.isAt}:${suffixID}`, JSON.stringify(meta), 'utf-8');
271
- else
272
- throw new RdErr("Extended attributes are not supported on this platform");
273
- }
214
+ /** @returns The result of {@link fp.lstat} for the current entry. */
215
+ lstat() { return fp.lstat(this.isAt); }
216
+ /** @returns The result of {@link fs.lstatSync} for the current entry. */
217
+ lstatSync() { return fs.lstatSync(this.isAt); }
218
+ /** @returns The result of {@link fp.stat} for the current entry. */
219
+ stat() { return fp.stat(this.isAt); }
220
+ /** @returns The result of {@link fs.statSync} for the current entry. */
221
+ statSync() { return fs.statSync(this.isAt); }
222
+ /** Type narrowing for {@link File} (similar to `instanceof` without unnecessary runtime checks). */
223
+ isFile() { return false; }
224
+ /** Type narrowing for {@link Folder} (similar to `instanceof` without unnecessary runtime checks). */
225
+ isDir() { return false; }
226
+ /** Type narrowing for {@link Folder} (similar to `instanceof` without unnecessary runtime checks). */
227
+ isFolder() { return false; }
228
+ /** Type narrowing for {@link Folder} (similar to `instanceof` without unnecessary runtime checks). */
229
+ 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
+ /** Type narrowing for {@link SymbolicLink} (similar to `instanceof` without unnecessary runtime checks). */
235
+ isSymlink() { return false; }
236
+ /** Type narrowing for {@link SymbolicLink} (similar to `instanceof` without unnecessary runtime checks). */
237
+ isSymbolicLink() { return false; }
238
+ /** Type narrowing for {@link UnusableRoad} (similar to `instanceof` without unnecessary runtime checks). */
239
+ isUnusable() { return false; }
240
+ /** Type narrowing for {@link BlockDevice} (similar to `instanceof` without unnecessary runtime checks). */
241
+ isBlockDevice() { return false; }
242
+ /** Type narrowing for {@link CharacterDevice} (similar to `instanceof` without unnecessary runtime checks). */
243
+ isCharacterDevice() { return false; }
244
+ /** Type narrowing for {@link Fifo} (similar to `instanceof` without unnecessary runtime checks). */
245
+ isFifo() { return false; }
246
+ /** Type narrowing for {@link Socket} (similar to `instanceof` without unnecessary runtime checks). */
247
+ isSocket() { return false; }
274
248
  }
249
+ /** Subclass of {@link Road} that represents a file. */
275
250
  export class File extends Road {
276
- get ext() { return ph.extname(this.isAt); }
277
- get noExt() { return ph.basename(this.isAt, this.ext); }
278
- static createSync(at) {
251
+ static async create(at_) {
279
252
  try {
280
- fs.accessSync(at, fsc.W_OK);
253
+ await fp.access(at_, fsc.W_OK);
281
254
  }
282
255
  catch {
283
- fs.writeFileSync(at, "");
256
+ await fp.writeFile(at_, "");
284
257
  }
285
- return new File(at, false);
258
+ return new File(at_, true);
286
259
  }
287
- static async create(at) {
260
+ static createSync(at_) {
288
261
  try {
289
- await fp.access(at, fsc.W_OK);
262
+ fs.accessSync(at_, fs.constants.W_OK);
290
263
  }
291
264
  catch {
292
- await fp.writeFile(at, "");
265
+ fs.writeFileSync(at_, "");
293
266
  }
294
- return new File(at, false);
267
+ return new File(at_, true);
295
268
  }
296
- readSync(encoding, flag) {
297
- if (encoding)
298
- return fs.readFileSync(this.isAt, { encoding: encoding, flag: flag });
269
+ get ext() { return ph.extname(this.isAt); }
270
+ get noExt() { return ph.basename(this.isAt, this.ext); }
271
+ async read(encoding_, flag_) {
272
+ if (encoding_)
273
+ return fp.readFile(this.isAt, { encoding: encoding_, flag: flag_ });
299
274
  else
300
- return fs.readFileSync(this.isAt);
275
+ return fp.readFile(this.isAt);
301
276
  }
302
- async read(encoding, flag) {
303
- if (encoding)
304
- return fp.readFile(this.isAt, { encoding: encoding, flag: flag });
277
+ readSync(encoding_, flag_) {
278
+ if (encoding_)
279
+ return fs.readFileSync(this.isAt, { encoding: encoding_, flag: flag_ });
305
280
  else
306
- return fp.readFile(this.isAt);
281
+ return fs.readFileSync(this.isAt);
307
282
  }
308
- // Bizarre reading
309
- *itBuffSync(chunkSize = 64 * 1024, flags = 'r', mode) {
310
- const fd = fs.openSync(this.isAt, flags, mode);
283
+ async *itBuff(chunkSize_ = 64 * 1024, flags_ = 'r', mode_) {
284
+ const fd = await fp.open(this.isAt, flags_, mode_);
311
285
  try {
312
- const buffer = Buffer.alloc(chunkSize);
286
+ const buffer = Buffer.alloc(chunkSize_);
313
287
  let bytesRead;
314
288
  do {
315
- bytesRead = fs.readSync(fd, buffer, 0, chunkSize, null);
289
+ const readResult = await fd.read(buffer, 0, chunkSize_, null);
290
+ bytesRead = readResult.bytesRead;
316
291
  if (bytesRead > 0)
317
292
  yield buffer.subarray(0, bytesRead);
318
- } while (bytesRead === chunkSize);
293
+ } while (bytesRead === chunkSize_);
319
294
  }
320
295
  finally {
321
- fs.closeSync(fd);
296
+ await fd.close();
322
297
  }
323
298
  }
324
- async *itBuff(chunkSize = 64 * 1024, flags = 'r', mode) {
325
- const fd = await fp.open(this.isAt, flags, mode);
299
+ *itBuffSync(chunkSize_ = 64 * 1024, flags_ = 'r', mode_) {
300
+ const fd = fs.openSync(this.isAt, flags_, mode_);
326
301
  try {
327
- const buffer = Buffer.alloc(chunkSize);
302
+ const buffer = Buffer.alloc(chunkSize_);
328
303
  let bytesRead;
329
304
  do {
330
- const readResult = await fd.read(buffer, 0, chunkSize, null);
331
- bytesRead = readResult.bytesRead;
305
+ bytesRead = fs.readSync(fd, buffer, 0, chunkSize_, null);
332
306
  if (bytesRead > 0)
333
307
  yield buffer.subarray(0, bytesRead);
334
- } while (bytesRead === chunkSize);
335
- }
336
- finally {
337
- await fd.close();
338
- }
339
- }
340
- async *itLines(options = { encoding: 'utf-8' }) {
341
- const readStream = fs.createReadStream(this.isAt, options);
342
- const rlInterface = rl.createInterface({ input: readStream, crlfDelay: Infinity });
343
- try {
344
- for await (const line of rlInterface)
345
- yield line;
308
+ } while (bytesRead === chunkSize_);
346
309
  }
347
310
  finally {
348
- rlInterface.close();
349
- readStream.destroy();
311
+ fs.closeSync(fd);
350
312
  }
351
313
  }
352
- computeHashSync(algorithm = "sha256", options, encoding) {
353
- const hash = cr.createHash(algorithm, options);
354
- for (const chunk of this.itBuffSync())
355
- hash.update(chunk);
356
- return encoding ? hash.digest(encoding) : hash.digest();
357
- }
358
- async computeHash(algorithm = "sha256", options, encoding) {
359
- const hash = cr.createHash(algorithm, options);
360
- for await (const chunk of this.itBuff())
361
- hash.update(chunk);
362
- return encoding ? hash.digest(encoding) : hash.digest();
363
- }
364
- async streamHash(algorithm = "sha256", options, encoding) {
365
- const hash = cr.createHash(algorithm, options);
366
- await sp.pipeline(fs.createReadStream(this.isAt), hash);
367
- return encoding ? hash.digest(encoding) : hash.digest();
368
- }
369
- writeSync(data, options) {
370
- using _ = this.initChangeSync();
371
- fs.writeFileSync(this.isAt, data, options);
314
+ async write(data_, options_) {
315
+ using _ = await this.lock();
316
+ await fp.writeFile(this.isAt, data_, options_);
372
317
  }
373
- async write(data, options) {
374
- using _ = await this.initChange();
375
- await fp.writeFile(this.isAt, data, options);
318
+ writeSync(data_, options_) {
319
+ using _ = this.lockSync();
320
+ fs.writeFileSync(this.isAt, data_, options_);
376
321
  }
377
- appendSync(data, options) {
378
- using _ = this.initChangeSync();
379
- fs.appendFileSync(this.isAt, data, options);
322
+ async append(data_, options_) {
323
+ using _ = await this.lock();
324
+ await fp.appendFile(this.isAt, data_, options_);
380
325
  }
381
- async append(data, options) {
382
- using _ = await this.initChange();
383
- await fp.appendFile(this.isAt, data, options);
326
+ appendSync(data_, options_) {
327
+ using _ = this.lockSync();
328
+ fs.appendFileSync(this.isAt, data_, options_);
384
329
  }
385
- async sameAs(other) {
386
- if (this.isAt === other.isAt)
387
- return true;
388
- else if ((await fp.lstat(this.isAt)).size !== (await fp.lstat(other.isAt)).size)
389
- return false;
390
- const thisIter = this.itBuff();
391
- const otherIter = other.itBuff();
392
- while (true) {
393
- const [a, b] = await Promise.all([thisIter.next(), otherIter.next()]);
394
- if (a.done && b.done)
395
- return true;
396
- if (a.done !== b.done)
397
- return false;
398
- if (!a.value.equals(b.value))
399
- return false;
400
- }
401
- }
402
- sameAsSync(other) {
403
- if (this.isAt === other.isAt)
404
- return true;
405
- else if (fs.statSync(this.isAt).size !== fs.statSync(other.isAt).size)
406
- return false;
407
- const thisIter = this.itBuffSync();
408
- const otherIter = other.itBuffSync();
409
- while (true) {
410
- const a = thisIter.next();
411
- const b = otherIter.next();
412
- if (a.done && b.done)
413
- return true;
414
- if (a.done !== b.done)
415
- return false;
416
- if (!a.value.equals(b.value))
417
- return false;
418
- }
330
+ async delete() {
331
+ using _ = await this.lock();
332
+ await fp.rm(this.isAt, { force: true });
419
333
  }
420
334
  deleteSync() {
421
- using _ = this.initChangeSync();
335
+ using _ = this.lockSync();
422
336
  fs.rmSync(this.isAt, { force: true });
423
337
  }
424
- async delete() {
425
- using _ = await this.initChange();
426
- await fp.rm(this.isAt, { force: true });
338
+ async move(into_) {
339
+ using _ = await this.lock();
340
+ const newPath = into_.join(this.name);
341
+ await fp.rename(this.isAt, newPath);
342
+ this.pointsTo = newPath;
427
343
  }
428
- moveSync(into) {
429
- using _ = this.initChangeSync();
430
- const newPath = into.join(this.name);
344
+ moveSync(into_) {
345
+ using _ = this.lockSync();
346
+ const newPath = into_.join(this.name);
431
347
  fs.renameSync(this.isAt, newPath);
432
348
  this.pointsTo = newPath;
433
349
  }
434
- async move(into) {
435
- using _ = await this.initChange();
436
- const newPath = into.join(this.name);
437
- await fp.rename(this.isAt, newPath);
438
- this.pointsTo = newPath;
350
+ async copy(into_) {
351
+ const newPath = into_.join(this.name);
352
+ await fp.copyFile(this.isAt, newPath);
353
+ return new File(newPath, false);
439
354
  }
440
- copySync(into) {
441
- const newPath = into.join(this.name);
355
+ copySync(into_) {
356
+ const newPath = into_.join(this.name);
442
357
  fs.copyFileSync(this.isAt, newPath);
443
358
  return new File(newPath, false);
444
359
  }
445
- async copy(into) {
446
- const newPath = into.join(this.name);
447
- await fp.copyFile(this.isAt, newPath);
448
- return new File(newPath, false);
360
+ async rename(to_) {
361
+ using _ = await this.lock();
362
+ const newPath = this.parent().join(to_);
363
+ await fp.rename(this.isAt, newPath);
364
+ this.pointsTo = newPath;
449
365
  }
450
- renameSync(to) {
451
- using _ = this.initChangeSync();
452
- const newPath = this.parent().join(to);
366
+ renameSync(to_) {
367
+ using _ = this.lockSync();
368
+ const newPath = this.parent().join(to_);
453
369
  fs.renameSync(this.isAt, newPath);
454
370
  this.pointsTo = newPath;
455
371
  }
456
- async rename(to) {
457
- using _ = await this.initChange();
458
- const newPath = this.parent().join(to);
459
- await fp.rename(this.isAt, newPath);
460
- this.pointsTo = newPath;
372
+ async check() { return (await fp.lstat(this.isAt)).isFile(); }
373
+ checkSync() { return fs.lstatSync(this.isAt).isFile(); }
374
+ isFile() { return true; }
375
+ }
376
+ export async function computeHash(f_, algorithm_ = "sha256", options_, encoding_) {
377
+ const hash = cr.createHash(algorithm_, options_);
378
+ for await (const chunk of f_.itBuff())
379
+ hash.update(chunk);
380
+ return encoding_ ? hash.digest(encoding_) : hash.digest();
381
+ }
382
+ export function computeHashSync(f_, algorithm_ = "sha256", options_, encoding_) {
383
+ const hash = cr.createHash(algorithm_, options_);
384
+ for (const chunk of f_.itBuffSync())
385
+ hash.update(chunk);
386
+ return encoding_ ? hash.digest(encoding_) : hash.digest();
387
+ }
388
+ export async function streamHash(f_, algorithm_ = "sha256", options_, encoding_) {
389
+ const hash = cr.createHash(algorithm_, options_);
390
+ for await (const chunk of f_.itBuff())
391
+ hash.update(chunk);
392
+ return encoding_ ? hash.digest(encoding_) : hash.digest();
393
+ }
394
+ export function streamHashSync(f_, algorithm_ = "sha256", options_, encoding_) {
395
+ const hash = cr.createHash(algorithm_, options_);
396
+ for (const chunk of f_.itBuffSync())
397
+ hash.update(chunk);
398
+ return encoding_ ? hash.digest(encoding_) : hash.digest();
399
+ }
400
+ export async function* itLines(f_, options_ = { encoding: 'utf-8' }) {
401
+ const readStream = fs.createReadStream(f_.isAt, options_);
402
+ const rlInterface = rl.createInterface({ input: readStream, crlfDelay: Infinity });
403
+ try {
404
+ for await (const line of rlInterface)
405
+ yield line;
461
406
  }
462
- resurrectSync() {
463
- using _ = this.initChangeSync();
464
- fs.writeFileSync(this.isAt, "");
407
+ finally {
408
+ rlInterface.close();
409
+ readStream.destroy();
465
410
  }
466
- async resurrect() {
467
- using _ = await this.initChange();
468
- await fp.writeFile(this.isAt, "");
411
+ }
412
+ export async function fileSameAs(f1_, f2_) {
413
+ if (f1_.isAt === f2_.isAt)
414
+ return true;
415
+ else if ((await fp.lstat(f1_.isAt)).size !== (await fp.lstat(f2_.isAt)).size)
416
+ return false;
417
+ const iter1 = f1_.itBuff();
418
+ const iter2 = f2_.itBuff();
419
+ while (true) {
420
+ const [a, b] = await Promise.all([iter1.next(), iter2.next()]);
421
+ if (a.done && b.done)
422
+ return true;
423
+ if (a.done !== b.done)
424
+ return false;
425
+ if (!a.value.equals(b.value))
426
+ return false;
427
+ }
428
+ }
429
+ export function fileSameAsSync(f1_, f2_) {
430
+ if (f1_.isAt === f2_.isAt)
431
+ return true;
432
+ else if (fs.statSync(f1_.isAt).size !== fs.statSync(f2_.isAt).size)
433
+ return false;
434
+ const iter1 = f1_.itBuffSync();
435
+ const iter2 = f2_.itBuffSync();
436
+ while (true) {
437
+ const a = iter1.next();
438
+ const b = iter2.next();
439
+ if (a.done && b.done)
440
+ return true;
441
+ if (a.done !== b.done)
442
+ return false;
443
+ if (!a.value.equals(b.value))
444
+ return false;
469
445
  }
470
446
  }
471
- export function entry() { return new File(process.argv[1], false); }
472
447
  export class Folder extends Road {
473
- static async create(at) {
448
+ static async create(at_) {
474
449
  try {
475
- await fp.access(at, fs.constants.F_OK);
450
+ await fp.access(at_, fsc.W_OK);
476
451
  }
477
452
  catch {
478
- await fp.mkdir(at, { recursive: true });
453
+ await fp.mkdir(at_, { recursive: true });
479
454
  }
480
- return new Folder(at, false);
455
+ return new Folder(at_, false);
481
456
  }
482
- static createSync(at) {
457
+ static createSync(at_) {
483
458
  try {
484
- fs.accessSync(at, fs.constants.F_OK);
459
+ fs.accessSync(at_, fsc.W_OK);
485
460
  }
486
461
  catch {
487
- fs.mkdirSync(at, { recursive: true });
462
+ fs.mkdirSync(at_, { recursive: true });
488
463
  }
489
- return new Folder(at, false);
464
+ return new Folder(at_, false);
490
465
  }
491
- join(...paths) {
492
- return ph.join(this.isAt, ...paths);
466
+ join(...paths_) {
467
+ return ph.join(this.isAt, ...paths_);
493
468
  }
494
- *itSync(expectedType) {
495
- for (const entry of fs.readdirSync(this.isAt)) {
496
- const road = factorySync(this.join(entry));
497
- if (!expectedType || road instanceof expectedType)
469
+ async *it(expectedType_) {
470
+ for (const entryName of await fp.readdir(this.isAt)) {
471
+ const road = await factory(this.join(entryName));
472
+ if (!expectedType_ || road instanceof expectedType_)
498
473
  yield road;
499
474
  }
500
475
  }
501
- async *it(expectedType) {
502
- for (const entry of await fp.readdir(this.isAt)) {
503
- const road = await factory(this.join(entry));
504
- if (!expectedType || road instanceof expectedType)
476
+ *itSync(expectedType_) {
477
+ for (const entry of fs.readdirSync(this.isAt)) {
478
+ const road = factorySync(this.join(entry));
479
+ if (!expectedType_ || road instanceof expectedType_)
505
480
  yield road;
506
481
  }
507
482
  }
508
- listSync(expectedType) {
509
- const entries = fs.readdirSync(this.isAt).map(entry => factorySync(this.join(entry)));
510
- if (!expectedType)
511
- return entries;
512
- return entries.filter(entry => entry instanceof expectedType);
513
- }
514
- async list(_expectedType) {
483
+ async list(expectedType_) {
515
484
  const entries = (await fp.readdir(this.isAt)).map(async (entry) => factory(this.join(entry)));
516
485
  const resolvedEntries = await Promise.all(entries);
517
- if (!_expectedType)
486
+ if (!expectedType_)
518
487
  return resolvedEntries;
519
- return resolvedEntries.filter(entry => entry instanceof _expectedType);
488
+ return resolvedEntries.filter(entry => entry instanceof expectedType_);
520
489
  }
521
- findSync(name, _expectedType) {
490
+ listSync(expectedType_) {
491
+ const entries = fs.readdirSync(this.isAt).map(entry => factorySync(this.join(entry)));
492
+ if (!expectedType_)
493
+ return entries;
494
+ return entries.filter(entry => entry instanceof expectedType_);
495
+ }
496
+ async find(name_, expectedType_) {
522
497
  try {
523
- const found = factorySync(this.join(name));
524
- if (!_expectedType)
498
+ await fp.access(this.join(name_), fs.constants.F_OK);
499
+ const found = await factory(this.join(name_));
500
+ if (!expectedType_)
525
501
  return found;
526
- if (found instanceof _expectedType)
502
+ if (found instanceof expectedType_)
527
503
  return found;
528
504
  return null;
529
505
  }
@@ -531,13 +507,12 @@ export class Folder extends Road {
531
507
  return null;
532
508
  }
533
509
  }
534
- async find(name, _expectedType) {
510
+ findSync(name_, expectedType_) {
535
511
  try {
536
- await fp.access(this.join(name), fs.constants.F_OK);
537
- const found = await factory(this.join(name));
538
- if (!_expectedType)
512
+ const found = factorySync(this.join(name_));
513
+ if (!expectedType_)
539
514
  return found;
540
- if (found instanceof _expectedType)
515
+ if (found instanceof expectedType_)
541
516
  return found;
542
517
  return null;
543
518
  }
@@ -545,66 +520,65 @@ export class Folder extends Road {
545
520
  return null;
546
521
  }
547
522
  }
548
- addSync(name, createable) {
549
- const newPath = this.join(name);
550
- createable.createSync(newPath);
551
- return factorySync(newPath);
552
- }
553
- async add(name, createable) {
554
- const newPath = this.join(name);
555
- await createable.create(newPath);
556
- return factory(newPath);
523
+ async add(name_, createable_) {
524
+ const newPath = this.join(name_);
525
+ await createable_.create(newPath);
526
+ return (await factory(newPath));
557
527
  }
558
- deleteSync(options = { recursive: true }) {
559
- using _ = this.initChangeSync();
560
- fs.rmSync(this.isAt, options);
528
+ addSync(name_, createable_) {
529
+ const newPath = this.join(name_);
530
+ createable_.createSync(newPath);
531
+ return factorySync(newPath);
561
532
  }
562
- async delete(options = { recursive: true }) {
563
- using _ = await this.initChange();
564
- await fp.rm(this.isAt, options);
533
+ async delete(options_ = { recursive: true }) {
534
+ using _ = await this.lock();
535
+ await fp.rm(this.isAt, options_);
565
536
  }
566
- moveSync(into) {
567
- using _ = this.initChangeSync();
568
- const newPath = into.join(this.name);
569
- fs.renameSync(this.isAt, newPath);
570
- this.pointsTo = newPath;
537
+ deleteSync(options_ = { recursive: true }) {
538
+ using _ = this.lockSync();
539
+ fs.rmSync(this.isAt, options_);
571
540
  }
572
- async move(into) {
573
- using _ = await this.initChange();
574
- const newPath = into.join(this.name);
541
+ async move(into_) {
542
+ using _ = await this.lock();
543
+ const newPath = into_.join(this.name);
575
544
  await fp.rename(this.isAt, newPath);
576
545
  this.pointsTo = newPath;
577
546
  }
578
- copySync(into) {
579
- const newPath = into.join(this.name);
580
- fs.cpSync(this.isAt, newPath, { recursive: true });
581
- return new Folder(newPath, false);
547
+ moveSync(into_) {
548
+ using _ = this.lockSync();
549
+ const newPath = into_.join(this.name);
550
+ fs.renameSync(this.isAt, newPath);
551
+ this.pointsTo = newPath;
582
552
  }
583
- async copy(into) {
584
- const newPath = into.join(this.name);
553
+ async copy(into_) {
554
+ const newPath = into_.join(this.name);
585
555
  await fp.cp(this.isAt, newPath, { recursive: true });
586
556
  return new Folder(newPath, false);
587
557
  }
588
- renameSync(to) {
589
- using _ = this.initChangeSync();
590
- const newPath = this.parent().join(to);
591
- fs.renameSync(this.isAt, newPath);
592
- this.pointsTo = newPath;
558
+ copySync(into_) {
559
+ const newPath = into_.join(this.name);
560
+ fs.cpSync(this.isAt, newPath, { recursive: true });
561
+ return new Folder(newPath, false);
593
562
  }
594
- async rename(to) {
595
- using _ = await this.initChange();
596
- const newPath = this.parent().join(to);
563
+ async rename(to_) {
564
+ using _ = await this.lock();
565
+ const newPath = this.parent().join(to_);
597
566
  await fp.rename(this.isAt, newPath);
598
567
  this.pointsTo = newPath;
599
568
  }
600
- resurrectSync() {
601
- using _ = this.initChangeSync();
602
- fs.mkdirSync(this.isAt, { recursive: true });
603
- }
604
- async resurrect() {
605
- using _ = await this.initChange();
606
- await fp.mkdir(this.isAt, { recursive: true });
569
+ renameSync(to_) {
570
+ using _ = this.lockSync();
571
+ const newPath = this.parent().join(to_);
572
+ fs.renameSync(this.isAt, newPath);
573
+ this.pointsTo = newPath;
607
574
  }
575
+ async check() { return (await fp.lstat(this.isAt)).isDirectory(); }
576
+ checkSync() { return fs.lstatSync(this.isAt).isDirectory(); }
577
+ isFolder() { return true; }
578
+ isDir() { return true; }
579
+ isDirectory() { return true; }
580
+ isDict() { return true; }
581
+ isDictionary() { return true; }
608
582
  }
609
583
  export function sysRoot() { return new Folder(ph.parse(process.cwd()).root, false); }
610
584
  export function home() { return new Folder(os.homedir(), false); }
@@ -612,131 +586,134 @@ export function tmp() { return new Folder(os.tmpdir(), false); }
612
586
  export function here() { return new Folder(process.cwd(), false); }
613
587
  export { Folder as Dir, Folder as Directory, Folder as Dict, Folder as Dictionary };
614
588
  export class SymbolicLink extends Road {
615
- static async create(at, target) {
589
+ static async create(at_, target_) {
616
590
  try {
617
- await fp.access(at, fs.constants.F_OK);
591
+ await fp.access(at_, fs.constants.F_OK);
618
592
  }
619
593
  catch {
620
- await fp.symlink(target.isAt, at);
594
+ await fp.symlink(target_.toString(), at_);
621
595
  }
622
- return new SymbolicLink(at, false);
596
+ return new SymbolicLink(at_, false);
623
597
  }
624
- static createSync(_at, _target) {
598
+ static createSync(at_, target_) {
625
599
  try {
626
- fs.accessSync(_at, fs.constants.F_OK);
600
+ fs.accessSync(at_, fs.constants.F_OK);
627
601
  }
628
602
  catch {
629
- fs.symlinkSync(_target.isAt, _at);
603
+ fs.symlinkSync(target_.toString(), at_);
630
604
  }
631
- return new SymbolicLink(_at, false);
632
- }
633
- targetSync() {
634
- return factorySync(ph.resolve(ph.dirname(this.isAt), fs.readlinkSync(this.isAt)));
605
+ return new SymbolicLink(at_, false);
635
606
  }
636
607
  async target() {
637
608
  return factory(ph.resolve(ph.dirname(this.isAt), await fp.readlink(this.isAt)));
638
609
  }
639
- retargetSync(_newTarget) {
640
- this.deleteSync();
641
- fs.symlinkSync(_newTarget.isAt, this.isAt);
610
+ targetSync() {
611
+ return factorySync(ph.resolve(ph.dirname(this.isAt), fs.readlinkSync(this.isAt)));
642
612
  }
643
- async retarget(_newTarget) {
613
+ async retarget(to_) {
644
614
  await this.delete();
645
- return fp.symlink(_newTarget.isAt, this.isAt);
615
+ return fp.symlink(to_.isAt, this.isAt);
646
616
  }
647
- deleteSync() {
648
- using _ = this.initChangeSync();
649
- fs.unlinkSync(this.isAt);
617
+ retargetSync(to_) {
618
+ this.deleteSync();
619
+ fs.symlinkSync(to_.isAt, this.isAt);
650
620
  }
651
621
  async delete() {
652
- using _ = await this.initChange();
622
+ using _ = await this.lock();
653
623
  await fp.unlink(this.isAt);
654
624
  }
655
- moveSync(_into) {
656
- using _ = this.initChangeSync();
657
- const newPath = _into.join(this.name);
658
- fs.renameSync(this.isAt, newPath);
659
- this.pointsTo = newPath;
625
+ deleteSync() {
626
+ using _ = this.lockSync();
627
+ fs.unlinkSync(this.isAt);
660
628
  }
661
- async move(_into) {
662
- using _ = await this.initChange();
663
- const newPath = _into.join(this.name);
629
+ async move(into_) {
630
+ using _ = await this.lock();
631
+ const newPath = into_.join(this.name);
664
632
  await fp.rename(this.isAt, newPath);
665
633
  this.pointsTo = newPath;
666
634
  }
667
- copySync(_into) {
668
- const newPath = _into.join(this.name);
669
- const target = this.targetSync();
670
- fs.symlinkSync(target.isAt, newPath);
671
- return new SymbolicLink(newPath, false);
635
+ moveSync(into_) {
636
+ using _ = this.lockSync();
637
+ const newPath = into_.join(this.name);
638
+ fs.renameSync(this.isAt, newPath);
639
+ this.pointsTo = newPath;
672
640
  }
673
- async copy(_into) {
674
- const newPath = _into.join(this.name);
641
+ async copy(into_) {
642
+ const newPath = into_.join(this.name);
675
643
  const target = await this.target();
676
644
  await fp.symlink(target.isAt, newPath);
677
645
  return new SymbolicLink(newPath, false);
678
646
  }
679
- renameSync(_to) {
680
- using _ = this.initChangeSync();
681
- const newPath = this.parent().join(_to);
682
- fs.renameSync(this.isAt, newPath);
683
- this.pointsTo = newPath;
647
+ copySync(into_) {
648
+ const newPath = into_.join(this.name);
649
+ const target = this.targetSync();
650
+ fs.symlinkSync(target.isAt, newPath);
651
+ return new SymbolicLink(newPath, false);
684
652
  }
685
- async rename(_to) {
686
- using _ = await this.initChange();
687
- const newPath = this.parent().join(_to);
653
+ async rename(to_) {
654
+ using _ = await this.lock();
655
+ const newPath = this.parent().join(to_);
688
656
  await fp.rename(this.isAt, newPath);
689
657
  this.pointsTo = newPath;
690
658
  }
691
- resurrectSync() {
692
- using _ = this.initChangeSync();
693
- const target = this.targetSync();
694
- fs.symlinkSync(target.isAt, this.isAt);
695
- }
696
- async resurrect() {
697
- using _ = await this.initChange();
698
- const target = await this.target();
699
- await fp.symlink(target.isAt, this.isAt);
659
+ renameSync(to_) {
660
+ using _ = this.lockSync();
661
+ const newPath = this.parent().join(to_);
662
+ fs.renameSync(this.isAt, newPath);
663
+ this.pointsTo = newPath;
700
664
  }
665
+ async check() { return (await fp.lstat(this.isAt)).isSymbolicLink(); }
666
+ checkSync() { return fs.lstatSync(this.isAt).isSymbolicLink(); }
667
+ isSymlink() { return true; }
668
+ isSymbolicLink() { return true; }
701
669
  }
702
670
  export { SymbolicLink as Symlink };
703
671
  export class UnusableRoad extends Road {
704
- mutable = false; // Modification is most likely to cause system issues (e.g. deleting a device file)
705
- constructor(_at, typeCheck) {
706
- super(_at, typeCheck);
672
+ mutable = false; // Modification will cause system issues (e.g. deleting a device file)
673
+ constructor(...args_) {
674
+ super(...args_);
707
675
  Object.freeze(this);
708
676
  }
709
- error() { throw new RdErr(`${this.constructor.name} at '${this.isAt}' is a system-level resource thus intentionally made immutable.`); }
710
- initChangeSync() { return this.error(); }
711
- async initChange() { return this.error(); }
712
- deleteSync() { return this.error(); }
677
+ error() { throw new RdErr(`${this.constructor.name} at '${this.isAt}' is a system-level resource thus not subject to modification.`); }
678
+ async lock() { return this.error(); }
679
+ lockSync() { return this.error(); }
713
680
  async delete() { return this.error(); }
714
- moveSync() { return this.error(); }
681
+ deleteSync() { return this.error(); }
715
682
  async move() { return this.error(); }
716
- copySync() { return this.error(); }
683
+ moveSync() { return this.error(); }
717
684
  async copy() { return this.error(); }
718
- renameSync() { return this.error(); }
685
+ copySync() { return this.error(); }
719
686
  async rename() { return this.error(); }
720
- resurrectSync() { return this.error(); }
721
- async resurrect() { return this.error(); }
687
+ renameSync() { return this.error(); }
688
+ isUnusable() { return true; }
722
689
  }
723
690
  export class BlockDevice extends UnusableRoad {
691
+ async check() { return (await fp.lstat(this.isAt)).isBlockDevice(); }
692
+ checkSync() { return fs.lstatSync(this.isAt).isBlockDevice(); }
693
+ isBlockDevice() { return true; }
724
694
  }
725
695
  export class CharacterDevice extends UnusableRoad {
696
+ async check() { return (await fp.lstat(this.isAt)).isCharacterDevice(); }
697
+ checkSync() { return fs.lstatSync(this.isAt).isCharacterDevice(); }
698
+ isCharacterDevice() { return true; }
726
699
  }
727
700
  export class Fifo extends UnusableRoad {
701
+ async check() { return (await fp.lstat(this.isAt)).isFIFO(); }
702
+ checkSync() { return fs.lstatSync(this.isAt).isFIFO(); }
703
+ isFifo() { return true; }
728
704
  }
729
705
  export class Socket extends UnusableRoad {
706
+ async check() { return (await fp.lstat(this.isAt)).isSocket(); }
707
+ checkSync() { return fs.lstatSync(this.isAt).isSocket(); }
708
+ isSocket() { return true; }
730
709
  }
731
- export let finalizer = null;
732
- export let toDelete = null;
733
- let exitHandlerRegistered = false;
710
+ let finalizer = null;
711
+ let toDelete = null;
712
+ let exitHandlerRegistered = null;
734
713
  /**
735
714
  * Forcefully cleans up all files and folders registered for cleanup on exit.
736
- *
737
- * @remarks This function is not recommended to be called manually, as it will delete all files and folders registered for cleanup on exit, which may lead to data loss if called at the wrong time. This function is intended to be called automatically when the process exits.
738
715
  */
739
- export function forceCleanupToDelete() {
716
+ function forceCleanupToDelete() {
740
717
  for (const path of toDelete ?? [])
741
718
  try {
742
719
  fs.rmSync(path, { force: true, recursive: true });
@@ -749,24 +726,22 @@ export function forceCleanupToDelete() {
749
726
  process.off('exit', forceCleanupToDelete);
750
727
  exitHandlerRegistered = false;
751
728
  }
752
- export function registerToCleanup(self) {
753
- if (!finalizer)
754
- finalizer = new FinalizationRegistry(p => { try {
755
- fs.rmSync(p, { force: true, recursive: true });
756
- }
757
- catch { } ; toDelete?.delete(p); });
758
- if (!toDelete)
759
- toDelete = new Set();
729
+ export function registerToCleanup(self_) {
730
+ finalizer ??= new FinalizationRegistry(p => { try {
731
+ fs.rmSync(p, { force: true, recursive: true });
732
+ }
733
+ catch { } ; toDelete?.delete(p); });
734
+ toDelete ??= new Set();
760
735
  if (!exitHandlerRegistered) {
761
736
  process.once('exit', forceCleanupToDelete);
762
737
  exitHandlerRegistered = true;
763
738
  }
764
- toDelete.add(self.isAt);
765
- finalizer.register(self, self.isAt, self);
739
+ toDelete.add(self_.isAt);
740
+ finalizer.register(self_, self_.isAt, self_);
766
741
  }
767
- export function Temp(createable, autoCleanup) {
768
- const t = createable.createSync(tmp().join(`instrumentality@${cr.randomUUID()}`));
769
- if (autoCleanup)
742
+ export function Temp(createable_, autoCleanup_) {
743
+ let t = createable_.createSync(tmp().join(`instrumentality@${cr.randomUUID()}`));
744
+ if (autoCleanup_)
770
745
  registerToCleanup(t);
771
746
  return Object.freeze(Object.assign(t, {
772
747
  [Symbol.dispose]() { try {
@@ -779,3 +754,4 @@ export function Temp(createable, autoCleanup) {
779
754
  catch { } toDelete?.delete(t.isAt); finalizer?.unregister(t); }
780
755
  }));
781
756
  }
757
+ 11;