@xfcfam/xf-fs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/src/api/A.d.ts +17 -0
- package/dist/src/api/A.d.ts.map +1 -0
- package/dist/src/api/A.js +17 -0
- package/dist/src/api/A.js.map +1 -0
- package/dist/src/business/B.d.ts +17 -0
- package/dist/src/business/B.d.ts.map +1 -0
- package/dist/src/business/B.js +17 -0
- package/dist/src/business/B.js.map +1 -0
- package/dist/src/repository/R.d.ts +17 -0
- package/dist/src/repository/R.d.ts.map +1 -0
- package/dist/src/repository/R.js +17 -0
- package/dist/src/repository/R.js.map +1 -0
- package/dist/src/repository/base/AuditedFileRepository.d.ts +86 -0
- package/dist/src/repository/base/AuditedFileRepository.d.ts.map +1 -0
- package/dist/src/repository/base/AuditedFileRepository.js +202 -0
- package/dist/src/repository/base/AuditedFileRepository.js.map +1 -0
- package/dist/src/repository/base/CachedFileRepository.d.ts +53 -0
- package/dist/src/repository/base/CachedFileRepository.d.ts.map +1 -0
- package/dist/src/repository/base/CachedFileRepository.js +107 -0
- package/dist/src/repository/base/CachedFileRepository.js.map +1 -0
- package/dist/src/repository/base/FileRepository.d.ts +170 -0
- package/dist/src/repository/base/FileRepository.d.ts.map +1 -0
- package/dist/src/repository/base/FileRepository.js +382 -0
- package/dist/src/repository/base/FileRepository.js.map +1 -0
- package/dist/src/repository/general/AuditedFileRepository.d.ts +86 -0
- package/dist/src/repository/general/AuditedFileRepository.d.ts.map +1 -0
- package/dist/src/repository/general/AuditedFileRepository.js +238 -0
- package/dist/src/repository/general/AuditedFileRepository.js.map +1 -0
- package/dist/src/repository/general/CachedFileRepository.d.ts +53 -0
- package/dist/src/repository/general/CachedFileRepository.d.ts.map +1 -0
- package/dist/src/repository/general/CachedFileRepository.js +107 -0
- package/dist/src/repository/general/CachedFileRepository.js.map +1 -0
- package/dist/src/repository/general/FileRepository.d.ts +180 -0
- package/dist/src/repository/general/FileRepository.d.ts.map +1 -0
- package/dist/src/repository/general/FileRepository.js +401 -0
- package/dist/src/repository/general/FileRepository.js.map +1 -0
- package/dist/src/repository/structs/DirectoryNotEmptyException.d.ts +13 -0
- package/dist/src/repository/structs/DirectoryNotEmptyException.d.ts.map +1 -0
- package/dist/src/repository/structs/DirectoryNotEmptyException.js +17 -0
- package/dist/src/repository/structs/DirectoryNotEmptyException.js.map +1 -0
- package/dist/src/repository/structs/FileAccessDeniedException.d.ts +14 -0
- package/dist/src/repository/structs/FileAccessDeniedException.d.ts.map +1 -0
- package/dist/src/repository/structs/FileAccessDeniedException.js +18 -0
- package/dist/src/repository/structs/FileAccessDeniedException.js.map +1 -0
- package/dist/src/repository/structs/FileEntry.d.ts +21 -0
- package/dist/src/repository/structs/FileEntry.d.ts.map +1 -0
- package/dist/src/repository/structs/FileEntry.js +2 -0
- package/dist/src/repository/structs/FileEntry.js.map +1 -0
- package/dist/src/repository/structs/FileNotFoundException.d.ts +13 -0
- package/dist/src/repository/structs/FileNotFoundException.d.ts.map +1 -0
- package/dist/src/repository/structs/FileNotFoundException.js +17 -0
- package/dist/src/repository/structs/FileNotFoundException.js.map +1 -0
- package/dist/src/repository/structs/FileStat.d.ts +24 -0
- package/dist/src/repository/structs/FileStat.d.ts.map +1 -0
- package/dist/src/repository/structs/FileStat.js +2 -0
- package/dist/src/repository/structs/FileStat.js.map +1 -0
- package/dist/src/repository/structs/TempFile.d.ts +19 -0
- package/dist/src/repository/structs/TempFile.d.ts.map +1 -0
- package/dist/src/repository/structs/TempFile.js +2 -0
- package/dist/src/repository/structs/TempFile.js.map +1 -0
- package/dist/src/repository/structs/WatchEvent.d.ts +18 -0
- package/dist/src/repository/structs/WatchEvent.d.ts.map +1 -0
- package/dist/src/repository/structs/WatchEvent.js +2 -0
- package/dist/src/repository/structs/WatchEvent.js.map +1 -0
- package/dist/src/repository/structs/Watcher.d.ts +18 -0
- package/dist/src/repository/structs/Watcher.d.ts.map +1 -0
- package/dist/src/repository/structs/Watcher.js +2 -0
- package/dist/src/repository/structs/Watcher.js.map +1 -0
- package/dist/src/repository/transfers/DirectoryNotEmptyException.d.ts +13 -0
- package/dist/src/repository/transfers/DirectoryNotEmptyException.d.ts.map +1 -0
- package/dist/src/repository/transfers/DirectoryNotEmptyException.js +17 -0
- package/dist/src/repository/transfers/DirectoryNotEmptyException.js.map +1 -0
- package/dist/src/repository/transfers/FileAccessDeniedException.d.ts +14 -0
- package/dist/src/repository/transfers/FileAccessDeniedException.d.ts.map +1 -0
- package/dist/src/repository/transfers/FileAccessDeniedException.js +18 -0
- package/dist/src/repository/transfers/FileAccessDeniedException.js.map +1 -0
- package/dist/src/repository/transfers/FileEntry.d.ts +21 -0
- package/dist/src/repository/transfers/FileEntry.d.ts.map +1 -0
- package/dist/src/repository/transfers/FileEntry.js +2 -0
- package/dist/src/repository/transfers/FileEntry.js.map +1 -0
- package/dist/src/repository/transfers/FileNotFoundException.d.ts +13 -0
- package/dist/src/repository/transfers/FileNotFoundException.d.ts.map +1 -0
- package/dist/src/repository/transfers/FileNotFoundException.js +17 -0
- package/dist/src/repository/transfers/FileNotFoundException.js.map +1 -0
- package/dist/src/repository/transfers/FileStat.d.ts +24 -0
- package/dist/src/repository/transfers/FileStat.d.ts.map +1 -0
- package/dist/src/repository/transfers/FileStat.js +2 -0
- package/dist/src/repository/transfers/FileStat.js.map +1 -0
- package/dist/src/repository/transfers/TempFile.d.ts +19 -0
- package/dist/src/repository/transfers/TempFile.d.ts.map +1 -0
- package/dist/src/repository/transfers/TempFile.js +2 -0
- package/dist/src/repository/transfers/TempFile.js.map +1 -0
- package/dist/src/repository/transfers/WatchEvent.d.ts +25 -0
- package/dist/src/repository/transfers/WatchEvent.d.ts.map +1 -0
- package/dist/src/repository/transfers/WatchEvent.js +2 -0
- package/dist/src/repository/transfers/WatchEvent.js.map +1 -0
- package/dist/src/repository/transfers/Watcher.d.ts +18 -0
- package/dist/src/repository/transfers/Watcher.d.ts.map +1 -0
- package/dist/src/repository/transfers/Watcher.js +2 -0
- package/dist/src/repository/transfers/Watcher.js.map +1 -0
- package/dist/src/repository/utils/EncodingUtils.d.ts +29 -0
- package/dist/src/repository/utils/EncodingUtils.d.ts.map +1 -0
- package/dist/src/repository/utils/EncodingUtils.js +36 -0
- package/dist/src/repository/utils/EncodingUtils.js.map +1 -0
- package/dist/src/repository/utils/PathUtils.d.ts +23 -0
- package/dist/src/repository/utils/PathUtils.d.ts.map +1 -0
- package/dist/src/repository/utils/PathUtils.js +53 -0
- package/dist/src/repository/utils/PathUtils.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Israel Sanjurjo and the XF contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# `@xfcfam/xf-fs`
|
|
2
|
+
|
|
3
|
+
Filesystem Access Layer Generalization for the **XF Architecture
|
|
4
|
+
Model** — encapsulates `node:fs` behind a coherent, XF-compliant API.
|
|
5
|
+
|
|
6
|
+
`xf-fs` is the canonical wrapper that lets an XF artefact talk to
|
|
7
|
+
the local filesystem without ever importing `node:fs` directly. It
|
|
8
|
+
follows the same shape as `@xfcfam/xf-rest` (HTTP protocol) and
|
|
9
|
+
`@xfcfam/xf-sql` (SQL protocol): one Generalization per protocol of
|
|
10
|
+
access, with cross-cutting variants for caching and audit.
|
|
11
|
+
|
|
12
|
+
## What it provides
|
|
13
|
+
|
|
14
|
+
| Component | Purpose |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `FileRepository` | Generalization. Wraps `node:fs`: read/write/append/delete/exists/stat, list/walk, mkdir/rmdir, streaming, watch, temp files. Concrete components extend it for their domain. |
|
|
17
|
+
| `CachedFileRepository` | Same protocol; adds an in-memory cache for `read`/`readBytes` with write-through invalidation. |
|
|
18
|
+
| `AuditedFileRepository` | Same protocol; adds overridable `onRead` / `onWrite` / `onDelete` / `onError` / … hooks for audit logs, metrics, tracing. |
|
|
19
|
+
| `FileEntry`, `FileStat`, `WatchEvent`, `Watcher`, `TempFile` | Transfer objects modelling directory entries, metadata, change events, watch handles, and temp-file handles. |
|
|
20
|
+
| `FileNotFoundException`, `FileAccessDeniedException`, `DirectoryNotEmptyException` | Typed Exceptions for the three modelled failure modes. Other failures propagate as native `Error`. |
|
|
21
|
+
| `PathUtils`, `EncodingUtils` | Pure helpers — path manipulation (`join` / `normalize` / `relative` / …) and BOM-aware UTF-8 encoding. |
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @xfcfam/xf-fs @xfcfam/xf
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`@xfcfam/xf` is a peer dependency.
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { FileRepository } from '@xfcfam/xf-fs'
|
|
35
|
+
|
|
36
|
+
interface User { id: string; name: string }
|
|
37
|
+
|
|
38
|
+
export class UsersFileRepository extends FileRepository {
|
|
39
|
+
constructor() { super({ rootPath: '/var/data/users' }) }
|
|
40
|
+
|
|
41
|
+
async findById(id: string): Promise<User> {
|
|
42
|
+
const text = await this.read(`${id}.json`)
|
|
43
|
+
return JSON.parse(text) as User
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async save(user: User): Promise<void> {
|
|
47
|
+
await this.write(`${user.id}.json`, JSON.stringify(user))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Inject it into the artefact's Access Layer injection `R`:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { UsersFileRepository } from './repository/logic/UsersFileRepository.js'
|
|
56
|
+
|
|
57
|
+
export class R {
|
|
58
|
+
private constructor() {}
|
|
59
|
+
static readonly usersFileRepository = new UsersFileRepository()
|
|
60
|
+
static async init() { await R.usersFileRepository.init() }
|
|
61
|
+
static async terminate() { await R.usersFileRepository.terminate() }
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Generalizations by protocol, not by feature
|
|
66
|
+
|
|
67
|
+
xf-fs deliberately exposes **one** `FileRepository` covering the
|
|
68
|
+
entire local-filesystem protocol (read, write, list, watch, stream,
|
|
69
|
+
temp). Variants subclass `FileRepository` only when they add a
|
|
70
|
+
**cross-cutting policy** over the same protocol:
|
|
71
|
+
|
|
72
|
+
- `CachedFileRepository` adds *caching* (still the same protocol).
|
|
73
|
+
- `AuditedFileRepository` adds *observability* (still the same
|
|
74
|
+
protocol).
|
|
75
|
+
|
|
76
|
+
Other protocols of access (S3, FTP, in-memory virtual FS) would land
|
|
77
|
+
in their own packages (`@xfcfam/xf-fs-s3`, `@xfcfam/xf-fs-memory`, …)
|
|
78
|
+
exposing their own `FileRepository` subclass — the consumer picks
|
|
79
|
+
the implementation that matches its environment, the API stays
|
|
80
|
+
uniform.
|
|
81
|
+
|
|
82
|
+
## Resource lifecycle
|
|
83
|
+
|
|
84
|
+
`Watcher` and `TempFile` are Transfers that wrap an OS handle. The
|
|
85
|
+
caller owns them and should call `.close()` when done. As a safety
|
|
86
|
+
net, `FileRepository.terminate()` closes every still-active watcher
|
|
87
|
+
and deletes every still-open temp file — so even a careless caller
|
|
88
|
+
won't leak handles on shutdown.
|
|
89
|
+
|
|
90
|
+
## Error model
|
|
91
|
+
|
|
92
|
+
Three filesystem errors are translated into typed XF Exceptions
|
|
93
|
+
because they have **clear domain meaning**:
|
|
94
|
+
|
|
95
|
+
- `FileNotFoundException` ← `ENOENT`
|
|
96
|
+
- `FileAccessDeniedException` ← `EACCES` / `EPERM`
|
|
97
|
+
- `DirectoryNotEmptyException` ← `ENOTEMPTY`
|
|
98
|
+
|
|
99
|
+
Anything else propagates as the native `Error` raised by `node:fs`.
|
|
100
|
+
This is consistent with the XF doctrine: native runtime exceptions
|
|
101
|
+
are well-formed transfer vehicles (like `String`, `Date`, `Promise`),
|
|
102
|
+
and a custom Exception is justified only when it represents a
|
|
103
|
+
concept of the artefact's domain.
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@xfcfam/xf-fs` — local-filesystem Access Layer Generalization for
|
|
3
|
+
* the XF Architecture Model (CFAM).
|
|
4
|
+
*
|
|
5
|
+
* Exposes:
|
|
6
|
+
*
|
|
7
|
+
* - **{@link FileRepository}** — the canonical Generalization. Wraps
|
|
8
|
+
* `node:fs` and exposes a single coherent API over the local
|
|
9
|
+
* filesystem protocol: text & byte I/O, directory traversal,
|
|
10
|
+
* streaming reads/writes, change watching, and temp-file allocation.
|
|
11
|
+
* Concrete components extend it for their domain.
|
|
12
|
+
*
|
|
13
|
+
* - **{@link CachedFileRepository}** — same protocol; adds an
|
|
14
|
+
* in-memory cache over `read`/`readBytes` with write-through
|
|
15
|
+
* invalidation.
|
|
16
|
+
*
|
|
17
|
+
* - **{@link AuditedFileRepository}** — same protocol; adds
|
|
18
|
+
* overridable `onRead` / `onWrite` / `onError` / … hooks for audit
|
|
19
|
+
* logging, metrics, and tracing.
|
|
20
|
+
*
|
|
21
|
+
* - **Transfers**: {@link FileEntry}, {@link FileStat},
|
|
22
|
+
* {@link WatchEvent}, {@link Watcher}, {@link TempFile}.
|
|
23
|
+
*
|
|
24
|
+
* - **Exceptions**: {@link FileNotFoundException},
|
|
25
|
+
* {@link FileAccessDeniedException},
|
|
26
|
+
* {@link DirectoryNotEmptyException}.
|
|
27
|
+
*
|
|
28
|
+
* - **Utilities**: {@link PathUtils}, {@link EncodingUtils}.
|
|
29
|
+
*
|
|
30
|
+
* See https://xfcfam.org for the full XF specification.
|
|
31
|
+
*/
|
|
32
|
+
export { FileRepository } from './src/repository/general/FileRepository.js';
|
|
33
|
+
export type { FileOptions } from './src/repository/general/FileRepository.js';
|
|
34
|
+
export { CachedFileRepository } from './src/repository/general/CachedFileRepository.js';
|
|
35
|
+
export { AuditedFileRepository } from './src/repository/general/AuditedFileRepository.js';
|
|
36
|
+
export type { FileOperation } from './src/repository/general/AuditedFileRepository.js';
|
|
37
|
+
export type { FileEntry } from './src/repository/transfers/FileEntry.js';
|
|
38
|
+
export type { FileStat } from './src/repository/transfers/FileStat.js';
|
|
39
|
+
export type { WatchEvent, WatchEventKind } from './src/repository/transfers/WatchEvent.js';
|
|
40
|
+
export type { Watcher } from './src/repository/transfers/Watcher.js';
|
|
41
|
+
export type { TempFile } from './src/repository/transfers/TempFile.js';
|
|
42
|
+
export { FileNotFoundException } from './src/repository/transfers/FileNotFoundException.js';
|
|
43
|
+
export { FileAccessDeniedException } from './src/repository/transfers/FileAccessDeniedException.js';
|
|
44
|
+
export { DirectoryNotEmptyException } from './src/repository/transfers/DirectoryNotEmptyException.js';
|
|
45
|
+
export { PathUtils } from './src/repository/utils/PathUtils.js';
|
|
46
|
+
export { EncodingUtils } from './src/repository/utils/EncodingUtils.js';
|
|
47
|
+
export type { Encoding } from './src/repository/utils/EncodingUtils.js';
|
|
48
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAA;AAC3E,YAAY,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAA;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAA;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAA;AACzF,YAAY,EAAE,aAAa,EAAE,MAAM,mDAAmD,CAAA;AAGtF,YAAY,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,YAAY,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAA;AACtE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAA;AAC1F,YAAY,EAAE,OAAO,EAAE,MAAM,uCAAuC,CAAA;AACpE,YAAY,EAAE,QAAQ,EAAE,MAAM,wCAAwC,CAAA;AAGtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qDAAqD,CAAA;AAC3F,OAAO,EAAE,yBAAyB,EAAE,MAAM,yDAAyD,CAAA;AACnG,OAAO,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAA;AAGrG,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA;AACvE,YAAY,EAAE,QAAQ,EAAE,MAAM,yCAAyC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@xfcfam/xf-fs` — local-filesystem Access Layer Generalization for
|
|
3
|
+
* the XF Architecture Model (CFAM).
|
|
4
|
+
*
|
|
5
|
+
* Exposes:
|
|
6
|
+
*
|
|
7
|
+
* - **{@link FileRepository}** — the canonical Generalization. Wraps
|
|
8
|
+
* `node:fs` and exposes a single coherent API over the local
|
|
9
|
+
* filesystem protocol: text & byte I/O, directory traversal,
|
|
10
|
+
* streaming reads/writes, change watching, and temp-file allocation.
|
|
11
|
+
* Concrete components extend it for their domain.
|
|
12
|
+
*
|
|
13
|
+
* - **{@link CachedFileRepository}** — same protocol; adds an
|
|
14
|
+
* in-memory cache over `read`/`readBytes` with write-through
|
|
15
|
+
* invalidation.
|
|
16
|
+
*
|
|
17
|
+
* - **{@link AuditedFileRepository}** — same protocol; adds
|
|
18
|
+
* overridable `onRead` / `onWrite` / `onError` / … hooks for audit
|
|
19
|
+
* logging, metrics, and tracing.
|
|
20
|
+
*
|
|
21
|
+
* - **Transfers**: {@link FileEntry}, {@link FileStat},
|
|
22
|
+
* {@link WatchEvent}, {@link Watcher}, {@link TempFile}.
|
|
23
|
+
*
|
|
24
|
+
* - **Exceptions**: {@link FileNotFoundException},
|
|
25
|
+
* {@link FileAccessDeniedException},
|
|
26
|
+
* {@link DirectoryNotEmptyException}.
|
|
27
|
+
*
|
|
28
|
+
* - **Utilities**: {@link PathUtils}, {@link EncodingUtils}.
|
|
29
|
+
*
|
|
30
|
+
* See https://xfcfam.org for the full XF specification.
|
|
31
|
+
*/
|
|
32
|
+
// ── Access ────────────────────────────────────────────────
|
|
33
|
+
export { FileRepository } from './src/repository/general/FileRepository.js';
|
|
34
|
+
export { CachedFileRepository } from './src/repository/general/CachedFileRepository.js';
|
|
35
|
+
export { AuditedFileRepository } from './src/repository/general/AuditedFileRepository.js';
|
|
36
|
+
// Exceptions
|
|
37
|
+
export { FileNotFoundException } from './src/repository/transfers/FileNotFoundException.js';
|
|
38
|
+
export { FileAccessDeniedException } from './src/repository/transfers/FileAccessDeniedException.js';
|
|
39
|
+
export { DirectoryNotEmptyException } from './src/repository/transfers/DirectoryNotEmptyException.js';
|
|
40
|
+
// Utilities
|
|
41
|
+
export { PathUtils } from './src/repository/utils/PathUtils.js';
|
|
42
|
+
export { EncodingUtils } from './src/repository/utils/EncodingUtils.js';
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,6DAA6D;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAA;AAE3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAA;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,mDAAmD,CAAA;AAUzF,aAAa;AACb,OAAO,EAAE,qBAAqB,EAAE,MAAM,qDAAqD,CAAA;AAC3F,OAAO,EAAE,yBAAyB,EAAE,MAAM,yDAAyD,CAAA;AACnG,OAAO,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAA;AAErG,YAAY;AACZ,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `A` is the canonical injection of the Interaction Layer. `@xfcfam/xf-fs`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `A` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `A` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export declare class A {
|
|
13
|
+
private constructor();
|
|
14
|
+
static init(): Promise<void>;
|
|
15
|
+
static terminate(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=A.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"A.d.ts","sourceRoot":"","sources":["../../../src/api/A.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,qBAAa,CAAC;IACZ,OAAO;WACM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;WACrB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CACxC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `A` is the canonical injection of the Interaction Layer. `@xfcfam/xf-fs`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `A` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `A` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export class A {
|
|
13
|
+
constructor() { }
|
|
14
|
+
static async init() { }
|
|
15
|
+
static async terminate() { }
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"A.js","sourceRoot":"","sources":["../../../src/api/A.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,CAAC;IACZ,gBAAuB,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAmB,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,SAAS,KAAmB,CAAC;CAC3C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `B` is the canonical injection of the Business Layer. `@xfcfam/xf-fs`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `B` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `B` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export declare class B {
|
|
13
|
+
private constructor();
|
|
14
|
+
static init(): Promise<void>;
|
|
15
|
+
static terminate(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=B.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"B.d.ts","sourceRoot":"","sources":["../../../src/business/B.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,qBAAa,CAAC;IACZ,OAAO;WACM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;WACrB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CACxC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `B` is the canonical injection of the Business Layer. `@xfcfam/xf-fs`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `B` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `B` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export class B {
|
|
13
|
+
constructor() { }
|
|
14
|
+
static async init() { }
|
|
15
|
+
static async terminate() { }
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"B.js","sourceRoot":"","sources":["../../../src/business/B.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,CAAC;IACZ,gBAAuB,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAmB,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,SAAS,KAAmB,CAAC;CAC3C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `R` is the canonical injection of the Access Layer. `@xfcfam/xf-fs`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `R` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `R` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export declare class R {
|
|
13
|
+
private constructor();
|
|
14
|
+
static init(): Promise<void>;
|
|
15
|
+
static terminate(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=R.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"R.d.ts","sourceRoot":"","sources":["../../../src/repository/R.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,qBAAa,CAAC;IACZ,OAAO;WACM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;WACrB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CACxC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `R` is the canonical injection of the Access Layer. `@xfcfam/xf-fs`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `R` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `R` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export class R {
|
|
13
|
+
constructor() { }
|
|
14
|
+
static async init() { }
|
|
15
|
+
static async terminate() { }
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"R.js","sourceRoot":"","sources":["../../../src/repository/R.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,CAAC;IACZ,gBAAuB,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAmB,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,SAAS,KAAmB,CAAC;CAC3C"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { FileRepository } from './FileRepository.js';
|
|
2
|
+
import type { FileEntry } from '../structs/FileEntry.js';
|
|
3
|
+
import type { FileStat } from '../structs/FileStat.js';
|
|
4
|
+
import type { Watcher } from '../structs/Watcher.js';
|
|
5
|
+
import type { TempFile } from '../structs/TempFile.js';
|
|
6
|
+
import type { WatchEvent } from '../structs/WatchEvent.js';
|
|
7
|
+
/**
|
|
8
|
+
* Canonical operations of a `FileRepository`, used as the discriminator
|
|
9
|
+
* passed to the {@link AuditedFileRepository.onError} hook so a single
|
|
10
|
+
* handler can branch on the operation that failed.
|
|
11
|
+
*/
|
|
12
|
+
export type FileOperation = 'read' | 'readBytes' | 'write' | 'append' | 'delete' | 'exists' | 'stat' | 'list' | 'walk' | 'mkdir' | 'rmdir' | 'readStream' | 'writeStream' | 'watch' | 'tempFile';
|
|
13
|
+
/**
|
|
14
|
+
* Generalization for Access Layer components that need to observe
|
|
15
|
+
* every filesystem operation — for audit logs, metrics, security
|
|
16
|
+
* tracing, or invalidation of higher-level caches.
|
|
17
|
+
*
|
|
18
|
+
* Same protocol as {@link FileRepository}; this subclass adds a
|
|
19
|
+
* cross-cutting **observability policy**. Every public method is
|
|
20
|
+
* intercepted: on success the matching `onX` hook is invoked with
|
|
21
|
+
* the operation's path and result summary; on failure `onError` is
|
|
22
|
+
* invoked with the operation name, path, and the (possibly
|
|
23
|
+
* translated) error before it is re-thrown.
|
|
24
|
+
*
|
|
25
|
+
* Hooks default to no-ops. Subclasses override only the ones they
|
|
26
|
+
* care about. They may be async — the interceptor `await`s them, so
|
|
27
|
+
* a hook can hit a remote logger or block briefly without losing
|
|
28
|
+
* events.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { AuditedFileRepository } from '@xfarch/xf-fs'
|
|
33
|
+
*
|
|
34
|
+
* export class AuditedConfigRepository extends AuditedFileRepository {
|
|
35
|
+
* constructor() { super({ rootPath: '/etc/app' }) }
|
|
36
|
+
*
|
|
37
|
+
* override async onWrite(path: string, sizeBytes: number) {
|
|
38
|
+
* await this.appendAuditLog(`WRITE ${path} ${sizeBytes}B`)
|
|
39
|
+
* }
|
|
40
|
+
* override async onError(op: FileOperation, path: string, err: unknown) {
|
|
41
|
+
* await this.appendAuditLog(`${op.toUpperCase()} FAIL ${path}: ${String(err)}`)
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* private async appendAuditLog(line: string) { ... }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare abstract class AuditedFileRepository extends FileRepository {
|
|
49
|
+
protected onRead(_path: string, _sizeBytes: number): Promise<void>;
|
|
50
|
+
protected onReadBytes(_path: string, _sizeBytes: number): Promise<void>;
|
|
51
|
+
protected onWrite(_path: string, _sizeBytes: number): Promise<void>;
|
|
52
|
+
protected onAppend(_path: string, _sizeBytes: number): Promise<void>;
|
|
53
|
+
protected onDelete(_path: string): Promise<void>;
|
|
54
|
+
protected onExists(_path: string, _exists: boolean): Promise<void>;
|
|
55
|
+
protected onStat(_path: string, _stat: FileStat): Promise<void>;
|
|
56
|
+
protected onList(_path: string, _entries: readonly FileEntry[]): Promise<void>;
|
|
57
|
+
protected onWalk(_path: string, _entries: readonly FileEntry[]): Promise<void>;
|
|
58
|
+
protected onMkdir(_path: string, _recursive: boolean): Promise<void>;
|
|
59
|
+
protected onRmdir(_path: string, _recursive: boolean): Promise<void>;
|
|
60
|
+
protected onReadStream(_path: string): void;
|
|
61
|
+
protected onWriteStream(_path: string): void;
|
|
62
|
+
protected onWatch(_path: string, _watcher: Watcher): Promise<void>;
|
|
63
|
+
protected onTempFile(_tempFile: TempFile): Promise<void>;
|
|
64
|
+
protected onError(_operation: FileOperation, _path: string, _error: unknown): Promise<void>;
|
|
65
|
+
read(path: string): Promise<string>;
|
|
66
|
+
readBytes(path: string): Promise<Uint8Array>;
|
|
67
|
+
write(path: string, content: string | Uint8Array): Promise<void>;
|
|
68
|
+
append(path: string, content: string | Uint8Array): Promise<void>;
|
|
69
|
+
delete(path: string): Promise<void>;
|
|
70
|
+
exists(path: string): Promise<boolean>;
|
|
71
|
+
stat(path: string): Promise<FileStat>;
|
|
72
|
+
list(path: string): Promise<FileEntry[]>;
|
|
73
|
+
walk(path: string): Promise<FileEntry[]>;
|
|
74
|
+
mkdir(path: string, options?: {
|
|
75
|
+
recursive?: boolean;
|
|
76
|
+
}): Promise<void>;
|
|
77
|
+
rmdir(path: string, options?: {
|
|
78
|
+
recursive?: boolean;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
readStream(path: string): ReadableStream<Uint8Array>;
|
|
81
|
+
writeStream(path: string): WritableStream<Uint8Array>;
|
|
82
|
+
watch(path: string, callback: (event: WatchEvent) => void): Promise<Watcher>;
|
|
83
|
+
tempFile(prefix?: string): Promise<TempFile>;
|
|
84
|
+
private static sizeOf;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=AuditedFileRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuditedFileRepository.d.ts","sourceRoot":"","sources":["../../../../src/repository/base/AuditedFileRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { FileRepository } from './FileRepository.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generalization for Access Layer components that need to observe
|
|
4
|
+
* every filesystem operation — for audit logs, metrics, security
|
|
5
|
+
* tracing, or invalidation of higher-level caches.
|
|
6
|
+
*
|
|
7
|
+
* Same protocol as {@link FileRepository}; this subclass adds a
|
|
8
|
+
* cross-cutting **observability policy**. Every public method is
|
|
9
|
+
* intercepted: on success the matching `onX` hook is invoked with
|
|
10
|
+
* the operation's path and result summary; on failure `onError` is
|
|
11
|
+
* invoked with the operation name, path, and the (possibly
|
|
12
|
+
* translated) error before it is re-thrown.
|
|
13
|
+
*
|
|
14
|
+
* Hooks default to no-ops. Subclasses override only the ones they
|
|
15
|
+
* care about. They may be async — the interceptor `await`s them, so
|
|
16
|
+
* a hook can hit a remote logger or block briefly without losing
|
|
17
|
+
* events.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { AuditedFileRepository } from '@xfarch/xf-fs'
|
|
22
|
+
*
|
|
23
|
+
* export class AuditedConfigRepository extends AuditedFileRepository {
|
|
24
|
+
* constructor() { super({ rootPath: '/etc/app' }) }
|
|
25
|
+
*
|
|
26
|
+
* override async onWrite(path: string, sizeBytes: number) {
|
|
27
|
+
* await this.appendAuditLog(`WRITE ${path} ${sizeBytes}B`)
|
|
28
|
+
* }
|
|
29
|
+
* override async onError(op: FileOperation, path: string, err: unknown) {
|
|
30
|
+
* await this.appendAuditLog(`${op.toUpperCase()} FAIL ${path}: ${String(err)}`)
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* private async appendAuditLog(line: string) { ... }
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export class AuditedFileRepository extends FileRepository {
|
|
38
|
+
// ─── Hooks (overridable; defaults are no-ops) ─────────────
|
|
39
|
+
async onRead(_path, _sizeBytes) { }
|
|
40
|
+
async onReadBytes(_path, _sizeBytes) { }
|
|
41
|
+
async onWrite(_path, _sizeBytes) { }
|
|
42
|
+
async onAppend(_path, _sizeBytes) { }
|
|
43
|
+
async onDelete(_path) { }
|
|
44
|
+
async onExists(_path, _exists) { }
|
|
45
|
+
async onStat(_path, _stat) { }
|
|
46
|
+
async onList(_path, _entries) { }
|
|
47
|
+
async onWalk(_path, _entries) { }
|
|
48
|
+
async onMkdir(_path, _recursive) { }
|
|
49
|
+
async onRmdir(_path, _recursive) { }
|
|
50
|
+
onReadStream(_path) { }
|
|
51
|
+
onWriteStream(_path) { }
|
|
52
|
+
async onWatch(_path, _watcher) { }
|
|
53
|
+
async onTempFile(_tempFile) { }
|
|
54
|
+
async onError(_operation, _path, _error) { }
|
|
55
|
+
// ─── Intercepted operations ───────────────────────────────
|
|
56
|
+
async read(path) {
|
|
57
|
+
try {
|
|
58
|
+
const content = await super.read(path);
|
|
59
|
+
await this.onRead(this.resolve(path), Buffer.byteLength(content, 'utf-8'));
|
|
60
|
+
return content;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
await this.onError('read', this.resolve(path), err);
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async readBytes(path) {
|
|
68
|
+
try {
|
|
69
|
+
const bytes = await super.readBytes(path);
|
|
70
|
+
await this.onReadBytes(this.resolve(path), bytes.byteLength);
|
|
71
|
+
return bytes;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
await this.onError('readBytes', this.resolve(path), err);
|
|
75
|
+
throw err;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async write(path, content) {
|
|
79
|
+
try {
|
|
80
|
+
await super.write(path, content);
|
|
81
|
+
await this.onWrite(this.resolve(path), AuditedFileRepository.sizeOf(content));
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
await this.onError('write', this.resolve(path), err);
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async append(path, content) {
|
|
89
|
+
try {
|
|
90
|
+
await super.append(path, content);
|
|
91
|
+
await this.onAppend(this.resolve(path), AuditedFileRepository.sizeOf(content));
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
await this.onError('append', this.resolve(path), err);
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async delete(path) {
|
|
99
|
+
try {
|
|
100
|
+
await super.delete(path);
|
|
101
|
+
await this.onDelete(this.resolve(path));
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
await this.onError('delete', this.resolve(path), err);
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async exists(path) {
|
|
109
|
+
const result = await super.exists(path);
|
|
110
|
+
await this.onExists(this.resolve(path), result);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
async stat(path) {
|
|
114
|
+
try {
|
|
115
|
+
const s = await super.stat(path);
|
|
116
|
+
await this.onStat(s.path, s);
|
|
117
|
+
return s;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
await this.onError('stat', this.resolve(path), err);
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async list(path) {
|
|
125
|
+
try {
|
|
126
|
+
const entries = await super.list(path);
|
|
127
|
+
await this.onList(this.resolve(path), entries);
|
|
128
|
+
return entries;
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
await this.onError('list', this.resolve(path), err);
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async walk(path) {
|
|
136
|
+
try {
|
|
137
|
+
const entries = await super.walk(path);
|
|
138
|
+
await this.onWalk(this.resolve(path), entries);
|
|
139
|
+
return entries;
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
await this.onError('walk', this.resolve(path), err);
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async mkdir(path, options = {}) {
|
|
147
|
+
try {
|
|
148
|
+
await super.mkdir(path, options);
|
|
149
|
+
await this.onMkdir(this.resolve(path), options.recursive ?? false);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
await this.onError('mkdir', this.resolve(path), err);
|
|
153
|
+
throw err;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async rmdir(path, options = {}) {
|
|
157
|
+
try {
|
|
158
|
+
await super.rmdir(path, options);
|
|
159
|
+
await this.onRmdir(this.resolve(path), options.recursive ?? false);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
await this.onError('rmdir', this.resolve(path), err);
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
readStream(path) {
|
|
167
|
+
const stream = super.readStream(path);
|
|
168
|
+
this.onReadStream(this.resolve(path));
|
|
169
|
+
return stream;
|
|
170
|
+
}
|
|
171
|
+
writeStream(path) {
|
|
172
|
+
const stream = super.writeStream(path);
|
|
173
|
+
this.onWriteStream(this.resolve(path));
|
|
174
|
+
return stream;
|
|
175
|
+
}
|
|
176
|
+
async watch(path, callback) {
|
|
177
|
+
try {
|
|
178
|
+
const watcher = await super.watch(path, callback);
|
|
179
|
+
await this.onWatch(this.resolve(path), watcher);
|
|
180
|
+
return watcher;
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
await this.onError('watch', this.resolve(path), err);
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async tempFile(prefix) {
|
|
188
|
+
try {
|
|
189
|
+
const handle = await super.tempFile(prefix);
|
|
190
|
+
await this.onTempFile(handle);
|
|
191
|
+
return handle;
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
await this.onError('tempFile', '', err);
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
static sizeOf(content) {
|
|
199
|
+
return typeof content === 'string' ? Buffer.byteLength(content, 'utf-8') : content.byteLength;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=AuditedFileRepository.js.map
|