7.1 KiB
| title | description |
|---|---|
| Development | 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:
go work use ./core/ts
Building
CoreTS is a library package with no standalone binary. It compiles as part of applications that import it:
go build ./...
Running Tests
Unit Tests
Unit tests cover the Go side without requiring Deno:
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:
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
core go test --run TestCheckPath_Good_Allowed
# or
go test -run TestCheckPath_Good_Allowed ./...
Test Coverage
core go cov
core go cov --open # Opens HTML report
Code Quality
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:
protoc --go_out=. --go-grpc_out=. proto/coredeno.proto
Ensure you have the protoc-gen-go and protoc-gen-go-grpc plugins installed:
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:
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
- Add the RPC and message definitions to
proto/coredeno.proto - Regenerate Go stubs:
protoc --go_out=. --go-grpc_out=. proto/coredeno.proto - Implement the handler in
server.gowith appropriate permission checks - Add the method to
runtime/client.ts(CoreService calls) orruntime/server.ts(DenoService calls) - If the method should be available to Workers, add it to
runtime/worker-entry.tsandruntime/modules.ts(dispatchRPC) - 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,_Uglysuffixes - Error context -- wrap errors with the subsystem prefix:
fmt.Errorf("coredeno: <context>: %w", err) - Thread safety -- use
sync.RWMutexfor 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.