@poe-platform/safe-fs 0.1.807 → 0.1.809
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1
- package/dist/safe-fs/contracts/filesystem.d.ts +20 -1
- package/dist/safe-fs/core.d.ts +3 -1
- package/dist/safe-fs/core.js +3 -1
- package/dist/safe-fs/fs/capabilities.d.ts +1 -1
- package/dist/safe-fs/fs/capabilities.js +9 -5
- package/dist/safe-fs/fs/devices/index.d.ts +3 -2
- package/dist/safe-fs/fs/devices/index.js +53 -9
- package/dist/safe-fs/fs/memory/atomic-view.d.ts +1 -0
- package/dist/safe-fs/fs/memory/index.d.ts +6 -3
- package/dist/safe-fs/fs/memory/index.js +101 -13
- package/dist/safe-fs/fs/mount/index.d.ts +4 -2
- package/dist/safe-fs/fs/mount/index.js +169 -10
- package/dist/safe-fs/fs/overlay/index.d.ts +3 -0
- package/dist/safe-fs/fs/overlay/index.js +177 -59
- package/dist/safe-fs/fs/overlay/memory-publication.d.ts +1 -0
- package/dist/safe-fs/fs/overlay/memory-publication.js +25 -0
- package/dist/safe-fs/fs/quota/index.js +9 -4
- package/dist/safe-fs/fs/read-file-stream.d.ts +7 -0
- package/dist/safe-fs/fs/read-file-stream.js +103 -0
- package/dist/safe-fs/fs/readonly/index.d.ts +1 -0
- package/dist/safe-fs/fs/readonly/index.js +10 -0
- package/dist/safe-fs/fs/real/index.d.ts +1 -0
- package/dist/safe-fs/fs/real/index.js +29 -0
- package/dist/safe-fs/fs/s3/filesystem.d.ts +2 -1
- package/dist/safe-fs/fs/s3/filesystem.js +55 -16
- package/dist/safe-fs/fs/scoped.js +13 -5
- package/dist/safe-fs/index.d.ts +1 -0
- package/dist/safe-fs/index.js +1 -0
- package/dist/safe-fs/runtime-core.d.ts +1 -0
- package/dist/safe-fs/runtime-core.js +1 -0
- package/dist/safe-fs/storage.d.ts +57 -0
- package/dist/safe-fs/storage.js +364 -0
- package/dist/safe-fs/testing/object-io-metrics.js +1 -0
- package/dist/xml-ast/errors.d.ts +4 -0
- package/dist/xml-ast/errors.js +7 -0
- package/dist/xml-ast/index.d.ts +2 -4
- package/dist/xml-ast/index.js +3 -7
- package/dist/xml-ast/stream.d.ts +31 -0
- package/dist/xml-ast/stream.js +179 -0
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -4,6 +4,17 @@ An asynchronous filesystem interface for memory, rooted host directories, S3, an
|
|
|
4
4
|
|
|
5
5
|
Under `workerd` and `browser` conditions, the root export includes portable filesystems, `S3FileSystem`, and `MockS3Client`. Supply an S3 transport to use object storage without Node globals.
|
|
6
6
|
|
|
7
|
+
The `storage` subpath exposes `PagedStorage` and `IntegerTable` for temporary
|
|
8
|
+
working data. Supply `{fs, cwd, env, signal}` and an optional page-cache count
|
|
9
|
+
(default 64 pages of 16 KiB). Scratch data uses retained positioned handles in
|
|
10
|
+
`env.TMPDIR` or `cwd`, with conditional unlink and cleanup through `close()`.
|
|
11
|
+
Providers must support those capabilities; there is no host fallback. Use an
|
|
12
|
+
external backend for large data because memory filesystems retain spilled bytes.
|
|
13
|
+
`IntegerTable.entries()` replays records in unsigned key order with a fixed
|
|
14
|
+
16-level node stack and batches of at most 128 records. It accepts zero-filled
|
|
15
|
+
caller storage with `allocate`, `read` and `write`; mutation during traversal
|
|
16
|
+
is rejected. Branch pointers use a separate fixed 128-entry cache.
|
|
17
|
+
|
|
7
18
|
## Quickstart
|
|
8
19
|
|
|
9
20
|
Install the standalone package; it does not depend on SafeJS, Safe Bash, or the `poe-code` CLI. These examples use TypeScript in a Node ESM application.
|
|
@@ -79,9 +90,11 @@ cause. Preserve cancellation/control exceptions separately. Cross-family
|
|
|
79
90
|
| Change the namespace | `mkdir`, `rm`, `rename`, `copyFile` |
|
|
80
91
|
| Remove an empty directory | Optional `rmdir`; never a recursive-delete fallback |
|
|
81
92
|
| Links and metadata | Optional `readlink`, `symlink`, `link`, `chmod`, `utimes`, `truncate` |
|
|
93
|
+
| Enumerate directories lazily | Optional `iterateDirectory`; memory, native and paged S3 backends, with mount/overlay/readonly/quota/device/scoped delegation; backend order, no sorted-array fallback |
|
|
82
94
|
| Stream bytes | Optional `readStream`, `writeStream`, using async iterables of byte chunks |
|
|
83
95
|
| Retain an open file | Optional `open` with positioned I/O and synchronization; memory and real backends support `noFollow: true` to atomically refuse a final symlink |
|
|
84
96
|
| Publish immutable objects atomically | Optional `publishFileConditional` with opaque identity/version stats and authoritative compare-and-publish |
|
|
97
|
+
| Publish a checked source set | Optional `publishStagedFileSet` publishes a staged file and retires sibling entries in one backend commit; mount views require all entries on the same supporting backend |
|
|
85
98
|
| Compare backing entries | Optional `compareEntry`, returning `same`, `distinct`, or `unknown` |
|
|
86
99
|
|
|
87
100
|
All required methods must exist, but a backend may reject an operation with `FsError`, such as `EROFS` for a write or `ENOTSUP` for unsupported semantics. Check optional methods and `capabilities` rather than assuming every backend behaves like a local disk. Errors expose `code` and may include `syscall`, `path`, `dest`, and `cause`.
|
|
@@ -124,7 +137,11 @@ For temporary staging that must be cleaned up after its parent moves, adapters
|
|
|
124
137
|
advertising `retainedStagingCleanup` accept `createStagedFile(..., { parent,
|
|
125
138
|
retainCleanup: true })`. Use the returned `staging.cleanup.remove()` and always
|
|
126
139
|
call `staging.cleanup.close()` in `finally`. Memory and its supported wrappers
|
|
127
|
-
retain only the owned staging entries; replacement entries remain protected.
|
|
140
|
+
retain only the owned staging entries; replacement entries remain protected. When
|
|
141
|
+
`atomicStagedFileMutation` is available, `publishStagedFile(..., { preserveIdentity: true,
|
|
142
|
+
parent, destination })` updates an existing file without breaking hardlink aliases.
|
|
143
|
+
Memory and its supported wrappers adopt staged bytes and timestamps while keeping
|
|
144
|
+
the destination identity and mode; unsupported adapters reject this option.
|
|
128
145
|
Overlays backed by stock Memory filesystems support conditional file mutations,
|
|
129
146
|
directory preparation, and confined staging. Publication may omit `ancestors`
|
|
130
147
|
when the destination shares the staging parent; the overlay checks the ancestry
|
|
@@ -295,6 +312,16 @@ Every raw filesystem operation accepts an optional `signal`. Additional fields a
|
|
|
295
312
|
|
|
296
313
|
`access` takes a separate mode bitmask from `ACCESS_MODES`. `chmod` takes a mode, `utimes` takes millisecond timestamps, and `truncate` takes a byte length (default 0). For conditional chmod, check `capabilitiesFor(path, { conditionalChmod: true })` (or `capabilities`) and require `conditionalChmod: true`; supply `parent`, `expected`, and complete root-to-parent `ancestors` together. An optional mutation-free `commitGuard` must return literal `true` synchronously. Memory validates at its metadata commit; mount and supported Memory overlays preserve wrapper ancestry. Real uses its existing externally isolated host-tree boundary and does not prevent races with other processes. Backend limits still apply. Node-shaped bridge methods translate their own options rather than accepting these raw option objects; see the [bridge signatures](src/bridge/filesystem.ts).
|
|
297
314
|
|
|
315
|
+
Overlay copy-up, copy, stream replacement/append and resize transfer retained ranges in 64 KiB chunks through the injected upper's stream writer. Resize streams the retained prefix and bounded zero chunks. Stream writes stage input before publication; cancellation or transfer failure leaves the destination unchanged and cleans staging. Stock Memory overlays transfer staged storage into existing upper inodes, preserving hardlinks without an additional whole-file buffer. `readFile` and `writeFile` remain buffering convenience APIs; use `readStream`/`writeStream` for large payloads. `maxBufferBytes` retains its existing optional total-byte admission semantics, not a working-memory guarantee.
|
|
316
|
+
|
|
317
|
+
A streaming overlay needs an upper with `atomicRename`, `streamingWrite`, retained reads (`retainedRead` plus `openReadFile`), exclusive create, directory mutation, permissions and timestamps. Both layers must provide retained, bounded range reads with stable file and directory identities. For example, inject these host-authorized adapters with `createOverlayFileSystem({ upper: externalSpool, lower: retainedSource })`, then use `overlay.writeStream(path, source)`. The upper must await each consumed chunk, keep transport queues bounded, and preserve ownership until publication. Supply bounded chunks at the input boundary; the overlay cannot undo a payload-wide allocation already made by its caller. Whole-file-only uppers explicitly reject transfer operations with `ENOTSUP`; they do not silently collect streams.
|
|
318
|
+
|
|
319
|
+
For Workers, `externalSpool` must store payloads outside isolate RAM and satisfy that contract, including atomic rename and retained identity. Memory and the S3 mock store payloads in RAM and are not external spools. Stock S3/WebDAV do not provide the atomic rename/retained-read contract needed for an overlay upper; setting a capability flag does not add it. A host must inject a qualified external adapter. On Node, rooted Real adapters provide the streaming overlay configuration under their documented external-tree isolation requirement. Mount and read-only wrappers forward the selected backend's capabilities; quota streaming additionally requires a real streaming-append route. S3's buffered append convenience path reports `streamingAppend: false`.
|
|
320
|
+
|
|
321
|
+
The reusable metadata-only fixture in [`tests/fixtures/bounded-filesystem.ts`](tests/fixtures/bounded-filesystem.ts) generates file ranges and consumes slow writes without storing payload bytes. [`tests/overlay-bounded.test.ts`](tests/overlay-bounded.test.ts) checks fixed transfer sizes, source backpressure, cancellation/error cleanup, byte results, and Memory publication allocation/identity. These deterministic conformance checks do not qualify a deployed Worker backend; follow the [Worker QA plan](../../docs/plans/safe-fs-bounded-overlay-qa.md) for runtime measurements.
|
|
322
|
+
|
|
323
|
+
`readFileStream(fs, path, options)` reads through the caller's native stream or retained range handle without calling `readFile`. It accepts the stream range/window options above, defaults to a 64 KiB read window, and closes owned readers on completion, failure or cancellation. `skipStream: true` selects retained ranges after an already-attempted stream reports `ENOTSUP`. Chunks are borrowed until the iterator advances. Native adapters must honor the requested window and backpressure. Whole-file-only backends receive `ENOTSUP`; use a streaming or retained-range backend for sequential Worker operations. This helper bounds its I/O window, not the consuming command's parser or output capture.
|
|
324
|
+
|
|
298
325
|
`collectBytes(source, { maxBytes, maxMemoryBytes, signal })` snapshots streamed chunks into one growing buffer. `maxBytes` and `maxMemoryBytes` accept explicit `Infinity`. `maxMemoryBytes` limits owned capacity, the current input's full backing buffer, and overlapping allocations during growth; exhaustion throws `EFBIG`. Collection limits are disabled by default on Node, browser, and Worker targets. Each call owns its budget; concurrent collections do not consume one another’s allowance. The returned view may retain geometric spare capacity. Configured budgets cover collection, not caller-retained results, transport buffering, archive decoding, strings, or the rest of the runtime; use streaming APIs and limit concurrent workloads for larger inputs.
|
|
299
326
|
|
|
300
327
|
<details>
|
|
@@ -76,6 +76,8 @@ export interface FileSystemCapabilities {
|
|
|
76
76
|
/** Owned staging serialized within a trusted host; requires external tree isolation. */
|
|
77
77
|
readonly trustedOwnedStaging?: boolean;
|
|
78
78
|
readonly atomicFileStaging?: boolean;
|
|
79
|
+
/** Staged payload publication can atomically preserve an existing inode. */
|
|
80
|
+
readonly atomicStagedFileMutation?: boolean;
|
|
79
81
|
/** Creates a cleanup handle bound to owned staging entries, independent of ancestor paths. */
|
|
80
82
|
readonly retainedStagingCleanup?: boolean;
|
|
81
83
|
readonly retainedStagingWrite?: boolean;
|
|
@@ -85,6 +87,8 @@ export interface FileSystemCapabilities {
|
|
|
85
87
|
readonly synchronousDirectoryValidation?: boolean;
|
|
86
88
|
/** Captures a followed destination path with a synchronous commit validator. */
|
|
87
89
|
readonly synchronousStagingResolution?: boolean;
|
|
90
|
+
/** Also binds the final symlink and its target in the synchronous validator. */
|
|
91
|
+
readonly synchronousFollowedStagingResolution?: boolean;
|
|
88
92
|
/** Invokes commitGuard in the same synchronous section as staged replacement. */
|
|
89
93
|
readonly guardedStagingPublication?: boolean;
|
|
90
94
|
readonly atomicFilePublication?: boolean;
|
|
@@ -127,6 +131,7 @@ export interface CapabilityQueryOptions extends OpenReadFileOptions {
|
|
|
127
131
|
* Omission retains the ordinary per-target query and its acquisition intent. */
|
|
128
132
|
readonly stagingAncestry?: boolean;
|
|
129
133
|
readonly stagingResolution?: boolean;
|
|
134
|
+
readonly followFinalSymlink?: boolean;
|
|
130
135
|
readonly create?: boolean;
|
|
131
136
|
readonly creation?: OpenFileOptions["creation"];
|
|
132
137
|
}
|
|
@@ -231,6 +236,10 @@ export interface FileStagingEntry {
|
|
|
231
236
|
export interface FileResolutionStep extends FileStagingEntry {
|
|
232
237
|
readonly linkTarget?: string;
|
|
233
238
|
}
|
|
239
|
+
export interface PrepareStagingResolutionOptions extends FsOptions {
|
|
240
|
+
/** Requires synchronousFollowedStagingResolution; omission preserves final-entry resolution. */
|
|
241
|
+
readonly followFinalSymlink?: boolean;
|
|
242
|
+
}
|
|
234
243
|
export interface FileStagingResolution {
|
|
235
244
|
readonly path: string;
|
|
236
245
|
readonly parent: FileStat;
|
|
@@ -277,6 +286,11 @@ export interface CreateStagedFileOptions extends FsOptions {
|
|
|
277
286
|
readonly mtimeMs?: number;
|
|
278
287
|
}
|
|
279
288
|
export interface PublishStagedFileOptions extends FsOptions {
|
|
289
|
+
/** Requires atomicStagedFileMutation and an existing regular destination.
|
|
290
|
+
* Atomically adopts staged bytes and timestamps while preserving destination
|
|
291
|
+
* identity, links and mode. Consumes the staged pathname as ordinary publication.
|
|
292
|
+
* Unsupported hosts must reject before effects, never silently rename. */
|
|
293
|
+
readonly preserveIdentity?: boolean;
|
|
280
294
|
readonly ancestors?: readonly FileStagingEntry[];
|
|
281
295
|
/** Trusted, mutation-free validation. Must return literal true synchronously;
|
|
282
296
|
* a promise is not acceptance. Requires guardedStagingPublication. */
|
|
@@ -337,7 +351,7 @@ export interface FileSystem {
|
|
|
337
351
|
* The returned guard checks current identities/search access synchronously;
|
|
338
352
|
* preparation is not validation, and the guard's result is not an async lease. */
|
|
339
353
|
prepareDirectoryAncestry?(ancestors: readonly FileStagingEntry[], options?: FsOptions): Promise<() => true>;
|
|
340
|
-
prepareStagingResolution?(path: string, options?:
|
|
354
|
+
prepareStagingResolution?(path: string, options?: PrepareStagingResolutionOptions): Promise<FileStagingResolution>;
|
|
341
355
|
removeStagedFile?(staging: FileStaging, options?: FsOptions): Promise<void>;
|
|
342
356
|
openReadFile?(path: string, options?: OpenReadFileOptions): Promise<FileReadHandle>;
|
|
343
357
|
openResizeFile?(path: string, options?: OpenResizeFileOptions): Promise<FileResizeHandle>;
|
|
@@ -352,6 +366,11 @@ export interface FileSystem {
|
|
|
352
366
|
lstat(path: string, options?: FsOptions): Promise<FileStat>;
|
|
353
367
|
compareEntry?(path: string, peer: FileSystem, peerPath: string, options?: FsOptions): Promise<EntryComparison>;
|
|
354
368
|
readdir(path: string, options?: ReadDirectoryOptions): Promise<DirectoryEntry[]>;
|
|
369
|
+
/** Optional bounded, lazy directory enumeration in backend-defined order.
|
|
370
|
+
* No complete listing/sort fallback. Consumers must retire the iterator on
|
|
371
|
+
* early exit. Namespace changes may reject or follow the backend's live view;
|
|
372
|
+
* this is not a tree snapshot or a retained path-authority grant. */
|
|
373
|
+
iterateDirectory?(path: string, options?: FsOptions): AsyncIterable<DirectoryEntry>;
|
|
355
374
|
mkdir(path: string, options?: MkdirOptions): Promise<void>;
|
|
356
375
|
rm(path: string, options?: RemoveOptions): Promise<void>;
|
|
357
376
|
/** Atomic final-entry removal; every directory is refused, including raced replacements. */
|
package/dist/safe-fs/core.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export * from "./fs/object-publication/index.js";
|
|
|
8
8
|
export * from "./fs/webdav/index.js";
|
|
9
9
|
export * from "./bridge/index.js";
|
|
10
10
|
export * from "./python/index.js";
|
|
11
|
-
export { parseXml, parseXmlSteps, XmlLimitError } from "./xml.js";
|
|
11
|
+
export { parseXml, parseXmlSteps, parseXmlStream, XmlLimitError } from "./xml.js";
|
|
12
12
|
export type { XmlName, XmlAttribute, XmlContent, XmlElement, XmlLimits } from "./xml.js";
|
|
13
13
|
export * from "./contracts/object.js";
|
|
14
14
|
export { ObjectAuthority } from "./fs/object-authority.js";
|
|
@@ -17,3 +17,5 @@ export * from "./fs/s3/http/index.js";
|
|
|
17
17
|
export { createFileSystem, readConfigRecord, validateFileSystemConfig } from "./config.js";
|
|
18
18
|
export type { FileSystemConfig, FileSystemAdapterDescriptor, FileSystemAdapterRegistry } from "./config.js";
|
|
19
19
|
export { createPortableFileSystemAdapterRegistry } from "./config.portable.js";
|
|
20
|
+
export { readFileStream } from "./fs/read-file-stream.js";
|
|
21
|
+
export { PagedStorage, IntegerTable, type PagedStorageContext } from "./storage.js";
|
package/dist/safe-fs/core.js
CHANGED
|
@@ -8,10 +8,12 @@ export * from "./fs/object-publication/index.js";
|
|
|
8
8
|
export * from "./fs/webdav/index.js";
|
|
9
9
|
export * from "./bridge/index.js";
|
|
10
10
|
export * from "./python/index.js";
|
|
11
|
-
export { parseXml, parseXmlSteps, XmlLimitError } from "./xml.js";
|
|
11
|
+
export { parseXml, parseXmlSteps, parseXmlStream, XmlLimitError } from "./xml.js";
|
|
12
12
|
export * from "./contracts/object.js";
|
|
13
13
|
export { ObjectAuthority } from "./fs/object-authority.js";
|
|
14
14
|
export * from "./fs/s3/index.js";
|
|
15
15
|
export * from "./fs/s3/http/index.js";
|
|
16
16
|
export { createFileSystem, readConfigRecord, validateFileSystemConfig } from "./config.js";
|
|
17
17
|
export { createPortableFileSystemAdapterRegistry } from "./config.portable.js";
|
|
18
|
+
export { readFileStream } from "./fs/read-file-stream.js";
|
|
19
|
+
export { PagedStorage, IntegerTable } from "./storage.js";
|
|
@@ -7,4 +7,4 @@ export declare function openRetainedResizeFile(filesystem: FileSystem, path: str
|
|
|
7
7
|
export declare function readOnlyCapabilities(capabilities: FileSystemCapabilities): FileSystemCapabilities;
|
|
8
8
|
export declare function quotaCapabilities(capabilities: FileSystemCapabilities): FileSystemCapabilities;
|
|
9
9
|
export declare function ownedMutationCapabilities(filesystem: FileSystem, capabilities?: FileSystemCapabilities): FileSystemCapabilities;
|
|
10
|
-
export declare function requireOwnedMutation(filesystem: FileSystem, path: string, capability: "retainedStagingCleanup" | "atomicFilePublication" | "atomicEntryRemoval" | "atomicEntryRemovalReceipt" | "atomicTreeRemoval" | "atomicFileMutation" | "atomicFileStaging" | "guardedStagingPublication" | "atomicStagingAncestry" | "atomicDirectoryMetadata", options: FsOptions, create?: boolean): Promise<void>;
|
|
10
|
+
export declare function requireOwnedMutation(filesystem: FileSystem, path: string, capability: "retainedStagingCleanup" | "atomicFilePublication" | "atomicEntryRemoval" | "atomicEntryRemovalReceipt" | "atomicTreeRemoval" | "atomicFileMutation" | "atomicFileStaging" | "atomicStagedFileMutation" | "guardedStagingPublication" | "atomicStagingAncestry" | "atomicDirectoryMetadata", options: FsOptions, create?: boolean): Promise<void>;
|
|
@@ -132,19 +132,19 @@ export function readOnlyCapabilities(capabilities) {
|
|
|
132
132
|
].filter(name => capabilities[name] !== undefined).map(name => [name, capabilities[name]]));
|
|
133
133
|
return Object.freeze({
|
|
134
134
|
...inspection, retainedStagingCleanup: false, retainedStagingWrite: false, readOnly: true, write: false, append: false, exclusiveCreate: false,
|
|
135
|
-
synchronousStagingResolution: false,
|
|
135
|
+
synchronousStagingResolution: false, synchronousFollowedStagingResolution: false,
|
|
136
136
|
mkdir: false, recursiveMkdir: false, remove: false, removeDirectory: false, recursiveRemove: false,
|
|
137
137
|
rename: false, copy: false, exclusiveCopy: false, truncate: false, streamingAppend: false,
|
|
138
138
|
randomAccessWrite: false, hardlinks: false, permissions: false, conditionalChmod: false, timestamps: false,
|
|
139
|
-
descriptorWriteStream: false, atomicResize: false, retainedResize: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicEntryRemovalReceipt: false, atomicTreeRemoval: false, atomicFileStaging: false, atomicDirectoryMetadata: false, trustedOwnedStaging: false,
|
|
139
|
+
descriptorWriteStream: false, atomicResize: false, retainedResize: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicEntryRemovalReceipt: false, atomicTreeRemoval: false, atomicStagedFileMutation: false, atomicFileStaging: false, atomicDirectoryMetadata: false, trustedOwnedStaging: false,
|
|
140
140
|
atomicFilePublication: false, atomicRename: false, atomicRenameNoReplace: false, streamingWrite: false,
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
export function quotaCapabilities(capabilities) {
|
|
144
|
-
const streamingWrite = requireCapabilities(capabilities.write, capabilities.append, !capabilities.readOnly);
|
|
145
|
-
const streamingAppend = requireCapabilities(capabilities.append, !capabilities.readOnly);
|
|
144
|
+
const streamingWrite = requireCapabilities(capabilities.write, capabilities.append, capabilities.streamingAppend, !capabilities.readOnly);
|
|
145
|
+
const streamingAppend = requireCapabilities(capabilities.append, capabilities.streamingAppend, !capabilities.readOnly);
|
|
146
146
|
const { streamingWrite: ignoredWrite, streamingAppend: ignoredAppend, ...rest } = capabilities;
|
|
147
|
-
return Object.freeze({ ...rest, synchronousStagingResolution: false, retainedStagingCleanup: false, retainedStagingWrite: false, atomicStagingAncestry: false, synchronousDirectoryValidation: false, guardedStagingPublication: false, atomicFilePublication: false, descriptorWriteStream: false, atomicResize: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicEntryRemovalReceipt: false, atomicFileStaging: false, atomicDirectoryMetadata: false, trustedOwnedStaging: false,
|
|
147
|
+
return Object.freeze({ ...rest, synchronousStagingResolution: false, synchronousFollowedStagingResolution: false, retainedStagingCleanup: false, retainedStagingWrite: false, atomicStagingAncestry: false, synchronousDirectoryValidation: false, guardedStagingPublication: false, atomicFilePublication: false, descriptorWriteStream: false, atomicResize: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicEntryRemovalReceipt: false, atomicStagedFileMutation: false, atomicFileStaging: false, atomicDirectoryMetadata: false, trustedOwnedStaging: false,
|
|
148
148
|
...(streamingWrite === undefined ? {} : { streamingWrite }),
|
|
149
149
|
...(streamingAppend === undefined ? {} : { streamingAppend }),
|
|
150
150
|
});
|
|
@@ -167,6 +167,8 @@ export function ownedMutationCapabilities(filesystem, capabilities = filesystem.
|
|
|
167
167
|
if (capabilities.atomicFileStaging === true && (capabilities.readOnly === true
|
|
168
168
|
|| typeof filesystem.createStagedFile !== "function" || typeof filesystem.publishStagedFile !== "function" || typeof filesystem.removeStagedFile !== "function"))
|
|
169
169
|
(unavailable ??= {}).atomicFileStaging = false;
|
|
170
|
+
if (capabilities.atomicStagedFileMutation === true && (capabilities.atomicFileStaging !== true || unavailable?.atomicFileStaging === false))
|
|
171
|
+
(unavailable ??= {}).atomicStagedFileMutation = false;
|
|
170
172
|
if (capabilities.retainedStagingCleanup === true && (capabilities.atomicFileStaging !== true || unavailable?.atomicFileStaging === false))
|
|
171
173
|
(unavailable ??= {}).retainedStagingCleanup = false;
|
|
172
174
|
if (capabilities.retainedStagingWrite === true && (capabilities.retainedStagingCleanup !== true || unavailable?.retainedStagingCleanup === false))
|
|
@@ -177,6 +179,8 @@ export function ownedMutationCapabilities(filesystem, capabilities = filesystem.
|
|
|
177
179
|
(unavailable ??= {}).synchronousDirectoryValidation = false;
|
|
178
180
|
if (capabilities.synchronousStagingResolution === true && (capabilities.readOnly === true || typeof filesystem.prepareStagingResolution !== "function"))
|
|
179
181
|
(unavailable ??= {}).synchronousStagingResolution = false;
|
|
182
|
+
if (capabilities.synchronousFollowedStagingResolution === true && (capabilities.synchronousStagingResolution !== true || unavailable?.synchronousStagingResolution === false))
|
|
183
|
+
(unavailable ??= {}).synchronousFollowedStagingResolution = false;
|
|
180
184
|
if (capabilities.guardedStagingPublication === true && (capabilities.atomicFileStaging !== true || unavailable?.atomicFileStaging === false))
|
|
181
185
|
(unavailable ??= {}).guardedStagingPublication = false;
|
|
182
186
|
if (capabilities.atomicDirectoryMetadata === true && (capabilities.readOnly === true || typeof filesystem.prepareDirectory !== "function"))
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FileDescriptor, OpenFileOptions } from "../../contracts/descriptor.js";
|
|
2
|
-
import type { AppendFileOptions, CapabilityQueryOptions, CopyFileOptions, DirectoryEntry, FileReadHandle, FileResizeHandle, FileResizeOperation, FileResizeOptions, FileStat, FileSystem, OpenReadFileOptions, OpenResizeFileOptions, FileSystemCapabilities, FsOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, WriteFileOptions, ConditionalFilePublicationOptions, ConditionalWriteFileOptions, ConditionalRemoveFileOptions, ConditionalRemoveEntryOptions, ConditionalRemoveEntryReceiptOptions, CreateStagedFileOptions, FileStaging, FileStagingEntry, FileStagingResolution, PublishStagedFileOptions, PublishStagedFileSetOptions, PrepareDirectoryOptions, StagedFileContent } from "../../contracts/filesystem.js";
|
|
2
|
+
import type { AppendFileOptions, CapabilityQueryOptions, CopyFileOptions, DirectoryEntry, FileReadHandle, FileResizeHandle, FileResizeOperation, FileResizeOptions, FileStat, FileSystem, OpenReadFileOptions, OpenResizeFileOptions, FileSystemCapabilities, FsOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, WriteFileOptions, ConditionalFilePublicationOptions, ConditionalWriteFileOptions, ConditionalRemoveFileOptions, ConditionalRemoveEntryOptions, ConditionalRemoveEntryReceiptOptions, CreateStagedFileOptions, FileStaging, FileStagingEntry, FileStagingResolution, PrepareStagingResolutionOptions, PublishStagedFileOptions, PublishStagedFileSetOptions, PrepareDirectoryOptions, StagedFileContent } from "../../contracts/filesystem.js";
|
|
3
3
|
import type { ByteSource } from "../../contracts/io.js";
|
|
4
4
|
import { type EntryLocation } from "../mount/comparison.js";
|
|
5
5
|
import { pathNamespace } from "../path-namespace.js";
|
|
@@ -25,6 +25,7 @@ export declare class DeviceFileSystem implements FileSystem {
|
|
|
25
25
|
writeFile(path: string, data: Uint8Array, options?: WriteFileOptions): Promise<void>;
|
|
26
26
|
appendFile(path: string, data: Uint8Array, options?: AppendFileOptions): Promise<void>;
|
|
27
27
|
writeStream(path: string, source: ByteSource, options?: WriteFileOptions): Promise<void>;
|
|
28
|
+
iterateDirectory(path: string, options?: FsOptions): AsyncIterable<DirectoryEntry>;
|
|
28
29
|
readdir(path: string, options?: ReadDirectoryOptions): Promise<DirectoryEntry[]>;
|
|
29
30
|
mkdir(path: string, options?: MkdirOptions): Promise<void>;
|
|
30
31
|
rm(path: string, options?: RemoveOptions): Promise<void>;
|
|
@@ -42,7 +43,7 @@ export declare class DeviceFileSystem implements FileSystem {
|
|
|
42
43
|
publishStagedFile(staging: FileStaging, destination: string, options: PublishStagedFileOptions): Promise<void>;
|
|
43
44
|
publishStagedFileSet(receipt: FileStaging, destination: string, supplied: PublishStagedFileSetOptions): Promise<FileStat>;
|
|
44
45
|
prepareDirectoryAncestry(ancestors: readonly FileStagingEntry[], options?: FsOptions): Promise<() => true>;
|
|
45
|
-
prepareStagingResolution(path: string, options?:
|
|
46
|
+
prepareStagingResolution(path: string, options?: PrepareStagingResolutionOptions): Promise<FileStagingResolution>;
|
|
46
47
|
removeStagedFile(staging: FileStaging, options?: FsOptions): Promise<void>;
|
|
47
48
|
prepareDirectory(path: string, options: PrepareDirectoryOptions): Promise<FileStat>;
|
|
48
49
|
rename(source: string, destination: string, options?: RenameOptions): Promise<void>;
|
|
@@ -13,14 +13,14 @@ import { openFileDescriptor } from "../descriptor.js";
|
|
|
13
13
|
import { normalizePath } from "../../contracts/virtual-path.js";
|
|
14
14
|
const views = new WeakMap();
|
|
15
15
|
const deviceCapabilities = Object.freeze({
|
|
16
|
-
synchronousStagingResolution: false,
|
|
16
|
+
synchronousStagingResolution: false, synchronousFollowedStagingResolution: false,
|
|
17
17
|
readOnly: false, read: true, stat: true, realpath: true, access: true, readdir: false,
|
|
18
18
|
write: true, append: true, exclusiveCreate: true, streamingRead: true, retainedRead: true,
|
|
19
19
|
streamingWrite: true, streamingAppend: true, independentWriteStreams: true, copy: true, exclusiveCopy: true,
|
|
20
20
|
remove: false, removeDirectory: false, recursiveRemove: false, rename: false,
|
|
21
21
|
mkdir: false, recursiveMkdir: false, symlinks: false, hardlinks: false, readlink: false,
|
|
22
22
|
permissions: false, timestamps: false, truncate: false, randomAccessWrite: false,
|
|
23
|
-
open: true, retainedStagingCleanup: false, atomicFilePublication: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicEntryRemovalReceipt: false, atomicTreeRemoval: false, atomicFileStaging: false, atomicDirectoryMetadata: false, trustedOwnedStaging: false,
|
|
23
|
+
open: true, retainedStagingCleanup: false, atomicFilePublication: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicEntryRemovalReceipt: false, atomicTreeRemoval: false, atomicStagedFileMutation: false, atomicFileStaging: false, atomicDirectoryMetadata: false, trustedOwnedStaging: false,
|
|
24
24
|
atomicRename: false, atomicRenameNoReplace: false, descriptorWriteStream: true, retainedResize: true, atomicResize: false,
|
|
25
25
|
});
|
|
26
26
|
function globalCapabilities(filesystem) {
|
|
@@ -31,6 +31,7 @@ function globalCapabilities(filesystem) {
|
|
|
31
31
|
synchronousDirectoryValidation: ["prepareDirectoryAncestry"], guardedStagingPublication: ["createStagedFile", "publishStagedFile", "removeStagedFile"],
|
|
32
32
|
synchronousStagingResolution: ["prepareStagingResolution"],
|
|
33
33
|
trustedOwnedStaging: ["createStagedFile", "publishStagedFile", "removeStagedFile", "writeFileConditional", "removeFileConditional", "prepareDirectory"],
|
|
34
|
+
atomicStagedFileMutation: ["createStagedFile", "publishStagedFile", "removeStagedFile"],
|
|
34
35
|
atomicFilePublication: ["publishFileConditional"], atomicEntryRemoval: ["removeEntryConditional"], atomicEntryRemovalReceipt: ["removeEntryConditional"], atomicTreeRemoval: ["removeTreeConditional"], atomicFileMutation: ["writeFileConditional", "removeFileConditional"], atomicFileStaging: ["createStagedFile", "publishStagedFile", "removeStagedFile"], atomicDirectoryMetadata: ["prepareDirectory"],
|
|
35
36
|
streamingRead: ["readStream"], streamingWrite: ["writeStream"], retainedRead: ["openReadFile"],
|
|
36
37
|
streamingAppend: ["writeStream"], descriptorWriteStream: ["writeStream"], retainedResize: ["openResizeFile"], atomicResize: ["resizeFile"],
|
|
@@ -178,7 +179,7 @@ export class DeviceFileSystem {
|
|
|
178
179
|
let selected;
|
|
179
180
|
const traversal = options.stagingResolution === true ? { virtual: false } : undefined;
|
|
180
181
|
try {
|
|
181
|
-
resolved = await this.#resolve(path, options, options.stagingResolution
|
|
182
|
+
resolved = await this.#resolve(path, options, options.stagingResolution === true ? options.followFinalSymlink === true : options.create !== undefined || options.creation !== "exclusive", options.create, traversal);
|
|
182
183
|
}
|
|
183
184
|
catch (error) {
|
|
184
185
|
options.signal?.throwIfAborted();
|
|
@@ -209,7 +210,7 @@ export class DeviceFileSystem {
|
|
|
209
210
|
// This intent cannot bind virtual traversal. Do not infer other path
|
|
210
211
|
// capabilities by resolving the shadowed entry in the backing filesystem.
|
|
211
212
|
if (traversal?.virtual)
|
|
212
|
-
return Object.freeze({ synchronousStagingResolution: false });
|
|
213
|
+
return Object.freeze({ synchronousStagingResolution: false, synchronousFollowedStagingResolution: false });
|
|
213
214
|
if (selected === undefined) {
|
|
214
215
|
const query = this.#filesystem.capabilitiesFor;
|
|
215
216
|
options.signal?.throwIfAborted();
|
|
@@ -228,6 +229,7 @@ export class DeviceFileSystem {
|
|
|
228
229
|
throw new FsError("EAGAIN", { path });
|
|
229
230
|
if ([resolution.path, ...resolution.traversed.map(entry => entry.path)].some(entry => entry === deviceDirectory || entry.startsWith(`${deviceDirectory}/`))) {
|
|
230
231
|
(unavailable ??= {}).synchronousStagingResolution = false;
|
|
232
|
+
unavailable.synchronousFollowedStagingResolution = false;
|
|
231
233
|
}
|
|
232
234
|
runStagingGuard(resolution.validate);
|
|
233
235
|
}
|
|
@@ -236,6 +238,7 @@ export class DeviceFileSystem {
|
|
|
236
238
|
if (!isFsError(error, "ENOTSUP"))
|
|
237
239
|
throw error;
|
|
238
240
|
(unavailable ??= {}).synchronousStagingResolution = false;
|
|
241
|
+
unavailable.synchronousFollowedStagingResolution = false;
|
|
239
242
|
}
|
|
240
243
|
}
|
|
241
244
|
if (resolved.startsWith(`${deviceDirectory}/`)) {
|
|
@@ -243,6 +246,7 @@ export class DeviceFileSystem {
|
|
|
243
246
|
u.atomicStagingAncestry = false;
|
|
244
247
|
u.synchronousDirectoryValidation = false;
|
|
245
248
|
u.synchronousStagingResolution = false;
|
|
249
|
+
u.synchronousFollowedStagingResolution = false;
|
|
246
250
|
}
|
|
247
251
|
if (typeof this.#filesystem.open !== "function" && capabilities.open !== false)
|
|
248
252
|
(unavailable ??= {}).open = false;
|
|
@@ -490,6 +494,40 @@ export class DeviceFileSystem {
|
|
|
490
494
|
throw new FsError("ENOTSUP", { path });
|
|
491
495
|
await this.#filesystem.writeStream(path, source, options);
|
|
492
496
|
}
|
|
497
|
+
async *iterateDirectory(path, options = {}) {
|
|
498
|
+
const resolved = await this.#resolve(path, options);
|
|
499
|
+
if (resolved === nullPath)
|
|
500
|
+
throw new FsError("ENOTDIR", { syscall: "iterateDirectory", path });
|
|
501
|
+
const virtual = resolved === "/" ? "dev" : resolved === deviceDirectory ? "null" : undefined;
|
|
502
|
+
if (!this.#filesystem.iterateDirectory)
|
|
503
|
+
throw new FsError("ENOTSUP", { syscall: "iterateDirectory", path });
|
|
504
|
+
let seen = false;
|
|
505
|
+
try {
|
|
506
|
+
for await (const entry of this.#filesystem.iterateDirectory(virtual ? resolved : path, options)) {
|
|
507
|
+
options.signal?.throwIfAborted();
|
|
508
|
+
if (entry.name === "." || entry.name === "..")
|
|
509
|
+
continue;
|
|
510
|
+
if (!entry.name || entry.name.includes("/") || entry.name.includes("\0"))
|
|
511
|
+
throw new FsError("EIO", { path, message: "invalid directory entry" });
|
|
512
|
+
if (entry.name === virtual) {
|
|
513
|
+
if (seen)
|
|
514
|
+
continue;
|
|
515
|
+
seen = true;
|
|
516
|
+
yield { name: virtual, type: virtual === "dev" ? "directory" : "character" };
|
|
517
|
+
}
|
|
518
|
+
else
|
|
519
|
+
yield { name: entry.name, type: entry.type };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
options.signal?.throwIfAborted();
|
|
524
|
+
if (!virtual || !isFsError(error, "ENOENT") && !isFsError(error, "ENOTDIR"))
|
|
525
|
+
throw error;
|
|
526
|
+
}
|
|
527
|
+
options.signal?.throwIfAborted();
|
|
528
|
+
if (virtual && !seen)
|
|
529
|
+
yield { name: virtual, type: virtual === "dev" ? "directory" : "character" };
|
|
530
|
+
}
|
|
493
531
|
readdir(path, options = {}) {
|
|
494
532
|
const fast = this.#tryResolveSync(path, options);
|
|
495
533
|
if (fast !== undefined && fast !== "/") {
|
|
@@ -633,6 +671,8 @@ export class DeviceFileSystem {
|
|
|
633
671
|
await this.#mutable(path, options, false);
|
|
634
672
|
await requireOwnedMutation(this.#filesystem, staging.directory.path, "atomicFileStaging", options);
|
|
635
673
|
await requireOwnedMutation(this.#filesystem, destination, "atomicFileStaging", options, options.destination === null);
|
|
674
|
+
if (options.preserveIdentity)
|
|
675
|
+
await requireOwnedMutation(this.#filesystem, destination, "atomicStagedFileMutation", options);
|
|
636
676
|
if (!this.#filesystem.publishStagedFile)
|
|
637
677
|
throw new FsError("ENOTSUP", { path: destination });
|
|
638
678
|
if (options.commitGuard !== undefined)
|
|
@@ -656,8 +696,12 @@ export class DeviceFileSystem {
|
|
|
656
696
|
const capabilities = await this.#filesystem.capabilitiesFor?.(destination, { ...options, create: options.destination === null }) ?? this.#filesystem.capabilities;
|
|
657
697
|
if (capabilities.readOnly === true)
|
|
658
698
|
throw new FsError("EROFS", { path: destination });
|
|
659
|
-
|
|
660
|
-
|
|
699
|
+
// The parent is an identity guard, not an entry being replaced. Root is a
|
|
700
|
+
// valid parent; the synthetic device directory still cannot own staging.
|
|
701
|
+
const parentPath = await this.#resolve(staging.parent.path, options, false);
|
|
702
|
+
if (parentPath === deviceDirectory || parentPath === nullPath)
|
|
703
|
+
throw new FsError("EBUSY", { path: staging.parent.path });
|
|
704
|
+
await this.#mutable(staging.directory.path, options, false);
|
|
661
705
|
const guards = [];
|
|
662
706
|
if (options.ancestors)
|
|
663
707
|
guards.push(await this.prepareDirectoryAncestry(options.ancestors, options));
|
|
@@ -712,19 +756,19 @@ export class DeviceFileSystem {
|
|
|
712
756
|
}
|
|
713
757
|
}
|
|
714
758
|
async prepareStagingResolution(path, options = {}) {
|
|
715
|
-
const controls = options.signal === undefined ? {} : { signal: options.signal };
|
|
759
|
+
const controls = { ...(options.signal === undefined ? {} : { signal: options.signal }), ...(options.followFinalSymlink === true ? { followFinalSymlink: true } : {}) };
|
|
716
760
|
controls.signal?.throwIfAborted();
|
|
717
761
|
if (normalizePath(path) !== path || !path.startsWith("/"))
|
|
718
762
|
throw new FsError("EINVAL", { path });
|
|
719
763
|
try {
|
|
720
764
|
const traversal = { virtual: false };
|
|
721
|
-
const resolved = await this.#resolve(path, controls,
|
|
765
|
+
const resolved = await this.#resolve(path, controls, controls.followFinalSymlink === true, undefined, traversal);
|
|
722
766
|
controls.signal?.throwIfAborted();
|
|
723
767
|
if (traversal.virtual)
|
|
724
768
|
throw new FsError("ENOTSUP", { path });
|
|
725
769
|
const declared = ownedMutationCapabilities(this.#filesystem, await this.#filesystem.capabilitiesFor?.(path, { ...controls, stagingResolution: true }) ?? this.#filesystem.capabilities);
|
|
726
770
|
controls.signal?.throwIfAborted();
|
|
727
|
-
if (declared.synchronousStagingResolution !== true || !this.#filesystem.prepareStagingResolution)
|
|
771
|
+
if (declared.synchronousStagingResolution !== true || controls.followFinalSymlink === true && declared.synchronousFollowedStagingResolution !== true || !this.#filesystem.prepareStagingResolution)
|
|
728
772
|
throw new FsError("ENOTSUP", { path });
|
|
729
773
|
const receipt = snapshotStagingResolution(await this.#filesystem.prepareStagingResolution(path, controls));
|
|
730
774
|
controls.signal?.throwIfAborted();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { FileStat, FileSystem } from "../../contracts/filesystem.js";
|
|
2
2
|
/** Internal authority: inspections and stock mutations share one JavaScript turn. */
|
|
3
3
|
export interface MemoryAtomicView {
|
|
4
|
+
replaceContents(source: string, destination: string): void;
|
|
4
5
|
stat(path: string): FileStat | undefined;
|
|
5
6
|
names(path: string): readonly string[];
|
|
6
7
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AppendFileOptions, CopyFileOptions, DirectoryEntry, EntryComparison, FileReadHandle, FileResizeHandle, FileStat, FileSystem, FileSystemCapabilities, FsOptions, ChmodOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, FileDescriptor, OpenFileOptions, OpenReadFileOptions, OpenResizeFileOptions, WriteFileOptions, ConditionalWriteFileOptions, ConditionalRemoveFileOptions, ConditionalRemoveEntryOptions, ConditionalRemoveEntryReceiptOptions, CreateStagedFileOptions, FileStaging, FileStagingEntry, FileStagingResolution, PublishStagedFileOptions, PublishStagedFileSetOptions, PrepareDirectoryOptions, StagedFileContent } from "../../contracts/filesystem.js";
|
|
1
|
+
import type { AppendFileOptions, CopyFileOptions, DirectoryEntry, EntryComparison, FileReadHandle, FileResizeHandle, FileStat, FileSystem, FileSystemCapabilities, FsOptions, ChmodOptions, RenameOptions, MkdirOptions, ReadDirectoryOptions, ReadFileOptions, ReadStreamOptions, RemoveOptions, FileDescriptor, OpenFileOptions, OpenReadFileOptions, OpenResizeFileOptions, WriteFileOptions, ConditionalWriteFileOptions, ConditionalRemoveFileOptions, ConditionalRemoveEntryOptions, ConditionalRemoveEntryReceiptOptions, CreateStagedFileOptions, FileStaging, FileStagingEntry, FileStagingResolution, PrepareStagingResolutionOptions, PublishStagedFileOptions, PublishStagedFileSetOptions, PrepareDirectoryOptions, StagedFileContent } from "../../contracts/filesystem.js";
|
|
2
2
|
import type { ByteSource } from "../../contracts/io.js";
|
|
3
3
|
import { type MemoryAtomicView } from "./atomic-view.js";
|
|
4
4
|
import { MemoryAllocation, MemoryLedger } from "./ledger.js";
|
|
@@ -49,6 +49,9 @@ type OwnedStore = {
|
|
|
49
49
|
root: DirectoryNode;
|
|
50
50
|
ledger: MemoryLedger;
|
|
51
51
|
capabilities: FileSystem["capabilities"];
|
|
52
|
+
customCapabilities?: FileSystemCapabilities;
|
|
53
|
+
identityScope?: symbol;
|
|
54
|
+
atomicView?: MemoryAtomicView;
|
|
52
55
|
intact: () => boolean;
|
|
53
56
|
};
|
|
54
57
|
export interface ConditionalMutationBinding {
|
|
@@ -59,7 +62,6 @@ export interface ConditionalMutationBinding {
|
|
|
59
62
|
}
|
|
60
63
|
export declare function bindConditionalMutation<Result>(identityScope: object | symbol | undefined, binding: ConditionalMutationBinding, action: () => Promise<Result>): Promise<Result>;
|
|
61
64
|
export declare class MemoryFileSystem implements FileSystem {
|
|
62
|
-
#private;
|
|
63
65
|
capabilitiesFor?: NonNullable<FileSystem["capabilitiesFor"]>;
|
|
64
66
|
private get identityScope();
|
|
65
67
|
get capabilities(): FileSystemCapabilities;
|
|
@@ -114,7 +116,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
114
116
|
createStagedFile(directoryPath: string, name: string, content: StagedFileContent, options: CreateStagedFileOptions): Promise<FileStaging>;
|
|
115
117
|
private retainStagingCleanup;
|
|
116
118
|
prepareDirectoryAncestry(ancestors: readonly FileStagingEntry[], options?: FsOptions): Promise<() => true>;
|
|
117
|
-
prepareStagingResolution(path: string, options?:
|
|
119
|
+
prepareStagingResolution(path: string, options?: PrepareStagingResolutionOptions): Promise<FileStagingResolution>;
|
|
118
120
|
private verifyDirectoryAncestry;
|
|
119
121
|
publishStagedFile(receipt: FileStaging, destination: string, options: PublishStagedFileOptions): Promise<void>;
|
|
120
122
|
publishStagedFileSet(receipt: FileStaging, destination: string, options: PublishStagedFileSetOptions): Promise<FileStat>;
|
|
@@ -132,6 +134,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
132
134
|
appendFile(path: string, data: Uint8Array, options?: AppendFileOptions): Promise<void>;
|
|
133
135
|
stat(path: string, options?: FsOptions): Promise<FileStat>;
|
|
134
136
|
lstat(path: string, options?: FsOptions): Promise<FileStat>;
|
|
137
|
+
iterateDirectory(path: string, options?: FsOptions): AsyncIterable<DirectoryEntry>;
|
|
135
138
|
readdir(path: string, options?: ReadDirectoryOptions): Promise<DirectoryEntry[]>;
|
|
136
139
|
tryMkdirFastSync(path: string, recursive: boolean, mode: number | undefined): boolean;
|
|
137
140
|
tryRmFastSync(path: string, recursive: boolean, force: boolean): boolean;
|