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 dynamicout_<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):
| Key | Type | Default | Notes |
|---|---|---|---|
label | text | "" | The node's canvas title; falls back to Destruct (<id prefix>) when empty. |
destructureAs | select | object | "object" or "array" — see the two modes below. |
declarationKind | select | const | const / let / var — the keyword used in the emitted destructuring statement. Also controls whether fields become virtual variables. |
fields | list | [] | Grown with a "+ Add Field" button. Each entry has a path (see grammar below) and an optional alias. |
enableRest | boolean | false | Adds 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 notation —
user.name. - Bracket with a number —
items[0], an ordinary array-index access, nestable likeitems[0].name. Resolved exactly like any other object/array key. - Bracket with an identifier —
hobbies[a]— this is not a key lookup. It's an array-slot destructure: at thehobbieskey, array elements are destructured positionally into named variables. Fields sharing the same array-parent path become slots in declaration order —user.hobbies[a]followed byuser.hobbies[b]destructuresuser.hobbiesas[a, b](first element intoa, second intob).
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.