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:
- Module Declaration - Namespace for the file
- Imports - External module dependencies
- Constants - Metadata and configuration values
- 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:
| Variable | Description |
|---|---|
$@ | 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:
| Filter | Description | Example |
|---|---|---|
int | Convert to integer | `$value |
string | Convert to string | `$count |
bool | Convert to boolean | `$flag |
pint | Parse and convert to int | `$input |
pstring | Parse and convert to string | `$data |
option | Lookup from flow options | `$key |
strings | Convert to string slice | `$items |
parse | Parse template expressions | `$template |
property | Treat 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
- WSL Specification - Complete grammar and syntax reference
- Simplified WSL Syntax - Concise pipeline-style syntax
- Migration Guide - Migrating from JSON to WSL
- Nested Const Blocks - Complex configuration support
- Custom Workflow Types - Semantic workflow types
- Orchestration - Feature and solution orchestration
- Conditional Flows - Conditional transitions and flow control
- Examples - More workflow examples