Skip to main content

Workflow Specific Language (WSL)

The Workflow Specific Language (WSL) is a domain-specific language designed for defining workflows in Kuetix Engine. It provides a declarative syntax for creating complex state machines with clear transitions, conditions, and error handling.

Why WSL?

WSL was created to provide a more intuitive and maintainable way to define workflows compared to JSON-based definitions. Key benefits include:

  • Readable Syntax - Clear, English-like syntax that's easy to understand
  • Type Safety - Built-in filters for type conversion and validation
  • Modularity - Import and extend other modules
  • Powerful Expressions - Template syntax for dynamic values
  • Error Handling - First-class support for error states and recovery

Basic Structure

A WSL file consists of:

  1. Module Declaration - Namespace for the file
  2. Imports - External module dependencies
  3. Constants - Metadata and configuration values
  4. Workflow Definitions - One or more workflows with states
module example

import services/common

const {
event: "greet",
description: "A simple hello world workflow example",
version: "1.0.0"
}

workflow hello_world {
start: Hello

state Hello {
action converse/speak.Say(message: "Hello World")
on success -> Response
}

state Response {
action services/common/response.ResponseValue(statusCode: 200)
end ok
}
}

Key Elements

Module Declaration

module my.namespace

Declares the namespace for the file. This is used for organizing and referencing workflows.

Imports

import services/common
import auth/login

Imports bring other modules into scope, making their transitions available.

Extends

extends "resolvers"

Indicates inheritance of resolvers/configurations from another source.

Constants

const {
event: "my_event",
description: "My workflow",
version: "1.0.0"
}

Defines key/value constants accessible as $constants.* within the workflow.

Workflow Declaration

WSL supports multiple workflow types for different organizational levels:

// Standard workflow
workflow workflow_name {
start: InitialState
state InitialState {
// ...
}
}

// Feature (orchestrates multiple workflows)
feature feature_name {
start: RunWorkflow
state RunWorkflow {
action workflow my_workflow
on success -> Complete
}
state Complete {
end ok
}
}

// Solution (orchestrates features and workflows)
solution solution_name {
start: RunFeature
state RunFeature {
action feature my_feature
on success -> Complete
}
state Complete {
end ok
}
}

// Custom types
microservice my_service {
start: Initialize
// ...
}

Type Hierarchy:

  • workflow - Individual process flow
  • feature - Orchestrates multiple workflows
  • solution - Orchestrates features and workflows
  • custom - Any identifier (microservice, project, etc.)

All types share the same state-based structure but allow for semantic organization.

States

state StateName(Param1, Param2) {
action module/service.Method(arg1: value1, arg2: value2) as Result
on success -> NextState(Result)
on error -> ErrorState
}

States contain:

  • Parameters - Optional input parameters from previous states
  • Action - The transition handler to execute
  • Alias - Optional capture of the action result (as Result)
  • Transitions - Success/error branches to other states

Terminal States

state FinalState {
action some/action.Finish()
end ok // or: end error
}

Terminal states use end ok or end error to complete the workflow.

Implicit Terminal with _

Use _ as a transition target to implicitly reference the next state in sequence. When _ is used in a trailing on success transition (the last state), it automatically marks the state as a terminal success (end ok):

workflow pipeline {
start: Step1

state Step1 {
action step/one.Execute()
on success -> _ // resolves to Step2 (next state)
on error -> HandleError
}

state Step2 {
action step/two.Execute()
on success -> _ // last state: becomes implicit "end ok"
}

state HandleError {
action response/Error(message: "Failed")
end error
}
}

This reduces boilerplate in linear workflows where states flow sequentially.

Expressions

References

Reference values using the $ prefix:

$User.ID // Access nested property
$constants.event // Access constant
$ResponseAsValue // Access captured result

Special $ Variables

WSL supports special symbol-rich $ variables that use non-alphanumeric characters. These provide access to runtime context and metadata:

VariableDescription
$@All arguments / full context
$?Last execution result / status
$^Parent context / caller reference

$-prefixed variables may also contain symbols like !, #, %, ^, &, *, _, -, +, =, ~, and ? in their names, enabling expressive variable naming:

action some/action.Do(context: $@, status: $?, parent: $^)

Template Syntax

Use <<...>> for dynamic value resolution:

action some/action.Do(value: "<<helpers.Id(Req.Email)>>")

Built-in Filters

Apply filters to values using the | operator:

statusCode: $value|int
"<<count|int>>"
age: $input|pint

Available Filters:

FilterDescriptionExample
intConvert to integer`$value
stringConvert to string`$count
boolConvert to boolean`$flag
pintParse and convert to int`$input
pstringParse and convert to string`$data
optionLookup from flow options`$key
stringsConvert to string slice`$items
parseParse template expressions`$template
propertyTreat value as property key`$name

Object Literals

Pass inline objects:

v: {response: $constants.event, statusCode: 202}

Nested Objects and Arrays

WSL supports complex nested data structures:

Arrays:

const {
colors: ["red", "green", "blue"],
matrix: [[1, 2], [3, 4]],
users: [
{name: "Alice", age: 30},
{name: "Bob", age: 25}
]
}

Nested Objects:

const {
database: {
connection: {
host: "localhost",
port: 5432
},
pool: {min: 5, max: 20}
}
}

Accessing Nested Data:

// Dot notation for objects
$constants.database.connection.host

// Index notation for arrays
$constants.colors.0 // "red"
$constants.users.0.name // "Alice"
$constants.matrix.1.0 // 3

Arrays and objects can be nested to arbitrary depth and support all WSL value types (strings, numbers, booleans, references, objects, and arrays).

Hello World Example

Here's the complete Hello World workflow from the repository:

module example

import services/common

const {
event: "greet",
description: "A simple hello world workflow example",
version: "1.0.0"
}

workflow wsl_hello_world {
start: Hello

state Hello {
action converse/speak.Say(on: message, v: {response: $constants.event, statusCode: 202}) as ResponseAsValue
on success -> Response(ResponseAsValue)
}

state Response(ResponseAsValue) {
action services/common/response.ResponseValue(message: $ResponseAsValue.message, statusCode: $ResponseAsValue.message.statusCode|int)
end ok
}
}

More Complex Example

A login workflow with multiple branches:

module auth.login
import services/common

workflow user_login {
start: Request

state Request {
action parameters/Request(type: "login") as Req
on success -> Lookup(Req)
}

state Lookup(Req) {
action workflow/repositorium/user/get(id: "<<helpers.Id(Req.Email)>>") as User
on success -> CheckPassword(Req, User)
on error -> Fail
}

state CheckPassword(Req, User) {
action login/CheckPassword(request: Req, user: User)
on success -> EncryptedId(User)
on error -> Invalid
}

state EncryptedId(User) {
action login/EncryptedId(user: User) as Enc
on success -> GenerateToken(User, Enc)
}

state GenerateToken(User, Enc) {
action login/GenerateToken(user: User) as Token
on success -> Persist(User)
on error -> TokenGenerationFailed
}

state Persist(User) {
action workflow/repositorium/user/set(id: $User.ID, entity: "user")
on success -> Respond(User, Token)
on error -> Respond(User, Token)
}

state Respond(User, Token) {
action response/ResponseEntity(entityName: "user", loginResponse: Token)
end ok
}

state Invalid {
action response/ResponseError(message: "Invalid credentials", code: 401)
end error
}

state TokenGenerationFailed {
action response/ResponseError(message: "Failed to generate access or refresh token", code: 500)
end error
}

state Fail {
action response/ResponseError(message: "Lookup failed", code: 404)
end error
}
}

IDE Support

Official editor plugins are available for both .wsl and .swsl files:

  • JetBrains IDEs (any IntelliJ Platform 2023.3+ IDE — IntelliJ IDEA, GoLand, WebStorm, PyCharm, etc.) — syntax highlighting, keyword and module/method completion, brace matching, comment toggling, structure view, color settings page.
  • Visual Studio Code (1.75+) — syntax highlighting, keyword and module/method completion, snippets, brace matching, auto-closing pairs, file icons.

Both plugins load module definitions from modules.json for context-aware completion. See IDE Plugins for installation and feature details.

Advanced Features

Simplified Syntax

For simple workflows, use the concise pipeline-style syntax:

import services/common

const {
msg: "Hello World",
code: 200
}

// Define error handler
def services/common/errors.OnAnyError(msg: "error", statusCode: 400) as errorHandler -> services/common/response.ResponseValue(message: $error.message, statusCode: 500) -> .

// Main action flow
converse/speak.Say(on: "message", v: $constants.msg) as response <- errorHandler -> services/common/response.ResponseValue(message: $response.message, statusCode: $constants.code) -> .

Learn more: Simplified WSL Syntax

Nested Const Blocks

WSL supports complex nested configurations in const blocks with objects and arrays:

const {
event: "data_process",
api: {
baseUrl: "https://api.example.com",
timeout: 30000,
endpoints: {
users: "/api/v1/users",
data: "/api/v1/data"
}
},
processing: {
batchSize: 100,
stages: ["validate", "transform", "load"]
}
}

Learn more: Nested Const Blocks

Custom Workflow Types

Use semantic type names instead of just workflow:

feature user_management {
start: Initialize
// ...
}

solution ecommerce_platform {
start: Setup
// ...
}

pipeline data_ingestion {
start: Extract
// ...
}

Learn more: Custom Workflow Types

Orchestration

Features and solutions can orchestrate multiple workflows with shared context:

feature data_processing {
start: Extract

state Extract {
action workflow extract_data
on success -> Transform
}

state Transform {
action workflow transform_data
on success -> Complete
}

state Complete {
end ok
}
}

Learn more: Orchestration & Runners

Conditional Flows

Control execution flow with conditions and branching:

state ValidateData {
action validator.Check(data: $input) as Result
on success when $Result.isValid -> ProcessData
on success when $Result.isValid == false -> HandleInvalid
}

Learn more: Conditional Flows

Learn More