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 / stringtext is semantically similar to string, but can differ in
constraints or usage. Both yield JS strings.integernumbernumber in JS.
booleantrue, false, or string equivalents
("yes", "no", "1", "0", etc.).date1679933567), converting
them to ISO strings.duration"5 minutes". This is stored as a number (ms)
internally.dictionarystring (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:
labelbranches: a record of branch IDs, each with:
labelwhen 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 |