feat: make node definitions type safe with zod

This commit is contained in:
2024-04-22 00:33:04 +02:00
parent 4c7c4cac2c
commit ad197db873
28 changed files with 221 additions and 147 deletions

View File

@ -1,4 +1,5 @@
import type { NodeInput } from "./inputs";
import { z } from "zod";
import { NodeInputSchema } from "./inputs";
export type { NodeInput } from "./inputs";
export type Node = {
@ -12,7 +13,7 @@ export type Node = {
parents?: Node[],
children?: Node[],
inputNodes?: Record<string, Node>
type?: NodeType;
type?: NodeDefinition;
downX?: number;
downY?: number;
x?: number;
@ -28,15 +29,19 @@ export type Node = {
position: [x: number, y: number]
}
export type NodeType = {
id: string;
inputs?: Record<string, NodeInput>
outputs?: string[];
meta?: {
title?: string;
},
execute?: (args: Int32Array) => Int32Array;
}
export const NodeDefinitionSchema = z.object({
id: z.string(),
inputs: z.record(NodeInputSchema).optional(),
outputs: z.array(z.string()).optional(),
meta: z.object({
description: z.string().optional(),
title: z.string().optional(),
}).optional(),
});
export type NodeDefinition = z.infer<typeof NodeDefinitionSchema> & {
execute(input: Int32Array): Int32Array;
};
export type Socket = {
node: Node;
@ -63,12 +68,12 @@ export interface NodeRegistry {
* @param id - The id of the node to get
* @returns The node with the given id, or undefined if no such node exists
*/
getNode: (id: string) => NodeType | undefined;
getNode: (id: string) => NodeDefinition | undefined;
/**
* Get all nodes
* @returns An array of all nodes
*/
getAllNodes: () => NodeType[];
getAllNodes: () => NodeDefinition[];
}
export interface RuntimeExecutor {

View File

@ -1,65 +1,78 @@
type NodeInputFloat = {
type: "float";
element?: "slider";
value?: number;
min?: number;
max?: number;
step?: number;
}
import { z } from "zod";
type NodeInputInteger = {
type: "integer";
element?: "slider";
value?: number;
min?: number;
max?: number;
}
type NodeInputBoolean = {
type: "boolean";
value?: boolean;
}
type NodeInputSelect = {
type: "select";
options: string[];
value?: number;
}
type NodeInputSeed = {
type: "seed"
value?: number;
}
type NodeInputVec3 = {
type: "vec3";
value?: number[];
}
type NodeInputModel = {
type: "model";
}
type NodeInputPlant = {
type: "plant"
}
type InputTypes = (NodeInputSeed | NodeInputBoolean | NodeInputFloat | NodeInputInteger | NodeInputSelect | NodeInputSeed | NodeInputVec3 | NodeInputModel | NodeInputPlant);
type InputId = InputTypes["type"];
type DefaultOptions = {
internal?: boolean;
external?: boolean;
setting?: string;
label?: string | false;
}
export type NodeInput = InputTypes & {
type: InputId | InputId[];
} & DefaultOptions;
const DefaultOptionsSchema = z.object({
internal: z.boolean().optional(),
external: z.boolean().optional(),
setting: z.string().optional(),
label: z.string().optional(),
description: z.string().optional(),
accepts: z.array(z.string()).optional(),
});
export type NodeInputType<T extends Record<string, NodeInput>> = {
[K in keyof T]: T[K]["value"]
};
export const NodeInputFloatSchema = z.object({
...DefaultOptionsSchema.shape,
type: z.literal("float"),
element: z.literal("slider").optional(),
value: z.number().optional(),
min: z.number().optional(),
max: z.number().optional(),
step: z.number().optional(),
});
export const NodeInputIntegerSchema = z.object({
...DefaultOptionsSchema.shape,
type: z.literal("integer"),
element: z.literal("slider").optional(),
value: z.number().optional(),
min: z.number().optional(),
max: z.number().optional(),
});
export const NodeInputBooleanSchema = z.object({
...DefaultOptionsSchema.shape,
type: z.literal("boolean"),
vale: z.boolean().optional(),
});
export const NodeInputSelectSchema = z.object({
...DefaultOptionsSchema.shape,
type: z.literal("select"),
value: z.number().optional(),
});
export const NodeInputSeedSchema = z.object({
...DefaultOptionsSchema.shape,
type: z.literal("seed"),
value: z.number().optional(),
});
export const NodeInputVec3Schema = z.object({
...DefaultOptionsSchema.shape,
type: z.literal("vec3"),
value: z.array(z.number()).optional(),
});
export const NodeInputModelSchema = z.object({
...DefaultOptionsSchema.shape,
type: z.literal("model"),
});
export const NodeInputPlantSchema = z.object({
...DefaultOptionsSchema.shape,
type: z.literal("plant"),
});
export const NodeInputSchema = z.union([
NodeInputSeedSchema,
NodeInputBooleanSchema,
NodeInputFloatSchema,
NodeInputIntegerSchema,
NodeInputSelectSchema,
NodeInputSeedSchema,
NodeInputVec3Schema,
NodeInputModelSchema,
NodeInputPlantSchema
]);
export type NodeInput = z.infer<typeof InputSchema>;

View File

@ -90,7 +90,7 @@ pub struct DefaultOptions {
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum NodeTypeOrArray {
pub enum NodeDefinitionOrArray {
Single(InputTypes),
Multiple(Vec<String>),
}
@ -124,10 +124,10 @@ impl<'de> Deserialize<'de> for NodeInput {
}
#[derive(Deserialize, Debug, Serialize)]
pub struct NodeType {
pub struct NodeDefinition {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub inputs: Option<HashMap<String, NodeInput>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub outputs: Option<Vec<String>>,
}