Data schema spec fix: TSON instead of JSON (#3566)

This commit is contained in:
Jonathan Dobson
2025-09-30 17:32:43 -04:00
committed by GitHub
parent d61179e132
commit 79ffc95e58
7 changed files with 863 additions and 53 deletions

View File

@@ -105,6 +105,12 @@ repos:
types: [javascript]
- repo: local
hooks:
- id: vitest-type-check
name: vitest
entry: bash -c 'cd skyvern-frontend && ([ -d node_modules ] || npm ci) && npm run test'
language: system
pass_filenames: false
files: ^skyvern-frontend/
- id: alembic-check
name: Alembic Check
entry: ./run_alembic_check.sh

View File

@@ -85,7 +85,8 @@
"prettier-plugin-tailwindcss": "^0.6.5",
"tailwindcss": "^3.4.17",
"typescript": "^5.5.3",
"vite": "^5.4.19"
"vite": "^5.4.19",
"vitest": "^3.2.4"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -946,9 +947,10 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
@@ -3810,6 +3812,16 @@
"react": "^18.0.0"
}
},
"node_modules/@types/chai": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz",
"integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/deep-eql": "*"
}
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
@@ -3853,6 +3865,13 @@
"@types/d3-selection": "*"
}
},
"node_modules/@types/deep-eql": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
@@ -4183,6 +4202,94 @@
"vite": "^4 || ^5"
}
},
"node_modules/@vitest/expect": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
"integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/spy": "3.2.4",
"@vitest/utils": "3.2.4",
"chai": "^5.2.0",
"tinyrainbow": "^2.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/pretty-format": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
"integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
"dev": true,
"license": "MIT",
"dependencies": {
"tinyrainbow": "^2.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/runner": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz",
"integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/utils": "3.2.4",
"pathe": "^2.0.3",
"strip-literal": "^3.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/snapshot": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz",
"integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.2.4",
"magic-string": "^0.30.17",
"pathe": "^2.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/spy": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
"integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tinyspy": "^4.0.3"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@vitest/utils": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
"integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/pretty-format": "3.2.4",
"loupe": "^3.1.4",
"tinyrainbow": "^2.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/@xyflow/react": {
"version": "12.1.1",
"resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.1.1.tgz",
@@ -4350,6 +4457,16 @@
"node": ">=8"
}
},
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -4529,6 +4646,16 @@
"node": ">= 0.8"
}
},
"node_modules/cac": {
"version": "6.7.14",
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -4593,6 +4720,23 @@
}
]
},
"node_modules/chai": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
"integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"assertion-error": "^2.0.1",
"check-error": "^2.1.1",
"deep-eql": "^5.0.1",
"loupe": "^3.1.0",
"pathval": "^2.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -4609,6 +4753,16 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/check-error": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
"integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 16"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -5010,6 +5164,16 @@
}
}
},
"node_modules/deep-eql": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -5214,6 +5378,13 @@
"node": ">= 0.4"
}
},
"node_modules/es-module-lexer": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
"dev": true,
"license": "MIT"
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
@@ -5523,6 +5694,16 @@
"node": ">=4.0"
}
},
"node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0"
}
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -5569,6 +5750,16 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/expect-type": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz",
"integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
@@ -6635,6 +6826,23 @@
"loose-envify": "cli.js"
}
},
"node_modules/loupe": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
"integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
"dev": true,
"license": "MIT"
},
"node_modules/magic-string": {
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -7085,6 +7293,23 @@
"node": ">=8"
}
},
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"dev": true,
"license": "MIT"
},
"node_modules/pathval": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
"integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.16"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -8112,6 +8337,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/siginfo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
"dev": true,
"license": "ISC"
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
@@ -8168,6 +8400,13 @@
"node": ">=0.10.0"
}
},
"node_modules/stackback": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
"dev": true,
"license": "MIT"
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -8176,6 +8415,13 @@
"node": ">= 0.8"
}
},
"node_modules/std-env": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
"integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
"dev": true,
"license": "MIT"
},
"node_modules/string-argv": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
@@ -8303,6 +8549,26 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-literal": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz",
"integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"js-tokens": "^9.0.1"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/strip-literal/node_modules/js-tokens": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
"dev": true,
"license": "MIT"
},
"node_modules/style-mod": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
@@ -8478,6 +8744,98 @@
"node": ">=0.8"
}
},
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
"dev": true,
"license": "MIT"
},
"node_modules/tinyexec": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
"dev": true,
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/tinypool": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
"integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.0.0 || >=20.0.0"
}
},
"node_modules/tinyrainbow": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
"integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/tinyspy": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz",
"integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -8774,6 +9132,142 @@
}
}
},
"node_modules/vite-node": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
"integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cac": "^6.7.14",
"debug": "^4.4.1",
"es-module-lexer": "^1.7.0",
"pathe": "^2.0.3",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
},
"bin": {
"vite-node": "vite-node.mjs"
},
"engines": {
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
}
},
"node_modules/vitest": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4",
"@vitest/mocker": "3.2.4",
"@vitest/pretty-format": "^3.2.4",
"@vitest/runner": "3.2.4",
"@vitest/snapshot": "3.2.4",
"@vitest/spy": "3.2.4",
"@vitest/utils": "3.2.4",
"chai": "^5.2.0",
"debug": "^4.4.1",
"expect-type": "^1.2.1",
"magic-string": "^0.30.17",
"pathe": "^2.0.3",
"picomatch": "^4.0.2",
"std-env": "^3.9.0",
"tinybench": "^2.9.0",
"tinyexec": "^0.3.2",
"tinyglobby": "^0.2.14",
"tinypool": "^1.1.1",
"tinyrainbow": "^2.0.0",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
"vite-node": "3.2.4",
"why-is-node-running": "^2.3.0"
},
"bin": {
"vitest": "vitest.mjs"
},
"engines": {
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@edge-runtime/vm": "*",
"@types/debug": "^4.1.12",
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
"@vitest/browser": "3.2.4",
"@vitest/ui": "3.2.4",
"happy-dom": "*",
"jsdom": "*"
},
"peerDependenciesMeta": {
"@edge-runtime/vm": {
"optional": true
},
"@types/debug": {
"optional": true
},
"@types/node": {
"optional": true
},
"@vitest/browser": {
"optional": true
},
"@vitest/ui": {
"optional": true
},
"happy-dom": {
"optional": true
},
"jsdom": {
"optional": true
}
}
},
"node_modules/vitest/node_modules/@vitest/mocker": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz",
"integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@vitest/spy": "3.2.4",
"estree-walker": "^3.0.3",
"magic-string": "^0.30.17"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"msw": "^2.4.9",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
},
"peerDependenciesMeta": {
"msw": {
"optional": true
},
"vite": {
"optional": true
}
}
},
"node_modules/vitest/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
@@ -8793,6 +9287,23 @@
"node": ">= 8"
}
},
"node_modules/why-is-node-running": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
"integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"siginfo": "^2.0.0",
"stackback": "0.0.2"
},
"bin": {
"why-is-node-running": "cli.js"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",

View File

@@ -13,6 +13,7 @@
"precommit": "lint-staged",
"run-artifact-server": "node artifactServer.js",
"serve": "npm run build && node localServer.js",
"test": "vitest run",
"start": "npm run serve & npm run run-artifact-server"
},
"dependencies": {
@@ -93,7 +94,8 @@
"prettier-plugin-tailwindcss": "^0.6.5",
"tailwindcss": "^3.4.17",
"typescript": "^5.5.3",
"vite": "^5.4.19"
"vite": "^5.4.19",
"vitest": "^3.2.4"
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx}": [

View File

@@ -17,6 +17,7 @@ import { AutoResizingTextarea } from "../AutoResizingTextarea/AutoResizingTextar
import { Button } from "../ui/button";
import { AxiosError } from "axios";
import { toast } from "../ui/use-toast";
import { TSON } from "@/util/tson";
import { cn } from "@/util/utils";
type Props = {
@@ -36,42 +37,13 @@ function WorkflowDataSchemaInputGroup({
const [generateWithAIActive, setGenerateWithAIActive] = useState(false);
const [generateWithAIPrompt, setGenerateWithAIPrompt] = useState("");
function computeJsonError(
jsonText: string,
): { message: string; line?: number; column?: number } | null {
try {
JSON.parse(jsonText);
return null;
} catch (e) {
const message = e instanceof Error ? e.message : "Invalid JSON";
// Try to extract position and compute line/column for friendlier feedback
const match = message.match(/position\s+(\d+)/i);
if (!match) {
return { message };
}
const pos = Number(match[1]);
if (Number.isNaN(pos)) {
return { message };
}
let line = 1;
let col = 1;
for (let i = 0; i < Math.min(pos, jsonText.length); i++) {
if (jsonText[i] === "\n") {
line += 1;
col = 1;
} else {
col += 1;
}
}
return { message, line, column: col };
}
}
const jsonError = useMemo(() => {
const tsonResult = useMemo(() => {
if (value === "null") return null;
return computeJsonError(value);
return TSON.parse(value);
}, [value]);
console.log({ tsonResult });
const getDataSchemaSuggestionMutation = useMutation({
mutationFn: async () => {
const client = await getClient(credentialGetter);
@@ -161,7 +133,9 @@ function WorkflowDataSchemaInputGroup({
<div
className={cn(
"rounded-md",
jsonError ? "ring-1 ring-red-500" : undefined,
tsonResult && !tsonResult.success
? "ring-1 ring-red-500"
: undefined,
)}
>
<CodeEditor
@@ -172,12 +146,8 @@ function WorkflowDataSchemaInputGroup({
fontSize={8}
/>
</div>
{jsonError && (
<div className="text-xs text-red-400">
{jsonError.line && jsonError.column
? `Invalid JSON (${jsonError.line}:${jsonError.column}) — ${jsonError.message}`
: `Invalid JSON — ${jsonError.message}`}
</div>
{tsonResult !== null && !tsonResult.success && tsonResult.error && (
<div className="text-xs text-red-400">{tsonResult.error}</div>
)}
</div>
)}

View File

@@ -2,6 +2,9 @@ import Dagre from "@dagrejs/dagre";
import type { Node } from "@xyflow/react";
import { Edge } from "@xyflow/react";
import { nanoid } from "nanoid";
import { TSON } from "@/util/tson";
import {
WorkflowBlockType,
WorkflowBlockTypes,
@@ -2188,10 +2191,12 @@ function getWorkflowErrors(nodes: Array<AppNode>): Array<string> {
}
// Validate Task data schema JSON when enabled (value different from "null")
if (node.data.dataSchema && node.data.dataSchema !== "null") {
try {
JSON.parse(node.data.dataSchema);
} catch {
errors.push(`${node.data.label}: Data schema is not valid JSON.`);
const result = TSON.parse(node.data.dataSchema);
if (!result.success) {
errors.push(
`${node.data.label}: Data schema has invalid templated JSON: ${result.error ?? "-"}`,
);
}
}
});
@@ -2227,10 +2232,12 @@ function getWorkflowErrors(nodes: Array<AppNode>): Array<string> {
}
// Validate Extraction data schema JSON when enabled (value different from "null")
if (node.data.dataSchema && node.data.dataSchema !== "null") {
try {
JSON.parse(node.data.dataSchema);
} catch {
errors.push(`${node.data.label}: Data schema is not valid JSON.`);
const result = TSON.parse(node.data.dataSchema);
if (!result.success) {
errors.push(
`${node.data.label}: Data schema has invalid templated JSON: ${result.error ?? "-"}`,
);
}
}
});

View File

@@ -0,0 +1,91 @@
// TSON.parse.test.ts
import { TSON } from "./tson";
import { describe, test, expect } from "vitest";
describe("TSON.parse", () => {
test("single top-level template works", () => {
const input = "{{ hello }}";
const result = TSON.parse(input);
expect(result.success).toBe(true);
expect(result.data).toEqual("<STUB>");
});
test("preserves double braces inside quoted strings", () => {
const input = '{"a": "{{ hello }}"}';
const result = TSON.parse(input);
expect(result.success).toBe(true);
expect(result.data).toEqual({ a: "{{ hello }}" });
});
test("replaces double braces outside strings with stub", () => {
const input = '{"a": {{ hello }} }';
const result = TSON.parse(input);
expect(result.success).toBe(true);
expect(result.data).toEqual({ a: "<STUB>" });
});
test("handles double braces in keys and values", () => {
const input = `
{
"hello": "world",
{{foo}}: "bar",
"baz": {{quux}}
}`;
const result = TSON.parse(input);
expect(result.success).toBe(true);
expect(result.data).toEqual({
hello: "world",
"<STUB>": "bar",
baz: "<STUB>",
});
});
test("does not allow trailing commas", () => {
const input = `
{
"hello": "world",
{{foo}}: "bar",
"baz": {{quux}},
}`;
const result = TSON.parse(input);
expect(result.success).toBe(false);
expect(result.error).toContain("Expected double-quoted property name");
});
test("detects unclosed double braces", () => {
const input = "{{ unclosed";
const result = TSON.parse(input);
expect(result.success).toBe(false);
expect(result.error).toContain("Unclosed");
});
test("detects unmatched closing double braces", () => {
const input = "closed }}";
const result = TSON.parse(input);
expect(result.success).toBe(false);
expect(result.error).toContain("Unmatched");
});
test("handles nested double braces", () => {
const input = "{{ {{ nested }} }}";
const result = TSON.parse(input);
expect(result.success).toBe(true);
expect(result.data).toEqual("<STUB>");
});
test("handles double braces in arrays", () => {
const input = '[{{ }}, {{ }}, "normal"]';
const result = TSON.parse(input);
expect(result.success).toBe(true);
expect(result.data).toEqual(["<STUB>", "<STUB>", "normal"]);
});
});

View File

@@ -0,0 +1,223 @@
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue };
interface ParseResult {
success: boolean;
data?: JSONValue;
error?: string;
}
const placeholder = () => "<STUB>";
/**
* TSON ("templated JSON") is a superset of JSON, where double curly braces {{...}} can:
* - exist anywhere outside of string literals, and
* - are treated as placeholders
*/
const TSON = {
parse(input: string): ParseResult {
try {
const balanceCheck = checkDoubleBraceBalance(input);
if (!balanceCheck.balanced) {
return {
success: false,
error: balanceCheck.error,
};
}
const pipeline = [
replaceBracesOutsideQuotes,
// removeTrailingCommas,
JSON.parse,
];
const parsed = pipeline.reduce((acc, fn) => fn(acc), input) as JSONValue;
return {
success: true,
data: parsed,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
},
};
function checkDoubleBraceBalance(input: string): {
balanced: boolean;
error?: string;
} {
let inString = false;
let escapeNext = false;
let depth = 0;
for (let i = 0; i < input.length; i++) {
const char = input[i];
const nextChar = input[i + 1];
// handle escape sequences
if (escapeNext) {
escapeNext = false;
continue;
}
if (char === "\\") {
escapeNext = true;
continue;
}
// inside-string tracking
if (char === '"') {
inString = !inString;
continue;
}
// double braces counts (only outside strings)
if (!inString) {
if (char === "{" && nextChar === "{") {
depth++;
i++; // skip next char
} else if (char === "}" && nextChar === "}") {
depth--;
if (depth < 0) {
return {
balanced: false,
error: `Unmatched closing }} at position ${i}`,
};
}
i++; // skip next char
}
}
}
if (depth > 0) {
return {
balanced: false,
error: `Unclosed {{ - missing ${depth} closing }}`,
};
}
return { balanced: true };
}
function replaceBracesOutsideQuotes(input: string): string {
let result = "";
let inString = false;
let escapeNext = false;
let inDoubleBrace = 0; // track nesting depth of {{...}}
for (let i = 0; i < input.length; i++) {
const char = input[i];
const nextChar = input[i + 1];
// escape sequences
if (escapeNext) {
if (inDoubleBrace === 0) {
result += char;
}
escapeNext = false;
continue;
}
if (char === "\\") {
if (inDoubleBrace === 0) {
result += char;
}
escapeNext = true;
continue;
}
// inside-string tracking
if (char === '"') {
inString = !inString;
if (inDoubleBrace === 0) {
result += char;
}
continue;
}
// double braces (only outside strings)
if (!inString) {
if (char === "{" && nextChar === "{") {
if (inDoubleBrace === 0) {
result += `"${placeholder()}"`;
}
inDoubleBrace++;
i++; // skip next char
continue;
} else if (char === "}" && nextChar === "}") {
inDoubleBrace--;
i++; // skip next char
continue;
}
}
// add characters when we're not inside double braces
if (inDoubleBrace === 0) {
result += char;
}
}
return result;
}
export function removeTrailingCommas(input: string): string {
let result = "";
let inString = false;
let escapeNext = false;
for (let i = 0; i < input.length; i++) {
const char = input[i];
// escape sequences
if (escapeNext) {
result += char;
escapeNext = false;
continue;
}
if (char === "\\") {
result += char;
escapeNext = true;
continue;
}
// inside-string tracking
if (char === '"') {
inString = !inString;
result += char;
continue;
}
// check for trailing commas (outside strings)
if (!inString && char === ",") {
// look-ahead for the next non-whitespace character
let j = i + 1;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
while (j < input.length && /\s/.test(input[j])) {
j++;
}
// if next non-whitespace is } or ], skip the comma
if (j < input.length && (input[j] === "}" || input[j] === "]")) {
continue; // Skip this comma
}
}
result += char;
}
return result;
}
export { TSON };