instrumentality 0.0.13 → 0.0.15
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/dom.d.ts.map +1 -1
- package/dist/dom.js +2 -2
- package/dist/road.d.ts +66 -47
- package/dist/road.d.ts.map +1 -1
- package/dist/road.js +169 -91
- package/package.json +1 -1
- package/src/dom.ts +2 -2
- package/src/road.ts +132 -89
package/src/road.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import * as cr from "node:crypto"
|
|
2
3
|
import * as fs from "node:fs"; import { constants as fsc } from "node:fs"
|
|
3
4
|
import * as fp from "node:fs/promises"
|
|
@@ -57,25 +58,6 @@ export { resolveDirent as resDirent }
|
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
|
|
60
|
-
/**
|
|
61
|
-
* Creates the appropriate subclass of {@link Road} based on the file mode of the specified path.
|
|
62
|
-
*
|
|
63
|
-
* @param path_ The path to follow.
|
|
64
|
-
* @returns A new instance of {@link Road}.
|
|
65
|
-
* @throws If {@link fp.lstat}/{@link fs.lstatSync} fails to retrieved the status of {@link path_}.
|
|
66
|
-
*/
|
|
67
|
-
export async function factory(path_: string) {
|
|
68
|
-
return new (resolveStat((await fp.lstat(path_)).mode))(path_, false)
|
|
69
|
-
}
|
|
70
|
-
export { factory as fac, factory as mk }
|
|
71
|
-
/** Sync version of {@link factory}. */
|
|
72
|
-
export function factorySync(path_: string) {
|
|
73
|
-
return new (resolveStat(fs.lstatSync(path_).mode))(path_, false)
|
|
74
|
-
}
|
|
75
|
-
export { factorySync as facSync, factorySync as mkSync }
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
61
|
/**
|
|
80
62
|
* A map that keeps track of locked roads to prevent concurrent modifications.
|
|
81
63
|
*
|
|
@@ -97,19 +79,28 @@ export let lockedRoads: Map<string, Promise<void>> | null = null
|
|
|
97
79
|
* 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.
|
|
98
80
|
*/
|
|
99
81
|
export abstract class Road {
|
|
100
|
-
/** The absolute path to the
|
|
82
|
+
/** The absolute path to the entry that this Road instance represents.
|
|
101
83
|
* @remarks Intentionally made protected to prevent external modification, as changing this value could lead to inconsistencies and unexpected behavior. */
|
|
102
84
|
protected pointsTo: string
|
|
103
|
-
/** Indicates
|
|
85
|
+
/** Indicates which operations are allowed on the entry represented by this Road instance.
|
|
104
86
|
* Changing this value does not affect the actual file system permissions, but rather serves as a safeguard within the application to prevent accidental modifications. */
|
|
105
|
-
|
|
87
|
+
writable = true
|
|
88
|
+
moveable = true
|
|
89
|
+
deletable = true
|
|
90
|
+
copyable = true
|
|
91
|
+
renameable = true
|
|
92
|
+
assertWrite() { if (!this.writable) throw new Err(`Road to '${this.isAt}' isn't writable.`) }
|
|
93
|
+
assertMove() { if (!this.moveable) throw new Err(`Road to '${this.isAt}' isn't moveable.`) }
|
|
94
|
+
assertDelete() { if (!this.deletable) throw new Err(`Road to '${this.isAt}' isn't deletable.`) }
|
|
95
|
+
assertCopy() { if (!this.copyable) throw new Err(`Road to '${this.isAt}' isn't copyable.`) }
|
|
96
|
+
assertRename() { if (!this.renameable) throw new Err(`Road to '${this.isAt}' isn't renameable.`) }
|
|
106
97
|
|
|
107
98
|
// Quick accessors
|
|
108
99
|
/** Copy of the absolute path. */
|
|
109
100
|
get isAt() { return this.pointsTo }
|
|
110
101
|
/** Name of the road without the path (including extensions). */
|
|
111
102
|
get name() { return this.isAt.slice(this.isAt.lastIndexOf(ph.sep) + 1) }
|
|
112
|
-
/** The amount of path segments in the absolute path to the
|
|
103
|
+
/** The amount of path segments in the absolute path to the entry represented by this Road instance, minus one (i.e., the depth of the path in the file system hierarchy). */
|
|
113
104
|
get depth() { return this.isAt.split(ph.sep).length - 1 }
|
|
114
105
|
/** Same as {@link isAt} but for compatibility with external APIs. */
|
|
115
106
|
toString(): string { return this.isAt }
|
|
@@ -124,7 +115,7 @@ export abstract class Road {
|
|
|
124
115
|
*/
|
|
125
116
|
constructor(path_: string, typeCheck_: boolean) {
|
|
126
117
|
this.pointsTo = ph.resolve(path_)
|
|
127
|
-
if (typeCheck_ && !this.checkSync())
|
|
118
|
+
if (typeCheck_ && !this.checkSync(true))
|
|
128
119
|
throw new Err(`Type mismatch: '${this.isAt}'`)
|
|
129
120
|
}
|
|
130
121
|
|
|
@@ -148,8 +139,6 @@ export abstract class Road {
|
|
|
148
139
|
ancestors(): Folder[] { return [...this.ancestorsIt()] }
|
|
149
140
|
|
|
150
141
|
protected reserveLock(allowConcurrent: boolean): Disposable & { previous: Promise<void> | undefined } {
|
|
151
|
-
if (!this.mutable)
|
|
152
|
-
throw new Err(`Road to '${this.isAt}' is immutable.`)
|
|
153
142
|
lockedRoads ??= new Map()
|
|
154
143
|
const { promise, resolve } = Promise.withResolvers<void>()
|
|
155
144
|
const isAt = this.isAt
|
|
@@ -168,7 +157,7 @@ export abstract class Road {
|
|
|
168
157
|
}
|
|
169
158
|
async lock(): Promise<Disposable> {
|
|
170
159
|
const l = this.reserveLock(true)
|
|
171
|
-
try { await l.previous } catch {}
|
|
160
|
+
try { await l.previous } catch { null }
|
|
172
161
|
return l
|
|
173
162
|
}
|
|
174
163
|
lockSync(): Disposable {
|
|
@@ -188,7 +177,7 @@ export abstract class Road {
|
|
|
188
177
|
async untilAccessible(abs: AbortSignal, expectMode = fsc.F_OK, cb_?: (err: unknown) => unknown): Promise<void> {
|
|
189
178
|
const watcher = fs.watch(this.isAt)
|
|
190
179
|
try {
|
|
191
|
-
for await (
|
|
180
|
+
for await (const _ of on(watcher, 'change', { signal: abs })) {
|
|
192
181
|
try {
|
|
193
182
|
await fp.access(this.isAt, expectMode)
|
|
194
183
|
return
|
|
@@ -209,7 +198,7 @@ export abstract class Road {
|
|
|
209
198
|
async onChange<T>(abs_: AbortSignal, cb_?: () => T) {
|
|
210
199
|
const watcher = fs.watch(this.isAt)
|
|
211
200
|
try {
|
|
212
|
-
for await (
|
|
201
|
+
for await (const _ of on(watcher, 'change', { signal: abs_ }))
|
|
213
202
|
return await cb_?.() ?? null
|
|
214
203
|
return null
|
|
215
204
|
}
|
|
@@ -228,43 +217,51 @@ export abstract class Road {
|
|
|
228
217
|
|
|
229
218
|
/** Wrapper around {@link fp.rm} with locking. */
|
|
230
219
|
async delete(): Promise<void> {
|
|
220
|
+
this.assertDelete()
|
|
231
221
|
using _ = await this.lock()
|
|
232
222
|
await fp.rm(this.isAt, { recursive: true, force: true })
|
|
233
223
|
}
|
|
234
224
|
/** Wrapper around {@link fs.rmSync} with locking. */
|
|
235
225
|
deleteSync(): void {
|
|
226
|
+
this.assertDelete()
|
|
236
227
|
using _ = this.lockSync()
|
|
237
228
|
fs.rmSync(this.isAt, { recursive: true, force: true })
|
|
238
229
|
}
|
|
239
230
|
async copy(into_: Folder): Promise<this> {
|
|
231
|
+
this.assertCopy()
|
|
240
232
|
const newPath = into_.join(this.name)
|
|
241
233
|
await fp.cp(this.isAt, newPath, { recursive: true, force: true })
|
|
242
234
|
return new (this.constructor as new (path: string, typeCheck: boolean) => this)(newPath, false)
|
|
243
235
|
}
|
|
244
236
|
copySync(into_: Folder): this {
|
|
237
|
+
this.assertCopy()
|
|
245
238
|
const newPath = into_.join(this.name)
|
|
246
239
|
fs.cpSync(this.isAt, newPath, { recursive: true, force: true })
|
|
247
240
|
return new (this.constructor as new (path: string, typeCheck: boolean) => this)(newPath, false)
|
|
248
241
|
}
|
|
249
242
|
async move(into_: Folder): Promise<void> {
|
|
243
|
+
this.assertMove()
|
|
250
244
|
using _ = await this.lock()
|
|
251
245
|
const newPath = into_.join(this.name)
|
|
252
246
|
await fp.rename(this.isAt, newPath)
|
|
253
247
|
this.pointsTo = newPath
|
|
254
248
|
}
|
|
255
249
|
moveSync(into_: Folder): void {
|
|
250
|
+
this.assertMove()
|
|
256
251
|
using _ = this.lockSync()
|
|
257
252
|
const newPath = into_.join(this.name)
|
|
258
253
|
fs.renameSync(this.isAt, newPath)
|
|
259
254
|
this.pointsTo = newPath
|
|
260
255
|
}
|
|
261
256
|
async rename(newName_: string): Promise<void> {
|
|
257
|
+
this.assertRename()
|
|
262
258
|
using _ = await this.lock()
|
|
263
259
|
const newPath = this.parent().join(newName_)
|
|
264
260
|
await fp.rename(this.isAt, newPath)
|
|
265
261
|
this.pointsTo = newPath
|
|
266
262
|
}
|
|
267
263
|
renameSync(newName_: string): void {
|
|
264
|
+
this.assertRename()
|
|
268
265
|
using _ = this.lockSync()
|
|
269
266
|
const newPath = this.parent().join(newName_)
|
|
270
267
|
fs.renameSync(this.isAt, newPath)
|
|
@@ -272,8 +269,8 @@ export abstract class Road {
|
|
|
272
269
|
}
|
|
273
270
|
|
|
274
271
|
// jsdocs for the abstract methods are in the subclasses
|
|
275
|
-
abstract check(): Promise<boolean>
|
|
276
|
-
abstract checkSync(): boolean
|
|
272
|
+
abstract check(throwOnError_: boolean): Promise<boolean>
|
|
273
|
+
abstract checkSync(throwOnError_: boolean): boolean
|
|
277
274
|
|
|
278
275
|
/** Type narrowing for {@link File} (similar to `instanceof` without unnecessary runtime checks). */
|
|
279
276
|
isFile(): this is File { return false as const }
|
|
@@ -298,9 +295,30 @@ export abstract class Road {
|
|
|
298
295
|
/** Type narrowing for {@link Socket} (similar to `instanceof` without unnecessary runtime checks). */
|
|
299
296
|
isSocket(): this is Socket { return false as const }
|
|
300
297
|
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
301
|
/** Constructor type for a subclass of {@link Road}. */
|
|
302
302
|
export type road_t<T extends Road> = new (...args_: ConstructorParameters<typeof Road>) => T
|
|
303
303
|
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Creates the appropriate subclass of {@link Road} based on the file mode of the specified path.
|
|
310
|
+
*
|
|
311
|
+
* @param path_ The path to follow.
|
|
312
|
+
* @returns A new instance of {@link Road}.
|
|
313
|
+
* @throws If {@link fp.lstat}/{@link fs.lstatSync} fails to retrieved the status of {@link path_}.
|
|
314
|
+
*/
|
|
315
|
+
export async function road(path_: string) { return new (resolveStat((await fp.lstat(path_)).mode))(path_, false) }
|
|
316
|
+
export { road as fac, road as mk, road as factory }
|
|
317
|
+
/** Sync version of {@link road}. */
|
|
318
|
+
export function roadSync(path_: string) { return new (resolveStat((fs.lstatSync(path_).mode)))(path_, false) }
|
|
319
|
+
export { roadSync as facSync, roadSync as mkSync, roadSync as factorySync }
|
|
320
|
+
|
|
321
|
+
|
|
304
322
|
|
|
305
323
|
|
|
306
324
|
/** Subclass of {@link Road} that represents a file. */
|
|
@@ -342,7 +360,7 @@ export class File extends Road {
|
|
|
342
360
|
async read(): Promise<Buffer>
|
|
343
361
|
async read(options_: Parameters<typeof fp.readFile>[1]): Promise<string>
|
|
344
362
|
async read(options_?: Parameters<typeof fp.readFile>[1]): Promise<Buffer | string> {
|
|
345
|
-
return fp.readFile(this.isAt, options_!)
|
|
363
|
+
return await fp.readFile(this.isAt, options_!)
|
|
346
364
|
}
|
|
347
365
|
readSync(): Buffer
|
|
348
366
|
readSync(options_: Parameters<typeof fs.readFileSync>[1]): string
|
|
@@ -406,8 +424,8 @@ export class File extends Road {
|
|
|
406
424
|
}
|
|
407
425
|
}
|
|
408
426
|
finally {
|
|
409
|
-
try { iter1.return?.(undefined) } catch {}
|
|
410
|
-
try { iter2.return?.(undefined) } catch {}
|
|
427
|
+
try { iter1.return?.(undefined) } catch { null }
|
|
428
|
+
try { iter2.return?.(undefined) } catch { null }
|
|
411
429
|
}
|
|
412
430
|
}
|
|
413
431
|
|
|
@@ -432,16 +450,20 @@ export class File extends Road {
|
|
|
432
450
|
sizeSync(): number { return this.lstatSync().size }
|
|
433
451
|
|
|
434
452
|
async writeAtomic(data_: Buffer | string, options_?: fs.WriteFileOptions) {
|
|
453
|
+
this.assertWrite()
|
|
435
454
|
using _ = await this.lock()
|
|
436
455
|
await this.parent().borrow(File, async tmp => {
|
|
456
|
+
tmp.assertWrite()
|
|
437
457
|
using _ = await tmp.lock()
|
|
438
458
|
await fp.writeFile(tmp.isAt, data_, options_)
|
|
439
459
|
await fp.rename(tmp.isAt, this.isAt)
|
|
440
460
|
})
|
|
441
461
|
}
|
|
442
462
|
writeAtomicSync(data_: Buffer | string, options_?: fs.WriteFileOptions) {
|
|
463
|
+
this.assertWrite()
|
|
443
464
|
using _ = this.lockSync()
|
|
444
465
|
this.parent().borrowSync(File, tmp => {
|
|
466
|
+
tmp.assertWrite()
|
|
445
467
|
using _ = tmp.lockSync()
|
|
446
468
|
fs.writeFileSync(tmp.isAt, data_, options_)
|
|
447
469
|
fs.renameSync(tmp.isAt, this.isAt)
|
|
@@ -449,14 +471,17 @@ export class File extends Road {
|
|
|
449
471
|
}
|
|
450
472
|
|
|
451
473
|
async write(data_: Buffer | string, options_?: fs.WriteFileOptions) {
|
|
474
|
+
this.assertWrite()
|
|
452
475
|
using _ = await this.lock()
|
|
453
476
|
await fp.writeFile(this.isAt, data_, options_)
|
|
454
477
|
}
|
|
455
478
|
writeSync(data_: Buffer | string, options_?: fs.WriteFileOptions) {
|
|
479
|
+
this.assertWrite()
|
|
456
480
|
using _ = this.lockSync()
|
|
457
481
|
fs.writeFileSync(this.isAt, data_, options_)
|
|
458
482
|
}
|
|
459
483
|
async writePast(offset_: number, data_: Buffer | string) {
|
|
484
|
+
this.assertWrite()
|
|
460
485
|
using _ = await this.lock()
|
|
461
486
|
const fd = await fp.open(this.isAt, 'r+')
|
|
462
487
|
try {
|
|
@@ -465,6 +490,7 @@ export class File extends Road {
|
|
|
465
490
|
} finally { await fd.close() }
|
|
466
491
|
}
|
|
467
492
|
writePastSync(offset_: number, data_: Buffer | string) {
|
|
493
|
+
this.assertWrite()
|
|
468
494
|
using _ = this.lockSync()
|
|
469
495
|
const fd = fs.openSync(this.isAt, 'r+')
|
|
470
496
|
try {
|
|
@@ -473,6 +499,7 @@ export class File extends Road {
|
|
|
473
499
|
} finally { fs.closeSync(fd) }
|
|
474
500
|
}
|
|
475
501
|
async truncWritePast(offset_: number, data_: Buffer | string) {
|
|
502
|
+
this.assertWrite()
|
|
476
503
|
using _ = await this.lock()
|
|
477
504
|
const fd = await fp.open(this.isAt, 'r+')
|
|
478
505
|
try {
|
|
@@ -483,6 +510,7 @@ export class File extends Road {
|
|
|
483
510
|
finally { await fd.close() }
|
|
484
511
|
}
|
|
485
512
|
truncWritePastSync(offset_: number, data_: Buffer | string) {
|
|
513
|
+
this.assertWrite()
|
|
486
514
|
using _ = this.lockSync()
|
|
487
515
|
const fd = fs.openSync(this.isAt, 'r+')
|
|
488
516
|
try {
|
|
@@ -493,16 +521,18 @@ export class File extends Road {
|
|
|
493
521
|
finally { fs.closeSync(fd) }
|
|
494
522
|
}
|
|
495
523
|
async append(data_: Buffer | string, options_?: fs.WriteFileOptions) {
|
|
524
|
+
this.assertWrite()
|
|
496
525
|
using _ = await this.lock()
|
|
497
526
|
await fp.appendFile(this.isAt, data_, options_)
|
|
498
527
|
}
|
|
499
528
|
appendSync(data_: Buffer | string, options_?: fs.WriteFileOptions) {
|
|
529
|
+
this.assertWrite()
|
|
500
530
|
using _ = this.lockSync()
|
|
501
531
|
fs.appendFileSync(this.isAt, data_, options_)
|
|
502
532
|
}
|
|
503
533
|
|
|
504
|
-
async check(): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isFile() } catch { return false } }
|
|
505
|
-
checkSync(): boolean { try { return fs.lstatSync(this.isAt).isFile() } catch { return false } }
|
|
534
|
+
async check(throwOnError_: boolean): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isFile() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
535
|
+
checkSync(throwOnError_: boolean): boolean { try { return fs.lstatSync(this.isAt).isFile() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
506
536
|
|
|
507
537
|
override isFile(): this is File { return true as const }
|
|
508
538
|
}
|
|
@@ -513,7 +543,8 @@ export function file(...args: ConstructorParameters<typeof File>): File { return
|
|
|
513
543
|
|
|
514
544
|
|
|
515
545
|
/** Helper type for filtering {@link Road} instances. */
|
|
516
|
-
export type filter_t<T extends Road> = ((
|
|
546
|
+
export type filter_t<T extends Road> = ((found: Road) => found is T)
|
|
547
|
+
|
|
517
548
|
|
|
518
549
|
export class Folder extends Road {
|
|
519
550
|
static async create(at_: string) {
|
|
@@ -591,52 +622,48 @@ export class Folder extends Road {
|
|
|
591
622
|
}
|
|
592
623
|
|
|
593
624
|
async find(name_: string): Promise<Road | null>
|
|
594
|
-
async find<T extends Road>(name_: string,
|
|
595
|
-
async find<T extends Road>(name_: string,
|
|
625
|
+
async find<T extends Road>(name_: string, filter_: filter_t<T>): Promise<T | null>
|
|
626
|
+
async find<T extends Road>(name_: string, filter_?: filter_t<T>): Promise<Road | T | null> {
|
|
596
627
|
try {
|
|
597
|
-
const found = await
|
|
598
|
-
if (!
|
|
628
|
+
const found = await road(this.join(name_))
|
|
629
|
+
if (!filter_ || filter_(found))
|
|
599
630
|
return found
|
|
600
|
-
if (found instanceof expect_)
|
|
601
|
-
return found as T
|
|
602
631
|
return null
|
|
603
|
-
}
|
|
604
|
-
catch { return null }
|
|
632
|
+
} catch { return null }
|
|
605
633
|
}
|
|
606
634
|
findSync(name_: string): Road | null
|
|
607
|
-
findSync<T extends Road>(name_: string,
|
|
608
|
-
findSync<T extends Road>(name_: string,
|
|
635
|
+
findSync<T extends Road>(name_: string, filter_: filter_t<T>): T | null
|
|
636
|
+
findSync<T extends Road>(name_: string, filter_?: filter_t<T>): Road | T | null {
|
|
609
637
|
try {
|
|
610
|
-
const found =
|
|
611
|
-
if (!
|
|
638
|
+
const found = roadSync(this.join(name_))
|
|
639
|
+
if (!filter_ || filter_(found))
|
|
612
640
|
return found
|
|
613
|
-
if (found instanceof expect_)
|
|
614
|
-
return found as T
|
|
615
641
|
return null
|
|
616
|
-
}
|
|
617
|
-
catch(e: unknown) { return null }
|
|
642
|
+
} catch { return null }
|
|
618
643
|
}
|
|
619
644
|
|
|
620
645
|
async add<T extends Road>(name_: string, createable_: { mk: (at: string) => Promise<T> }): Promise<T> {
|
|
646
|
+
this.assertWrite()
|
|
621
647
|
const newPath = this.join(name_)
|
|
622
648
|
await createable_.mk(newPath)
|
|
623
|
-
return (await
|
|
649
|
+
return (await road(newPath)) as unknown as T
|
|
624
650
|
}
|
|
625
651
|
addSync<T extends Road>(name_: string, createable_: { mkSync: (at: string) => T }): T {
|
|
652
|
+
this.assertWrite()
|
|
626
653
|
const newPath = this.join(name_)
|
|
627
654
|
createable_.mkSync(newPath)
|
|
628
|
-
return
|
|
655
|
+
return roadSync(newPath) as unknown as T
|
|
629
656
|
}
|
|
630
657
|
|
|
631
658
|
async borrow<T extends Road>(createable_: { mk: (at: string) => Promise<T> }, cb_: (r: T) => Promise<void> | void): Promise<void> {
|
|
632
|
-
const
|
|
633
|
-
try { await cb_(
|
|
634
|
-
finally { await fp.rm(
|
|
659
|
+
const created = await createable_.mk(this.join(`instrumentality@${crypto.randomUUID()}`))
|
|
660
|
+
try { await cb_(created) }
|
|
661
|
+
finally { await fp.rm(created.isAt, { recursive: true, force: true }) }
|
|
635
662
|
}
|
|
636
663
|
borrowSync<T extends Road>(createable_: { mkSync: (at: string) => T }, cb_: (r: T) => void): void {
|
|
637
|
-
const
|
|
638
|
-
try { cb_(
|
|
639
|
-
finally { fs.rmSync(
|
|
664
|
+
const created = createable_.mkSync(this.join(`instrumentality@${crypto.randomUUID()}`))
|
|
665
|
+
try { cb_(created) }
|
|
666
|
+
finally { fs.rmSync(created.isAt, { recursive: true, force: true }) }
|
|
640
667
|
}
|
|
641
668
|
|
|
642
669
|
async size(): Promise<number> {
|
|
@@ -652,8 +679,8 @@ export class Folder extends Road {
|
|
|
652
679
|
return size
|
|
653
680
|
}
|
|
654
681
|
|
|
655
|
-
async check(): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isDirectory() } catch { return false } }
|
|
656
|
-
checkSync(): boolean { try { return fs.lstatSync(this.isAt).isDirectory() } catch { return false } }
|
|
682
|
+
async check(throwOnError_: boolean): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isDirectory() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
683
|
+
checkSync(throwOnError_: boolean): boolean { try { return fs.lstatSync(this.isAt).isDirectory() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
657
684
|
|
|
658
685
|
override isFolder(): this is Folder { return true as const }
|
|
659
686
|
override isDir(): this is Folder { return true as const }
|
|
@@ -663,13 +690,14 @@ export class Folder extends Road {
|
|
|
663
690
|
|
|
664
691
|
export function folder(...args: ConstructorParameters<typeof Folder>): Folder { return new Folder(...args) }
|
|
665
692
|
export function dir(...args: ConstructorParameters<typeof Folder>): Folder { return new Folder(...args) }
|
|
693
|
+
export function directory(...args: ConstructorParameters<typeof Folder>): Folder { return new Folder(...args) }
|
|
666
694
|
|
|
667
695
|
|
|
668
696
|
export function sysRoot() { return new Folder(ph.parse(process.cwd()).root, false) }
|
|
669
697
|
export function home() { return new Folder(os.homedir(), false) }
|
|
670
698
|
export function tmp() { return new Folder(os.tmpdir(), false) }
|
|
671
699
|
export function here() { return new Folder(process.cwd(), false) }
|
|
672
|
-
export { Folder as Dir, Folder as Directory
|
|
700
|
+
export { Folder as Dir, Folder as Directory }
|
|
673
701
|
|
|
674
702
|
|
|
675
703
|
|
|
@@ -688,17 +716,21 @@ export class SymbolicLink extends Road {
|
|
|
688
716
|
static readonly mkSync: typeof SymbolicLink.createSync = SymbolicLink.createSync
|
|
689
717
|
|
|
690
718
|
async target() {
|
|
691
|
-
return
|
|
719
|
+
return await road(ph.resolve(ph.dirname(this.isAt), await fp.readlink(this.isAt)))
|
|
692
720
|
}
|
|
693
721
|
targetSync() {
|
|
694
|
-
return
|
|
722
|
+
return roadSync(ph.resolve(ph.dirname(this.isAt), fs.readlinkSync(this.isAt)))
|
|
695
723
|
}
|
|
696
724
|
async retarget(to_: Road) {
|
|
725
|
+
this.assertDelete()
|
|
726
|
+
this.assertWrite()
|
|
697
727
|
using _ = await this.lock()
|
|
698
728
|
await fp.unlink(this.isAt)
|
|
699
729
|
await fp.symlink(to_.isAt, this.isAt)
|
|
700
730
|
}
|
|
701
731
|
retargetSync(to_: Road) {
|
|
732
|
+
this.assertDelete()
|
|
733
|
+
this.assertWrite()
|
|
702
734
|
using _ = this.lockSync()
|
|
703
735
|
fs.unlinkSync(this.isAt)
|
|
704
736
|
fs.symlinkSync(to_.isAt, this.isAt)
|
|
@@ -708,16 +740,18 @@ export class SymbolicLink extends Road {
|
|
|
708
740
|
sizeSync(): number { return this.lstatSync().size }
|
|
709
741
|
|
|
710
742
|
override async delete() {
|
|
743
|
+
this.assertDelete()
|
|
711
744
|
using _ = await this.lock()
|
|
712
745
|
await fp.unlink(this.isAt)
|
|
713
746
|
}
|
|
714
747
|
override deleteSync() {
|
|
748
|
+
this.assertDelete()
|
|
715
749
|
using _ = this.lockSync()
|
|
716
750
|
fs.unlinkSync(this.isAt)
|
|
717
751
|
}
|
|
718
752
|
|
|
719
|
-
async check(): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isSymbolicLink() } catch { return false } }
|
|
720
|
-
checkSync(): boolean { try { return fs.lstatSync(this.isAt).isSymbolicLink() } catch { return false } }
|
|
753
|
+
async check(throwOnError_: boolean): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isSymbolicLink() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
754
|
+
checkSync(throwOnError_: boolean): boolean { try { return fs.lstatSync(this.isAt).isSymbolicLink() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
721
755
|
|
|
722
756
|
override isSymlink(): this is SymbolicLink { return true as const }
|
|
723
757
|
override isSymbolicLink(): this is SymbolicLink { return true as const }
|
|
@@ -731,55 +765,64 @@ export function symlink(...args: ConstructorParameters<typeof SymbolicLink>): Sy
|
|
|
731
765
|
|
|
732
766
|
|
|
733
767
|
|
|
768
|
+
/**
|
|
769
|
+
* Represents a system-level resource that is not subject to modification.
|
|
770
|
+
* All modification operations will throw an error.
|
|
771
|
+
*
|
|
772
|
+
* One could say 'this road truly is *unusable*.' hehe
|
|
773
|
+
*/
|
|
734
774
|
export abstract class UnusableRoad extends Road {
|
|
735
|
-
|
|
736
|
-
|
|
775
|
+
// Modification will cause system issues (e.g. deleting a device file)
|
|
776
|
+
override readonly writable = false as never
|
|
777
|
+
override readonly moveable = false as never
|
|
778
|
+
override readonly deletable = false as never
|
|
779
|
+
override readonly copyable = false as never
|
|
780
|
+
size(): Promise<0> { return Promise.resolve(0) }
|
|
737
781
|
sizeSync(): 0 { return 0 }
|
|
738
782
|
error(): never { throw new Err(`${this.constructor.name} at '${this.isAt}' is a system-level resource thus not subject to modification.`) }
|
|
739
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be locked */
|
|
783
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be locked */
|
|
740
784
|
override lock(): never { return this.error() }
|
|
741
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be locked */
|
|
785
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be locked */
|
|
742
786
|
override lockSync(): never { return this.error() }
|
|
743
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be deleted */
|
|
787
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be deleted */
|
|
744
788
|
override delete(): never { return this.error() }
|
|
745
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be deleted */
|
|
789
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be deleted */
|
|
746
790
|
override deleteSync(): never { return this.error() }
|
|
747
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be moved */
|
|
791
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be moved */
|
|
748
792
|
override move(): never { return this.error() }
|
|
749
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be moved */
|
|
793
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be moved */
|
|
750
794
|
override moveSync(): never { return this.error() }
|
|
751
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be copied */
|
|
795
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be copied */
|
|
752
796
|
override copy(): never { return this.error() }
|
|
753
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be copied */
|
|
797
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be copied */
|
|
754
798
|
override copySync(): never { return this.error() }
|
|
755
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be renamed */
|
|
799
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be renamed */
|
|
756
800
|
override rename(): never { return this.error() }
|
|
757
|
-
/** @deprecated System-level resources (UnusableRoad) can't/shouldn't be renamed */
|
|
801
|
+
/** @deprecated System-level resources ({@link UnusableRoad}) can't/shouldn't be renamed */
|
|
758
802
|
override renameSync(): never { return this.error() }
|
|
759
|
-
|
|
760
803
|
override isUnusable(): this is UnusableRoad { return true as const }
|
|
761
804
|
}
|
|
762
805
|
export class BlockDevice extends UnusableRoad {
|
|
763
|
-
async check(): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isBlockDevice() } catch { return false } }
|
|
764
|
-
checkSync(): boolean { try { return fs.lstatSync(this.isAt).isBlockDevice() } catch { return false } }
|
|
806
|
+
async check(throwOnError_: boolean): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isBlockDevice() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
807
|
+
checkSync(throwOnError_: boolean): boolean { try { return fs.lstatSync(this.isAt).isBlockDevice() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
765
808
|
override isBlockDevice(): this is BlockDevice { return true as const }
|
|
766
809
|
}
|
|
767
810
|
export function blockDevice(...args: ConstructorParameters<typeof BlockDevice>): BlockDevice { return new BlockDevice(...args) }
|
|
768
811
|
export class CharacterDevice extends UnusableRoad {
|
|
769
|
-
async check(): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isCharacterDevice() } catch { return false } }
|
|
770
|
-
checkSync(): boolean { try { return fs.lstatSync(this.isAt).isCharacterDevice() } catch { return false } }
|
|
812
|
+
async check(throwOnError_: boolean): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isCharacterDevice() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
813
|
+
checkSync(throwOnError_: boolean): boolean { try { return fs.lstatSync(this.isAt).isCharacterDevice() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
771
814
|
override isCharacterDevice(): this is CharacterDevice { return true as const }
|
|
772
815
|
}
|
|
773
816
|
export function characterDevice(...args: ConstructorParameters<typeof CharacterDevice>): CharacterDevice { return new CharacterDevice(...args) }
|
|
774
817
|
export class Fifo extends UnusableRoad {
|
|
775
|
-
async check(): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isFIFO() } catch { return false } }
|
|
776
|
-
checkSync(): boolean { try { return fs.lstatSync(this.isAt).isFIFO() } catch { return false } }
|
|
818
|
+
async check(throwOnError_: boolean): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isFIFO() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
819
|
+
checkSync(throwOnError_: boolean): boolean { try { return fs.lstatSync(this.isAt).isFIFO() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
777
820
|
override isFifo(): this is Fifo { return true as const }
|
|
778
821
|
}
|
|
779
822
|
export function fifo(...args: ConstructorParameters<typeof Fifo>): Fifo { return new Fifo(...args) }
|
|
780
823
|
export class Socket extends UnusableRoad {
|
|
781
|
-
async check(): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isSocket() } catch { return false } }
|
|
782
|
-
checkSync(): boolean { try { return fs.lstatSync(this.isAt).isSocket() } catch { return false } }
|
|
824
|
+
async check(throwOnError_: boolean): Promise<boolean> { try { return (await fp.lstat(this.isAt)).isSocket() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
825
|
+
checkSync(throwOnError_: boolean): boolean { try { return fs.lstatSync(this.isAt).isSocket() } catch (e) { if (throwOnError_) throw e; return false } }
|
|
783
826
|
override isSocket(): this is Socket { return true as const }
|
|
784
827
|
}
|
|
785
828
|
export function socket(...args: ConstructorParameters<typeof Socket>): Socket { return new Socket(...args) }
|