Skip to content

Commit be60d91

Browse files
authored
Merge pull request #92 from code0-tech/feat/#45
Adding SubFlow logic
2 parents e417e58 + 1811be3 commit be60d91

10 files changed

Lines changed: 103 additions & 70 deletions

File tree

entrypoint/mapper.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
NodeParameter,
88
NodeParameterValue,
99
ParameterDefinition,
10+
SubFlowValue,
11+
SubFlowValueSetting,
1012
} from "@code0-tech/sagittarius-graphql-types";
1113
import {
1214
DefinitionDataType,
@@ -16,6 +18,8 @@ import {
1618
ParameterDefinition as TucanaParameterDefinition,
1719
FlowSetting as TucanaFlowSetting,
1820
ValidationFlow,
21+
SubFlow as TucanaSubFlow,
22+
SubFlowSetting as TucanaSubFlowSetting,
1923
} from "@code0-tech/tucana/shared";
2024
import {toAllowedValue} from "@code0-tech/tucana/helpers";
2125

@@ -55,15 +59,15 @@ function mapFlow(flow: ValidationFlow): Flow {
5559
function mapFlowSetting(flowSetting: TucanaFlowSetting): FlowSetting {
5660
return {
5761
__typename: "FlowSetting",
58-
id: gid('FlowSetting', flowSetting.databaseId) as FlowSetting['id'],
62+
id: gid('FlowSetting', flowSetting.databaseId!) as FlowSetting['id'],
5963
flowSettingIdentifier: flowSetting.flowSettingId,
6064
value: flowSetting.value ? toAllowedValue(flowSetting.value) : null,
6165
}
6266
}
6367

6468
function mapNodeFunction(nodeFunction: TucanaNodeFunction): NodeFunction {
6569
return {
66-
id: gid('NodeFunction', nodeFunction.databaseId) as NodeFunction['id'],
70+
id: gid('NodeFunction', nodeFunction.databaseId!) as NodeFunction['id'],
6771
functionDefinition: {
6872
identifier: nodeFunction.runtimeFunctionId
6973
},
@@ -102,11 +106,8 @@ function mapNodeParameter(nodeParameter: TucanaNodeParameter): NodeParameter {
102106
value.parameterIndex = Number(target.inputType.parameterIndex)
103107
value.inputIndex = Number(target.inputType.inputIndex)
104108
}
105-
} else if (nodeParameterValue?.oneofKind === 'nodeFunctionId') {
106-
value = {
107-
__typename: 'NodeFunctionIdWrapper',
108-
id: gid('NodeFunction', nodeParameterValue.nodeFunctionId) as NodeFunction['id']
109-
}
109+
} else if (nodeParameterValue?.oneofKind === 'subFlow') {
110+
value = mapSubFlow(nodeParameterValue.subFlow);
110111
}
111112

112113
return {
@@ -131,6 +132,34 @@ function mapParameterDefinition(parameterDefinition: TucanaParameterDefinition):
131132
}
132133
}
133134

135+
function mapSubFlow(subFlow: TucanaSubFlow): SubFlowValue {
136+
const value: SubFlowValue = {
137+
__typename: 'SubFlowValue',
138+
signature: subFlow.signature,
139+
settings: subFlow.settings.map(mapSubFlowSetting)
140+
};
141+
142+
if (subFlow.executionReference.oneofKind === 'startingNodeId') {
143+
value.startingNodeId = gid('NodeFunction', subFlow.executionReference.startingNodeId) as NodeFunction['id'];
144+
} else if (subFlow.executionReference.oneofKind === 'functionIdentifier') {
145+
value.functionDefinition = {
146+
identifier: subFlow.executionReference.functionIdentifier
147+
} as FunctionDefinition;
148+
}
149+
150+
return value;
151+
}
152+
153+
function mapSubFlowSetting(setting: TucanaSubFlowSetting): SubFlowValueSetting {
154+
return {
155+
__typename: 'SubFlowValueSetting',
156+
identifier: setting.identifier,
157+
defaultValue: setting.defaultValue ? toAllowedValue(setting.defaultValue) : null,
158+
optional: setting.optional,
159+
hidden: setting.hidden
160+
};
161+
}
162+
134163
function mapDataType(dataType: DefinitionDataType): DataType {
135164
return {
136165
identifier: dataType.identifier,

entrypoint/package-lock.json

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

entrypoint/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"dependencies": {
66
"@code0-tech/triangulum": "file:../",
7-
"@code0-tech/tucana": "0.0.68",
7+
"@code0-tech/tucana": "0.0.72",
88
"@protobuf-ts/runtime": "^2.11.1"
99
},
1010
"devDependencies": {

gem/Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ GEM
101101
rubyzip (2.4.1)
102102
stringio (3.2.0)
103103
tsort (0.2.0)
104-
tucana (0.0.67)
104+
tucana (0.0.72)
105105
grpc (~> 1.64)
106106
unicode-display_width (3.2.0)
107107
unicode-emoji (~> 4.1)

gem/spec/support/protobuf_factories.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ def reference_node(node_id:, paths: [])
2929
end
3030

3131
def node_function_value(node_id)
32-
Tucana::Shared::NodeValue.new(node_function_id: node_id)
32+
Tucana::Shared::NodeValue.new(
33+
sub_flow: Tucana::Shared::SubFlow.new(
34+
starting_node_id: node_id
35+
)
36+
)
3337
end
3438

3539
def node(id:, function_id:, parameters: [], next_node_id: nil)

gem/triangulum.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
2424
spec.add_dependency 'base64', '~> 0.3'
2525
spec.add_dependency 'json', '~> 2.19'
2626
spec.add_dependency 'open3', '~> 0.2'
27-
spec.add_dependency 'tucana', '~> 0.0', '>= 0.0.67'
27+
spec.add_dependency 'tucana', '~> 0.0', '>= 0.0.72'
2828

2929
spec.add_development_dependency 'irb', '~> 1.17'
3030
spec.add_development_dependency 'rake', '~> 13.0'

package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"build": "vite build"
2424
},
2525
"devDependencies": {
26-
"@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2531971636-6b64e2c0adb50c5e7b78390e5fcd72c807e694b9",
26+
"@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2554302654-2b34b39821c688ebf0e1067f2aee8b42274e2a44",
2727
"@types/node": "^25.6.0",
2828
"@typescript/vfs": "^1.6.4",
2929
"typescript": "^6.0.3",
@@ -33,7 +33,7 @@
3333
"lossless-json": "^4.3.0"
3434
},
3535
"peerDependencies": {
36-
"@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2531971636-6b64e2c0adb50c5e7b78390e5fcd72c807e694b9",
36+
"@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2554302654-2b34b39821c688ebf0e1067f2aee8b42274e2a44",
3737
"@typescript/vfs": "^1.6.4",
3838
"typescript": "^5.9.3 || ^6.0.2",
3939
"lossless-json": "^4.3.0"

src/utils.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import {
44
Flow,
55
FunctionDefinition,
66
NodeFunction,
7-
NodeFunctionIdWrapper,
7+
SubFlowValue,
88
NodeParameter,
9-
ReferenceValue
9+
ReferenceValue, Maybe
1010
} from "@code0-tech/sagittarius-graphql-types";
1111
import ts from "typescript";
1212
import {createSystem, createVirtualTypeScriptEnvironment, VirtualTypeScriptEnvironment} from "@typescript/vfs"
@@ -109,20 +109,20 @@ export function generateFlowSourceCode(
109109
): string {
110110
const nodes = flow?.nodes?.nodes || [];
111111
const funcMap = new Map(functions?.map(f => [f.identifier, f]));
112-
const visited = new Set<string>();
112+
const visited = new Set<NodeFunction['id']>();
113113

114-
const generateNodeCode = (nodeId: string, indent: string = ""): string => {
115-
const node = nodes.find(n => n?.id === nodeId);
114+
const generateNodeCode = (id: NodeFunction['id'] | FunctionDefinition['identifier'], indent: string = ""): string => {
115+
const node = nodes.find(n => n?.id === id);
116116
if (!node || !node.functionDefinition) return "";
117-
visited.add(nodeId);
117+
if (id?.includes("NodeFunction")) visited.add(id as NodeFunction['id']);
118118

119119
const funcDef = funcMap.get(node.functionDefinition.identifier);
120120
if (!funcDef) return `${indent}// Error: Function ${node.functionDefinition.identifier} not found\n`;
121121

122122
const params = (node.parameters?.nodes as NodeParameter[]) || [];
123123
const args = params.map((p, index) => {
124124
const val = p.value;
125-
if (!val) return isForInference ? `/* @pos ${nodeId} ${index} */ {}` : `/* @pos ${nodeId} ${index} */ undefined`;
125+
if (!val) return isForInference ? `/* @pos ${id} ${index} */ {}` : `/* @pos ${id} ${index} */ undefined`;
126126
if (val.__typename === "ReferenceValue") {
127127
const ref = val as ReferenceValue;
128128
let refCode = typeof ref.inputIndex === "number"
@@ -131,19 +131,19 @@ export function generateFlowSourceCode(
131131
ref.referencePath?.forEach(pathObj => {
132132
refCode += `?.${pathObj.path}`;
133133
});
134-
return `/* @pos ${nodeId} ${index} */ ${refCode}`;
134+
return `/* @pos ${id} ${index} */ ${refCode}`;
135135
}
136136
if (val.__typename === "LiteralValue") {
137137
const jsonString = stringify(val?.value)
138-
return `/* @pos ${nodeId} ${index} */ ${jsonString}`;
138+
return `/* @pos ${id} ${index} */ ${jsonString}`;
139139
}
140-
if (val.__typename === "NodeFunctionIdWrapper") {
141-
const wrapper = val as NodeFunctionIdWrapper;
142-
const lambdaArgName = `p_${sanitizeId(nodeId)}_${index}`;
143-
const subTreeCode = generateNodeCode(wrapper.id!, indent + " ");
144-
return `/* @pos ${nodeId} ${index} */ (...${lambdaArgName}) => {\n${subTreeCode}${indent}}`;
140+
if (val.__typename === "SubFlowValue") {
141+
const wrapper = val as SubFlowValue;
142+
const lambdaArgName = `p_${sanitizeId(id as string)}_${index}`;
143+
const subTreeCode = generateNodeCode(wrapper.startingNodeId || wrapper.functionDefinition?.id!, indent + " ");
144+
return `/* @pos ${id} ${index} */ (...${lambdaArgName}) => {\n${subTreeCode}${indent}}`;
145145
}
146-
return isForInference ? `/* @pos ${nodeId} ${index} */ {}` : `/* @pos ${nodeId} ${index} */ undefined`;
146+
return isForInference ? `/* @pos ${id} ${index} */ {}` : `/* @pos ${id} ${index} */ undefined`;
147147
});
148148

149149
const varName = `node_${sanitizeId(node.id!)}`;
@@ -155,21 +155,21 @@ export function generateFlowSourceCode(
155155
let code = `${indent}`;
156156

157157
if (node.functionDefinition.identifier === "std::control::return") {
158-
code += `return /* @pos ${nodeId} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
158+
code += `return /* @pos ${id} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
159159
} else if (node.functionDefinition.identifier === "std::control::if") {
160-
code += `const ${varName} = /* @pos ${nodeId} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
160+
code += `const ${varName} = /* @pos ${id} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
161161
code += `if(${args[0]}) {
162-
${generateNodeCode((node.parameters?.nodes?.[1]?.value as NodeFunctionIdWrapper)?.id!, indent + " ")}
162+
${generateNodeCode(((node.parameters?.nodes?.[1]?.value as SubFlowValue)?.startingNodeId || (node.parameters?.nodes?.[1]?.value as SubFlowValue)?.functionDefinition?.identifier), indent + " ")}
163163
}`
164164
} else if (node.functionDefinition.identifier === "std::control::if_else") {
165-
code += `const ${varName} = /* @pos ${nodeId} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
165+
code += `const ${varName} = /* @pos ${id} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
166166
code += `if(${args[0]}) {
167-
${generateNodeCode((node.parameters?.nodes?.[1]?.value as NodeFunctionIdWrapper)?.id!, indent + " ")}
167+
${generateNodeCode(((node.parameters?.nodes?.[1]?.value as SubFlowValue)?.startingNodeId || (node.parameters?.nodes?.[1]?.value as SubFlowValue)?.functionDefinition?.identifier), indent + " ")}
168168
} else {
169-
${generateNodeCode((node.parameters?.nodes?.[2]?.value as NodeFunctionIdWrapper)?.id!, indent + " ")}
169+
${generateNodeCode(((node.parameters?.nodes?.[2]?.value as SubFlowValue)?.startingNodeId || (node.parameters?.nodes?.[2]?.value as SubFlowValue)?.functionDefinition?.identifier), indent + " ")}
170170
}`
171171
} else {
172-
code += `const ${varName} = /* @pos ${nodeId} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
172+
code += `const ${varName} = /* @pos ${id} null */ ${funcName}(${args.join(", ")})${needsAnyCast ? "" : ""} ;\n`
173173
}
174174

175175
if (node.nextNodeId) code += generateNodeCode(node.nextNodeId, indent);
@@ -181,9 +181,9 @@ export function generateFlowSourceCode(
181181
const funcDeclarations = functions?.map(f => `declare function fn_${f.identifier?.replace(/::/g, '_')}${f.signature}`).join('\n');
182182

183183
const nextNodeIds = new Set(nodes.map(n => n?.nextNodeId).filter(id => !!id));
184-
const subTreeIds = new Set<string>();
185-
nodes.forEach(n => n?.parameters?.nodes?.forEach((p: any) => {
186-
if (p?.value?.__typename === "NodeFunctionIdWrapper" && p.value.id) subTreeIds.add(p.value.id);
184+
const subTreeIds = new Set<NodeFunction['id'] | FunctionDefinition['id']>();
185+
nodes.forEach(n => n?.parameters?.nodes?.forEach((p: Maybe<NodeParameter>) => {
186+
if (p?.value?.__typename === "SubFlowValue" && (p.value.startingNodeId || p.value.functionDefinition?.id)) subTreeIds.add(p.value.startingNodeId || p.value.functionDefinition?.id);
187187
}));
188188

189189
const flowCode = flow ? `const flow_${sanitizeId(flow.id ?? "")} = /* @pos null null */ flow(${flow.settings?.nodes?.map((setting, index) => `/* @pos null ${index} */ ${stringify(setting?.value)}`).join(", ") ?? ""});` : ""

0 commit comments

Comments
 (0)