This manual details every aspect of the GLIDE Parser, including how the workflow DSL is structured, how references are extracted, how environment variables and events are integrated, and how input/output field types are strictly enforced.
parseWorkflow
API
GLIDE is a DSL for describing workflows that may be triggered by specific events
(file_upload
, manual
, etc.). Each workflow can contain:
integer
,
date
, file
, etc.
when
conditions referencing either previously executed steps, environment variables, or
event data fields.The parser orchestrates a multi-phase validation:
valibot
schemas).$
usage).$workflow
references.GelEngine
with known field paths for expression evaluation.
This manual covers everything from the top-level workflow shape down to
the smallest input
or output
field constraint rule.
A GLIDE workflow definition must conform to WorkflowSchema
:
Property | Description | Constraints |
---|---|---|
name |
Human-friendly workflow name. | Non-empty, up to 64 characters (trimmed). |
id |
Unique UUID for referencing the workflow. | Must parse as a valid UUID. |
compatibility |
ISO date/time indicating version/compatibility date. | Must parse as an isoDate in valibot . |
environment |
A record of key-value pairs for environment variables. | Optional. If present, must not contain references. Key limit: 16 total (enforced by
EnvironmentSchema ).
|
trigger |
An object describing event type (on ) and optional when condition. |
Based on TriggerSchema ; known event types (e.g. file_upload , manual ).
|
steps |
A map of step IDs to linear or fork step definitions. |
Optional. If present, must pass StepsSchema checks. Max 100 steps total (including nested
forks). |
lookup_table |
Reserved field for arbitrary usage or extension. | Not used by the parser. Free-form (any in schema). |
Steps can be linear or fork. We define LinearStep
and ForkStep
objects.
A linear step has uses
, input
, output
, when
, auth
,
etc.
A fork step has multiple branches
, each with a label
and when
.
The trigger
block indicates which EventType
the workflow listens to. Examples:
file_upload
: The workflow is triggered when a file is uploaded. The parser ensures
$workflow.file.*
references are valid if the user references them in expressions.
manual
: Manually triggered with arbitrary data
inside $workflow.payload
.
If workflow.trigger.on
does not match the event.type
you pass into
parseWorkflow
,
the parser throws a WorkflowParseError
.
By default, the DSL can reference $workflow.file
or $workflow.payload
only if it matches
the correct event type.
Each input field in a linear step can be one of many types, defined by
NarrowedFieldSchema
:
text
/ string
text
is semantically similar to string
, but can differ in
constraints or usage. Both yield JS strings.integer
number
number
in JS.
boolean
true
, false
, or string equivalents
("yes"
, "no"
, "1"
, "0"
, etc.).date
1679933567
), converting
them to ISO strings.duration
"5 minutes"
. This is stored as a number (ms)
internally.dictionary
string
(or templated) values, subject to constraints like max_size
,
min_value_length
, etc. in DictionaryConstraints
.
Yields a Record<string, string>
in JS.
password
{{ ... }}
allowed). Must be a literal,
non-empty string.file
Each field also has constraints via CommonConstraintsSchema
or
StringLikeConstraintsSchema
or NumberConstraints
, depending on the type. Examples:
allow_references
: If false
, no {{ ... }}
expressions can appear in
value
.
min_length
, max_length
: For string-based fields, sets length boundaries.min
, max
: For numeric fields, sets numeric range constraints.pattern
: For strings, a regex pattern must match (if specified).dictionary
-specific constraints like min_size
, max_size
,
min_value_length
, max_value_length
apply to keys and values.
Each field declares required: true|false
. If true
, value
must be present.
If false
, the field may be omitted, or it can have a default
value.
In the parser, a missing optional field is replaced by default
if provided; otherwise it’s just
undefined
.
The InputSchema
supports extendable
fields. If extendable: true
,
there is an extended
block with the same constraints, effectively merging new field definitions.
The same concept exists for OutputSchema
.
GLIDE allows referencing environment variables ($env.*
), workflow event data
($workflow.file.*
or $workflow.payload.*
),
and previously executed steps ($myStep.someOutput
).
The parser is responsible for ensuring these references are legal:
$forkId
)—only linear steps produce output that can be referenced.
workflow
or env
) outside known fields (based
on getKnownPaths
for events).
The function extractReferences
uses regex to find $
references in strings and function
calls.
The function checkReferencesInExpression
ensures each reference is permitted.
Steps are declared under workflow.steps
. The StepsSchema is quite strict:
countTotalSteps <= 100
: No more than 100 steps total (including nested in forks).fork
steps must be terminal at their level (no steps after a fork in the same
object) and can have at most 5 branches (fork width
limit).fork depth
limit of 3 nested forks maximum.^[a-z_]+$
ensures only lowercase letters + underscores up to length 32.Each linear step has:
uses
(string referencing an action, e.g. "org/repo@v1"
)label
(short display name)when
(optional GEL expression controlling whether step runs)auth
(optional: basic or bearer tokens)config
(optional: retries, timeouts, etc.)input
(must pass InputSchema
)output
(optional, must pass OutputSchema
)Each fork step has:
label
branches
: a record of branch IDs, each with:
label
when
expressionsteps
array, the first of which must be a linear step by schema rulesparseWorkflow
APIrawJson
is a string, parse it to an object.
If parsing fails, WorkflowParseError("Invalid JSON")
is thrown.parse(WorkflowSchema, parsedJson)
via valibot.
If there's a mismatch (missing name
, invalid id
, etc.), a ValiError
or
WorkflowParseError
arises.
event.type
must match
workflow.trigger.on
. Otherwise, WorkflowParseError
is thrown.
workflow.file
or workflow.payload
depending on the event
type.context.globalVariables
with
[ { name: "workflow", validPaths: [...] }, { name: "env", validPaths: [...] } ]
.
context.steps
as all linear step IDs that appear in the top-level or branch sequence,
plus forkIds
for any fork steps.{{ $... }}
references.when
, auth
fields, input
fields.
For forks, gather from branches[*].when
, etc.checkReferencesInExpression
ensures no self-reference or forward-reference, no fork
referencing, etc.WorkflowContext
:
state.env
is set from workflow.environment
.state.workflow.file
or state.workflow.payload
is set from event
(based on type).GelEngine
is created with environment fields plus
$workflow.file.*
/$workflow.payload.*
if needed.
WorkflowContext
object for runtime consumption.parseWorkflow
Signaturefunction parseWorkflow(
rawJson: string | object,
event: InferOutput<typeof WorkflowEventSchema>,
durable?: IWorkflowDurableExecutor
): WorkflowContext;
In addition to the steps above, if a fork is discovered, the parser does not attempt to reorder or flatten steps; it only ensures that after a fork, no further steps appear at that same level.
The parser can throw:
workflow.trigger.on
!= event.type
.WorkflowValidationIssue
objects summarizing issues (passed in the
issues
array).
valibot
if core schema checks fail (StepsSchema
constraints, etc.).Callers must catch these errors if they want custom handling. Otherwise, they bubble up.
string
Input Field{ "name": "MinimalExample", "id": "68cbb317-00bf-4eb1-9768-d8fc3d503e2e", "compatibility": "2025-01-30T00:00:00.000Z", "environment": { "HELLO": "Hello world" }, "trigger": { "on": "manual" }, "steps": { "init": { "kind": "linear", "uses": "myaction@v1", "label": "Init", "input": { "extendable": false, "fields": { "greeting": { "label": "Greeting", "help": "A simple greeting", "type": "string", "required": true, "value": "{{ $env.HELLO }}" } } } } } }
The parser ensures $env.HELLO
is valid and references an actual environment variable,
that init
is a linear step with required string
field, etc.
file_upload
Event// event => "file_upload" const fileEvent = { type: "file_upload", name: "hello.txt", dir: "/", path: "/hello.txt", size: 123, kind: "text", mime: "text/plain", tags: [], metadata: {} }; const workflow = { "name": "SplitFlow", "id": "ff7bc3c5-018c-4f2c-9559-857b466596d5", "compatibility": "2025-01-30T00:00:00.000Z", "trigger": { "on": "file_upload", "when": "$workflow.file.size lt 2000" }, "steps": { "check": { "kind": "linear", "uses": "check/file@v1", "label": "CheckFile", "input": { "extendable": false, "fields": { "filePath": { "label": "File Path", "help": "Path of the file", "type": "string", "required": true, "value": "{{ $workflow.file.path }}" } } } }, "fork1": { "kind": "fork", "label": "branchBySize", "branches": { "small": { "label": "SmallFile", "when": "$check.filePath eq '/hello.txt'", "steps": { "confirm": { "kind": "linear", "uses": "some_action@v2", "label": "ConfirmSmall", "input": { "extendable": false, "fields": { "info": { "label": "Info", "help": "Some message", "type": "string", "required": true, "value": "This is a small file!" } } } } } }, "other": { "label": "OtherFile", "when": "$check.filePath ne '/hello.txt'", "steps": { "handle": { "kind": "linear", "uses": "some_other_action@v3", "label": "HandleOther", "input": { "extendable": false, "fields": { "info": { "label": "Info", "help": "Some message", "type": "string", "required": true, "value": "This is some other file!" } } } } } } } } } };
The parser ensures no additional steps appear after fork1
at the same level. The branches are
validated:
small
and other
are valid branch IDs (^[a-z_]+$
).$check.filePath
are valid, referencing the check
step.Reference | Description | Link |
---|---|---|
valibot schemas | Describes how the parser validates JSON structures with valibot . |
View Code |
actions.ts | Integrates actionBuilder with parser references for typed input. |
View Code |