Skip to content

Concepts

Expeditions

An Esquie expedition is a collection of one or more steps that define a workflow to be executed. It is usually defined in YAML, but it can also be written in TypeScript for more advanced use cases.

yaml
# These properties are displayed in User Interfaces
_: Expedition
description: |
  For those who come after!

# The form defines the inputs that will be requested to the user
form:
  expedition_number:
    type: number
    default: 33
    required: true

# The steps define the actions to be taken in this expedition
steps:
  - _: Say hi
    log: Bonjour, mes amis de l'expédition ${expedition_number} !

Steps

A step is a single action within an expedition. It consists of a module, along with additional properties that describe how the step should be executed.

yaml
- _: Say hi
  if: ${expedition_number >= 33}
  log: Bonjour, mes amis de l'expédition ${expedition_number} !
  with:
    cwd: /tmp
    timeout: 30s
  let:
    expedition_number: ${Math.floor(Math.random() * 100)}

Modules

An EcmaScript module (simply referred to as module) is a reusable piece of code that performs a specific task.

Esquie comes with a set of built-in modules, but additional modules can be imported from many sources:

  • file: for local files (may also be relative or absolute paths)
  • http:/https: for remote files
  • npm: for npm packages
  • jsr: for packages from jsr.io
yaml
- _: Say hi
  https://raw.githubusercontent.com/esquie/example/main/mod.ts:
    expedition_number: 33
  with:
    permissions:
      import: raw.githubusercontent.com

TIP

Since importing and executing third-party code can be risky, Esquie executes modules in a secure sandboxed environment.

Module inputs and outputs validation

To ensure operability, Esquie requires modules to explicitly declare the shape of their expected inputs and outputs using Zod schemas.

Esquie will automatically template, parse, default and validate inputs before passing them to the module's handler function. Similarly, it will validate the outputs returned by the handler function before making them available to subsequent steps.

IMPORTANT

Because of the way sandboxing works, module outputs must be serializable with the structured clone algorithm (which supports most common data types).

Templating

To make expeditions dynamic and adaptable to different situations, Esquie supports templating using the same syntax as EcmaScript template literals.

yaml
- _: Say hi
  log: Bonjour, mes amis de l'expédition ${expedition_number} !
  let:
    expedition_number: ${Math.floor(Math.random() * 100)}

Esquie actually evaluates template expressions using JavaScript, offering access to its full range of language features.

There is however a few rules about how templating works in Esquie:

  • Only strings are templatized
    • The backtick delimiters (`) are implicit
    • Use backslashes (\${}) to escape template expressions
  • Wrapping the expression entirely in ${} preserves result type
    • Meaning the result of a templated expression can also be used for non-string properties
  • Templating applies recursively
    • However, templated results are not re-templatized, even if they contain template expressions.
  • Templating applies in the order keys are declared
    • In some cases, it may be possible for expressions to reference previously templatized properties.
    • for and let are exemptions to this rule, as they are always templatized first (in this order).
  • Templating is limited to their current step permissions
    • They can only perform operations allowed by the current step's permissions.

TIP

It is good practice to not let template expressions modify any state or have any observable side effects.

Sandboxing

Each step is executed in a separate Web Worker thread which isolates it from the main thread and other steps, while fine-grained permissions control ensures it can only perform actions it is explicitly allowed to.

Expeditions are run without any permissions by default, meaning you must explicitly grant permissions to steps that need them. Sub-steps inherit their parent's permissions and may be run with a subset of them, but never with more permissions than their parent.

yaml
- _: Download example.com homepage
  fetch.download:
    url: https://example.com
    path: /tmp/example.html
  with:
    cwd: /tmp
    permissions:
      read: true
      net: example.com
      write: /tmp

WARNING

These restrictions are enforced by the runtime itself, and thus, they cannot be bypassed by userland code by any means.

For more information, refer to Deno permissions system.

Released under the MIT License.