@bytecodealliance/preview2-shim
Advanced tools
Comparing version 0.0.17 to 0.0.18
import * as clocks from "./clocks.js"; | ||
import * as filesystem from "./filesystem.js"; | ||
import * as http from "./http.js"; | ||
import * as io from "./io.js"; | ||
import * as logging from "./logging.js"; | ||
import * as io from "../common/io.js"; | ||
import * as poll from "./poll.js"; | ||
@@ -16,3 +15,2 @@ import * as random from "./random.js"; | ||
io, | ||
logging, | ||
poll, | ||
@@ -19,0 +17,0 @@ random, |
@@ -1,6 +0,9 @@ | ||
export class IgnoreStream { | ||
read (_len) { | ||
return [new Uint8Array([]), 'ended']; | ||
let id = 0; | ||
class Error { | ||
constructor (msg) { | ||
this.msg = msg; | ||
} | ||
write (_bytes) { | ||
toDebugString () { | ||
return this.msg; | ||
} | ||
@@ -10,111 +13,154 @@ } | ||
/** | ||
* @typedef {{ read: (len: number) => [Uint8Array, 'open' | 'ended'], drop?: () => {} }} ReadableStream | ||
* @typedef {{ write: (buf: Uint8Array) {}, drop?: () => {}, flush?: () => {}, check?: () => BigInt }} WriteableStream | ||
*/ | ||
* @typedef {{ | ||
* read?: (len: BigInt) => Uint8Array, | ||
* blockingRead: (len: BigInt) => Uint8Array, | ||
* skip?: (len: BigInt) => BigInt, | ||
* blockingSkip?: (len: BigInt) => BigInt, | ||
* subscribe: () => void, | ||
* drop: () => void, | ||
* }} InputStreamHandler | ||
* | ||
* @typedef {{ | ||
* checkWrite?: () -> BigInt, | ||
* write: (buf: Uint8Array) => BigInt, | ||
* blockingWriteAndFlush?: (buf: Uint8Array) => void, | ||
* flush?: () => void, | ||
* blockingFlush: () => void, | ||
* writeZeroes?: (len: BigInt) => void, | ||
* blockingWriteZeroes?: (len: BigInt) => void, | ||
* blockingWriteZeroesAndFlush?: (len: BigInt) => void, | ||
* splice?: (src: InputStream, len: BigInt) => BigInt, | ||
* blockingSplice?: (src: InputStream, len: BigInt) => BigInt, | ||
* forward?: (src: InputStream) => void, | ||
* subscribe?: () => void, | ||
* drop: () => void, | ||
* }} OutputStreamHandler | ||
* | ||
**/ | ||
// NOTE: pending asyncify work, all stream methods are synchronous and blocking | ||
export class Io { | ||
constructor ( | ||
stdout = new IgnoreStream(), | ||
stderr = new IgnoreStream(), | ||
stdin = new IgnoreStream() | ||
) { | ||
this.streamCnt = 3; | ||
this.streamEntries = { | ||
0: stdin, | ||
1: stdout, | ||
2: stderr | ||
class InputStream { | ||
/** | ||
* @param {InputStreamHandler} handler | ||
*/ | ||
constructor (handler) { | ||
if (!handler) | ||
console.trace('no handler'); | ||
this.id = ++id; | ||
this.handler = handler; | ||
} | ||
read(len) { | ||
if (this.handler.read) | ||
return this.handler.read(len); | ||
return this.handler.blockingRead.call(this, len); | ||
} | ||
blockingRead(len) { | ||
return this.handler.blockingRead.call(this, len); | ||
} | ||
skip(len) { | ||
if (this.handler.skip) | ||
return this.handler.skip.call(this, len); | ||
if (this.handler.read) { | ||
const bytes = this.handler.read.call(this, len); | ||
return BigInt(bytes.byteLength); | ||
} | ||
const io = this; | ||
this.streams = { | ||
read(s, len) { | ||
return io.getStream(s).read(len); | ||
}, | ||
blockingRead(s, len) { | ||
return io.getStream(s).read(len); | ||
}, | ||
skip(s, _len) { | ||
console.log(`[streams] Skip ${s}`); | ||
}, | ||
blockingSkip(s, _len) { | ||
console.log(`[streams] Blocking skip ${s}`); | ||
}, | ||
subscribeToInputStream(s) { | ||
console.log(`[streams] Subscribe to input stream ${s}`); | ||
}, | ||
dropInputStream(s) { | ||
io.dropStream(s); | ||
}, | ||
checkWrite(s) { | ||
return io.getStream(s).check() || 1000_000n; | ||
}, | ||
write(s, buf) { | ||
io.getStream(s).write(buf); | ||
}, | ||
blockingWriteAndFlush(s, buf) { | ||
const stream = io.getStream(s); | ||
stream.write(buf); | ||
if (stream.flush) | ||
stream.flush(); | ||
}, | ||
flush(s) { | ||
const stream = io.getStream(s); | ||
if (stream.flush) | ||
stream.flush(); | ||
}, | ||
blockingFlush(s) { | ||
const stream = io.getStream(s); | ||
if (stream.flush) | ||
stream.flush(); | ||
}, | ||
writeZeroes(s, _len) { | ||
console.log(`[streams] Write zeroes ${s}`); | ||
}, | ||
blockingWriteZeroes(s, _len) { | ||
console.log(`[streams] Blocking write zeroes ${s}`); | ||
}, | ||
splice(s, _src, _len) { | ||
console.log(`[streams] Splice ${s}`); | ||
}, | ||
blockingSplice(s, _src, _len) { | ||
console.log(`[streams] Blocking splice ${s}`); | ||
}, | ||
forward(s, _src) { | ||
console.log(`[streams] Forward ${s}`); | ||
}, | ||
subscribeToOutputStream(s) { | ||
console.log(`[streams] Subscribe to output stream ${s}`); | ||
}, | ||
dropOutputStream(s) { | ||
io.dropStream(s); | ||
} | ||
}; | ||
return this.blockingSkip.call(this, len); | ||
} | ||
blockingSkip(len) { | ||
if (this.handler.blockingSkip) | ||
return this.handler.blockingSkip.call(this, len); | ||
const bytes = this.handler.blockingRead.call(this, len); | ||
return BigInt(bytes.byteLength); | ||
} | ||
subscribe() { | ||
console.log(`[streams] Subscribe to input stream ${this.id}`); | ||
} | ||
drop () { | ||
if (this.handler.drop) | ||
this.handler.drop.call(this); | ||
} | ||
} | ||
class OutputStream { | ||
/** | ||
* | ||
* @param {ReadableStream | WriteableStream} stream | ||
* @returns {number} | ||
* @param {OutputStreamHandler} handler | ||
*/ | ||
createStream (stream) { | ||
this.streamEntries[this.streamCnt] = stream; | ||
return this.streamCnt++; | ||
constructor (handler) { | ||
if (!handler) | ||
console.trace('no handler'); | ||
this.id = ++id; | ||
this.open = true; | ||
this.handler = handler; | ||
} | ||
/** | ||
* @param {number} sid | ||
* @returns {ReadableStream | WriteableStream} | ||
*/ | ||
getStream (sid) { | ||
const stream = this.streamEntries[sid]; | ||
if (!stream) throw new Error(); | ||
return stream; | ||
checkWrite(len) { | ||
if (!this.open) | ||
return 0n; | ||
if (this.handler.checkWrite) | ||
return this.handler.checkWrite.call(this, len); | ||
return 1_000_000n; | ||
} | ||
dropStream (sid) { | ||
const stream = this.streamEntries[sid]; | ||
if (stream.drop) stream.drop(); | ||
delete this.streamEntries[sid]; | ||
write(buf) { | ||
this.handler.write.call(this, buf); | ||
} | ||
blockingWriteAndFlush(buf) { | ||
/// Perform a write of up to 4096 bytes, and then flush the stream. Block | ||
/// until all of these operations are complete, or an error occurs. | ||
/// | ||
/// This is a convenience wrapper around the use of `check-write`, | ||
/// `subscribe`, `write`, and `flush`, and is implemented with the | ||
/// following pseudo-code: | ||
/// | ||
/// ```text | ||
/// let pollable = this.subscribe(); | ||
/// while !contents.is_empty() { | ||
/// // Wait for the stream to become writable | ||
/// poll-one(pollable); | ||
/// let Ok(n) = this.check-write(); // eliding error handling | ||
/// let len = min(n, contents.len()); | ||
/// let (chunk, rest) = contents.split_at(len); | ||
/// this.write(chunk ); // eliding error handling | ||
/// contents = rest; | ||
/// } | ||
/// this.flush(); | ||
/// // Wait for completion of `flush` | ||
/// poll-one(pollable); | ||
/// // Check for any errors that arose during `flush` | ||
/// let _ = this.check-write(); // eliding error handling | ||
/// ``` | ||
this.handler.write.call(this, buf); | ||
} | ||
flush() { | ||
if (this.handler.flush) | ||
this.handler.flush.call(this); | ||
} | ||
blockingFlush() { | ||
this.open = true; | ||
} | ||
writeZeroes(len) { | ||
this.write.call(this, new Uint8Array(Number(len))); | ||
} | ||
blockingWriteZeroes(len) { | ||
this.blockingWrite.call(this, new Uint8Array(Number(len))); | ||
} | ||
blockingWriteZeroesAndFlush(len) { | ||
this.blockingWriteAndFlush.call(this, new Uint8Array(Number(len))); | ||
} | ||
splice(src, len) { | ||
const spliceLen = Math.min(len, this.checkWrite.call(this)); | ||
const bytes = src.read(spliceLen); | ||
this.write.call(this, bytes); | ||
return bytes.byteLength; | ||
} | ||
blockingSplice(_src, _len) { | ||
console.log(`[streams] Blocking splice ${this.id}`); | ||
} | ||
forward(_src) { | ||
console.log(`[streams] Forward ${this.id}`); | ||
} | ||
subscribe() { | ||
console.log(`[streams] Subscribe to output stream ${this.id}`); | ||
} | ||
drop() { | ||
} | ||
} | ||
export const streams = { Error, InputStream, OutputStream }; |
import { argv, env, cwd } from 'node:process'; | ||
import { streams } from '../common/io.js'; | ||
const { InputStream, OutputStream } = streams; | ||
@@ -23,5 +25,38 @@ let _env = Object.entries(env), _args = argv, _cwd = cwd(); | ||
const stdinStream = new InputStream({ | ||
blockingRead (_len) { | ||
// TODO | ||
}, | ||
subscribe () { | ||
// TODO | ||
}, | ||
drop () { | ||
// TODO | ||
} | ||
}); | ||
const stdoutStream = new OutputStream({ | ||
write (contents) { | ||
process.stdout.write(contents); | ||
}, | ||
blockingFlush () { | ||
}, | ||
drop () { | ||
} | ||
}); | ||
const stderrStream = new OutputStream({ | ||
write (contents) { | ||
process.stderr.write(contents); | ||
}, | ||
blockingFlush () { | ||
}, | ||
drop () { | ||
} | ||
}); | ||
export const stdin = { | ||
InputStream, | ||
getStdin () { | ||
return 0; | ||
return stdinStream; | ||
} | ||
@@ -31,4 +66,5 @@ }; | ||
export const stdout = { | ||
OutputStream, | ||
getStdout () { | ||
return 1; | ||
return stdoutStream; | ||
} | ||
@@ -38,8 +74,17 @@ }; | ||
export const stderr = { | ||
OutputStream, | ||
getStderr () { | ||
return 2; | ||
return stderrStream; | ||
} | ||
}; | ||
class TerminalInput {} | ||
class TerminalOutput {} | ||
const terminalStdoutInstance = new TerminalOutput(); | ||
const terminalStderrInstance = new TerminalOutput(); | ||
const terminalStdinInstance = new TerminalInput(); | ||
export const terminalInput = { | ||
TerminalInput, | ||
dropTerminalInput () {} | ||
@@ -49,2 +94,3 @@ }; | ||
export const terminalOutput = { | ||
TerminalOutput, | ||
dropTerminalOutput () {} | ||
@@ -54,4 +100,5 @@ }; | ||
export const terminalStderr = { | ||
TerminalOutput, | ||
getTerminalStderr () { | ||
return 0; | ||
return terminalStderrInstance; | ||
} | ||
@@ -61,4 +108,5 @@ }; | ||
export const terminalStdin = { | ||
TerminalInput, | ||
getTerminalStdin () { | ||
return 1; | ||
return terminalStdinInstance; | ||
} | ||
@@ -68,5 +116,6 @@ }; | ||
export const terminalStdout = { | ||
TerminalOutput, | ||
getTerminalStdout () { | ||
return 2; | ||
return terminalStdoutInstance; | ||
} | ||
}; |
@@ -1,62 +0,10 @@ | ||
import { _io } from './io.js'; | ||
import { streams } from '../common/io.js'; | ||
import { environment } from './cli.js'; | ||
import { constants, readSync, openSync, opendirSync, closeSync, fstatSync, lstatSync, statSync, writeSync } from 'node:fs'; | ||
import { constants, readSync, openSync, opendirSync, closeSync, fstatSync, lstatSync, statSync, writeSync, mkdirSync } from 'node:fs'; | ||
import { platform } from 'node:process'; | ||
const { InputStream, OutputStream, Error: StreamError } = streams; | ||
const isWindows = platform === 'win32'; | ||
class ReadableFileStream { | ||
constructor (hostFd, position) { | ||
this.hostFd = hostFd; | ||
this.position = Number(position); | ||
} | ||
read (len) { | ||
const buf = new Uint8Array(Number(len)); | ||
const bytesRead = readSync(this.hostFd, buf, 0, buf.byteLength, this.position); | ||
this.position += bytesRead; | ||
if (bytesRead < buf.byteLength) | ||
return [new Uint8Array(buf.buffer, 0, bytesRead), bytesRead === 0 ? 'ended' : 'open']; | ||
return [buf, 'open']; | ||
} | ||
} | ||
class WriteableFileStream { | ||
constructor (hostFd, position) { | ||
this.hostFd = hostFd; | ||
this.position = Number(position); | ||
} | ||
write (contents) { | ||
let totalWritten = 0; | ||
while (totalWritten !== contents.byteLength) { | ||
const bytesWritten = writeSync(this.hostFd, contents, null, null, this.position); | ||
totalWritten += bytesWritten; | ||
contents = new Uint8Array(contents.buffer, bytesWritten); | ||
} | ||
this.position += contents.byteLength; | ||
} | ||
} | ||
class DirStream { | ||
constructor (dir) { | ||
this.dir = dir; | ||
} | ||
read () { | ||
let entry; | ||
try { | ||
entry = this.dir.readSync(); | ||
} catch (e) { | ||
throw convertFsError(e); | ||
} | ||
if (entry === null) { | ||
return null; | ||
} | ||
const name = entry.name; | ||
const type = lookupType(entry); | ||
return { name, type }; | ||
} | ||
drop () { | ||
this.dir.closeSync(); | ||
} | ||
} | ||
const nsMagnitude = 1_000_000_000_000n; | ||
@@ -89,14 +37,7 @@ function nsToDateTime (ns) { | ||
* @typedef { | ||
* { stream: number } | | ||
* { hostPreopen: string } | | ||
* { fullPath: string, fd: number } | ||
* } Descriptor | ||
* } DescriptorProps | ||
*/ | ||
export class FileSystem { | ||
getDescriptor (fd) { | ||
const descriptor = this.descriptors[fd]; | ||
if (!descriptor) throw 'bad-descriptor'; | ||
return descriptor; | ||
} | ||
// Note: This should implement per-segment semantics of openAt, but we cannot currently | ||
@@ -107,3 +48,3 @@ // due to the lack of support for openat() in Node.js. | ||
// TODO: support followSymlinks | ||
getFullPath (fd, subpath, _followSymlinks) { | ||
getFullPath (descriptor, subpath, _followSymlinks) { | ||
if (subpath.indexOf('\\') !== -1) | ||
@@ -120,3 +61,3 @@ subpath = subpath.replace(/\\/g, '/'); | ||
throw 'no-entry'; | ||
fd = bestPreopenMatch[0]; | ||
descriptor = bestPreopenMatch[0]; | ||
subpath = subpath.slice(bestPreopenMatch[1]); | ||
@@ -128,3 +69,2 @@ if (subpath[0] === '/') | ||
subpath = subpath.slice(subpath[1] === '/' ? 2 : 1); | ||
const descriptor = this.getDescriptor(fd); | ||
if (descriptor.hostPreopen) | ||
@@ -137,106 +77,157 @@ return descriptor.hostPreopen + (descriptor.hostPreopen.endsWith('/') ? '' : '/') + subpath; | ||
* | ||
* @param {import('./io.js').Io} io | ||
* @param {[string, string][]} preopens | ||
* @param {import('./cli.js').environment} environment | ||
* @returns | ||
*/ | ||
constructor (io, preopens, environment) { | ||
constructor (preopens, environment) { | ||
const fs = this; | ||
this.cwd = environment.initialCwd(); | ||
// io always has streams 0, 1, 2 as stdio streams | ||
this.descriptorCnt = 3; | ||
class FileInputStream extends InputStream { | ||
constructor (hostFd, position) { | ||
super({ | ||
blockingRead (len) { | ||
const buf = new Uint8Array(Number(len)); | ||
try { | ||
var bytesRead = readSync(this.hostFd, buf, 0, buf.byteLength, this.position); | ||
} catch (e) { | ||
throw { tag: 'last-operation-failed', val: new StreamError(e.message) }; | ||
} | ||
this.position += bytesRead; | ||
if (bytesRead < buf.byteLength) { | ||
if (bytesRead === 0) | ||
throw { tag: 'closed' }; | ||
return new Uint8Array(buf.buffer, 0, bytesRead); | ||
} | ||
return buf; | ||
}, | ||
subscribe () { | ||
// TODO | ||
}, | ||
drop () { | ||
// TODO | ||
} | ||
}); | ||
this.hostFd = hostFd; | ||
this.position = Number(position); | ||
} | ||
} | ||
class FileOutputStream extends OutputStream { | ||
constructor (hostFd, position) { | ||
super({ | ||
write (contents) { | ||
let totalWritten = 0; | ||
while (totalWritten !== contents.byteLength) { | ||
const bytesWritten = writeSync(this.hostFd, contents, null, null, this.position); | ||
totalWritten += bytesWritten; | ||
contents = new Uint8Array(contents.buffer, bytesWritten); | ||
} | ||
this.position += contents.byteLength; | ||
}, | ||
blockingFlush () { | ||
}, | ||
drop () { | ||
} | ||
}); | ||
this.hostFd = hostFd; | ||
this.position = Number(position); | ||
} | ||
} | ||
class DirectoryEntryStream { | ||
constructor (dir) { | ||
this.dir = dir; | ||
} | ||
readDirectoryEntry () { | ||
let entry; | ||
try { | ||
entry = this.dir.readSync(); | ||
} catch (e) { | ||
throw convertFsError(e); | ||
} | ||
if (entry === null) { | ||
return null; | ||
} | ||
const name = entry.name; | ||
const type = lookupType(entry); | ||
return { name, type }; | ||
} | ||
drop () { | ||
this.dir.closeSync(); | ||
} | ||
} | ||
/** | ||
* @type {Record<number, Descriptor>} | ||
* @implements {DescriptorProps} | ||
*/ | ||
this.descriptors = { | ||
0: { stream: 0 }, | ||
1: { stream: 1 }, | ||
2: { stream: 2 }, | ||
}; | ||
this.preopenEntries = []; | ||
for (const [virtualPath, hostPreopen] of Object.entries(preopens)) { | ||
const preopenEntry = [this.descriptorCnt, virtualPath]; | ||
this.preopenEntries.push(preopenEntry); | ||
this.descriptors[this.descriptorCnt++] = { hostPreopen }; | ||
} | ||
const fs = this; | ||
this.preopens = { | ||
getDirectories () { | ||
return fs.preopenEntries; | ||
class Descriptor { | ||
constructor () { | ||
this.id = fs.descriptorCnt++; | ||
} | ||
}; | ||
this.types = { | ||
readViaStream(fd, offset) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (descriptor.stream) | ||
return descriptor.stream; | ||
if (descriptor.hostPreopen) | ||
readViaStream(offset) { | ||
if (this.hostPreopen) | ||
throw { tag: 'last-operation-failed', val: new StreamError }; | ||
return new FileInputStream(this.fd, offset); | ||
} | ||
writeViaStream(offset) { | ||
if (this.hostPreopen) | ||
throw 'is-directory'; | ||
return io.createStream(new ReadableFileStream(descriptor.fd, offset)); | ||
}, | ||
return new FileOutputStream(this.fd, offset); | ||
} | ||
writeViaStream(fd, offset) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (descriptor.stream) | ||
return descriptor.stream; | ||
if (descriptor.hostPreopen) | ||
throw 'is-directory'; | ||
return io.createStream(new WriteableFileStream(descriptor.fd, offset)); | ||
}, | ||
appendViaStream() { | ||
console.log(`[filesystem] APPEND STREAM ${this.id}`); | ||
} | ||
appendViaStream(fd) { | ||
console.log(`[filesystem] APPEND STREAM ${fd}`); | ||
}, | ||
advise(offset, length, advice) { | ||
console.log(`[filesystem] ADVISE`, this.id, offset, length, advice); | ||
} | ||
advise(fd, offset, length, advice) { | ||
console.log(`[filesystem] ADVISE`, fd, offset, length, advice); | ||
}, | ||
syncData() { | ||
console.log(`[filesystem] SYNC DATA ${this.id}`); | ||
} | ||
syncData(fd) { | ||
console.log(`[filesystem] SYNC DATA ${fd}`); | ||
}, | ||
getFlags() { | ||
console.log(`[filesystem] FLAGS FOR ${this.id}`); | ||
} | ||
getFlags(fd) { | ||
console.log(`[filesystem] FLAGS FOR ${fd}`); | ||
}, | ||
getType(fd) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (descriptor.stream) return 'fifo'; | ||
if (descriptor.hostPreopen) return 'directory'; | ||
const stats = fstatSync(descriptor.fd); | ||
getType() { | ||
if (this.hostPreopen) return 'directory'; | ||
const stats = fstatSync(this.fd); | ||
return lookupType(stats); | ||
}, | ||
} | ||
setFlags(fd, flags) { | ||
console.log(`[filesystem] SET FLAGS ${fd} ${JSON.stringify(flags)}`); | ||
}, | ||
setFlags(flags) { | ||
console.log(`[filesystem] SET FLAGS ${this.id} ${JSON.stringify(flags)}`); | ||
} | ||
setSize(fd, size) { | ||
console.log(`[filesystem] SET SIZE`, fd, size); | ||
}, | ||
setSize(size) { | ||
console.log(`[filesystem] SET SIZE`, this.id, size); | ||
} | ||
setTimes(fd, dataAccessTimestamp, dataModificationTimestamp) { | ||
console.log(`[filesystem] SET TIMES`, fd, dataAccessTimestamp, dataModificationTimestamp); | ||
}, | ||
setTimes(dataAccessTimestamp, dataModificationTimestamp) { | ||
console.log(`[filesystem] SET TIMES`, this.id, dataAccessTimestamp, dataModificationTimestamp); | ||
} | ||
read(fd, length, offset) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (!descriptor.fullPath) throw 'bad-descriptor'; | ||
read(length, offset) { | ||
if (!this.fullPath) throw 'bad-descriptor'; | ||
const buf = new Uint8Array(length); | ||
const bytesRead = readSync(descriptor.fd, buf, Number(offset), length, 0); | ||
const bytesRead = readSync(this.fd, buf, Number(offset), length, 0); | ||
const out = new Uint8Array(buf.buffer, 0, bytesRead); | ||
return [out, bytesRead === 0 ? 'ended' : 'open']; | ||
}, | ||
} | ||
write(fd, buffer, offset) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (!descriptor.fullPath) throw 'bad-descriptor'; | ||
return BigInt(writeSync(descriptor.fd, buffer, Number(offset), buffer.byteLength - offset, 0)); | ||
}, | ||
write(buffer, offset) { | ||
if (!this.fullPath) throw 'bad-descriptor'; | ||
return BigInt(writeSync(this.fd, buffer, Number(offset), buffer.byteLength - offset, 0)); | ||
} | ||
readDirectory(fd) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (!descriptor.fullPath) throw 'bad-descriptor'; | ||
readDirectory() { | ||
if (!this.fullPath) throw 'bad-descriptor'; | ||
try { | ||
const dir = opendirSync(isWindows ? descriptor.fullPath.slice(1) : descriptor.fullPath); | ||
return io.createStream(new DirStream(dir)); | ||
const dir = opendirSync(isWindows ? this.fullPath.slice(1) : this.fullPath); | ||
return new DirectoryEntryStream(dir); | ||
} | ||
@@ -246,19 +237,23 @@ catch (e) { | ||
} | ||
}, | ||
} | ||
sync(fd) { | ||
console.log(`[filesystem] SYNC`, fd); | ||
}, | ||
sync() { | ||
console.log(`[filesystem] SYNC`, this.id); | ||
} | ||
createDirectoryAt(fd, path) { | ||
const entry = fs.getOrCreateChildDescriptor(fd, path, { create: true, directory: true }); | ||
if (entry.source) throw 'exist'; | ||
}, | ||
createDirectoryAt(path) { | ||
const fullPath = fs.getFullPath(this, path); | ||
try { | ||
mkdirSync(fullPath); | ||
} | ||
catch (e) { | ||
throw convertFsError(e); | ||
} | ||
} | ||
stat(fd) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (descriptor.stream || descriptor.hostPreopen) throw 'invalid'; | ||
stat() { | ||
if (this.hostPreopen) throw 'invalid'; | ||
let stats; | ||
try { | ||
stats = fstatSync(descriptor.fd, { bigint: true }); | ||
stats = fstatSync(this.fd, { bigint: true }); | ||
} | ||
@@ -277,6 +272,6 @@ catch (e) { | ||
}; | ||
}, | ||
} | ||
statAt(fd, pathFlags, path) { | ||
const fullPath = fs.getFullPath(fd, path, false); | ||
statAt(pathFlags, path) { | ||
const fullPath = fs.getFullPath(this, path, false); | ||
let stats; | ||
@@ -298,15 +293,14 @@ try { | ||
}; | ||
}, | ||
} | ||
setTimesAt(fd) { | ||
console.log(`[filesystem] SET TIMES AT`, fd); | ||
}, | ||
setTimesAt() { | ||
console.log(`[filesystem] SET TIMES AT`, this.id); | ||
} | ||
linkAt(fd) { | ||
console.log(`[filesystem] LINK AT`, fd); | ||
}, | ||
linkAt() { | ||
console.log(`[filesystem] LINK AT`, this.id); | ||
} | ||
openAt(fd, pathFlags, path, openFlags, descriptorFlags, modes) { | ||
const fullPath = fs.getFullPath(fd, path, pathFlags.symlinkFollow); | ||
const childFd = fs.descriptorCnt++; | ||
openAt(pathFlags, path, openFlags, descriptorFlags, modes) { | ||
const fullPath = fs.getFullPath(this, path, pathFlags.symlinkFollow); | ||
let fsOpenFlags = 0x0; | ||
@@ -342,3 +336,3 @@ if (openFlags.create) | ||
const fd = openSync(isWindows ? fullPath.slice(1) : fullPath, fsOpenFlags, fsMode); | ||
fs.descriptors[childFd] = { fullPath, fd }; | ||
return Object.assign(new Descriptor(), { fullPath, fd }); | ||
} | ||
@@ -348,76 +342,62 @@ catch (e) { | ||
} | ||
return childFd; | ||
}, | ||
} | ||
readlinkAt(fd) { | ||
console.log(`[filesystem] READLINK AT`, fd); | ||
}, | ||
readlinkAt() { | ||
console.log(`[filesystem] READLINK AT`, this.id); | ||
} | ||
removeDirectoryAt(fd) { | ||
console.log(`[filesystem] REMOVE DIR AT`, fd); | ||
}, | ||
removeDirectoryAt() { | ||
console.log(`[filesystem] REMOVE DIR AT`, this.id); | ||
} | ||
renameAt(fd) { | ||
console.log(`[filesystem] RENAME AT`, fd); | ||
}, | ||
renameAt() { | ||
console.log(`[filesystem] RENAME AT`, this.id); | ||
} | ||
symlinkAt(fd) { | ||
console.log(`[filesystem] SYMLINK AT`, fd); | ||
}, | ||
symlinkAt() { | ||
console.log(`[filesystem] SYMLINK AT`, this.id); | ||
} | ||
unlinkFileAt(fd) { | ||
console.log(`[filesystem] UNLINK FILE AT`, fd); | ||
}, | ||
unlinkFileAt() { | ||
console.log(`[filesystem] UNLINK FILE AT`, this.id); | ||
} | ||
changeFilePermissionsAt(fd) { | ||
console.log(`[filesystem] CHANGE FILE PERMISSIONS AT`, fd); | ||
}, | ||
changeFilePermissionsAt() { | ||
console.log(`[filesystem] CHANGE FILE PERMISSIONS AT`, this.id); | ||
} | ||
changeDirectoryPermissionsAt(fd) { | ||
console.log(`[filesystem] CHANGE DIR PERMISSIONS AT`, fd); | ||
}, | ||
changeDirectoryPermissionsAt() { | ||
console.log(`[filesystem] CHANGE DIR PERMISSIONS AT`, this.id); | ||
} | ||
lockShared(fd) { | ||
console.log(`[filesystem] LOCK SHARED`, fd); | ||
}, | ||
lockShared() { | ||
console.log(`[filesystem] LOCK SHARED`, this.id); | ||
} | ||
lockExclusive(fd) { | ||
console.log(`[filesystem] LOCK EXCLUSIVE`, fd); | ||
}, | ||
lockExclusive() { | ||
console.log(`[filesystem] LOCK EXCLUSIVE`, this.id); | ||
} | ||
tryLockShared(fd) { | ||
console.log(`[filesystem] TRY LOCK SHARED`, fd); | ||
}, | ||
tryLockShared() { | ||
console.log(`[filesystem] TRY LOCK SHARED`, this.id); | ||
} | ||
tryLockExclusive(fd) { | ||
console.log(`[filesystem] TRY LOCK EXCLUSIVE`, fd); | ||
}, | ||
tryLockExclusive() { | ||
console.log(`[filesystem] TRY LOCK EXCLUSIVE`, this.id); | ||
} | ||
unlock(fd) { | ||
console.log(`[filesystem] UNLOCK`, fd); | ||
}, | ||
unlock() { | ||
console.log(`[filesystem] UNLOCK`, this.id); | ||
} | ||
dropDescriptor(fd) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (descriptor.fd) | ||
closeSync(descriptor.fd); | ||
delete fs.descriptors[fd]; | ||
}, | ||
drop() { | ||
if (this.fd) | ||
closeSync(this.fd); | ||
} | ||
readDirectoryEntry(sid) { | ||
return io.getStream(sid).read(); | ||
}, | ||
dropDirectoryEntryStream(sid) { | ||
io.dropStream(sid); | ||
}, | ||
metadataHash(fd) { | ||
const descriptor = fs.getDescriptor(fd); | ||
if (descriptor.stream) | ||
return { upper: 0n, lower: descriptor.stream} | ||
if (descriptor.hostPreopen) | ||
return { upper: 0n, lower: BigInt(fd) }; | ||
metadataHash() { | ||
if (this.hostPreopen) | ||
return { upper: 0n, lower: BigInt(this.id) }; | ||
try { | ||
const stats = fstatSync(descriptor.fd, { bigint: true }); | ||
const stats = fstatSync(this.fd, { bigint: true }); | ||
return { upper: stats.mtimeNs, lower: stats.ino }; | ||
@@ -428,6 +408,6 @@ } | ||
} | ||
}, | ||
} | ||
metadataHashAt(fd, pathFlags, path) { | ||
const fullPath = fs.getFullPath(fd, path, false); | ||
metadataHashAt(pathFlags, path) { | ||
const fullPath = fs.getFullPath(this, path, false); | ||
try { | ||
@@ -441,7 +421,23 @@ const stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(isWindows ? fullPath.slice(1) : fullPath, { bigint: true }); | ||
} | ||
} | ||
this.descriptorCnt = 3; | ||
this.preopenEntries = []; | ||
for (const [virtualPath, hostPreopen] of Object.entries(preopens)) { | ||
const preopenEntry = [Object.assign(new Descriptor(), { hostPreopen }), virtualPath]; | ||
this.preopenEntries.push(preopenEntry); | ||
} | ||
this.preopens = { | ||
Descriptor, | ||
getDirectories () { | ||
return fs.preopenEntries; | ||
} | ||
}; | ||
this.types = { | ||
Descriptor, | ||
}; | ||
} | ||
} | ||
const _fs = new FileSystem(_io, { '/': '/' }, environment); | ||
const _fs = new FileSystem({ '/': '/' }, environment); | ||
@@ -448,0 +444,0 @@ export const { preopens, types } = _fs; |
import { WasiHttp } from '../http/wasi-http.js'; | ||
import { _io } from './io.js'; | ||
import { streams } from '../common/io.js'; | ||
const http = new WasiHttp(_io); | ||
const http = new WasiHttp(streams); | ||
export const { incomingHandler, outgoingHandler, types } = http; |
import * as clocks from "./clocks.js"; | ||
import * as filesystem from "./filesystem.js"; | ||
import * as http from "./http.js"; | ||
import * as io from "./io.js"; | ||
import * as logging from "./logging.js"; | ||
import * as io from "../common/io.js"; | ||
import * as poll from "./poll.js"; | ||
@@ -16,3 +15,2 @@ import * as random from "./random.js"; | ||
io, | ||
logging, | ||
poll, | ||
@@ -19,0 +17,0 @@ random, |
{ | ||
"name": "@bytecodealliance/preview2-shim", | ||
"version": "0.0.17", | ||
"version": "0.0.18", | ||
"description": "WASI Preview2 shim for JS environments", | ||
@@ -14,2 +14,6 @@ "author": "Guy Bedford, Eduardo Rodrigues<16357187+eduardomourar@users.noreply.github.com>", | ||
}, | ||
"./io": { | ||
"types": "./types/io.d.ts", | ||
"default": "./lib/common/io.js" | ||
}, | ||
"./*": { | ||
@@ -16,0 +20,0 @@ "types": "./types/*.d.ts", |
export namespace WasiCliTerminalInput { | ||
/** | ||
* Dispose of the specified terminal-input after which it may no longer | ||
* be used. | ||
*/ | ||
export function dropTerminalInput(this_: TerminalInput): void; | ||
} | ||
/** | ||
* The input side of a terminal. | ||
* | ||
* This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). | ||
*/ | ||
export type TerminalInput = number; |
export namespace WasiCliTerminalOutput { | ||
/** | ||
* Dispose of the specified terminal-output, after which it may no longer | ||
* be used. | ||
*/ | ||
export function dropTerminalOutput(this_: TerminalOutput): void; | ||
} | ||
/** | ||
* The output side of a terminal. | ||
* | ||
* This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). | ||
*/ | ||
export type TerminalOutput = number; |
@@ -19,3 +19,3 @@ export namespace WasiClocksMonotonicClock { | ||
} | ||
import type { Pollable } from '../interfaces/wasi-poll-poll'; | ||
import type { Pollable } from '../interfaces/wasi-io-poll'; | ||
export { Pollable }; | ||
@@ -22,0 +22,0 @@ /** |
@@ -11,12 +11,7 @@ export namespace WasiClocksTimezone { | ||
*/ | ||
export function display(this_: Timezone, when: Datetime): TimezoneDisplay; | ||
export function display(when: Datetime): TimezoneDisplay; | ||
/** | ||
* The same as `display`, but only return the UTC offset. | ||
*/ | ||
export function utcOffset(this_: Timezone, when: Datetime): number; | ||
/** | ||
* Dispose of the specified input-stream, after which it may no longer | ||
* be used. | ||
*/ | ||
export function dropTimezone(this_: Timezone): void; | ||
export function utcOffset(when: Datetime): number; | ||
} | ||
@@ -26,12 +21,2 @@ import type { Datetime } from '../interfaces/wasi-clocks-wall-clock'; | ||
/** | ||
* A timezone. | ||
* | ||
* In timezones that recognize daylight saving time, also known as daylight | ||
* time and summer time, the information returned from the functions varies | ||
* over time to reflect these adjustments. | ||
* | ||
* This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). | ||
*/ | ||
export type Timezone = number; | ||
/** | ||
* Information useful for displaying the timezone of a specific `datetime`. | ||
@@ -38,0 +23,0 @@ * |
@@ -12,3 +12,3 @@ export namespace WasiFilesystemTypes { | ||
*/ | ||
export function readViaStream(this_: Descriptor, offset: Filesize): InputStream; | ||
export { Descriptor }; | ||
/** | ||
@@ -22,3 +22,2 @@ * Return a stream for writing to a file, if available. | ||
*/ | ||
export function writeViaStream(this_: Descriptor, offset: Filesize): OutputStream; | ||
/** | ||
@@ -32,3 +31,2 @@ * Return a stream for appending to a file, if available. | ||
*/ | ||
export function appendViaStream(this_: Descriptor): OutputStream; | ||
/** | ||
@@ -39,3 +37,2 @@ * Provide file advisory information on a descriptor. | ||
*/ | ||
export function advise(this_: Descriptor, offset: Filesize, length: Filesize, advice: Advice): void; | ||
/** | ||
@@ -49,3 +46,2 @@ * Synchronize the data of a file to disk. | ||
*/ | ||
export function syncData(this_: Descriptor): void; | ||
/** | ||
@@ -59,3 +55,2 @@ * Get flags associated with a descriptor. | ||
*/ | ||
export function getFlags(this_: Descriptor): DescriptorFlags; | ||
/** | ||
@@ -73,3 +68,2 @@ * Get the dynamic type of a descriptor. | ||
*/ | ||
export function getType(this_: Descriptor): DescriptorType; | ||
/** | ||
@@ -81,3 +75,2 @@ * Adjust the size of an open file. If this increases the file's size, the | ||
*/ | ||
export function setSize(this_: Descriptor, size: Filesize): void; | ||
/** | ||
@@ -90,3 +83,2 @@ * Adjust the timestamps of an open file or directory. | ||
*/ | ||
export function setTimes(this_: Descriptor, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; | ||
/** | ||
@@ -105,3 +97,2 @@ * Read from a descriptor, without using and updating the descriptor's offset. | ||
*/ | ||
export function read(this_: Descriptor, length: Filesize, offset: Filesize): [Uint8Array, boolean]; | ||
/** | ||
@@ -118,3 +109,2 @@ * Write to a descriptor, without using and updating the descriptor's offset. | ||
*/ | ||
export function write(this_: Descriptor, buffer: Uint8Array, offset: Filesize): Filesize; | ||
/** | ||
@@ -131,3 +121,2 @@ * Read directory entries from a directory. | ||
*/ | ||
export function readDirectory(this_: Descriptor): DirectoryEntryStream; | ||
/** | ||
@@ -141,3 +130,2 @@ * Synchronize the data and metadata of a file to disk. | ||
*/ | ||
export function sync(this_: Descriptor): void; | ||
/** | ||
@@ -148,3 +136,2 @@ * Create a directory. | ||
*/ | ||
export function createDirectoryAt(this_: Descriptor, path: string): void; | ||
/** | ||
@@ -161,3 +148,2 @@ * Return the attributes of an open file or directory. | ||
*/ | ||
export function stat(this_: Descriptor): DescriptorStat; | ||
/** | ||
@@ -172,3 +158,2 @@ * Return the attributes of a file or directory. | ||
*/ | ||
export function statAt(this_: Descriptor, pathFlags: PathFlags, path: string): DescriptorStat; | ||
/** | ||
@@ -182,3 +167,2 @@ * Adjust the timestamps of a file or directory. | ||
*/ | ||
export function setTimesAt(this_: Descriptor, pathFlags: PathFlags, path: string, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; | ||
/** | ||
@@ -189,3 +173,2 @@ * Create a hard link. | ||
*/ | ||
export function linkAt(this_: Descriptor, oldPathFlags: PathFlags, oldPath: string, newDescriptor: Descriptor, newPath: string): void; | ||
/** | ||
@@ -211,3 +194,2 @@ * Open a file or directory. | ||
*/ | ||
export function openAt(this_: Descriptor, pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags, modes: Modes): Descriptor; | ||
/** | ||
@@ -221,3 +203,2 @@ * Read the contents of a symbolic link. | ||
*/ | ||
export function readlinkAt(this_: Descriptor, path: string): string; | ||
/** | ||
@@ -230,3 +211,2 @@ * Remove a directory. | ||
*/ | ||
export function removeDirectoryAt(this_: Descriptor, path: string): void; | ||
/** | ||
@@ -237,3 +217,2 @@ * Rename a filesystem object. | ||
*/ | ||
export function renameAt(this_: Descriptor, oldPath: string, newDescriptor: Descriptor, newPath: string): void; | ||
/** | ||
@@ -247,3 +226,2 @@ * Create a symbolic link (also known as a "symlink"). | ||
*/ | ||
export function symlinkAt(this_: Descriptor, oldPath: string, newPath: string): void; | ||
/** | ||
@@ -261,3 +239,2 @@ * Check accessibility of a filesystem path. | ||
*/ | ||
export function accessAt(this_: Descriptor, pathFlags: PathFlags, path: string, type: AccessType): void; | ||
/** | ||
@@ -269,3 +246,2 @@ * Unlink a filesystem object that is not a directory. | ||
*/ | ||
export function unlinkFileAt(this_: Descriptor, path: string): void; | ||
/** | ||
@@ -279,3 +255,2 @@ * Change the permissions of a filesystem object that is not a directory. | ||
*/ | ||
export function changeFilePermissionsAt(this_: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; | ||
/** | ||
@@ -293,3 +268,2 @@ * Change the permissions of a directory. | ||
*/ | ||
export function changeDirectoryPermissionsAt(this_: Descriptor, pathFlags: PathFlags, path: string, modes: Modes): void; | ||
/** | ||
@@ -317,3 +291,2 @@ * Request a shared advisory lock for an open file. | ||
*/ | ||
export function lockShared(this_: Descriptor): void; | ||
/** | ||
@@ -343,3 +316,2 @@ * Request an exclusive advisory lock for an open file. | ||
*/ | ||
export function lockExclusive(this_: Descriptor): void; | ||
/** | ||
@@ -368,3 +340,2 @@ * Request a shared advisory lock for an open file. | ||
*/ | ||
export function tryLockShared(this_: Descriptor): void; | ||
/** | ||
@@ -395,3 +366,2 @@ * Request an exclusive advisory lock for an open file. | ||
*/ | ||
export function tryLockExclusive(this_: Descriptor): void; | ||
/** | ||
@@ -402,18 +372,3 @@ * Release a shared or exclusive lock on an open file. | ||
*/ | ||
export function unlock(this_: Descriptor): void; | ||
/** | ||
* Dispose of the specified `descriptor`, after which it may no longer | ||
* be used. | ||
*/ | ||
export function dropDescriptor(this_: Descriptor): void; | ||
/** | ||
* Read a single directory entry from a `directory-entry-stream`. | ||
*/ | ||
export function readDirectoryEntry(this_: DirectoryEntryStream): DirectoryEntry | undefined; | ||
/** | ||
* Dispose of the specified `directory-entry-stream`, after which it may no longer | ||
* be used. | ||
*/ | ||
export function dropDirectoryEntryStream(this_: DirectoryEntryStream): void; | ||
/** | ||
* Test whether two descriptors refer to the same filesystem object. | ||
@@ -426,3 +381,2 @@ * | ||
*/ | ||
export function isSameObject(this_: Descriptor, other: Descriptor): boolean; | ||
/** | ||
@@ -449,3 +403,2 @@ * Return a hash of the metadata associated with a filesystem object referred | ||
*/ | ||
export function metadataHash(this_: Descriptor): MetadataHashValue; | ||
/** | ||
@@ -457,3 +410,19 @@ * Return a hash of the metadata associated with a filesystem object referred | ||
*/ | ||
export function metadataHashAt(this_: Descriptor, pathFlags: PathFlags, path: string): MetadataHashValue; | ||
/** | ||
* Read a single directory entry from a `directory-entry-stream`. | ||
*/ | ||
export { DirectoryEntryStream }; | ||
/** | ||
* Attempts to extract a filesystem-related `error-code` from the stream | ||
* `error` provided. | ||
* | ||
* Stream operations which return `stream-error::last-operation-failed` | ||
* have a payload with more information about the operation that failed. | ||
* This payload can be passed through to this function to see if there's | ||
* filesystem-related information about the error to return. | ||
* | ||
* Note that this function is fallible because not all stream-related | ||
* errors are filesystem-related errors. | ||
*/ | ||
export function filesystemErrorCode(err: Error): ErrorCode | undefined; | ||
} | ||
@@ -464,2 +433,4 @@ import type { InputStream } from '../interfaces/wasi-io-streams'; | ||
export { OutputStream }; | ||
import type { Error } from '../interfaces/wasi-io-streams'; | ||
export { Error }; | ||
import type { Datetime } from '../interfaces/wasi-clocks-wall-clock'; | ||
@@ -654,12 +625,21 @@ export { Datetime }; | ||
* Last data access timestamp. | ||
* | ||
* If the `option` is none, the platform doesn't maintain an access | ||
* timestamp for this file. | ||
*/ | ||
dataAccessTimestamp: Datetime, | ||
dataAccessTimestamp?: Datetime, | ||
/** | ||
* Last data modification timestamp. | ||
* | ||
* If the `option` is none, the platform doesn't maintain a | ||
* modification timestamp for this file. | ||
*/ | ||
dataModificationTimestamp: Datetime, | ||
dataModificationTimestamp?: Datetime, | ||
/** | ||
* Last file status change timestamp. | ||
* Last file status-change timestamp. | ||
* | ||
* If the `option` is none, the platform doesn't maintain a | ||
* status-change timestamp for this file. | ||
*/ | ||
statusChangeTimestamp: Datetime, | ||
statusChangeTimestamp?: Datetime, | ||
} | ||
@@ -854,10 +834,2 @@ /** | ||
/** | ||
* A descriptor is a reference to a filesystem object, which may be a file, | ||
* directory, named pipe, special file, or other object on which filesystem | ||
* calls may be made. | ||
* | ||
* This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). | ||
*/ | ||
export type Descriptor = number; | ||
/** | ||
* A 128-bit hash value, split into parts because wasm doesn't have a | ||
@@ -876,7 +848,43 @@ * 128-bit integer type. | ||
} | ||
/** | ||
* A stream of directory entries. | ||
* | ||
* This [represents a stream of `dir-entry`](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Streams). | ||
*/ | ||
export type DirectoryEntryStream = number; | ||
export class DirectoryEntryStream { | ||
readDirectoryEntry(): DirectoryEntry | undefined; | ||
} | ||
export class Descriptor { | ||
readViaStream(offset: Filesize): InputStream; | ||
writeViaStream(offset: Filesize): OutputStream; | ||
appendViaStream(): OutputStream; | ||
advise(offset: Filesize, length: Filesize, advice: Advice): void; | ||
syncData(): void; | ||
getFlags(): DescriptorFlags; | ||
getType(): DescriptorType; | ||
setSize(size: Filesize): void; | ||
setTimes(dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; | ||
read(length: Filesize, offset: Filesize): [Uint8Array, boolean]; | ||
write(buffer: Uint8Array, offset: Filesize): Filesize; | ||
readDirectory(): DirectoryEntryStream; | ||
sync(): void; | ||
createDirectoryAt(path: string): void; | ||
stat(): DescriptorStat; | ||
statAt(pathFlags: PathFlags, path: string): DescriptorStat; | ||
setTimesAt(pathFlags: PathFlags, path: string, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void; | ||
linkAt(oldPathFlags: PathFlags, oldPath: string, newDescriptor: Descriptor, newPath: string): void; | ||
openAt(pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags, modes: Modes): Descriptor; | ||
readlinkAt(path: string): string; | ||
removeDirectoryAt(path: string): void; | ||
renameAt(oldPath: string, newDescriptor: Descriptor, newPath: string): void; | ||
symlinkAt(oldPath: string, newPath: string): void; | ||
accessAt(pathFlags: PathFlags, path: string, type: AccessType): void; | ||
unlinkFileAt(path: string): void; | ||
changeFilePermissionsAt(pathFlags: PathFlags, path: string, modes: Modes): void; | ||
changeDirectoryPermissionsAt(pathFlags: PathFlags, path: string, modes: Modes): void; | ||
lockShared(): void; | ||
lockExclusive(): void; | ||
tryLockShared(): void; | ||
tryLockExclusive(): void; | ||
unlock(): void; | ||
isSameObject(other: Descriptor): boolean; | ||
metadataHash(): MetadataHashValue; | ||
metadataHashAt(pathFlags: PathFlags, path: string): MetadataHashValue; | ||
} |
export namespace WasiIoStreams { | ||
/** | ||
* Returns a string that's suitable to assist humans in debugging this | ||
* error. | ||
* | ||
* The returned string will change across platforms and hosts which | ||
* means that parsing it, for example, would be a | ||
* platform-compatibility hazard. | ||
*/ | ||
export { Error }; | ||
/** | ||
* Perform a non-blocking read from the stream. | ||
@@ -10,4 +19,4 @@ * | ||
* empty list and `stream-status:open` indicates no more data is | ||
* available at this time, and that the pollable given by | ||
* `subscribe-to-input-stream` will be ready when more data is available. | ||
* available at this time, and that the pollable given by `subscribe` | ||
* will be ready when more data is available. | ||
* | ||
@@ -27,3 +36,3 @@ * Once a stream has reached the end, subsequent calls to `read` or | ||
*/ | ||
export function read(this_: InputStream, len: bigint): [Uint8Array, StreamStatus]; | ||
export { InputStream }; | ||
/** | ||
@@ -33,3 +42,2 @@ * Read bytes from a stream, after blocking until at least one byte can | ||
*/ | ||
export function blockingRead(this_: InputStream, len: bigint): [Uint8Array, StreamStatus]; | ||
/** | ||
@@ -49,3 +57,2 @@ * Skip bytes from a stream. | ||
*/ | ||
export function skip(this_: InputStream, len: bigint): [bigint, StreamStatus]; | ||
/** | ||
@@ -55,3 +62,2 @@ * Skip bytes from a stream, after blocking until at least one byte | ||
*/ | ||
export function blockingSkip(this_: InputStream, len: bigint): [bigint, StreamStatus]; | ||
/** | ||
@@ -65,13 +71,3 @@ * Create a `pollable` which will resolve once either the specified stream | ||
*/ | ||
export function subscribeToInputStream(this_: InputStream): Pollable; | ||
/** | ||
* Dispose of the specified `input-stream`, after which it may no longer | ||
* be used. | ||
* Implementations may trap if this `input-stream` is dropped while child | ||
* `pollable` resources are still alive. | ||
* After this `input-stream` is dropped, implementations may report any | ||
* corresponding `output-stream` has `stream-state.closed`. | ||
*/ | ||
export function dropInputStream(this_: InputStream): void; | ||
/** | ||
* Check readiness for writing. This function never blocks. | ||
@@ -83,7 +79,7 @@ * | ||
* | ||
* When this function returns 0 bytes, the `subscribe-to-output-stream` | ||
* pollable will become ready when this function will report at least | ||
* 1 byte, or an error. | ||
* When this function returns 0 bytes, the `subscribe` pollable will | ||
* become ready when this function will report at least 1 byte, or an | ||
* error. | ||
*/ | ||
export function checkWrite(this_: OutputStream): bigint; | ||
export { OutputStream }; | ||
/** | ||
@@ -98,3 +94,2 @@ * Perform a write. This function never blocks. | ||
*/ | ||
export function write(this_: OutputStream, contents: Uint8Array): void; | ||
/** | ||
@@ -105,24 +100,23 @@ * Perform a write of up to 4096 bytes, and then flush the stream. Block | ||
* This is a convenience wrapper around the use of `check-write`, | ||
* `subscribe-to-output-stream`, `write`, and `flush`, and is implemented | ||
* with the following pseudo-code: | ||
* `subscribe`, `write`, and `flush`, and is implemented with the | ||
* following pseudo-code: | ||
* | ||
* ```text | ||
* let pollable = subscribe-to-output-stream(this); | ||
* let pollable = this.subscribe(); | ||
* while !contents.is_empty() { | ||
* // Wait for the stream to become writable | ||
* poll-oneoff(pollable); | ||
* let Ok(n) = check-write(this); // eliding error handling | ||
* poll-one(pollable); | ||
* let Ok(n) = this.check-write(); // eliding error handling | ||
* let len = min(n, contents.len()); | ||
* let (chunk, rest) = contents.split_at(len); | ||
* write(this, chunk); // eliding error handling | ||
* this.write(chunk ); // eliding error handling | ||
* contents = rest; | ||
* } | ||
* flush(this); | ||
* this.flush(); | ||
* // Wait for completion of `flush` | ||
* poll-oneoff(pollable); | ||
* poll-one(pollable); | ||
* // Check for any errors that arose during `flush` | ||
* let _ = check-write(this); // eliding error handling | ||
* let _ = this.check-write(); // eliding error handling | ||
* ``` | ||
*/ | ||
export function blockingWriteAndFlush(this_: OutputStream, contents: Uint8Array): void; | ||
/** | ||
@@ -137,6 +131,5 @@ * Request to flush buffered output. This function never blocks. | ||
* writes (`check-write` will return `ok(0)`) until the flush has | ||
* completed. The `subscribe-to-output-stream` pollable will become ready | ||
* when the flush has completed and the stream can accept more writes. | ||
* completed. The `subscribe` pollable will become ready when the | ||
* flush has completed and the stream can accept more writes. | ||
*/ | ||
export function flush(this_: OutputStream): void; | ||
/** | ||
@@ -146,3 +139,2 @@ * Request to flush buffered output, and block until flush completes | ||
*/ | ||
export function blockingFlush(this_: OutputStream): void; | ||
/** | ||
@@ -160,3 +152,2 @@ * Create a `pollable` which will resolve once the output-stream | ||
*/ | ||
export function subscribeToOutputStream(this_: OutputStream): Pollable; | ||
/** | ||
@@ -170,118 +161,107 @@ * Write zeroes to a stream. | ||
*/ | ||
export function writeZeroes(this_: OutputStream, len: bigint): void; | ||
/** | ||
* Read from one stream and write to another. | ||
* Perform a write of up to 4096 zeroes, and then flush the stream. | ||
* Block until all of these operations are complete, or an error | ||
* occurs. | ||
* | ||
* This function returns the number of bytes transferred; it may be less | ||
* than `len`. | ||
* This is a convenience wrapper around the use of `check-write`, | ||
* `subscribe`, `write-zeroes`, and `flush`, and is implemented with | ||
* the following pseudo-code: | ||
* | ||
* Unlike other I/O functions, this function blocks until all the data | ||
* read from the input stream has been written to the output stream. | ||
*/ | ||
export function splice(this_: OutputStream, src: InputStream, len: bigint): [bigint, StreamStatus]; | ||
* ```text | ||
* let pollable = this.subscribe(); | ||
* while num_zeroes != 0 { | ||
* // Wait for the stream to become writable | ||
* poll-one(pollable); | ||
* let Ok(n) = this.check-write(); // eliding error handling | ||
* let len = min(n, num_zeroes); | ||
* this.write-zeroes(len); // eliding error handling | ||
* num_zeroes -= len; | ||
* } | ||
* this.flush(); | ||
* // Wait for completion of `flush` | ||
* poll-one(pollable); | ||
* // Check for any errors that arose during `flush` | ||
* let _ = this.check-write(); // eliding error handling | ||
* ``` | ||
*/ | ||
/** | ||
* Read from one stream and write to another. | ||
* | ||
* This function returns the number of bytes transferred; it may be less | ||
* than `len`. | ||
* | ||
* Unlike other I/O functions, this function blocks until all the data | ||
* read from the input stream has been written to the output stream. | ||
*/ | ||
/** | ||
* Read from one stream and write to another, with blocking. | ||
* | ||
* This is similar to `splice`, except that it blocks until at least | ||
* one byte can be read. | ||
*/ | ||
/** | ||
* Forward the entire contents of an input stream to an output stream. | ||
* | ||
* This function repeatedly reads from the input stream and writes | ||
* the data to the output stream, until the end of the input stream | ||
* is reached, or an error is encountered. | ||
* | ||
* Unlike other I/O functions, this function blocks until the end | ||
* of the input stream is seen and all the data has been written to | ||
* the output stream. | ||
* | ||
* This function returns the number of bytes transferred, and the status of | ||
* the output stream. | ||
*/ | ||
} | ||
import type { Pollable } from '../interfaces/wasi-io-poll'; | ||
export { Pollable }; | ||
/** | ||
* Read from one stream and write to another, with blocking. | ||
* | ||
* This is similar to `splice`, except that it blocks until at least | ||
* one byte can be read. | ||
* An error for input-stream and output-stream operations. | ||
*/ | ||
export function blockingSplice(this_: OutputStream, src: InputStream, len: bigint): [bigint, StreamStatus]; | ||
export type StreamError = StreamErrorLastOperationFailed | StreamErrorClosed; | ||
/** | ||
* Forward the entire contents of an input stream to an output stream. | ||
* The last operation (a write or flush) failed before completion. | ||
* | ||
* This function repeatedly reads from the input stream and writes | ||
* the data to the output stream, until the end of the input stream | ||
* is reached, or an error is encountered. | ||
* | ||
* Unlike other I/O functions, this function blocks until the end | ||
* of the input stream is seen and all the data has been written to | ||
* the output stream. | ||
* | ||
* This function returns the number of bytes transferred, and the status of | ||
* the output stream. | ||
* More information is available in the `error` payload. | ||
*/ | ||
export function forward(this_: OutputStream, src: InputStream): [bigint, StreamStatus]; | ||
export interface StreamErrorLastOperationFailed { | ||
tag: 'last-operation-failed', | ||
val: Error, | ||
} | ||
/** | ||
* Dispose of the specified `output-stream`, after which it may no longer | ||
* be used. | ||
* Implementations may trap if this `output-stream` is dropped while | ||
* child `pollable` resources are still alive. | ||
* After this `output-stream` is dropped, implementations may report any | ||
* corresponding `input-stream` has `stream-state.closed`. | ||
* The stream is closed: no more input will be accepted by the | ||
* stream. A closed output-stream will return this error on all | ||
* future operations. | ||
*/ | ||
export function dropOutputStream(this_: OutputStream): void; | ||
} | ||
import type { Pollable } from '../interfaces/wasi-poll-poll'; | ||
export { Pollable }; | ||
/** | ||
* Streams provide a sequence of data and then end; once they end, they | ||
* no longer provide any further data. | ||
* | ||
* For example, a stream reading from a file ends when the stream reaches | ||
* the end of the file. For another example, a stream reading from a | ||
* socket ends when the socket is closed. | ||
* # Variants | ||
* | ||
* ## `"open"` | ||
* | ||
* The stream is open and may produce further data. | ||
* ## `"ended"` | ||
* | ||
* When reading, this indicates that the stream will not produce | ||
* further data. | ||
* When writing, this indicates that the stream will no longer be read. | ||
* Further writes are still permitted. | ||
*/ | ||
export type StreamStatus = 'open' | 'ended'; | ||
/** | ||
* An input bytestream. In the future, this will be replaced by handle | ||
* types. | ||
* | ||
* `input-stream`s are *non-blocking* to the extent practical on underlying | ||
* platforms. I/O operations always return promptly; if fewer bytes are | ||
* promptly available than requested, they return the number of bytes promptly | ||
* available, which could even be zero. To wait for data to be available, | ||
* use the `subscribe-to-input-stream` function to obtain a `pollable` which | ||
* can be polled for using `wasi:poll/poll.poll_oneoff`. | ||
* | ||
* And at present, it is a `u32` instead of being an actual handle, until | ||
* the wit-bindgen implementation of handles and resources is ready. | ||
* | ||
* This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). | ||
*/ | ||
export type InputStream = number; | ||
/** | ||
* An output bytestream. In the future, this will be replaced by handle | ||
* types. | ||
* | ||
* `output-stream`s are *non-blocking* to the extent practical on | ||
* underlying platforms. Except where specified otherwise, I/O operations also | ||
* always return promptly, after the number of bytes that can be written | ||
* promptly, which could even be zero. To wait for the stream to be ready to | ||
* accept data, the `subscribe-to-output-stream` function to obtain a | ||
* `pollable` which can be polled for using `wasi:poll`. | ||
* | ||
* And at present, it is a `u32` instead of being an actual handle, until | ||
* the wit-bindgen implementation of handles and resources is ready. | ||
* | ||
* This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). | ||
*/ | ||
export type OutputStream = number; | ||
/** | ||
* An error for output-stream operations. | ||
* | ||
* Contrary to input-streams, a closed output-stream is reported using | ||
* an error. | ||
* # Variants | ||
* | ||
* ## `"last-operation-failed"` | ||
* | ||
* The last operation (a write or flush) failed before completion. | ||
* ## `"closed"` | ||
* | ||
* The stream is closed: no more input will be accepted by the | ||
* stream. A closed output-stream will return this error on all | ||
* future operations. | ||
*/ | ||
export type WriteError = 'last-operation-failed' | 'closed'; | ||
export interface StreamErrorClosed { | ||
tag: 'closed', | ||
} | ||
export class OutputStream { | ||
checkWrite(): bigint; | ||
write(contents: Uint8Array): void; | ||
blockingWriteAndFlush(contents: Uint8Array): void; | ||
flush(): void; | ||
blockingFlush(): void; | ||
subscribe(): Pollable; | ||
writeZeroes(len: bigint): void; | ||
blockingWriteZeroesAndFlush(len: bigint): void; | ||
splice(src: InputStream, len: bigint): bigint; | ||
blockingSplice(src: InputStream, len: bigint): bigint; | ||
forward(src: InputStream): bigint; | ||
} | ||
export class Error { | ||
toDebugString(): string; | ||
} | ||
export class InputStream { | ||
read(len: bigint): Uint8Array; | ||
blockingRead(len: bigint): Uint8Array; | ||
skip(len: bigint): bigint; | ||
blockingSkip(len: bigint): bigint; | ||
subscribe(): Pollable; | ||
} | ||
export namespace WasiRandomRandom { | ||
/** | ||
* Return `len` cryptographically-secure pseudo-random bytes. | ||
* Return `len` cryptographically-secure random or pseudo-random bytes. | ||
* | ||
* This function must produce data from an adequately seeded | ||
* cryptographically-secure pseudo-random number generator (CSPRNG), so it | ||
* must not block, from the perspective of the calling program, and the | ||
* returned data is always unpredictable. | ||
* This function must produce data at least as cryptographically secure and | ||
* fast as an adequately seeded cryptographically-secure pseudo-random | ||
* number generator (CSPRNG). It must not block, from the perspective of | ||
* the calling program, under any circumstances, including on the first | ||
* request and on requests for numbers of bytes. The returned data must | ||
* always be unpredictable. | ||
* | ||
* This function must always return fresh pseudo-random data. Deterministic | ||
* environments must omit this function, rather than implementing it with | ||
* deterministic data. | ||
* This function must always return fresh data. Deterministic environments | ||
* must omit this function, rather than implementing it with deterministic | ||
* data. | ||
*/ | ||
export function getRandomBytes(len: bigint): Uint8Array; | ||
/** | ||
* Return a cryptographically-secure pseudo-random `u64` value. | ||
* Return a cryptographically-secure random or pseudo-random `u64` value. | ||
* | ||
* This function returns the same type of pseudo-random data as | ||
* `get-random-bytes`, represented as a `u64`. | ||
* This function returns the same type of data as `get-random-bytes`, | ||
* represented as a `u64`. | ||
*/ | ||
export function getRandomU64(): bigint; | ||
} |
@@ -24,5 +24,5 @@ export namespace WasiSocketsIpNameLookup { | ||
* # Typical errors | ||
* - `invalid-name`: `name` is a syntactically invalid domain name. | ||
* - `invalid-name`: `name` is an IP address. | ||
* - `address-family-not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) | ||
* - `invalid-argument`: `name` is a syntactically invalid domain name. | ||
* - `invalid-argument`: `name` is an IP address. | ||
* - `not-supported`: The specified `address-family` is not supported. (EAI_FAMILY) | ||
* | ||
@@ -42,3 +42,2 @@ * # References: | ||
* addresses have been exhausted, this function returns `none`. | ||
* After which, you should release the stream with `drop-resolve-address-stream`. | ||
* | ||
@@ -53,10 +52,4 @@ * This function never returns IPv4-mapped IPv6 addresses. | ||
*/ | ||
export function resolveNextAddress(this_: ResolveAddressStream): IpAddress | undefined; | ||
export { ResolveAddressStream }; | ||
/** | ||
* Dispose of the specified `resolve-address-stream`, after which it may no longer be used. | ||
* | ||
* Note: this function is scheduled to be removed when Resources are natively supported in Wit. | ||
*/ | ||
export function dropResolveAddressStream(this_: ResolveAddressStream): void; | ||
/** | ||
* Create a `pollable` which will resolve once the stream is ready for I/O. | ||
@@ -67,5 +60,4 @@ * | ||
*/ | ||
export function subscribe(this_: ResolveAddressStream): Pollable; | ||
} | ||
import type { Pollable } from '../interfaces/wasi-poll-poll'; | ||
import type { Pollable } from '../interfaces/wasi-io-poll'; | ||
export { Pollable }; | ||
@@ -80,2 +72,6 @@ import type { Network } from '../interfaces/wasi-sockets-network'; | ||
export { IpAddressFamily }; | ||
export type ResolveAddressStream = number; | ||
export class ResolveAddressStream { | ||
resolveNextAddress(): IpAddress | undefined; | ||
subscribe(): Pollable; | ||
} |
export namespace WasiSocketsNetwork { | ||
/** | ||
* Dispose of the specified `network`, after which it may no longer be used. | ||
* | ||
* Note: this function is scheduled to be removed when Resources are natively supported in Wit. | ||
*/ | ||
export function dropNetwork(this_: Network): void; | ||
} | ||
/** | ||
* An opaque resource that represents access to (a subset of) the network. | ||
* This enables context-based security for networking. | ||
* There is no need for this to map 1:1 to a physical network interface. | ||
* | ||
* FYI, In the future this will be replaced by handle types. | ||
*/ | ||
export type Network = number; | ||
/** | ||
* Error codes. | ||
@@ -27,2 +13,3 @@ * | ||
* - `out-of-memory` | ||
* - `concurrency-conflict` | ||
* | ||
@@ -45,2 +32,7 @@ * See each individual API for what the POSIX equivalents are. They sometimes differ per API. | ||
* POSIX equivalent: EOPNOTSUPP | ||
* ## `"invalid-argument"` | ||
* | ||
* One of the arguments is invalid. | ||
* | ||
* POSIX equivalent: EINVAL | ||
* ## `"out-of-memory"` | ||
@@ -57,2 +49,4 @@ * | ||
* This operation is incompatible with another asynchronous operation that is already in progress. | ||
* | ||
* POSIX equivalent: EALREADY | ||
* ## `"not-in-progress"` | ||
@@ -70,35 +64,8 @@ * | ||
* Note: this is scheduled to be removed when `future`s are natively supported. | ||
* ## `"address-family-not-supported"` | ||
* ## `"invalid-state"` | ||
* | ||
* The specified address-family is not supported. | ||
* ## `"address-family-mismatch"` | ||
* | ||
* An IPv4 address was passed to an IPv6 resource, or vice versa. | ||
* ## `"invalid-remote-address"` | ||
* | ||
* The socket address is not a valid remote address. E.g. the IP address is set to INADDR_ANY, or the port is set to 0. | ||
* ## `"ipv4-only-operation"` | ||
* | ||
* The operation is only supported on IPv4 resources. | ||
* ## `"ipv6-only-operation"` | ||
* | ||
* The operation is only supported on IPv6 resources. | ||
* The operation is not valid in the socket's current state. | ||
* ## `"new-socket-limit"` | ||
* | ||
* A new socket resource could not be created because of a system limit. | ||
* ## `"already-attached"` | ||
* | ||
* The socket is already attached to another network. | ||
* ## `"already-bound"` | ||
* | ||
* The socket is already bound. | ||
* ## `"already-connected"` | ||
* | ||
* The socket is already in the Connection state. | ||
* ## `"not-bound"` | ||
* | ||
* The socket is not bound to any local address. | ||
* ## `"not-connected"` | ||
* | ||
* The socket is not in the Connection state. | ||
* ## `"address-not-bindable"` | ||
@@ -109,15 +76,6 @@ * | ||
* | ||
* A bind operation failed because the provided address is already in use. | ||
* ## `"ephemeral-ports-exhausted"` | ||
* | ||
* A bind operation failed because there are no ephemeral ports available. | ||
* A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. | ||
* ## `"remote-unreachable"` | ||
* | ||
* The remote address is not reachable | ||
* ## `"already-listening"` | ||
* | ||
* The socket is already in the Listener state. | ||
* ## `"not-listening"` | ||
* | ||
* The socket is already in the Listener state. | ||
* ## `"connection-refused"` | ||
@@ -129,7 +87,7 @@ * | ||
* The connection was reset. | ||
* ## `"connection-aborted"` | ||
* | ||
* A connection was aborted. | ||
* ## `"datagram-too-large"` | ||
* | ||
* ## `"invalid-name"` | ||
* | ||
* The provided name is a syntactically invalid domain name. | ||
* ## `"name-unresolvable"` | ||
@@ -145,3 +103,3 @@ * | ||
*/ | ||
export type ErrorCode = 'unknown' | 'access-denied' | 'not-supported' | 'out-of-memory' | 'timeout' | 'concurrency-conflict' | 'not-in-progress' | 'would-block' | 'address-family-not-supported' | 'address-family-mismatch' | 'invalid-remote-address' | 'ipv4-only-operation' | 'ipv6-only-operation' | 'new-socket-limit' | 'already-attached' | 'already-bound' | 'already-connected' | 'not-bound' | 'not-connected' | 'address-not-bindable' | 'address-in-use' | 'ephemeral-ports-exhausted' | 'remote-unreachable' | 'already-listening' | 'not-listening' | 'connection-refused' | 'connection-reset' | 'datagram-too-large' | 'invalid-name' | 'name-unresolvable' | 'temporary-resolver-failure' | 'permanent-resolver-failure'; | ||
export type ErrorCode = 'unknown' | 'access-denied' | 'not-supported' | 'invalid-argument' | 'out-of-memory' | 'timeout' | 'concurrency-conflict' | 'not-in-progress' | 'would-block' | 'invalid-state' | 'new-socket-limit' | 'address-not-bindable' | 'address-in-use' | 'remote-unreachable' | 'connection-refused' | 'connection-reset' | 'connection-aborted' | 'datagram-too-large' | 'name-unresolvable' | 'temporary-resolver-failure' | 'permanent-resolver-failure'; | ||
/** | ||
@@ -148,0 +106,0 @@ * # Variants |
@@ -14,5 +14,4 @@ export namespace WasiSocketsTcpCreateSocket { | ||
* # Typical errors | ||
* - `not-supported`: The host does not support TCP sockets. (EOPNOTSUPP) | ||
* - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) | ||
* - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) | ||
* - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) | ||
* - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) | ||
* | ||
@@ -19,0 +18,0 @@ * # References |
@@ -15,8 +15,9 @@ export namespace WasiSocketsTcp { | ||
* # Typical `start` errors | ||
* - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) | ||
* - `already-bound`: The socket is already bound. (EINVAL) | ||
* - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) | ||
* - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) | ||
* - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) | ||
* - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL) | ||
* - `invalid-state`: The socket is already bound. (EINVAL) | ||
* | ||
* # Typical `finish` errors | ||
* - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) | ||
* - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) | ||
* - `address-in-use`: Address is already in use. (EADDRINUSE) | ||
@@ -33,4 +34,3 @@ * - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) | ||
*/ | ||
export function startBind(this_: TcpSocket, network: Network, localAddress: IpSocketAddress): void; | ||
export function finishBind(this_: TcpSocket): void; | ||
export { TcpSocket }; | ||
/** | ||
@@ -43,10 +43,21 @@ * Connect to a remote endpoint. | ||
* | ||
* POSIX mentions: | ||
* > If connect() fails, the state of the socket is unspecified. Conforming applications should | ||
* > close the file descriptor and create a new socket before attempting to reconnect. | ||
* | ||
* WASI prescribes the following behavior: | ||
* - If `connect` fails because an input/state validation error, the socket should remain usable. | ||
* - If a connection was actually attempted but failed, the socket should become unusable for further network communication. | ||
* Besides `drop`, any method after such a failure may return an error. | ||
* | ||
* # Typical `start` errors | ||
* - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) | ||
* - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) | ||
* - `invalid-remote-address`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) | ||
* - `already-attached`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. | ||
* - `already-connected`: The socket is already in the Connection state. (EISCONN) | ||
* - `already-listening`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) | ||
* - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EALREADY) | ||
* - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) | ||
* - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) | ||
* - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address, but the socket has `ipv6-only` enabled. (EINVAL, EADDRNOTAVAIL on Illumos) | ||
* - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) | ||
* - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) | ||
* - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) | ||
* - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. | ||
* - `invalid-state`: The socket is already in the Connection state. (EISCONN) | ||
* - `invalid-state`: The socket is already in the Listener state. (EOPNOTSUPP, EINVAL on Windows) | ||
* | ||
@@ -57,4 +68,5 @@ * # Typical `finish` errors | ||
* - `connection-reset`: The connection was reset. (ECONNRESET) | ||
* - `connection-aborted`: The connection was aborted. (ECONNABORTED) | ||
* - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) | ||
* - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) | ||
* - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) | ||
* - `not-in-progress`: A `connect` operation is not in progress. | ||
@@ -69,10 +81,3 @@ * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) | ||
*/ | ||
export function startConnect(this_: TcpSocket, network: Network, remoteAddress: IpSocketAddress): void; | ||
/** | ||
* Note: the returned `input-stream` and `output-stream` are child | ||
* resources of the `tcp-socket`. Implementations may trap if the | ||
* `tcp-socket` is dropped before both of these streams are dropped. | ||
*/ | ||
export function finishConnect(this_: TcpSocket): [InputStream, OutputStream]; | ||
/** | ||
* Start listening for new connections. | ||
@@ -87,9 +92,8 @@ * | ||
* # Typical `start` errors | ||
* - `not-bound`: The socket is not bound to any local address. (EDESTADDRREQ) | ||
* - `already-connected`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) | ||
* - `already-listening`: The socket is already in the Listener state. | ||
* - `concurrency-conflict`: Another `bind`, `connect` or `listen` operation is already in progress. (EINVAL on BSD) | ||
* - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) | ||
* - `invalid-state`: The socket is already in the Connection state. (EISCONN, EINVAL on BSD) | ||
* - `invalid-state`: The socket is already in the Listener state. | ||
* | ||
* # Typical `finish` errors | ||
* - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) | ||
* - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) | ||
* - `not-in-progress`: A `listen` operation is not in progress. | ||
@@ -104,8 +108,13 @@ * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) | ||
*/ | ||
export function startListen(this_: TcpSocket): void; | ||
export function finishListen(this_: TcpSocket): void; | ||
/** | ||
* Accept a new client socket. | ||
* | ||
* The returned socket is bound and in the Connection state. | ||
* The returned socket is bound and in the Connection state. The following properties are inherited from the listener socket: | ||
* - `address-family` | ||
* - `ipv6-only` | ||
* - `keep-alive` | ||
* - `no-delay` | ||
* - `unicast-hop-limit` | ||
* - `receive-buffer-size` | ||
* - `send-buffer-size` | ||
* | ||
@@ -115,12 +124,8 @@ * On success, this function returns the newly accepted client socket along with | ||
* | ||
* Note: the returned `input-stream` and `output-stream` are child | ||
* resources of the returned `tcp-socket`. Implementations may trap if the | ||
* `tcp-socket` is dropped before its child streams are dropped. | ||
* | ||
* # Typical errors | ||
* - `not-listening`: Socket is not in the Listener state. (EINVAL) | ||
* - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) | ||
* - `invalid-state`: Socket is not in the Listener state. (EINVAL) | ||
* - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) | ||
* - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) | ||
* - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) | ||
* | ||
* Host implementations must skip over transient errors returned by the native accept syscall. | ||
* | ||
* # References | ||
@@ -132,8 +137,13 @@ * - <https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html> | ||
*/ | ||
export function accept(this_: TcpSocket): [TcpSocket, InputStream, OutputStream]; | ||
/** | ||
* Get the bound local address. | ||
* | ||
* POSIX mentions: | ||
* > If the socket has not been bound to a local name, the value | ||
* > stored in the object pointed to by `address` is unspecified. | ||
* | ||
* WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. | ||
* | ||
* # Typical errors | ||
* - `not-bound`: The socket is not bound to any local address. | ||
* - `invalid-state`: The socket is not bound to any local address. | ||
* | ||
@@ -146,8 +156,7 @@ * # References | ||
*/ | ||
export function localAddress(this_: TcpSocket): IpSocketAddress; | ||
/** | ||
* Get the bound remote address. | ||
* Get the remote address. | ||
* | ||
* # Typical errors | ||
* - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) | ||
* - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) | ||
* | ||
@@ -160,3 +169,2 @@ * # References | ||
*/ | ||
export function remoteAddress(this_: TcpSocket): IpSocketAddress; | ||
/** | ||
@@ -167,3 +175,2 @@ * Whether this is a IPv4 or IPv6 socket. | ||
*/ | ||
export function addressFamily(this_: TcpSocket): IpAddressFamily; | ||
/** | ||
@@ -175,9 +182,6 @@ * Whether IPv4 compatibility (dual-stack) mode is disabled or not. | ||
* # Typical errors | ||
* - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. | ||
* - `already-bound`: (set) The socket is already bound. | ||
* - `invalid-state`: (set) The socket is already bound. | ||
* - `not-supported`: (get/set) `this` socket is an IPv4 socket. | ||
* - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) | ||
* - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) | ||
*/ | ||
export function ipv6Only(this_: TcpSocket): boolean; | ||
export function setIpv6Only(this_: TcpSocket, value: boolean): void; | ||
/** | ||
@@ -187,22 +191,13 @@ * Hints the desired listen queue size. Implementations are free to ignore this. | ||
* # Typical errors | ||
* - `already-connected`: (set) The socket is already in the Connection state. | ||
* - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) | ||
* - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. | ||
* - `invalid-state`: (set) The socket is already in the Connection state. | ||
*/ | ||
export function setListenBacklogSize(this_: TcpSocket, value: bigint): void; | ||
/** | ||
* Equivalent to the SO_KEEPALIVE socket option. | ||
* | ||
* # Typical errors | ||
* - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) | ||
*/ | ||
export function keepAlive(this_: TcpSocket): boolean; | ||
export function setKeepAlive(this_: TcpSocket, value: boolean): void; | ||
/** | ||
* Equivalent to the TCP_NODELAY socket option. | ||
* | ||
* # Typical errors | ||
* - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) | ||
* The default value is `false`. | ||
*/ | ||
export function noDelay(this_: TcpSocket): boolean; | ||
export function setNoDelay(this_: TcpSocket, value: boolean): void; | ||
/** | ||
@@ -212,8 +207,6 @@ * Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. | ||
* # Typical errors | ||
* - `already-connected`: (set) The socket is already in the Connection state. | ||
* - `already-listening`: (set) The socket is already in the Listener state. | ||
* - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) | ||
* - `invalid-argument`: (set) The TTL value must be 1 or higher. | ||
* - `invalid-state`: (set) The socket is already in the Connection state. | ||
* - `invalid-state`: (set) The socket is already in the Listener state. | ||
*/ | ||
export function unicastHopLimit(this_: TcpSocket): number; | ||
export function setUnicastHopLimit(this_: TcpSocket, value: number): void; | ||
/** | ||
@@ -232,21 +225,11 @@ * The kernel buffer space reserved for sends/receives on this socket. | ||
* # Typical errors | ||
* - `already-connected`: (set) The socket is already in the Connection state. | ||
* - `already-listening`: (set) The socket is already in the Listener state. | ||
* - `concurrency-conflict`: (set) A `bind`, `connect` or `listen` operation is already in progress. (EALREADY) | ||
* - `invalid-state`: (set) The socket is already in the Connection state. | ||
* - `invalid-state`: (set) The socket is already in the Listener state. | ||
*/ | ||
export function receiveBufferSize(this_: TcpSocket): bigint; | ||
export function setReceiveBufferSize(this_: TcpSocket, value: bigint): void; | ||
export function sendBufferSize(this_: TcpSocket): bigint; | ||
export function setSendBufferSize(this_: TcpSocket, value: bigint): void; | ||
/** | ||
* Create a `pollable` which will resolve once the socket is ready for I/O. | ||
* | ||
* The created `pollable` is a child resource of the `tcp-socket`. | ||
* Implementations may trap if the `tcp-socket` is dropped before all | ||
* derived `pollable`s created with this function are dropped. | ||
* | ||
* Note: this function is here for WASI Preview2 only. | ||
* It's planned to be removed when `future` is natively supported in Preview3. | ||
*/ | ||
export function subscribe(this_: TcpSocket): Pollable; | ||
/** | ||
@@ -265,3 +248,3 @@ * Initiate a graceful shutdown. | ||
* # Typical errors | ||
* - `not-connected`: The socket is not in the Connection state. (ENOTCONN) | ||
* - `invalid-state`: The socket is not in the Connection state. (ENOTCONN) | ||
* | ||
@@ -274,11 +257,2 @@ * # References | ||
*/ | ||
export function shutdown(this_: TcpSocket, shutdownType: ShutdownType): void; | ||
/** | ||
* Dispose of the specified `tcp-socket`, after which it may no longer be used. | ||
* | ||
* Similar to the POSIX `close` function. | ||
* | ||
* Note: this function is scheduled to be removed when Resources are natively supported in Wit. | ||
*/ | ||
export function dropTcpSocket(this_: TcpSocket): void; | ||
} | ||
@@ -289,3 +263,3 @@ import type { InputStream } from '../interfaces/wasi-io-streams'; | ||
export { OutputStream }; | ||
import type { Pollable } from '../interfaces/wasi-poll-poll'; | ||
import type { Pollable } from '../interfaces/wasi-io-poll'; | ||
export { Pollable }; | ||
@@ -301,6 +275,2 @@ import type { Network } from '../interfaces/wasi-sockets-network'; | ||
/** | ||
* A TCP socket handle. | ||
*/ | ||
export type TcpSocket = number; | ||
/** | ||
* # Variants | ||
@@ -319,1 +289,29 @@ * | ||
export type ShutdownType = 'receive' | 'send' | 'both'; | ||
export class TcpSocket { | ||
startBind(network: Network, localAddress: IpSocketAddress): void; | ||
finishBind(): void; | ||
startConnect(network: Network, remoteAddress: IpSocketAddress): void; | ||
finishConnect(): [InputStream, OutputStream]; | ||
startListen(): void; | ||
finishListen(): void; | ||
accept(): [TcpSocket, InputStream, OutputStream]; | ||
localAddress(): IpSocketAddress; | ||
remoteAddress(): IpSocketAddress; | ||
addressFamily(): IpAddressFamily; | ||
ipv6Only(): boolean; | ||
setIpv6Only(value: boolean): void; | ||
setListenBacklogSize(value: bigint): void; | ||
keepAlive(): boolean; | ||
setKeepAlive(value: boolean): void; | ||
noDelay(): boolean; | ||
setNoDelay(value: boolean): void; | ||
unicastHopLimit(): number; | ||
setUnicastHopLimit(value: number): void; | ||
receiveBufferSize(): bigint; | ||
setReceiveBufferSize(value: bigint): void; | ||
sendBufferSize(): bigint; | ||
setSendBufferSize(value: bigint): void; | ||
subscribe(): Pollable; | ||
shutdown(shutdownType: ShutdownType): void; | ||
} |
@@ -14,5 +14,4 @@ export namespace WasiSocketsUdpCreateSocket { | ||
* # Typical errors | ||
* - `not-supported`: The host does not support UDP sockets. (EOPNOTSUPP) | ||
* - `address-family-not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) | ||
* - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) | ||
* - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) | ||
* - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) | ||
* | ||
@@ -19,0 +18,0 @@ * # References: |
@@ -14,8 +14,7 @@ export namespace WasiSocketsUdp { | ||
* # Typical `start` errors | ||
* - `address-family-mismatch`: The `local-address` has the wrong address family. (EINVAL) | ||
* - `already-bound`: The socket is already bound. (EINVAL) | ||
* - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) | ||
* - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) | ||
* - `invalid-state`: The socket is already bound. (EINVAL) | ||
* | ||
* # Typical `finish` errors | ||
* - `ephemeral-ports-exhausted`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) | ||
* - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) | ||
* - `address-in-use`: Address is already in use. (EADDRINUSE) | ||
@@ -32,4 +31,3 @@ * - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) | ||
*/ | ||
export function startBind(this_: UdpSocket, network: Network, localAddress: IpSocketAddress): void; | ||
export function finishBind(this_: UdpSocket): void; | ||
export { UdpSocket }; | ||
/** | ||
@@ -49,10 +47,10 @@ * Set the destination address. | ||
* # Typical `start` errors | ||
* - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) | ||
* - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) | ||
* - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) | ||
* - `already-attached`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. | ||
* - `concurrency-conflict`: Another `bind` or `connect` operation is already in progress. (EALREADY) | ||
* - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) | ||
* - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) | ||
* - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) | ||
* - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) | ||
* - `invalid-argument`: The socket is already bound to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. | ||
* | ||
* # Typical `finish` errors | ||
* - `ephemeral-ports-exhausted`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) | ||
* - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) | ||
* - `not-in-progress`: A `connect` operation is not in progress. | ||
@@ -67,4 +65,2 @@ * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) | ||
*/ | ||
export function startConnect(this_: UdpSocket, network: Network, remoteAddress: IpSocketAddress): void; | ||
export function finishConnect(this_: UdpSocket): void; | ||
/** | ||
@@ -78,3 +74,3 @@ * Receive messages on the socket. | ||
* # Typical errors | ||
* - `not-bound`: The socket is not bound to any local address. (EINVAL) | ||
* - `invalid-state`: The socket is not bound to any local address. (EINVAL) | ||
* - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) | ||
@@ -93,3 +89,2 @@ * - `would-block`: There is no pending data available to be read at the moment. (EWOULDBLOCK, EAGAIN) | ||
*/ | ||
export function receive(this_: UdpSocket, maxResults: bigint): Datagram[]; | ||
/** | ||
@@ -111,7 +106,8 @@ * Send messages on the socket. | ||
* # Typical errors | ||
* - `address-family-mismatch`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) | ||
* - `invalid-remote-address`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) | ||
* - `invalid-remote-address`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) | ||
* - `already-connected`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) | ||
* - `not-bound`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. | ||
* - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) | ||
* - `invalid-argument`: `remote-address` is a non-IPv4-mapped IPv6 address, but the socket was bound to a specific IPv4-mapped IPv6 address. (or vice versa) | ||
* - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) | ||
* - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) | ||
* - `invalid-argument`: The socket is in "connected" mode and the `datagram.remote-address` does not match the address passed to `connect`. (EISCONN) | ||
* - `invalid-state`: The socket is not bound to any local address. Unlike POSIX, this function does not perform an implicit bind. | ||
* - `remote-unreachable`: The remote address is not reachable. (ECONNREFUSED, ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN) | ||
@@ -131,8 +127,13 @@ * - `datagram-too-large`: The datagram is too large. (EMSGSIZE) | ||
*/ | ||
export function send(this_: UdpSocket, datagrams: Datagram[]): bigint; | ||
/** | ||
* Get the current bound address. | ||
* | ||
* POSIX mentions: | ||
* > If the socket has not been bound to a local name, the value | ||
* > stored in the object pointed to by `address` is unspecified. | ||
* | ||
* WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. | ||
* | ||
* # Typical errors | ||
* - `not-bound`: The socket is not bound to any local address. | ||
* - `invalid-state`: The socket is not bound to any local address. | ||
* | ||
@@ -145,3 +146,2 @@ * # References | ||
*/ | ||
export function localAddress(this_: UdpSocket): IpSocketAddress; | ||
/** | ||
@@ -151,3 +151,3 @@ * Get the address set with `connect`. | ||
* # Typical errors | ||
* - `not-connected`: The socket is not connected to a remote address. (ENOTCONN) | ||
* - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) | ||
* | ||
@@ -160,3 +160,2 @@ * # References | ||
*/ | ||
export function remoteAddress(this_: UdpSocket): IpSocketAddress; | ||
/** | ||
@@ -167,3 +166,2 @@ * Whether this is a IPv4 or IPv6 socket. | ||
*/ | ||
export function addressFamily(this_: UdpSocket): IpAddressFamily; | ||
/** | ||
@@ -175,17 +173,9 @@ * Whether IPv4 compatibility (dual-stack) mode is disabled or not. | ||
* # Typical errors | ||
* - `ipv6-only-operation`: (get/set) `this` socket is an IPv4 socket. | ||
* - `already-bound`: (set) The socket is already bound. | ||
* - `not-supported`: (get/set) `this` socket is an IPv4 socket. | ||
* - `invalid-state`: (set) The socket is already bound. | ||
* - `not-supported`: (set) Host does not support dual-stack sockets. (Implementations are not required to.) | ||
* - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) | ||
*/ | ||
export function ipv6Only(this_: UdpSocket): boolean; | ||
export function setIpv6Only(this_: UdpSocket, value: boolean): void; | ||
/** | ||
* Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. | ||
* | ||
* # Typical errors | ||
* - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) | ||
*/ | ||
export function unicastHopLimit(this_: UdpSocket): number; | ||
export function setUnicastHopLimit(this_: UdpSocket, value: number): void; | ||
/** | ||
@@ -202,10 +192,3 @@ * The kernel buffer space reserved for sends/receives on this socket. | ||
* Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. | ||
* | ||
* # Typical errors | ||
* - `concurrency-conflict`: (set) Another `bind` or `connect` operation is already in progress. (EALREADY) | ||
*/ | ||
export function receiveBufferSize(this_: UdpSocket): bigint; | ||
export function setReceiveBufferSize(this_: UdpSocket, value: bigint): void; | ||
export function sendBufferSize(this_: UdpSocket): bigint; | ||
export function setSendBufferSize(this_: UdpSocket, value: bigint): void; | ||
/** | ||
@@ -217,11 +200,4 @@ * Create a `pollable` which will resolve once the socket is ready for I/O. | ||
*/ | ||
export function subscribe(this_: UdpSocket): Pollable; | ||
/** | ||
* Dispose of the specified `udp-socket`, after which it may no longer be used. | ||
* | ||
* Note: this function is scheduled to be removed when Resources are natively supported in Wit. | ||
*/ | ||
export function dropUdpSocket(this_: UdpSocket): void; | ||
} | ||
import type { Pollable } from '../interfaces/wasi-poll-poll'; | ||
import type { Pollable } from '../interfaces/wasi-io-poll'; | ||
export { Pollable }; | ||
@@ -236,6 +212,2 @@ import type { Network } from '../interfaces/wasi-sockets-network'; | ||
export { IpAddressFamily }; | ||
/** | ||
* A UDP socket handle. | ||
*/ | ||
export type UdpSocket = number; | ||
export interface Datagram { | ||
@@ -245,1 +217,22 @@ data: Uint8Array, | ||
} | ||
export class UdpSocket { | ||
startBind(network: Network, localAddress: IpSocketAddress): void; | ||
finishBind(): void; | ||
startConnect(network: Network, remoteAddress: IpSocketAddress): void; | ||
finishConnect(): void; | ||
receive(maxResults: bigint): Datagram[]; | ||
send(datagrams: Datagram[]): bigint; | ||
localAddress(): IpSocketAddress; | ||
remoteAddress(): IpSocketAddress; | ||
addressFamily(): IpAddressFamily; | ||
ipv6Only(): boolean; | ||
setIpv6Only(value: boolean): void; | ||
unicastHopLimit(): number; | ||
setUnicastHopLimit(value: number): void; | ||
receiveBufferSize(): bigint; | ||
setReceiveBufferSize(value: bigint): void; | ||
sendBufferSize(): bigint; | ||
setSendBufferSize(value: bigint): void; | ||
subscribe(): Pollable; | ||
} |
@@ -16,4 +16,4 @@ import { WasiCliEnvironment } from './interfaces/wasi-cli-environment'; | ||
import { WasiFilesystemTypes } from './interfaces/wasi-filesystem-types'; | ||
import { WasiIoPoll } from './interfaces/wasi-io-poll'; | ||
import { WasiIoStreams } from './interfaces/wasi-io-streams'; | ||
import { WasiPollPoll } from './interfaces/wasi-poll-poll'; | ||
import { WasiRandomInsecure } from './interfaces/wasi-random-insecure'; | ||
@@ -20,0 +20,0 @@ import { WasiRandomInsecureSeed } from './interfaces/wasi-random-insecure-seed'; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
154781
53
4500