Initial package implementation: operations registry, call protocol, and adapters
Extracted from alkhub_ts packages/core/operations/ and packages/core/mcp/. - Runtime-agnostic (injected fs/env deps, no Deno globals) - Direct @logtape/logtape import instead of logger wrapper - PendingRequestMap with pubsub-wired call protocol - Peer-dep isolation for MCP adapter (sub-path export) - Schema const naming convention (XSchema + X type alias) - 68 tests passing, build + lint + test all green
This commit is contained in:
137
docs/architecture/build-distribution.md
Normal file
137
docs/architecture/build-distribution.md
Normal file
@@ -0,0 +1,137 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-04-30
|
||||
---
|
||||
|
||||
# Build & Distribution
|
||||
|
||||
Dependencies, project structure, sub-path exports, peer deps, and build tooling.
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Runtime
|
||||
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| `@alkdev/typebox` | Schema system. `Type` for building schemas, `Value` for validation, `KindGuard` for schema assertion. |
|
||||
| `@alkdev/pubsub` | Call protocol transport. `PendingRequestMap` creates an internal `PubSub` for event routing. |
|
||||
| `@logtape/logtape` | Structured logging. Direct import, no wrapper. See [ADR-001](decisions/001-logger-direct-import.md). |
|
||||
|
||||
### Peer (Optional)
|
||||
|
||||
| Package | Required By | Purpose |
|
||||
|---------|-------------|---------|
|
||||
| `@modelcontextprotocol/sdk` | `from_mcp` sub-path | MCP client transport (stdio, HTTP). Dynamic import — only loaded when `createMCPClient` is called. |
|
||||
|
||||
### Dev
|
||||
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| `tsup` | Build tool. Dual ESM + CJS with declarations. |
|
||||
| `typescript` | Type checking (`tsc --noEmit` for lint). |
|
||||
| `vitest` | Test runner. |
|
||||
| `@vitest/coverage-v8` | V8 coverage provider. |
|
||||
| `@modelcontextprotocol/sdk` | Dev dep for MCP tests. Also listed as optional peer. |
|
||||
| `@types/node` | Node.js type definitions. |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
@alkdev/operations/
|
||||
src/
|
||||
index.ts # Barrel: re-exports all public API
|
||||
types.ts # Core types: IOperationDefinition, OperationSpec, OperationType, etc.
|
||||
registry.ts # OperationRegistry: register, execute, get, list
|
||||
validation.ts # assertIsSchema, validateOrThrow, collectErrors, formatValueErrors
|
||||
call.ts # PendingRequestMap, buildCallHandler, CallEventMap, event types
|
||||
subscribe.ts # subscribe(): direct AsyncGenerator execution
|
||||
env.ts # buildEnv(): namespace-keyed env with direct/call-protocol modes
|
||||
error.ts # CallError, InfrastructureErrorCode, mapError
|
||||
from_schema.ts # FromSchema: JSON Schema → TypeBox conversion
|
||||
from_openapi.ts # FromOpenAPI, FromOpenAPIFile, FromOpenAPIUrl
|
||||
from_mcp.ts # createMCPClient, closeMCPClient, MCPClientLoader
|
||||
scanner.ts # scanOperations: filesystem auto-discovery
|
||||
test/
|
||||
# Unit tests per module
|
||||
docs/
|
||||
architecture.md
|
||||
architecture/
|
||||
README.md
|
||||
api-surface.md
|
||||
call-protocol.md
|
||||
adapters.md
|
||||
build-distribution.md
|
||||
decisions/
|
||||
package.json
|
||||
tsconfig.json
|
||||
tsup.config.ts
|
||||
vitest.config.ts
|
||||
```
|
||||
|
||||
## Sub-Path Exports
|
||||
|
||||
```json
|
||||
{
|
||||
"exports": {
|
||||
".": {
|
||||
"import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
|
||||
"require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
|
||||
},
|
||||
"./from-mcp": {
|
||||
"import": { "types": "./dist/from-mcp.d.ts", "default": "./dist/from-mcp.js" },
|
||||
"require": { "types": "./dist/from-mcp.d.cts", "default": "./dist/from-mcp.cjs" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `./from-mcp` sub-path isolates the MCP SDK peer dependency. Consumers that don't use MCP don't need to install `@modelcontextprotocol/sdk`. See [ADR-003](decisions/003-peer-dep-adapters.md).
|
||||
|
||||
The main barrel (`src/index.ts`) re-exports everything including `createMCPClient` and `MCPClientLoader` for convenience. The sub-path exists for explicit dependency isolation, not for excluding from the barrel.
|
||||
|
||||
## Build
|
||||
|
||||
- **Tool**: `tsup` — produces dual ESM + CJS with declarations
|
||||
- **Entry points**: `src/index.ts`, `src/from_mcp.ts`
|
||||
- **Format**: ESM + CJS
|
||||
- **Target**: `es2022`
|
||||
- **Splitting**: enabled
|
||||
|
||||
```ts
|
||||
// tsup.config.ts
|
||||
import { defineConfig } from 'tsup'
|
||||
|
||||
export default defineConfig({
|
||||
entry: ['src/index.ts', 'src/from_mcp.ts'],
|
||||
format: ['esm', 'cjs'],
|
||||
dts: true,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
splitting: true,
|
||||
target: 'es2022',
|
||||
})
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Command | Purpose |
|
||||
|--------|---------|---------|
|
||||
| `build` | `tsup` | Build ESM + CJS + declarations |
|
||||
| `lint` | `tsc --noEmit` | Type-check only (no emit) |
|
||||
| `test` | `vitest run` | Run tests |
|
||||
| `test:watch` | `vitest` | Watch mode |
|
||||
| `test:coverage` | `vitest run --coverage` | Coverage report (v8) |
|
||||
|
||||
## Testing
|
||||
|
||||
- **Runner**: `vitest`
|
||||
- **Coverage**: `@vitest/coverage-v8`
|
||||
- **Config**: `vitest.config.ts`
|
||||
|
||||
Tests should mock external services (MCP servers, HTTP endpoints) and use injectable FS interfaces (`ScannerFS`, `OpenAPIFS`) rather than real filesystem access.
|
||||
|
||||
## Targets
|
||||
|
||||
- **Publish**: npm (`@alkdev/operations`)
|
||||
- **Runtime**: Node 18+, Deno, Bun — pure JS except `from_mcp` which requires `@modelcontextprotocol/sdk`
|
||||
- **Deno compatibility**: Source is standard TypeScript with no Deno-specific APIs. Runtime-agnostic FS injection means Deno can provide its own `ScannerFS` and `OpenAPIFS` implementations
|
||||
Reference in New Issue
Block a user