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:
2026-04-30 12:34:26 +00:00
parent 9c41f683ee
commit 29f0dd7af0
37 changed files with 9287 additions and 0 deletions

56
src/env.ts Normal file
View File

@@ -0,0 +1,56 @@
import { OperationType } from "./types.js";
import type { OperationContext, OperationEnv } from "./types.js";
import type { OperationRegistry } from "./registry.js";
import { getLogger } from "@logtape/logtape";
const logger = getLogger("operations:env");
export interface PendingRequestMap {
call(operationId: string, input: unknown, options?: { parentRequestId?: string; identity?: unknown }): Promise<unknown>;
}
export interface EnvOptions {
registry: OperationRegistry;
context: OperationContext;
allowedNamespaces?: string[];
callMap?: PendingRequestMap;
}
export function buildEnv(options: EnvOptions): OperationEnv {
const { registry, context, allowedNamespaces, callMap } = options;
const operations = registry.list();
const namespaces: OperationEnv = {};
for (const operation of operations) {
if (allowedNamespaces && !allowedNamespaces.includes(operation.namespace)) {
continue;
}
if (operation.type === OperationType.SUBSCRIPTION) {
continue;
}
if (!namespaces[operation.namespace]) {
namespaces[operation.namespace] = {};
}
const operationId = `${operation.namespace}.${operation.name}`;
if (callMap) {
namespaces[operation.namespace][operation.name] = async (input: unknown) => {
logger.debug(`Call protocol: ${operationId}`);
return await callMap.call(operationId, input, {
parentRequestId: context.requestId,
});
};
} else {
namespaces[operation.namespace][operation.name] = async (input: unknown) => {
logger.debug(`Executing: ${operationId}`);
return await registry.execute(operationId, input, context);
};
}
}
return namespaces;
}