npm package: @hwy-fm/std
Description: A framework built on [`@hwy-fm/kernel`](https://www.npmjs.com/package/@hwy-fm/kernel).
Author: hwyn
Latest version: 0.1.3

# @hwy-fm/std

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-Strict-blue.svg)](https://www.typescriptlang.org/)

A framework built on [`@hwy-fm/kernel`](https://www.npmjs.com/package/@hwy-fm/kernel).

[Kernel](https://www.npmjs.com/package/@hwy-fm/kernel) is a statically compiled computation kernel that provides five primitives and leaves all architectural decisions to you. std makes those decisions — 8 Slots, a default protocol, one-liner decorators — so you can write business logic directly. Everything std provides is built through Kernel's public API. You can customize any Slot, add new ones, or replace the entire architecture.

## Quick Start

```bash
npm install -g @hwy-fm/cli
hwy create my-app
cd my-app && npm install && hwy start
```

## Pre-configured Slots

std registers 8 Slots across all three pipeline stages:

### INGRESS (request processing)

| Slot | Composer | Order | Typical Use |
|------|----------|-------|-------------|
| `Catch` | onion | 1st | Error boundary — wraps all subsequent Slots |
| `Receive` | linear | 2nd | Parse / deserialize incoming data |
| `Identify` | linear | 3rd | Authentication — resolve caller identity |
| `Guard` | linear | 4th | Authorization — check permissions |
| `Check` | linear | 5th | Validation — verify input constraints |

### PROCESS (business logic)

| Slot | Composer | Use |
|------|----------|-----|
| `Process` | linear | Business logic entry point (Seed) |

### EGRESS (response / cleanup)

| Slot | Composer | Order | Use |
|------|----------|-------|-----|
| `Deliver` | egress | 1st | Serialize and send response |
| `Trace` | egress | 2nd | Logging, metrics, tracing |

EGRESS Slots always execute (finally semantics), even when the pipeline fails.

## Slot Builder API

Each Slot is a `StdSlotBuilder` with typed decorators:

```typescript
// Class decorator — @Injectable() is auto-applied
@Kernel.Guard.instruction('**')
class AuthGuard {
  execute(ctx) { /* ... */ }
}

// Method decorator — host class needs @Injectable()
@Injectable()
class MyService {
  @Kernel.Process.seed('users')
  getUsers(ctx) { ctx.result = []; }

  @Kernel.Process.seed('orders')
  getOrders(ctx) { ctx.result = []; }
}
```

| Method | Description |
|--------|-------------|
| `.instruction()` | Register an Instruction (no filter — runs for all Seeds) |
| `.instruction('pattern')` | Register an Instruction matching a pattern |
| `.instruction(entry)` | Register with full entry options |
| `.seed('pattern')` | Register a Seed with a match pattern |
| `.seed(entry)` | Register a Seed with full entry options |
| `.exclude(token)` | Exclude a specific Instruction from this Slot |
| `.reset()` | Reset all Instructions in this Slot |

### Composer-Typed Signatures

Decorators validate method signatures at compile time based on the Slot's composer:

| Composer | Method Signature | Example |
|----------|-----------------|---------|
| `linear` | `(ctx) => any` | Guard, Check, Process |
| `onion` | `(ctx, next) => any` | Catch |
| `egress` | `(ctx, error?) => any` | Deliver, Trace |

```typescript
// ✅ Correct — onion composer expects (ctx, next)
@Kernel.Catch.instruction()
class ErrorBoundary {
  async execute(ctx, next) {
    try { await next(); }
    catch (e) { ctx.result = { error: e.message }; }
  }
}

// ✅ Correct — egress receives optional error
@Kernel.Deliver.instruction()
class ResponseWriter {
  execute(ctx, error?) {
    if (error) ctx.result = { error: error.message };
  }
}
```

## Custom Slots

Declare additional Slots beyond the 8 pre-configured ones:

```typescript
import { Kernel } from '@hwy-fm/kernel';

// Declare a custom INGRESS slot
const Throttle = Kernel.ingress('throttle', {
  composer: 'linear',
  anchors: { after: ['identify'], before: ['guard'] },
});

@Throttle.instruction('**')
class RateLimiter {
  execute(ctx) { /* rate limiting logic */ }
}
```

| Method | Description |
|--------|-------------|
| `Kernel.ingress(name, options?)` | Declare an INGRESS Slot |
| `Kernel.process(name, options?)` | Declare a PROCESS Slot |
| `Kernel.egress(name, options?)` | Declare an EGRESS Slot |

## Custom Slot Methods

Extend all `StdSlotBuilder` instances with reusable decorator logic via `defineSlotMethod`.

The callback returns a `SlotMethodDescriptor`:

| Field | Description |
|-------|-------------|
| `entry` | Registration metadata (match, order, protocol, etc.). Defaults to `{}` |
| `kind` | `'instruction'` (default) or `'seed'` — controls which registry the decorator writes to |
| `decorate` | Optional transform hook — receives `(target, propertyKey?, descriptor?)` before registration |

```typescript
// A timing decorator — wraps execute to add performance logging
Kernel.defineSlotMethod('timed', (label: string) => ({
  decorate(target, propertyKey, descriptor) {
    const original = propertyKey ? descriptor!.value : target.prototype.execute;
    const wrapped = async (...args) => {
      const start = performance.now();
      const result = await original(...args);
      console.log(`[${label}] ${performance.now() - start}ms`);
      return result;
    };
    if (propertyKey) descriptor!.value = wrapped;
    else target.prototype.execute = wrapped;
  },
}));

// Now available on every Slot:
@Kernel.Process.timed('build')
class BuildHandler {
  execute(ctx) { /* ... */ }
}

// Register as Seed instead of Instruction
Kernel.defineSlotMethod('handler', (match: string) => ({
  entry: { match },
  kind: 'seed',
}));

@Kernel.Process.handler('build')
class BuildSeed {
  execute(ctx) { ctx.result = { ok: true }; }
}
```

### Type-Safe Custom Methods

Use `StdSlotMethodRegistry` declaration merging to get full TypeScript support for custom methods:

```typescript
declare module '@hwy-fm/std' {
  interface StdSlotMethodRegistry {
    timed(label: string): StdDecorator;
    handler(match: string): StdDecorator;
  }
}
```

Once declared, `Kernel.Process.timed('build')` returns a `StdTypedDecorator` that validates `execute()` signatures at compile time — exactly like the built-in `instruction()` and `seed()`.

## Gateway

Cross-kernel dispatch — typed base class that wraps `createContext()` + `forward()`:

```typescript
import { Gateway } from '@hwy-fm/std';

const ViewKernel = KernelModel();

@Kernel.Deliver.instruction()
class ViewBridge extends Gateway(ViewKernel) {
  prepare(ctx) {
    return {
      metadata: { path: ctx.metadata.view },
      input: { data: ctx.result },
    };
  }
}
```

| Method | Description |
|--------|-------------|
| `prepare(ctx)` | Return `{ metadata, input, protocol?, injector? }` or `null` to skip |
| `resolve(result, ctx)` | Handle the target Kernel's result (default: `ctx.result = result`) |
| `execute(ctx)` | Auto-called — runs `prepare()` → `forward()` → `resolve()` |

Gateway automatically:
- Creates a context for the target Kernel
- Uses `forward()` (lightweight, no duplicate telemetry)
- Propagates `signal` for timeout / cancellation
- Shares the debug trace across Kernels

## STD_PROTOCOL

`STD_PROTOCOL` is a built-in protocol identifier. All std decorators register under this protocol by default.

To override the protocol at bootstrap time (e.g. for platform-specific adapters), provide a custom value:

```typescript
@Kernel.bootstrap({}, [
  { provide: STD_PROTOCOL, useValue: MY_CUSTOM_PROTOCOL },
])
class App { /* ... */ }
```

The built-in `StdAdmission` automatically remaps `STD_PROTOCOL` to the provided value at compile time.

## Application Metadata

`@Kernel.bootstrap()` accepts a metadata argument — either a plain object or a DI token pointing to a class that implements `MetadataInfo`. The resolved data is injected as `APPLICATION_METADATA` and can be consumed via the `@Input` decorator.

### Plain Object

```typescript
@Kernel.bootstrap({ api: '/v1', title: 'MyApp', db: { host: 'localhost' } })
class App {
  main() {
    // Called automatically after bootstrap completes
  }
}
```

### Async Loader (MetadataInfo)

When metadata needs to be loaded asynchronously (remote config, environment files, etc.), provide a class that implements `MetadataInfo`:

```typescript
@Injectable()
class RemoteConfig implements MetadataInfo {
  async load() {
    return { api: '/v1', title: 'MyApp' };
  }
}

@Kernel.bootstrap(RemoteConfig)
class App { }
```

### Consuming with @Input

Use `@Input('path')` on constructor parameters or properties to read values from the resolved metadata. Supports nested paths with dot notation:

```typescript
@Kernel.Process.seed('home')
class HomeHandler {
  @Input('api') api!: string;            // '/v1'
  @Input('title') title!: string;        // 'MyApp'
  @Input('db.host') dbHost!: string;     // 'localhost'

  execute(ctx) {
    ctx.result = { api: this.api, title: this.title };
  }
}
```

`@Input` supports properties and constructor parameters. It does not support method-level parameter injection.

## SlotName Constants

```typescript
import { SlotName } from '@hwy-fm/std';

SlotName.Catch     // 'catch'
SlotName.Receive   // 'receive'
SlotName.Identify  // 'identify'
SlotName.Guard     // 'guard'
SlotName.Check     // 'check'
SlotName.Process   // 'process'
SlotName.Deliver   // 'deliver'
SlotName.Trace     // 'trace'
```

## Related Packages

| Package | Description |
|---------|-------------|
| [`@hwy-fm/kernel`](https://www.npmjs.com/package/@hwy-fm/kernel) | The pipeline engine — raw API, multi-kernel isolation, full extensibility |
| [`@hwy-fm/di`](https://www.npmjs.com/package/@hwy-fm/di) | Dependency injection — Scoping, Hooks, Async Governance |
| [`@hwy-fm/cli`](https://www.npmjs.com/package/@hwy-fm/cli) | Build tools + CLI scaffolding |

## License

MIT © [hwyn](https://github.com/hwyn)