Host functions are capabilities provided by the runtime, not implemented in JavaScript. They represent interfaces that the host must fulfill. By making this explicit with a host:// import scheme, we:
- Clarify the runtime contract — What the host must provide
- Enable alternative hosts — Different runtimes can implement the same interfaces
- Separate concerns — Pure library code vs host-dependent code
- Make dependencies visible — Easy to see what host capabilities a module needs
These are the Rust ops currently exposed to JavaScript:
readFile(path: string): Promise<string>readFileBinary(path: string): Promise<Uint8Array>writeFile(path: string, content: string): Promise<void>writeFileBinary(path: string, content: Uint8Array): Promise<void>isFile(path: string): Promise<boolean>exists(path: string): Promise<boolean>lstat(path: string): Promise<FileStat>mkdir(path: string): Promise<void>readdir(path: string): Promise<string[]>tmpdir(): string
fetch(url: string, options?: RequestInit): Promise<Response>
serve(options: ServeOptions): Server
spawn(cmd: string[], options?: SpawnOptions): Process
setTimeout(callback: () => void, ms: number): numberclearTimeout(id: number): voidsetInterval(callback: () => void, ms: number): numberclearInterval(id: number): void
watchFile(path: string): AsyncIterable<WatchEvent>watchDirectory(path: string): AsyncIterable<WatchEvent>
randomBytes(length: number): Uint8Array
log(...args: any[]): voiddebug(...args: any[]): void
host://<namespace>/<optional-path>
| Namespace | Description | Exports |
|---|---|---|
host://fs |
File system operations | readFile, writeFile, isFile, exists, lstat, mkdir, readdir, tmpdir |
host://http |
HTTP client | fetch |
host://http/server |
HTTP server | serve |
host://process |
Subprocess spawning | spawn |
host://time |
Timers | setTimeout, clearTimeout, setInterval, clearInterval |
host://watch |
File watching | watchFile, watchDirectory |
host://crypto |
Cryptographic utilities | randomBytes |
host://console |
Logging | log, debug |
// Before (importing from "funee")
import { readFile, writeFile } from "funee";
import { fetch } from "funee";
import { serve } from "funee";
import { spawn } from "funee";
import { log, debug } from "funee";
// After (importing from host://)
import { readFile, writeFile, isFile } from "host://fs";
import { fetch } from "host://http";
import { serve } from "host://http/server";
import { spawn } from "host://process";
import { log, debug } from "host://console";
import { setTimeout, clearTimeout } from "host://time";
import { watchFile, watchDirectory } from "host://watch";
import { randomBytes } from "host://crypto";The "funee" import becomes pure JavaScript/TypeScript library code:
import {
// Macros (compile-time, not host functions)
closure, canonicalName, definition,
Closure, CanonicalName, Definition, createMacro,
// Assertions (pure JS)
assertThat, is, not, both, contains, matches,
greaterThan, lessThan,
// Testing framework (pure JS, uses host://time internally)
scenario, runScenarios, runScenariosWatch,
// Streams/axax (pure JS async iterables)
fromArray, toArray, map, filter, reduce, pipe,
// Utilities (pure JS)
join, // path joining is pure string manipulation
} from "funee";- Modify
load_module.rsto recognizehost://scheme - Create host module stubs that re-export from runtime bootstrap
- Update
http_loader.rsto handlehost://specially (not HTTP fetch)
Create TypeScript declaration files for each host namespace:
funee-lib/
host/
fs.ts # host://fs
http.ts # host://http
server.ts # host://http/server
process.ts # host://process
time.ts # host://time
watch.ts # host://watch
crypto.ts # host://crypto
console.ts # host://console
- Move host function wrappers to
funee-lib/host/*.ts - Update internal imports in funee-lib to use
host:// - Re-export from funee-lib/index.ts for backward compatibility (deprecation period)
- Update all test fixtures to use
host://imports - Update examples
- Update README
For backward compatibility:
// funee-lib/index.ts
// @deprecated Use host://fs instead
export { readFile, writeFile } from "host://fs";
// @deprecated Use host://http instead
export { fetch } from "host://http";| Scheme | Pros | Cons |
|---|---|---|
host:// |
Clear "provided by host" semantics | Could confuse with hostnames |
runtime:// |
Clear it's runtime-provided | Longer |
builtin:// |
Node.js precedent | Less clear about replaceability |
native:// |
Clear it's native code | Could confuse with native modules |
Decision: host:// — Short, clear semantics about the host/guest boundary.
Each host:// module has full TypeScript types:
// host://fs types
export declare function readFile(path: string): Promise<string>;
export declare function writeFile(path: string, content: string): Promise<void>;
export declare function isFile(path: string): Promise<boolean>;
// ...
// host://http types
export declare function fetch(
url: string | URL,
init?: RequestInit
): Promise<Response>;
// host://process types
export interface SpawnOptions {
cmd: string[];
cwd?: string;
env?: Record<string, string>;
stdin?: "piped" | "inherit" | "null";
stdout?: "piped" | "inherit" | "null";
stderr?: "piped" | "inherit" | "null";
}
export declare function spawn(options: SpawnOptions): Process;
export declare function spawn(cmd: string, args?: string[]): Promise<CommandOutput>;- Explicit host dependency — Easy to see what a module needs from the host
- Portable code — Pure
"funee"imports work anywhere - Alternative runtimes — Browser, edge workers, etc. can provide different
host://implementations - Tree-shaking — Unused host modules aren't loaded
- Documentation — The URL itself documents what capability is needed
- Testing — Easy to mock
host://imports in tests
- Should timers be globals or imports? — Currently
setTimeoutis a global. Keep as global for compatibility? - Version in URL? —
host://fs@1for future breaking changes? - Capability detection? —
import { available } from "host://fs"to check if host provides it?
The host:// import scheme is now fully implemented:
- Bundler recognizes
host://URLs — Handled inload_module.rs - Host modules resolve to runtime bootstrap — Each namespace maps to pre-loaded runtime code
- Backward compatibility preserved — Old
"funee"imports still work (re-export from host modules)
- When the bundler encounters
import { readFile } from "host://fs", it recognizes thehost://scheme - Instead of fetching from network, it resolves to the corresponding host module definition
- The host module exports functions that call into Rust ops via deno_core's op system
- The runtime bootstrap pre-loads all host module definitions before user code runs
funee-lib/
host/
fs.ts # host://fs → re-exports from bootstrap
http.ts # host://http → re-exports from bootstrap
server.ts # host://http/server → re-exports from bootstrap
process.ts # host://process → re-exports from bootstrap
time.ts # host://time → re-exports from bootstrap
watch.ts # host://watch → re-exports from bootstrap
crypto.ts # host://crypto → re-exports from bootstrap
console.ts # host://console → re-exports from bootstrap
All host:// imports are covered by self-hosted tests in tests/self-hosted/. These tests use the actual runtime and verify that host functions work correctly.