Files
lda-wf/web/apps/console/src/workspace/domain/authoring-contract-models.ts
T

240 lines
8.1 KiB
TypeScript

import * as v from "valibot";
export type AuthoringPathOrigin =
| "workflow_input"
| "workflow_state"
| "runtime_context"
| "step_input"
| "step_output"
| "workflow_output";
export type AuthoringPathAvailability = "available" | "conditional";
export type AuthoringPathUse =
| "step_input"
| "step_output_source"
| "state_target"
| "workflow_output";
export type AuthoringPathOption = {
readonly path: string;
readonly label: string;
readonly origin: AuthoringPathOrigin;
readonly schema: Readonly<Record<string, unknown>>;
readonly required: boolean;
readonly availability: AuthoringPathAvailability;
readonly uses: ReadonlyArray<AuthoringPathUse>;
readonly description?: string | undefined;
readonly reason?: string | undefined;
};
export type AuthoringStepContract = {
readonly stepId: string;
readonly label: string;
readonly description?: string | undefined;
readonly inputTargets?: ReadonlyArray<AuthoringPathOption> | undefined;
readonly outputSources?: ReadonlyArray<AuthoringPathOption> | undefined;
readonly outcomes?: ReadonlyArray<string> | undefined;
};
export type AuthoringContractInventory = {
readonly workspaceId: string;
readonly revision: number;
readonly selectedStepId: string | null;
readonly readableSources: ReadonlyArray<AuthoringPathOption>;
readonly stepInputTargets: ReadonlyArray<AuthoringPathOption>;
readonly stepOutputSources: ReadonlyArray<AuthoringPathOption>;
readonly stateTargets: ReadonlyArray<AuthoringPathOption>;
readonly workflowOutputTargets: ReadonlyArray<AuthoringPathOption>;
readonly entrySteps: ReadonlyArray<AuthoringStepContract>;
readonly workflowOutcomes: ReadonlyArray<string>;
readonly warnings: ReadonlyArray<string>;
};
const JsonObjectSchema = v.record(v.string(), v.unknown());
const AuthoringPathOriginSchema = v.union([
v.literal("workflow_input"),
v.literal("workflow_state"),
v.literal("runtime_context"),
v.literal("step_input"),
v.literal("step_output"),
v.literal("workflow_output"),
]);
const AuthoringPathAvailabilitySchema = v.union([
v.literal("available"),
v.literal("conditional"),
]);
const AuthoringPathUseSchema = v.union([
v.literal("step_input"),
v.literal("step_output_source"),
v.literal("state_target"),
v.literal("workflow_output"),
]);
const AuthoringPathOptionWireSchema = v.object({
path: v.string(),
label: v.string(),
origin: AuthoringPathOriginSchema,
schema: JsonObjectSchema,
required: v.boolean(),
availability: AuthoringPathAvailabilitySchema,
uses: v.array(AuthoringPathUseSchema),
description: v.optional(v.string()),
reason: v.optional(v.string()),
});
const AuthoringStepContractWireSchema = v.object({
step_id: v.string(),
label: v.string(),
description: v.optional(v.string()),
input_targets: v.optional(v.array(AuthoringPathOptionWireSchema)),
output_sources: v.optional(v.array(AuthoringPathOptionWireSchema)),
outcomes: v.optional(v.array(v.string())),
});
const AuthoringContractInventoryWireSchema = v.object({
workspace_id: v.string(),
revision: v.number(),
selected_step_id: v.nullable(v.string()),
readable_sources: v.array(AuthoringPathOptionWireSchema),
step_input_targets: v.array(AuthoringPathOptionWireSchema),
step_output_sources: v.array(AuthoringPathOptionWireSchema),
state_targets: v.array(AuthoringPathOptionWireSchema),
workflow_output_targets: v.array(AuthoringPathOptionWireSchema),
entry_steps: v.array(AuthoringStepContractWireSchema),
workflow_outcomes: v.array(v.string()),
warnings: v.array(v.string()),
});
const AuthoringPathOptionBrowserSchema = v.object({
path: v.string(),
label: v.string(),
origin: AuthoringPathOriginSchema,
schema: JsonObjectSchema,
required: v.boolean(),
availability: AuthoringPathAvailabilitySchema,
uses: v.array(AuthoringPathUseSchema),
description: v.optional(v.string()),
reason: v.optional(v.string()),
});
const AuthoringStepContractBrowserSchema = v.object({
stepId: v.string(),
label: v.string(),
description: v.optional(v.string()),
inputTargets: v.optional(v.array(AuthoringPathOptionBrowserSchema)),
outputSources: v.optional(v.array(AuthoringPathOptionBrowserSchema)),
outcomes: v.optional(v.array(v.string())),
});
const AuthoringContractInventoryBrowserSchema = v.object({
workspaceId: v.string(),
revision: v.number(),
selectedStepId: v.nullable(v.string()),
readableSources: v.array(AuthoringPathOptionBrowserSchema),
stepInputTargets: v.array(AuthoringPathOptionBrowserSchema),
stepOutputSources: v.array(AuthoringPathOptionBrowserSchema),
stateTargets: v.array(AuthoringPathOptionBrowserSchema),
workflowOutputTargets: v.array(AuthoringPathOptionBrowserSchema),
entrySteps: v.array(AuthoringStepContractBrowserSchema),
workflowOutcomes: v.array(v.string()),
warnings: v.array(v.string()),
});
type AuthoringPathOptionWire = v.InferOutput<typeof AuthoringPathOptionWireSchema>;
type AuthoringStepContractWire = v.InferOutput<typeof AuthoringStepContractWireSchema>;
type AuthoringContractInventoryWire = v.InferOutput<
typeof AuthoringContractInventoryWireSchema
>;
type AuthoringContractInventoryBrowser = v.InferOutput<
typeof AuthoringContractInventoryBrowserSchema
>;
const decode = <T>(
label: string,
schema: v.GenericSchema<unknown, T>,
value: unknown,
): T => {
const result = v.safeParse(schema, value);
if (result.success) return result.output;
throw new Error(
`${label} is malformed: ${result.issues[0]?.message ?? "unknown issue"}`,
);
};
const mapPathOption = (option: AuthoringPathOptionWire): AuthoringPathOption => ({
path: option.path,
label: option.label,
origin: option.origin,
schema: option.schema,
required: option.required,
availability: option.availability,
uses: option.uses,
...(option.description === undefined ? {} : { description: option.description }),
...(option.reason === undefined ? {} : { reason: option.reason }),
});
const mapStepContract = (step: AuthoringStepContractWire): AuthoringStepContract => ({
stepId: step.step_id,
label: step.label,
...(step.description === undefined ? {} : { description: step.description }),
...(step.input_targets === undefined
? {}
: { inputTargets: step.input_targets.map(mapPathOption) }),
...(step.output_sources === undefined
? {}
: { outputSources: step.output_sources.map(mapPathOption) }),
...(step.outcomes === undefined ? {} : { outcomes: step.outcomes }),
});
const mapInventory = (
inventory: AuthoringContractInventoryWire,
): AuthoringContractInventory => ({
workspaceId: inventory.workspace_id,
revision: inventory.revision,
selectedStepId: inventory.selected_step_id,
readableSources: inventory.readable_sources.map(mapPathOption),
stepInputTargets: inventory.step_input_targets.map(mapPathOption),
stepOutputSources: inventory.step_output_sources.map(mapPathOption),
stateTargets: inventory.state_targets.map(mapPathOption),
workflowOutputTargets: inventory.workflow_output_targets.map(mapPathOption),
entrySteps: inventory.entry_steps.map(mapStepContract),
workflowOutcomes: inventory.workflow_outcomes,
warnings: inventory.warnings,
});
const mapBrowserInventory = (
inventory: AuthoringContractInventoryBrowser,
): AuthoringContractInventory => ({
workspaceId: inventory.workspaceId,
revision: inventory.revision,
selectedStepId: inventory.selectedStepId,
readableSources: inventory.readableSources,
stepInputTargets: inventory.stepInputTargets,
stepOutputSources: inventory.stepOutputSources,
stateTargets: inventory.stateTargets,
workflowOutputTargets: inventory.workflowOutputTargets,
entrySteps: inventory.entrySteps,
workflowOutcomes: inventory.workflowOutcomes,
warnings: inventory.warnings,
});
const AuthoringContractResponseSchema = v.union([
AuthoringContractInventoryWireSchema,
AuthoringContractInventoryBrowserSchema,
]);
export const decodeAuthoringContractInventory = (
value: unknown,
): AuthoringContractInventory => {
const decoded = decode(
"AuthoringContractInventory",
AuthoringContractResponseSchema,
value,
);
return "workspace_id" in decoded ? mapInventory(decoded) : mapBrowserInventory(decoded);
};