workflow/PSL (Pop Script Language)

5.5 PSL (Pop Script Language)

In most visual workflow cases, you can achieve your goals using drag‑and‑drop nodes + parameter configuration.
However, in certain scenarios, you may still need:

  • More fine‑grained data processing (filtering, aggregation, mapping, renaming fields)
  • Combining outputs from multiple nodes using complex rules
  • Flexible control of global variables
  • Writing custom “if … then …” logic

To handle these cases, Pop provides PSL (Pop Script Language):

A lightweight scripting “glue language” that runs inside workflows.
Its syntax resembles TypeScript/JavaScript,
but with special runtime injections such as input, nodes, and globals — designed specifically for workflow data operations.


1. Overview of the Script Runtime Environment

Inside nodes that support scripting (e.g., Script Node, Custom Expression, Conditional Script), PSL injects these common objects during execution:

Identifier Type / Meaning Typical Usage
input Current node’s input set (list of NodeOutput) Read upstream outputs, aggregate multiple inputs
nodes Snapshot index of all other nodes in workflow Read outputs from any node
globals Global variable object, readable/writable Store counters, settings, shared states
output The node’s final output (return value) Determines the output of the script node

Conceptually, PSL behaves like:

function script({ input, nodes, globals }) {
  // Write PSL code here
  return ...; // Final output
}

PSL does not provide a full browser environment:

  • No window, document, or DOM
  • No direct network requests (use HTTP Node instead)
  • No direct file access (use file‑related nodes)

Its purpose is workflow data processing & logic orchestration.


2. The input Object: Accessing Node Inputs

input represents all inputs received by the current node and is one of the most frequently used PSL objects.

2.1 Quick Access to the First Input

input.value; // value of the first input item
input.type; // type: "text", "json", etc.
input.meta; // metadata of first input item
input.output; // full output structure

Useful when there is only one upstream input and you simply want to post‑process it.

2.2 Access All Inputs: input[index] / input.all

input[0].value; // value of input #1
input[0].type; // data type
input[0].meta; // metadata

input.all[0].output?.value;
input.all[1].meta;

Both input[index] and input.all[index] support .output / .value / .type / .meta.

2.3 Aggregation Helpers: length / first / byPort / from / flatValues / ofType

input.length;
input.first();
input.byPort("default");
input.from("node_12");

// Collect all values
input.flatValues();

// Filter by data type
input.ofType("text");
input.ofType("text").map((o) => o.value);

Example:

// Combine all text inputs into a paragraph
const text = input
  .ofType("text")
  .map((o) => o.value)
  .join("\n");

return text;

3. The nodes Object: Access Output of Any Node

nodes is a global index mapping for accessing outputs of any workflow node.

3.1 Basic Usage

nodes[1].output.value; // output of node #2
nodes[8].outputs[0].type; // type of node #9's first output
nodes.sn(2).output; // sn(2) = node #3
nodes[3].outputs[1].value; // node #4's second output

nodes[n].output is shorthand for nodes[n].outputs[0].

Example:

const a = nodes[2].output.value;
const b = nodes[5].output.value;

return { equal: a === b, a, b };

4. The globals Object: Global State

globals stores workflow‑level state:

  • Counters
  • Configuration
  • Cached values from previous actions

4.1 Read Globals

globals.name;
globals.config;

4.2 Write Globals (Recommended: globals.set)

globals.set("name", "lisi");
globals.set("counter", (globals.counter || 0) + 1);

return globals.counter;

Always prefer globals.set(key, value) for maintainability.


5. Common Helpers & Snippets

5.1 ofType()

const texts = input.ofType("text");
return texts.map((o) => o.value);

5.2 flatValues()

const values = input.flatValues();
const nodeValues = nodes[1].outputs.flatValues();

6. Common Script Examples

Example 1 — Join All Text Inputs

return input
  .ofType("text")
  .map((o) => o.value)
  .join(" ");

Example 2 — Standard Response Structure

return {
  ok: !!input.value,
  values: input.flatValues(),
};

Example 3 — Multi‑Type Outputs

return [
  { type: "boolean", value: input.value === "ok" },
  { type: "text", value: String(nodes[1].type) },
];

Example 4 — Conditional Output

if (input.length > 0) {
  return input.flatValues().join("\n");
} else {
  return "No data";
}

7. Notes & Best Practices

  1. Keep scripts pure (same input → same output).
  2. Avoid heavy computation; use dedicated nodes instead.
  3. Store only state/config in globals.
  4. Use the in‑editor Script Help Panel for instant references.

8. Summary

PSL is Pop’s built‑in scripting layer that enables:

  • Reading workflow‑wide data (input, nodes, globals)
  • Flexible logic and branching
  • Lightweight data transformation

Recommended usage philosophy:

  1. Use nodes to build workflows.
  2. Use PSL only when you need precise logic glue.
  3. Reuse PSL snippets across workflows for productivity.