Skip to content

Commit c3c95ba

Browse files
committed
docs: update documentation for host:// imports
- README.md: Add comprehensive Import Structure section explaining host:// vs funee imports, migration guide, and quick reference table - HOST_FUNCTIONS_DESIGN.md: Add Implementation Notes section with current status and architecture details - Update all examples to use host:// imports for host functions: - demo.ts - github-stats.ts - validator-demo.ts - watch-demo/math.test.ts - watch-demo/debug.ts Host namespaces: - host://fs - File system - host://http - HTTP client - host://http/server - HTTP server - host://process - Subprocess - host://time - Timers - host://watch - File watching - host://crypto - Random bytes - host://console - Logging
1 parent eb215a3 commit c3c95ba

7 files changed

Lines changed: 147 additions & 66 deletions

File tree

HOST_FUNCTIONS_DESIGN.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,39 @@ export declare function spawn(cmd: string, args?: string[]): Promise<CommandOutp
219219
1. **Should timers be globals or imports?** — Currently `setTimeout` is a global. Keep as global for compatibility?
220220
2. **Version in URL?**`host://fs@1` for future breaking changes?
221221
3. **Capability detection?**`import { available } from "host://fs"` to check if host provides it?
222+
223+
## Implementation Notes
224+
225+
### Current Status
226+
227+
The `host://` import scheme is now fully implemented:
228+
229+
1. **Bundler recognizes `host://` URLs** — Handled in `load_module.rs`
230+
2. **Host modules resolve to runtime bootstrap** — Each namespace maps to pre-loaded runtime code
231+
3. **Backward compatibility preserved** — Old `"funee"` imports still work (re-export from host modules)
232+
233+
### How It Works
234+
235+
1. When the bundler encounters `import { readFile } from "host://fs"`, it recognizes the `host://` scheme
236+
2. Instead of fetching from network, it resolves to the corresponding host module definition
237+
3. The host module exports functions that call into Rust ops via deno_core's op system
238+
4. The runtime bootstrap pre-loads all host module definitions before user code runs
239+
240+
### File Structure
241+
242+
```
243+
funee-lib/
244+
host/
245+
fs.ts # host://fs → re-exports from bootstrap
246+
http.ts # host://http → re-exports from bootstrap
247+
server.ts # host://http/server → re-exports from bootstrap
248+
process.ts # host://process → re-exports from bootstrap
249+
time.ts # host://time → re-exports from bootstrap
250+
watch.ts # host://watch → re-exports from bootstrap
251+
crypto.ts # host://crypto → re-exports from bootstrap
252+
console.ts # host://console → re-exports from bootstrap
253+
```
254+
255+
### Testing
256+
257+
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.

README.md

Lines changed: 98 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ funee is a TypeScript runtime designed for functional programming. It bundles an
88

99
- **Compile-time macros** — Transform code at bundle time, not runtime
1010
- **HTTP imports** — Import directly from URLs (like Deno)
11+
- **Host imports (`host://`)** — Explicit host-provided capabilities
1112
- **Declaration-level tree-shaking** — Only include what's actually used
1213
- **Full runtime** — HTTP server, fetch, filesystem, subprocess, timers
1314
- **`using` keyword support** — TypeScript 5.2+ explicit resource management
@@ -26,7 +27,7 @@ cargo build --release
2627

2728
```typescript
2829
// hello.ts
29-
import { log } from "funee";
30+
import { log } from "host://console";
3031

3132
export default () => {
3233
log("Hello, funee!");
@@ -63,7 +64,7 @@ funee --reload main.ts
6364
Create web servers with automatic resource cleanup:
6465

6566
```typescript
66-
import { serve, createResponse, createJsonResponse } from "funee";
67+
import { serve, createResponse, createJsonResponse } from "host://http/server";
6768

6869
export default async () => {
6970
await using server = serve({
@@ -86,7 +87,7 @@ export default async () => {
8687
Web-standard fetch with full Response/Headers support:
8788

8889
```typescript
89-
import { fetch } from "funee";
90+
import { fetch } from "host://http";
9091

9192
export default async () => {
9293
const res = await fetch("https://api.github.com/users/octocat");
@@ -100,7 +101,8 @@ export default async () => {
100101
Transform code at bundle time with full AST access:
101102

102103
```typescript
103-
import { closure, Closure, log } from "funee";
104+
import { closure, Closure } from "funee";
105+
import { log } from "host://console";
104106

105107
// closure macro captures the expression as AST
106108
const add = closure((a: number, b: number) => a + b);
@@ -137,7 +139,7 @@ export default () => add(1, 2);
137139
### File System
138140

139141
```typescript
140-
import { readFile, writeFile, isFile, readdir, tempDir } from "funee";
142+
import { readFile, writeFile, isFile, readdir, tempDir } from "host://fs";
141143

142144
export default async () => {
143145
// Disposable temp directory (auto-deletes on scope exit)
@@ -154,7 +156,7 @@ export default async () => {
154156
### Subprocess
155157

156158
```typescript
157-
import { spawn } from "funee";
159+
import { spawn } from "host://process";
158160

159161
export default async () => {
160162
// Simple form — run and capture output
@@ -178,6 +180,8 @@ export default async () => {
178180
### Timers
179181

180182
```typescript
183+
import { setTimeout, clearTimeout, setInterval, clearInterval } from "host://time";
184+
181185
export default async () => {
182186
// setTimeout with cancellation
183187
const id = setTimeout(() => console.log("fired"), 1000);
@@ -197,7 +201,8 @@ export default async () => {
197201
Re-run scenarios when referenced files change:
198202

199203
```typescript
200-
import { scenario, runScenariosWatch, closure, assertThat, is, log } from "funee";
204+
import { scenario, runScenariosWatch, closure, assertThat, is } from "funee";
205+
import { log } from "host://console";
201206
import { add } from "./math.ts";
202207

203208
const scenarios = [
@@ -220,8 +225,9 @@ export default async () => {
220225
```typescript
221226
import {
222227
assertThat, is, not, both, contains, matches,
223-
greaterThan, lessThan, scenario, runScenarios, closure, log
228+
greaterThan, lessThan, scenario, runScenarios, closure
224229
} from "funee";
230+
import { log } from "host://console";
225231

226232
const scenarios = [
227233
scenario({
@@ -280,49 +286,110 @@ export default () => used();
280286
// Output: only `used` appears
281287
```
282288

283-
## Standard Library
289+
## Import Structure
290+
291+
funee uses a two-layer import system:
292+
293+
### Host Imports (`host://`)
294+
295+
Host-provided runtime capabilities — things that require the native runtime:
296+
297+
```typescript
298+
// File system
299+
import { readFile, writeFile, isFile, lstat, readdir, mkdir, tmpdir, tempDir } from "host://fs";
300+
301+
// HTTP client
302+
import { fetch, httpGetJSON, httpPostJSON } from "host://http";
303+
304+
// HTTP server
305+
import { serve, createResponse, createJsonResponse } from "host://http/server";
306+
307+
// Subprocess
308+
import { spawn } from "host://process";
309+
310+
// Timers
311+
import { setTimeout, clearTimeout, setInterval, clearInterval } from "host://time";
312+
313+
// File watching
314+
import { watchFile, watchDirectory } from "host://watch";
315+
316+
// Crypto
317+
import { randomBytes } from "host://crypto";
318+
319+
// Console
320+
import { log, debug } from "host://console";
321+
```
322+
323+
### Library Imports (`"funee"`)
284324

285-
Import from `"funee"`:
325+
Pure JavaScript/TypeScript library code — works anywhere:
286326

287327
```typescript
288328
import {
289-
// Core
290-
log, debug,
291-
292-
// Macros
329+
// Macros (compile-time)
293330
Closure, CanonicalName, Definition, createMacro,
294331
closure, canonicalName, definition,
295332

296-
// HTTP
297-
fetch, serve, createResponse, createJsonResponse,
298-
httpGetJSON, httpPostJSON,
299-
300-
// Filesystem
301-
readFile, writeFile, isFile, lstat, readdir, mkdir,
302-
join, tmpdir, tempDir,
303-
304-
// Process
305-
spawn,
306-
307-
// Assertions
333+
// Assertions (pure JS)
308334
assertThat, is, not, both, contains, matches,
309335
greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual,
310336

311-
// Testing
337+
// Testing framework
312338
scenario, runScenarios, runScenariosWatch,
313339

314-
// Streams
340+
// Streams (async iterables)
315341
fromArray, toArray, map, filter, reduce, pipe,
316342
fromString, toString, fromBuffer, toBuffer,
317343

318344
// Utilities
319345
cryptoRandomString, someString, someDirectory,
320-
321-
// Watch
322-
watchFile, watchDirectory,
346+
join, // path joining is pure string manipulation
323347
} from "funee";
324348
```
325349

350+
### Why `host://`?
351+
352+
The `host://` scheme makes host dependencies explicit:
353+
354+
1. **Clear contract** — See exactly what runtime capabilities a module needs
355+
2. **Portability** — Pure `"funee"` imports work in any JavaScript environment
356+
3. **Alternative runtimes** — Browser, edge workers, etc. can provide different `host://` implementations
357+
4. **Testing** — Easy to mock `host://` imports
358+
5. **Tree-shaking** — Unused host modules aren't loaded
359+
360+
### Migration Guide
361+
362+
Moving from old-style imports to `host://`:
363+
364+
```typescript
365+
// ❌ Old way (deprecated)
366+
import { readFile, writeFile } from "funee";
367+
import { fetch } from "funee";
368+
import { serve } from "funee";
369+
import { spawn } from "funee";
370+
import { log, debug } from "funee";
371+
372+
// ✅ New way
373+
import { readFile, writeFile } from "host://fs";
374+
import { fetch } from "host://http";
375+
import { serve } from "host://http/server";
376+
import { spawn } from "host://process";
377+
import { log, debug } from "host://console";
378+
```
379+
380+
**Quick reference:**
381+
382+
| Old import | New import |
383+
|------------|------------|
384+
| `readFile`, `writeFile`, `isFile`, `readdir`, `mkdir`, `tmpdir`, `tempDir` | `host://fs` |
385+
| `fetch`, `httpGetJSON`, `httpPostJSON` | `host://http` |
386+
| `serve`, `createResponse`, `createJsonResponse` | `host://http/server` |
387+
| `spawn` | `host://process` |
388+
| `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` | `host://time` |
389+
| `watchFile`, `watchDirectory` | `host://watch` |
390+
| `randomBytes` | `host://crypto` |
391+
| `log`, `debug` | `host://console` |
392+
326393
## Architecture
327394

328395
Built in Rust using:

examples/demo.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,9 @@
22
* 🎸 Funee Capabilities Demo
33
*/
44

5-
import {
6-
log,
7-
writeFile,
8-
readFile,
9-
isFile,
10-
assertThat,
11-
is,
12-
cryptoRandomString,
13-
tmpdir,
14-
} from "funee";
5+
import { log } from "host://console";
6+
import { writeFile, readFile, isFile, tmpdir } from "host://fs";
7+
import { assertThat, is, cryptoRandomString } from "funee";
158

169
export default async () => {
1710
// ═══════════════════════════════════════════════════════════════

examples/github-stats.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,10 @@
55
* async iterables, and generates a formatted report.
66
*/
77

8-
import {
9-
httpGetJSON,
10-
log,
11-
map,
12-
toArray,
13-
fromArray,
14-
writeFile,
15-
} from "funee";
8+
import { log } from "host://console";
9+
import { httpGetJSON } from "host://http";
10+
import { writeFile } from "host://fs";
11+
import { map, toArray, fromArray } from "funee";
1612

1713
// Types
1814
type GithubRepo = {

examples/validator-demo.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,8 @@
55
* assertion library.
66
*/
77

8-
import {
9-
scenario,
10-
runScenarios,
11-
assertThat,
12-
is,
13-
log,
14-
Closure,
15-
} from "funee";
8+
import { log } from "host://console";
9+
import { scenario, runScenarios, assertThat, is, Closure } from "funee";
1610

1711
// Helper to create inline closures
1812
const verify = <T>(fn: T): Closure<T> => ({

examples/watch-demo/debug.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
* Debug: Check if closure macro captures references correctly
33
*/
44

5-
import { closure, log } from "funee";
5+
import { log } from "host://console";
6+
import { closure } from "funee";
67
import { add } from "./math.ts";
78

89
// Just capture, don't execute

examples/watch-demo/math.test.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,8 @@
88
* The references Map still tells the watcher which files matter.
99
*/
1010

11-
import {
12-
scenario,
13-
runScenarios,
14-
assertThat,
15-
is,
16-
log,
17-
Closure,
18-
} from "funee";
11+
import { log } from "host://console";
12+
import { scenario, runScenarios, assertThat, is, Closure } from "funee";
1913

2014
import { add, multiply } from "./math.ts";
2115

0 commit comments

Comments
 (0)