Jstream

Jstream

  • Docs
  • API
  • Help
  • Editor
  • Blog

›Parser

Parser

  • Parser API

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:

  1. Each flow may appear multiple times at the same flow. For example. flow2 appears multiple times in base.
  2. With the help of the graph array and the node's childrenIndexes and parentsIndexes, 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.

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, pathsGroupsgives 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 in pathsGroups is different then all the values of it's parents in pathsGroups - 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.

Last updated on 8/24/2019 by stavalfi
  • Flow Type
  • id
  • name & hasPredefinedName
  • defaultNodeIndex
  • graph
  • pathsGroups
  • Extensions
Jstream
Docs
Getting Started (or other categories)Guides (or other categories)API Reference (or other categories)
Community
User ShowcaseStack OverflowProject ChatTwitter
More
BlogGitHubStar
Facebook Open Source
Copyright © 2019 Stav Alfi