# Console/WASI Output Strategy

## Target Architecture

Two compilation targets with different binary formats:

| Target                    | Binary Format  | Imports             | Runtimes                             |
| ------------------------- | -------------- | ------------------- | ------------------------------------ |
| `--target host` (default) | Core WASM-GC   | Custom `console::*` | `@zena-lang/runtime` (Node, browser) |
| `--target wasi`           | Component (CM) | `wasi:cli/stdout`   | wasmtime, jco, any WASI runtime      |

## Core WASM vs Component Model Binary Format

**They are different binary formats**, not just metadata. Key differences:

| Aspect        | Core WASM Module                        | Component                                         |
| ------------- | --------------------------------------- | ------------------------------------------------- |
| Magic bytes   | `\0asm` + version `0x01 0x00 0x00 0x00` | `\0asm` + version `0x0d 0x00` + layer `0x01 0x00` |
| Instantiation | `WebAssembly.instantiate()`             | Requires component-aware runtime                  |
| Structure     | Single module                           | Can contain multiple core modules + metadata      |
| Type system   | Core types (i32, f64, funcref, etc.)    | Rich types (string, record, variant, list, etc.)  |
| Memory        | Can be shared/exported                  | Private to component                              |

**Reference**: [Component Model Binary
Format](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Binary.md)

### Can Zena Emit Components Directly?

**Yes, technically.** The component binary format is well-specified. However:

1. **Complexity**: Components have a richer type system and structure
2. **Tooling exists**: `wasm-tools component new` already does this well
3. **Separation of concerns**: Core WASM + metadata → Component is a clean
   pipeline

**Short-term**: Use `wasm-tools component embed` + `wasm-tools component new`
**Long-term**: Could integrate component emission into the compiler

## What is wasm-tools?

`wasm-tools` is the Bytecode Alliance toolkit for WASM manipulation:

```bash
# Step 1: Embed WIT metadata describing the component interface
wasm-tools component embed --world my-world wit/ core.wasm -o embedded.wasm

# Step 2: Wrap as component
wasm-tools component new embedded.wasm -o component.wasm
```

The core module needs embedded WIT metadata describing its imports/exports in
component type terms. This metadata is typically:

- Generated by `wit-bindgen` during compilation (for Rust, C, etc.)
- Added manually with `wasm-tools component embed`

## Why Component Model for WASI?

WASI Preview 2 (current) and Preview 3 (coming, with async) are built on the
Component Model. Benefits:

- **Standard interface**: All WASI runtimes speak the same language
- **Future-proof**: Preview 3 async will use the same model
- **Composability**: Components can be linked together
- **wasmtime native**: Full support with `-W gc=y -W component-model=y`

## What is wasm-tools?

`wasm-tools` is the Bytecode Alliance toolkit for WASM manipulation. Key
subcommand:

```bash
wasm-tools component new core.wasm -o component.wasm
```

This **wraps** a core WASM module into a Component. The core module needs
embedded WIT metadata describing its imports/exports.

## Implementation Plan

### Phase 1: Two-Step Build (Simplest)

1. Add `--target wasi` flag to compiler
2. When `--target wasi`:
   - Import WASI functions via canonical ABI
   - Embed WIT metadata using `wasm-tools component embed`
   - User runs `wasm-tools component new` to create final component

```bash
# Build with WASI target
zena build main.zena --target wasi -o main.core.wasm

# Wrap as component (separate step for now)
wasm-tools component new main.core.wasm -o main.wasm

# Run with wasmtime
wasmtime run main.wasm
```

### Phase 2: Integrated Component Output

Integrate component wrapping into the compiler:

```bash
# Single command produces component
zena build main.zena --target wasi -o main.wasm
wasmtime run main.wasm
```

### Phase 3: Preview 3 Async

When WASI Preview 3 stabilizes:

- Add async function support to Zena
- Map to Preview 3 `future` and `stream` types

## Console Implementation by Target

### `--target host` (current behavior)

```
packages/stdlib/zena/
└── console.zena          # Uses @external("console", "log_string")
```

Runtime (`@zena-lang/runtime`) provides:

```typescript
{
  console: {
    log_string: (strRef, len) => {
      /* read GC string, print */
    };
  }
}
```

### `--target wasi`

```
packages/stdlib/zena/
└── console/wasi.zena     # Uses WASI canonical ABI
```

Uses `wasi:cli/stdout` stream. In Preview 2 this means:

- Get `stdout` stream from `wasi:cli/environment`
- Write UTF-8 bytes via `wasi:io/streams.write`

## File Structure

```
packages/stdlib/zena/
└── console/
    ├── interface.zena    # Shared Console interface
    ├── host.zena         # @external("console", ...) implementation
    └── wasi.zena         # WASI stream implementation
```

The compiler switches which file backs `zena:console` based on `--target`,
via the `virtual` entry in `stdlib-manifest.json` (host → `console/host.zena`,
wasi → `console/wasi.zena`). The implementation files are not importable by
name; they share `interface.zena` via relative imports.

## The GC + Component Model Challenge

Zena uses WASM-GC (structs, arrays, etc). The Component Model historically used
Linear Memory.

**Good news**: Component Model GC (`component-model-gc`) is being added!

- wasmtime: `-W component-model-gc=y` flag exists
- This allows GC types to cross component boundaries

For strings:

- **Host target**: Pass GC string ref + length, host reads via `$stringGetByte`
- **WASI target**: May need to copy to linear memory for canonical ABI, OR use
  CM-GC when stable

## Testing Matrix

| Test                | Host Target          | WASI Target          |
| ------------------- | -------------------- | -------------------- |
| Node.js             | `@zena-lang/runtime` | `node:wasi` or `jco` |
| Browser             | `@zena-lang/runtime` | `jco` transpile      |
| wasmtime            | ❌                   | ✅ Native            |
| Other WASI runtimes | ❌                   | ✅                   |

## Next Steps

1. [ ] Add `--target` flag to CLI (`host` default, `wasi` option)
2. [ ] Create `console/wasi.zena` using WASI Preview 2 streams
3. [ ] Add WIT metadata embedding to codegen
4. [ ] Test with `wasm-tools component new` + wasmtime
5. [ ] Later: Integrate component wrapping into compiler
