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​