Parser API
The real power comes from the result of the parsing. You will get everything you need to use the graphs.
The following is the parsed configuration of the following code:
import { parse } from '@jstream/parser'
console.log(parse('flow1'))
{
"splitters": {
"extends": "/"
},
"flows": [
{
"id": "4623",
"name": "flow1",
"defaultNodeIndex": 0,
"graph": [
{
"path": ["flow1"],
"childrenIndexes": [],
"parentsIndexes": []
}
],
"pathsGroups": [["4624"]]
}
]
}
Flow Type
// https://stackoverflow.com/questions/56877697/get-all-combinations-of-a-type/56877972#56877972
// type Combinations = all combinations of a type (including an empty type)
type ParsedFlow = {
id: string
hasPredefinedName: boolean
name: string
pathsGroups: string[][]
graph: {
path: string[]
childrenIndexes: number[]
parentsIndexes: number[]
}[]
} & (
| {}
| {
defaultNodeIndex: number
}
| {
extendedFlowIndex: number
}
| {
extendedFlowIndex: number
defaultNodeIndex: number
})
id
In production build of the parser library, it will be a valid uui-id using uuid library. For development builds of the parser library, it will be an incremented numbers (for better debugging). Unless you are a contribe to this library right now, you will work with uuids.
name & hasPredefinedName
The name of the flow. If you didn't provide a name, parser will try to infer the name:
If the graph has a single node, parser will check if there isn't an already defined flow with that node-name. if not, the name of this flow will be equal to it's single node's name.
If the parser infered a name, hasPredefinedName=true
. Else, the parser will
prodvide a random (unique) name and hasPredefinedName=false
.
defaultNodeIndex
parser will infer (if possible) the default-node that will be used when using this flow in other flows. For example:
[
{
name: 'flow1',
graph: 'a:b',
default_path: 'b',
},
'flow1:flow2', // same as "flow1/b:flow2
]
flow1
will be parse to:
{
id: '5029',
name: 'flow1',
defaultNodeIndex: 1,
graph: [
{
path: ['flow1', 'a'],
childrenIndexes: [1],
parentsIndexes: [],
},
{
path: ['flow1', 'b'],
childrenIndexes: [],
parentsIndexes: [0],
},
],
// ...
}
We specified that b
will be the default node when writing flow1
in other flows. so flow1
equals to flow1/b
. flow1/b
's index (defaultNodeIndex
) is 1
.
The infering algorithm can do much more than that. For more information about default_path
, please read in the docs.
graph
An array of nodes.
Each node contains:
path
- contains all the flows that this node is a member in. path[0]
will contain the flow's name if it has one.
childrenIndexes
- children node indexes.
parentsIndexes
- parents node indexes.
pathsGroups
This feature is the opposite of the path
property of each node.
while the path
of each nodes tells me which flows a node belongs,
this feature tells us which nodes each appearance of a flow consist.
Each node belongs to multiple flows. the name of those flows is listed in the node.path
array. Let's have a look at the following configuration:
[
{
"name": "base",
"graph": "base1:base2",
"default_path": "base2",
"extends_flows": [
{
"name": "flow1",
"graph": "a:b",
"default_path": "b"
},
{
"name": "flow2",
"graph": "c:d",
"default_path": "d"
},
"flow1:flow2"
]
}
]
"flow1:flow2"
produce the following graph:
Let's have a different look at this graph:
At the above image, we see nodes and edges as the last image but we also see which flows, each node belongs in a clear way.
For example, node l
belongs to flows: base
, base1
, flow1
, a
.
if we convert each of those flows to numbers, then this node belogs
to flows: [1,2,3,4]
(the order of this array is important; from the top
most flow to the most inner flow).
If we create an array such that each cell will be an array representing the numbers of the flows as we just did, we will get the following matrix:
At this point we can conclude the followings:
- Each flow may appear multiple times at the same flow.
For example.
flow2
appears multiple times inbase
. - With the help of the
graph
array and the node'schildrenIndexes
andparentsIndexes
, we can know which nodes belongs to each appearance of each flow. For example,flow2
appears 2 times at the graph:- The nodes lll,lV are members of the first appearance of
flow2
in the graph. - The nodes Vll,lX are members of the second appearance of
flow2
in the graph.
- The nodes lll,lV are members of the first appearance of
Still, there are alot of questions to answer, but first let's talk about one practical use case of this feature:
flower package is a redux-extentsion for managing functions.
Every node can be a function. If a fucntion is async, we can run the same function
multiple times concurrently. But how can we contorl the concurrency level of each
function? Well, thats easy. As every node belongs to multiple flows, so is a function.
All we have to do it to specify every flow's max-concurrency-level and with
the help of pathsGroups
, we can know how much functions
(different and the same functions) we are running at the
same time in every appearance of every flow (remmber: pathsGroups
will give us the
abolity to know which functions belongs to a specific appearance of a specific flow).
Implementations notes about pathsGroups
:
- The numbers in the last image will be replaced by uuids in production build of parser.
- The same numbers (dev build of parser)/ uuids (prod build of parser) will be given to every different appearance of every flow - same as the image above: Each appearance of flow2 received a different number.
- You may think that
pathsGroups
can be generated, also, by creating a matrix from all the node's path property. Well, you can't. First of all,pathsGroups
can tell you where are the different appearance of every flow while you can't get it in other ways. Secondly,pathsGroups
gives the same appearance of every flow the same unique uuid so you can know where it start and where it ends. (to know where it start: go to the parent recorsivly and stop when you are looking at a node that it's value inpathsGroups
is different then all the values of it's parents inpathsGroups
- thats because every flow has a single entry point!). For more explaination, please open a github issue/ contact me in other ways.
Extensions
The parser support additional properties which can be passed to the inital configuration that is sent to the parser.