Files
operations/docs/architecture/api-surface.md
glm-5.1 29f0dd7af0 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
2026-04-30 12:34:26 +00:00

11 KiB

status, last_updated
status last_updated
draft 2026-04-30

API Surface

All public types, registry, call protocol, subscribe, env, validation, and adapters. See call-protocol.md for detailed call protocol semantics and adapters.md for adapter internals.

Core Types

OperationType

enum OperationType {
  QUERY = "query",
  MUTATION = "mutation",
  SUBSCRIPTION = "subscription",
}
  • QUERY — read-only, no side effects
  • MUTATION — write, has side effects
  • SUBSCRIPTION — async generator, yields multiple values over time

Identity

interface Identity {
  id: string
  scopes: string[]
  resources?: Record<string, string[]>
}

Caller security context. scopes are global permissions (AND-checked against requiredScopes). resources maps "type:id" to action arrays (checked against resourceType/resourceAction). Derived from keypal ApiKeyMetadata.

AccessControl

type AccessControl = Static<typeof AccessControlSchema>

const AccessControlSchema = Type.Object({
  requiredScopes: Type.Array(Type.String()),
  requiredScopesAny: Type.Optional(Type.Array(Type.String())),
  resourceType: Type.Optional(Type.String()),
  resourceAction: Type.Optional(Type.String()),
  customAuth: Type.Optional(Type.String()),
})
Field Semantics
requiredScopes AND — caller must have ALL listed scopes
requiredScopesAny OR — caller must have at least ONE listed scope
resourceType Resource category for resource-scoped checks
resourceAction Required action on the resource
customAuth Name of custom auth function (not yet enforced)

ErrorDefinition

type ErrorDefinition = Static<typeof ErrorDefinitionSchema>

const ErrorDefinitionSchema = Type.Object({
  code: Type.String(),
  description: Type.String(),
  schema: Type.Unknown(),
  httpStatus: Type.Optional(Type.Number()),
})

Declared on IOperationDefinition.errorSchemas. Contract between operation and callers about what errors it may produce.

OperationContext

type OperationContext = Static<typeof OperationContextSchema> & {
  env?: OperationEnv
  stream?: () => AsyncIterable<unknown>
  pubsub?: unknown
}

Passed to every handler. env provides namespace-keyed access to other operations (via buildEnv). stream and pubsub support subscription and event patterns.

OperationSpec

interface OperationSpec<TInput = unknown, TOutput = unknown> {
  name: string
  namespace: string
  version: string
  type: OperationType
  title?: string
  description: string
  tags?: string[]
  inputSchema: TSchema
  outputSchema: TSchema
  errorSchemas?: ErrorDefinition[]
  accessControl: AccessControl
  _meta?: Record<string, unknown>
}

Serializable, hashable subset of an operation definition. No handler — safe to send over the wire.

IOperationDefinition

interface IOperationDefinition<TInput, TOutput, TContext> extends OperationSpec<TInput, TOutput> {
  handler: OperationHandler<TInput, TOutput, TContext> | SubscriptionHandler<TInput, TOutput, TContext>
}

Full definition including the runtime handler. Registered with OperationRegistry.

OperationHandler / SubscriptionHandler

type OperationHandler<TInput, TOutput, TContext> = (
  input: TInput, context: TContext,
) => Promise<TOutput> | TOutput

type SubscriptionHandler<TInput, TOutput, TContext> = (
  input: TInput, context: TContext,
) => AsyncGenerator<TOutput, void, unknown>

OperationHandler returns a single value. SubscriptionHandler yields values over time.

OperationEnv

type OperationEnv = Record<string, Record<string, (input: unknown) => Promise<unknown>>>

Namespace-keyed operation map. Accessed as env.namespace.operationName(input). Created by buildEnv.

Registry

OperationRegistry

Method Signature Description
register(operation) (operation: IOperationDefinition) => void Register by {namespace}.{name} key. Validates schemas.
registerAll(operations) (operations: IOperationDefinition[]) => void Bulk register.
get(id) (id: string) => IOperationDefinition | undefined Get by full id ("namespace.name").
getByName(namespace, name) (namespace: string, name: string) => IOperationDefinition | undefined Get by parts.
list() () => IOperationDefinition[] All registered operations.
getSpec(id) (id: string) => OperationSpec | undefined Serializable spec (no handler).
getAllSpecs() () => OperationSpec[] All serializable specs.
execute(operationId, input, context) (id: string, input: TInput, ctx: OperationContext) => Promise<TOutput> Validate input, run handler, warn on output mismatch. Throws if not found or validation fails.

Registration key format: {namespace}.{name}. Overwrite on duplicate.

execute validates input with validateOrThrow before calling the handler. Output validation uses collectErrors and logs warnings — it does not throw.

Call Protocol

PendingRequestMap

See call-protocol.md for full semantics.

Method Signature Description
constructor(eventTarget?) (eventTarget?: EventTarget) Creates internal pubsub, wires subscription handlers for responded/error/aborted.
call(operationId, input, options?) Promise<unknown> Publish call.requested, return Promise that resolves on call.responded.
respond(requestId, output) void Publish call.responded.
emitError(requestId, code, message, details?) void Publish call.error.
abort(requestId) void Publish call.aborted, reject pending Promise.
getPendingCount() number Number of in-flight requests.

CallHandler

type CallHandler = (event: CallRequestedEvent) => Promise<void>

Created by buildCallHandler({ registry, eventTarget? }). Subscribes to call.requested, checks access control, validates input, executes via registry. On success: no-op (handler is expected to publish call.responded through the PendingRequestMap). On failure: throws CallError.

CallEventMap

const CallEventMap = {
  "call.requested": Type.Object({ ... }),
  "call.responded": Type.Object({ ... }),
  "call.aborted": Type.Object({ ... }),
  "call.error": Type.Object({ ... }),
}

Typed event map compatible with @alkdev/pubsub. See call-protocol.md for event shapes.

Event Types

Type Fields Description
CallRequestedEvent requestId, operationId, input, parentRequestId?, deadline?, identity? Initiates a call
CallRespondedEvent requestId, output Successful response
CallAbortedEvent requestId Call cancelled
CallErrorEvent requestId, code, message, details? Error response

Subscribe

subscribe

function subscribe(
  registry: OperationRegistry,
  operationId: string,
  input: unknown,
  context: OperationContext,
): AsyncGenerator<unknown, void, unknown>

Direct subscription execution. Gets the operation, casts its handler to AsyncGenerator, yields each value. Properly cleans up the generator on iteration stop (calls generator.return() in finally).

This is the synchronous alternative to the call protocol's call.requestedcall.responded flow for subscriptions. Use subscribe() for in-process subscription consumption; use PendingRequestMap for cross-transport subscription.

Env Builder

buildEnv

function buildEnv(options: EnvOptions): OperationEnv

interface EnvOptions {
  registry: OperationRegistry
  context: OperationContext
  allowedNamespaces?: string[]
  callMap?: PendingRequestMap
}

Creates a namespace-keyed OperationEnv for nested operation calls. Two modes:

  • Direct mode: buildEnv({ registry, context }) — env functions call registry.execute()
  • Call protocol mode: buildEnv({ registry, context, callMap }) — env functions call callMap.call(), publishing call.requested events with parentRequestId for call graph tracking

SUBSCRIPTION operations are filtered out — env only provides QUERY and MUTATION operations for nested calls.

allowedNamespaces restricts which namespaces are available.

Validation

Export Signature Description
assertIsSchema(schema, context?) (unknown, string?) => void Throws if schema is not a valid TypeBox schema.
validateOrThrow(schema, value, context?) (TSchema, unknown, string?) => void Throws with formatted errors if value fails schema check.
collectErrors(schema, value) (TSchema, unknown) => Array<{path, message}> Returns errors array (empty if valid).
formatValueErrors(errors, indent?) (Iterable<{path, message}>, string?) => string Human-readable error formatting.

Error Model

CallError

class CallError extends Error {
  readonly code: CallErrorCode
  readonly details?: unknown
  constructor(code: CallErrorCode, message: string, details?: unknown)
}

InfrastructureErrorCode

enum InfrastructureErrorCode {
  OPERATION_NOT_FOUND = "OPERATION_NOT_FOUND",
  ACCESS_DENIED = "ACCESS_DENIED",
  VALIDATION_ERROR = "VALIDATION_ERROR",
  TIMEOUT = "TIMEOUT",
  ABORTED = "ABORTED",
  EXECUTION_ERROR = "EXECUTION_ERROR",
  UNKNOWN_ERROR = "UNKNOWN_ERROR",
}

CallErrorCode is InfrastructureErrorCode | string — domain codes from errorSchemas are plain strings.

mapError

function mapError(error: unknown, errorSchemas?: { code: string; schema: unknown }[]): CallError

Converts any thrown value to CallError. If the thrown value is already a CallError, returns it. If it's an Error and errorSchemas are provided, matches against declared error codes. Falls back to EXECUTION_ERROR for unmatched Error instances and UNKNOWN_ERROR for non-Error values.

Schema Conversion

FromSchema

function FromSchema<T>(T: T): TSchema

Converts JSON Schema to TypeBox TSchema. Handles: allOf, anyOf, oneOf, enum, object (with required tracking), tuple, array, const, $ref, primitives (string, number, integer, boolean, null). Unknown shapes fall back to Type.Unknown().

Used internally by FromOpenAPI to convert OpenAPI JSON Schema definitions to TypeBox. Also used by from_mcp to convert MCP tool inputSchema (which is JSON Schema).

Adapters

See adapters.md for detailed adapter documentation.

Adapter Import Description
FromOpenAPI Main barrel OpenAPI spec → IOperationDefinition[]
FromOpenAPIFile Main barrel OpenAPI file → IOperationDefinition[]
FromOpenAPIUrl Main barrel OpenAPI URL → IOperationDefinition[]
createMCPClient from-mcp sub-path MCP server → MCPClientWrapper with tool operations
closeMCPClient from-mcp sub-path Close MCP client connection
MCPClientLoader from-mcp sub-path Manage multiple MCP servers
scanOperations Main barrel Filesystem auto-discovery of operation definitions