chore: establish hwlab l0 contract skeleton
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
export const ENVIRONMENT_DEV = "dev";
|
||||
export const DEV_ENDPOINT = "http://74.48.78.17:6667";
|
||||
export const JSON_RPC_VERSION = "2.0";
|
||||
|
||||
export const SERVICE_IDS = Object.freeze([
|
||||
"hwlab-cloud-api",
|
||||
"hwlab-cloud-web",
|
||||
"hwlab-agent-mgr",
|
||||
"hwlab-agent-worker",
|
||||
"hwlab-gateway",
|
||||
"hwlab-gateway-simu",
|
||||
"hwlab-box-simu",
|
||||
"hwlab-patch-panel",
|
||||
"hwlab-router",
|
||||
"hwlab-tunnel-client",
|
||||
"hwlab-edge-proxy",
|
||||
"hwlab-cli",
|
||||
"hwlab-agent-skills"
|
||||
]);
|
||||
|
||||
export const TABLES = Object.freeze([
|
||||
"projects",
|
||||
"gateway_sessions",
|
||||
"box_resources",
|
||||
"box_capabilities",
|
||||
"wiring_configs",
|
||||
"patch_panel_status",
|
||||
"hardware_operations",
|
||||
"audit_events",
|
||||
"agent_sessions",
|
||||
"worker_sessions",
|
||||
"agent_trace_events",
|
||||
"evidence_records"
|
||||
]);
|
||||
|
||||
export const ERROR_CODES = Object.freeze({
|
||||
parseError: -32700,
|
||||
invalidRequest: -32600,
|
||||
methodNotFound: -32601,
|
||||
invalidParams: -32602,
|
||||
internalError: -32603,
|
||||
hwlabUnknown: -32000,
|
||||
projectNotFound: -32010,
|
||||
sessionNotFound: -32011,
|
||||
capabilityUnavailable: -32020,
|
||||
resourceLocked: -32021,
|
||||
wiringInvalid: -32022,
|
||||
operationRejected: -32023,
|
||||
evidenceMissing: -32030,
|
||||
auditRequired: -32040
|
||||
});
|
||||
|
||||
const methodPattern = /^[a-z][a-z0-9]*(\.[a-z][a-z0-9_]*)+$/;
|
||||
|
||||
export function isFrozenServiceId(serviceId) {
|
||||
return SERVICE_IDS.includes(serviceId);
|
||||
}
|
||||
|
||||
export function validateMeta(meta) {
|
||||
if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
|
||||
throw new Error("meta must be an object");
|
||||
}
|
||||
if (!meta.traceId) {
|
||||
throw new Error("meta.traceId is required");
|
||||
}
|
||||
if (!isFrozenServiceId(meta.serviceId)) {
|
||||
throw new Error(`unknown serviceId ${JSON.stringify(meta.serviceId)}`);
|
||||
}
|
||||
if (meta.environment !== ENVIRONMENT_DEV) {
|
||||
throw new Error(`unsupported environment ${JSON.stringify(meta.environment)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function validateRequest(envelope) {
|
||||
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
||||
throw new Error("request must be an object");
|
||||
}
|
||||
if (envelope.jsonrpc !== JSON_RPC_VERSION) {
|
||||
throw new Error(`jsonrpc must be ${JSON_RPC_VERSION}`);
|
||||
}
|
||||
if (envelope.id === undefined || envelope.id === null) {
|
||||
throw new Error("id is required");
|
||||
}
|
||||
if (!methodPattern.test(envelope.method)) {
|
||||
throw new Error(`invalid method ${JSON.stringify(envelope.method)}`);
|
||||
}
|
||||
if (
|
||||
envelope.params !== undefined &&
|
||||
(!envelope.params || typeof envelope.params !== "object" || Array.isArray(envelope.params))
|
||||
) {
|
||||
throw new Error("params must be an object when present");
|
||||
}
|
||||
validateMeta(envelope.meta);
|
||||
}
|
||||
|
||||
export function validateResponse(envelope) {
|
||||
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
||||
throw new Error("response must be an object");
|
||||
}
|
||||
if (envelope.jsonrpc !== JSON_RPC_VERSION) {
|
||||
throw new Error(`jsonrpc must be ${JSON_RPC_VERSION}`);
|
||||
}
|
||||
if (envelope.id === undefined || envelope.id === null) {
|
||||
throw new Error("id is required");
|
||||
}
|
||||
const hasResult = Object.hasOwn(envelope, "result");
|
||||
const hasError = Object.hasOwn(envelope, "error");
|
||||
if (hasResult === hasError) {
|
||||
throw new Error("response must contain exactly one of result or error");
|
||||
}
|
||||
if (hasError) {
|
||||
validateRpcError(envelope.error);
|
||||
}
|
||||
validateMeta(envelope.meta);
|
||||
}
|
||||
|
||||
export function validateRpcError(error) {
|
||||
if (!error || typeof error !== "object" || Array.isArray(error)) {
|
||||
throw new Error("error must be an object");
|
||||
}
|
||||
if (!Number.isInteger(error.code)) {
|
||||
throw new Error("error.code must be an integer");
|
||||
}
|
||||
if (!error.message || typeof error.message !== "string") {
|
||||
throw new Error("error.message is required");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user