wasm-platform-kit 0.0.0-stage → 0.2.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 +224 -2
- package/assets/wasi/LICENSE +220 -0
- package/assets/wasi/cli.d.ts +43 -0
- package/assets/wasi/cli.js +147 -0
- package/assets/wasi/clocks.d.ts +3 -0
- package/assets/wasi/clocks.js +55 -0
- package/assets/wasi/common.d.ts +5 -0
- package/assets/wasi/common.js +16 -0
- package/assets/wasi/config.d.ts +2 -0
- package/assets/wasi/config.js +7 -0
- package/assets/wasi/environment.d.ts +5 -0
- package/assets/wasi/environment.js +24 -0
- package/assets/wasi/filesystem.d.ts +117 -0
- package/assets/wasi/filesystem.js +341 -0
- package/assets/wasi/helpers/cli.d.ts +7 -0
- package/assets/wasi/helpers/cli.js +25 -0
- package/assets/wasi/helpers/filesystem.d.ts +6 -0
- package/assets/wasi/helpers/filesystem.js +10 -0
- package/assets/wasi/http.d.ts +13 -0
- package/assets/wasi/http.js +953 -0
- package/assets/wasi/in-memory-filesystem.d.ts +38 -0
- package/assets/wasi/in-memory-filesystem.js +620 -0
- package/assets/wasi/in-memory-http.d.ts +8 -0
- package/assets/wasi/in-memory-http.js +11 -0
- package/assets/wasi/in-memory-sockets.d.ts +34 -0
- package/assets/wasi/in-memory-sockets.js +294 -0
- package/assets/wasi/index.d.ts +7 -0
- package/assets/wasi/index.js +7 -0
- package/assets/wasi/io.d.ts +67 -0
- package/assets/wasi/io.js +366 -0
- package/assets/wasi/opfs-filesystem.d.ts +52 -0
- package/assets/wasi/opfs-filesystem.js +262 -0
- package/assets/wasi/random.d.ts +4 -0
- package/assets/wasi/random.js +51 -0
- package/assets/wasi/sockets.d.ts +9 -0
- package/assets/wasi/sockets.js +85 -0
- package/browser.mjs +209 -0
- package/client.mjs +138 -0
- package/index.d.ts +199 -0
- package/install.d.ts +22 -0
- package/install.mjs +176 -0
- package/native-registration.mjs +70 -0
- package/node.mjs +332 -0
- package/package.json +66 -4
- package/policy.mjs +433 -0
- package/worker.mjs +389 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { checkedU64 } from "./common.js";
|
|
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
|
+
}
|
|
19
|
+
export const monotonicClock = {
|
|
20
|
+
resolution() {
|
|
21
|
+
// usually we dont get sub-millisecond accuracy in the browser
|
|
22
|
+
// Note: is there a better way to determine this?
|
|
23
|
+
return BigInt(1e6);
|
|
24
|
+
},
|
|
25
|
+
now() {
|
|
26
|
+
// performance.now() is in milliseconds, but we want nanoseconds
|
|
27
|
+
return BigInt(Math.floor(performance.now() * 1e6));
|
|
28
|
+
},
|
|
29
|
+
subscribeInstant(instant) {
|
|
30
|
+
instant = checkedU64(instant, "instant");
|
|
31
|
+
const now = monotonicClock.now();
|
|
32
|
+
if (instant <= now) {
|
|
33
|
+
return pollableCreate();
|
|
34
|
+
}
|
|
35
|
+
return monotonicClock.subscribeDuration(instant - now);
|
|
36
|
+
},
|
|
37
|
+
subscribeDuration(duration) {
|
|
38
|
+
duration = checkedU64(duration, "duration");
|
|
39
|
+
if (duration === 0n) {
|
|
40
|
+
return pollableCreate();
|
|
41
|
+
}
|
|
42
|
+
return pollableCreate(timeout(duration));
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
export const wallClock = {
|
|
46
|
+
now() {
|
|
47
|
+
let now = Date.now(); // in milliseconds
|
|
48
|
+
const seconds = BigInt(Math.floor(now / 1e3));
|
|
49
|
+
const nanoseconds = (now % 1e3) * 1e6;
|
|
50
|
+
return { seconds, nanoseconds };
|
|
51
|
+
},
|
|
52
|
+
resolution() {
|
|
53
|
+
return { seconds: 0n, nanoseconds: 1e6 };
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { environment as EnvironmentNamespace } from "../../types/cli.js";
|
|
2
|
+
export declare function _setEnv(envObj: Record<string, string>): void;
|
|
3
|
+
export declare function _setArgs(args: string[]): void;
|
|
4
|
+
export declare function _setCwd(cwd: string): void;
|
|
5
|
+
export declare const environment: typeof EnvironmentNamespace;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { _setCwd as fsSetCwd } from "./config.js";
|
|
2
|
+
let _env = [];
|
|
3
|
+
let _args = [];
|
|
4
|
+
let _cwd = "/";
|
|
5
|
+
export function _setEnv(envObj) {
|
|
6
|
+
_env = Object.entries(envObj);
|
|
7
|
+
}
|
|
8
|
+
export function _setArgs(args) {
|
|
9
|
+
_args = args;
|
|
10
|
+
}
|
|
11
|
+
export function _setCwd(cwd) {
|
|
12
|
+
fsSetCwd((_cwd = cwd));
|
|
13
|
+
}
|
|
14
|
+
export const environment = {
|
|
15
|
+
getEnvironment() {
|
|
16
|
+
return _env;
|
|
17
|
+
},
|
|
18
|
+
getArguments() {
|
|
19
|
+
return _args;
|
|
20
|
+
},
|
|
21
|
+
initialCwd() {
|
|
22
|
+
return _cwd;
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { types as TypesNamespace, preopens as PreopensNamespace } from "../../types/filesystem.js";
|
|
2
|
+
import type { FileData } from "./in-memory-filesystem.js";
|
|
3
|
+
export { _setCwd } from "./config.js";
|
|
4
|
+
export { InMemoryFilesystemAdapter } from "./in-memory-filesystem.js";
|
|
5
|
+
export type { FileData, FileDataEntry } from "./in-memory-filesystem.js";
|
|
6
|
+
export { OpfsFilesystemAdapter, loadOpfsCapability } from "./opfs-filesystem.js";
|
|
7
|
+
export type { OpfsCapability } from "./opfs-filesystem.js";
|
|
8
|
+
type Filesize = TypesNamespace.Filesize;
|
|
9
|
+
type OpenFlags = TypesNamespace.OpenFlags;
|
|
10
|
+
type PathFlags = TypesNamespace.PathFlags;
|
|
11
|
+
export interface BrowserDirectoryEntryStream {
|
|
12
|
+
readDirectoryEntry(): TypesNamespace.DirectoryEntry | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface BrowserFilesystemDescriptor extends Omit<TypesNamespace.Descriptor, "isSameObject" | "linkAt" | "openAt" | "readDirectory" | "renameAt"> {
|
|
15
|
+
readDirectory(): BrowserDirectoryEntryStream;
|
|
16
|
+
linkAt(oldPathFlags: PathFlags, oldPath: string, newDescriptor: BrowserFilesystemDescriptor, newPath: string): void;
|
|
17
|
+
openAt(pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: TypesNamespace.DescriptorFlags): BrowserFilesystemDescriptor;
|
|
18
|
+
renameAt(oldPath: string, newDescriptor: BrowserFilesystemDescriptor, newPath: string): void;
|
|
19
|
+
isSameObject(other: BrowserFilesystemDescriptor): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Advisory locking, for host application code holding a descriptor directly
|
|
22
|
+
* (not part of the WASI guest-facing surface). Adapters that support it should
|
|
23
|
+
* implement all five; `tryLock*` return whether the lock was acquired instead
|
|
24
|
+
* of throwing.
|
|
25
|
+
*/
|
|
26
|
+
lockShared?(): void;
|
|
27
|
+
lockExclusive?(): void;
|
|
28
|
+
tryLockShared?(): boolean;
|
|
29
|
+
tryLockExclusive?(): boolean;
|
|
30
|
+
unlock?(): void;
|
|
31
|
+
}
|
|
32
|
+
export interface BrowserFilesystemAdapter<Capability = unknown> {
|
|
33
|
+
getRoot(capability: Capability): BrowserFilesystemDescriptor;
|
|
34
|
+
dispose?(): void | Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export interface BrowserFilesystemConfig<Capability> {
|
|
37
|
+
adapter: BrowserFilesystemAdapter<Capability>;
|
|
38
|
+
preopens: Record<string, Capability>;
|
|
39
|
+
}
|
|
40
|
+
declare class DirectoryEntryStream implements TypesNamespace.DirectoryEntryStream {
|
|
41
|
+
#private;
|
|
42
|
+
static _create(implementation: BrowserDirectoryEntryStream): DirectoryEntryStream;
|
|
43
|
+
readDirectoryEntry(): TypesNamespace.DirectoryEntry | undefined;
|
|
44
|
+
}
|
|
45
|
+
declare class Descriptor implements TypesNamespace.Descriptor {
|
|
46
|
+
#private;
|
|
47
|
+
_getImplementation(descriptor: Descriptor): BrowserFilesystemDescriptor;
|
|
48
|
+
static _create(implementation: BrowserFilesystemDescriptor): Descriptor;
|
|
49
|
+
readViaStream(offset: Filesize): import("../../types/interfaces/wasi-io-streams.js").InputStream;
|
|
50
|
+
writeViaStream(offset: Filesize): import("../../types/interfaces/wasi-io-streams.js").OutputStream;
|
|
51
|
+
appendViaStream(): import("../../types/interfaces/wasi-io-streams.js").OutputStream;
|
|
52
|
+
advise(offset: Filesize, length: Filesize, advice: TypesNamespace.Advice): void;
|
|
53
|
+
syncData(): void;
|
|
54
|
+
getFlags(): TypesNamespace.DescriptorFlags;
|
|
55
|
+
getType(): TypesNamespace.DescriptorType;
|
|
56
|
+
setSize(size: Filesize): void;
|
|
57
|
+
setTimes(dataAccessTimestamp: TypesNamespace.NewTimestamp, dataModificationTimestamp: TypesNamespace.NewTimestamp): void;
|
|
58
|
+
read(length: Filesize, offset: Filesize): [Uint8Array<ArrayBufferLike>, boolean];
|
|
59
|
+
write(buffer: Uint8Array, offset: Filesize): bigint;
|
|
60
|
+
readDirectory(): DirectoryEntryStream;
|
|
61
|
+
sync(): void;
|
|
62
|
+
createDirectoryAt(path: string): void;
|
|
63
|
+
stat(): TypesNamespace.DescriptorStat;
|
|
64
|
+
statAt(pathFlags: PathFlags, path: string): TypesNamespace.DescriptorStat;
|
|
65
|
+
setTimesAt(pathFlags: PathFlags, path: string, dataAccessTimestamp: TypesNamespace.NewTimestamp, dataModificationTimestamp: TypesNamespace.NewTimestamp): void;
|
|
66
|
+
linkAt(oldPathFlags: PathFlags, oldPath: string, newDescriptor: TypesNamespace.Descriptor, newPath: string): void;
|
|
67
|
+
openAt(pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: TypesNamespace.DescriptorFlags): Descriptor;
|
|
68
|
+
readlinkAt(path: string): string;
|
|
69
|
+
removeDirectoryAt(path: string): void;
|
|
70
|
+
renameAt(oldPath: string, newDescriptor: TypesNamespace.Descriptor, newPath: string): void;
|
|
71
|
+
symlinkAt(oldPath: string, newPath: string): void;
|
|
72
|
+
unlinkFileAt(path: string): void;
|
|
73
|
+
isSameObject(other: TypesNamespace.Descriptor): boolean;
|
|
74
|
+
metadataHash(): TypesNamespace.MetadataHashValue;
|
|
75
|
+
metadataHashAt(pathFlags: PathFlags, path: string): TypesNamespace.MetadataHashValue;
|
|
76
|
+
lockShared(): void | undefined;
|
|
77
|
+
lockExclusive(): void | undefined;
|
|
78
|
+
tryLockShared(): boolean;
|
|
79
|
+
tryLockExclusive(): boolean;
|
|
80
|
+
unlock(): void | undefined;
|
|
81
|
+
}
|
|
82
|
+
export declare const preopens: typeof PreopensNamespace;
|
|
83
|
+
/** Create isolated filesystem namespaces backed by an application-selected adapter. */
|
|
84
|
+
export declare function createFilesystem<Capability>({ adapter, preopens: configuredPreopens, }: BrowserFilesystemConfig<Capability>): {
|
|
85
|
+
types: typeof TypesNamespace;
|
|
86
|
+
preopens: typeof PreopensNamespace;
|
|
87
|
+
dispose(): void | Promise<void>;
|
|
88
|
+
};
|
|
89
|
+
export declare function _setFileData(fileData: FileData): void;
|
|
90
|
+
export declare function _getFileData(): string;
|
|
91
|
+
/**
|
|
92
|
+
* Replace all preopens with the given set.
|
|
93
|
+
* @param preopensConfig - Map of virtual paths to file data entries
|
|
94
|
+
*/
|
|
95
|
+
export declare function _setPreopens(preopensConfig: Record<string, FileData>): void;
|
|
96
|
+
/**
|
|
97
|
+
* Add a single preopen mapping.
|
|
98
|
+
* @param virtualPath - The virtual path visible to the guest
|
|
99
|
+
* @param fileData - The file data object representing the directory
|
|
100
|
+
*/
|
|
101
|
+
export declare function _addPreopen(virtualPath: string, fileData: FileData): void;
|
|
102
|
+
/**
|
|
103
|
+
* Add a single preopen backed by a custom adapter (e.g. `OpfsFilesystemAdapter`) instead of the
|
|
104
|
+
* default in-memory one. Lets a host wire an alternative `BrowserFilesystemAdapter` into the
|
|
105
|
+
* top-level `wasi:filesystem/preopens` singleton that transpiled components import statically.
|
|
106
|
+
* @param virtualPath - The virtual path visible to the guest
|
|
107
|
+
* @param adapter - The adapter that will back this preopen
|
|
108
|
+
* @param capability - The adapter-specific capability to load as the preopen's root
|
|
109
|
+
*/
|
|
110
|
+
export declare function _addPreopenWithAdapter<Capability>(virtualPath: string, adapter: BrowserFilesystemAdapter<Capability>, capability: Capability): void;
|
|
111
|
+
/** Clear all preopens, giving the guest no filesystem access. */
|
|
112
|
+
export declare function _clearPreopens(): void;
|
|
113
|
+
/** Get current preopens configuration. */
|
|
114
|
+
export declare function _getPreopens(): [Descriptor, string][];
|
|
115
|
+
/** Reject host paths because browser filesystems require explicit capabilities. */
|
|
116
|
+
export declare function _createPreopenDescriptor(hostPreopen: string): void;
|
|
117
|
+
export declare const types: typeof TypesNamespace;
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { environment } from "./environment.js";
|
|
2
|
+
import { InMemoryFilesystemAdapter } from "./in-memory-filesystem.js";
|
|
3
|
+
import { _setCwd } from "./config.js";
|
|
4
|
+
export { _setCwd } from "./config.js";
|
|
5
|
+
export { InMemoryFilesystemAdapter } from "./in-memory-filesystem.js";
|
|
6
|
+
export { OpfsFilesystemAdapter, loadOpfsCapability } from "./opfs-filesystem.js";
|
|
7
|
+
class DirectoryEntryStream {
|
|
8
|
+
#implementation;
|
|
9
|
+
static _create(implementation) {
|
|
10
|
+
const stream = new DirectoryEntryStream();
|
|
11
|
+
stream.#implementation = implementation;
|
|
12
|
+
return stream;
|
|
13
|
+
}
|
|
14
|
+
readDirectoryEntry() {
|
|
15
|
+
return this.#implementation.readDirectoryEntry();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const directoryEntryStreamCreate = DirectoryEntryStream._create;
|
|
19
|
+
// @ts-expect-error - Deleting static method
|
|
20
|
+
delete DirectoryEntryStream._create;
|
|
21
|
+
class Descriptor {
|
|
22
|
+
#implementation;
|
|
23
|
+
_getImplementation(descriptor) {
|
|
24
|
+
return descriptor.#implementation;
|
|
25
|
+
}
|
|
26
|
+
static _create(implementation) {
|
|
27
|
+
const descriptor = new Descriptor();
|
|
28
|
+
descriptor.#implementation = implementation;
|
|
29
|
+
return descriptor;
|
|
30
|
+
}
|
|
31
|
+
readViaStream(offset) {
|
|
32
|
+
return this.#implementation.readViaStream(offset);
|
|
33
|
+
}
|
|
34
|
+
writeViaStream(offset) {
|
|
35
|
+
return this.#implementation.writeViaStream(offset);
|
|
36
|
+
}
|
|
37
|
+
appendViaStream() {
|
|
38
|
+
return this.#implementation.appendViaStream();
|
|
39
|
+
}
|
|
40
|
+
advise(offset, length, advice) {
|
|
41
|
+
return this.#implementation.advise(offset, length, advice);
|
|
42
|
+
}
|
|
43
|
+
syncData() {
|
|
44
|
+
return this.#implementation.syncData();
|
|
45
|
+
}
|
|
46
|
+
getFlags() {
|
|
47
|
+
return this.#implementation.getFlags();
|
|
48
|
+
}
|
|
49
|
+
getType() {
|
|
50
|
+
return this.#implementation.getType();
|
|
51
|
+
}
|
|
52
|
+
setSize(size) {
|
|
53
|
+
return this.#implementation.setSize(size);
|
|
54
|
+
}
|
|
55
|
+
setTimes(dataAccessTimestamp, dataModificationTimestamp) {
|
|
56
|
+
return this.#implementation.setTimes(dataAccessTimestamp, dataModificationTimestamp);
|
|
57
|
+
}
|
|
58
|
+
read(length, offset) {
|
|
59
|
+
return this.#implementation.read(length, offset);
|
|
60
|
+
}
|
|
61
|
+
write(buffer, offset) {
|
|
62
|
+
return this.#implementation.write(buffer, offset);
|
|
63
|
+
}
|
|
64
|
+
readDirectory() {
|
|
65
|
+
return directoryEntryStreamCreate(this.#implementation.readDirectory());
|
|
66
|
+
}
|
|
67
|
+
sync() {
|
|
68
|
+
return this.#implementation.sync();
|
|
69
|
+
}
|
|
70
|
+
createDirectoryAt(path) {
|
|
71
|
+
return this.#implementation.createDirectoryAt(path);
|
|
72
|
+
}
|
|
73
|
+
stat() {
|
|
74
|
+
return this.#implementation.stat();
|
|
75
|
+
}
|
|
76
|
+
statAt(pathFlags, path) {
|
|
77
|
+
return this.#implementation.statAt(pathFlags, path);
|
|
78
|
+
}
|
|
79
|
+
setTimesAt(pathFlags, path, dataAccessTimestamp, dataModificationTimestamp) {
|
|
80
|
+
return this.#implementation.setTimesAt(pathFlags, path, dataAccessTimestamp, dataModificationTimestamp);
|
|
81
|
+
}
|
|
82
|
+
linkAt(oldPathFlags, oldPath, newDescriptor, newPath) {
|
|
83
|
+
return this.#implementation.linkAt(oldPathFlags, oldPath, descriptorGetImplementation(newDescriptor), newPath);
|
|
84
|
+
}
|
|
85
|
+
openAt(pathFlags, path, openFlags, flags) {
|
|
86
|
+
return descriptorCreate(this.#implementation.openAt(pathFlags, path, openFlags, flags));
|
|
87
|
+
}
|
|
88
|
+
readlinkAt(path) {
|
|
89
|
+
return this.#implementation.readlinkAt(path);
|
|
90
|
+
}
|
|
91
|
+
removeDirectoryAt(path) {
|
|
92
|
+
return this.#implementation.removeDirectoryAt(path);
|
|
93
|
+
}
|
|
94
|
+
renameAt(oldPath, newDescriptor, newPath) {
|
|
95
|
+
return this.#implementation.renameAt(oldPath, descriptorGetImplementation(newDescriptor), newPath);
|
|
96
|
+
}
|
|
97
|
+
symlinkAt(oldPath, newPath) {
|
|
98
|
+
return this.#implementation.symlinkAt(oldPath, newPath);
|
|
99
|
+
}
|
|
100
|
+
unlinkFileAt(path) {
|
|
101
|
+
return this.#implementation.unlinkFileAt(path);
|
|
102
|
+
}
|
|
103
|
+
isSameObject(other) {
|
|
104
|
+
return this.#implementation.isSameObject(descriptorGetImplementation(other));
|
|
105
|
+
}
|
|
106
|
+
metadataHash() {
|
|
107
|
+
return this.#implementation.metadataHash();
|
|
108
|
+
}
|
|
109
|
+
metadataHashAt(pathFlags, path) {
|
|
110
|
+
return this.#implementation.metadataHashAt(pathFlags, path);
|
|
111
|
+
}
|
|
112
|
+
lockShared() {
|
|
113
|
+
return this.#implementation.lockShared?.();
|
|
114
|
+
}
|
|
115
|
+
lockExclusive() {
|
|
116
|
+
return this.#implementation.lockExclusive?.();
|
|
117
|
+
}
|
|
118
|
+
tryLockShared() {
|
|
119
|
+
return this.#implementation.tryLockShared?.() ?? false;
|
|
120
|
+
}
|
|
121
|
+
tryLockExclusive() {
|
|
122
|
+
return this.#implementation.tryLockExclusive?.() ?? false;
|
|
123
|
+
}
|
|
124
|
+
unlock() {
|
|
125
|
+
return this.#implementation.unlock?.();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const descriptorGetImplementation = Descriptor.prototype._getImplementation;
|
|
129
|
+
// @ts-expect-error - Deleting prototype method
|
|
130
|
+
delete Descriptor.prototype._getImplementation;
|
|
131
|
+
const descriptorCreate = Descriptor._create;
|
|
132
|
+
// @ts-expect-error - Deleting static method
|
|
133
|
+
delete Descriptor._create;
|
|
134
|
+
const defaultAdapter = new InMemoryFilesystemAdapter();
|
|
135
|
+
let _fileData = { dir: {} };
|
|
136
|
+
let _preopens = [];
|
|
137
|
+
let _rootPreopen = null;
|
|
138
|
+
export const preopens = {
|
|
139
|
+
getDirectories() {
|
|
140
|
+
return _preopens;
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
/** Create isolated filesystem namespaces backed by an application-selected adapter. */
|
|
144
|
+
export function createFilesystem({ adapter, preopens: configuredPreopens, }) {
|
|
145
|
+
const entries = Object.entries(configuredPreopens).map(([guestPath, capability]) => [descriptorCreate(adapter.getRoot(capability)), guestPath]);
|
|
146
|
+
let disposed = false;
|
|
147
|
+
let disposal;
|
|
148
|
+
return {
|
|
149
|
+
types,
|
|
150
|
+
preopens: {
|
|
151
|
+
getDirectories() {
|
|
152
|
+
if (disposed) {
|
|
153
|
+
throw new Error("filesystem adapter has been disposed");
|
|
154
|
+
}
|
|
155
|
+
return [...entries];
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
dispose() {
|
|
159
|
+
if (disposed) {
|
|
160
|
+
return disposal;
|
|
161
|
+
}
|
|
162
|
+
disposed = true;
|
|
163
|
+
disposal = adapter.dispose?.();
|
|
164
|
+
return disposal;
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export function _setFileData(fileData) {
|
|
169
|
+
_fileData = fileData;
|
|
170
|
+
if (_rootPreopen) {
|
|
171
|
+
_rootPreopen[0] = descriptorCreate(defaultAdapter.getRoot(fileData));
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
_setPreopens({ "/": fileData });
|
|
175
|
+
}
|
|
176
|
+
const cwd = environment.initialCwd();
|
|
177
|
+
_setCwd(cwd || "/");
|
|
178
|
+
}
|
|
179
|
+
export function _getFileData() {
|
|
180
|
+
return JSON.stringify(_fileData);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Replace all preopens with the given set.
|
|
184
|
+
* @param preopensConfig - Map of virtual paths to file data entries
|
|
185
|
+
*/
|
|
186
|
+
export function _setPreopens(preopensConfig) {
|
|
187
|
+
_preopens = [];
|
|
188
|
+
_rootPreopen = null;
|
|
189
|
+
for (const [virtualPath, fileData] of Object.entries(preopensConfig)) {
|
|
190
|
+
_addPreopen(virtualPath, fileData);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Add a single preopen mapping.
|
|
195
|
+
* @param virtualPath - The virtual path visible to the guest
|
|
196
|
+
* @param fileData - The file data object representing the directory
|
|
197
|
+
*/
|
|
198
|
+
export function _addPreopen(virtualPath, fileData) {
|
|
199
|
+
const descriptor = descriptorCreate(defaultAdapter.getRoot(fileData));
|
|
200
|
+
const entry = [descriptor, virtualPath];
|
|
201
|
+
_preopens.push(entry);
|
|
202
|
+
if (virtualPath === "/") {
|
|
203
|
+
_rootPreopen = entry;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Add a single preopen backed by a custom adapter (e.g. `OpfsFilesystemAdapter`) instead of the
|
|
208
|
+
* default in-memory one. Lets a host wire an alternative `BrowserFilesystemAdapter` into the
|
|
209
|
+
* top-level `wasi:filesystem/preopens` singleton that transpiled components import statically.
|
|
210
|
+
* @param virtualPath - The virtual path visible to the guest
|
|
211
|
+
* @param adapter - The adapter that will back this preopen
|
|
212
|
+
* @param capability - The adapter-specific capability to load as the preopen's root
|
|
213
|
+
*/
|
|
214
|
+
export function _addPreopenWithAdapter(virtualPath, adapter, capability) {
|
|
215
|
+
const descriptor = descriptorCreate(adapter.getRoot(capability));
|
|
216
|
+
const entry = [descriptor, virtualPath];
|
|
217
|
+
_preopens.push(entry);
|
|
218
|
+
if (virtualPath === "/") {
|
|
219
|
+
_rootPreopen = entry;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/** Clear all preopens, giving the guest no filesystem access. */
|
|
223
|
+
export function _clearPreopens() {
|
|
224
|
+
_preopens = [];
|
|
225
|
+
_rootPreopen = null;
|
|
226
|
+
}
|
|
227
|
+
/** Get current preopens configuration. */
|
|
228
|
+
export function _getPreopens() {
|
|
229
|
+
return [..._preopens];
|
|
230
|
+
}
|
|
231
|
+
/** Reject host paths because browser filesystems require explicit capabilities. */
|
|
232
|
+
export function _createPreopenDescriptor(hostPreopen) {
|
|
233
|
+
throw new TypeError(`browser preopen ${JSON.stringify(hostPreopen)} is a host path; configure browser file data or an adapter instead`);
|
|
234
|
+
}
|
|
235
|
+
export const types = {
|
|
236
|
+
Descriptor,
|
|
237
|
+
DirectoryEntryStream,
|
|
238
|
+
filesystemErrorCode: (err) => {
|
|
239
|
+
let message;
|
|
240
|
+
if ("payload" in err) {
|
|
241
|
+
message = err.payload;
|
|
242
|
+
}
|
|
243
|
+
else if ("message" in err) {
|
|
244
|
+
message = err.message;
|
|
245
|
+
}
|
|
246
|
+
return convertFsError(message);
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
function convertFsError(e) {
|
|
250
|
+
switch (e.code) {
|
|
251
|
+
case "EACCES":
|
|
252
|
+
return "access";
|
|
253
|
+
case "EAGAIN":
|
|
254
|
+
case "EWOULDBLOCK":
|
|
255
|
+
return "would-block";
|
|
256
|
+
case "EALREADY":
|
|
257
|
+
return "already";
|
|
258
|
+
case "EBADF":
|
|
259
|
+
return "bad-descriptor";
|
|
260
|
+
case "EBUSY":
|
|
261
|
+
return "busy";
|
|
262
|
+
case "EDEADLK":
|
|
263
|
+
return "deadlock";
|
|
264
|
+
case "EDQUOT":
|
|
265
|
+
return "quota";
|
|
266
|
+
case "EEXIST":
|
|
267
|
+
return "exist";
|
|
268
|
+
case "EFBIG":
|
|
269
|
+
return "file-too-large";
|
|
270
|
+
case "EILSEQ":
|
|
271
|
+
return "illegal-byte-sequence";
|
|
272
|
+
case "EINPROGRESS":
|
|
273
|
+
return "in-progress";
|
|
274
|
+
case "EINTR":
|
|
275
|
+
return "interrupted";
|
|
276
|
+
case "EINVAL":
|
|
277
|
+
return "invalid";
|
|
278
|
+
case "EIO":
|
|
279
|
+
return "io";
|
|
280
|
+
case "EISDIR":
|
|
281
|
+
return "is-directory";
|
|
282
|
+
case "ELOOP":
|
|
283
|
+
return "loop";
|
|
284
|
+
case "EMLINK":
|
|
285
|
+
return "too-many-links";
|
|
286
|
+
case "EMSGSIZE":
|
|
287
|
+
return "message-size";
|
|
288
|
+
case "ENAMETOOLONG":
|
|
289
|
+
return "name-too-long";
|
|
290
|
+
case "ENODEV":
|
|
291
|
+
return "no-device";
|
|
292
|
+
case "ENOENT":
|
|
293
|
+
return "no-entry";
|
|
294
|
+
case "ENOLCK":
|
|
295
|
+
return "no-lock";
|
|
296
|
+
case "ENOMEM":
|
|
297
|
+
return "insufficient-memory";
|
|
298
|
+
case "ENOSPC":
|
|
299
|
+
return "insufficient-space";
|
|
300
|
+
case "ENOTDIR":
|
|
301
|
+
case "ERR_FS_EISDIR":
|
|
302
|
+
return "not-directory";
|
|
303
|
+
case "ENOTEMPTY":
|
|
304
|
+
return "not-empty";
|
|
305
|
+
case "ENOTRECOVERABLE":
|
|
306
|
+
return "not-recoverable";
|
|
307
|
+
case "ENOTSUP":
|
|
308
|
+
return "unsupported";
|
|
309
|
+
case "ENOTTY":
|
|
310
|
+
return "no-tty";
|
|
311
|
+
// windows gives this error for badly structured `//` reads
|
|
312
|
+
// this seems like a slightly better error than unknown given
|
|
313
|
+
// that it's a common footgun
|
|
314
|
+
case -4094:
|
|
315
|
+
case "ENXIO":
|
|
316
|
+
return "no-such-device";
|
|
317
|
+
case "EOVERFLOW":
|
|
318
|
+
return "overflow";
|
|
319
|
+
case "EPERM":
|
|
320
|
+
return "not-permitted";
|
|
321
|
+
case "EPIPE":
|
|
322
|
+
return "pipe";
|
|
323
|
+
case "EROFS":
|
|
324
|
+
return "read-only";
|
|
325
|
+
case "ESPIPE":
|
|
326
|
+
return "invalid-seek";
|
|
327
|
+
case "ETXTBSY":
|
|
328
|
+
return "text-file-busy";
|
|
329
|
+
case "EXDEV":
|
|
330
|
+
return "cross-device";
|
|
331
|
+
case "UNKNOWN":
|
|
332
|
+
switch (e.errno) {
|
|
333
|
+
case -4094:
|
|
334
|
+
return "no-such-device";
|
|
335
|
+
default:
|
|
336
|
+
throw e;
|
|
337
|
+
}
|
|
338
|
+
default:
|
|
339
|
+
throw e;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type InputStreamHandler } from "../io.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build an `InputStreamHandler` that serves a fixed buffer, then reports `closed`.
|
|
4
|
+
*
|
|
5
|
+
* Pass the result to `createCli({ stdin })` or `_setStdin` to feed a guest a fixed stdin.
|
|
6
|
+
*/
|
|
7
|
+
export declare function bufferedInputStream(input: Uint8Array | string): InputStreamHandler;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { pollableCreate } from "../io.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build an `InputStreamHandler` that serves a fixed buffer, then reports `closed`.
|
|
4
|
+
*
|
|
5
|
+
* Pass the result to `createCli({ stdin })` or `_setStdin` to feed a guest a fixed stdin.
|
|
6
|
+
*/
|
|
7
|
+
export function bufferedInputStream(input) {
|
|
8
|
+
const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
9
|
+
let offset = 0;
|
|
10
|
+
return {
|
|
11
|
+
blockingRead(len) {
|
|
12
|
+
if (offset >= bytes.length) {
|
|
13
|
+
throw { tag: "closed" };
|
|
14
|
+
}
|
|
15
|
+
const remaining = bytes.length - offset;
|
|
16
|
+
const toRead = len < BigInt(remaining) ? Number(len) : remaining;
|
|
17
|
+
const chunk = bytes.slice(offset, offset + toRead);
|
|
18
|
+
offset += toRead;
|
|
19
|
+
return chunk;
|
|
20
|
+
},
|
|
21
|
+
subscribe() {
|
|
22
|
+
return pollableCreate();
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type WriteFileAtOptions } from "../../common/helpers/filesystem.js";
|
|
2
|
+
export type { WriteFileAtOptions };
|
|
3
|
+
/** Write bytes into a preopened directory's tree from host JS, without a guest. */
|
|
4
|
+
export declare function writeFileAt(virtualPath: string, path: string, data: Uint8Array, options?: WriteFileAtOptions): void;
|
|
5
|
+
/** Read the full contents of a file under a preopened directory from host JS, without a guest. */
|
|
6
|
+
export declare function readFileAt(virtualPath: string, path: string): Uint8Array;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { _getPreopens } from "../filesystem.js";
|
|
2
|
+
import { writeFileAt as writeFileAtImpl, readFileAt as readFileAtImpl, } from "../../common/helpers/filesystem.js";
|
|
3
|
+
/** Write bytes into a preopened directory's tree from host JS, without a guest. */
|
|
4
|
+
export function writeFileAt(virtualPath, path, data, options = {}) {
|
|
5
|
+
writeFileAtImpl(_getPreopens(), virtualPath, path, data, options);
|
|
6
|
+
}
|
|
7
|
+
/** Read the full contents of a file under a preopened directory from host JS, without a guest. */
|
|
8
|
+
export function readFileAt(virtualPath, path) {
|
|
9
|
+
return readFileAtImpl(_getPreopens(), virtualPath, path);
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { incomingHandler as IncomingHandlerNamespace, outgoingHandler as OutgoingHandlerNamespace, types as TypesNamespace } from "../../types/http.js";
|
|
2
|
+
export { InMemoryHttpClient } from "./in-memory-http.js";
|
|
3
|
+
/** Enable or disable Fetch `ReadableStream` request bodies. Disabled by default. */
|
|
4
|
+
export declare function _setRequestStreaming(enabled: boolean): void;
|
|
5
|
+
export declare const outgoingHandler: typeof OutgoingHandlerNamespace;
|
|
6
|
+
export declare const incomingHandler: typeof IncomingHandlerNamespace;
|
|
7
|
+
export type WasiIncomingHandler = (request: TypesNamespace.IncomingRequest, responseOut: TypesNamespace.ResponseOutparam) => void | Promise<void>;
|
|
8
|
+
export type WebIncomingHandler = (request: Request) => Response | Promise<Response>;
|
|
9
|
+
/** Create a `wasi:http/incoming-handler` namespace backed by a Web Request handler. */
|
|
10
|
+
export declare function createIncomingHandler(handler: WebIncomingHandler): typeof IncomingHandlerNamespace;
|
|
11
|
+
/** Translate a browser Request through a WASI handler with a synchronously readable body. */
|
|
12
|
+
export declare function handleIncomingRequest(request: Request, handler: WasiIncomingHandler): Promise<Response>;
|
|
13
|
+
export declare const types: typeof TypesNamespace;
|