refactor: share console operation execution
This commit is contained in:
@@ -5,6 +5,9 @@ import {
|
||||
type AddCapabilityStepInput,
|
||||
type CreateEmptyDraftInput,
|
||||
type CreateFromCapabilityInput,
|
||||
type InputBinding,
|
||||
type InputPathBinding,
|
||||
type InputValueBinding,
|
||||
type SetDraftRouteInput,
|
||||
type UpdateCapabilityStepInput,
|
||||
} from "./draft-workspace-models.js";
|
||||
@@ -27,6 +30,18 @@ const canonicalWorkspace = {
|
||||
draft: { steps: [] },
|
||||
};
|
||||
|
||||
const pathBinding = {
|
||||
target: "text",
|
||||
path: "input.text",
|
||||
} satisfies InputPathBinding;
|
||||
|
||||
const valueBinding = {
|
||||
target: "mode",
|
||||
value: { value: "full" },
|
||||
} satisfies InputValueBinding;
|
||||
|
||||
const canonicalBindings = [pathBinding, valueBinding] satisfies ReadonlyArray<InputBinding>;
|
||||
|
||||
const createExecutor = () => {
|
||||
const run = vi.fn();
|
||||
const executor: ConsoleWriteExecutor = {
|
||||
@@ -79,7 +94,7 @@ describe("DraftAuthoringClient", () => {
|
||||
routeFromOutcome: " success ",
|
||||
routes: { success: "enrich" },
|
||||
inputMap: { text: "input.text" },
|
||||
inputBindings: [{ target: "text", path: "input.text" }],
|
||||
inputBindings: canonicalBindings,
|
||||
bindOutputs: { result: "state.result" },
|
||||
description: "Enrich report",
|
||||
retry: 2,
|
||||
@@ -91,7 +106,7 @@ describe("DraftAuthoringClient", () => {
|
||||
stepId: " enrich ",
|
||||
update: {
|
||||
description: "Updated",
|
||||
input: [{ target: "text", value: "updated" }],
|
||||
input: [valueBinding],
|
||||
retry: null,
|
||||
timeoutSeconds: 45,
|
||||
},
|
||||
@@ -162,7 +177,7 @@ describe("DraftAuthoringClient", () => {
|
||||
route_from_outcome: "success",
|
||||
routes: { success: "enrich" },
|
||||
input_map: { text: "input.text" },
|
||||
input_bindings: [{ target: "text", path: "input.text" }],
|
||||
input_bindings: canonicalBindings,
|
||||
bind_outputs: { result: "state.result" },
|
||||
desc: "Enrich report",
|
||||
retry: 2,
|
||||
@@ -179,7 +194,7 @@ describe("DraftAuthoringClient", () => {
|
||||
step_id: "enrich",
|
||||
update: {
|
||||
desc: "Updated",
|
||||
input: [{ target: "text", value: "updated" }],
|
||||
input: [valueBinding],
|
||||
retry: null,
|
||||
timeout_seconds: 45,
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
type CreateEmptyDraftInput,
|
||||
type CreateFromCapabilityInput,
|
||||
type DraftWorkspace,
|
||||
type InputBinding,
|
||||
type SetDraftRouteInput,
|
||||
type UpdateCapabilityStepInput,
|
||||
} from "./draft-workspace-models.js";
|
||||
@@ -50,6 +51,11 @@ const ifDefined = <T>(
|
||||
if (value !== undefined) target[key] = value;
|
||||
};
|
||||
|
||||
const copyBindings = (
|
||||
bindings: ReadonlyArray<InputBinding> | null | undefined,
|
||||
): InputBinding[] | null | undefined =>
|
||||
bindings === undefined || bindings === null ? bindings : [...bindings];
|
||||
|
||||
export const createDraftAuthoringClient = (
|
||||
executor: ConsoleWriteExecutor,
|
||||
): DraftAuthoringClient => ({
|
||||
@@ -114,7 +120,7 @@ export const createDraftAuthoringClient = (
|
||||
);
|
||||
ifDefined(params, "routes", input.routes);
|
||||
ifDefined(params, "input_map", input.inputMap);
|
||||
ifDefined(params, "input_bindings", input.inputBindings);
|
||||
ifDefined(params, "input_bindings", copyBindings(input.inputBindings));
|
||||
ifDefined(params, "bind_outputs", input.bindOutputs);
|
||||
ifDefined(params, "desc", input.description);
|
||||
ifDefined(params, "retry", input.retry);
|
||||
@@ -126,7 +132,7 @@ export const createDraftAuthoringClient = (
|
||||
const operation = "workflow.draft_workspaces.update_capability_step";
|
||||
const update: Record<string, unknown> = {};
|
||||
ifDefined(update, "desc", input.update.description);
|
||||
ifDefined(update, "input", input.update.input);
|
||||
ifDefined(update, "input", copyBindings(input.update.input));
|
||||
ifDefined(update, "retry", input.update.retry);
|
||||
ifDefined(update, "timeout_seconds", input.update.timeoutSeconds);
|
||||
return executor.run(
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
decodeDraftWorkspacePage,
|
||||
type AddCapabilityStepInput,
|
||||
type CreateEmptyDraftInput,
|
||||
type InputBinding,
|
||||
} from "./draft-workspace-models.js";
|
||||
|
||||
const summary = {
|
||||
@@ -15,6 +16,25 @@ const summary = {
|
||||
};
|
||||
|
||||
describe("draft workspace models", () => {
|
||||
it("requires canonical path or value binding shapes", () => {
|
||||
const canonicalBinding = {
|
||||
target: "text",
|
||||
path: "input.text",
|
||||
} satisfies InputBinding;
|
||||
|
||||
const invalidInput: AddCapabilityStepInput = {
|
||||
workspaceId: "draft-report",
|
||||
revision: 1,
|
||||
stepId: "read",
|
||||
capabilityName: "demo.read",
|
||||
// @ts-expect-error Binding payloads must use canonical path/value keys.
|
||||
inputBindings: [{ sourcePath: "input.text", targetPath: "text" }],
|
||||
};
|
||||
|
||||
expect(canonicalBinding).toEqual({ target: "text", path: "input.text" });
|
||||
expect(invalidInput).toBeDefined();
|
||||
});
|
||||
|
||||
it("exposes camelCase inputs for draft authoring", () => {
|
||||
const emptyInput = {
|
||||
workspaceId: "draft-report",
|
||||
|
||||
@@ -2,6 +2,32 @@ import * as v from "valibot";
|
||||
|
||||
export type JsonObject = Record<string, unknown>;
|
||||
|
||||
export type InputPath =
|
||||
| string
|
||||
| {
|
||||
readonly parts: string[];
|
||||
readonly root: "input" | "state" | "context";
|
||||
};
|
||||
|
||||
export type LocalInputPath =
|
||||
| string
|
||||
| {
|
||||
readonly parts: string[];
|
||||
readonly root: "local";
|
||||
};
|
||||
|
||||
export type InputPathBinding = {
|
||||
readonly path: InputPath;
|
||||
readonly target: LocalInputPath;
|
||||
};
|
||||
|
||||
export type InputValueBinding = {
|
||||
readonly target: LocalInputPath;
|
||||
readonly value: JsonObject;
|
||||
};
|
||||
|
||||
export type InputBinding = InputPathBinding | InputValueBinding;
|
||||
|
||||
export type CreateEmptyDraftInput = {
|
||||
readonly workspaceId: string;
|
||||
readonly name: string;
|
||||
@@ -36,7 +62,7 @@ export type AddCapabilityStepInput = {
|
||||
readonly routeFromOutcome?: string;
|
||||
readonly routes?: Record<string, string> | null;
|
||||
readonly inputMap?: Record<string, string> | null;
|
||||
readonly inputBindings?: ReadonlyArray<unknown> | null;
|
||||
readonly inputBindings?: ReadonlyArray<InputBinding> | null;
|
||||
readonly bindOutputs?: Record<string, string>;
|
||||
readonly description?: string | null;
|
||||
readonly retry?: number | null;
|
||||
@@ -49,7 +75,7 @@ export type UpdateCapabilityStepInput = {
|
||||
readonly stepId: string;
|
||||
readonly update: {
|
||||
readonly description?: string | null;
|
||||
readonly input?: ReadonlyArray<unknown> | null;
|
||||
readonly input?: ReadonlyArray<InputBinding> | null;
|
||||
readonly retry?: number | null;
|
||||
readonly timeoutSeconds?: number | null;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
import { callOperation, ConsoleApiError } from "../../connection/api.js";
|
||||
import type {
|
||||
OperationName,
|
||||
RpcResponse,
|
||||
} from "../../connection/contracts.js";
|
||||
import type { EvidenceRecord } from "../../app/state.js";
|
||||
import { ConsoleClientError, type ConsoleClientErrorKind } from "./errors.js";
|
||||
|
||||
export interface ConsoleExecutor {
|
||||
run<T>(
|
||||
operation: OperationName,
|
||||
params: unknown,
|
||||
decode: (value: unknown) => T,
|
||||
): Promise<T>;
|
||||
}
|
||||
|
||||
export type ConsoleExecutorOptions = {
|
||||
readonly target: string;
|
||||
readonly recordEvidence: (record: EvidenceRecord) => void;
|
||||
readonly allocateEvidenceId?: (operation: string) => string;
|
||||
readonly shouldRecordEvidence?: () => boolean;
|
||||
readonly invoke?: (
|
||||
operation: OperationName,
|
||||
target: string,
|
||||
params: unknown,
|
||||
) => Promise<RpcResponse>;
|
||||
};
|
||||
|
||||
const errorKindForCode = (code: string): ConsoleClientErrorKind => {
|
||||
switch (code) {
|
||||
case "invalid_target":
|
||||
case "upstream_unreachable":
|
||||
return "connection";
|
||||
case "unknown_operation":
|
||||
return "operation";
|
||||
case "rpc_decode_error":
|
||||
return "decode";
|
||||
default:
|
||||
return "operation";
|
||||
}
|
||||
};
|
||||
|
||||
const errorMessage = (error: unknown): string =>
|
||||
error instanceof Error ? error.message : String(error);
|
||||
|
||||
const clientErrorKindForInvocation = (
|
||||
error: unknown,
|
||||
): "decode" | "transport" =>
|
||||
error instanceof ConsoleApiError && error.kind !== "transport"
|
||||
? "decode"
|
||||
: "transport";
|
||||
|
||||
/** Keeps read and write executors on one evidence/error protocol. */
|
||||
export const createConsoleExecutor = (
|
||||
options: ConsoleExecutorOptions,
|
||||
): ConsoleExecutor => {
|
||||
let evidenceSequence = 0;
|
||||
const invoke = options.invoke ?? callOperation;
|
||||
const allocateEvidenceId =
|
||||
options.allocateEvidenceId ??
|
||||
((operation: string): string => `${operation}-${evidenceSequence++}`);
|
||||
|
||||
const record = (
|
||||
operation: OperationName,
|
||||
label: string,
|
||||
equivalentCli: string,
|
||||
request: unknown,
|
||||
response: unknown,
|
||||
durationMs: number,
|
||||
): void => {
|
||||
if (options.shouldRecordEvidence?.() === false) return;
|
||||
options.recordEvidence({
|
||||
id: allocateEvidenceId(operation),
|
||||
operation,
|
||||
label,
|
||||
equivalentCli,
|
||||
request,
|
||||
response,
|
||||
durationMs,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
async run<T>(
|
||||
operation: OperationName,
|
||||
params: unknown,
|
||||
decode: (value: unknown) => T,
|
||||
): Promise<T> {
|
||||
const startedAt = performance.now();
|
||||
const durationSinceStart = (): number =>
|
||||
Math.max(0, Math.round(performance.now() - startedAt));
|
||||
|
||||
let response: RpcResponse;
|
||||
try {
|
||||
response = await invoke(operation, options.target, params);
|
||||
} catch (error) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: operation failed before CLI metadata",
|
||||
null,
|
||||
null,
|
||||
durationSinceStart(),
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
clientErrorKindForInvocation(error),
|
||||
operation,
|
||||
errorMessage(error),
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: operation failed before CLI metadata",
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
durationSinceStart(),
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
errorKindForCode(response.error.code),
|
||||
operation,
|
||||
response.error.message,
|
||||
);
|
||||
}
|
||||
|
||||
if (response.operation !== operation) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: response operation mismatch",
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
response.durationMs,
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
"operation",
|
||||
operation,
|
||||
`operation mismatch: requested ${operation}, received ${response.operation}`,
|
||||
);
|
||||
}
|
||||
|
||||
record(
|
||||
response.operation,
|
||||
response.label,
|
||||
response.equivalentCli,
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
response.durationMs,
|
||||
);
|
||||
|
||||
try {
|
||||
return decode(response.interpreted);
|
||||
} catch (error) {
|
||||
throw new ConsoleClientError(
|
||||
"decode",
|
||||
operation,
|
||||
errorMessage(error),
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,164 +1,11 @@
|
||||
import { callOperation, ConsoleApiError } from "../../connection/api.js";
|
||||
import type {
|
||||
OperationName,
|
||||
RpcResponse,
|
||||
} from "../../connection/contracts.js";
|
||||
import type { EvidenceRecord } from "../../app/state.js";
|
||||
import { ConsoleClientError, type ConsoleClientErrorKind } from "./errors.js";
|
||||
import {
|
||||
createConsoleExecutor,
|
||||
type ConsoleExecutor,
|
||||
type ConsoleExecutorOptions,
|
||||
} from "./executor-protocol.js";
|
||||
|
||||
export interface ConsoleReadExecutor {
|
||||
run<T>(
|
||||
operation: OperationName,
|
||||
params: unknown,
|
||||
decode: (value: unknown) => T,
|
||||
): Promise<T>;
|
||||
}
|
||||
export interface ConsoleReadExecutor extends ConsoleExecutor {}
|
||||
|
||||
type InvokeOperation = (
|
||||
operation: OperationName,
|
||||
target: string,
|
||||
params: unknown,
|
||||
) => Promise<RpcResponse>;
|
||||
|
||||
type EvidenceIdAllocator = (operation: string) => string;
|
||||
|
||||
const errorKindForCode = (code: string): ConsoleClientErrorKind => {
|
||||
switch (code) {
|
||||
case "invalid_target":
|
||||
case "upstream_unreachable":
|
||||
return "connection";
|
||||
case "unknown_operation":
|
||||
return "operation";
|
||||
case "rpc_decode_error":
|
||||
return "decode";
|
||||
default:
|
||||
return "operation";
|
||||
}
|
||||
};
|
||||
|
||||
const errorMessage = (error: unknown): string =>
|
||||
error instanceof Error ? error.message : String(error);
|
||||
|
||||
const clientErrorKindForInvocation = (
|
||||
error: unknown,
|
||||
): "decode" | "transport" =>
|
||||
error instanceof ConsoleApiError && error.kind !== "transport"
|
||||
? "decode"
|
||||
: "transport";
|
||||
|
||||
export const createConsoleReadExecutor = (options: {
|
||||
readonly target: string;
|
||||
readonly recordEvidence: (record: EvidenceRecord) => void;
|
||||
readonly allocateEvidenceId?: EvidenceIdAllocator;
|
||||
readonly shouldRecordEvidence?: () => boolean;
|
||||
readonly invoke?: InvokeOperation;
|
||||
}): ConsoleReadExecutor => {
|
||||
let evidenceSequence = 0;
|
||||
const invoke = options.invoke ?? callOperation;
|
||||
const allocateEvidenceId =
|
||||
options.allocateEvidenceId ??
|
||||
((operation: string): string => `${operation}-${evidenceSequence++}`);
|
||||
|
||||
const record = (
|
||||
operation: OperationName,
|
||||
label: string,
|
||||
equivalentCli: string,
|
||||
request: unknown,
|
||||
response: unknown,
|
||||
durationMs: number,
|
||||
): void => {
|
||||
if (options.shouldRecordEvidence?.() === false) return;
|
||||
options.recordEvidence({
|
||||
id: allocateEvidenceId(operation),
|
||||
operation,
|
||||
label,
|
||||
equivalentCli,
|
||||
request,
|
||||
response,
|
||||
durationMs,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
async run<T>(
|
||||
operation: OperationName,
|
||||
params: unknown,
|
||||
decode: (value: unknown) => T,
|
||||
): Promise<T> {
|
||||
const startedAt = performance.now();
|
||||
const durationSinceStart = (): number =>
|
||||
Math.max(0, Math.round(performance.now() - startedAt));
|
||||
let response: RpcResponse;
|
||||
try {
|
||||
response = await invoke(operation, options.target, params);
|
||||
} catch (error) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: operation failed before CLI metadata",
|
||||
null,
|
||||
null,
|
||||
durationSinceStart(),
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
clientErrorKindForInvocation(error),
|
||||
operation,
|
||||
errorMessage(error),
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: operation failed before CLI metadata",
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
durationSinceStart(),
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
errorKindForCode(response.error.code),
|
||||
operation,
|
||||
response.error.message,
|
||||
);
|
||||
}
|
||||
|
||||
if (response.operation !== operation) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: response operation mismatch",
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
response.durationMs,
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
"operation",
|
||||
operation,
|
||||
`operation mismatch: requested ${operation}, received ${response.operation}`,
|
||||
);
|
||||
}
|
||||
|
||||
record(
|
||||
response.operation,
|
||||
response.label,
|
||||
response.equivalentCli,
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
response.durationMs,
|
||||
);
|
||||
|
||||
try {
|
||||
return decode(response.interpreted);
|
||||
} catch (error) {
|
||||
throw new ConsoleClientError(
|
||||
"decode",
|
||||
operation,
|
||||
errorMessage(error),
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
export const createConsoleReadExecutor = (
|
||||
options: ConsoleExecutorOptions,
|
||||
): ConsoleReadExecutor => createConsoleExecutor(options);
|
||||
|
||||
@@ -1,165 +1,11 @@
|
||||
import { callOperation, ConsoleApiError } from "../../connection/api.js";
|
||||
import type {
|
||||
OperationName,
|
||||
RpcResponse,
|
||||
} from "../../connection/contracts.js";
|
||||
import type { EvidenceRecord } from "../../app/state.js";
|
||||
import { ConsoleClientError, type ConsoleClientErrorKind } from "./errors.js";
|
||||
import {
|
||||
createConsoleExecutor,
|
||||
type ConsoleExecutor,
|
||||
type ConsoleExecutorOptions,
|
||||
} from "./executor-protocol.js";
|
||||
|
||||
export interface ConsoleWriteExecutor {
|
||||
run<T>(
|
||||
operation: OperationName,
|
||||
params: unknown,
|
||||
decode: (value: unknown) => T,
|
||||
): Promise<T>;
|
||||
}
|
||||
export interface ConsoleWriteExecutor extends ConsoleExecutor {}
|
||||
|
||||
type InvokeOperation = (
|
||||
operation: OperationName,
|
||||
target: string,
|
||||
params: unknown,
|
||||
) => Promise<RpcResponse>;
|
||||
|
||||
type EvidenceIdAllocator = (operation: string) => string;
|
||||
|
||||
const errorKindForCode = (code: string): ConsoleClientErrorKind => {
|
||||
switch (code) {
|
||||
case "invalid_target":
|
||||
case "upstream_unreachable":
|
||||
return "connection";
|
||||
case "unknown_operation":
|
||||
return "operation";
|
||||
case "rpc_decode_error":
|
||||
return "decode";
|
||||
default:
|
||||
return "operation";
|
||||
}
|
||||
};
|
||||
|
||||
const errorMessage = (error: unknown): string =>
|
||||
error instanceof Error ? error.message : String(error);
|
||||
|
||||
const clientErrorKindForInvocation = (
|
||||
error: unknown,
|
||||
): "decode" | "transport" =>
|
||||
error instanceof ConsoleApiError && error.kind !== "transport"
|
||||
? "decode"
|
||||
: "transport";
|
||||
|
||||
export const createConsoleWriteExecutor = (options: {
|
||||
readonly target: string;
|
||||
readonly recordEvidence: (record: EvidenceRecord) => void;
|
||||
readonly allocateEvidenceId?: EvidenceIdAllocator;
|
||||
readonly shouldRecordEvidence?: () => boolean;
|
||||
readonly invoke?: InvokeOperation;
|
||||
}): ConsoleWriteExecutor => {
|
||||
let evidenceSequence = 0;
|
||||
const invoke = options.invoke ?? callOperation;
|
||||
const allocateEvidenceId =
|
||||
options.allocateEvidenceId ??
|
||||
((operation: string): string => `${operation}-${evidenceSequence++}`);
|
||||
|
||||
const record = (
|
||||
operation: OperationName,
|
||||
label: string,
|
||||
equivalentCli: string,
|
||||
request: unknown,
|
||||
response: unknown,
|
||||
durationMs: number,
|
||||
): void => {
|
||||
if (options.shouldRecordEvidence?.() === false) return;
|
||||
options.recordEvidence({
|
||||
id: allocateEvidenceId(operation),
|
||||
operation,
|
||||
label,
|
||||
equivalentCli,
|
||||
request,
|
||||
response,
|
||||
durationMs,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
async run<T>(
|
||||
operation: OperationName,
|
||||
params: unknown,
|
||||
decode: (value: unknown) => T,
|
||||
): Promise<T> {
|
||||
const startedAt = performance.now();
|
||||
const durationSinceStart = (): number =>
|
||||
Math.max(0, Math.round(performance.now() - startedAt));
|
||||
|
||||
let response: RpcResponse;
|
||||
try {
|
||||
response = await invoke(operation, options.target, params);
|
||||
} catch (error) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: operation failed before CLI metadata",
|
||||
null,
|
||||
null,
|
||||
durationSinceStart(),
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
clientErrorKindForInvocation(error),
|
||||
operation,
|
||||
errorMessage(error),
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: operation failed before CLI metadata",
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
durationSinceStart(),
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
errorKindForCode(response.error.code),
|
||||
operation,
|
||||
response.error.message,
|
||||
);
|
||||
}
|
||||
|
||||
if (response.operation !== operation) {
|
||||
record(
|
||||
operation,
|
||||
`${operation} failed`,
|
||||
"unavailable: response operation mismatch",
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
response.durationMs,
|
||||
);
|
||||
throw new ConsoleClientError(
|
||||
"operation",
|
||||
operation,
|
||||
`operation mismatch: requested ${operation}, received ${response.operation}`,
|
||||
);
|
||||
}
|
||||
|
||||
record(
|
||||
response.operation,
|
||||
response.label,
|
||||
response.equivalentCli,
|
||||
response.exchange.request,
|
||||
response.exchange.response,
|
||||
response.durationMs,
|
||||
);
|
||||
|
||||
try {
|
||||
return decode(response.interpreted);
|
||||
} catch (error) {
|
||||
throw new ConsoleClientError(
|
||||
"decode",
|
||||
operation,
|
||||
errorMessage(error),
|
||||
{ cause: error },
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
export const createConsoleWriteExecutor = (
|
||||
options: ConsoleExecutorOptions,
|
||||
): ConsoleWriteExecutor => createConsoleExecutor(options);
|
||||
|
||||
Reference in New Issue
Block a user