@flowgram.ai/form-materials 0.4.1 → 0.4.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowgram.ai/form-materials",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "homepage": "https://flowgram.ai/",
5
5
  "repository": "https://github.com/bytedance/flowgram.ai",
6
6
  "license": "MIT",
@@ -35,8 +35,8 @@
35
35
  "@codemirror/state": "~6.5.2",
36
36
  "typescript": "^5.8.3",
37
37
  "zod": "^3.24.4",
38
- "@flowgram.ai/editor": "0.4.1",
39
- "@flowgram.ai/json-schema": "0.4.1"
38
+ "@flowgram.ai/editor": "0.4.3",
39
+ "@flowgram.ai/json-schema": "0.4.3"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/lodash": "^4.14.137",
@@ -52,8 +52,8 @@
52
52
  "tsup": "^8.0.1",
53
53
  "typescript": "^5.8.3",
54
54
  "vitest": "^0.34.6",
55
- "@flowgram.ai/eslint-config": "0.4.1",
56
- "@flowgram.ai/ts-config": "0.4.1"
55
+ "@flowgram.ai/eslint-config": "0.4.3",
56
+ "@flowgram.ai/ts-config": "0.4.3"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "react": ">=16.8",
@@ -9,6 +9,7 @@ import { JsonSchemaTypeManager, JsonSchemaUtils } from '@flowgram.ai/json-schema
9
9
  import { useScopeAvailable } from '@flowgram.ai/editor';
10
10
 
11
11
  import { IFlowValue } from '@/typings';
12
+ import { FlowValueUtils } from '@/shared';
12
13
  import { DisplaySchemaTag } from '@/components/display-schema-tag';
13
14
 
14
15
  interface PropsType {
@@ -31,18 +32,7 @@ export function DisplayFlowValue({ value, title, showIconInTree }: PropsType) {
31
32
  return { type: 'string' };
32
33
  }
33
34
  if (value?.type === 'constant') {
34
- if (value?.schema) {
35
- return value?.schema;
36
- }
37
- if (typeof value?.content === 'string') {
38
- return { type: 'string' };
39
- }
40
- if (typeof value?.content === 'number') {
41
- return { type: 'number' };
42
- }
43
- if (typeof value?.content === 'boolean') {
44
- return { type: 'boolean' };
45
- }
35
+ return FlowValueUtils.inferConstantJsonSchema(value);
46
36
  }
47
37
 
48
38
  return { type: 'unknown' };
@@ -3,15 +3,19 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import React from 'react';
6
+ import React, { useMemo } from 'react';
7
7
 
8
- import { IFlowValue } from '@/typings';
8
+ import { isPlainObject } from 'lodash';
9
+ import { useScopeAvailable } from '@flowgram.ai/editor';
10
+
11
+ import { FlowValueUtils } from '@/shared';
9
12
  import { DisplayFlowValue } from '@/components/display-flow-value';
10
13
 
11
14
  import { DisplayInputsWrapper } from './styles';
15
+ import { DisplaySchemaTag } from '../display-schema-tag';
12
16
 
13
17
  interface PropsType {
14
- value?: Record<string, IFlowValue | undefined>;
18
+ value?: any;
15
19
  showIconInTree?: boolean;
16
20
  }
17
21
 
@@ -20,9 +24,43 @@ export function DisplayInputsValues({ value, showIconInTree }: PropsType) {
20
24
 
21
25
  return (
22
26
  <DisplayInputsWrapper>
23
- {childEntries.map(([key, value]) => (
24
- <DisplayFlowValue key={key} title={key} value={value} showIconInTree={showIconInTree} />
25
- ))}
27
+ {childEntries.map(([key, value]) => {
28
+ if (FlowValueUtils.isFlowValue(value)) {
29
+ return (
30
+ <DisplayFlowValue key={key} title={key} value={value} showIconInTree={showIconInTree} />
31
+ );
32
+ }
33
+
34
+ if (isPlainObject(value)) {
35
+ return (
36
+ <DisplayInputsValueAllInTag
37
+ key={key}
38
+ title={key}
39
+ value={value}
40
+ showIconInTree={showIconInTree}
41
+ />
42
+ );
43
+ }
44
+
45
+ return null;
46
+ })}
26
47
  </DisplayInputsWrapper>
27
48
  );
28
49
  }
50
+
51
+ export function DisplayInputsValueAllInTag({
52
+ value,
53
+ title,
54
+ showIconInTree,
55
+ }: PropsType & {
56
+ title: string;
57
+ }) {
58
+ const available = useScopeAvailable();
59
+
60
+ const schema = useMemo(
61
+ () => FlowValueUtils.inferJsonSchema(value, available.scope),
62
+ [available.version, value]
63
+ );
64
+
65
+ return <DisplaySchemaTag title={title} value={schema} showIconInTree={showIconInTree} />;
66
+ }
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { useMemo, useState } from 'react';
6
+ import { useEffect, useMemo, useRef, useState } from 'react';
7
7
 
8
8
  import { IJsonSchema } from '@flowgram.ai/json-schema';
9
9
  import { useScopeAvailable } from '@flowgram.ai/editor';
@@ -33,9 +33,30 @@ export function useSelectSchema(
33
33
  defaultSelectSchema = value?.schema || defaultSelectSchema;
34
34
  }
35
35
 
36
+ const changeVersion = useRef(0);
37
+ const effectVersion = useRef(0);
38
+
36
39
  const [selectSchema, setSelectSchema] = useState(defaultSelectSchema);
37
40
 
38
- return [selectSchema, setSelectSchema] as const;
41
+ useEffect(() => {
42
+ effectVersion.current += 1;
43
+ if (changeVersion.current === effectVersion.current) {
44
+ return;
45
+ }
46
+ effectVersion.current = changeVersion.current;
47
+
48
+ if (value?.type === 'constant' && value?.schema) {
49
+ setSelectSchema(value?.schema);
50
+ return;
51
+ }
52
+ }, [value]);
53
+
54
+ const setSelectSchemaWithVersionUpdate = (schema: IJsonSchema) => {
55
+ setSelectSchema(schema);
56
+ changeVersion.current += 1;
57
+ };
58
+
59
+ return [selectSchema, setSelectSchemaWithVersionUpdate] as const;
39
60
  }
40
61
 
41
62
  export function useIncludeSchema(schemaFromProps?: IJsonSchema) {
@@ -5,11 +5,16 @@
5
5
 
6
6
  import React from 'react';
7
7
 
8
- import { JsonSchemaUtils, IJsonSchema } from '@flowgram.ai/json-schema';
8
+ import {
9
+ JsonSchemaUtils,
10
+ IJsonSchema,
11
+ useTypeManager,
12
+ type JsonSchemaTypeManager,
13
+ } from '@flowgram.ai/json-schema';
9
14
  import { IconButton } from '@douyinfe/semi-ui';
10
15
  import { IconSetting } from '@douyinfe/semi-icons';
11
16
 
12
- import { IFlowConstantRefValue } from '@/typings';
17
+ import { IFlowConstantRefValue, IFlowConstantValue } from '@/typings';
13
18
  import { createInjectMaterial } from '@/shared';
14
19
  import { InjectVariableSelector } from '@/components/variable-selector';
15
20
  import { TypeSelector } from '@/components/type-selector';
@@ -32,6 +37,12 @@ interface PropsType {
32
37
  };
33
38
  }
34
39
 
40
+ const DEFAULT_VALUE: IFlowConstantValue = {
41
+ type: 'constant',
42
+ content: '',
43
+ schema: { type: 'string' },
44
+ };
45
+
35
46
  export function DynamicValueInput({
36
47
  value,
37
48
  onChange,
@@ -44,6 +55,8 @@ export function DynamicValueInput({
44
55
  const [selectSchema, setSelectSchema] = useSelectSchema(schemaFromProps, constantProps, value);
45
56
  const includeSchema = useIncludeSchema(schemaFromProps);
46
57
 
58
+ const typeManager = useTypeManager() as JsonSchemaTypeManager;
59
+
47
60
  const renderTypeSelector = () => {
48
61
  if (schemaFromProps) {
49
62
  return <TypeSelector value={schemaFromProps} readonly={true} />;
@@ -60,24 +73,20 @@ export function DynamicValueInput({
60
73
  value={selectSchema}
61
74
  onChange={(_v) => {
62
75
  setSelectSchema(_v || { type: 'string' });
63
- let content;
64
76
 
77
+ const schema = _v || { type: 'string' };
78
+ let content = typeManager.getDefaultValue(schema);
65
79
  if (_v?.type === 'object') {
66
80
  content = '{}';
67
81
  }
68
-
69
82
  if (_v?.type === 'array') {
70
83
  content = '[]';
71
84
  }
72
85
 
73
- if (_v?.type === 'boolean') {
74
- content = false;
75
- }
76
-
77
86
  onChange({
78
87
  type: 'constant',
79
88
  content,
80
- schema: _v || { type: 'string' },
89
+ schema,
81
90
  });
82
91
  }}
83
92
  readonly={readonly}
@@ -92,7 +101,7 @@ export function DynamicValueInput({
92
101
  <InjectVariableSelector
93
102
  style={{ width: '100%' }}
94
103
  value={value?.content}
95
- onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : undefined)}
104
+ onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : DEFAULT_VALUE)}
96
105
  includeSchema={includeSchema}
97
106
  readonly={readonly}
98
107
  />
@@ -107,16 +116,16 @@ export function DynamicValueInput({
107
116
  onChange={(_v) => onChange({ type: 'constant', content: _v, schema: constantSchema })}
108
117
  schema={constantSchema || { type: 'string' }}
109
118
  readonly={readonly}
110
- strategies={[...(constantProps?.strategies || [])]}
111
119
  fallbackRenderer={() => (
112
120
  <InjectVariableSelector
113
121
  style={{ width: '100%' }}
114
- onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : undefined)}
122
+ onChange={(_v) => onChange(_v ? { type: 'ref', content: _v } : DEFAULT_VALUE)}
115
123
  includeSchema={includeSchema}
116
124
  readonly={readonly}
117
125
  />
118
126
  )}
119
127
  {...constantProps}
128
+ strategies={[...(constantProps?.strategies || [])]}
120
129
  />
121
130
  );
122
131
  };
@@ -67,7 +67,18 @@ export function InputsValues({
67
67
  </UIRow>
68
68
  ))}
69
69
  </UIRows>
70
- <Button disabled={readonly} icon={<IconPlus />} size="small" onClick={() => add()}>
70
+ <Button
71
+ disabled={readonly}
72
+ icon={<IconPlus />}
73
+ size="small"
74
+ onClick={() =>
75
+ add({
76
+ type: 'constant',
77
+ content: '',
78
+ schema: { type: 'string' },
79
+ })
80
+ }
81
+ >
71
82
  {I18n.t('Add')}
72
83
  </Button>
73
84
  </div>
@@ -21,8 +21,9 @@ export function useChildList(
21
21
  onChange?: (value: any) => void
22
22
  ): {
23
23
  canAddField: boolean;
24
+ hasChildren: boolean;
24
25
  list: ListItem[];
25
- add: (key?: string) => void;
26
+ add: (defaultValue?: any) => void;
26
27
  updateKey: (id: string, key: string) => void;
27
28
  updateValue: (id: string, value: any) => void;
28
29
  remove: (id: string) => void;
@@ -50,8 +51,6 @@ export function useChildList(
50
51
  return undefined;
51
52
  }, [value]);
52
53
 
53
- console.log('debugger objectListValue', objectListValue);
54
-
55
54
  const { list, add, updateKey, updateValue, remove } = useObjectList<any>({
56
55
  value: objectListValue,
57
56
  onChange: (value) => {
@@ -60,8 +59,14 @@ export function useChildList(
60
59
  sortIndexKey: (value) => (FlowValueUtils.isFlowValue(value) ? 'extra.index' : ''),
61
60
  });
62
61
 
62
+ const hasChildren = useMemo(
63
+ () => canAddField && (list.length > 0 || Object.keys(objectListValue || {}).length > 0),
64
+ [canAddField, list.length, Object.keys(objectListValue || {}).length]
65
+ );
66
+
63
67
  return {
64
68
  canAddField,
69
+ hasChildren,
65
70
  list,
66
71
  add,
67
72
  updateKey,
@@ -47,7 +47,13 @@ export function InputsValuesTree(props: PropsType) {
47
47
  disabled={readonly}
48
48
  icon={<IconPlus />}
49
49
  size="small"
50
- onClick={add}
50
+ onClick={() => {
51
+ add({
52
+ type: 'constant',
53
+ content: '',
54
+ schema: { type: 'string' },
55
+ });
56
+ }}
51
57
  >
52
58
  {I18n.t('Add')}
53
59
  </Button>
@@ -3,12 +3,13 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import React, { useState } from 'react';
6
+ import React, { useMemo, useState } from 'react';
7
7
 
8
8
  import { I18n } from '@flowgram.ai/editor';
9
9
  import { IconButton, Input } from '@douyinfe/semi-ui';
10
10
  import { IconChevronDown, IconChevronRight, IconDelete } from '@douyinfe/semi-icons';
11
11
 
12
+ import { IFlowConstantValue } from '@/typings';
12
13
  import { ConstantInputStrategy } from '@/components/constant-input';
13
14
 
14
15
  import { PropsType } from './types';
@@ -64,14 +65,26 @@ export function InputValueRow(
64
65
  } = props;
65
66
  const [collapse, setCollapse] = useState(false);
66
67
 
67
- const { canAddField, list, add, updateKey, updateValue, remove } = useChildList(
68
+ const { canAddField, hasChildren, list, add, updateKey, updateValue, remove } = useChildList(
68
69
  value,
69
70
  onUpdateValue
70
71
  );
71
72
 
72
- const hasChildren = canAddField && list.length > 0;
73
+ const strategies = useMemo(
74
+ () => [...(hasChildren ? [AddObjectChildStrategy] : []), ...(constantProps?.strategies || [])],
75
+ [hasChildren, constantProps?.strategies]
76
+ );
73
77
 
74
- const flowDisplayValue = hasChildren ? { type: 'constant', schema: { type: ' object' } } : value;
78
+ const flowDisplayValue = useMemo(
79
+ () =>
80
+ hasChildren
81
+ ? ({
82
+ type: 'constant',
83
+ schema: { type: 'object' },
84
+ } as IFlowConstantValue)
85
+ : value,
86
+ [hasChildren, value]
87
+ );
75
88
 
76
89
  return (
77
90
  <>
@@ -101,10 +114,7 @@ export function InputValueRow(
101
114
  hasError={hasError}
102
115
  constantProps={{
103
116
  ...constantProps,
104
- strategies: [
105
- ...(hasChildren ? [AddObjectChildStrategy] : []),
106
- ...(constantProps?.strategies || []),
107
- ],
117
+ strategies,
108
118
  }}
109
119
  />
110
120
  <UIActions>
@@ -115,7 +125,11 @@ export function InputValueRow(
115
125
  theme="borderless"
116
126
  icon={<IconAddChildren />}
117
127
  onClick={() => {
118
- add();
128
+ add({
129
+ type: 'constant',
130
+ content: '',
131
+ schema: { type: 'string' },
132
+ });
119
133
  setCollapse(true);
120
134
  }}
121
135
  />
@@ -134,7 +134,7 @@ export function VariableTagInject() {
134
134
  // 基于 {{var}} 的正则进行匹配,匹配后进行自定义渲染
135
135
  useLayoutEffect(() => {
136
136
  const atMatcher = new MatchDecorator({
137
- regexp: /\{\{([^\}]+)\}\}/g,
137
+ regexp: /\{\{([^\}\{]+)\}\}/g,
138
138
  decoration: (match) =>
139
139
  Decoration.replace({
140
140
  widget: new VariableTagWidget({
@@ -32,6 +32,17 @@ export function InputsTree({ inputsValues }: { inputsValues: Record<string, IFlo
32
32
  return;
33
33
  }
34
34
 
35
+ /**
36
+ * When user input {{xxxx}}, {{{xxx}}}(more brackets if possible), replace all brackets with {{xxxx}}
37
+ */
38
+ let { from, to } = range;
39
+ while (editor.$view.state.doc.sliceString(from - 1, from) === '{') {
40
+ from--;
41
+ }
42
+ while (editor.$view.state.doc.sliceString(to, to + 1) === '}') {
43
+ to++;
44
+ }
45
+
35
46
  editor.replaceText({
36
47
  ...range,
37
48
  text: '{{' + variablePath + '}}',
@@ -5,13 +5,12 @@
5
5
 
6
6
  import React from 'react';
7
7
 
8
- import { IFlowValue } from '@/typings';
9
8
  import { PromptEditor, PromptEditorPropsType } from '@/components/prompt-editor';
10
9
 
11
10
  import { InputsTree } from './extensions/inputs-tree';
12
11
 
13
12
  interface PropsType extends PromptEditorPropsType {
14
- inputsValues: Record<string, IFlowValue>;
13
+ inputsValues: any;
15
14
  }
16
15
 
17
16
  export function PromptEditorWithInputs({ inputsValues, ...restProps }: PropsType) {
@@ -5,7 +5,7 @@
5
5
 
6
6
  import React, { useMemo } from 'react';
7
7
 
8
- import { last } from 'lodash';
8
+ import { isPlainObject, last } from 'lodash';
9
9
  import {
10
10
  type ArrayType,
11
11
  ASTMatch,
@@ -16,7 +16,7 @@ import {
16
16
  import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
17
17
  import { Tree } from '@douyinfe/semi-ui';
18
18
 
19
- import { IFlowValue } from '@/typings';
19
+ import { FlowValueUtils } from '@/shared';
20
20
 
21
21
  type VariableField = BaseVariableField<{ icon?: string | JSX.Element; title?: string }>;
22
22
 
@@ -24,7 +24,7 @@ export function InputsPicker({
24
24
  inputsValues,
25
25
  onSelect,
26
26
  }: {
27
- inputsValues: Record<string, IFlowValue>;
27
+ inputsValues: any;
28
28
  onSelect: (v: string) => void;
29
29
  }) {
30
30
  const available = useScopeAvailable();
@@ -76,23 +76,40 @@ export function InputsPicker({
76
76
  };
77
77
  };
78
78
 
79
- const treeData: TreeNodeData[] = useMemo(
80
- () =>
81
- Object.entries(inputsValues).map(([key, value]) => {
82
- if (value?.type === 'ref') {
83
- const variable = available.getByKeyPath(value.content || []);
79
+ const getTreeData = (value: any, keyPath: string[]): TreeNodeData | undefined => {
80
+ const currKey = keyPath.join('.');
84
81
 
85
- if (variable) {
86
- return renderVariable(variable, [key]);
87
- }
82
+ if (FlowValueUtils.isFlowValue(value)) {
83
+ if (FlowValueUtils.isRef(value)) {
84
+ const variable = available.getByKeyPath(value.content || []);
85
+ if (variable) {
86
+ return renderVariable(variable, keyPath);
88
87
  }
88
+ }
89
+ return {
90
+ key: currKey,
91
+ value: currKey,
92
+ label: last(keyPath),
93
+ };
94
+ }
89
95
 
90
- return {
91
- key,
92
- value: key,
93
- label: key,
94
- };
95
- }),
96
+ if (isPlainObject(value)) {
97
+ return {
98
+ key: currKey,
99
+ value: currKey,
100
+ label: last(keyPath),
101
+ children: Object.entries(value)
102
+ .map(([key, value]) => getTreeData(value, [...keyPath, key])!)
103
+ .filter(Boolean),
104
+ };
105
+ }
106
+ };
107
+
108
+ const treeData: TreeNodeData[] = useMemo(
109
+ () =>
110
+ Object.entries(inputsValues)
111
+ .map(([key, value]) => getTreeData(value, [key])!)
112
+ .filter(Boolean),
96
113
  []
97
114
  );
98
115
 
@@ -143,7 +143,7 @@ export function VariableTagInject() {
143
143
  // 基于 {{var}} 的正则进行匹配,匹配后进行自定义渲染
144
144
  useLayoutEffect(() => {
145
145
  const atMatcher = new MatchDecorator({
146
- regexp: /\{\{([^\}]+)\}\}/g,
146
+ regexp: /\{\{([^\}\{]+)\}\}/g,
147
147
  decoration: (match) =>
148
148
  Decoration.replace({
149
149
  widget: new VariableTagWidget({
@@ -30,8 +30,20 @@ export function VariableTree() {
30
30
  return;
31
31
  }
32
32
 
33
+ /**
34
+ * When user input {{xxxx}}, {{{xxx}}}(more brackets if possible), replace all brackets with {{xxxx}}
35
+ */
36
+ let { from, to } = range;
37
+ while (editor.$view.state.doc.sliceString(from - 1, from) === '{') {
38
+ from--;
39
+ }
40
+ while (editor.$view.state.doc.sliceString(to, to + 1) === '}') {
41
+ to++;
42
+ }
43
+
33
44
  editor.replaceText({
34
- ...range,
45
+ from,
46
+ to,
35
47
  text: '{{' + variablePath + '}}',
36
48
  });
37
49
 
@@ -86,12 +86,22 @@ export function TypeSelector(props: TypeSelectorProps) {
86
86
  []
87
87
  );
88
88
 
89
+ const isDisabled = readonly || disabled;
90
+
89
91
  return (
90
92
  <Cascader
91
- disabled={readonly || disabled}
93
+ disabled={isDisabled}
92
94
  size="small"
93
95
  triggerRender={() => (
94
- <IconButton size="small" style={style} disabled={readonly || disabled} icon={icon} />
96
+ <IconButton
97
+ size="small"
98
+ style={{
99
+ ...(isDisabled ? { pointerEvents: 'none' } : {}),
100
+ ...(style || {}),
101
+ }}
102
+ disabled={isDisabled}
103
+ icon={icon}
104
+ />
95
105
  )}
96
106
  treeData={options}
97
107
  value={selectValue}
package/src/index.ts CHANGED
@@ -10,3 +10,4 @@ export * from './typings';
10
10
  export * from './form-plugins';
11
11
  export * from './plugins';
12
12
  export * from './validate';
13
+ export * from './hooks';
@@ -14,7 +14,7 @@ export const extraSchema = z
14
14
 
15
15
  export const constantSchema = z.object({
16
16
  type: z.literal('constant'),
17
- content: z.union([z.string(), z.number(), z.boolean()]).optional(),
17
+ content: z.any().optional(),
18
18
  schema: z.any().optional(),
19
19
  extra: extraSchema,
20
20
  });
@@ -121,7 +121,7 @@ export namespace FlowValueUtils {
121
121
  */
122
122
  export function getTemplateKeyPaths(value: IFlowTemplateValue) {
123
123
  // find all keyPath wrapped in {{}}
124
- const keyPathReg = /{{(.*?)}}/g;
124
+ const keyPathReg = /\{\{([^\}\{]+)\}\}/g;
125
125
  return uniq(value.content?.match(keyPathReg) || []).map((_keyPath) =>
126
126
  _keyPath.slice(2, -2).split('.')
127
127
  );
@@ -13,7 +13,7 @@ export type FlowValueType = 'constant' | 'ref' | 'expression' | 'template';
13
13
 
14
14
  export interface IFlowConstantValue {
15
15
  type: 'constant';
16
- content?: string | number | boolean;
16
+ content?: any;
17
17
  schema?: IJsonSchema;
18
18
  extra?: IFlowValueExtra;
19
19
  }
@@ -3,10 +3,11 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { isNil, uniq } from 'lodash';
6
+ import { isNil } from 'lodash';
7
7
  import { FeedbackLevel, FlowNodeEntity, getNodeScope } from '@flowgram.ai/editor';
8
8
 
9
- import { IFlowTemplateValue, IFlowValue } from '@/typings';
9
+ import { IFlowValue } from '@/typings';
10
+ import { FlowValueUtils } from '@/shared';
10
11
 
11
12
  interface Context {
12
13
  node: FlowNodeEntity;
@@ -43,7 +44,7 @@ export function validateFlowValue(value: IFlowValue | undefined, ctx: Context) {
43
44
  }
44
45
 
45
46
  if (value?.type === 'template') {
46
- const allRefs = getTemplateKeyPaths(value);
47
+ const allRefs = FlowValueUtils.getTemplateKeyPaths(value);
47
48
 
48
49
  for (const ref of allRefs) {
49
50
  const variable = getNodeScope(node).available.getByKeyPath(ref);
@@ -58,16 +59,3 @@ export function validateFlowValue(value: IFlowValue | undefined, ctx: Context) {
58
59
 
59
60
  return undefined;
60
61
  }
61
-
62
- /**
63
- * get template key paths
64
- * @param value
65
- * @returns
66
- */
67
- function getTemplateKeyPaths(value: IFlowTemplateValue) {
68
- // find all keyPath wrapped in {{}}
69
- const keyPathReg = /{{(.*?)}}/g;
70
- return uniq(value.content?.match(keyPathReg) || []).map((_keyPath) =>
71
- _keyPath.slice(2, -2).split('.')
72
- );
73
- }