Files
operations/docs/architecture/build-distribution.md
glm-5.1 d0017df2bf Update architecture docs for handler separation and pubsub API changes
- api-surface.md: Updated registry API table (registerSpec, registerHandler,
  getHandler, separated spec/handler storage), OperationSpec description,
  IOperationDefinition marked as convenience type, adapter return types
- call-protocol.md: Added pubsub EventEnvelope unwrapping details,
  subscribe(type, id) 2-arg API, handler separation in buildCallHandler
  and subscribe(), handler separation section
- adapters.md: Updated return types (OperationSpec & { handler }),
  scanner validates against OperationSpecSchema, new module shape examples
  showing spec-only and spec+handler patterns, typemap mention
- README.md: Core principle updated for spec/handler separation
- build-distribution.md: Updated pubsub dep description, registry.ts description
- AGENTS.md: Updated key points, source layout, provenance status
2026-05-09 08:34:41 +00:00

4.8 KiB

status, last_updated
status last_updated
draft 2026-05-09

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. Uses subscribe(type, id) and publish(type, id, payload) API with EventEnvelope wrapping.
@logtape/logtape Structured logging. Direct import, no wrapper. See ADR-001.

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: registerSpec, registerHandler, 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

{
  "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.

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
// 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