ts/docs/development.md

200 lines
7.1 KiB
Markdown
Raw Normal View History

---
title: Development
description: How to build, test, and contribute to CoreTS.
---
# Development
## Prerequisites
- **Go 1.26+** (uses Go workspaces)
- **Deno 2.x** (required for integration tests)
- **protoc** + Go/gRPC plugins (only if regenerating protobuf stubs)
This module is part of a Go workspace at `~/Code/go.work`. After cloning, ensure the workspace includes it:
```bash
go work use ./core/ts
```
## Building
CoreTS is a library package with no standalone binary. It compiles as part of applications that import it:
```bash
go build ./...
```
## Running Tests
### Unit Tests
Unit tests cover the Go side without requiring Deno:
```bash
core go test
# or
go test ./...
```
Tests use the `_Good`, `_Bad`, `_Ugly` suffix convention:
- `_Good` -- happy path
- `_Bad` -- expected error conditions
- `_Ugly` -- panics and edge cases
### Integration Tests
Integration tests require a working Deno installation and are gated behind the `integration` build tag:
```bash
go test -tags integration -timeout 60s ./...
```
These tests boot the full CoreTS stack (Go gRPC server + Deno sidecar + Workers) and verify end-to-end communication. They are organised in tiers:
| Tier | Test | What it proves |
|------|------|----------------|
| 1 | `TestIntegration_FullBoot_Good` | Go gRPC server starts, Deno sidecar launches, store round-trip works |
| 2 | `TestIntegration_Tier2_Bidirectional_Good` | Go can call Deno (LoadModule/UnloadModule/ModuleStatus), bidirectional communication |
| 3 | `TestIntegration_Tier3_WorkerIsolation_Good` | Module Workers can call back to Go via the I/O bridge (store write from inside a Worker) |
| 4 | `TestIntegration_Tier4_MarketplaceInstall_Good` | Full marketplace flow: install from Git, load module, verify I/O bridge, unload, remove |
If Deno is not installed, integration tests are automatically skipped.
### Single Test
```bash
core go test --run TestCheckPath_Good_Allowed
# or
go test -run TestCheckPath_Good_Allowed ./...
```
### Test Coverage
```bash
core go cov
core go cov --open # Opens HTML report
```
## Code Quality
```bash
core go fmt # Format
core go lint # Lint
core go vet # Vet
core go qa # All of the above + tests
core go qa full # + race detector, vulnerability scan
```
## Regenerating Protobuf Stubs
If you modify `proto/coredeno.proto`, regenerate the Go stubs:
```bash
protoc --go_out=. --go-grpc_out=. proto/coredeno.proto
```
Ensure you have the `protoc-gen-go` and `protoc-gen-go-grpc` plugins installed:
```bash
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
```
The Deno side loads the `.proto` file dynamically at runtime via `@grpc/proto-loader`, so no TypeScript code generation is needed.
## Project Structure
```
forge.lthn.ai/core/ts/
├── coredeno.go # Options, Permissions, Sidecar types
├── coredeno_test.go # Unit tests for options and sidecar creation
├── lifecycle.go # Sidecar Start/Stop/IsRunning
├── lifecycle_test.go # Unit tests for process lifecycle
├── listener.go # ListenGRPC -- Unix socket gRPC server
├── listener_test.go # Unit tests for gRPC listener
├── server.go # CoreService gRPC implementation
├── server_test.go # Unit tests for gRPC handlers + permissions
├── denoclient.go # DenoClient JSON-RPC client
├── permissions.go # CheckPath/CheckNet/CheckRun helpers
├── permissions_test.go # Unit tests for permission checks
├── service.go # Framework Service integration
├── service_test.go # Unit tests for service lifecycle
├── integration_test.go # End-to-end tests (build tag: integration)
├── go.mod
├── go.sum
├── proto/
│ ├── coredeno.proto # Service + message definitions
│ ├── coredeno.pb.go # Generated protobuf code
│ └── coredeno_grpc.pb.go # Generated gRPC stubs
└── runtime/
├── main.ts # Deno entry point
├── client.ts # CoreService gRPC client (Deno calls Go)
├── server.ts # DenoService JSON-RPC server (Go calls Deno)
├── modules.ts # Module registry + Worker isolation
├── worker-entry.ts # Worker bootstrap script
├── polyfill.ts # Deno 2.x http2/grpc-js patches
├── deno.json # Deno configuration + npm imports
├── deno.lock # Lock file
└── testdata/
└── test-module.ts # Test fixture for integration tests
```
## Writing a TypeScript Module
A module is a TypeScript file that exports an `init` function:
```typescript
export async function init(core: any) {
// Use the I/O bridge to interact with Go-managed resources
await core.storeSet("my-module", "status", "running");
const data = await core.fileRead("./data/config.json");
console.log("Config loaded:", data);
}
```
The `core` object provides:
| Method | Description |
|--------|-------------|
| `core.storeGet(group, key)` | Read from the key-value store |
| `core.storeSet(group, key, value)` | Write to the key-value store |
| `core.fileRead(path)` | Read a file (permission-gated) |
| `core.fileWrite(path, content)` | Write a file (permission-gated) |
| `core.processStart(command, args)` | Start a subprocess (permission-gated) |
| `core.processStop(processId)` | Stop a subprocess |
All operations are relayed through the Go gRPC server and checked against the module's declared permissions.
## Adding a New gRPC Method
1. Add the RPC and message definitions to `proto/coredeno.proto`
2. Regenerate Go stubs: `protoc --go_out=. --go-grpc_out=. proto/coredeno.proto`
3. Implement the handler in `server.go` with appropriate permission checks
4. Add the method to `runtime/client.ts` (CoreService calls) or `runtime/server.ts` (DenoService calls)
5. If the method should be available to Workers, add it to `runtime/worker-entry.ts` and `runtime/modules.ts` (`dispatchRPC`)
6. Write unit tests (`server_test.go`) and integration tests (`integration_test.go`)
## Coding Standards
- **UK English** in documentation and user-facing strings (colour, organisation, centre)
- **Strict typing** -- all Go function parameters and return types must be declared
- **Test naming** -- use `_Good`, `_Bad`, `_Ugly` suffixes
- **Error context** -- wrap errors with the subsystem prefix: `fmt.Errorf("coredeno: <context>: %w", err)`
- **Thread safety** -- use `sync.RWMutex` for shared state; the Sidecar and DenoClient are both thread-safe
- **Secure by default** -- empty permission lists deny all access; reserved store namespaces are blocked
## Dependency Graph
```
forge.lthn.ai/core/ts
├── forge.lthn.ai/core/go (DI container, ServiceRuntime)
├── forge.lthn.ai/core/go-io (Sandboxed Medium, MockMedium, Store)
├── forge.lthn.ai/core/go-scm (Manifest loading, Marketplace installer)
├── google.golang.org/grpc (gRPC server + client)
└── google.golang.org/protobuf (Protocol buffer runtime)
```
CoreTS has no circular dependencies. It depends on the core framework but the framework does not depend on it.