Skip to content

Commit 4ab66cf

Browse files
authored
feat: passing code in query params as compressed (#7)
* feat: passing code in query params as compressed * backwards compatible
1 parent 14281c7 commit 4ab66cf

6 files changed

+115
-54
lines changed

bun.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"name": "3d-viewer-svg",
66
"dependencies": {
77
"@tscircuit/core": "^0.0.301",
8-
"@tscircuit/create-snippet-url": "^0.0.6",
8+
"@tscircuit/create-snippet-url": "^0.0.7",
99
"@tscircuit/eval": "^0.0.87",
1010
"@tscircuit/prompt-benchmarks": "^0.0.33",
1111
"@tscircuit/runframe": "^0.0.149",
@@ -363,7 +363,7 @@
363363

364364
"@tscircuit/core": ["@tscircuit/[email protected]", "", { "dependencies": { "@lume/kiwi": "^0.4.3", "@tscircuit/footprinter": "^0.0.97", "@tscircuit/infgrid-ijump-astar": "^0.0.33", "@tscircuit/math-utils": "^0.0.9", "@tscircuit/props": "^0.0.137", "@tscircuit/schematic-autolayout": "^0.0.6", "@tscircuit/soup-util": "^0.0.41", "circuit-json": "^0.0.135", "circuit-json-to-connectivity-map": "^0.0.17", "format-si-unit": "^0.0.3", "nanoid": "^5.0.7", "performance-now": "^2.1.0", "react-reconciler": "^0.31.0", "react-reconciler-18": "npm:[email protected]", "schematic-symbols": "^0.0.113", "transformation-matrix": "^2.16.1", "zod": "^3.23.8" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-j7gMTBxx0nOETnHqg01J88XrLB+TZ1CkKDU9kkWltyQS82iRsszUsVaS9CcNa9afZW25suRjxxCidHEv4lWKdA=="],
365365

366-
"@tscircuit/create-snippet-url": ["@tscircuit/[email protected].6", "", { "dependencies": { "fflate": "^0.8.2" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-Fy1RTnHTj4nXmcYp6QQMEIIH18yevEnMlTfjyad5h+0IfL6vmkeUA3vLFVSXdvSraxvzRBZ4SbcjhjS94pLTMA=="],
366+
"@tscircuit/create-snippet-url": ["@tscircuit/[email protected].7", "", { "dependencies": { "fflate": "^0.8.2" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-IMGacii8z+RQ4QmNayWI3DVrgog8ZlCIowWx1zjUDzuPnVp7Fm+5Xv4ABjW+ol5j0qwO4vzvqh4Qe/rbGU7kyA=="],
367367

368368
"@tscircuit/eval": ["@tscircuit/[email protected]", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-4IAABMe7CBxxpmRT2QaBjr+yDlo0q3j05MiAMZg79+LleE8nc7oyv6d/zMnIy8U+ehvnZ+z5fOAOZIB/ICDQcQ=="],
369369

circuit-json-preview-html.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export const circuitJsonPreviewHtml = (code: string) => {
2+
return `
3+
<!DOCTYPE html>
4+
<html>
5+
<body>
6+
<div id="root"></div>
7+
<script>
8+
// Add process polyfill
9+
window.process = {
10+
env: {
11+
NODE_ENV: 'production'
12+
}
13+
};
14+
window.CIRCUIT_JSON = ${JSON.stringify(code)};
15+
window.CIRCUIT_JSON_PREVIEW_PROPS = { defaultActiveTab: "cad" };
16+
</script>
17+
<script src="/standalone-preview.min.js"></script>
18+
</body>
19+
</html>
20+
`
21+
}

endpoint.ts

+63-32
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { getUncompressedSnippetString } from "@tscircuit/create-snippet-url"
12
import { CircuitRunner } from "@tscircuit/eval/eval"
2-
import { getErrorSvg } from "./getErrorSvg"
3-
import { getIndexPageHtml } from "./get-index-page-html"
3+
import { circuitJsonPreviewHtml } from "./circuit-json-preview-html"
44
import { getHtmlForGeneratedUrlPage } from "./get-html-for-generated-url-page"
5+
import { getIndexPageHtml } from "./get-index-page-html"
6+
import { getErrorSvg } from "./getErrorSvg"
57

68
type Result<T, E = Error> = [T, null] | [null, E]
79

@@ -11,33 +13,63 @@ async function unwrapPromise<T>(promise: Promise<T>): Promise<Result<T>> {
1113
.catch<[null, Error]>((err) => [null, err])
1214
}
1315

16+
function unwrapSyncError<T>(fn: () => T): Result<T> {
17+
try {
18+
return [fn(), null]
19+
} catch (err) {
20+
return [null, err as Error]
21+
}
22+
}
23+
24+
1425
export default async (req: Request) => {
1526
const url = new URL(req.url.replace("/api", "/"))
27+
const host = `${url.protocol}//${url.host}`
1628

1729
if (url.pathname === "/health") {
1830
return new Response(JSON.stringify({ ok: true }))
1931
}
2032

33+
if (url.pathname === "/generate_url") {
34+
const code = url.searchParams.get("code")
35+
return new Response(await getHtmlForGeneratedUrlPage(code!, host), {
36+
headers: { "Content-Type": "text/html" },
37+
})
38+
}
39+
2140
if (url.pathname === "/" && !url.searchParams.get("code")) {
2241
return new Response(getIndexPageHtml(), {
2342
headers: { "Content-Type": "text/html" },
2443
})
2544
}
2645

27-
if (url.pathname === "/start_runframe" && url.searchParams.get("code")) {
28-
const userCode = url.searchParams.get("code")
29-
if (!userCode) {
30-
return new Response(
31-
JSON.stringify({ ok: false, error: "No code parameter provided" }),
32-
{ status: 400 },
33-
)
34-
}
35-
const worker = new CircuitRunner()
36-
37-
const [, executeError] = await unwrapPromise(
38-
worker.executeWithFsMap({
39-
fsMap: {
40-
"entrypoint.tsx": `
46+
const rawCode = url.searchParams.get("code")
47+
if (!rawCode) {
48+
return new Response(
49+
JSON.stringify({ ok: false, error: "No code parameter provided" }),
50+
{ status: 400 },
51+
)
52+
}
53+
54+
const gzipPrefix = "data:application/gzip;base64,"
55+
56+
const normalizedCode = rawCode.startsWith(gzipPrefix)
57+
? rawCode
58+
: gzipPrefix + rawCode
59+
60+
const [userCode, userCodeErr] = unwrapSyncError(() =>
61+
getUncompressedSnippetString(normalizedCode),
62+
)
63+
if (userCodeErr) {
64+
return errorResponse(userCodeErr)
65+
}
66+
67+
const worker = new CircuitRunner()
68+
69+
const [, executeError] = await unwrapPromise(
70+
worker.executeWithFsMap({
71+
fsMap: {
72+
"entrypoint.tsx": `
4173
import * as UserComponents from "./UserCode.tsx";
4274
4375
const hasBoard = ${userCode.includes("<board").toString()};
@@ -55,26 +87,25 @@ export default async (req: Request) => {
5587
)
5688
);
5789
`,
58-
"UserCode.tsx": userCode,
59-
},
60-
entrypoint: "entrypoint.tsx",
61-
}),
62-
)
90+
"UserCode.tsx": userCode,
91+
},
92+
entrypoint: "entrypoint.tsx",
93+
}),
94+
)
6395

64-
if (executeError) return errorResponse(executeError)
96+
if (executeError) return errorResponse(executeError)
6597

66-
const [, renderError] = await unwrapPromise(worker.renderUntilSettled())
67-
if (renderError) return errorResponse(renderError)
98+
const [, renderError] = await unwrapPromise(worker.renderUntilSettled())
99+
if (renderError) return errorResponse(renderError)
68100

69-
const [circuitJson, jsonError] = await unwrapPromise(worker.getCircuitJson())
70-
if (jsonError) return errorResponse(jsonError)
101+
const [circuitJson, jsonError] = await unwrapPromise(worker.getCircuitJson())
102+
if (jsonError) return errorResponse(jsonError)
71103

72-
if (circuitJson) {
73-
const html = await getHtmlForGeneratedUrlPage(circuitJson as any)
74-
return new Response(html, {
75-
headers: { "Content-Type": "text/html" },
76-
})
77-
}
104+
if (circuitJson) {
105+
const html = await circuitJsonPreviewHtml(circuitJson as any)
106+
return new Response(html, {
107+
headers: { "Content-Type": "text/html" },
108+
})
78109
}
79110
}
80111

get-html-for-generated-url-page.ts

+27-18
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
export const getHtmlForGeneratedUrlPage = async (code: string) => {
1+
import { createSnippetUrl, getCompressedBase64SnippetString } from "@tscircuit/create-snippet-url"
2+
3+
export const getHtmlForGeneratedUrlPage = async (code: string, urlPrefix = "https://code-runner-preview.tscircuit.com") => {
4+
const snippetUrl = createSnippetUrl(code)
5+
const compressedCode = getCompressedBase64SnippetString(code)
6+
const circuitJsonPreviewUrl = `${urlPrefix}/start_runframe/?code=${encodeURIComponent(compressedCode)}`
7+
28
return `
3-
<!DOCTYPE html>
4-
<html>
5-
<body>
6-
<div id="root"></div>
7-
<script>
8-
// Add process polyfill
9-
window.process = {
10-
env: {
11-
NODE_ENV: 'production'
12-
}
13-
};
14-
window.CIRCUIT_JSON = ${JSON.stringify(code)};
15-
window.CIRCUIT_JSON_PREVIEW_PROPS = { defaultActiveTab: "cad" };
16-
</script>
17-
<script src="/standalone-preview.min.js"></script>
18-
</body>
19-
</html>
9+
<!DOCTYPE html>
10+
<html>
11+
<body>
12+
<h1>code-runner-preview.tscircuit.com</h1>
13+
<table>
14+
<tr>
15+
<th>Type</th>
16+
<th>URL</th>
17+
</tr>
18+
<tr>
19+
<td>Snippet URL</td>
20+
<td><a href="${snippetUrl}">${snippetUrl}</a></td>
21+
</tr>
22+
<tr>
23+
<td>Circuit JSON Preview</td>
24+
<td><a href="${circuitJsonPreviewUrl}">${circuitJsonPreviewUrl}</a></td>
25+
</tr>
26+
</table>
27+
</body>
28+
</html>
2029
`
2130
}

get-index-page-html.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const getIndexPageHtml = () => {
1010
<p>
1111
To do this programmatically see the <a href="https://github.com/tscircuit/create-snippet-url">create-snippet-url</a> package.
1212
</p>
13-
<form action="/start_runframe" method="GET">
13+
<form action="/generate_url" method="GET">
1414
<textarea
1515
name="code"
1616
id="code"

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"dependencies": {
1212
"@tscircuit/core": "^0.0.301",
13-
"@tscircuit/create-snippet-url": "^0.0.6",
13+
"@tscircuit/create-snippet-url": "^0.0.7",
1414
"@tscircuit/eval": "^0.0.87",
1515
"@tscircuit/prompt-benchmarks": "^0.0.33",
1616
"@tscircuit/runframe": "^0.0.149",

0 commit comments

Comments
 (0)