diff --git a/packages/jdm-editor/package.json b/packages/jdm-editor/package.json index e0e552a4..ec658af7 100644 --- a/packages/jdm-editor/package.json +++ b/packages/jdm-editor/package.json @@ -54,6 +54,7 @@ "reactflow": "11.11.3", "ts-pattern": "^5.1.2", "use-debounce": "^10.0.0", + "zod": "^3.23.8", "zustand": "^4.5.2" }, "devDependencies": { diff --git a/packages/jdm-editor/src/components/decision-graph/dg.stories-values.ts b/packages/jdm-editor/src/components/decision-graph/dg.stories-values.ts index 247e4cf8..88558fb0 100644 --- a/packages/jdm-editor/src/components/decision-graph/dg.stories-values.ts +++ b/packages/jdm-editor/src/components/decision-graph/dg.stories-values.ts @@ -31,13 +31,11 @@ export const defaultGraph = { inputs: [ { id: 'HVo_JpALi8', - type: 'expression', field: 'cart.weight', name: 'Cart Weight (Kg)', }, { id: 'HW6mSVfLbs', - type: 'expression', field: 'customer.country', name: 'Customer Country', }, @@ -47,7 +45,6 @@ export const defaultGraph = { field: 'shippingFee', id: '3EGDrV0ssV', name: 'Shipping Fee', - type: 'expression', }, ], rules: [ diff --git a/packages/jdm-editor/src/components/decision-graph/nodes/specifications/decision-table.specification.tsx b/packages/jdm-editor/src/components/decision-graph/nodes/specifications/decision-table.specification.tsx index 8b3b2eb9..7298e44a 100644 --- a/packages/jdm-editor/src/components/decision-graph/nodes/specifications/decision-table.specification.tsx +++ b/packages/jdm-editor/src/components/decision-graph/nodes/specifications/decision-table.specification.tsx @@ -40,7 +40,6 @@ export const decisionTableSpecification: NodeSpecification { { id: crypto.randomUUID(), name: 'Input', - type: 'expression', }, ]; } @@ -95,7 +93,6 @@ export const parseDecisionTable = (decisionTable?: DecisionTableType) => { id: crypto.randomUUID(), field: 'output', name: 'Output', - type: 'expression', }, ]; } diff --git a/packages/jdm-editor/src/components/decision-table/dialog/field-add-dialog.tsx b/packages/jdm-editor/src/components/decision-table/dialog/field-add-dialog.tsx index 6d439b43..b990193f 100644 --- a/packages/jdm-editor/src/components/decision-table/dialog/field-add-dialog.tsx +++ b/packages/jdm-editor/src/components/decision-table/dialog/field-add-dialog.tsx @@ -48,10 +48,9 @@ export const FieldAdd: React.FC = (props) => { form={form} layout='vertical' initialValues={{ type: 'expression' }} - onFinish={({ type, field, name, defaultValue }) => { + onFinish={({ field, name, defaultValue }) => { onSuccess?.({ id: crypto.randomUUID(), - type: type || 'expression', field: (field || '')?.trim?.()?.length > 0 ? field : undefined, name, defaultValue, diff --git a/packages/jdm-editor/src/components/decision-table/dt.stories.tsx b/packages/jdm-editor/src/components/decision-table/dt.stories.tsx index 75204d3d..ae8189a5 100644 --- a/packages/jdm-editor/src/components/decision-table/dt.stories.tsx +++ b/packages/jdm-editor/src/components/decision-table/dt.stories.tsx @@ -12,13 +12,11 @@ const shippingFeesDefault = { inputs: [ { id: 'HVo_JpALi8', - type: 'expression', field: 'cart.weight', name: 'Cart Weight (Kg)', }, { id: 'HW6mSVfLbs', - type: 'expression', field: 'customer.country', name: 'Customer Country', }, @@ -28,7 +26,6 @@ const shippingFeesDefault = { field: 'shippingFee', id: '3EGDrV0ssV', name: 'Shipping Fee', - type: 'expression', }, ], rules: [ diff --git a/packages/jdm-editor/src/helpers/excel-file-utils.ts b/packages/jdm-editor/src/helpers/excel-file-utils.ts index b3f32280..d57bee9e 100644 --- a/packages/jdm-editor/src/helpers/excel-file-utils.ts +++ b/packages/jdm-editor/src/helpers/excel-file-utils.ts @@ -178,7 +178,6 @@ const parseSpreadsheetData = (spreadSheetData: any) => { name: header.value, field: headerMeta?.name, _type: headerMeta?.type, - type: 'expression', id: headerMeta?.id, defaultValue: '', }; @@ -190,7 +189,6 @@ const parseSpreadsheetData = (spreadSheetData: any) => { id: column.id, name: column?.name, field: column?.field, - type: column?.type, defaultValue: column?.defaultValue, })) as TableSchemaItem[]; @@ -200,7 +198,6 @@ const parseSpreadsheetData = (spreadSheetData: any) => { id: column.id, name: column?.name, field: column?.field, - type: column?.type, defaultValue: column?.defaultValue, })) as TableSchemaItem[]; diff --git a/packages/jdm-editor/src/helpers/schema.ts b/packages/jdm-editor/src/helpers/schema.ts new file mode 100644 index 00000000..68b80b2b --- /dev/null +++ b/packages/jdm-editor/src/helpers/schema.ts @@ -0,0 +1,128 @@ +import { z } from 'zod'; + +const id = z.string().default(crypto.randomUUID); + +const nodeCommon = z.object({ + id, + name: z.string(), + position: z.object({ x: z.number(), y: z.number() }).default({ x: 0, y: 0 }), +}); + +export const inputNodeSchema = z + .object({ + type: z.literal('inputNode'), + }) + .merge(nodeCommon); + +export const outputNodeSchema = z + .object({ + type: z.literal('outputNode'), + }) + .merge(nodeCommon); + +export const decisionTableSchema = z + .object({ + type: z.literal('decisionTableNode'), + content: z.object({ + hitPolicy: z.enum(['first', 'collect']).default('first'), + rules: z.array(z.record(z.string(), z.string())).default([]), + inputs: z.array( + z.object({ + id, + name: z.string().nullish(), + field: z.string().nullish(), + defaultValue: z.string().nullish(), + }), + ), + outputs: z.array( + z.object({ + id, + name: z.string().nullish(), + field: z.string().nullish(), + defaultValue: z.string().nullish(), + }), + ), + }), + }) + .merge(nodeCommon); + +export const functionNodeSchema = z + .object({ + type: z.literal('functionNode'), + content: z.string().nullish(), + }) + .merge(nodeCommon); + +export const expressionNodeSchema = z + .object({ + type: z.literal('expressionNode'), + content: z.object({ + expressions: z.array( + z.object({ + id, + key: z.string().default(''), + value: z.string().default(''), + }), + ), + }), + }) + .merge(nodeCommon); + +export const decisionNodeSchema = z + .object({ + type: z.literal('decisionNode'), + content: z.object({ + key: z.string(), + }), + }) + .merge(nodeCommon); + +export const switchNodeSchema = z + .object({ + type: z.literal('switchNode'), + content: z.object({ + hitPolicy: z.enum(['first', 'collect']).default('first'), + statements: z.array( + z.object({ + id, + condition: z.string().default(''), + }), + ), + }), + }) + .merge(nodeCommon); + +export const customNodeSchema = z + .object({ + type: z.literal('customNode'), + content: z.object({ + kind: z.string(), + config: z.any(), + }), + }) + .merge(nodeCommon); + +export const nodeSchema = z.discriminatedUnion('type', [ + decisionNodeSchema, + expressionNodeSchema, + functionNodeSchema, + decisionTableSchema, + switchNodeSchema, + customNodeSchema, + inputNodeSchema, + outputNodeSchema, +]); + +export const edgeSchema = z.object({ + id: z.string(), + sourceId: z.string(), + targetId: z.string(), + sourceHandle: z.string().nullish(), + type: z.enum(['edge']), +}); + +export const decisionModelSchema = z.object({ + contentType: z.string().default('application/vnd.gorules.decision'), + nodes: z.array(nodeSchema).default([]), + edges: z.array(edgeSchema).default([]), +}); diff --git a/packages/jdm-editor/src/index.ts b/packages/jdm-editor/src/index.ts index 090b94d7..6ed24ff3 100644 --- a/packages/jdm-editor/src/index.ts +++ b/packages/jdm-editor/src/index.ts @@ -4,3 +4,4 @@ export * from './components'; export * from './theme'; export { codemirror } from './helpers/codemirror'; +export * from './helpers/schema'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38c3b130..e1d1cf3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -146,6 +146,9 @@ importers: use-debounce: specifier: ^10.0.0 version: 10.0.0(react@18.3.1) + zod: + specifier: ^3.23.8 + version: 3.23.8 zustand: specifier: ^4.5.2 version: 4.5.2(@types/react@18.3.3)(immer@10.1.1)(react@18.3.1) @@ -7005,6 +7008,9 @@ packages: resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} engines: {node: '>= 10'} + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zustand@4.5.2: resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==} engines: {node: '>=12.7.0'} @@ -15317,6 +15323,8 @@ snapshots: compress-commons: 4.1.2 readable-stream: 3.6.2 + zod@3.23.8: {} + zustand@4.5.2(@types/react@18.3.3)(immer@10.1.1)(react@18.3.1): dependencies: use-sync-external-store: 1.2.0(react@18.3.1)