@bytecodealliance/preview2-shim 0.23.0 → 0.24.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/README.md CHANGED
@@ -4,12 +4,145 @@ WASI Preview2 implementations for Node.js & browsers.
4
4
 
5
5
  Node.js support is fully tested and conformant against the Wasmtime test suite.
6
6
 
7
- Browser support is considered experimental, and not currently suitable for production applications.
7
+ Browser support is available with the platform limitations documented below.
8
8
 
9
9
  The Node.js implementation owns its worker artifact. Direct package use and supported downstream
10
10
  bundlers should resolve it through the public shim imports; applications do not need to import or
11
11
  copy files from `dist/io`.
12
12
 
13
+ ## Browser support matrix
14
+
15
+ Browser defaults are capability-safe: clocks and secure randomness use Web APIs, stdout and stderr
16
+ write to the console, stdin is closed, outbound HTTP uses `fetch`, filesystem preopens must be
17
+ configured explicitly, and raw sockets are unavailable unless an embedding supplies an adapter.
18
+
19
+ | WASI area | Browser status | Default capability |
20
+ | ----------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------- |
21
+ | CLI environment and arguments | Configurable per `WASIShim`; compatibility setters are global | Empty snapshots unless configured |
22
+ | CLI stdin | Adapter-backed | Closed stream |
23
+ | CLI stdout and stderr | Web API | Console-backed, preserving split UTF-8 writes until flush/newline |
24
+ | CLI terminals | Adapter-backed | No terminal resource |
25
+ | Clocks | Web API | `performance.now`, `Date.now`, and timer-backed pollables |
26
+ | Random | Web API | `crypto.getRandomValues`, including requests larger than 64 KiB |
27
+ | I/O streams and poll | Implemented browser resources | Non-blocking streams depend on their injected handlers |
28
+ | Filesystem | Adapter-backed; opt-in in-memory compatibility implementation | No persistent storage is selected implicitly |
29
+ | Outbound HTTP | Web API | Delegates to `fetch` |
30
+ | Incoming HTTP | Adapter-backed; opt-in in-memory client | Browsers cannot listen for arbitrary inbound HTTP |
31
+ | TCP and UDP | Adapter-backed; opt-in in-memory implementations | Raw sockets are not exposed by standard browsers |
32
+ | DNS | Host adapter required | DNS is not exposed independently by standard browsers |
33
+ | `WASIShim` instantiation | Implemented | Interface namespaces can be overridden per instance |
34
+
35
+ An operation is not considered supported merely because its interface shape exists. Adapter-backed
36
+ rows require the embedding application to provide that capability; unavailable operations fail with
37
+ a WASI-domain error instead of logging or returning a placeholder resource.
38
+
39
+ ### Detailed browser capabilities
40
+
41
+ The following table describes the built-in browser implementation. An application-provided
42
+ namespace can replace any row through `WASIShim`.
43
+
44
+ | Interface | Implemented | Host adapter required | Unsupported by browser implementation |
45
+ | ----------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------- |
46
+ | `wasi:cli` | environment, arguments, initial cwd, exit, stream and terminal accessors | stdin/stdout/stderr handlers and terminal resources | — |
47
+ | `wasi:clocks` | wall clock, monotonic clock, timer subscriptions | — | timezone APIs (not part of Preview 2) |
48
+ | `wasi:random` | secure and insecure bytes, insecure seed | — | — |
49
+ | `wasi:io` | errors, input/output streams, poll and pollables | readiness and I/O behavior for injected stream handlers | synchronous blocking of the browser event loop |
50
+ | `wasi:filesystem` | descriptors, files, directories, links, metadata, streams, preopens through the ephemeral adapter | persistent storage, permissions, and external file handles | symbolic-link creation and reading |
51
+ | `wasi:http/outgoing-handler` | Fetch-backed requests and buffered request bodies | Fetch implementation and network permission | request/response trailers; streaming uploads |
52
+ | `wasi:http/incoming-handler` | request/response translation, injectable handler, and in-memory client | HTTP server, service worker, or other request source | direct browser listening |
53
+ | `wasi:sockets/ip-name-lookup` | interface shape only | complete interface replacement | built-in DNS lookup |
54
+ | `wasi:sockets/tcp*` | opt-in in-memory server/client implementation | adapter for external connectivity | built-in raw TCP |
55
+ | `wasi:sockets/udp*` | opt-in in-memory server/client implementation | adapter for external connectivity | built-in raw UDP |
56
+
57
+ Outbound HTTP buffers a requested body until `outgoing-body.finish` before calling `fetch`.
58
+ This preserves complete-body semantics across browsers but does not provide streaming upload or
59
+ upload backpressure. Incoming Fetch bodies retain their asynchronous stream behavior. HTTP
60
+ trailers are not implemented.
61
+
62
+ Chromium-based browsers can opt into Fetch request streaming. This setting uses a
63
+ `ReadableStream` request body with `duplex: "half"`; unsupported browsers reject the request, so
64
+ applications should enable it only after applying their own browser support policy or feature
65
+ detection:
66
+
67
+ ```js
68
+ import { http } from "@bytecodealliance/preview2-shim";
69
+
70
+ http._setRequestStreaming(true);
71
+ ```
72
+
73
+ The setting affects subsequent requests made through the browser HTTP shim. Call
74
+ `http._setRequestStreaming(false)` to restore portable completion buffering.
75
+
76
+ Browser applications select storage explicitly. The bundled file-data adapter is ephemeral and must
77
+ be opted into:
78
+
79
+ ```js
80
+ import { filesystem } from "@bytecodealliance/preview2-shim";
81
+ import { WASIShim } from "@bytecodealliance/preview2-shim/instantiation";
82
+
83
+ const shim = new WASIShim({
84
+ environment: { MODE: "browser" },
85
+ arguments: ["component"],
86
+ stdout: { write: (bytes) => terminal.write(bytes) },
87
+ browserFilesystem: {
88
+ adapter: new filesystem.InMemoryFilesystemAdapter(),
89
+ preopens: { "/data": { dir: {} } },
90
+ },
91
+ sandbox: { enableNetwork: false },
92
+ });
93
+ ```
94
+
95
+ The browser shim does not request File System Access permissions or choose IndexedDB/OPFS on an
96
+ application's behalf. Applications that need another storage model implement the generated
97
+ `wasi:filesystem/types` and `wasi:filesystem/preopens` namespaces and inject them through the
98
+ `filesystem` option:
99
+
100
+ ```js
101
+ const shim = new WASIShim({
102
+ filesystem: {
103
+ types: applicationFilesystemTypes,
104
+ preopens: applicationFilesystemPreopens,
105
+ },
106
+ });
107
+ ```
108
+
109
+ This keeps permission prompts, handle acquisition, persistence, and synchronization policy in
110
+ application code. Raw TCP, UDP, and DNS are denied by default; outbound HTTP remains a separate
111
+ `fetch` capability.
112
+
113
+ Deterministic in-memory transports are available for components that act as servers and for tests:
114
+
115
+ ```js
116
+ import { http, sockets } from "@bytecodealliance/preview2-shim";
117
+
118
+ const tcpSockets = new sockets.InMemoryTcpSockets();
119
+ const tcpClient = tcpSockets.connect(serverAddress);
120
+ const udpSockets = new sockets.InMemoryUdpSockets();
121
+ const udpClient = udpSockets.createClient(clientAddress);
122
+ const httpClient = new http.InMemoryHttpClient(component.incomingHandler);
123
+
124
+ const shim = new WASIShim({ tcpSockets, udpSockets });
125
+ ```
126
+
127
+ These implementations route bytes only within the current JavaScript realm; they do not grant raw
128
+ browser network access. `InMemoryHttpClient.fetch(request)` returns a standard Web `Response`.
129
+
130
+ For a small application-owned implementation, see the
131
+ [Map-backed browser filesystem test shim](./test/fixtures/filesystem-shim/in-memory-map.ts). It keeps named
132
+ roots in an in-memory `Map`, implements `createPreopens`, and is intentionally example code rather
133
+ than a published or supported filesystem package. The example is exercised through the reusable
134
+ [filesystem implementation test suite](./test/filesystem-conformance.ts), which can also be pointed
135
+ at other implementations.
136
+
137
+ Browser filesystem adapters own the capabilities passed in `preopens` and the roots returned from
138
+ `getRoot`. A root may be shared by multiple descriptors and preopen names; the adapter is therefore
139
+ responsible for persistence and synchronization of shared mutations. Calling `dispose` on the
140
+ namespace returned by `createFilesystem` calls the adapter's optional `dispose` method once and
141
+ invalidates further preopen access. `WASIShim` does not currently cascade disposal, so embeddings
142
+ using external handles must retain and dispose their application-owned filesystem namespace or
143
+ adapter themselves. The bundled in-memory adapter keeps all state in memory and shares mutations
144
+ for the same file-data object.
145
+
13
146
  # Features
14
147
 
15
148
  ## WASI Shim object for easy instantiation
@@ -57,9 +190,9 @@ const component = await instantiate(loader, new WASIShim().getImportObject());
57
190
 
58
191
  ## Sandboxing
59
192
 
60
- By default, the preview2-shim provides full access to the host filesystem, environment variables,
61
- and network - matching the default behavior of Node.js libraries. However, you can configure
62
- sandboxing to restrict what guests can access.
193
+ On Node.js, the preview2-shim provides host filesystem, environment, and network access by default,
194
+ matching the usual behavior of Node.js libraries. Browser defaults expose no filesystem preopens or
195
+ raw sockets. Both platforms can configure which capabilities a guest receives.
63
196
 
64
197
  ### Using WASIShim for sandboxing
65
198
 
@@ -78,7 +211,7 @@ const sandboxedShim = new WASIShim({
78
211
  },
79
212
  });
80
213
 
81
- // Limited filesystem access - map virtual paths to host paths
214
+ // Node.js only: map virtual paths to host paths
82
215
  const limitedShim = new WASIShim({
83
216
  sandbox: {
84
217
  preopens: {
@@ -95,13 +228,21 @@ const component = await instantiate(loader, sandboxedShim.getImportObject());
95
228
  ### Notes on sandboxing
96
229
 
97
230
  - By default (when no options are passed), the shim is providing full access to match typical
98
- Node.js library behavior.
231
+ Node.js library behavior. In browsers, filesystem preopens remain empty until the application
232
+ explicitly injects filesystem namespaces or selects the ephemeral file-data adapter.
233
+ - `sandbox.preopens` maps guest paths to Node.js host paths on Node.js. With
234
+ `browserFilesystem`, the same option maps guest paths to capabilities understood by its adapter
235
+ and overrides `browserFilesystem.preopens`. A custom `filesystem` can implement
236
+ `createPreopens(preopens)` to interpret the provided properties and return its own
237
+ `wasi:filesystem/preopens` namespace. The shim passes those properties through unchanged.
99
238
  - Each `WASIShim` instance has its own isolated preopens, environment variables, and arguments.
100
239
  Multiple instances with different configurations will not affect each other.
101
240
  - The direct preopen functions (`_setPreopens`, `_clearPreopens`, etc.) modify global state and
102
241
  affect all components not using `WASIShim` with explicit configuration. For isolation, prefer
103
242
  using `WASIShim` with the `sandbox` option containing `preopens` and `env`.
104
- - When `sandbox.enableNetwork: false`, all socket and HTTP operations will throw "access-denied" errors.
243
+ - When `sandbox.enableNetwork: false`, Node.js socket operations receive an instance-local denied
244
+ network capability. Outbound HTTP is a separate Fetch capability; replace or omit the HTTP
245
+ namespace when the embedding must deny it as well.
105
246
 
106
247
  [jco]: https://www.npmjs.com/package/@bytecodealliance/jco
107
248
 
@@ -1,4 +1,4 @@
1
- import type { exit as ExitNamespace, stderr as StderrNamespace, stdin as StdinNamespace, stdout as StdoutNamespace, terminalInput as TerminalInputNamespace, terminalOutput as TerminalOutputNamespace, terminalStderr as TerminalStderrNamespace, terminalStdin as TerminalStdinNamespace, terminalStdout as TerminalStdoutNamespace } from "../../types/cli.js";
1
+ import type { environment as EnvironmentNamespace, exit as ExitNamespace, stderr as StderrNamespace, stdin as StdinNamespace, stdout as StdoutNamespace, terminalInput as TerminalInputNamespace, terminalOutput as TerminalOutputNamespace, terminalStderr as TerminalStderrNamespace, terminalStdin as TerminalStdinNamespace, terminalStdout as TerminalStdoutNamespace } from "../../types/cli.js";
2
2
  import { type InputStreamHandler, type OutputStreamHandler } from "./io.js";
3
3
  export { _setEnv, _setArgs, environment } from "./environment.js";
4
4
  export { _setCwd } from "./config.js";
@@ -6,6 +6,14 @@ export declare const exit: typeof ExitNamespace;
6
6
  export declare function _setStdin(handler: InputStreamHandler): void;
7
7
  export declare function _setStderr(handler: OutputStreamHandler): void;
8
8
  export declare function _setStdout(handler: OutputStreamHandler): void;
9
+ export interface BrowserCliConfig {
10
+ environment?: Record<string, string>;
11
+ arguments?: string[];
12
+ initialCwd?: string;
13
+ stdin?: InputStreamHandler;
14
+ stdout?: OutputStreamHandler;
15
+ stderr?: OutputStreamHandler;
16
+ }
9
17
  export declare const stdin: typeof StdinNamespace;
10
18
  export declare const stdout: typeof StdoutNamespace;
11
19
  export declare const stderr: typeof StderrNamespace;
@@ -14,3 +22,16 @@ export declare const terminalOutput: typeof TerminalOutputNamespace;
14
22
  export declare const terminalStderr: typeof TerminalStderrNamespace;
15
23
  export declare const terminalStdin: typeof TerminalStdinNamespace;
16
24
  export declare const terminalStdout: typeof TerminalStdoutNamespace;
25
+ /** Create isolated browser CLI interfaces without changing compatibility globals. */
26
+ export declare function createCli(config?: BrowserCliConfig): {
27
+ environment: typeof EnvironmentNamespace;
28
+ exit: typeof ExitNamespace;
29
+ stdin: typeof StdinNamespace;
30
+ stdout: typeof StdoutNamespace;
31
+ stderr: typeof StderrNamespace;
32
+ terminalInput: typeof TerminalInputNamespace;
33
+ terminalOutput: typeof TerminalOutputNamespace;
34
+ terminalStdin: typeof TerminalStdinNamespace;
35
+ terminalStdout: typeof TerminalStdoutNamespace;
36
+ terminalStderr: typeof TerminalStderrNamespace;
37
+ };
@@ -29,41 +29,46 @@ export function _setStdout(handler) {
29
29
  stdoutStream.handler = handler;
30
30
  }
31
31
  const stdinStream = inputStreamCreate({
32
- blockingRead(_len) {
33
- // TODO
34
- return new Uint8Array(0);
32
+ blockingRead() {
33
+ throw { tag: "closed" };
35
34
  },
36
35
  subscribe() {
37
- // TODO
38
36
  return pollableCreate();
39
37
  },
40
- [symbolDispose]() {
41
- // TODO
42
- },
43
- });
44
- const textDecoder = new TextDecoder();
45
- const stdoutStream = outputStreamCreate({
46
- write(contents) {
47
- if (contents.at(-1) == 10) {
48
- // console.log already appends a new line
49
- contents = contents.subarray(0, -1);
50
- }
51
- console.log(textDecoder.decode(contents));
52
- },
53
- blockingFlush() { },
54
38
  [symbolDispose]() { },
55
39
  });
56
- const stderrStream = outputStreamCreate({
57
- write(contents) {
58
- if (contents.at(-1) == 10) {
59
- // console.error already appends a new line
60
- contents = contents.subarray(0, -1);
40
+ function consoleStream(writeLine) {
41
+ const decoder = new TextDecoder();
42
+ let pending = "";
43
+ const emitCompleteLines = () => {
44
+ const lines = pending.split("\n");
45
+ pending = lines.pop();
46
+ for (const line of lines) {
47
+ writeLine(line.endsWith("\r") ? line.slice(0, -1) : line);
61
48
  }
62
- console.error(textDecoder.decode(contents));
63
- },
64
- blockingFlush() { },
65
- [symbolDispose]() { },
66
- });
49
+ };
50
+ return {
51
+ write(contents) {
52
+ pending += decoder.decode(contents, { stream: true });
53
+ emitCompleteLines();
54
+ },
55
+ flush() {
56
+ pending += decoder.decode();
57
+ if (pending) {
58
+ writeLine(pending);
59
+ }
60
+ pending = "";
61
+ },
62
+ blockingFlush() {
63
+ this.flush?.();
64
+ },
65
+ drop() {
66
+ this.flush?.();
67
+ },
68
+ };
69
+ }
70
+ const stdoutStream = outputStreamCreate(consoleStream((line) => console.log(line)));
71
+ const stderrStream = outputStreamCreate(consoleStream((line) => console.error(line)));
67
72
  export const stdin = {
68
73
  getStdin() {
69
74
  return stdinStream;
@@ -83,9 +88,6 @@ class TerminalInput {
83
88
  }
84
89
  class TerminalOutput {
85
90
  }
86
- const terminalStdoutInstance = new TerminalOutput();
87
- const terminalStderrInstance = new TerminalOutput();
88
- const terminalStdinInstance = new TerminalInput();
89
91
  export const terminalInput = {
90
92
  TerminalInput,
91
93
  };
@@ -94,16 +96,46 @@ export const terminalOutput = {
94
96
  };
95
97
  export const terminalStderr = {
96
98
  getTerminalStderr() {
97
- return terminalStderrInstance;
99
+ return undefined;
98
100
  },
99
101
  };
100
102
  export const terminalStdin = {
101
103
  getTerminalStdin() {
102
- return terminalStdinInstance;
104
+ return undefined;
103
105
  },
104
106
  };
105
107
  export const terminalStdout = {
106
108
  getTerminalStdout() {
107
- return terminalStdoutInstance;
109
+ return undefined;
108
110
  },
109
111
  };
112
+ /** Create isolated browser CLI interfaces without changing compatibility globals. */
113
+ export function createCli(config = {}) {
114
+ const stdinInstance = inputStreamCreate(config.stdin ?? {
115
+ blockingRead() {
116
+ throw { tag: "closed" };
117
+ },
118
+ subscribe: () => pollableCreate(),
119
+ });
120
+ const stdoutInstance = outputStreamCreate(config.stdout ?? consoleStream((line) => console.log(line)));
121
+ const stderrInstance = outputStreamCreate(config.stderr ?? consoleStream((line) => console.error(line)));
122
+ const env = Object.entries(config.environment ?? {});
123
+ const args = [...(config.arguments ?? [])];
124
+ const cwd = config.initialCwd ?? "/";
125
+ return {
126
+ environment: {
127
+ getEnvironment: () => env.map(([key, value]) => [key, value]),
128
+ getArguments: () => [...args],
129
+ initialCwd: () => cwd,
130
+ },
131
+ exit,
132
+ stdin: { getStdin: () => stdinInstance },
133
+ stdout: { getStdout: () => stdoutInstance },
134
+ stderr: { getStderr: () => stderrInstance },
135
+ terminalInput,
136
+ terminalOutput,
137
+ terminalStdin,
138
+ terminalStdout,
139
+ terminalStderr,
140
+ };
141
+ }
@@ -1,4 +1,21 @@
1
+ import { checkedU64 } from "./common.js";
1
2
  import { pollableCreate } from "./io.js";
3
+ const MAX_TIMEOUT_MS = 0x7fffffff;
4
+ function timeout(durationNs) {
5
+ let remainingMs = Number((durationNs + 999999n) / 1000000n);
6
+ return new Promise((resolve) => {
7
+ const next = () => {
8
+ if (remainingMs <= 0) {
9
+ resolve();
10
+ return;
11
+ }
12
+ const delay = Math.min(remainingMs, MAX_TIMEOUT_MS);
13
+ remainingMs -= delay;
14
+ setTimeout(next, delay);
15
+ };
16
+ next();
17
+ });
18
+ }
2
19
  export const monotonicClock = {
3
20
  resolution() {
4
21
  // usually we dont get sub-millisecond accuracy in the browser
@@ -10,17 +27,19 @@ export const monotonicClock = {
10
27
  return BigInt(Math.floor(performance.now() * 1e6));
11
28
  },
12
29
  subscribeInstant(instant) {
13
- instant = BigInt(instant);
30
+ instant = checkedU64(instant, "instant");
14
31
  const now = monotonicClock.now();
15
32
  if (instant <= now) {
16
- return pollableCreate(new Promise((resolve) => setTimeout(resolve, 0)));
33
+ return pollableCreate();
17
34
  }
18
35
  return monotonicClock.subscribeDuration(instant - now);
19
36
  },
20
37
  subscribeDuration(duration) {
21
- duration = BigInt(duration);
22
- const ms = duration <= 0n ? 0 : Number(duration / 1000000n);
23
- return pollableCreate(new Promise((resolve) => setTimeout(resolve, ms)));
38
+ duration = checkedU64(duration, "duration");
39
+ if (duration === 0n) {
40
+ return pollableCreate();
41
+ }
42
+ return pollableCreate(timeout(duration));
24
43
  },
25
44
  };
26
45
  export const wallClock = {
@@ -0,0 +1,5 @@
1
+ export declare const MAX_U64: bigint;
2
+ /** Validate and return a WebAssembly u64 value. */
3
+ export declare function checkedU64(value: bigint, name: string): bigint;
4
+ /** Convert a WebAssembly u64 value to a lossless JavaScript number. */
5
+ export declare function checkedU64AsNumber(value: bigint, name: string): number;
@@ -0,0 +1,16 @@
1
+ export const MAX_U64 = (1n << 64n) - 1n;
2
+ /** Validate and return a WebAssembly u64 value. */
3
+ export function checkedU64(value, name) {
4
+ if (typeof value !== "bigint" || value < 0n || value > MAX_U64) {
5
+ throw new TypeError(`${name} must be a valid u64`);
6
+ }
7
+ return value;
8
+ }
9
+ /** Convert a WebAssembly u64 value to a lossless JavaScript number. */
10
+ export function checkedU64AsNumber(value, name) {
11
+ checkedU64(value, name);
12
+ if (value > BigInt(Number.MAX_SAFE_INTEGER)) {
13
+ throw new RangeError(`${name} exceeds JavaScript's safe integer range`);
14
+ }
15
+ return Number(value);
16
+ }
@@ -1,104 +1,75 @@
1
1
  import { types as TypesNamespace, preopens as PreopensNamespace } from "../../types/filesystem.js";
2
- import { InputStream as IInputStream, OutputStream as IOutputStream } from "../../types/interfaces/wasi-io-streams.js";
2
+ import type { FileData } from "./in-memory-filesystem.js";
3
3
  export { _setCwd } from "./config.js";
4
+ export { InMemoryFilesystemAdapter } from "./in-memory-filesystem.js";
5
+ export type { FileData, FileDataEntry } from "./in-memory-filesystem.js";
4
6
  type Filesize = TypesNamespace.Filesize;
5
7
  type OpenFlags = TypesNamespace.OpenFlags;
6
8
  type PathFlags = TypesNamespace.PathFlags;
7
- export interface FileDataEntry {
8
- dir?: Record<string, FileDataEntry>;
9
- source?: Uint8Array | string;
9
+ export interface BrowserDirectoryEntryStream {
10
+ readDirectoryEntry(): TypesNamespace.DirectoryEntry | undefined;
11
+ }
12
+ export interface BrowserFilesystemDescriptor extends Omit<TypesNamespace.Descriptor, "isSameObject" | "linkAt" | "openAt" | "readDirectory" | "renameAt"> {
13
+ readDirectory(): BrowserDirectoryEntryStream;
14
+ linkAt(oldPathFlags: PathFlags, oldPath: string, newDescriptor: BrowserFilesystemDescriptor, newPath: string): void;
15
+ openAt(pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: TypesNamespace.DescriptorFlags): BrowserFilesystemDescriptor;
16
+ renameAt(oldPath: string, newDescriptor: BrowserFilesystemDescriptor, newPath: string): void;
17
+ isSameObject(other: BrowserFilesystemDescriptor): boolean;
18
+ }
19
+ export interface BrowserFilesystemAdapter<Capability = unknown> {
20
+ getRoot(capability: Capability): BrowserFilesystemDescriptor;
21
+ dispose?(): void;
22
+ }
23
+ export interface BrowserFilesystemConfig<Capability> {
24
+ adapter: BrowserFilesystemAdapter<Capability>;
25
+ preopens: Record<string, Capability>;
10
26
  }
11
- /**
12
- * Root file data structure representing a filesystem tree.
13
- * Each entry is either a directory (has `dir` property) or a file (has `source` property).
14
- * @example
15
- * // A simple filesystem with one directory containing one file:
16
- * const fileData = {
17
- * dir: {
18
- * 'myfile.txt': { source: new Uint8Array([72, 101, 108, 108, 111]) }
19
- * }
20
- * };
21
- */
22
- export type FileData = FileDataEntry;
23
- export declare function _setFileData(fileData: FileData): void;
24
- export declare function _getFileData(): string;
25
27
  declare class DirectoryEntryStream implements TypesNamespace.DirectoryEntryStream {
26
- idx: number;
27
- entries: [string, FileDataEntry][];
28
- static _create(entries: [string, FileDataEntry][]): DirectoryEntryStream;
28
+ #private;
29
+ static _create(implementation: BrowserDirectoryEntryStream): DirectoryEntryStream;
29
30
  readDirectoryEntry(): TypesNamespace.DirectoryEntry | undefined;
30
31
  }
31
32
  declare class Descriptor implements TypesNamespace.Descriptor {
32
33
  #private;
33
- _getEntry(descriptor: Descriptor): FileDataEntry;
34
- static _create(entry: FileDataEntry | any, isStream?: boolean): Descriptor;
35
- readViaStream(_offset: bigint): IInputStream;
36
- writeViaStream(_offset: bigint): IOutputStream;
37
- appendViaStream(): IOutputStream;
34
+ _getImplementation(descriptor: Descriptor): BrowserFilesystemDescriptor;
35
+ static _create(implementation: BrowserFilesystemDescriptor): Descriptor;
36
+ readViaStream(offset: Filesize): import("../../types/interfaces/wasi-io-streams.js").InputStream;
37
+ writeViaStream(offset: Filesize): import("../../types/interfaces/wasi-io-streams.js").OutputStream;
38
+ appendViaStream(): import("../../types/interfaces/wasi-io-streams.js").OutputStream;
38
39
  advise(offset: Filesize, length: Filesize, advice: TypesNamespace.Advice): void;
39
40
  syncData(): void;
40
41
  getFlags(): TypesNamespace.DescriptorFlags;
41
- getType(): "directory" | "fifo" | "regular-file" | "unknown";
42
- setSize(size: bigint): void;
43
- setTimes(dataAccessTimestamp: any, dataModificationTimestamp: any): void;
44
- read(length: bigint, offset: bigint): [Uint8Array<ArrayBufferLike>, boolean];
42
+ getType(): TypesNamespace.DescriptorType;
43
+ setSize(size: Filesize): void;
44
+ setTimes(dataAccessTimestamp: TypesNamespace.NewTimestamp, dataModificationTimestamp: TypesNamespace.NewTimestamp): void;
45
+ read(length: Filesize, offset: Filesize): [Uint8Array<ArrayBufferLike>, boolean];
45
46
  write(buffer: Uint8Array, offset: Filesize): bigint;
46
47
  readDirectory(): DirectoryEntryStream;
47
48
  sync(): void;
48
49
  createDirectoryAt(path: string): void;
49
- stat(): {
50
- type: "directory" | "regular-file" | "unknown";
51
- linkCount: bigint;
52
- size: bigint;
53
- dataAccessTimestamp: {
54
- seconds: bigint;
55
- nanoseconds: number;
56
- };
57
- dataModificationTimestamp: {
58
- seconds: bigint;
59
- nanoseconds: number;
60
- };
61
- statusChangeTimestamp: {
62
- seconds: bigint;
63
- nanoseconds: number;
64
- };
65
- };
66
- statAt(_pathFlags: PathFlags, path: string): {
67
- type: "directory" | "regular-file" | "unknown";
68
- linkCount: bigint;
69
- size: bigint;
70
- dataAccessTimestamp: {
71
- seconds: bigint;
72
- nanoseconds: number;
73
- };
74
- dataModificationTimestamp: {
75
- seconds: bigint;
76
- nanoseconds: number;
77
- };
78
- statusChangeTimestamp: {
79
- seconds: bigint;
80
- nanoseconds: number;
81
- };
82
- };
83
- setTimesAt(): void;
84
- linkAt(): void;
85
- openAt(_pathFlags: PathFlags, path: string, openFlags: OpenFlags, _flags: TypesNamespace.DescriptorFlags): Descriptor;
86
- readlinkAt(_path: string): string;
87
- removeDirectoryAt(): void;
88
- renameAt(): void;
89
- symlinkAt(): void;
90
- unlinkFileAt(): void;
50
+ stat(): TypesNamespace.DescriptorStat;
51
+ statAt(pathFlags: PathFlags, path: string): TypesNamespace.DescriptorStat;
52
+ setTimesAt(pathFlags: PathFlags, path: string, dataAccessTimestamp: TypesNamespace.NewTimestamp, dataModificationTimestamp: TypesNamespace.NewTimestamp): void;
53
+ linkAt(oldPathFlags: PathFlags, oldPath: string, newDescriptor: TypesNamespace.Descriptor, newPath: string): void;
54
+ openAt(pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: TypesNamespace.DescriptorFlags): Descriptor;
55
+ readlinkAt(path: string): string;
56
+ removeDirectoryAt(path: string): void;
57
+ renameAt(oldPath: string, newDescriptor: TypesNamespace.Descriptor, newPath: string): void;
58
+ symlinkAt(oldPath: string, newPath: string): void;
59
+ unlinkFileAt(path: string): void;
91
60
  isSameObject(other: TypesNamespace.Descriptor): boolean;
92
- metadataHash(): {
93
- upper: bigint;
94
- lower: bigint;
95
- };
96
- metadataHashAt(_pathFlags: any, _path: string): {
97
- upper: bigint;
98
- lower: bigint;
99
- };
61
+ metadataHash(): TypesNamespace.MetadataHashValue;
62
+ metadataHashAt(pathFlags: PathFlags, path: string): TypesNamespace.MetadataHashValue;
100
63
  }
101
64
  export declare const preopens: typeof PreopensNamespace;
65
+ /** Create isolated filesystem namespaces backed by an application-selected adapter. */
66
+ export declare function createFilesystem<Capability>({ adapter, preopens: configuredPreopens, }: BrowserFilesystemConfig<Capability>): {
67
+ types: typeof TypesNamespace;
68
+ preopens: typeof PreopensNamespace;
69
+ dispose(): void;
70
+ };
71
+ export declare function _setFileData(fileData: FileData): void;
72
+ export declare function _getFileData(): string;
102
73
  /**
103
74
  * Replace all preopens with the given set.
104
75
  * @param preopensConfig - Map of virtual paths to file data entries
@@ -110,23 +81,10 @@ export declare function _setPreopens(preopensConfig: Record<string, FileData>):
110
81
  * @param fileData - The file data object representing the directory
111
82
  */
112
83
  export declare function _addPreopen(virtualPath: string, fileData: FileData): void;
113
- /**
114
- * Clear all preopens, giving the guest no filesystem access.
115
- *
116
- * This functionality exists mostly to maintain backwards compatibility. Prefer setting preopens
117
- * via `WASIShim` rather than making top level changes to preopens using these functions.
118
- */
84
+ /** Clear all preopens, giving the guest no filesystem access. */
119
85
  export declare function _clearPreopens(): void;
120
- /**
121
- * Get current preopens configuration.
122
- * @returns Array of [descriptor, virtualPath] pairs
123
- */
86
+ /** Get current preopens configuration. */
124
87
  export declare function _getPreopens(): [Descriptor, string][];
125
- /**
126
- * Create a preopen descriptor for a host path.
127
- * This is used internally to create isolated preopen instances.
128
- * @param hostPreopen - The host filesystem path
129
- * @returns A preopen descriptor
130
- */
131
- export declare function _createPreopenDescriptor(hostPreopen: string): Descriptor;
88
+ /** Reject host paths because browser filesystems require explicit capabilities. */
89
+ export declare function _createPreopenDescriptor(hostPreopen: string): void;
132
90
  export declare const types: typeof TypesNamespace;