Examples

Object Transformer

Transform, manipulate, and apply recipes to JSON objects with real-time preview.

This example demonstrates a component-based JSON transformation system with visual tree editor, 40+ transforms, recipe export/import, and template mode for arrays.

Key Architecture: All components register with a shared desk using VueAirport's useCheckIn. Child components access shared state directly through desk registration, enabling clean separation of concerns.

/ 999

Object
name
robert anderson
age
46
active
true
city
strasbourg
address
street
126 oak ave
zip
10026
custom
info
custom info 26
tags
Array(3)
[0]
avant-garde
[1]
experimental
[2]
contemporary
hobbies
Array(3)
[0]
choreography
[1]
contemporary dance
[2]
butoh
dob
1976-03-27T00:00:00.000Z
email
robert.anderson26@example.com
phone
+33 9 36 36 36 36

[
  {
    "name": "marina abramović",
    "age": 20,
    "active": false,
    "city": "marseille",
    "address": {
      "street": "100 main st",
      "zip": "10000",
      "custom": {
        "info": "custom info 0",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović0@example.com",
    "phone": "+33 1 10 10 10 10"
  },
  {
    "name": "pina cunningham",
    "age": 21,
    "active": true,
    "city": "paris",
    "address": {
      "street": "101 elm st",
      "zip": "10001",
      "custom": {
        "info": "custom info 1",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1951-02-02T00:00:00.000Z",
    "email": "pina.cunningham1@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 22,
    "active": true,
    "city": "lyon",
    "address": {
      "street": "102 oak ave",
      "zip": "10002"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1952-03-03T00:00:00.000Z",
    "email": "laurie.de keersmaeker2@example.com",
    "phone": "+33 3 12 12 12 12"
  },
  {
    "name": "romeo bausch",
    "age": 23,
    "active": false,
    "city": "toulouse",
    "address": {
      "street": "103 maple dr",
      "zip": "10003"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1953-04-04T00:00:00.000Z",
    "email": "romeo.bausch3@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 24,
    "active": true,
    "city": "nice",
    "address": {
      "street": "104 pine rd",
      "zip": "10004"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1954-05-05T00:00:00.000Z",
    "email": "anne Teresa.childs4@example.com",
    "phone": "+33 5 14 14 14 14"
  },
  {
    "name": "william forsythe",
    "age": 25,
    "active": true,
    "city": "nantes",
    "address": {
      "street": "105 cedar ln",
      "zip": "10005",
      "custom": {
        "info": "custom info 5",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe5@example.com"
  },
  {
    "name": "robert anderson",
    "age": 26,
    "active": false,
    "city": "strasbourg",
    "address": {
      "street": "106 birch way",
      "zip": "10006",
      "custom": {
        "info": "custom info 6",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1956-07-07T00:00:00.000Z",
    "email": "robert.anderson6@example.com",
    "phone": "+33 7 16 16 16 16"
  },
  {
    "name": "merce pite",
    "age": 27,
    "active": true,
    "city": "montpellier",
    "address": {
      "street": "107 ash ct",
      "zip": "10007"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1957-08-08T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 28,
    "active": true,
    "city": "bordeaux",
    "address": {
      "street": "108 main st",
      "zip": "10008"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1958-09-09T00:00:00.000Z",
    "phone": "+33 9 18 18 18 18"
  },
  {
    "name": "crystal castellucci",
    "age": 29,
    "active": false,
    "city": "lille",
    "address": {
      "street": "109 elm st",
      "zip": "10009"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1959-10-10T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 30,
    "active": true,
    "city": "marseille",
    "address": {
      "street": "110 oak ave",
      "zip": "10010",
      "custom": {
        "info": "custom info 10",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović10@example.com",
    "phone": "+33 2 20 20 20 20"
  },
  {
    "name": "pina cunningham",
    "age": 31,
    "active": true,
    "city": "paris",
    "address": {
      "street": "111 maple dr",
      "zip": "10011",
      "custom": {
        "info": "custom info 11",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1961-12-12T00:00:00.000Z",
    "email": "pina.cunningham11@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 32,
    "active": false,
    "city": "lyon",
    "address": {
      "street": "112 pine rd",
      "zip": "10012"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1962-01-13T00:00:00.000Z",
    "email": "laurie.de keersmaeker12@example.com",
    "phone": "+33 4 22 22 22 22"
  },
  {
    "name": "romeo bausch",
    "age": 33,
    "active": true,
    "city": "toulouse",
    "address": {
      "street": "113 cedar ln",
      "zip": "10013"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1963-02-14T00:00:00.000Z",
    "email": "romeo.bausch13@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 34,
    "active": true,
    "city": "nice",
    "address": {
      "street": "114 birch way",
      "zip": "10014"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1964-03-15T00:00:00.000Z",
    "email": "anne Teresa.childs14@example.com",
    "phone": "+33 6 24 24 24 24"
  },
  {
    "name": "william forsythe",
    "age": 35,
    "active": false,
    "city": "nantes",
    "address": {
      "street": "115 ash ct",
      "zip": "10015",
      "custom": {
        "info": "custom info 15",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe15@example.com"
  },
  {
    "name": "robert anderson",
    "age": 36,
    "active": true,
    "city": "strasbourg",
    "address": {
      "street": "116 main st",
      "zip": "10016",
      "custom": {
        "info": "custom info 16",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1966-05-17T00:00:00.000Z",
    "email": "robert.anderson16@example.com",
    "phone": "+33 8 26 26 26 26"
  },
  {
    "name": "merce pite",
    "age": 37,
    "active": true,
    "city": "montpellier",
    "address": {
      "street": "117 elm st",
      "zip": "10017"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1967-06-18T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 38,
    "active": false,
    "city": "bordeaux",
    "address": {
      "street": "118 oak ave",
      "zip": "10018"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1968-07-19T00:00:00.000Z",
    "phone": "+33 1 28 28 28 28"
  },
  {
    "name": "crystal castellucci",
    "age": 39,
    "active": true,
    "city": "lille",
    "address": {
      "street": "119 maple dr",
      "zip": "10019"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1969-08-20T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 40,
    "active": true,
    "city": "marseille",
    "address": {
      "street": "120 pine rd",
      "zip": "10020",
      "custom": {
        "info": "custom info 20",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović20@example.com",
    "phone": "+33 3 30 30 30 30"
  },
  {
    "name": "pina cunningham",
    "age": 41,
    "active": false,
    "city": "paris",
    "address": {
      "street": "121 cedar ln",
      "zip": "10021",
      "custom": {
        "info": "custom info 21",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1971-10-22T00:00:00.000Z",
    "email": "pina.cunningham21@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 42,
    "active": true,
    "city": "lyon",
    "address": {
      "street": "122 birch way",
      "zip": "10022"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1972-11-23T00:00:00.000Z",
    "email": "laurie.de keersmaeker22@example.com",
    "phone": "+33 5 32 32 32 32"
  },
  {
    "name": "romeo bausch",
    "age": 43,
    "active": true,
    "city": "toulouse",
    "address": {
      "street": "123 ash ct",
      "zip": "10023"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1973-12-24T00:00:00.000Z",
    "email": "romeo.bausch23@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 44,
    "active": false,
    "city": "nice",
    "address": {
      "street": "124 main st",
      "zip": "10024"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1974-01-25T00:00:00.000Z",
    "email": "anne Teresa.childs24@example.com",
    "phone": "+33 7 34 34 34 34"
  },
  {
    "name": "william forsythe",
    "age": 45,
    "active": true,
    "city": "nantes",
    "address": {
      "street": "125 elm st",
      "zip": "10025",
      "custom": {
        "info": "custom info 25",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe25@example.com"
  },
  {
    "name": "robert anderson",
    "age": 46,
    "active": true,
    "city": "strasbourg",
    "address": {
      "street": "126 oak ave",
      "zip": "10026",
      "custom": {
        "info": "custom info 26",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1976-03-27T00:00:00.000Z",
    "email": "robert.anderson26@example.com",
    "phone": "+33 9 36 36 36 36"
  },
  {
    "name": "merce pite",
    "age": 47,
    "active": false,
    "city": "montpellier",
    "address": {
      "street": "127 maple dr",
      "zip": "10027"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1977-04-28T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 48,
    "active": true,
    "city": "bordeaux",
    "address": {
      "street": "128 pine rd",
      "zip": "10028"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1978-05-01T00:00:00.000Z",
    "phone": "+33 2 38 38 38 38"
  },
  {
    "name": "crystal castellucci",
    "age": 49,
    "active": true,
    "city": "lille",
    "address": {
      "street": "129 cedar ln",
      "zip": "10029"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1979-06-02T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 50,
    "active": false,
    "city": "marseille",
    "address": {
      "street": "130 birch way",
      "zip": "10030",
      "custom": {
        "info": "custom info 30",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović30@example.com",
    "phone": "+33 4 40 40 40 40"
  },
  {
    "name": "pina cunningham",
    "age": 51,
    "active": true,
    "city": "paris",
    "address": {
      "street": "131 ash ct",
      "zip": "10031",
      "custom": {
        "info": "custom info 31",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1981-08-04T00:00:00.000Z",
    "email": "pina.cunningham31@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 52,
    "active": true,
    "city": "lyon",
    "address": {
      "street": "132 main st",
      "zip": "10032"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1982-09-05T00:00:00.000Z",
    "email": "laurie.de keersmaeker32@example.com",
    "phone": "+33 6 42 42 42 42"
  },
  {
    "name": "romeo bausch",
    "age": 53,
    "active": false,
    "city": "toulouse",
    "address": {
      "street": "133 elm st",
      "zip": "10033"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1983-10-06T00:00:00.000Z",
    "email": "romeo.bausch33@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 54,
    "active": true,
    "city": "nice",
    "address": {
      "street": "134 oak ave",
      "zip": "10034"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1984-11-07T00:00:00.000Z",
    "email": "anne Teresa.childs34@example.com",
    "phone": "+33 8 44 44 44 44"
  },
  {
    "name": "william forsythe",
    "age": 55,
    "active": true,
    "city": "nantes",
    "address": {
      "street": "135 maple dr",
      "zip": "10035",
      "custom": {
        "info": "custom info 35",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe35@example.com"
  },
  {
    "name": "robert anderson",
    "age": 56,
    "active": false,
    "city": "strasbourg",
    "address": {
      "street": "136 pine rd",
      "zip": "10036",
      "custom": {
        "info": "custom info 36",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1986-01-09T00:00:00.000Z",
    "email": "robert.anderson36@example.com",
    "phone": "+33 1 46 46 46 46"
  },
  {
    "name": "merce pite",
    "age": 57,
    "active": true,
    "city": "montpellier",
    "address": {
      "street": "137 cedar ln",
      "zip": "10037"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1987-02-10T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 58,
    "active": true,
    "city": "bordeaux",
    "address": {
      "street": "138 birch way",
      "zip": "10038"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1988-03-11T00:00:00.000Z",
    "phone": "+33 3 48 48 48 48"
  },
  {
    "name": "crystal castellucci",
    "age": 59,
    "active": false,
    "city": "lille",
    "address": {
      "street": "139 ash ct",
      "zip": "10039"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1989-04-12T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 60,
    "active": true,
    "city": "marseille",
    "address": {
      "street": "140 main st",
      "zip": "10040",
      "custom": {
        "info": "custom info 40",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović40@example.com",
    "phone": "+33 5 50 50 50 50"
  },
  {
    "name": "pina cunningham",
    "age": 61,
    "active": true,
    "city": "paris",
    "address": {
      "street": "141 elm st",
      "zip": "10041",
      "custom": {
        "info": "custom info 41",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1991-06-14T00:00:00.000Z",
    "email": "pina.cunningham41@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 62,
    "active": false,
    "city": "lyon",
    "address": {
      "street": "142 oak ave",
      "zip": "10042"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1992-07-15T00:00:00.000Z",
    "email": "laurie.de keersmaeker42@example.com",
    "phone": "+33 7 52 52 52 52"
  },
  {
    "name": "romeo bausch",
    "age": 63,
    "active": true,
    "city": "toulouse",
    "address": {
      "street": "143 maple dr",
      "zip": "10043"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1993-08-16T00:00:00.000Z",
    "email": "romeo.bausch43@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 64,
    "active": true,
    "city": "nice",
    "address": {
      "street": "144 pine rd",
      "zip": "10044"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1994-09-17T00:00:00.000Z",
    "email": "anne Teresa.childs44@example.com",
    "phone": "+33 9 54 54 54 54"
  },
  {
    "name": "william forsythe",
    "age": 65,
    "active": false,
    "city": "nantes",
    "address": {
      "street": "145 cedar ln",
      "zip": "10045",
      "custom": {
        "info": "custom info 45",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe45@example.com"
  },
  {
    "name": "robert anderson",
    "age": 66,
    "active": true,
    "city": "strasbourg",
    "address": {
      "street": "146 birch way",
      "zip": "10046",
      "custom": {
        "info": "custom info 46",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1996-11-19T00:00:00.000Z",
    "email": "robert.anderson46@example.com",
    "phone": "+33 2 56 56 56 56"
  },
  {
    "name": "merce pite",
    "age": 67,
    "active": true,
    "city": "montpellier",
    "address": {
      "street": "147 ash ct",
      "zip": "10047"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1997-12-20T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 68,
    "active": false,
    "city": "bordeaux",
    "address": {
      "street": "148 main st",
      "zip": "10048"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1998-01-21T00:00:00.000Z",
    "phone": "+33 4 58 58 58 58"
  },
  {
    "name": "crystal castellucci",
    "age": 69,
    "active": true,
    "city": "lille",
    "address": {
      "street": "149 elm st",
      "zip": "10049"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1999-02-22T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 20,
    "active": true,
    "city": "marseille",
    "address": {
      "street": "150 oak ave",
      "zip": "10050",
      "custom": {
        "info": "custom info 50",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović50@example.com",
    "phone": "+33 6 60 60 60 60"
  },
  {
    "name": "pina cunningham",
    "age": 21,
    "active": false,
    "city": "paris",
    "address": {
      "street": "151 maple dr",
      "zip": "10051",
      "custom": {
        "info": "custom info 51",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "2001-04-24T00:00:00.000Z",
    "email": "pina.cunningham51@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 22,
    "active": true,
    "city": "lyon",
    "address": {
      "street": "152 pine rd",
      "zip": "10052"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "2002-05-25T00:00:00.000Z",
    "email": "laurie.de keersmaeker52@example.com",
    "phone": "+33 8 62 62 62 62"
  },
  {
    "name": "romeo bausch",
    "age": 23,
    "active": true,
    "city": "toulouse",
    "address": {
      "street": "153 cedar ln",
      "zip": "10053"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "2003-06-26T00:00:00.000Z",
    "email": "romeo.bausch53@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 24,
    "active": false,
    "city": "nice",
    "address": {
      "street": "154 birch way",
      "zip": "10054"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "2004-07-27T00:00:00.000Z",
    "email": "anne Teresa.childs54@example.com",
    "phone": "+33 1 64 64 64 64"
  },
  {
    "name": "william forsythe",
    "age": 25,
    "active": true,
    "city": "nantes",
    "address": {
      "street": "155 ash ct",
      "zip": "10055",
      "custom": {
        "info": "custom info 55",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe55@example.com"
  },
  {
    "name": "robert anderson",
    "age": 26,
    "active": true,
    "city": "strasbourg",
    "address": {
      "street": "156 main st",
      "zip": "10056",
      "custom": {
        "info": "custom info 56",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1951-09-01T00:00:00.000Z",
    "email": "robert.anderson56@example.com",
    "phone": "+33 3 66 66 66 66"
  },
  {
    "name": "merce pite",
    "age": 27,
    "active": false,
    "city": "montpellier",
    "address": {
      "street": "157 elm st",
      "zip": "10057"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1952-10-02T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 28,
    "active": true,
    "city": "bordeaux",
    "address": {
      "street": "158 oak ave",
      "zip": "10058"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1953-11-03T00:00:00.000Z",
    "phone": "+33 5 68 68 68 68"
  },
  {
    "name": "crystal castellucci",
    "age": 29,
    "active": true,
    "city": "lille",
    "address": {
      "street": "159 maple dr",
      "zip": "10059"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1954-12-04T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 30,
    "active": false,
    "city": "marseille",
    "address": {
      "street": "160 pine rd",
      "zip": "10060",
      "custom": {
        "info": "custom info 60",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović60@example.com",
    "phone": "+33 7 70 70 70 70"
  },
  {
    "name": "pina cunningham",
    "age": 31,
    "active": true,
    "city": "paris",
    "address": {
      "street": "161 cedar ln",
      "zip": "10061",
      "custom": {
        "info": "custom info 61",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1956-02-06T00:00:00.000Z",
    "email": "pina.cunningham61@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 32,
    "active": true,
    "city": "lyon",
    "address": {
      "street": "162 birch way",
      "zip": "10062"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1957-03-07T00:00:00.000Z",
    "email": "laurie.de keersmaeker62@example.com",
    "phone": "+33 9 72 72 72 72"
  },
  {
    "name": "romeo bausch",
    "age": 33,
    "active": false,
    "city": "toulouse",
    "address": {
      "street": "163 ash ct",
      "zip": "10063"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1958-04-08T00:00:00.000Z",
    "email": "romeo.bausch63@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 34,
    "active": true,
    "city": "nice",
    "address": {
      "street": "164 main st",
      "zip": "10064"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1959-05-09T00:00:00.000Z",
    "email": "anne Teresa.childs64@example.com",
    "phone": "+33 2 74 74 74 74"
  },
  {
    "name": "william forsythe",
    "age": 35,
    "active": true,
    "city": "nantes",
    "address": {
      "street": "165 elm st",
      "zip": "10065",
      "custom": {
        "info": "custom info 65",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe65@example.com"
  },
  {
    "name": "robert anderson",
    "age": 36,
    "active": false,
    "city": "strasbourg",
    "address": {
      "street": "166 oak ave",
      "zip": "10066",
      "custom": {
        "info": "custom info 66",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1961-07-11T00:00:00.000Z",
    "email": "robert.anderson66@example.com",
    "phone": "+33 4 76 76 76 76"
  },
  {
    "name": "merce pite",
    "age": 37,
    "active": true,
    "city": "montpellier",
    "address": {
      "street": "167 maple dr",
      "zip": "10067"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1962-08-12T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 38,
    "active": true,
    "city": "bordeaux",
    "address": {
      "street": "168 pine rd",
      "zip": "10068"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1963-09-13T00:00:00.000Z",
    "phone": "+33 6 78 78 78 78"
  },
  {
    "name": "crystal castellucci",
    "age": 39,
    "active": false,
    "city": "lille",
    "address": {
      "street": "169 cedar ln",
      "zip": "10069"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1964-10-14T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 40,
    "active": true,
    "city": "marseille",
    "address": {
      "street": "170 birch way",
      "zip": "10070",
      "custom": {
        "info": "custom info 70",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović70@example.com",
    "phone": "+33 8 80 80 80 80"
  },
  {
    "name": "pina cunningham",
    "age": 41,
    "active": true,
    "city": "paris",
    "address": {
      "street": "171 ash ct",
      "zip": "10071",
      "custom": {
        "info": "custom info 71",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1966-12-16T00:00:00.000Z",
    "email": "pina.cunningham71@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 42,
    "active": false,
    "city": "lyon",
    "address": {
      "street": "172 main st",
      "zip": "10072"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1967-01-17T00:00:00.000Z",
    "email": "laurie.de keersmaeker72@example.com",
    "phone": "+33 1 82 82 82 82"
  },
  {
    "name": "romeo bausch",
    "age": 43,
    "active": true,
    "city": "toulouse",
    "address": {
      "street": "173 elm st",
      "zip": "10073"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1968-02-18T00:00:00.000Z",
    "email": "romeo.bausch73@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 44,
    "active": true,
    "city": "nice",
    "address": {
      "street": "174 oak ave",
      "zip": "10074"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1969-03-19T00:00:00.000Z",
    "email": "anne Teresa.childs74@example.com",
    "phone": "+33 3 84 84 84 84"
  },
  {
    "name": "william forsythe",
    "age": 45,
    "active": false,
    "city": "nantes",
    "address": {
      "street": "175 maple dr",
      "zip": "10075",
      "custom": {
        "info": "custom info 75",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe75@example.com"
  },
  {
    "name": "robert anderson",
    "age": 46,
    "active": true,
    "city": "strasbourg",
    "address": {
      "street": "176 pine rd",
      "zip": "10076",
      "custom": {
        "info": "custom info 76",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1971-05-21T00:00:00.000Z",
    "email": "robert.anderson76@example.com",
    "phone": "+33 5 86 86 86 86"
  },
  {
    "name": "merce pite",
    "age": 47,
    "active": true,
    "city": "montpellier",
    "address": {
      "street": "177 cedar ln",
      "zip": "10077"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1972-06-22T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 48,
    "active": false,
    "city": "bordeaux",
    "address": {
      "street": "178 birch way",
      "zip": "10078"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1973-07-23T00:00:00.000Z",
    "phone": "+33 7 88 88 88 88"
  },
  {
    "name": "crystal castellucci",
    "age": 49,
    "active": true,
    "city": "lille",
    "address": {
      "street": "179 ash ct",
      "zip": "10079"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1974-08-24T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 50,
    "active": true,
    "city": "marseille",
    "address": {
      "street": "180 main st",
      "zip": "10080",
      "custom": {
        "info": "custom info 80",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović80@example.com",
    "phone": "+33 9 90 90 90 90"
  },
  {
    "name": "pina cunningham",
    "age": 51,
    "active": false,
    "city": "paris",
    "address": {
      "street": "181 elm st",
      "zip": "10081",
      "custom": {
        "info": "custom info 81",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1976-10-26T00:00:00.000Z",
    "email": "pina.cunningham81@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 52,
    "active": true,
    "city": "lyon",
    "address": {
      "street": "182 oak ave",
      "zip": "10082"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1977-11-27T00:00:00.000Z",
    "email": "laurie.de keersmaeker82@example.com",
    "phone": "+33 2 92 92 92 92"
  },
  {
    "name": "romeo bausch",
    "age": 53,
    "active": true,
    "city": "toulouse",
    "address": {
      "street": "183 maple dr",
      "zip": "10083"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1978-12-28T00:00:00.000Z",
    "email": "romeo.bausch83@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 54,
    "active": false,
    "city": "nice",
    "address": {
      "street": "184 pine rd",
      "zip": "10084"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1979-01-01T00:00:00.000Z",
    "email": "anne Teresa.childs84@example.com",
    "phone": "+33 4 94 94 94 94"
  },
  {
    "name": "william forsythe",
    "age": 55,
    "active": true,
    "city": "nantes",
    "address": {
      "street": "185 cedar ln",
      "zip": "10085",
      "custom": {
        "info": "custom info 85",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe85@example.com"
  },
  {
    "name": "robert anderson",
    "age": 56,
    "active": true,
    "city": "strasbourg",
    "address": {
      "street": "186 birch way",
      "zip": "10086",
      "custom": {
        "info": "custom info 86",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1981-03-03T00:00:00.000Z",
    "email": "robert.anderson86@example.com",
    "phone": "+33 6 96 96 96 96"
  },
  {
    "name": "merce pite",
    "age": 57,
    "active": false,
    "city": "montpellier",
    "address": {
      "street": "187 ash ct",
      "zip": "10087"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1982-04-04T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 58,
    "active": true,
    "city": "bordeaux",
    "address": {
      "street": "188 main st",
      "zip": "10088"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1983-05-05T00:00:00.000Z",
    "phone": "+33 8 98 98 98 98"
  },
  {
    "name": "crystal castellucci",
    "age": 59,
    "active": true,
    "city": "lille",
    "address": {
      "street": "189 elm st",
      "zip": "10089"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1984-06-06T00:00:00.000Z"
  },
  {
    "name": "marina abramović",
    "age": 60,
    "active": false,
    "city": "marseille",
    "address": {
      "street": "190 oak ave",
      "zip": "10090",
      "custom": {
        "info": "custom info 90",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "marina.abramović90@example.com",
    "phone": "+33 1 10 10 10 10"
  },
  {
    "name": "pina cunningham",
    "age": 61,
    "active": true,
    "city": "paris",
    "address": {
      "street": "191 maple dr",
      "zip": "10091",
      "custom": {
        "info": "custom info 91",
        "tags": [
          "avant-garde",
          "experimental"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1986-08-08T00:00:00.000Z",
    "email": "pina.cunningham91@example.com"
  },
  {
    "name": "laurie de keersmaeker",
    "age": 62,
    "active": true,
    "city": "lyon",
    "address": {
      "street": "192 pine rd",
      "zip": "10092"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1987-09-09T00:00:00.000Z",
    "email": "laurie.de keersmaeker92@example.com",
    "phone": "+33 3 12 12 12 12"
  },
  {
    "name": "romeo bausch",
    "age": 63,
    "active": false,
    "city": "toulouse",
    "address": {
      "street": "193 cedar ln",
      "zip": "10093"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1988-10-10T00:00:00.000Z",
    "email": "romeo.bausch93@example.com"
  },
  {
    "name": "anne Teresa childs",
    "age": 64,
    "active": true,
    "city": "nice",
    "address": {
      "street": "194 birch way",
      "zip": "10094"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1989-11-11T00:00:00.000Z",
    "email": "anne Teresa.childs94@example.com",
    "phone": "+33 5 14 14 14 14"
  },
  {
    "name": "william forsythe",
    "age": 65,
    "active": true,
    "city": "nantes",
    "address": {
      "street": "195 ash ct",
      "zip": "10095",
      "custom": {
        "info": "custom info 95",
        "tags": [
          "avant-garde",
          "experimental",
          "contemporary"
        ]
      }
    },
    "hobbies": [
      "performance art",
      "installation",
      "video art"
    ],
    "email": "william.forsythe95@example.com"
  },
  {
    "name": "robert anderson",
    "age": 66,
    "active": false,
    "city": "strasbourg",
    "address": {
      "street": "196 main st",
      "zip": "10096",
      "custom": {
        "info": "custom info 96",
        "tags": [
          "avant-garde"
        ]
      }
    },
    "hobbies": [
      "choreography",
      "contemporary dance",
      "butoh"
    ],
    "dob": "1991-01-13T00:00:00.000Z",
    "email": "robert.anderson96@example.com",
    "phone": "+33 7 16 16 16 16"
  },
  {
    "name": "merce pite",
    "age": 67,
    "active": true,
    "city": "montpellier",
    "address": {
      "street": "197 elm st",
      "zip": "10097"
    },
    "hobbies": [
      "experimental music",
      "multimedia",
      "storytelling"
    ],
    "dob": "1992-02-14T00:00:00.000Z"
  },
  {
    "name": "lucinda wilson",
    "age": 68,
    "active": true,
    "city": "bordeaux",
    "address": {
      "street": "198 oak ave",
      "zip": "10098"
    },
    "hobbies": [
      "theatre direction",
      "scenography",
      "dramaturgy"
    ],
    "dob": "1993-03-15T00:00:00.000Z",
    "phone": "+33 9 18 18 18 18"
  },
  {
    "name": "crystal castellucci",
    "age": 69,
    "active": false,
    "city": "lille",
    "address": {
      "street": "199 maple dr",
      "zip": "10099"
    },
    "hobbies": [
      "physical theatre",
      "improvisation",
      "composition"
    ],
    "dob": "1994-04-16T00:00:00.000Z"
  }
]

Component-Based Architecture

This example showcases VueAirport's component-based pattern where:

  • No props are passed between components
  • Each component registers independently with the desk using useCheckIn
  • Shared state (tree, transforms, mode) is accessed through desk context
  • Components communicate through desk updates, not parent-child props

Project Structure

object-transformer/
├── index.ts                           # Shared types and desk key
├── ExampleTransformer.vue             # Demo wrapper (creates desk)
├── ModeToggle.vue                     # Object/Model mode switch
├── NodeActions.vue                    # Transform/delete actions
├── NodeKeyEditor.vue                  # Inline key editing
├── NodeOpen.vue                       # Expand/collapse toggle
├── ObjectNode.vue                     # Recursive tree node
├── ObjectPreview.vue                  # JSON preview display
├── ObjectTransformer.vue              # Root container component
├── RecipePreview.vue                  # Recipe JSON preview
├── TransformParam.vue                 # Transform parameters editor
├── TransformSelect.vue                # Transform selector dropdown
├── transforms/                        # Transform category components
│   ├── TransformArray.vue             # Array transforms (Map, Filter, etc.)
│   ├── TransformBoolean.vue           # Boolean transforms
│   ├── TransformDate.vue              # Date transforms (Format, ISO, etc.)
│   ├── TransformNumber.vue            # Number transforms (Round, Abs, etc.)
│   ├── TransformObject.vue            # Object transforms (Merge, Flatten, etc.)
│   └── TransformString.vue            # String transforms (Split, Uppercase, etc.)
└── utils/
    ├── common-structural-handlers.util.ts    # Common structural transform handlers
    ├── functional.util.ts                    # Functional utilities (partition, etc.)
    ├── model-mode.util.ts                    # Model mode helpers
    ├── model-rules.util.ts                   # Model transformation rules
    ├── node-builder.util.ts                  # Tree node construction
    ├── node-editing.util.ts                  # Node editing operations
    ├── node-transforms.util.ts               # Node transform application
    ├── node-utilities.util.ts                # Node utility functions
    ├── structural-transform-handlers.util.ts # Structural transform handlers
    ├── transform-propagation.util.ts         # Structural transform propagation
    ├── transform-recipe.util.ts              # Recipe serialization/application
    └── type-guards.util.ts                   # Type guard utilities

Root Component (ExampleTransformer.vue)

Creates the desk and provides initial data. No props are passed to children except an id - they all register independently:

<script setup lang="ts">
import {
  ObjectTransformer,
  ObjectPreview,
  RecipePreview,
  ObjectNode,
  ModeToggle,
  TransformString,
  TransformNumber,
  TransformDate,
  TransformBoolean,
  TransformObject,
  TransformArray,
} from '.';

// Array of user objects for model mode demonstration
const data = [
  {
    name: 'john doe',
    age: 30,
    dob: new Date('1993-05-15T00:00:00Z'),
    active: true,
    city: 'marseille',
    address: {
      street: '123 main st',
      zip: '13001',
      custom: {
        info: 'some custom info',
        tags: ['tag1', 'tag2'],
      },
    },
    hobbies: ['reading', 'traveling', 'swimming'],
  },
  {
    name: 'jane smith',
    age: 28,
    active: false,
    city: 'paris',
    address: {
      street: '456 elm st',
      zip: '75001',
    },
    hobbies: ['cooking', 'painting'],
  },
  {
    name: 'bob wilson',
    age: 35,
    dob: new Date('1988-03-20T00:00:00Z'),
    active: true,
    city: 'lyon',
    address: {
      street: '789 oak ave',
      zip: '69001',
      custom: {
        info: 'another info',
      },
    },
  },
];
</script>

<template>
  <div class="space-y-4 max-h-196 overflow-auto">
    <!-- ObjectTransformer creates the desk internally -->
    <ObjectTransformer :data="data" class="flex md:flex-row w-full">
      <!-- Transform category components - register independently -->
      <TransformString />
      <TransformNumber />
      <TransformDate />
      <TransformBoolean />
      <TransformObject />
      <TransformArray />

      <!-- Tree editor - registers independently -->
      <div class="flex-1 flex flex-col gap-2">
        <ModeToggle />
        <ObjectNode />
      </div>

      <!-- Preview panels - register independently -->
      <div class="flex-1 flex flex-col gap-2 h-full">
        <ObjectPreview />
        <RecipePreview />
      </div>
    </ObjectTransformer>
  </div>
</template>

Notice: No props drilling! Each component (TransformString, ObjectNode, ObjectPreview, etc.) accesses the desk independently through useCheckIn.

Transform Components (transforms/TransformString.vue)

Transform components register transform definitions with the desk on mount. Each transform category is a Vue component:

<script setup lang="ts">
import { useCheckIn } from 'vue-airport';
import type { ObjectTransformerContext, Transform } from '..';
import { ObjectTransformerDeskKey, registerStructuralTransformHandler } from '..';

type DeskWithContext = typeof desk & ObjectTransformerContext;

// Register structural transform handler for 'split'
registerStructuralTransformHandler('split', (current, lastKey, result) => {
  if (!Array.isArray(result.parts)) return;

  // Create new properties from parts
  result.parts.forEach((part: any, index: number) => {
    const newKey = `${lastKey}_${index}`;
    current[newKey] = part;
  });

  // Remove source if specified
  if (result.removeSource) {
    delete current[lastKey];
  }
});

const transforms: Transform[] = [
  {
    name: 'Split',
    structural: true, // Structural transform creates new nodes
    if: (node) => node.type === 'string',
    params: [{ key: 'delimiter', label: 'Delimiter', type: 'text', default: ' ' }],
    fn: (v: string, delimiter: string) => {
      if (typeof v !== 'string') return v;
      return {
        __structuralChange: true,
        action: 'split' as const,
        parts: v.split(delimiter),
        removeSource: false,
      };
    },
  },
  {
    name: 'Uppercase',
    if: (node) => node.type === 'string',
    fn: (v: any) => (typeof v === 'string' ? v.toUpperCase() : v),
  },
  {
    name: 'Capitalized',
    if: (node) => node.type === 'string',
    fn: (v: any) =>
      typeof v === 'string' ? v.charAt(0).toUpperCase() + v.slice(1).toLowerCase() : v,
  },
  {
    name: 'To Object',
    structural: true,
    if: (node) => node.type === 'string',
    fn: (v: string) => {
      if (typeof v !== 'string') return v;
      return {
        __structuralChange: true,
        action: 'toObject' as const,
        object: { object: { value: v } },
        removeSource: false,
      };
    },
  },
  // ... 10+ more string transforms
];

// Register with desk on mount - no props needed!
const { checkIn } = useCheckIn<Transform, ObjectTransformerContext>();
const { desk } = checkIn(ObjectTransformerDeskKey, {
  id: 'string-transform',
  autoCheckIn: true,
});

onMounted(() => {
  const d = desk as DeskWithContext;
  d.addTransforms(...transforms); // Register transforms with shared desk
});
</script>

<template>
  <!-- No UI - just registers transforms on mount -->
  <div class="hidden"></div>
</template>

Key Points:

  • Each transform component registers its transforms via desk.addTransforms()
  • No props passed - component accesses desk independently
  • Transforms are registered once on mount
  • Other components (like TransformSelect) access these transforms from desk context
  • Enables modular organization by data type (string, number, date, etc.)

Desk-Based Component Registration

ObjectTransformer creates a desk with shared context:

// ObjectTransformer.vue (simplified)
const { createDesk } = useCheckIn<ObjectNodeData, TransformerContext>();
const { desk } = createDesk(OBJECT_TRANSFORMER_DESK_KEY, {
  devTools: true,
  context: {
    tree: ref<ObjectNodeData[]>([]),
    originalData: ref(props.data),
    mode: ref<'object' | 'model'>('object'),
    transforms: ALL_TRANSFORMS,
    // ... other shared state
  },
});

Child components register without any props:

// ObjectNode.vue, ObjectPreview.vue, TransformString.vue, etc.
const { checkIn } = useCheckIn<ObjectNodeData, TransformerContext>();
const { desk } = checkIn(OBJECT_TRANSFORMER_DESK_KEY, {
  autoCheckIn: false,
  watchData: false,
});

// Access shared state directly
const tree = computed(() => desk!.tree);
const mode = computed(() => desk!.mode.value);
const transforms = computed(() => desk!.transforms);

This pattern eliminates props drilling and enables flexible component composition.

Key Concepts

Three-Tier Key System

Each node maintains three distinct keys for robust rename tracking:

  • firstKey: Immutable original key at creation (never changes)
  • originalKey: Current expected key reflecting parent structural changes
  • key: Actual display key (custom if manually renamed via keyModified flag)

This system ensures:

  • Child renames persist when parent nodes are renamed or transformed
  • Recipe paths remain accurate for structural transformations
  • Undo/redo operations preserve user intentions

Example flow:

// Initial state
{ firstKey: 'name', originalKey: 'name', key: 'name', keyModified: false }

// User renames to 'fullName'
{ firstKey: 'name', originalKey: 'name', key: 'fullName', keyModified: true }

// Parent applies 'Split' transform creating 'name_object'
// Child's originalKey updates, but user rename preserved
{ firstKey: 'name', originalKey: 'name_object', key: 'fullName', keyModified: true }

Transform Types

Transforms are categorized by structural impact:

Structural Transforms (create/remove nodes):

  • To Object, Split, Array to Properties
  • Trigger node tree rebuilding
  • Update originalKey for affected children
  • Preserve user renames via keyModified flag

Non-Structural Transforms (modify values):

  • Capitalized, Uppercase, Lowercase, Trim, Number, etc.
  • Update node's transform property
  • No tree restructuring needed

Recipe System

Recipes are portable JSON documents describing transformations:

interface TransformRecipe {
  steps: Array<{
    path: string[];           // Path to target node (for transforms/deletions)
    renamePath: string[];     // Path for renames (reflects current structure)
    transform?: string;       // Transform name
    params?: any;            // Transform parameters
  }>;
  deletions: string[][];     // Paths to deleted nodes
  renames: Array<{
    path: string[];          // Path to renamed node
    newKey: string;          // New key name
  }>;
}

Recipe Generation (buildRecipe):

  • Traverses tree collecting transforms, deletions, renames
  • Uses originalPath for data operations (transforms/deletions)
  • Uses renamePath for rename operations (current structure)
  • Skips numeric indices for array templates (template mode)

Recipe Application (applyRecipe):

  • Detects array data with object recipe → applies to each element
  • Sequential execution: transforms → deletions → sorted renames
  • Renames sorted by path depth (parents before children)

Mode Switching

Object Mode: Edit single object directly

Model Mode:

  • For arrays, uses first element as template
  • Recipe applied to all array elements
  • Enables batch transformations

Node Tracking

Nodes created by structural transforms maintain identity:

  • splitSourceId: ID of source node that created this node
  • splitIndex: Position in split (0, 1, 2...)

This enables node reuse when structural transforms re-execute, preserving user customizations.

How It Works

  1. Initial Tree Build: buildNodeTree creates recursive structure from data
  2. User Interactions:
    • Rename keys → Updates key and sets keyModified: true
    • Apply transform → Updates transform property, triggers propagation if structural
    • Delete node → Marks isDeleted: true
  3. Structural Transform Propagation:
    • Identifies existing nodes by splitSourceId and splitIndex
    • Reuses nodes to preserve user customizations
    • Updates originalKey for children affected by parent changes
    • Preserves key if keyModified: true
  4. Recipe Export:
    • Traverses tree with dual path tracking
    • Serializes transforms, deletions, renames
    • Downloads as JSON file
  5. Recipe Import:
    • Applies recipe to originalData
    • Rebuilds tree from transformed result
    • Displays final output
  6. Preview Update:
    • Computed property applies all transforms
    • Real-time JSON preview with syntax highlighting

Recipe Workflow

Exporting a Recipe

  1. User applies transforms, renames, deletions to tree
  2. Clicks "Export Recipe"
  3. buildRecipe traverses tree:
    • Collects all transforms with originalPath (data reference)
    • Collects deletions with originalPath
    • Collects renames with renamePath (current structure)
  4. JSON file downloads with complete transformation definition

Importing a Recipe

  1. User selects JSON recipe file
  2. applyRecipe detects data type:
    • If array + object recipe → template mode (apply to all elements)
    • Otherwise → direct application
  3. applySingleRecipe executes sequentially:
    • Apply transforms at specified paths
    • Delete nodes at specified paths
    • Rename nodes (sorted by depth)
  4. Tree rebuilds from transformed result
  5. Preview shows final output

Template Mode for Arrays

When importing an object recipe to array data:

// Recipe for single object
const recipe = {
  steps: [
    { path: ['name'], renamePath: ['name'], transform: 'Capitalized' }
  ],
  renames: [
    { path: ['name'], newKey: 'fullName' }
  ]
};

// Applied to array
const arrayData = [
  { name: 'alice' },
  { name: 'bob' }
];

// Result: recipe applied to each element
const result = [
  { fullName: 'Alice' },
  { fullName: 'Bob' }
];

Available Transforms (40+)

Case Transforms: Capitalized, Uppercase, Lowercase, Title Case, Camel Case, Snake Case, Kebab Case

Type Conversions: Number, String, Boolean, Parse JSON, Stringify JSON

String Manipulation: Trim, Trim Start, Trim End, Reverse, Length, Truncate, Pad Start, Pad End

Structural: To Object, Split, Array to Properties, Flatten, Merge

Numeric: Absolute, Round, Floor, Ceil, Increment, Decrement

Date/Time: Format Date, ISO String, Timestamp

Data Operations: Remove Nulls, Remove Empty Strings, Deduplicate, Sort

Usage

This pattern is ideal for:

  • JSON data transformation pipelines
  • API response reshaping
  • Data migration with reproducible recipes
  • Batch transformations on arrays
  • Visual data mapping tools
  • ETL (Extract, Transform, Load) workflows

Why Component-Based with Desk Registration?

Traditional approach (props drilling):

<!-- ❌ Props drilling - brittle and verbose -->
<ObjectTransformer :data="data">
  <ObjectNode :tree="tree" :transforms="transforms" :mode="mode">
    <NodeActions :tree="tree" :transforms="transforms" />
    <NodeKeyEditor :node="node" :tree="tree" @update="handleUpdate" />
  </ObjectNode>
  <ObjectPreview :tree="tree" :original="data" />
</ObjectTransformer>

VueAirport approach (desk registration):

<!-- ✅ No props - components register independently -->
<ObjectTransformer :data="data">
  <ObjectNode />
  <ObjectPreview />
  <TransformString />
  <RecipePreview />
</ObjectTransformer>

Benefits:

  • No props drilling: Components access shared state directly
  • Flexible composition: Add/remove/reorder components freely
  • Clean separation: Each component manages its own registration
  • Type safety: Full TypeScript support through desk context
  • DevTools integration: Automatic state tracking and debugging

The combination of visual editing, portable recipes, template mode, and component-based architecture makes it a powerful tool for complex data transformations.