@skychameleon/workflow 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/package.json +15 -4
  2. package/workflow/blocks/base.schema.ts +48 -0
  3. package/workflow/blocks/chat_bot/input-file.schema.ts +37 -11
  4. package/workflow/blocks/chat_bot/input-text.schema.ts +62 -25
  5. package/workflow/blocks/chat_bot/send-file.schema.ts +31 -13
  6. package/workflow/blocks/chat_bot/send-msg.schema.ts +19 -0
  7. package/workflow/blocks/general/run-code.schema.ts +15 -0
  8. package/workflow/blocks/general/start.schema.ts +26 -0
  9. package/workflow/blocks/general/var-set.schema.ts +34 -0
  10. package/workflow/blocks/registry.ts +34 -17
  11. package/workflow/blocks/shared/general.types.ts +77 -0
  12. package/workflow/blocks/validation/duplicate-vars.start.ts +22 -0
  13. package/workflow/blocks/validation/regex.ts +1 -0
  14. package/workflow/edges/base.schema.ts +19 -0
  15. package/workflow/edges/query-rule.schema.ts +62 -0
  16. package/workflow/validation/check-edge-to-blocks.ts +31 -0
  17. package/workflow/validation/check-rule-var.ts +25 -0
  18. package/workflow/validation/duplicate-id-blocks.ts +26 -0
  19. package/workflow/validation/duplicate-id-edges.ts +24 -0
  20. package/workflow/workflow.schema.ts +41 -0
  21. package/index.ts +0 -0
  22. package/workflow/blocks/base.schema.d.ts +0 -19
  23. package/workflow/blocks/chat_bot/send-msg.schema.d.ts +0 -11
  24. package/workflow/blocks/general/run-code.schema.d.ts +0 -8
  25. package/workflow/blocks/general/start.schema.d.ts +0 -10
  26. package/workflow/blocks/general/var-set.schema.d.ts +0 -25
  27. package/workflow/blocks/shared/general.types.d.ts +0 -63
  28. package/workflow/edges/base.schema.d.ts +0 -9
  29. package/workflow/edges/query-rule.schema.d.ts +0 -38
  30. package/workflow/workflow.schema.d.ts +0 -18
package/package.json CHANGED
@@ -1,19 +1,30 @@
1
1
  {
2
2
  "name": "@skychameleon/workflow",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Everything about workflow",
5
5
  "license": "ISC",
6
6
  "type": "module",
7
7
  "scripts": {
8
+ "test": "npx tsx --test",
8
9
  "format": "npx prettier --write ."
9
10
  },
10
11
  "publishConfig": {
11
12
  "access": "public"
12
13
  },
13
14
  "devDependencies": {
14
- "@skychameleon/core": "^1.0.3"
15
+ "@skychameleon/core": "^1.0.3",
16
+ "@types/node": "^25.3.3",
17
+ "tsx": "^4.21.0"
18
+ },
19
+ "dependencies": {
20
+ "zod": "^4.3.6"
15
21
  },
16
22
  "exports": {
17
- "./workflow.schema": "./workflow/workflow.schema.d.ts"
23
+ "./workflow.schema": "./workflow/workflow.schema.ts",
24
+ "./edges": "./workflow/query-rule.schema.ts",
25
+ "./blocks": "./workflow/registry.ts"
26
+ },
27
+ "peerDependencies": {
28
+ "zod": "^3.22.0"
18
29
  }
19
- }
30
+ }
@@ -0,0 +1,48 @@
1
+ import z from "zod";
2
+ import {
3
+ EntranceHandlerZod,
4
+ ExitHandlerZod,
5
+ type EntranceHandler,
6
+ type ExitHandler,
7
+ } from "./shared/general.types";
8
+
9
+ export const ChatBotBlockZodTypes = z.enum([
10
+ "send-file",
11
+ "send-msg",
12
+ "input-text",
13
+ "input-file",
14
+ ]);
15
+ export type ChatBotBlockTypes = z.infer<typeof ChatBotBlockZodTypes>;
16
+
17
+ export const MainBlockZodTypes = z.enum(["start", "var-set", "run-code"]);
18
+ export type MainBlockTypes = z.infer<typeof MainBlockZodTypes>;
19
+
20
+ export const BlockZodTypes = z.union([ChatBotBlockZodTypes, MainBlockZodTypes]);
21
+ export type BlockTypes = z.infer<typeof BlockZodTypes>;
22
+
23
+ export const BlockCategoryZod = z.enum(["pending", "action"]);
24
+ export type BlockCategory = z.infer<typeof BlockCategoryZod>;
25
+
26
+ export const createBaseBlockSchema = <
27
+ Type extends BlockTypes,
28
+ Category extends BlockCategory,
29
+ >(
30
+ type: Type,
31
+ category: Category,
32
+ ) =>
33
+ z.object({
34
+ _id: z.string(),
35
+ index: z.number(),
36
+ category: z.literal(category),
37
+ type: z.literal(type),
38
+ label: z.string(),
39
+ x: z.number(),
40
+ y: z.number(),
41
+ width: z.number(),
42
+ height: z.number(),
43
+ entrance: EntranceHandlerZod,
44
+ exit: ExitHandlerZod,
45
+ });
46
+
47
+ export type BaseBlockSchema = z.infer<ReturnType<typeof createBaseBlockSchema>>
48
+ export default createBaseBlockSchema;
@@ -1,16 +1,42 @@
1
- import type { BaseBlockSchema } from "../base.schema";
2
- import type { VarSetBlockSchema } from "../general/var-set.schema";
3
- import type {
4
- AdditionalOptions,
5
- InputVarChatBotType,
6
- SendText,
1
+ import type z from "zod";
2
+ import createBaseBlockSchema from "../base.schema";
3
+ import { VarSetBlockSchemaZod } from "../general/var-set.schema";
4
+ import {
5
+ AdditionalOptionsZod,
6
+ InputVarChatBotTypeZod,
7
+ SendTextZod,
7
8
  } from "../shared/general.types";
8
9
 
9
10
  /**
10
11
  * Блок отвечающий за получения файла
11
12
  */
12
- export interface InputFileChatBotBlockSchema extends BaseBlockSchema<"input-file"> {
13
- sendText: SendText;
14
- varSet: Omit<VarSetBlockSchema<InputVarChatBotType>, "code">;
15
- additionalOptions: Pick<AdditionalOptions, "back_button" | "skip_button">;
16
- }
13
+ export const InputFileChatBotBlockSchemaZod = createBaseBlockSchema(
14
+ "input-file",
15
+ "pending",
16
+ ).extend({
17
+ sendText: SendTextZod,
18
+ varSet: VarSetBlockSchemaZod.omit({
19
+ code: true,
20
+ })
21
+ .pick({
22
+ name: true,
23
+ varType: true,
24
+ completion: true,
25
+ })
26
+ .refine(
27
+ (varSet) => InputVarChatBotTypeZod.safeParse(varSet.varType).success,
28
+ {
29
+ message:
30
+ "varType must be one of InputVarChatBotType values for input-text block",
31
+ path: ["varSet", "varType"],
32
+ },
33
+ ),
34
+ additionalOptions: AdditionalOptionsZod.pick({
35
+ back_button: true,
36
+ skip_button: true,
37
+ }),
38
+ });
39
+ type InputFileChatBotBlockSchema = z.infer<
40
+ typeof InputFileChatBotBlockSchemaZod
41
+ >;
42
+ export default InputFileChatBotBlockSchema;
@@ -1,35 +1,72 @@
1
- import type { BaseBlockSchema } from "../base.schema";
2
- import type { VarSetBlockSchema } from "../general/var-set.schema";
3
- import type {
4
- AdditionalOptions,
5
- CodeExpression,
6
- InputVarChatBotType,
7
- SendText,
1
+ import z from "zod";
2
+ import {
3
+ AdditionalOptionsZod,
4
+ CodeExpressionZod,
5
+ InputVarChatBotTypeZod,
6
+ SendTextZod,
8
7
  } from "../shared/general.types";
8
+ import createBaseBlockSchema from "../base.schema";
9
+ import { VarSetBlockSchemaZod } from "../general/var-set.schema";
9
10
 
10
11
  /**
11
12
  * Типы для составления клавиатуры
12
13
  * Если создается кодом - KeyboardCode, то он должен вернуть объекта типа KeyboardConstructor
13
14
  */
14
- export type KeyboardCode = CodeExpression;
15
- export type KeyboardConstructor = {
16
- text:
17
- | string
18
- | {
19
- useCodeExpression: boolean;
20
- code: CodeExpression;
21
- };
22
- callback_data: string;
23
- }[];
15
+ export const KeyboardCodeZod = CodeExpressionZod;
16
+ export type KeyboardCode = z.infer<typeof KeyboardCodeZod>;
17
+
18
+ export const KeyboardConstructorZod = z.array(
19
+ z.object({
20
+ text: z.union([
21
+ z.string(),
22
+ z.object({
23
+ useCodeExpression: z.boolean(),
24
+ code: KeyboardCodeZod,
25
+ }),
26
+ ]),
27
+ callback_data: z.string(),
28
+ }),
29
+ );
30
+
31
+ export type KeyboardConstructor = z.infer<typeof KeyboardConstructorZod>;
24
32
 
25
33
  /**
26
34
  * Блок отвечающий за получения текстового сообщения
27
35
  */
28
- export interface InputTextChatBotBlockSchema extends BaseBlockSchema<"input-text"> {
29
- sendText: SendText;
30
- validator: false | CodeExpression; // валидирует полученное сообщение, в случае не сопаставление можно выдать ошибку
31
- convertor: false | CodeExpression; // конвертирует полученное сообщение в другое значение&тип
32
- varSet: Omit<VarSetBlockSchema<InputVarChatBotType>, "code">;
33
- keyboard: false | KeyboardConstructor | KeyboardCode;
34
- additionalOptions: Pick<AdditionalOptions, "back_button" | "skip_button">;
35
- }
36
+ export const InputTextChatBotBlockSchemaZod = createBaseBlockSchema(
37
+ "input-text",
38
+ "pending",
39
+ ).extend({
40
+ sendText: SendTextZod,
41
+ validator: z.union([z.literal(false), CodeExpressionZod]),
42
+ convertor: z.union([z.literal(false), CodeExpressionZod]),
43
+ varSet: VarSetBlockSchemaZod.omit({
44
+ code: true,
45
+ })
46
+ .pick({
47
+ name: true,
48
+ varType: true,
49
+ completion: true,
50
+ })
51
+ .refine(
52
+ (varSet) => InputVarChatBotTypeZod.safeParse(varSet.varType).success,
53
+ {
54
+ message:
55
+ "varType must be one of InputVarChatBotType values for input-text block",
56
+ path: ["varSet", "varType"],
57
+ },
58
+ ),
59
+ keyboard: z.union([
60
+ z.literal(false),
61
+ KeyboardCodeZod,
62
+ KeyboardConstructorZod,
63
+ ]),
64
+ additionalOptions: AdditionalOptionsZod.pick({
65
+ back_button: true,
66
+ skip_button: true,
67
+ }),
68
+ });
69
+ type InputTextChatBotBlockSchema = z.infer<
70
+ typeof InputTextChatBotBlockSchemaZod
71
+ >;
72
+ export default InputTextChatBotBlockSchema;
@@ -1,19 +1,37 @@
1
- import type { BaseBlockSchema } from "../base.schema";
2
- import type { VarSetBlockSchema } from "../general/var-set.schema";
3
- import type {
4
- AdditionalOptions,
5
- CodeExpression,
1
+ import z from "zod";
2
+ import createBaseBlockSchema from "../base.schema";
3
+ import {
6
4
  VarType,
7
- SendText,
5
+ SendTextZod,
6
+ AdditionalOptionsZod,
8
7
  } from "../shared/general.types";
8
+ import { VarSetBlockSchemaZod } from "../general/var-set.schema";
9
9
 
10
10
  /**
11
11
  * Блок отвечающий за отправление файлов
12
12
  */
13
- export interface SendFileChatBotBlockSchema extends BaseBlockSchema<"send_file"> {
14
- several: boolean;
15
- sendText: SendText;
16
- varSet: Omit<VarSetBlockSchema<VarType.BOOLEAN>, "code">;
17
- errorText: string;
18
- additionalOptions: Pick<AdditionalOptions, "send_other_chat">;
19
- }
13
+ export const SendFileChatBotBlockSchemaZod = createBaseBlockSchema(
14
+ "send-file",
15
+ "action",
16
+ ).extend({
17
+ several: z.boolean(),
18
+ sendText: SendTextZod,
19
+ varSet: VarSetBlockSchemaZod.omit({
20
+ code: true,
21
+ })
22
+ .pick({
23
+ name: true,
24
+ varType: true,
25
+ completion: true,
26
+ })
27
+ .refine((varSet) => varSet.varType === VarType.BOOLEAN, {
28
+ message: "varType must be BOOLEAN for send-file block",
29
+ path: ["varSet", "varType"],
30
+ }),
31
+ errorText: z.string(),
32
+ additionalOptions: AdditionalOptionsZod.pick({
33
+ send_other_chat: true,
34
+ }),
35
+ });
36
+ type SendFileChatBotBlockSchema = z.infer<typeof SendFileChatBotBlockSchemaZod>;
37
+ export default SendFileChatBotBlockSchema;
@@ -0,0 +1,19 @@
1
+ import z from "zod";
2
+ import createBaseBlockSchema from "../base.schema";
3
+ import { AdditionalOptionsZod, SendTextZod } from "../shared/general.types";
4
+
5
+ /**
6
+ * Блок отвечающий за отправление текстового сообщения
7
+ */
8
+ export const SendMsgChatBotBlockSchemaZod = createBaseBlockSchema(
9
+ "send-msg",
10
+ "action",
11
+ ).extend({
12
+ markdown: z.boolean(),
13
+ sendText: SendTextZod,
14
+ additionalOptions: AdditionalOptionsZod.pick({
15
+ send_other_chat: true,
16
+ }),
17
+ });
18
+ type SendMsgChatBotBlockSchema = z.infer<typeof SendMsgChatBotBlockSchemaZod>;
19
+ export default SendMsgChatBotBlockSchema;
@@ -0,0 +1,15 @@
1
+ import z from "zod";
2
+ import createBaseBlockSchema from "../base.schema";
3
+ import { CodeExpressionZod } from "../shared/general.types";
4
+
5
+ /**
6
+ * Блок отвечающий за запуск кода
7
+ */
8
+ export const RunCodeBlockSchemaZod = createBaseBlockSchema(
9
+ "start",
10
+ "action",
11
+ ).extend({
12
+ code: CodeExpressionZod,
13
+ });
14
+ type RunCodeBlockSchema = z.infer<typeof RunCodeBlockSchemaZod>;
15
+ export default RunCodeBlockSchema;
@@ -0,0 +1,26 @@
1
+ import z from "zod";
2
+ import createBaseBlockSchema from "../base.schema";
3
+ import { VarSetBlockSchemaZod } from "./var-set.schema";
4
+ import { validateDuplicateVars } from "../validation/duplicate-vars.start";
5
+
6
+ /**
7
+ * Блок отвечающий за запуск сценария и содержащий все необходимые переменные для работы
8
+ */
9
+ export const StartBlockSchemaZod = createBaseBlockSchema(
10
+ "start",
11
+ "action",
12
+ ).extend({
13
+ vars: z
14
+ .array(
15
+ VarSetBlockSchemaZod.pick({
16
+ name: true,
17
+ varType: true,
18
+ code: true,
19
+ completion: true,
20
+ }),
21
+ )
22
+ .superRefine(validateDuplicateVars),
23
+ });
24
+ type StartBlockSchema = z.infer<typeof StartBlockSchemaZod>;
25
+
26
+ export default StartBlockSchema;
@@ -0,0 +1,34 @@
1
+ import z from "zod";
2
+ import createBaseBlockSchema from "../base.schema";
3
+ import {
4
+ CodeExpressionZod,
5
+ VarTypeZod,
6
+ InputVarChatBotTypeZod,
7
+ type VarType,
8
+ } from "../shared/general.types";
9
+ import { regexVarName } from "../validation/regex";
10
+
11
+ export const CompletionTypesZod = z.enum(["function", "variable"]);
12
+ export type CompletionTypes = z.infer<typeof CompletionTypesZod>;
13
+
14
+ export const VarSetBlockSchemaZod = createBaseBlockSchema(
15
+ "var-set",
16
+ "action",
17
+ ).extend({
18
+ name: z.string().regex(regexVarName),
19
+ varType: z.union([VarTypeZod, InputVarChatBotTypeZod]),
20
+ code: CodeExpressionZod,
21
+ completion: z.union([
22
+ z.literal(false),
23
+ z.object({
24
+ label: z.string().optional(),
25
+ type: CompletionTypesZod.optional(),
26
+ detail: z.string().optional(),
27
+ info: z.string().optional(),
28
+ apply: z.string().optional(),
29
+ }),
30
+ ]),
31
+ });
32
+
33
+ type VarSetBlock = z.infer<typeof VarSetBlockSchemaZod>;
34
+ export default VarSetBlock;
@@ -1,18 +1,35 @@
1
- import type { RunCodeBlockSchema } from "./general/run-code.schema.d.ts";
2
- import type { StartBlockSchema } from "./general/start.schema.d.ts";
3
- import type { VarSetBlockSchema } from "./general/var-set.schema.d.ts";
4
- import type { InputVarChatBotType, VarType } from "./shared/general.types.d.ts";
5
- import type { SendMsgChatBotBlockSchema } from "./chat_bot/send-msg.schema.d.ts";
6
- import type { SendFileChatBotBlockSchema } from "./chat_bot/send-file.schema.d.ts";
7
- import type { InputTextChatBotBlockSchema } from "./chat_bot/input-text.schema.d.ts";
8
- import type { InputFileChatBotBlockSchema } from "./chat_bot/input-file.schema.d.ts";
1
+ import z from "zod";
2
+ import { StartBlockSchemaZod } from "./general/start.schema.ts";
3
+ import { VarSetBlockSchemaZod } from "./general/var-set.schema.ts";
4
+ import { RunCodeBlockSchemaZod } from "./general/run-code.schema.ts";
5
+ import { SendMsgChatBotBlockSchemaZod } from "./chat_bot/send-msg.schema.ts";
6
+ import { SendFileChatBotBlockSchemaZod } from "./chat_bot/send-file.schema.ts";
7
+ import { InputTextChatBotBlockSchemaZod } from "./chat_bot/input-text.schema.ts";
8
+ import { InputFileChatBotBlockSchemaZod } from "./chat_bot/input-file.schema.ts";
9
9
 
10
- export type GeneralBlockSchema =
11
- | StartBlockSchema
12
- | VarSetBlockSchema<VarType | InputVarChatBotType>
13
- | RunCodeBlockSchema;
14
- export type ChatBotBlockSchema =
15
- | SendMsgChatBotBlockSchema
16
- | SendFileChatBotBlockSchema
17
- | InputTextChatBotBlockSchema
18
- | InputFileChatBotBlockSchema;
10
+ export const GeneralBlockSchemaZods = z.union([
11
+ StartBlockSchemaZod,
12
+ VarSetBlockSchemaZod,
13
+ RunCodeBlockSchemaZod,
14
+ ]);
15
+
16
+ export type GeneralBlocksSchema = z.infer<typeof GeneralBlockSchemaZods>;
17
+
18
+ export const ChatBotBlocksSchemaZods = z.union([
19
+ SendMsgChatBotBlockSchemaZod,
20
+ SendFileChatBotBlockSchemaZod,
21
+ InputTextChatBotBlockSchemaZod,
22
+ InputFileChatBotBlockSchemaZod,
23
+ ]);
24
+
25
+ export type ChatBotBlocksSchema = z.infer<typeof ChatBotBlocksSchemaZods>;
26
+
27
+ export default {
28
+ StartBlockSchemaZod,
29
+ VarSetBlockSchemaZod,
30
+ RunCodeBlockSchemaZod,
31
+ SendMsgChatBotBlockSchemaZod,
32
+ SendFileChatBotBlockSchemaZod,
33
+ InputTextChatBotBlockSchemaZod,
34
+ InputFileChatBotBlockSchemaZod
35
+ }
@@ -0,0 +1,77 @@
1
+ import z from "zod";
2
+
3
+ export const CodeExpressionZod = z.string();
4
+ export type CodeExpression = z.infer<typeof CodeExpressionZod>;
5
+ /**
6
+ * Обработчик блока после выхода из основной логики бота
7
+ */
8
+ export const ExitHandlerZod = z.union([
9
+ z.literal(false),
10
+ z.object({ code: z.string() }),
11
+ ]);
12
+ export type ExitHandler = z.infer<typeof EntranceHandlerZod>;
13
+ /**
14
+ * Обработчик блока перед входом в основную логику бота
15
+ */
16
+ export const EntranceHandlerZod = z.union([
17
+ z.literal(false),
18
+ z.object({ code: z.string() }),
19
+ ]);
20
+ export type EntranceHandler = z.infer<typeof EntranceHandlerZod>;
21
+
22
+ export const DynamicTextZod = z.object({
23
+ useCodeExpression: z.boolean(),
24
+ code: CodeExpressionZod,
25
+ });
26
+ export type DynamicText = z.infer<typeof DynamicTextZod>;
27
+
28
+ export const SendTextZod = z.union([z.string(), DynamicTextZod]);
29
+ export type SendText = z.infer<typeof SendTextZod>;
30
+
31
+ export const ChatIDZod = z.number();
32
+ export type ChatID = z.infer<typeof ChatIDZod>;
33
+ /**
34
+ * Дополнительные опции для чат-ботов
35
+ */
36
+ export const AdditionalOptionsZod = z.object({
37
+ back_button: z.union([
38
+ z.literal(false),
39
+ z.string(),
40
+ z.object({
41
+ useCodeExpression: z.boolean(),
42
+ code: CodeExpressionZod,
43
+ }),
44
+ ]),
45
+ skip_button: z.union([
46
+ z.literal(false),
47
+ z.string(),
48
+ z.object({
49
+ useCodeExpression: z.boolean(),
50
+ code: CodeExpressionZod,
51
+ }),
52
+ ]),
53
+ send_other_chat: z.union([z.literal(false), ChatIDZod]),
54
+ });
55
+ export type AdditionalOptions = z.infer<typeof AdditionalOptionsZod>;
56
+
57
+ /**
58
+ * Типы переменных
59
+ *
60
+ * Типы для полученных сообщений из чат-ботов
61
+ * Общие типы
62
+ */
63
+ export enum InputVarChatBotType {
64
+ FILE = "file",
65
+ }
66
+ export const InputVarChatBotTypeZod = z.enum(InputVarChatBotType);
67
+
68
+ export enum VarType {
69
+ STRING = "string",
70
+ NUMBER = "number",
71
+ BOOLEAN = "boolean",
72
+ OBJECT = "object",
73
+ ARRAY = "array",
74
+ FUNCTION = "function",
75
+ ANY = "any",
76
+ }
77
+ export const VarTypeZod = z.enum(VarType);
@@ -0,0 +1,22 @@
1
+ import { ZodIssueCode } from "zod/v3";
2
+ import type VarSetBlock from "../general/var-set.schema";
3
+ import type z from "zod";
4
+
5
+ export const validateDuplicateVars = (
6
+ vars: Pick<VarSetBlock, "name" | "varType" | "code" | "completion">[],
7
+ ctx: z.RefinementCtx,
8
+ ) => {
9
+ const seen = new Set<string>();
10
+
11
+ vars.forEach((v, index) => {
12
+ if (seen.has(v.name)) {
13
+ ctx.addIssue({
14
+ code: ZodIssueCode.custom,
15
+ message: `Переменная "${v.name}" уже объявлена в этом блоке`,
16
+ path: [index, "name"],
17
+ });
18
+ } else {
19
+ seen.add(v.name)
20
+ }
21
+ });
22
+ };
@@ -0,0 +1 @@
1
+ export const regexVarName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/g;
@@ -0,0 +1,19 @@
1
+ import z from "zod";
2
+
3
+ export const EdgeTypesZod = z.enum(["query-rule"]);
4
+ export type EdgeTypes = z.infer<typeof EdgeTypesZod>;
5
+
6
+ const createBaseEdgeSchemaZod = <Type extends EdgeTypes, Guard>(
7
+ type: Type,
8
+ guard: Guard,
9
+ ) =>
10
+ z.object({
11
+ _id: z.string(),
12
+ label: z.string().optional(),
13
+ type: z.literal(type),
14
+ priority: z.number(),
15
+ source: z.string(),
16
+ target: z.string(),
17
+ guards: guard,
18
+ });
19
+ export default createBaseEdgeSchemaZod;
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ import { VarTypeZod } from "../blocks/shared/general.types";
3
+ import createBaseEdgeSchemaZod from "./base.schema";
4
+ import { VarSetBlockSchemaZod } from "../blocks/general/var-set.schema";
5
+
6
+ export enum LogicOperator {
7
+ AND = "and",
8
+ OR = "or",
9
+ }
10
+ export const LogicOperatorZod = z.enum(LogicOperator);
11
+ export type LogicOperatorType = z.infer<typeof LogicOperatorZod>;
12
+
13
+ export enum ConditionList {
14
+ EMPTY = "empty",
15
+ NOT_EMPTY = "not_empty",
16
+ EQUAL = "equal",
17
+ NOT_EQUAL = "not_equal",
18
+ LESS = "less",
19
+ LESS_OR_EQUAL = "less_or_equal",
20
+ GREATER = "greater",
21
+ GREATER_OR_EQUAL = "greater_or_equal",
22
+ IS_NULL = "is_null",
23
+ IS_NOT_NULL = "is_not_null",
24
+ }
25
+ export const ConditionListZod = z.enum(ConditionList);
26
+ export type ConditionListType = z.infer<typeof ConditionListZod>;
27
+
28
+ export const ValueModeZod = z.object({
29
+ mode: z.enum(["value", "variable", "expression"]),
30
+ value: z.string(),
31
+ });
32
+ export type ValueMode = z.infer<typeof ValueModeZod>;
33
+
34
+ export const SingleRuleGuardSchema = z.object({
35
+ var: VarSetBlockSchemaZod.pick({
36
+ name: true,
37
+ varType: true,
38
+ }),
39
+ condition: ConditionListZod,
40
+ valueMode: ValueModeZod,
41
+ });
42
+ export type SingleRuleGuard = z.infer<typeof SingleRuleGuardSchema>;
43
+
44
+ export interface RuleGuardGroupSchema {
45
+ operator: LogicOperator;
46
+ guards: (SingleRuleGuard | RuleGuardGroupSchema)[];
47
+ }
48
+ export const RuleGuardGroupSchemaZod: z.ZodType<RuleGuardGroupSchema> = z.lazy(
49
+ () =>
50
+ z.object({
51
+ operator: LogicOperatorZod,
52
+ guards: z.array(
53
+ z.union([SingleRuleGuardSchema, RuleGuardGroupSchemaZod]),
54
+ )
55
+ }),
56
+ );
57
+
58
+ export const QueryRuleEdgeSchemaZod = createBaseEdgeSchemaZod(
59
+ "query-rule",
60
+ RuleGuardGroupSchemaZod,
61
+ );
62
+ export type QueryRuleEdgeSchema = z.infer<typeof QueryRuleEdgeSchemaZod>;
@@ -0,0 +1,31 @@
1
+ import type z from "zod";
2
+ import type { ChatBotBlocksSchema, GeneralBlocksSchema } from "../blocks/registry";
3
+ import type { QueryRuleEdgeSchema } from "../edges/query-rule.schema"
4
+ import { ZodIssueCode } from "zod/v3";
5
+
6
+
7
+ export const checkEdgeToBlocks = (
8
+ blocks: (ChatBotBlocksSchema | GeneralBlocksSchema)[],
9
+ edges: QueryRuleEdgeSchema[],
10
+ ctx: z.RefinementCtx
11
+ ) => {
12
+ const blockIds = new Set(blocks.map(block => block._id))
13
+
14
+ edges.forEach((edge, index) => {
15
+ if (!blockIds.has(edge.source)) {
16
+ ctx.addIssue({
17
+ code: ZodIssueCode.custom,
18
+ message: `Связь "${edge._id}" ссылается на несуществующий блок source: "${edge.source}"`,
19
+ path: ["states", "edges", index, "source"]
20
+ })
21
+ }
22
+
23
+ if (!blockIds.has(edge.target)) {
24
+ ctx.addIssue({
25
+ code: ZodIssueCode.custom,
26
+ message: `Связь "${edge._id}" ссылается на несуществующий блок target: "${edge.target}"`,
27
+ path: ["states", "edges", index, "target"]
28
+ })
29
+ }
30
+ })
31
+ }
@@ -0,0 +1,25 @@
1
+ import type z from "zod";
2
+ import type { ChatBotBlocksSchema, GeneralBlocksSchema } from "../blocks/registry";
3
+ import type { QueryRuleEdgeSchema } from "../edges/query-rule.schema"
4
+
5
+
6
+
7
+ /**
8
+ * Проверяет, есть ли в текущем edge доступ к переменным в rule
9
+ * Для текущего rule в edge доступ к блокам имеющие переменную(ые) доступно только до edge
10
+ */
11
+ export const checkRuleVar = (
12
+ blocks: (ChatBotBlocksSchema | GeneralBlocksSchema)[],
13
+ edges: QueryRuleEdgeSchema[],
14
+ ctx: z.RefinementCtx
15
+ ) => {
16
+ const blockIndexed = Object.groupBy(blocks, ({ index }) => index)
17
+
18
+ edges.forEach((edge) => {
19
+ const beginBlock = edge.source;
20
+ const block = blocks.find((block) => block._id == beginBlock)
21
+ if (!block) return
22
+
23
+
24
+ })
25
+ }
@@ -0,0 +1,26 @@
1
+ import type z from "zod";
2
+ import type { ChatBotBlocksSchema, GeneralBlocksSchema } from "../blocks/registry";
3
+ import { ZodIssueCode } from "zod/v3";
4
+
5
+ /**
6
+ * Валидация блоков на id. Должны быть уникальными
7
+ */
8
+ export const duplicateIdBlocks = (
9
+ blocks: (ChatBotBlocksSchema | GeneralBlocksSchema)[],
10
+ ctx: z.RefinementCtx
11
+ ) => {
12
+ const seen = new Set<string>();
13
+
14
+ blocks.forEach((block, index) => {
15
+ if (seen.has(block._id)) {
16
+
17
+ ctx.addIssue({
18
+ code: ZodIssueCode.custom,
19
+ message: `_id "${block._id}" с индексом ${block.index} уже используется`,
20
+ path: [index, "name"],
21
+ });
22
+ } else {
23
+ seen.add(block._id)
24
+ }
25
+ })
26
+ }
@@ -0,0 +1,24 @@
1
+ import type z from "zod";
2
+ import { ZodIssueCode } from "zod/v3";
3
+ import type { QueryRuleEdgeSchema } from "../edges/query-rule.schema";
4
+
5
+ /**
6
+ * Валидация связей на id. Должны быть уникальными
7
+ */
8
+ export const duplicateIdEdges = (
9
+ edges: QueryRuleEdgeSchema[],
10
+ ctx: z.RefinementCtx
11
+ ) => {
12
+ const seen = new Set<string>();
13
+ edges.forEach((edge, index) => {
14
+ if (seen.has(edge._id)) {
15
+ ctx.addIssue({
16
+ code: ZodIssueCode.custom,
17
+ message: `_id "${edge._id}" уже используется`,
18
+ path: [index, "name"],
19
+ });
20
+ } else {
21
+ seen.add(edge._id)
22
+ }
23
+ })
24
+ }
@@ -0,0 +1,41 @@
1
+ import z from "zod";
2
+ import {
3
+ ChatBotBlocksSchemaZods,
4
+ GeneralBlockSchemaZods,
5
+ } from "./blocks/registry";
6
+ import {
7
+ QueryRuleEdgeSchemaZod,
8
+ type QueryRuleEdgeSchema,
9
+ } from "./edges/query-rule.schema";
10
+ import { checkEdgeToBlocks } from "./validation/check-edge-to-blocks";
11
+ import { duplicateIdBlocks } from "./validation/duplicate-id-blocks";
12
+ import { duplicateIdEdges } from "./validation/duplicate-id-edges";
13
+ import { checkRuleVar } from "./validation/check-rule-var";
14
+
15
+ export const MetadataSchemaZod = z.object({
16
+ name: z.string(),
17
+ description: z.string(),
18
+ exportedAt: z.iso.datetime(),
19
+ });
20
+
21
+ export type MetadataSchema = z.infer<typeof MetadataSchemaZod>;
22
+
23
+ export const WorkflowSchemaZod = z.object({
24
+ metadata: MetadataSchemaZod,
25
+ states: z.object({
26
+ edges: z.array(QueryRuleEdgeSchemaZod)
27
+ .superRefine((edges, ctx) => duplicateIdEdges(edges, ctx)),
28
+ blocks: z.array(z.union([GeneralBlockSchemaZods, ChatBotBlocksSchemaZods]))
29
+ .superRefine((blocks, ctx) => duplicateIdBlocks(blocks, ctx)),
30
+ }),
31
+ }).superRefine((data, ctx) => {
32
+ const { states } = data
33
+ const { edges, blocks } = states
34
+
35
+ checkEdgeToBlocks(blocks, edges, ctx)
36
+ // checkRuleVar(blocks, edges, ctx)
37
+ });
38
+
39
+ type WorkflowSchema = z.infer<typeof WorkflowSchemaZod>;
40
+
41
+ export default WorkflowSchema;
package/index.ts DELETED
File without changes
@@ -1,19 +0,0 @@
1
- import type { EntranceHandler, ExitHandler } from "./shared/general.types";
2
-
3
- type ChatBotBlockTypes = "send_file" | "send_msg" | "input-text" | "input-file";
4
-
5
- type MainBlockTypes = "start" | "var_set" | "run_code";
6
-
7
- type BlockTypes = MainBlockTypes | ChatBotBlockTypes;
8
-
9
- export interface BaseBlockSchema<T extends BlockTypes> {
10
- _id: string;
11
- type: T;
12
- label: string;
13
- x: number;
14
- y: number;
15
- width: number;
16
- height: number;
17
- entrance: EntranceHandler;
18
- exit: ExitHandler;
19
- }
@@ -1,11 +0,0 @@
1
- import type { BaseBlockSchema } from "../base.schema";
2
- import type { AdditionalOptions, SendText } from "../shared/general.types";
3
-
4
- /**
5
- * Блок отвечающий за отправление текстового сообщения
6
- */
7
- export interface SendMsgChatBotBlockSchema extends BaseBlockSchema<"send_msg"> {
8
- markdown: boolean;
9
- sendText: SendText;
10
- additionalOptions: Pick<AdditionalOptions, "send_other_chat">;
11
- }
@@ -1,8 +0,0 @@
1
- import type { BaseBlockSchema } from "../base.schema";
2
-
3
- /**
4
- * Блок отвечающий за запуск кода
5
- */
6
- export interface RunCodeBlockSchema extends BaseBlockSchema<"run_code"> {
7
- code: string;
8
- }
@@ -1,10 +0,0 @@
1
- import type { BaseBlockSchema } from "../base.schema";
2
- import type { VarType } from "../shared/general.types";
3
- import type { VarSetBlockSchema } from "./var-set.schema";
4
-
5
- /**
6
- * Блок отвечающий за запуск сценария и содержащий все необходимые переменные для работы
7
- */
8
- export interface StartBlockSchema extends BaseBlockSchema<"start"> {
9
- vars: VarSetBlockSchema<VarType>[];
10
- }
@@ -1,25 +0,0 @@
1
- import type { BaseBlockSchema } from "../base.schema";
2
- import type { VarType, InputVarChatBotType } from "../shared/general.types";
3
-
4
- type CompletionTypes = `function` | `variable`;
5
-
6
- /**
7
- * Блок отвечающий за создание переменной в контексте клиента
8
- */
9
- export interface VarSetBlockSchema<
10
- Type extends VarType | InputVarChatBotType,
11
- > extends BaseBlockSchema<"var_set"> {
12
- name: string;
13
- varType: Type;
14
- code: string;
15
- completion:
16
- | false
17
- | {
18
- // для подсказки в редакторе-codemirror
19
- label?: string;
20
- type?: CompletionTypes;
21
- detail?: string;
22
- info?: string;
23
- apply?: string;
24
- };
25
- }
@@ -1,63 +0,0 @@
1
- export type CodeExpression = string;
2
- /**
3
- * Обработчик блока после выхода из основной логики бота
4
- */
5
- export type ExitHandler =
6
- | false
7
- | {
8
- code: string;
9
- };
10
- /**
11
- * Обработчик блока перед входом в основную логику бота
12
- */
13
- export type EntranceHandler =
14
- | false
15
- | {
16
- code: string;
17
- };
18
-
19
- interface DynamicText {
20
- useCodeExpression: boolean;
21
- code: CodeExpression;
22
- }
23
- export type SendText = string | DynamicText;
24
- export type ChatID = number;
25
- /**
26
- * Дополнительные опции для чат-ботов
27
- */
28
- export type AdditionalOptions = {
29
- back_button:
30
- | false
31
- | string
32
- | {
33
- useCodeExpression: boolean;
34
- code: CodeExpression;
35
- };
36
- skip_button:
37
- | false
38
- | string
39
- | {
40
- useCodeExpression: boolean;
41
- code: CodeExpression;
42
- };
43
- send_other_chat: false | ChatID;
44
- };
45
-
46
- /**
47
- * Типы переменных
48
- *
49
- * Типы для полученных сообщений из чат-ботов
50
- * Общие типы
51
- */
52
- export enum InputVarChatBotType {
53
- FILE = "file",
54
- }
55
- export enum VarType {
56
- STRING = "string",
57
- NUMBER = "number",
58
- BOOLEAN = "boolean",
59
- OBJECT = "object",
60
- ARRAY = "array",
61
- FUNCTION = "function",
62
- ANY = "any",
63
- }
@@ -1,9 +0,0 @@
1
- export type EdgeTypes = "query-rule";
2
-
3
- export interface BaseEdgeSchema<Type extends EdgeTypes, Guard> {
4
- label?: string;
5
- type: Type;
6
- priority: number;
7
- to: string;
8
- guards: Guard;
9
- }
@@ -1,38 +0,0 @@
1
- import type { VarType } from "../blocks/shared/general.types";
2
- import type { BaseEdgeSchema } from "./base.schema";
3
-
4
- export enum LogicOperator {
5
- AND = "and",
6
- OR = "or",
7
- }
8
- export enum ConditionList {
9
- EMPTY = "empty",
10
- NOT_EMPTY = "not_empty",
11
- EQUAL = "equal",
12
- NOT_EQUAL = "not_equal",
13
- LESS = "less",
14
- LESS_OR_EQUAL = "less_or_equal",
15
- GREATER = "greater",
16
- GREATER_OR_EQUAL = "greater_or_equal",
17
- IS_NULL = "is_null",
18
- IS_NOT_NULL = "is_not_null",
19
- }
20
-
21
- export interface SingleRuleGuard {
22
- var: string;
23
- typeVar: VarType;
24
- /*--*/
25
- condition: ConditionList;
26
- /*--*/
27
- valueMode: {
28
- mode: "value" | "variable" | "expression";
29
- value: string;
30
- };
31
- }
32
-
33
- export interface RuleGuardGroup {
34
- operator: LogicOperator;
35
- guards: (SingleRuleGuard | RuleGuardGroup)[];
36
- }
37
-
38
- export type QueryRuleEdgeSchema = BaseEdgeSchema<"query-rule", RuleGuardGroup>;
@@ -1,18 +0,0 @@
1
- import type { ChatBotBlockSchema, GeneralBlockSchema } from "./blocks/registry";
2
- import type { QueryRuleEdgeSchema } from "./edges/query-rule.schema";
3
-
4
- export interface Metadata {
5
- name: string;
6
- description: string;
7
- exportedAt: string;
8
- }
9
-
10
- interface WorkflowSchema {
11
- states: {
12
- blocks: (GeneralBlockSchema | ChatBotBlockSchema)[];
13
- edges: QueryRuleEdgeSchema[];
14
- };
15
- metadata: Metadata;
16
- }
17
-
18
- export default WorkflowSchema;