Compare commits
53 Commits
fix-editor
...
fix-files-
Author | SHA1 | Date | |
---|---|---|---|
6be2c02506 | |||
1d92b6ea6f | |||
f683ff6480 | |||
2da60ff4e4 | |||
ae7ff3f46b | |||
7559e9804f | |||
5132850cb0 | |||
6b761cc490 | |||
c674c0cab6 | |||
08c131b52d | |||
618c1e81b1 | |||
c2f4f0b6ff | |||
98da0487e4 | |||
71004c61b2 | |||
170bb45143 | |||
cd59b19ac7 | |||
61235551d3 | |||
86db64a83b | |||
d4c65ad1a3 | |||
aac602d9db | |||
2eb2c4c39b | |||
e8a3944b9e | |||
d0a9c8548c | |||
6c615f1a4f | |||
6a31161c0a | |||
a74f7bf71a | |||
2e68b0b537 | |||
02ea851fb7 | |||
7ed19188d4 | |||
74a4352323 | |||
870783940d | |||
051bf1164a | |||
deb32352fb | |||
de4923ec1e | |||
769f52816f | |||
49ca13a6c8 | |||
dead84ac4d | |||
478a332a2e | |||
2163b1dfb7 | |||
8c3e40975e | |||
62a3d6d8f7 | |||
08d562ee54 | |||
db1410f587 | |||
7a80734c25 | |||
0a21cb2637 | |||
7dd67f72d8 | |||
5bf264b807 | |||
6f6926a621 | |||
1c860bd4d9 | |||
c5247a2aaa | |||
94df975842 | |||
2fbabbd403 | |||
9f0b6a8fdc |
@ -29,7 +29,9 @@ npm run dev
|
||||
|
||||
### Backend
|
||||
|
||||
The backend consists of a primary Express and Socket.io server, and 3 Cloudflare Workers microservices for the D1 database, R2 storage, and Workers AI. The D1 database also contains a [service binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) to the R2 storage worker.
|
||||
The backend consists of a primary Express and Socket.io server, and 3 Cloudflare Workers microservices for the D1 database, R2 storage, and Workers AI. The D1 database also contains a [service binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) to the R2 storage worker. Each open sandbox instantiates a secure Linux sandboxes on E2B, which is used for the terminal and live preview.
|
||||
|
||||
You will need to make an account on [E2B](https://e2b.dev/) to get an API key.
|
||||
|
||||
#### Socket.io server
|
||||
|
||||
@ -181,3 +183,4 @@ It should be in the form `category(scope or module): message` in your commit mes
|
||||
- [Express](https://expressjs.com/)
|
||||
- [Socket.io](https://socket.io/)
|
||||
- [Drizzle ORM](https://orm.drizzle.team/)
|
||||
- [E2B](https://e2b.dev/)
|
||||
|
@ -101,7 +101,7 @@ export default {
|
||||
return success
|
||||
} else if (method === "PUT") {
|
||||
const initSchema = z.object({
|
||||
type: z.enum(["react", "node"]),
|
||||
type: z.string(),
|
||||
name: z.string(),
|
||||
userId: z.string(),
|
||||
visibility: z.enum(["public", "private"]),
|
||||
|
@ -26,7 +26,7 @@ export const sandbox = sqliteTable("sandbox", {
|
||||
.primaryKey()
|
||||
.unique(),
|
||||
name: text("name").notNull(),
|
||||
type: text("type", { enum: ["react", "node"] }).notNull(),
|
||||
type: text("type").notNull(),
|
||||
visibility: text("visibility", { enum: ["public", "private"] }),
|
||||
createdAt: integer("createdAt", { mode: "timestamp_ms" }),
|
||||
userId: text("user_id")
|
||||
|
@ -1,7 +1,13 @@
|
||||
# Set WORKERS_KEY to be the same as KEY in /backend/storage/wrangler.toml.
|
||||
# Set DATABASE_WORKER_URL and STORAGE_WORKER_URL after deploying the workers.
|
||||
# DOKKU_HOST and DOKKU_USERNAME are used to authenticate via SSH with the Dokku server
|
||||
# DOKKU_KEY is the path to an SSH (.pem) key on the local machine
|
||||
|
||||
PORT=4000
|
||||
WORKERS_KEY=
|
||||
DATABASE_WORKER_URL=
|
||||
STORAGE_WORKER_URL=
|
||||
E2B_API_KEY=
|
||||
DOKKU_HOST=
|
||||
DOKKU_USERNAME=
|
||||
DOKKU_KEY=
|
512
backend/server/package-lock.json
generated
512
backend/server/package-lock.json
generated
@ -15,22 +15,25 @@
|
||||
"e2b": "^0.16.1",
|
||||
"express": "^4.19.2",
|
||||
"rate-limiter-flexible": "^5.0.3",
|
||||
"simple-git": "^3.25.0",
|
||||
"socket.io": "^4.7.5",
|
||||
"ssh2": "^1.15.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/ssh2": "^1.15.0",
|
||||
"nodemon": "^3.1.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.24.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz",
|
||||
"integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==",
|
||||
"version": "7.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz",
|
||||
"integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@ -60,9 +63,9 @@
|
||||
}
|
||||
},
|
||||
"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.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
@ -75,10 +78,44 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@kwsites/file-exists": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
|
||||
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@kwsites/file-exists/node_modules/debug": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@kwsites/file-exists/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/@kwsites/promise-deferred": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
|
||||
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz",
|
||||
"integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg=="
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.11",
|
||||
@ -149,9 +186,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz",
|
||||
"integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==",
|
||||
"version": "4.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz",
|
||||
"integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
@ -173,9 +210,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.12.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
|
||||
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
|
||||
"version": "20.14.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.15.tgz",
|
||||
"integrity": "sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
@ -213,11 +250,23 @@
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"dev": true
|
||||
"node_modules/@types/ssh2": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.1.tgz",
|
||||
"integrity": "sha512-ZIbEqKAsi5gj35y4P4vkJYly642wIbY6PqoN0xiyQGshKUGXR9WQjF/iF9mXBQ8uBKy3ezfsCkcoHKhd0BzuDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^18.11.18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ssh2/node_modules/@types/node": {
|
||||
"version": "18.19.44",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.44.tgz",
|
||||
"integrity": "sha512-ZsbGerYg72WMXUIE9fYxtvfzLEuq6q8mKERdWFnqTmOvudMxnz+CBNRoOwJ2kNpFOncrKjT1hZwxjlFgQ9qvQA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
@ -232,9 +281,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
||||
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@ -244,10 +293,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
|
||||
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
|
||||
"version": "8.3.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
|
||||
"integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@ -298,6 +350,14 @@
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/asn1": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
|
||||
"dependencies": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@ -312,6 +372,14 @@
|
||||
"node": "^4.5.0 || >= 5.9"
|
||||
}
|
||||
},
|
||||
"node_modules/bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
|
||||
"dependencies": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
@ -358,12 +426,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.0.1"
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -382,6 +450,15 @@
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/buildcheck": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz",
|
||||
"integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@ -423,14 +500,6 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk/node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@ -527,28 +596,6 @@
|
||||
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/concurrently/node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/concurrently/node_modules/supports-color": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
@ -593,6 +640,20 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/cpu-features": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz",
|
||||
"integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buildcheck": "~0.0.6",
|
||||
"nan": "^2.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
@ -676,9 +737,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/e2b": {
|
||||
"version": "0.16.1",
|
||||
"resolved": "https://registry.npmjs.org/e2b/-/e2b-0.16.1.tgz",
|
||||
"integrity": "sha512-2L1R/REEB+EezD4Q4MmcXXNATjvCYov2lv/69+PY6V95+wl1PZblIMTYAe7USxX6P6sqANxNs+kXqZr6RvXkSw==",
|
||||
"version": "0.16.2",
|
||||
"resolved": "https://registry.npmjs.org/e2b/-/e2b-0.16.2.tgz",
|
||||
"integrity": "sha512-xKmVK4ipgVQPJ/uyyrfH9LnaawERRWt8U2UZhdhGfzdL/QU/OpBjuhoIbFCv1Uy6qXV4nIiJ6Nw4MBC4HmXf1g==",
|
||||
"dependencies": {
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
"normalize-path": "^3.0.0",
|
||||
@ -695,39 +756,6 @@
|
||||
"utf-8-validate": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/e2b/node_modules/utf-8-validate": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.4.tgz",
|
||||
"integrity": "sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/e2b/node_modules/ws": {
|
||||
"version": "8.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
|
||||
"integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@ -747,9 +775,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz",
|
||||
"integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==",
|
||||
"version": "6.5.5",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
|
||||
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
"@types/cors": "^2.8.12",
|
||||
@ -760,16 +788,16 @@
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.11.0"
|
||||
"ws": "~8.17.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
|
||||
"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
@ -783,9 +811,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
@ -803,6 +831,26 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/engine.io/node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
@ -885,9 +933,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@ -1001,12 +1049,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
@ -1161,18 +1208,6 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
@ -1247,6 +1282,12 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/nan": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz",
|
||||
"integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
@ -1267,9 +1308,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz",
|
||||
"integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz",
|
||||
"integrity": "sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
@ -1295,9 +1336,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
@ -1311,25 +1352,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
|
||||
"integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
|
||||
"node_modules/nodemon/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"abbrev": "1"
|
||||
},
|
||||
"bin": {
|
||||
"nopt": "bin/nopt.js"
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
@ -1349,9 +1396,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
|
||||
"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@ -1528,13 +1578,10 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
@ -1630,6 +1677,41 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-git": {
|
||||
"version": "3.25.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.25.0.tgz",
|
||||
"integrity": "sha512-KIY5sBnzc4yEcJXW7Tdv4viEz8KyG+nU0hay+DWZasvdFOYKeUZ6Xc25LUHHjw0tinPT7O1eY6pzX7pRT1K8rw==",
|
||||
"dependencies": {
|
||||
"@kwsites/file-exists": "^1.1.1",
|
||||
"@kwsites/promise-deferred": "^1.1.1",
|
||||
"debug": "^4.3.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/steveukx/git-js?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-git/node_modules/debug": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/simple-git/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
@ -1660,18 +1742,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter": {
|
||||
"version": "2.5.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz",
|
||||
"integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==",
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
|
||||
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
|
||||
"dependencies": {
|
||||
"debug": "~4.3.4",
|
||||
"ws": "~8.11.0"
|
||||
"ws": "~8.17.1"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
@ -1689,6 +1771,26 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/socket.io-adapter/node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
@ -1702,9 +1804,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
@ -1723,9 +1825,9 @@
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/socket.io/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
@ -1748,6 +1850,23 @@
|
||||
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
|
||||
"integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ=="
|
||||
},
|
||||
"node_modules/ssh2": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.15.0.tgz",
|
||||
"integrity": "sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"asn1": "^0.2.6",
|
||||
"bcrypt-pbkdf": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"cpu-features": "~0.0.9",
|
||||
"nan": "^2.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
@ -1781,15 +1900,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
@ -1813,13 +1934,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/touch": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
|
||||
"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
||||
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"nopt": "~1.0.10"
|
||||
},
|
||||
"bin": {
|
||||
"nodetouch": "bin/nodetouch.js"
|
||||
}
|
||||
@ -1876,9 +1994,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
|
||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
|
||||
},
|
||||
"node_modules/tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
@ -1893,9 +2016,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.4.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@ -1925,12 +2048,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/utf-8-validate": {
|
||||
"version": "5.0.10",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
|
||||
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.4.tgz",
|
||||
"integrity": "sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
@ -1977,15 +2099,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
@ -2004,12 +2126,6 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
@ -2045,9 +2161,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.22.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz",
|
||||
"integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==",
|
||||
"version": "3.23.8",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
||||
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
@ -17,13 +17,16 @@
|
||||
"e2b": "^0.16.1",
|
||||
"express": "^4.19.2",
|
||||
"rate-limiter-flexible": "^5.0.3",
|
||||
"simple-git": "^3.25.0",
|
||||
"socket.io": "^4.7.5",
|
||||
"ssh2": "^1.15.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/ssh2": "^1.15.0",
|
||||
"nodemon": "^3.1.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.4.5"
|
||||
|
37
backend/server/src/DokkuClient.ts
Normal file
37
backend/server/src/DokkuClient.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { SSHSocketClient, SSHConfig } from "./SSHSocketClient"
|
||||
|
||||
export interface DokkuResponse {
|
||||
ok: boolean;
|
||||
output: string;
|
||||
}
|
||||
|
||||
export class DokkuClient extends SSHSocketClient {
|
||||
|
||||
constructor(config: SSHConfig) {
|
||||
super(
|
||||
config,
|
||||
"/var/run/dokku-daemon/dokku-daemon.sock"
|
||||
)
|
||||
}
|
||||
|
||||
async sendCommand(command: string): Promise<DokkuResponse> {
|
||||
try {
|
||||
const response = await this.sendData(command);
|
||||
|
||||
if (typeof response !== "string") {
|
||||
throw new Error("Received data is not a string");
|
||||
}
|
||||
|
||||
return JSON.parse(response);
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to send command: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async listApps(): Promise<string[]> {
|
||||
const response = await this.sendCommand("apps:list");
|
||||
return response.output.split("\n").slice(1); // Split by newline and ignore the first line (header)
|
||||
}
|
||||
}
|
||||
|
||||
export { SSHConfig };
|
90
backend/server/src/SSHSocketClient.ts
Normal file
90
backend/server/src/SSHSocketClient.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { Client } from "ssh2";
|
||||
|
||||
export interface SSHConfig {
|
||||
host: string;
|
||||
port?: number;
|
||||
username: string;
|
||||
privateKey: Buffer;
|
||||
}
|
||||
|
||||
export class SSHSocketClient {
|
||||
private conn: Client;
|
||||
private config: SSHConfig;
|
||||
private socketPath: string;
|
||||
private isConnected: boolean = false;
|
||||
|
||||
constructor(config: SSHConfig, socketPath: string) {
|
||||
this.conn = new Client();
|
||||
this.config = { ...config, port: 22};
|
||||
this.socketPath = socketPath;
|
||||
|
||||
this.setupTerminationHandlers();
|
||||
}
|
||||
|
||||
private setupTerminationHandlers() {
|
||||
process.on("SIGINT", this.closeConnection.bind(this));
|
||||
process.on("SIGTERM", this.closeConnection.bind(this));
|
||||
}
|
||||
|
||||
private closeConnection() {
|
||||
console.log("Closing SSH connection...");
|
||||
this.conn.end();
|
||||
this.isConnected = false;
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
connect(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.conn
|
||||
.on("ready", () => {
|
||||
console.log("SSH connection established");
|
||||
this.isConnected = true;
|
||||
resolve();
|
||||
})
|
||||
.on("error", (err) => {
|
||||
console.error("SSH connection error:", err);
|
||||
this.isConnected = false;
|
||||
reject(err);
|
||||
})
|
||||
.on("close", () => {
|
||||
console.log("SSH connection closed");
|
||||
this.isConnected = false;
|
||||
})
|
||||
.connect(this.config);
|
||||
});
|
||||
}
|
||||
|
||||
sendData(data: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.isConnected) {
|
||||
reject(new Error("SSH connection is not established"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.conn.exec(
|
||||
`echo "${data}" | nc -U ${this.socketPath}`,
|
||||
(err, stream) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
stream
|
||||
.on("close", (code: number, signal: string) => {
|
||||
reject(
|
||||
new Error(
|
||||
`Stream closed with code ${code} and signal ${signal}`
|
||||
)
|
||||
);
|
||||
})
|
||||
.on("data", (data: Buffer) => {
|
||||
resolve(data.toString());
|
||||
})
|
||||
.stderr.on("data", (data: Buffer) => {
|
||||
reject(new Error(data.toString()));
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
82
backend/server/src/SecureGitClient.ts
Normal file
82
backend/server/src/SecureGitClient.ts
Normal file
@ -0,0 +1,82 @@
|
||||
import simpleGit, { SimpleGit } from "simple-git";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import os from "os";
|
||||
|
||||
export type FileData = {
|
||||
id: string;
|
||||
data: string;
|
||||
};
|
||||
|
||||
export class SecureGitClient {
|
||||
private gitUrl: string;
|
||||
private sshKeyPath: string;
|
||||
|
||||
constructor(gitUrl: string, sshKeyPath: string) {
|
||||
this.gitUrl = gitUrl;
|
||||
this.sshKeyPath = sshKeyPath;
|
||||
}
|
||||
|
||||
async pushFiles(fileData: FileData[], repository: string): Promise<void> {
|
||||
let tempDir: string | undefined;
|
||||
|
||||
try {
|
||||
// Create a temporary directory
|
||||
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-push-'));
|
||||
console.log(`Temporary directory created: ${tempDir}`);
|
||||
|
||||
// Write files to the temporary directory
|
||||
console.log(`Writing ${fileData.length} files.`);
|
||||
for (const { id, data } of fileData) {
|
||||
const filePath = path.join(tempDir, id);
|
||||
const dirPath = path.dirname(filePath);
|
||||
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(filePath, data);
|
||||
}
|
||||
|
||||
// Initialize the simple-git instance with the temporary directory and custom SSH command
|
||||
const git: SimpleGit = simpleGit(tempDir, {
|
||||
config: [
|
||||
'core.sshCommand=ssh -i ' + this.sshKeyPath + ' -o IdentitiesOnly=yes'
|
||||
]
|
||||
}).outputHandler((_command, stdout, stderr) => {
|
||||
stdout.pipe(process.stdout);
|
||||
stderr.pipe(process.stderr);
|
||||
});;
|
||||
|
||||
// Initialize a new Git repository
|
||||
await git.init();
|
||||
|
||||
// Add remote repository
|
||||
await git.addRemote("origin", `${this.gitUrl}:${repository}`);
|
||||
|
||||
// Add files to the repository
|
||||
for (const {id, data} of fileData) {
|
||||
await git.add(id);
|
||||
}
|
||||
|
||||
// Commit the changes
|
||||
await git.commit("Add files.");
|
||||
|
||||
// Push the changes to the remote repository
|
||||
await git.push("origin", "master", {'--force': null});
|
||||
|
||||
console.log("Files successfully pushed to the repository");
|
||||
|
||||
if (tempDir) {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
console.log(`Temporary directory removed: ${tempDir}`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (tempDir) {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
console.log(`Temporary directory removed: ${tempDir}`);
|
||||
}
|
||||
console.error("Error pushing files to the repository:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@ const processFiles = async (paths: string[], id: string) => {
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
const isFile = i === parts.length - 1 && part.includes(".");
|
||||
const isFile = i === parts.length - 1 && part.length;
|
||||
const existing = current.children.find((child) => child.name === part);
|
||||
|
||||
if (existing) {
|
||||
|
@ -1,10 +1,12 @@
|
||||
import os from "os";
|
||||
import path from "path";
|
||||
import cors from "cors";
|
||||
import express, { Express } from "express";
|
||||
import dotenv from "dotenv";
|
||||
import { createServer } from "http";
|
||||
import { Server } from "socket.io";
|
||||
import { DokkuClient } from "./DokkuClient";
|
||||
import { SecureGitClient, FileData } from "./SecureGitClient";
|
||||
import fs from "fs";
|
||||
|
||||
import { z } from "zod";
|
||||
import { User } from "./types";
|
||||
@ -112,6 +114,25 @@ io.use(async (socket, next) => {
|
||||
|
||||
const lockManager = new LockManager();
|
||||
|
||||
if (!process.env.DOKKU_HOST) console.error('Environment variable DOKKU_HOST is not defined');
|
||||
if (!process.env.DOKKU_USERNAME) console.error('Environment variable DOKKU_USERNAME is not defined');
|
||||
if (!process.env.DOKKU_KEY) console.error('Environment variable DOKKU_KEY is not defined');
|
||||
|
||||
const client =
|
||||
process.env.DOKKU_HOST && process.env.DOKKU_KEY && process.env.DOKKU_USERNAME
|
||||
? new DokkuClient({
|
||||
host: process.env.DOKKU_HOST,
|
||||
username: process.env.DOKKU_USERNAME,
|
||||
privateKey: fs.readFileSync(process.env.DOKKU_KEY),
|
||||
})
|
||||
: null;
|
||||
client?.connect();
|
||||
|
||||
const git = process.env.DOKKU_HOST && process.env.DOKKU_KEY ? new SecureGitClient(
|
||||
`dokku@${process.env.DOKKU_HOST}`,
|
||||
process.env.DOKKU_KEY
|
||||
) : null;
|
||||
|
||||
io.on("connection", async (socket) => {
|
||||
try {
|
||||
if (inactivityTimeout) clearTimeout(inactivityTimeout);
|
||||
@ -137,10 +158,6 @@ io.on("connection", async (socket) => {
|
||||
if (!containers[data.sandboxId]) {
|
||||
containers[data.sandboxId] = await Sandbox.create();
|
||||
console.log("Created container ", data.sandboxId);
|
||||
io.emit(
|
||||
"previewURL",
|
||||
"https://" + containers[data.sandboxId].getHostname(5173)
|
||||
);
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error(`Error creating container ${data.sandboxId}:`, e);
|
||||
@ -192,6 +209,8 @@ io.on("connection", async (socket) => {
|
||||
|
||||
// todo: send diffs + debounce for efficiency
|
||||
socket.on("saveFile", async (fileId: string, body: string) => {
|
||||
if (!fileId) return; // handles saving when no file is open
|
||||
|
||||
try {
|
||||
if (Buffer.byteLength(body, "utf-8") > MAX_BODY_SIZE) {
|
||||
socket.emit(
|
||||
@ -252,6 +271,59 @@ io.on("connection", async (socket) => {
|
||||
}
|
||||
);
|
||||
|
||||
interface CallbackResponse {
|
||||
success: boolean;
|
||||
apps?: string[];
|
||||
message?: string;
|
||||
}
|
||||
|
||||
socket.on(
|
||||
"list",
|
||||
async (callback: (response: CallbackResponse) => void) => {
|
||||
console.log("Retrieving apps list...");
|
||||
try {
|
||||
if (!client) throw Error("Failed to retrieve apps list: No Dokku client")
|
||||
callback({
|
||||
success: true,
|
||||
apps: await client.listApps()
|
||||
});
|
||||
} catch (error) {
|
||||
callback({
|
||||
success: false,
|
||||
message: "Failed to retrieve apps list",
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
socket.on(
|
||||
"deploy",
|
||||
async (callback: (response: CallbackResponse) => void) => {
|
||||
try {
|
||||
// Push the project files to the Dokku server
|
||||
console.log("Deploying project ${data.sandboxId}...");
|
||||
if (!git) throw Error("Failed to retrieve apps list: No git client")
|
||||
// Remove the /project/[id]/ component of each file path:
|
||||
const fixedFilePaths = sandboxFiles.fileData.map((file) => {
|
||||
return {
|
||||
...file,
|
||||
id: file.id.split("/").slice(2).join("/"),
|
||||
};
|
||||
});
|
||||
// Push all files to Dokku.
|
||||
await git.pushFiles(fixedFilePaths, data.sandboxId);
|
||||
callback({
|
||||
success: true,
|
||||
});
|
||||
} catch (error) {
|
||||
callback({
|
||||
success: false,
|
||||
message: "Failed to deploy project: " + error,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
socket.on("createFile", async (name: string, callback) => {
|
||||
try {
|
||||
const size: number = await getProjectSize(data.sandboxId);
|
||||
@ -420,8 +492,26 @@ io.on("connection", async (socket) => {
|
||||
await lockManager.acquireLock(data.sandboxId, async () => {
|
||||
try {
|
||||
terminals[id] = await containers[data.sandboxId].terminal.start({
|
||||
onData: (data: string) => {
|
||||
io.emit("terminalResponse", { id, data });
|
||||
onData: (responseData: string) => {
|
||||
io.emit("terminalResponse", { id, data: responseData });
|
||||
|
||||
function extractPortNumber(inputString: string) {
|
||||
// Remove ANSI escape codes
|
||||
const cleanedString = inputString.replace(/\x1B\[[0-9;]*m/g, '');
|
||||
// Regular expression to match port number
|
||||
const regex = /http:\/\/localhost:(\d+)/;
|
||||
// If a match is found, return the port number
|
||||
const match = cleanedString.match(regex);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
const port = parseInt(extractPortNumber(responseData) ?? "");
|
||||
if (port) {
|
||||
io.emit(
|
||||
"previewURL",
|
||||
"https://" + containers[data.sandboxId].getHostname(port)
|
||||
);
|
||||
}
|
||||
|
||||
},
|
||||
size: { cols: 80, rows: 20 },
|
||||
onExit: () => console.log("Terminal exited", id),
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { z } from "zod"
|
||||
import startercode from "./startercode"
|
||||
|
||||
export interface Env {
|
||||
R2: R2Bucket
|
||||
@ -137,19 +136,25 @@ export default {
|
||||
} else if (path === "/api/init" && method === "POST") {
|
||||
const initSchema = z.object({
|
||||
sandboxId: z.string(),
|
||||
type: z.enum(["react", "node"]),
|
||||
type: z.string(),
|
||||
})
|
||||
|
||||
const body = await request.json()
|
||||
const { sandboxId, type } = initSchema.parse(body)
|
||||
|
||||
console.log(startercode[type])
|
||||
console.log(`Copying template: ${type}`);
|
||||
|
||||
await Promise.all(
|
||||
startercode[type].map(async (file) => {
|
||||
await env.R2.put(`projects/${sandboxId}/${file.name}`, file.body)
|
||||
})
|
||||
)
|
||||
const templateDirectory = `templates/${type}`;
|
||||
|
||||
// List all objects under the directory
|
||||
const { objects } = await env.R2.list({ prefix: templateDirectory });
|
||||
|
||||
// Copy each object to the new directory
|
||||
for (const { key } of objects) {
|
||||
const destinationKey = key.replace(templateDirectory, `projects/${sandboxId}`);
|
||||
const fileBody = await env.R2.get(key).then(res => res?.body ?? "");
|
||||
await env.R2.put(destinationKey, fileBody);
|
||||
}
|
||||
|
||||
return success
|
||||
} else {
|
||||
|
@ -1,151 +0,0 @@
|
||||
const startercode = {
|
||||
node: [
|
||||
{ name: "index.js", body: `console.log("Hello World!")` },
|
||||
{
|
||||
name: "package.json",
|
||||
body: `{
|
||||
"name": "nodejs",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.0.6"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
],
|
||||
react: [
|
||||
{
|
||||
name: "package.json",
|
||||
body: `{
|
||||
"name": "react",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"vite": "^5.2.0"
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "vite.config.js",
|
||||
body: `import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173,
|
||||
host: "0.0.0.0",
|
||||
}
|
||||
})
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "index.html",
|
||||
body: `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>React Starter Code</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "src/App.css",
|
||||
body: `div {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #000;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #777;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
margin-top: 16px;
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "src/App.jsx",
|
||||
body: `import './App.css'
|
||||
import { useState } from 'react'
|
||||
|
||||
function App() {
|
||||
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>React Starter Code</h1>
|
||||
<p>
|
||||
Edit App.jsx to get started.
|
||||
</p>
|
||||
<button onClick={() => setCount(count => count + 1)}>
|
||||
Clicked {count} times
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "src/main.jsx",
|
||||
body: `import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
`,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default startercode
|
@ -3,7 +3,7 @@ CLERK_SECRET_KEY=
|
||||
NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY=
|
||||
LIVEBLOCKS_SECRET_KEY=
|
||||
|
||||
NEXT_PUBLIC_SERVER_PORT=4000
|
||||
NEXT_PUBLIC_SERVER_URL=http://localhost:4000
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
|
||||
# Set WORKER_URLs after deploying the workers.
|
||||
|
@ -63,14 +63,6 @@ const CodeEditor = dynamic(() => import("@/components/editor"), {
|
||||
loading: () => <Loading />,
|
||||
})
|
||||
|
||||
function getReactDefinitionFile() {
|
||||
const reactDefinitionFile = fs.readFileSync(
|
||||
"node_modules/@types/react/index.d.ts",
|
||||
"utf8"
|
||||
)
|
||||
return reactDefinitionFile
|
||||
}
|
||||
|
||||
export default async function CodePage({ params }: { params: { id: string } }) {
|
||||
const user = await currentUser()
|
||||
const sandboxId = params.id
|
||||
@ -94,8 +86,6 @@ export default async function CodePage({ params }: { params: { id: string } }) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const reactDefinitionFile = getReactDefinitionFile()
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden overscroll-none w-screen flex flex-col h-screen bg-background">
|
||||
<Room id={sandboxId}>
|
||||
@ -104,7 +94,6 @@ export default async function CodePage({ params }: { params: { id: string } }) {
|
||||
<CodeEditor
|
||||
userData={userData}
|
||||
sandboxData={sandboxData}
|
||||
reactDefinitionFile={reactDefinitionFile}
|
||||
/>
|
||||
</div>
|
||||
</Room>
|
||||
|
@ -6,6 +6,9 @@ import { ThemeProvider } from "@/components/layout/themeProvider"
|
||||
import { ClerkProvider } from "@clerk/nextjs"
|
||||
import { Toaster } from "@/components/ui/sonner"
|
||||
import { Analytics } from "@vercel/analytics/react"
|
||||
import { TerminalProvider } from '@/context/TerminalContext';
|
||||
import { PreviewProvider } from "@/context/PreviewContext";
|
||||
import { SocketProvider } from '@/context/SocketContext'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Sandbox",
|
||||
@ -13,7 +16,7 @@ export const metadata: Metadata = {
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) {
|
||||
@ -27,7 +30,13 @@ export default function RootLayout({
|
||||
forcedTheme="dark"
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SocketProvider>
|
||||
<PreviewProvider>
|
||||
<TerminalProvider>
|
||||
{children}
|
||||
</TerminalProvider>
|
||||
</PreviewProvider>
|
||||
</SocketProvider>
|
||||
<Analytics />
|
||||
<Toaster position="bottom-left" richColors />
|
||||
</ThemeProvider>
|
||||
@ -35,4 +44,4 @@ export default function RootLayout({
|
||||
</html>
|
||||
</ClerkProvider>
|
||||
)
|
||||
}
|
||||
}
|
13
frontend/app/providers.js
Normal file
13
frontend/app/providers.js
Normal file
@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
import posthog from "posthog-js"
|
||||
import { PostHogProvider } from "posthog-js/react"
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
|
||||
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST,
|
||||
})
|
||||
}
|
||||
|
||||
export function PHProvider({ children }) {
|
||||
return <PostHogProvider client={posthog}>{children}</PostHogProvider>
|
||||
}
|
@ -49,10 +49,8 @@ export default function Dashboard({
|
||||
const q = searchParams.get("q")
|
||||
const router = useRouter()
|
||||
|
||||
useEffect(() => {
|
||||
if (!sandboxes) {
|
||||
router.refresh()
|
||||
}
|
||||
useEffect(() => { // update the dashboard to show a new project
|
||||
router.refresh()
|
||||
}, [sandboxes])
|
||||
|
||||
return (
|
||||
|
@ -37,43 +37,41 @@ import { useRouter } from "next/navigation"
|
||||
import { Loader2 } from "lucide-react"
|
||||
import { Button } from "../ui/button"
|
||||
|
||||
type TOptions = "react" | "node" | "python" | "more"
|
||||
|
||||
const data: {
|
||||
id: TOptions
|
||||
id: string
|
||||
name: string
|
||||
icon: string
|
||||
description: string
|
||||
disabled: boolean
|
||||
}[] = [
|
||||
{
|
||||
id: "react",
|
||||
id: "reactjs",
|
||||
name: "React",
|
||||
icon: "/project-icons/react.svg",
|
||||
description: "A JavaScript library for building user interfaces",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "node",
|
||||
name: "Node",
|
||||
id: "vanillajs",
|
||||
name: "HTML/JS",
|
||||
icon: "/project-icons/more.svg",
|
||||
description: "More coming soon, feel free to contribute on GitHub",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "nextjs",
|
||||
name: "NextJS",
|
||||
icon: "/project-icons/node.svg",
|
||||
description: "A JavaScript runtime built on the V8 JavaScript engine",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "python",
|
||||
name: "Python",
|
||||
id: "streamlit",
|
||||
name: "Streamlit",
|
||||
icon: "/project-icons/python.svg",
|
||||
description: "A high-level, general-purpose language, coming soon",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
id: "more",
|
||||
name: "More Languages",
|
||||
icon: "/project-icons/more.svg",
|
||||
description: "More coming soon, feel free to contribute on GitHub",
|
||||
disabled: true,
|
||||
},
|
||||
description: "A JavaScript runtime built on the V8 JavaScript engine",
|
||||
disabled: false,
|
||||
}
|
||||
]
|
||||
|
||||
const formSchema = z.object({
|
||||
@ -95,7 +93,7 @@ export default function NewProjectModal({
|
||||
open: boolean
|
||||
setOpen: (open: boolean) => void
|
||||
}) {
|
||||
const [selected, setSelected] = useState<TOptions>("react")
|
||||
const [selected, setSelected] = useState("react")
|
||||
const [loading, setLoading] = useState(false)
|
||||
const router = useRouter()
|
||||
|
||||
|
@ -12,7 +12,7 @@ import { toast } from "sonner";
|
||||
import { useEffect, useState } from "react";
|
||||
import { CanvasRevealEffect } from "./projectCard/revealEffect";
|
||||
|
||||
const colors = {
|
||||
const colors: { [key: string]: number[][] } = {
|
||||
react: [
|
||||
[71, 207, 237],
|
||||
[30, 126, 148],
|
||||
|
@ -3,7 +3,6 @@
|
||||
import { SetStateAction, useCallback, useEffect, useRef, useState } from "react"
|
||||
import monaco from "monaco-editor"
|
||||
import Editor, { BeforeMount, OnMount } from "@monaco-editor/react"
|
||||
import { Socket, io } from "socket.io-client"
|
||||
import { toast } from "sonner"
|
||||
import { useClerk } from "@clerk/nextjs"
|
||||
|
||||
@ -31,27 +30,32 @@ import Loading from "./loading"
|
||||
import PreviewWindow from "./preview"
|
||||
import Terminals from "./terminals"
|
||||
import { ImperativePanelHandle } from "react-resizable-panels"
|
||||
import { PreviewProvider, usePreview } from '@/context/PreviewContext';
|
||||
import { useSocket } from "@/context/SocketContext"
|
||||
|
||||
export default function CodeEditor({
|
||||
userData,
|
||||
sandboxData,
|
||||
reactDefinitionFile,
|
||||
}: {
|
||||
userData: User
|
||||
sandboxData: Sandbox
|
||||
reactDefinitionFile: string
|
||||
}) {
|
||||
const socketRef = useRef<Socket | null>(null);
|
||||
|
||||
// Initialize socket connection if it doesn't exist
|
||||
if (!socketRef.current) {
|
||||
socketRef.current = io(
|
||||
`${window.location.protocol}//${window.location.hostname}:${process.env.NEXT_PUBLIC_SERVER_PORT}?userId=${userData.id}&sandboxId=${sandboxData.id}`,
|
||||
{
|
||||
timeout: 2000,
|
||||
//SocketContext functions and effects
|
||||
const { socket, setUserAndSandboxId } = useSocket();
|
||||
|
||||
useEffect(() => {
|
||||
// Ensure userData.id and sandboxData.id are available before attempting to connect
|
||||
if (userData.id && sandboxData.id) {
|
||||
// Check if the socket is not initialized or not connected
|
||||
if (!socket || (socket && !socket.connected)) {
|
||||
// Initialize socket connection
|
||||
setUserAndSandboxId(userData.id, sandboxData.id);
|
||||
}
|
||||
);}
|
||||
}
|
||||
}, [socket, userData.id, sandboxData.id, setUserAndSandboxId]);
|
||||
|
||||
//Preview Button state
|
||||
const [isPreviewCollapsed, setIsPreviewCollapsed] = useState(true)
|
||||
const [disableAccess, setDisableAccess] = useState({
|
||||
isDisabled: false,
|
||||
@ -97,6 +101,13 @@ export default function CodeEditor({
|
||||
// Preview state
|
||||
const [previewURL, setPreviewURL] = useState<string>("");
|
||||
|
||||
const loadPreviewURL = (url: string) => {
|
||||
// This will cause a reload if previewURL changed.
|
||||
setPreviewURL(url);
|
||||
// If the URL didn't change, still reload the preview.
|
||||
previewWindowRef.current?.refreshIframe();
|
||||
}
|
||||
|
||||
const isOwner = sandboxData.userId === userData.id
|
||||
const clerk = useClerk()
|
||||
|
||||
@ -105,6 +116,16 @@ export default function CodeEditor({
|
||||
const [provider, setProvider] = useState<TypedLiveblocksProvider>()
|
||||
const userInfo = useSelf((me) => me.info)
|
||||
|
||||
// Liveblocks providers map to prevent reinitializing providers
|
||||
type ProviderData = {
|
||||
provider: LiveblocksProvider<never, never, never, never>;
|
||||
yDoc: Y.Doc;
|
||||
yText: Y.Text;
|
||||
binding?: MonacoBinding;
|
||||
onSync: (isSynced: boolean) => void;
|
||||
};
|
||||
const providersMap = useRef(new Map<string, ProviderData>());
|
||||
|
||||
// Refs for libraries / features
|
||||
const editorContainerRef = useRef<HTMLDivElement>(null)
|
||||
const monacoRef = useRef<typeof monaco | null>(null)
|
||||
@ -112,6 +133,7 @@ export default function CodeEditor({
|
||||
const generateWidgetRef = useRef<HTMLDivElement>(null)
|
||||
const previewPanelRef = useRef<ImperativePanelHandle>(null)
|
||||
const editorPanelRef = useRef<ImperativePanelHandle>(null)
|
||||
const previewWindowRef = useRef<{ refreshIframe: () => void }>(null)
|
||||
|
||||
// Pre-mount editor keybindings
|
||||
const handleEditorWillMount: BeforeMount = (monaco) => {
|
||||
@ -306,9 +328,9 @@ export default function CodeEditor({
|
||||
);
|
||||
console.log(`Saving file...${activeFileId}`);
|
||||
console.log(`Saving file...${value}`);
|
||||
socketRef.current?.emit("saveFile", activeFileId, value);
|
||||
}, Number(process.env.FILE_SAVE_DEBOUNCE_DELAY)||1000),
|
||||
[socketRef]
|
||||
socket?.emit("saveFile", activeFileId, value);
|
||||
}, Number(process.env.FILE_SAVE_DEBOUNCE_DELAY) || 1000),
|
||||
[socket]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -332,56 +354,88 @@ export default function CodeEditor({
|
||||
|
||||
if (!editorRef || !tab || !model) return
|
||||
|
||||
const yDoc = new Y.Doc()
|
||||
const yText = yDoc.getText(tab.id)
|
||||
const yProvider: any = new LiveblocksProvider(room, yDoc)
|
||||
let providerData: ProviderData;
|
||||
|
||||
const onSync = (isSynced: boolean) => {
|
||||
if (isSynced) {
|
||||
const text = yText.toString()
|
||||
if (text === "") {
|
||||
if (activeFileContent) {
|
||||
yText.insert(0, activeFileContent)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
yText.insert(0, editorRef.getValue())
|
||||
}, 0)
|
||||
// When a file is opened for the first time, create a new provider and store in providersMap.
|
||||
if (!providersMap.current.has(tab.id)) {
|
||||
const yDoc = new Y.Doc();
|
||||
const yText = yDoc.getText(tab.id);
|
||||
const yProvider = new LiveblocksProvider(room, yDoc);
|
||||
|
||||
// Inserts the file content into the editor once when the tab is changed.
|
||||
const onSync = (isSynced: boolean) => {
|
||||
if (isSynced) {
|
||||
const text = yText.toString()
|
||||
if (text === "") {
|
||||
if (activeFileContent) {
|
||||
yText.insert(0, activeFileContent)
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
yText.insert(0, editorRef.getValue())
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yProvider.on("sync", onSync)
|
||||
|
||||
// Save the provider to the map.
|
||||
providerData = { provider: yProvider, yDoc, yText, onSync };
|
||||
providersMap.current.set(tab.id, providerData);
|
||||
|
||||
} else {
|
||||
// When a tab is opened that has been open before, reuse the existing provider.
|
||||
providerData = providersMap.current.get(tab.id)!;
|
||||
}
|
||||
|
||||
yProvider.on("sync", onSync)
|
||||
|
||||
setProvider(yProvider)
|
||||
|
||||
const binding = new MonacoBinding(
|
||||
yText,
|
||||
providerData.yText,
|
||||
model,
|
||||
new Set([editorRef]),
|
||||
yProvider.awareness as Awareness
|
||||
)
|
||||
providerData.provider.awareness as unknown as Awareness
|
||||
);
|
||||
|
||||
providerData.binding = binding;
|
||||
setProvider(providerData.provider);
|
||||
|
||||
return () => {
|
||||
yDoc.destroy()
|
||||
yProvider.destroy()
|
||||
binding.destroy()
|
||||
yProvider.off("sync", onSync)
|
||||
}
|
||||
}, [editorRef, room, activeFileContent])
|
||||
// Cleanup logic
|
||||
if (binding) {
|
||||
binding.destroy();
|
||||
}
|
||||
if (providerData.binding) {
|
||||
providerData.binding = undefined;
|
||||
}
|
||||
};
|
||||
}, [room, activeFileContent]);
|
||||
|
||||
// Added this effect to clean up when the component unmounts
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// Clean up all providers when the component unmounts
|
||||
providersMap.current.forEach((data) => {
|
||||
if (data.binding) {
|
||||
data.binding.destroy();
|
||||
}
|
||||
data.provider.disconnect();
|
||||
data.yDoc.destroy();
|
||||
});
|
||||
providersMap.current.clear();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Connection/disconnection effect
|
||||
useEffect(() => {
|
||||
socketRef.current?.connect()
|
||||
|
||||
socket?.connect()
|
||||
return () => {
|
||||
socketRef.current?.disconnect()
|
||||
socket?.disconnect()
|
||||
}
|
||||
}, [])
|
||||
}, [socket])
|
||||
|
||||
// Socket event listener effect
|
||||
useEffect(() => {
|
||||
const onConnect = () => {}
|
||||
const onConnect = () => { }
|
||||
|
||||
const onDisconnect = () => {
|
||||
setTerminals([])
|
||||
@ -410,25 +464,24 @@ export default function CodeEditor({
|
||||
})
|
||||
}
|
||||
|
||||
socketRef.current?.on("connect", onConnect)
|
||||
socketRef.current?.on("disconnect", onDisconnect)
|
||||
socketRef.current?.on("loaded", onLoadedEvent)
|
||||
socketRef.current?.on("error", onError)
|
||||
socketRef.current?.on("terminalResponse", onTerminalResponse)
|
||||
socketRef.current?.on("disableAccess", onDisableAccess)
|
||||
socketRef.current?.on("previewURL", setPreviewURL)
|
||||
socket?.on("connect", onConnect)
|
||||
socket?.on("disconnect", onDisconnect)
|
||||
socket?.on("loaded", onLoadedEvent)
|
||||
socket?.on("error", onError)
|
||||
socket?.on("terminalResponse", onTerminalResponse)
|
||||
socket?.on("disableAccess", onDisableAccess)
|
||||
socket?.on("previewURL", loadPreviewURL)
|
||||
|
||||
return () => {
|
||||
socketRef.current?.off("connect", onConnect)
|
||||
socketRef.current?.off("disconnect", onDisconnect)
|
||||
socketRef.current?.off("loaded", onLoadedEvent)
|
||||
socketRef.current?.off("error", onError)
|
||||
socketRef.current?.off("terminalResponse", onTerminalResponse)
|
||||
socketRef.current?.off("disableAccess", onDisableAccess)
|
||||
socketRef.current?.off("previewURL", setPreviewURL)
|
||||
socket?.off("connect", onConnect)
|
||||
socket?.off("disconnect", onDisconnect)
|
||||
socket?.off("loaded", onLoadedEvent)
|
||||
socket?.off("error", onError)
|
||||
socket?.off("terminalResponse", onTerminalResponse)
|
||||
socket?.off("disableAccess", onDisableAccess)
|
||||
socket?.off("previewURL", loadPreviewURL)
|
||||
}
|
||||
// }, []);
|
||||
}, [terminals])
|
||||
}, [socket, terminals, setTerminals, setFiles, toast, setDisableAccess, isOwner, loadPreviewURL]);
|
||||
|
||||
// Helper functions for tabs:
|
||||
|
||||
@ -440,7 +493,7 @@ export default function CodeEditor({
|
||||
// Debounced function to get file content
|
||||
const debouncedGetFile = useCallback(
|
||||
debounce((tabId, callback) => {
|
||||
socketRef.current?.emit('getFile', tabId, callback);
|
||||
socket?.emit('getFile', tabId, callback);
|
||||
}, 300), // 300ms debounce delay, adjust as needed
|
||||
[]
|
||||
);
|
||||
@ -486,8 +539,8 @@ export default function CodeEditor({
|
||||
? numTabs === 1
|
||||
? null
|
||||
: index < numTabs - 1
|
||||
? tabs[index + 1].id
|
||||
: tabs[index - 1].id
|
||||
? tabs[index + 1].id
|
||||
: tabs[index - 1].id
|
||||
: activeFileId
|
||||
|
||||
setTabs((prev) => prev.filter((t) => t.id !== id))
|
||||
@ -544,7 +597,7 @@ export default function CodeEditor({
|
||||
return false
|
||||
}
|
||||
|
||||
socketRef.current?.emit("renameFile", id, newName)
|
||||
socket?.emit("renameFile", id, newName)
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) => (tab.id === id ? { ...tab, name: newName } : tab))
|
||||
)
|
||||
@ -553,7 +606,7 @@ export default function CodeEditor({
|
||||
}
|
||||
|
||||
const handleDeleteFile = (file: TFile) => {
|
||||
socketRef.current?.emit("deleteFile", file.id, (response: (TFolder | TFile)[]) => {
|
||||
socket?.emit("deleteFile", file.id, (response: (TFolder | TFile)[]) => {
|
||||
setFiles(response)
|
||||
})
|
||||
closeTab(file.id)
|
||||
@ -563,11 +616,11 @@ export default function CodeEditor({
|
||||
setDeletingFolderId(folder.id)
|
||||
console.log("deleting folder", folder.id)
|
||||
|
||||
socketRef.current?.emit("getFolder", folder.id, (response: string[]) =>
|
||||
socket?.emit("getFolder", folder.id, (response: string[]) =>
|
||||
closeTabs(response)
|
||||
)
|
||||
|
||||
socketRef.current?.emit("deleteFolder", folder.id, (response: (TFolder | TFile)[]) => {
|
||||
socket?.emit("deleteFolder", folder.id, (response: (TFolder | TFile)[]) => {
|
||||
setFiles(response)
|
||||
setDeletingFolderId("")
|
||||
})
|
||||
@ -580,7 +633,7 @@ export default function CodeEditor({
|
||||
<DisableAccessModal
|
||||
message={disableAccess.message}
|
||||
open={disableAccess.isDisabled}
|
||||
setOpen={() => {}}
|
||||
setOpen={() => { }}
|
||||
/>
|
||||
<Loading />
|
||||
</>
|
||||
@ -589,216 +642,211 @@ export default function CodeEditor({
|
||||
return (
|
||||
<>
|
||||
{/* Copilot DOM elements */}
|
||||
<div ref={generateRef} />
|
||||
<div className="z-50 p-1" ref={generateWidgetRef}>
|
||||
{generate.show && ai ? (
|
||||
<GenerateInput
|
||||
user={userData}
|
||||
socket={socketRef.current}
|
||||
width={generate.width - 90}
|
||||
data={{
|
||||
fileName: tabs.find((t) => t.id === activeFileId)?.name ?? "",
|
||||
code: editorRef?.getValue() ?? "",
|
||||
line: generate.line,
|
||||
}}
|
||||
editor={{
|
||||
language: editorLanguage,
|
||||
}}
|
||||
onExpand={() => {
|
||||
editorRef?.changeViewZones(function (changeAccessor) {
|
||||
changeAccessor.removeZone(generate.id)
|
||||
<PreviewProvider>
|
||||
<div ref={generateRef} />
|
||||
<div className="z-50 p-1" ref={generateWidgetRef}>
|
||||
{generate.show && ai ? (
|
||||
<GenerateInput
|
||||
user={userData}
|
||||
socket={socket!}
|
||||
width={generate.width - 90}
|
||||
data={{
|
||||
fileName: tabs.find((t) => t.id === activeFileId)?.name ?? "",
|
||||
code: editorRef?.getValue() ?? "",
|
||||
line: generate.line,
|
||||
}}
|
||||
editor={{
|
||||
language: editorLanguage,
|
||||
}}
|
||||
onExpand={() => {
|
||||
editorRef?.changeViewZones(function (changeAccessor) {
|
||||
changeAccessor.removeZone(generate.id)
|
||||
|
||||
if (!generateRef.current) return
|
||||
const id = changeAccessor.addZone({
|
||||
afterLineNumber: cursorLine,
|
||||
heightInLines: 12,
|
||||
domNode: generateRef.current,
|
||||
if (!generateRef.current) return
|
||||
const id = changeAccessor.addZone({
|
||||
afterLineNumber: cursorLine,
|
||||
heightInLines: 12,
|
||||
domNode: generateRef.current,
|
||||
})
|
||||
setGenerate((prev) => {
|
||||
return { ...prev, id }
|
||||
})
|
||||
})
|
||||
}}
|
||||
onAccept={(code: string) => {
|
||||
const line = generate.line
|
||||
setGenerate((prev) => {
|
||||
return { ...prev, id }
|
||||
return {
|
||||
...prev,
|
||||
show: !prev.show,
|
||||
}
|
||||
})
|
||||
})
|
||||
}}
|
||||
onAccept={(code: string) => {
|
||||
const line = generate.line
|
||||
setGenerate((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
show: !prev.show,
|
||||
}
|
||||
})
|
||||
const file = editorRef?.getValue()
|
||||
const file = editorRef?.getValue()
|
||||
|
||||
const lines = file?.split("\n") || []
|
||||
lines.splice(line - 1, 0, code)
|
||||
const updatedFile = lines.join("\n")
|
||||
editorRef?.setValue(updatedFile)
|
||||
}}
|
||||
onClose={() => {
|
||||
setGenerate((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
show: !prev.show,
|
||||
}
|
||||
})
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
const lines = file?.split("\n") || []
|
||||
lines.splice(line - 1, 0, code)
|
||||
const updatedFile = lines.join("\n")
|
||||
editorRef?.setValue(updatedFile)
|
||||
}}
|
||||
onClose={() => {
|
||||
setGenerate((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
show: !prev.show,
|
||||
}
|
||||
})
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* Main editor components */}
|
||||
<Sidebar
|
||||
sandboxData={sandboxData}
|
||||
files={files}
|
||||
selectFile={selectFile}
|
||||
handleRename={handleRename}
|
||||
handleDeleteFile={handleDeleteFile}
|
||||
handleDeleteFolder={handleDeleteFolder}
|
||||
socket={socketRef.current}
|
||||
setFiles={setFiles}
|
||||
addNew={(name, type) => addNew(name, type, setFiles, sandboxData)}
|
||||
deletingFolderId={deletingFolderId}
|
||||
// AI Copilot Toggle
|
||||
ai={ai}
|
||||
setAi={setAi}
|
||||
/>
|
||||
{/* Main editor components */}
|
||||
<Sidebar
|
||||
sandboxData={sandboxData}
|
||||
files={files}
|
||||
selectFile={selectFile}
|
||||
handleRename={handleRename}
|
||||
handleDeleteFile={handleDeleteFile}
|
||||
handleDeleteFolder={handleDeleteFolder}
|
||||
socket={socket!}
|
||||
setFiles={setFiles}
|
||||
addNew={(name, type) => addNew(name, type, setFiles, sandboxData)}
|
||||
deletingFolderId={deletingFolderId}
|
||||
// AI Copilot Toggle
|
||||
ai={ai}
|
||||
setAi={setAi}
|
||||
/>
|
||||
|
||||
{/* Shadcn resizeable panels: https://ui.shadcn.com/docs/components/resizable */}
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel
|
||||
className="p-2 flex flex-col"
|
||||
maxSize={80}
|
||||
minSize={30}
|
||||
defaultSize={60}
|
||||
ref={editorPanelRef}
|
||||
>
|
||||
<div className="h-10 w-full flex gap-2 overflow-auto tab-scroll">
|
||||
{/* File tabs */}
|
||||
{tabs.map((tab) => (
|
||||
<Tab
|
||||
key={tab.id}
|
||||
saved={tab.saved}
|
||||
selected={activeFileId === tab.id}
|
||||
onClick={(e) => {
|
||||
selectFile(tab)
|
||||
}}
|
||||
onClose={() => closeTab(tab.id)}
|
||||
>
|
||||
{tab.name}
|
||||
</Tab>
|
||||
))}
|
||||
</div>
|
||||
{/* Monaco editor */}
|
||||
<div
|
||||
ref={editorContainerRef}
|
||||
className="grow w-full overflow-hidden rounded-md relative"
|
||||
{/* Shadcn resizeable panels: https://ui.shadcn.com/docs/components/resizable */}
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel
|
||||
className="p-2 flex flex-col"
|
||||
maxSize={80}
|
||||
minSize={30}
|
||||
defaultSize={60}
|
||||
ref={editorPanelRef}
|
||||
>
|
||||
{!activeFileId ? (
|
||||
<>
|
||||
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-muted-foreground/50 select-none">
|
||||
<FileJson className="w-6 h-6 mr-3" />
|
||||
No file selected.
|
||||
</div>
|
||||
</>
|
||||
) : // Note clerk.loaded is required here due to a bug: https://github.com/clerk/javascript/issues/1643
|
||||
clerk.loaded ? (
|
||||
<>
|
||||
{provider && userInfo ? (
|
||||
<Cursors yProvider={provider} userInfo={userInfo} />
|
||||
) : null}
|
||||
<Editor
|
||||
height="100%"
|
||||
language={editorLanguage}
|
||||
beforeMount={handleEditorWillMount}
|
||||
onMount={handleEditorMount}
|
||||
onChange={(value) => {
|
||||
if (value === activeFileContent) {
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) =>
|
||||
tab.id === activeFileId
|
||||
? { ...tab, saved: true }
|
||||
: tab
|
||||
)
|
||||
)
|
||||
} else {
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) =>
|
||||
tab.id === activeFileId
|
||||
? { ...tab, saved: false }
|
||||
: tab
|
||||
)
|
||||
)
|
||||
}
|
||||
<div className="h-10 w-full flex gap-2 overflow-auto tab-scroll">
|
||||
{/* File tabs */}
|
||||
{tabs.map((tab) => (
|
||||
<Tab
|
||||
key={tab.id}
|
||||
saved={tab.saved}
|
||||
selected={activeFileId === tab.id}
|
||||
onClick={(e) => {
|
||||
selectFile(tab)
|
||||
}}
|
||||
options={{
|
||||
tabSize: 2,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
padding: {
|
||||
bottom: 4,
|
||||
top: 4,
|
||||
},
|
||||
scrollBeyondLastLine: false,
|
||||
fixedOverflowWidgets: true,
|
||||
fontFamily: "var(--font-geist-mono)",
|
||||
}}
|
||||
theme="vs-dark"
|
||||
value={activeFileContent}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-muted-foreground/50 select-none">
|
||||
<Loader2 className="animate-spin w-6 h-6 mr-3" />
|
||||
Waiting for Clerk to load...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel defaultSize={40}>
|
||||
<ResizablePanelGroup direction="vertical">
|
||||
<ResizablePanel
|
||||
ref={previewPanelRef}
|
||||
defaultSize={4}
|
||||
collapsedSize={4}
|
||||
minSize={25}
|
||||
collapsible
|
||||
className="p-2 flex flex-col"
|
||||
onCollapse={() => setIsPreviewCollapsed(true)}
|
||||
onExpand={() => setIsPreviewCollapsed(false)}
|
||||
onClose={() => closeTab(tab.id)}
|
||||
>
|
||||
{tab.name}
|
||||
</Tab>
|
||||
))}
|
||||
</div>
|
||||
{/* Monaco editor */}
|
||||
<div
|
||||
ref={editorContainerRef}
|
||||
className="grow w-full overflow-hidden rounded-md relative"
|
||||
>
|
||||
<PreviewWindow
|
||||
collapsed={isPreviewCollapsed}
|
||||
open={() => {
|
||||
previewPanelRef.current?.expand()
|
||||
setIsPreviewCollapsed(false)
|
||||
}}
|
||||
src={previewURL}
|
||||
/>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel
|
||||
defaultSize={50}
|
||||
minSize={20}
|
||||
className="p-2 flex flex-col"
|
||||
>
|
||||
{isOwner ? (
|
||||
<Terminals
|
||||
terminals={terminals}
|
||||
setTerminals={setTerminals}
|
||||
socket={socketRef.current}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-lg font-medium text-muted-foreground/50 select-none">
|
||||
<TerminalSquare className="w-4 h-4 mr-2" />
|
||||
No terminal access.
|
||||
</div>
|
||||
)}
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
{!activeFileId ? (
|
||||
<>
|
||||
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-muted-foreground/50 select-none">
|
||||
<FileJson className="w-6 h-6 mr-3" />
|
||||
No file selected.
|
||||
</div>
|
||||
</>
|
||||
) : // Note clerk.loaded is required here due to a bug: https://github.com/clerk/javascript/issues/1643
|
||||
clerk.loaded ? (
|
||||
<>
|
||||
{provider && userInfo ? (
|
||||
<Cursors yProvider={provider} userInfo={userInfo} />
|
||||
) : null}
|
||||
<Editor
|
||||
height="100%"
|
||||
language={editorLanguage}
|
||||
beforeMount={handleEditorWillMount}
|
||||
onMount={handleEditorMount}
|
||||
onChange={(value) => {
|
||||
if (value === activeFileContent) {
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) =>
|
||||
tab.id === activeFileId
|
||||
? { ...tab, saved: true }
|
||||
: tab
|
||||
)
|
||||
)
|
||||
} else {
|
||||
setTabs((prev) =>
|
||||
prev.map((tab) =>
|
||||
tab.id === activeFileId
|
||||
? { ...tab, saved: false }
|
||||
: tab
|
||||
)
|
||||
)
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
tabSize: 2,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
padding: {
|
||||
bottom: 4,
|
||||
top: 4,
|
||||
},
|
||||
scrollBeyondLastLine: false,
|
||||
fixedOverflowWidgets: true,
|
||||
fontFamily: "var(--font-geist-mono)",
|
||||
}}
|
||||
theme="vs-dark"
|
||||
value={activeFileContent}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-xl font-medium text-muted-foreground/50 select-none">
|
||||
<Loader2 className="animate-spin w-6 h-6 mr-3" />
|
||||
Waiting for Clerk to load...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel defaultSize={40}>
|
||||
<ResizablePanelGroup direction="vertical">
|
||||
<ResizablePanel
|
||||
ref={usePreview().previewPanelRef}
|
||||
defaultSize={4}
|
||||
collapsedSize={4}
|
||||
minSize={25}
|
||||
collapsible
|
||||
className="p-2 flex flex-col"
|
||||
onCollapse={() => setIsPreviewCollapsed(true)}
|
||||
onExpand={() => setIsPreviewCollapsed(false)}
|
||||
>
|
||||
<PreviewWindow
|
||||
open={() => {
|
||||
usePreview().previewPanelRef.current?.expand()
|
||||
setIsPreviewCollapsed(false)
|
||||
}} collapsed={isPreviewCollapsed} src={previewURL} ref={previewWindowRef} />
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel
|
||||
defaultSize={50}
|
||||
minSize={20}
|
||||
className="p-2 flex flex-col"
|
||||
>
|
||||
{isOwner ? (
|
||||
<Terminals />
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-lg font-medium text-muted-foreground/50 select-none">
|
||||
<TerminalSquare className="w-4 h-4 mr-2" />
|
||||
No terminal access.
|
||||
</div>
|
||||
)}
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
</PreviewProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
78
frontend/components/editor/navbar/deploy.tsx
Normal file
78
frontend/components/editor/navbar/deploy.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useTerminal } from "@/context/TerminalContext";
|
||||
import { Play, Pause, Globe, Globe2 } from "lucide-react";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { Sandbox, User } from "@/lib/types";
|
||||
|
||||
export default function DeployButtonModal({
|
||||
userData,
|
||||
data,
|
||||
}: {
|
||||
userData: User;
|
||||
data: Sandbox;
|
||||
}) {
|
||||
const { deploy } = useTerminal();
|
||||
const [isDeploying, setIsDeploying] = useState(false);
|
||||
|
||||
const handleDeploy = () => {
|
||||
if (isDeploying) {
|
||||
console.log("Stopping deployment...");
|
||||
setIsDeploying(false);
|
||||
} else {
|
||||
console.log("Starting deployment...");
|
||||
setIsDeploying(true);
|
||||
deploy(() => {
|
||||
setIsDeploying(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline">
|
||||
<Globe className="w-4 h-4 mr-2" />
|
||||
Deploy
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="p-4 w-full max-w-xs sm:max-w-sm md:max-w-md lg:max-w-lg xl:max-w-xl rounded-lg shadow-lg" style={{ backgroundColor: 'rgb(10,10,10)', color: 'white' }}>
|
||||
<h3 className="font-semibold text-gray-300 mb-2">Domains</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
<DeploymentOption
|
||||
icon={<Globe className="text-gray-500 w-5 h-5" />}
|
||||
domain={`${data.id}.gitwit.app`}
|
||||
timestamp="Deployed 1h ago"
|
||||
user={userData.name}
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline" className="mt-4 w-full bg-[#0a0a0a] text-white hover:bg-[#262626]" onClick={handleDeploy}>
|
||||
{isDeploying ? "Deploying..." : "Update"}
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function DeploymentOption({ icon, domain, timestamp, user }: { icon: React.ReactNode; domain: string; timestamp: string; user: string }) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2 w-full text-left p-2 rounded-md border border-gray-700 bg-gray-900">
|
||||
<div className="flex items-start gap-2 relative">
|
||||
<div className="flex-shrink-0">{icon}</div>
|
||||
<a
|
||||
href={`https://${domain}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-semibold text-gray-300 hover:underline"
|
||||
>
|
||||
{domain}
|
||||
</a>
|
||||
</div>
|
||||
<p className="text-sm text-gray-400 mt-0 ml-7">{timestamp} • {user}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -11,6 +11,8 @@ import { useState } from "react";
|
||||
import EditSandboxModal from "./edit";
|
||||
import ShareSandboxModal from "./share";
|
||||
import { Avatars } from "../live/avatars";
|
||||
import RunButtonModal from "./run";
|
||||
import DeployButtonModal from "./deploy";
|
||||
|
||||
export default function Navbar({
|
||||
userData,
|
||||
@ -19,15 +21,13 @@ export default function Navbar({
|
||||
}: {
|
||||
userData: User;
|
||||
sandboxData: Sandbox;
|
||||
shared: {
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
shared: { id: string; name: string }[];
|
||||
}) {
|
||||
const [isEditOpen, setIsEditOpen] = useState(false);
|
||||
const [isShareOpen, setIsShareOpen] = useState(false);
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
|
||||
const isOwner = sandboxData.userId === userData.id;
|
||||
const isOwner = sandboxData.userId === userData.id;;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -62,18 +62,29 @@ export default function Navbar({
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<RunButtonModal
|
||||
isRunning={isRunning}
|
||||
setIsRunning={setIsRunning}
|
||||
sandboxData={sandboxData}
|
||||
/>
|
||||
<div className="flex items-center h-full space-x-4">
|
||||
<Avatars />
|
||||
|
||||
{isOwner ? (
|
||||
<>
|
||||
<DeployButtonModal
|
||||
data={sandboxData}
|
||||
userData={userData}
|
||||
/>
|
||||
<Button variant="outline" onClick={() => setIsShareOpen(true)}>
|
||||
<Users className="w-4 h-4 mr-2" />
|
||||
Share
|
||||
</Button>
|
||||
</>
|
||||
) : null}
|
||||
<UserButton userData={userData} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
68
frontend/components/editor/navbar/run.tsx
Normal file
68
frontend/components/editor/navbar/run.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
"use client";
|
||||
|
||||
import { Play, StopCircle } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useTerminal } from "@/context/TerminalContext";
|
||||
import { usePreview } from "@/context/PreviewContext";
|
||||
import { toast } from "sonner";
|
||||
import { Sandbox } from "@/lib/types";
|
||||
|
||||
export default function RunButtonModal({
|
||||
isRunning,
|
||||
setIsRunning,
|
||||
sandboxData,
|
||||
}: {
|
||||
isRunning: boolean;
|
||||
setIsRunning: (running: boolean) => void;
|
||||
sandboxData: Sandbox;
|
||||
}) {
|
||||
const { createNewTerminal, terminals, closeTerminal } = useTerminal();
|
||||
const { setIsPreviewCollapsed, previewPanelRef } = usePreview();
|
||||
|
||||
const handleRun = () => {
|
||||
if (isRunning) {
|
||||
console.log('Stopping sandbox...');
|
||||
console.log('Closing Preview Window');
|
||||
|
||||
terminals.forEach(term => {
|
||||
if (term.terminal) {
|
||||
closeTerminal(term.id);
|
||||
console.log('Closing Terminal', term.id);
|
||||
}
|
||||
});
|
||||
|
||||
setIsPreviewCollapsed(true);
|
||||
previewPanelRef.current?.collapse();
|
||||
} else {
|
||||
console.log('Running sandbox...');
|
||||
console.log('Opening Terminal');
|
||||
console.log('Opening Preview Window');
|
||||
|
||||
if (terminals.length < 4) {
|
||||
if (sandboxData.type === "streamlit") {
|
||||
createNewTerminal(
|
||||
"pip install -r requirements.txt && streamlit run main.py --server.runOnSave true"
|
||||
);
|
||||
} else {
|
||||
createNewTerminal("yarn install && yarn dev");
|
||||
}
|
||||
} else {
|
||||
toast.error("You reached the maximum # of terminals.");
|
||||
console.error("Maximum number of terminals reached.");
|
||||
}
|
||||
|
||||
setIsPreviewCollapsed(false);
|
||||
previewPanelRef.current?.expand();
|
||||
}
|
||||
setIsRunning(!isRunning);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button variant="outline" onClick={handleRun}>
|
||||
{isRunning ? <StopCircle className="w-4 h-4 mr-2" /> : <Play className="w-4 h-4 mr-2" />}
|
||||
{isRunning ? 'Stop' : 'Run'}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,18 +1,14 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Globe,
|
||||
Link,
|
||||
RotateCw,
|
||||
TerminalSquare,
|
||||
UnfoldVertical,
|
||||
} from "lucide-react"
|
||||
import { useRef, useState } from "react"
|
||||
import { useEffect, useRef, useState, useImperativeHandle, forwardRef } from "react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
export default function PreviewWindow({
|
||||
export default forwardRef(function PreviewWindow({
|
||||
collapsed,
|
||||
open,
|
||||
src
|
||||
@ -20,29 +16,39 @@ export default function PreviewWindow({
|
||||
collapsed: boolean
|
||||
open: () => void
|
||||
src: string
|
||||
}) {
|
||||
const ref = useRef<HTMLIFrameElement>(null)
|
||||
},
|
||||
ref: React.Ref<{
|
||||
refreshIframe: () => void
|
||||
}>) {
|
||||
const frameRef = useRef<HTMLIFrameElement>(null)
|
||||
const [iframeKey, setIframeKey] = useState(0)
|
||||
const refreshIframe = () => {
|
||||
setIframeKey(prev => prev + 1)
|
||||
}
|
||||
// Refresh the preview when the URL changes.
|
||||
useEffect(refreshIframe, [src])
|
||||
// Expose refreshIframe method to the parent.
|
||||
useImperativeHandle(ref, () => ({ refreshIframe }))
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`${
|
||||
collapsed ? "h-full" : "h-10"
|
||||
} select-none w-full flex gap-2`}
|
||||
className={`${collapsed ? "h-full" : "h-10"
|
||||
} select-none w-full flex gap-2`}
|
||||
>
|
||||
<div className="h-8 rounded-md px-3 bg-secondary flex items-center w-full justify-between">
|
||||
<div className="text-xs">Preview</div>
|
||||
<div className="flex space-x-1 translate-x-1">
|
||||
{collapsed ? (
|
||||
<PreviewButton onClick={open}>
|
||||
<UnfoldVertical className="w-4 h-4" />
|
||||
<PreviewButton disabled onClick={() => { }}>
|
||||
<TerminalSquare className="w-4 h-4" />
|
||||
</PreviewButton>
|
||||
) : (
|
||||
<>
|
||||
{/* Todo, make this open inspector */}
|
||||
{/* <PreviewButton disabled onClick={() => {}}>
|
||||
<TerminalSquare className="w-4 h-4" />
|
||||
{/* Removed the unfoldvertical button since we have the same thing via the run button.
|
||||
|
||||
<PreviewButton onClick={open}>
|
||||
<UnfoldVertical className="w-4 h-4" />
|
||||
</PreviewButton> */}
|
||||
|
||||
<PreviewButton
|
||||
@ -53,14 +59,7 @@ export default function PreviewWindow({
|
||||
>
|
||||
<Link className="w-4 h-4" />
|
||||
</PreviewButton>
|
||||
<PreviewButton
|
||||
onClick={() => {
|
||||
// if (ref.current) {
|
||||
// ref.current.contentWindow?.location.reload();
|
||||
// }
|
||||
setIframeKey((prev) => prev + 1)
|
||||
}}
|
||||
>
|
||||
<PreviewButton onClick={refreshIframe}>
|
||||
<RotateCw className="w-3 h-3" />
|
||||
</PreviewButton>
|
||||
</>
|
||||
@ -72,7 +71,7 @@ export default function PreviewWindow({
|
||||
<div className="w-full grow rounded-md overflow-hidden bg-foreground">
|
||||
<iframe
|
||||
key={iframeKey}
|
||||
ref={ref}
|
||||
ref={frameRef}
|
||||
width={"100%"}
|
||||
height={"100%"}
|
||||
src={src}
|
||||
@ -81,7 +80,7 @@ export default function PreviewWindow({
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
function PreviewButton({
|
||||
children,
|
||||
@ -94,9 +93,8 @@ function PreviewButton({
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
disabled ? "pointer-events-none opacity-50" : ""
|
||||
} p-0.5 h-5 w-5 ml-0.5 flex items-center justify-center transition-colors bg-transparent hover:bg-muted-foreground/25 cursor-pointer rounded-sm`}
|
||||
className={`${disabled ? "pointer-events-none opacity-50" : ""
|
||||
} p-0.5 h-5 w-5 ml-0.5 flex items-center justify-center transition-colors bg-transparent hover:bg-muted-foreground/25 cursor-pointer rounded-sm`}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
|
@ -2,35 +2,45 @@
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Tab from "@/components/ui/tab";
|
||||
import { closeTerminal, createTerminal } from "@/lib/terminal";
|
||||
import { Terminal } from "@xterm/xterm";
|
||||
import { Loader2, Plus, SquareTerminal, TerminalSquare } from "lucide-react";
|
||||
import { Socket } from "socket.io-client";
|
||||
import { toast } from "sonner";
|
||||
import EditorTerminal from "./terminal";
|
||||
import { useState } from "react";
|
||||
import { useTerminal } from "@/context/TerminalContext";
|
||||
import { useEffect } from "react";
|
||||
import { useSocket } from "@/context/SocketContext"
|
||||
|
||||
export default function Terminals() {
|
||||
|
||||
const { socket } = useSocket();
|
||||
|
||||
const {
|
||||
terminals,
|
||||
setTerminals,
|
||||
createNewTerminal,
|
||||
closeTerminal,
|
||||
activeTerminalId,
|
||||
setActiveTerminalId,
|
||||
creatingTerminal,
|
||||
} = useTerminal();
|
||||
|
||||
export default function Terminals({
|
||||
terminals,
|
||||
setTerminals,
|
||||
socket,
|
||||
}: {
|
||||
terminals: { id: string; terminal: Terminal | null }[];
|
||||
setTerminals: React.Dispatch<
|
||||
React.SetStateAction<
|
||||
{
|
||||
id: string;
|
||||
terminal: Terminal | null;
|
||||
}[]
|
||||
>
|
||||
>;
|
||||
socket: Socket;
|
||||
}) {
|
||||
const [activeTerminalId, setActiveTerminalId] = useState("");
|
||||
const [creatingTerminal, setCreatingTerminal] = useState(false);
|
||||
const [closingTerminal, setClosingTerminal] = useState("");
|
||||
const activeTerminal = terminals.find((t) => t.id === activeTerminalId);
|
||||
|
||||
// Effect to set the active terminal when a new one is created
|
||||
useEffect(() => {
|
||||
if (terminals.length > 0 && !activeTerminalId) {
|
||||
setActiveTerminalId(terminals[terminals.length - 1].id);
|
||||
}
|
||||
}, [terminals, activeTerminalId, setActiveTerminalId]);
|
||||
|
||||
const handleCreateTerminal = () => {
|
||||
if (terminals.length >= 4) {
|
||||
toast.error("You reached the maximum # of terminals.");
|
||||
return;
|
||||
}
|
||||
createNewTerminal();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-10 w-full overflow-auto flex gap-2 shrink-0 tab-scroll">
|
||||
@ -39,18 +49,7 @@ export default function Terminals({
|
||||
key={term.id}
|
||||
creating={creatingTerminal}
|
||||
onClick={() => setActiveTerminalId(term.id)}
|
||||
onClose={() =>
|
||||
closeTerminal({
|
||||
term,
|
||||
terminals,
|
||||
setTerminals,
|
||||
setActiveTerminalId,
|
||||
setClosingTerminal,
|
||||
socket,
|
||||
activeTerminalId,
|
||||
})
|
||||
}
|
||||
closing={closingTerminal === term.id}
|
||||
onClose={() => closeTerminal(term.id)}
|
||||
selected={activeTerminalId === term.id}
|
||||
>
|
||||
<SquareTerminal className="w-4 h-4 mr-2" />
|
||||
@ -59,18 +58,7 @@ export default function Terminals({
|
||||
))}
|
||||
<Button
|
||||
disabled={creatingTerminal}
|
||||
onClick={() => {
|
||||
if (terminals.length >= 4) {
|
||||
toast.error("You reached the maximum # of terminals.");
|
||||
return;
|
||||
}
|
||||
createTerminal({
|
||||
setTerminals,
|
||||
setActiveTerminalId,
|
||||
setCreatingTerminal,
|
||||
socket,
|
||||
});
|
||||
}}
|
||||
onClick={handleCreateTerminal}
|
||||
size="smIcon"
|
||||
variant={"secondary"}
|
||||
className={`font-normal shrink-0 select-none text-muted-foreground disabled:opacity-50`}
|
||||
@ -111,4 +99,4 @@ export default function Terminals({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
@ -55,7 +55,6 @@ export default function EditorTerminal({
|
||||
fitAddon.fit();
|
||||
|
||||
const disposableOnData = term.onData((data) => {
|
||||
console.log("terminalData", id, data);
|
||||
socket.emit("terminalData", id, data);
|
||||
});
|
||||
|
||||
@ -74,6 +73,20 @@ export default function EditorTerminal({
|
||||
};
|
||||
}, [term, terminalRef.current]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!term) return;
|
||||
const handleTerminalResponse = (response: { id: string; data: string }) => {
|
||||
if (response.id === id) {
|
||||
term.write(response.data);
|
||||
}
|
||||
};
|
||||
socket.on("terminalResponse", handleTerminalResponse);
|
||||
|
||||
return () => {
|
||||
socket.off("terminalResponse", handleTerminalResponse);
|
||||
};
|
||||
}, [term, id, socket]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
33
frontend/components/ui/popover.tsx
Normal file
33
frontend/components/ui/popover.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Popover = PopoverPrimitive.Root
|
||||
|
||||
const PopoverTrigger = PopoverPrimitive.Trigger
|
||||
|
||||
const PopoverAnchor = PopoverPrimitive.Anchor
|
||||
|
||||
const PopoverContent = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
))
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
|
34
frontend/context/PreviewContext.tsx
Normal file
34
frontend/context/PreviewContext.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
"use client"
|
||||
|
||||
import React, { createContext, useContext, useState, useRef } from 'react';
|
||||
import { ImperativePanelHandle } from "react-resizable-panels";
|
||||
|
||||
interface PreviewContextType {
|
||||
isPreviewCollapsed: boolean;
|
||||
setIsPreviewCollapsed: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
previewURL: string;
|
||||
setPreviewURL: React.Dispatch<React.SetStateAction<string>>;
|
||||
previewPanelRef: React.RefObject<ImperativePanelHandle>;
|
||||
}
|
||||
|
||||
const PreviewContext = createContext<PreviewContextType | undefined>(undefined);
|
||||
|
||||
export const PreviewProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [isPreviewCollapsed, setIsPreviewCollapsed] = useState(true);
|
||||
const [previewURL, setPreviewURL] = useState<string>("");
|
||||
const previewPanelRef = useRef<ImperativePanelHandle>(null);
|
||||
|
||||
return (
|
||||
<PreviewContext.Provider value={{ isPreviewCollapsed, setIsPreviewCollapsed, previewURL, setPreviewURL, previewPanelRef }}>
|
||||
{children}
|
||||
</PreviewContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const usePreview = () => {
|
||||
const context = useContext(PreviewContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('usePreview must be used within a PreviewProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
63
frontend/context/SocketContext.tsx
Normal file
63
frontend/context/SocketContext.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { io, Socket } from 'socket.io-client';
|
||||
|
||||
interface SocketContextType {
|
||||
socket: Socket | null;
|
||||
setUserAndSandboxId: (userId: string, sandboxId: string) => void;
|
||||
}
|
||||
|
||||
const SocketContext = createContext<SocketContextType | undefined>(undefined);
|
||||
|
||||
export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [userId, setUserId] = useState<string | null>(null);
|
||||
const [sandboxId, setSandboxId] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (userId && sandboxId) {
|
||||
console.log("Initializing socket connection...");
|
||||
const newSocket = io(`${process.env.NEXT_PUBLIC_SERVER_URL}?userId=${userId}&sandboxId=${sandboxId}`);
|
||||
console.log("Socket instance:", newSocket);
|
||||
setSocket(newSocket);
|
||||
|
||||
newSocket.on('connect', () => {
|
||||
console.log("Socket connected:", newSocket.id);
|
||||
});
|
||||
|
||||
newSocket.on('disconnect', () => {
|
||||
console.log("Socket disconnected");
|
||||
});
|
||||
|
||||
return () => {
|
||||
console.log("Disconnecting socket...");
|
||||
newSocket.disconnect();
|
||||
};
|
||||
}
|
||||
}, [userId, sandboxId]);
|
||||
|
||||
const setUserAndSandboxId = (newUserId: string, newSandboxId: string) => {
|
||||
setUserId(newUserId);
|
||||
setSandboxId(newSandboxId);
|
||||
};
|
||||
|
||||
const value = {
|
||||
socket,
|
||||
setUserAndSandboxId,
|
||||
};
|
||||
|
||||
return (
|
||||
<SocketContext.Provider value={ value }>
|
||||
{children}
|
||||
</SocketContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useSocket = (): SocketContextType => {
|
||||
const context = useContext(SocketContext);
|
||||
if (!context) {
|
||||
throw new Error('useSocket must be used within a SocketProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
95
frontend/context/TerminalContext.tsx
Normal file
95
frontend/context/TerminalContext.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import { createTerminal as createTerminalHelper, closeTerminal as closeTerminalHelper } from '@/lib/terminal';
|
||||
import { useSocket } from '@/context/SocketContext';
|
||||
|
||||
interface TerminalContextType {
|
||||
terminals: { id: string; terminal: Terminal | null }[];
|
||||
setTerminals: React.Dispatch<React.SetStateAction<{ id: string; terminal: Terminal | null }[]>>;
|
||||
activeTerminalId: string;
|
||||
setActiveTerminalId: React.Dispatch<React.SetStateAction<string>>;
|
||||
creatingTerminal: boolean;
|
||||
setCreatingTerminal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
createNewTerminal: (command?: string) => Promise<void>;
|
||||
closeTerminal: (id: string) => void;
|
||||
deploy: (callback: () => void) => void;
|
||||
}
|
||||
|
||||
const TerminalContext = createContext<TerminalContextType | undefined>(undefined);
|
||||
|
||||
export const TerminalProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const { socket } = useSocket();
|
||||
const [terminals, setTerminals] = useState<{ id: string; terminal: Terminal | null }[]>([]);
|
||||
const [activeTerminalId, setActiveTerminalId] = useState<string>('');
|
||||
const [creatingTerminal, setCreatingTerminal] = useState<boolean>(false);
|
||||
|
||||
const createNewTerminal = async (command?: string): Promise<void> => {
|
||||
if (!socket) return;
|
||||
setCreatingTerminal(true);
|
||||
try {
|
||||
createTerminalHelper({
|
||||
setTerminals,
|
||||
setActiveTerminalId,
|
||||
setCreatingTerminal,
|
||||
command,
|
||||
socket,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error creating terminal:", error);
|
||||
} finally {
|
||||
setCreatingTerminal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const closeTerminal = (id: string) => {
|
||||
if (!socket) return;
|
||||
const terminalToClose = terminals.find(term => term.id === id);
|
||||
if (terminalToClose) {
|
||||
closeTerminalHelper({
|
||||
term: terminalToClose,
|
||||
terminals,
|
||||
setTerminals,
|
||||
setActiveTerminalId,
|
||||
setClosingTerminal: () => {},
|
||||
socket,
|
||||
activeTerminalId,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const deploy = (callback: () => void) => {
|
||||
if (!socket) console.error("Couldn't deploy: No socket");
|
||||
console.log("Deploying...")
|
||||
socket?.emit("deploy", () => {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
const value = {
|
||||
terminals,
|
||||
setTerminals,
|
||||
activeTerminalId,
|
||||
setActiveTerminalId,
|
||||
creatingTerminal,
|
||||
setCreatingTerminal,
|
||||
createNewTerminal,
|
||||
closeTerminal,
|
||||
deploy
|
||||
};
|
||||
|
||||
return (
|
||||
<TerminalContext.Provider value={value}>
|
||||
{children}
|
||||
</TerminalContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useTerminal = (): TerminalContextType => {
|
||||
const context = useContext(TerminalContext);
|
||||
if (!context) {
|
||||
throw new Error('useTerminal must be used within a TerminalProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
@ -8,6 +8,7 @@ export const createTerminal = ({
|
||||
setTerminals,
|
||||
setActiveTerminalId,
|
||||
setCreatingTerminal,
|
||||
command,
|
||||
socket,
|
||||
}: {
|
||||
setTerminals: React.Dispatch<React.SetStateAction<{
|
||||
@ -16,6 +17,7 @@ export const createTerminal = ({
|
||||
}[]>>;
|
||||
setActiveTerminalId: React.Dispatch<React.SetStateAction<string>>;
|
||||
setCreatingTerminal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
command?: string;
|
||||
socket: Socket;
|
||||
|
||||
}) => {
|
||||
@ -29,6 +31,7 @@ export const createTerminal = ({
|
||||
setTimeout(() => {
|
||||
socket.emit("createTerminal", id, () => {
|
||||
setCreatingTerminal(false);
|
||||
if (command) socket.emit("terminalData", id, command + "\n");
|
||||
});
|
||||
}, 1000);
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ export type User = {
|
||||
export type Sandbox = {
|
||||
id: string;
|
||||
name: string;
|
||||
type: "react" | "node";
|
||||
type: string;
|
||||
visibility: "public" | "private";
|
||||
createdAt: Date;
|
||||
userId: string;
|
||||
|
444
frontend/package-lock.json
generated
444
frontend/package-lock.json
generated
@ -24,6 +24,7 @@
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.1.1",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
@ -40,6 +41,7 @@
|
||||
"monaco-themes": "^0.4.4",
|
||||
"next": "14.1.3",
|
||||
"next-themes": "^0.3.0",
|
||||
"posthog-js": "^1.147.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18",
|
||||
"react-hook-form": "^7.51.3",
|
||||
@ -1118,6 +1120,419 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
|
||||
"integrity": "sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.0",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.0",
|
||||
"@radix-ui/react-focus-guards": "1.1.0",
|
||||
"@radix-ui/react-focus-scope": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-popper": "1.2.0",
|
||||
"@radix-ui/react-portal": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-slot": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||
"aria-hidden": "^1.1.1",
|
||||
"react-remove-scroll": "2.5.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-arrow": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
||||
"integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
||||
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
||||
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz",
|
||||
"integrity": "sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-escape-keydown": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-guards": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz",
|
||||
"integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-scope": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz",
|
||||
"integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-id": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
|
||||
"integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-popper": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
||||
"integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.0.0",
|
||||
"@radix-ui/react-arrow": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0",
|
||||
"@radix-ui/react-use-rect": "1.1.0",
|
||||
"@radix-ui/react-use-size": "1.1.0",
|
||||
"@radix-ui/rect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz",
|
||||
"integrity": "sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz",
|
||||
"integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
||||
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
||||
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-escape-keydown": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
||||
"integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
||||
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-rect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
|
||||
"integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==",
|
||||
"dependencies": {
|
||||
"@radix-ui/rect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-size": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
|
||||
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/rect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
|
||||
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/react-remove-scroll": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz",
|
||||
"integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==",
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.4",
|
||||
"react-style-singleton": "^2.2.1",
|
||||
"tslib": "^2.1.0",
|
||||
"use-callback-ref": "^1.3.0",
|
||||
"use-sidecar": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popper": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz",
|
||||
@ -3269,6 +3684,30 @@
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||
},
|
||||
"node_modules/posthog-js": {
|
||||
"version": "1.147.0",
|
||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.147.0.tgz",
|
||||
"integrity": "sha512-cALKIcix1W9xMsfUUI/QUFhtBeabDGsQCb3lpZEFd7WCPNLOcGmpeomm4xMX7MO2OnfK6Ov91/HNyfK1wncjSA==",
|
||||
"dependencies": {
|
||||
"fflate": "^0.4.8",
|
||||
"preact": "^10.19.3",
|
||||
"web-vitals": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/posthog-js/node_modules/fflate": {
|
||||
"version": "0.4.8",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz",
|
||||
"integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.22.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.22.1.tgz",
|
||||
"integrity": "sha512-jRYbDDgMpIb5LHq3hkI0bbl+l/TQ9UnkdQ0ww+lp+4MMOdqaUYdFc5qeyP+IV8FAd/2Em7drVPeKdQxsiWCf/A==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/pvtsutils": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz",
|
||||
@ -4083,6 +4522,11 @@
|
||||
"@types/jasmine": "^3.6.3"
|
||||
}
|
||||
},
|
||||
"node_modules/web-vitals": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.1.tgz",
|
||||
"integrity": "sha512-U6bAxeudnhDqcXNl50JC4hLlqox9DZnngxfISZm3DMZnonW35xtJOVUc091L+DOY+6hVZVpKXoiCP0RiT6339Q=="
|
||||
},
|
||||
"node_modules/webcrypto-core": {
|
||||
"version": "1.7.9",
|
||||
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.9.tgz",
|
||||
|
@ -25,6 +25,7 @@
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-popover": "^1.1.1",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
@ -41,6 +42,7 @@
|
||||
"monaco-themes": "^0.4.4",
|
||||
"next": "14.1.3",
|
||||
"next-themes": "^0.3.0",
|
||||
"posthog-js": "^1.147.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18",
|
||||
"react-hook-form": "^7.51.3",
|
||||
|
630
package-lock.json
generated
Normal file
630
package-lock.json
generated
Normal file
@ -0,0 +1,630 @@
|
||||
{
|
||||
"name": "sandbox",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@radix-ui/react-popover": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz",
|
||||
"integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.6.10",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz",
|
||||
"integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.6.0",
|
||||
"@floating-ui/utils": "^0.2.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz",
|
||||
"integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz",
|
||||
"integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA=="
|
||||
},
|
||||
"node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||
},
|
||||
"node_modules/@radix-ui/react-arrow": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
||||
"integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
||||
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz",
|
||||
"integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz",
|
||||
"integrity": "sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-escape-keydown": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-guards": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz",
|
||||
"integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-scope": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz",
|
||||
"integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-id": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
|
||||
"integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
|
||||
"integrity": "sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.0",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.0",
|
||||
"@radix-ui/react-focus-guards": "1.1.0",
|
||||
"@radix-ui/react-focus-scope": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-popper": "1.2.0",
|
||||
"@radix-ui/react-portal": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-slot": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||
"aria-hidden": "^1.1.1",
|
||||
"react-remove-scroll": "2.5.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popper": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
||||
"integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.0.0",
|
||||
"@radix-ui/react-arrow": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0",
|
||||
"@radix-ui/react-use-rect": "1.1.0",
|
||||
"@radix-ui/react-use-size": "1.1.0",
|
||||
"@radix-ui/rect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-portal": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz",
|
||||
"integrity": "sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz",
|
||||
"integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
||||
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
||||
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-escape-keydown": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
|
||||
"integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
||||
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-rect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz",
|
||||
"integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==",
|
||||
"dependencies": {
|
||||
"@radix-ui/rect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-size": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
|
||||
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/rect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
|
||||
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz",
|
||||
"integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
|
||||
},
|
||||
"node_modules/get-nonce": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz",
|
||||
"integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==",
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.4",
|
||||
"react-style-singleton": "^2.2.1",
|
||||
"tslib": "^2.1.0",
|
||||
"use-callback-ref": "^1.3.0",
|
||||
"use-sidecar": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll-bar": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz",
|
||||
"integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==",
|
||||
"dependencies": {
|
||||
"react-style-singleton": "^2.2.1",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-style-singleton": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz",
|
||||
"integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==",
|
||||
"dependencies": {
|
||||
"get-nonce": "^1.0.0",
|
||||
"invariant": "^2.2.4",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
|
||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
|
||||
},
|
||||
"node_modules/use-callback-ref": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz",
|
||||
"integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
||||
"integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==",
|
||||
"dependencies": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
package.json
Normal file
5
package.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@radix-ui/react-popover": "^1.1.1"
|
||||
}
|
||||
}
|
47
tests/index.ts
Normal file
47
tests/index.ts
Normal file
@ -0,0 +1,47 @@
|
||||
// Import necessary modules
|
||||
import { io, Socket } from "socket.io-client";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
interface CallbackResponse {
|
||||
success: boolean;
|
||||
apps?: string[];
|
||||
message?: string;
|
||||
}
|
||||
|
||||
let socketRef: Socket = io(
|
||||
`http://localhost:4000?userId=user_2hFB6KcK6bb3Gx9241UXsxFq4kO&sandboxId=v30a2c48xal03tzio7mapt19`,
|
||||
{
|
||||
timeout: 2000,
|
||||
}
|
||||
);
|
||||
|
||||
socketRef.on("connect", async () => {
|
||||
console.log("Connected to the server");
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
socketRef.emit("list", (response: CallbackResponse) => {
|
||||
if (response.success) {
|
||||
console.log("List of apps:", response.apps);
|
||||
} else {
|
||||
console.log("Error:", response.message);
|
||||
}
|
||||
});
|
||||
|
||||
socketRef.emit("deploy", (response: CallbackResponse) => {
|
||||
if (response.success) {
|
||||
console.log("It worked!");
|
||||
} else {
|
||||
console.log("Error:", response.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socketRef.on("disconnect", () => {
|
||||
console.log("Disconnected from the server");
|
||||
});
|
||||
|
||||
socketRef.on("connect_error", (error: Error) => {
|
||||
console.error("Connection error:", error);
|
||||
});
|
310
tests/package-lock.json
generated
Normal file
310
tests/package-lock.json
generated
Normal file
@ -0,0 +1,310 @@
|
||||
{
|
||||
"name": "socket-io-test",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "socket-io-test",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5",
|
||||
"socket.io-client": "^4.7.5",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw=="
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.14.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz",
|
||||
"integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
||||
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
|
||||
"integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
|
||||
"integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.4.5",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
|
||||
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.5.4",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz",
|
||||
"integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.7.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
|
||||
"integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.5.2",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js",
|
||||
"ts-node-cwd": "dist/bin-cwd.js",
|
||||
"ts-node-esm": "dist/bin-esm.js",
|
||||
"ts-node-script": "dist/bin-script.js",
|
||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||
"ts-script": "dist/bin-script-deprecated.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": ">=1.2.50",
|
||||
"@swc/wasm": ">=1.2.50",
|
||||
"@types/node": "*",
|
||||
"typescript": ">=2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
|
||||
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
tests/package.json
Normal file
21
tests/package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "socket-io-test",
|
||||
"version": "1.0.0",
|
||||
"description": "A test script for socket.io-client using ES6 modules and TypeScript",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "npm run build && node dist/index.js"
|
||||
},
|
||||
"author": "Your Name",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5",
|
||||
"socket.io-client": "^4.7.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.0.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
}
|
13
tests/tsconfig.json
Normal file
13
tests/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["index.ts"]
|
||||
}
|
Reference in New Issue
Block a user