Skip to main content

Destruct / Unpack — logic.destruct

Performs real ES6-style destructuring on a wired Object or Array input, unpacking user-specified property paths onto dynamically-created named output pins — with optional aliases, const/let/var declaration control, and an optional ...rest pin collecting whatever wasn't explicitly destructured.

Object mode — flat + nested field

Array-slot grammar — positional destructure of a nested array

Array mode with rest

  • Inputs: in (exec) — "In"; data_in (value) — "Object/Array"
  • Outputs: out (exec) — "Next"; plus one dynamic out_<fieldId> value pin per configured field (labeled with that field's resolved alias/variable name, falling back to its raw path); rest (value) — "...rest", present only when "Enable ...rest pin" is checked
  • Config fields (a dedicated side-panel form, not the generic schema-driven one):
KeyTypeDefaultNotes
labeltext""The node's canvas title; falls back to Destruct (<id prefix>) when empty.
destructureAsselectobject"object" or "array" — see the two modes below.
declarationKindselectconstconst / let / var — the keyword used in the emitted destructuring statement. Also controls whether fields become virtual variables.
fieldslist[]Grown with a "+ Add Field" button. Each entry has a path (see grammar below) and an optional alias.
enableRestbooleanfalseAdds the rest output pin.

Each field's alias input is disabled (showing "(ignored — uses bracket name)") whenever its path is an array-slot path — see below for why.

Path grammar

A field's path is dot/bracket property notation resolved against the wired input:

  • Dot notationuser.name.
  • Bracket with a numberitems[0], an ordinary array-index access, nestable like items[0].name. Resolved exactly like any other object/array key.
  • Bracket with an identifierhobbies[a] — this is not a key lookup. It's an array-slot destructure: at the hobbies key, array elements are destructured positionally into named variables. Fields sharing the same array-parent path become slots in declaration order — user.hobbies[a] followed by user.hobbies[b] destructures user.hobbies as [a, b] (first element into a, second into b).

Array mode paths are different again: each path must be a plain non-negative integer (the array position), not dot/bracket notation.

Only the final segment of a path may be an array-slot bracket — a mid-path one, or a root-level one with no parent key (bare [a]), is rejected at validation time, since there's nowhere to hang the [a, b] = [] pattern off of. ...rest is not yet supported inside a nested array-slot group (e.g. you can't do hobbies[a, ...rest]).

Auto-naming and collisions

With no alias, a field's variable name is auto-derived from its path's final segment (or the full path, sanitized, if that segment isn't a valid identifier — e.g. a numeric key). Names colliding across fields get a numeric suffix (name, name_2, name_3, ...). An array-slot field's variable name is its bracket name (hobbies[a]a) and always wins a collision over an auto-derived or aliased name — which is also why its alias input is disabled in the side panel. Duplicate aliases across fields are a hard validation error.

What it compiles to

Object mode (the default) emits real nested ES6 object destructuring — not a flat extraction, and not backed by any external library:

// fields: "name", "user.email" aliased to "email"
const { name, user: { email } = {} } = data ?? {};

A key referenced both as its own leaf field and as the parent of a nested path is emitted twice — once bare, once as key: { ... } — which is valid, if slightly unusual, ES6 destructuring syntax:

// fields: "user", "user.profile.name" aliased to "userName"
const { user, user: { profile: { name: userName } } = {} } = data ?? {};

An array-slot group nests the same way, using array syntax at the group's key:

// fields: "user.hobbies[a]", "user.hobbies[b]"
const { user: { hobbies: [a, b] = [] } = {} } = data ?? {};

Array mode emits positional array destructuring instead, with holes (bare commas) for any skipped position:

// destructureAs: "array"; fields at positions 0 and 2, aliased "a" and "c"; rest enabled
const [a, , c, ...rest] = data ?? [];

Rest in object mode is not native ...rest — the generated code needs to handle both an object and an array turning up at runtime and return the matching shape, so it goes through a small internal helper. You don't need to know its implementation: "rest" simply collects everything you didn't explicitly destructure, whether the input turns out to be an object or an array when the code actually runs.

Virtual variables

When Keyword is let or var (never const), every field on that Destruct node becomes a virtual variable: it shows up in a new "Destructured Fields" section of the Variables panel, grouped under the Destruct node's own (renameable) label, one row per field showing its resolved variable name and a let/var badge. Drag a row by its handle onto the canvas for the same Get <name> / Set <name> popup an ordinary declared variable offers, creating ordinary Get Variable / Set Variable nodes bound to that field.

The one behavioral difference from an ordinary let/var variable: Set on a virtual variable emits a bare reassignment, never a redeclaration —

name = "Alice";

— because the Destruct node itself already created the binding when its destructuring statement ran; a second const/let/var on the same name would be a duplicate- declaration SyntaxError. A const Destruct node contributes no virtual variables at all: there's nothing to reassign, and a Get-only row would just duplicate what the field's own output pin already does directly on the canvas.

Virtual variables are normally visible only within the same graph their Destruct node lives in (the main canvas, or one specific Function/Handler Function's own nested Blueprint graph) — never across a graph boundary. There's exactly one exception: a Destruct node wired directly onto a Begin node's un-nested trunk (not inside a Route handler, not inside another Function's body, not inside a Branch/Switch arm) is the one wiring shape where the destructured binding genuinely lands at true top-level module scope — so its virtual variables are also visible inside every Function/Handler Function Graph in the same file, via a second "Module Destructured Fields" section parallel to the existing Module Variables section. A Destruct wired anywhere else keeps its virtual variables strictly local. If a virtual-variable reference becomes invalid — its origin field or Destruct node was removed, or a cross-graph reference points at a Destruct that isn't actually Begin-trunk-reachable — validation catches it with a clear error rather than silently miscompiling.

Use case: Unpacking a request body or an upstream API response into individually named, individually wireable values in one step — including pulling specific elements out of a nested array by position — without a chain of separate Path Extractor nodes.