-
Notifications
You must be signed in to change notification settings - Fork 0
chore: add code runner #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThis PR introduces a complete code execution and interactive example platform consisting of two new services: "code-executor" (a security-hardened Node.js/Express service for TypeScript code execution) and "code-runner" (a Next.js frontend for rendering interactive code examples), along with deployment configurations, documentation, and embedding utilities. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser / Client
participant Runner as Code Runner<br/>(Next.js)
participant API as Code Runner API<br/>(POST /api/run)
participant Executor as Code Executor<br/>(Express Service)
rect rgb(200, 220, 255)
Note over Browser,Executor: Code Execution Flow
Browser->>Runner: Click "Run" button
Runner->>API: POST /api/run<br/>{ code, language }
API->>API: Validate request<br/>(schema, env vars)
alt Validation failed
API-->>Runner: 400 Bad Request
else Valid request
API->>Executor: POST /typescript<br/>Authorization header
rect rgb(255, 200, 200)
Note over Executor: Execution Phase
Executor->>Executor: Validate code patterns<br/>(blocked keywords)
Executor->>Executor: Wrap code in sandbox<br/>+ console.log capture
Executor->>Executor: Execute via tsx<br/>(timeout, size limits)
Executor->>Executor: Monitor stdout/stderr
end
alt Execution error
Executor-->>API: { stderr: error message }
else Success
Executor-->>API: { stdout: output }
end
alt Has stderr only
API-->>Runner: 500 Execution Error
else Has stdout
API-->>Runner: 200 { output }
end
end
Runner->>Browser: Display output<br/>in console panel
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
light-token/cookbook/create-mint.mdx (1)
2-2: Fix typo in title.The title contains a typo: "wih" should be "with".
🔎 Proposed fix
-title: Create Mint Account wih Token Metadata +title: Create Mint Account with Token Metadata
🧹 Nitpick comments (8)
code-runner/src/app/api/run/route.ts (1)
54-59: Consider handling cases where both stdout and stderr exist.The current logic only returns an error if stderr exists without stdout. However, it's common for code to produce both stdout (normal output) and stderr (warnings/errors). The current implementation silently discards stderr when stdout is present, which could hide important warnings or non-fatal errors from users.
Consider returning both stdout and stderr to provide complete execution feedback.
🔎 Proposed enhancement to include stderr in response
- if (result.stderr && !result.stdout) { + if (result.stderr && !result.stdout) { return NextResponse.json( { error: "Execution error", details: { error: result.stderr } }, { status: 500 } ); } - return NextResponse.json({ output: result.stdout || "" }, { status: 200 }); + return NextResponse.json( + { + output: result.stdout || "", + ...(result.stderr && { stderr: result.stderr }) + }, + { status: 200 } + );code-executor/README.md (1)
95-97: Add language specifiers to fenced code blocks.The fenced code blocks on lines 95 and 102 are missing language specifiers, which helps with syntax highlighting and accessibility.
🔎 Proposed fix
### Health Check -``` +```http GET /health Response: { "status": "ok" }Execute TypeScript
-
+http
POST /typescript
Headers:
Authorization: Bearer <EXECUTOR_API_KEY>
Content-Type: application/json</details> Also applies to: 102-113 </blockquote></details> <details> <summary>code-executor/docker-compose.yml (1)</summary><blockquote> `20-21`: **Consider restricting tmpfs permissions.** The tmpfs mount uses `mode=1777` (world-writable with sticky bit). Since the container runs as a single non-root `executor` user, a more restrictive permission like `mode=1700` would reduce the attack surface without impacting functionality. <details> <summary>🔎 Proposed fix</summary> ```diff tmpfs: - - /app/.temp:size=50M,mode=1777 + - /app/.temp:size=50M,mode=1700snippets/jsx/code-runner-embed.jsx (1)
1-17: Add input validation for the example parameter.The
exampleprop is directly interpolated into the iframe URL without validation. While current usage appears to use hardcoded values, adding validation would prevent potential path traversal or unexpected behavior if the component is reused with dynamic inputs.🔎 Proposed validation
export const CodeRunnerEmbed = ({ example, height = "450px" }) => { + // Validate example contains only safe characters (alphanumeric, hyphens) + if (!/^[a-z0-9-]+$/.test(example)) { + console.error(`Invalid example name: ${example}`); + return null; + } + const baseUrl = process.env.CODE_RUNNER_URL || "http://localhost:3030"; return ( <iframe src={`${baseUrl}/embed/${example}`} style={{ width: "100%", height: height, border: "none", borderRadius: "8px", overflow: "hidden", }} allow="clipboard-write" + sandbox="allow-scripts allow-same-origin" title={`${example} code example`} /> ); };Note: Evaluate if
sandboxattributes are compatible with your embed functionality requirements.code-runner/src/lib/highlighter.ts (1)
5-13: Consider adding error handling for highlighter initialization.The lazy initialization is clean, but consider adding error handling for the
createHighlightercall in case Shiki fails to initialize (e.g., missing themes or language files).🔎 Optional: Add error handling
export async function getHighlighter(): Promise<Highlighter> { if (!highlighter) { - highlighter = await createHighlighter({ - themes: ["github-dark"], - langs: ["typescript", "ts"], - }); + try { + highlighter = await createHighlighter({ + themes: ["github-dark"], + langs: ["typescript", "ts"], + }); + } catch (error) { + console.error("Failed to initialize highlighter:", error); + throw new Error("Syntax highlighter initialization failed"); + } } return highlighter; }code-executor/src/server.ts (1)
27-34: Consider timing-safe comparison for API key validation.The current string comparison on Line 29 could theoretically be vulnerable to timing attacks, though the risk is low given the network latency typically dominates any timing differences.
🔎 Optional: Use timing-safe comparison
+import crypto from "crypto"; + function authMiddleware(req: Request, res: Response, next: NextFunction) { const authHeader = req.headers.authorization; - if (!authHeader || authHeader !== `Bearer ${API_KEY}`) { + const expected = `Bearer ${API_KEY}`; + const valid = authHeader && authHeader.length === expected.length && + crypto.timingSafeEqual(Buffer.from(authHeader), Buffer.from(expected)); + if (!valid) { res.status(401).json({ error: "Unauthorized" }); return; } next(); }code-executor/src/executor.ts (2)
16-26: Blocked patterns provide baseline protection but consider additional restrictions.The current blocked patterns prevent access to
child_process,fs,process.env,eval,Function, andvm. However, consider adding:
- Network-related modules (
http,https,net,dgram)- Additional dangerous patterns (
__dirname,__filename,import.meta.url)- Dynamic imports (
import())🔎 Enhanced blocked patterns
const BLOCKED_PATTERNS = [ /require\s*\(\s*['"]child_process['"]\s*\)/, /require\s*\(\s*['"]fs['"]\s*\)/, /import\s+.*from\s+['"]child_process['"]/, /import\s+.*from\s+['"]fs['"]/, /process\.env/, /eval\s*\(/, /Function\s*\(/, /vm\./, /require\s*\(\s*['"]vm['"]\s*\)/, + /require\s*\(\s*['"]http['"]\s*\)/, + /require\s*\(\s*['"]https['"]\s*\)/, + /require\s*\(\s*['"]net['"]\s*\)/, + /import\s+.*from\s+['"]http['"]/, + /import\s+.*from\s+['"]https['"]/, + /import\s+.*from\s+['"]net['"]/, + /__dirname/, + /__filename/, + /import\.meta\.url/, + /import\s*\(/, ];
63-141: Recommend additional operational safeguards for production deployment.While the code includes good baseline protections (blocked patterns, timeouts, output limits), executing arbitrary user code requires defense-in-depth:
- Container isolation: Run in a lightweight container with no network access
- Resource limits: Use cgroups or Docker to limit CPU, memory, and I/O
- Filesystem isolation: Use read-only filesystem or minimal writable tmpfs
- User isolation: Run as unprivileged user with no sudo access
- Network isolation: Block all outbound network connections
- Monitoring: Log and alert on suspicious patterns or resource exhaustion
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
code-executor/package-lock.jsonis excluded by!**/package-lock.jsoncode-runner/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (33)
code-executor/.dockerignorecode-executor/.gitignorecode-executor/Dockerfilecode-executor/README.mdcode-executor/docker-compose.ymlcode-executor/fly.tomlcode-executor/package.jsoncode-executor/src/executor.tscode-executor/src/server.tscode-executor/tsconfig.jsoncode-runner/.gitignorecode-runner/README.mdcode-runner/next.config.jscode-runner/package.jsoncode-runner/postcss.config.jscode-runner/src/app/api/run/route.tscode-runner/src/app/embed/create-mint/page.tsxcode-runner/src/app/embed/layout.tsxcode-runner/src/app/globals.csscode-runner/src/app/layout.tsxcode-runner/src/app/page.tsxcode-runner/src/components/code-icon.tsxcode-runner/src/components/code-runner.tsxcode-runner/src/components/copy-button.tsxcode-runner/src/components/ui/resizable.tsxcode-runner/src/components/ui/tabs.tsxcode-runner/src/lib/highlighter.tscode-runner/src/lib/utils.tscode-runner/tailwind.config.jscode-runner/tsconfig.jsonlight-token/cookbook/create-mint.mdxlight-token/toolkits/for-streaming-tokens.mdxsnippets/jsx/code-runner-embed.jsx
🧰 Additional context used
🧬 Code graph analysis (3)
code-runner/src/components/ui/resizable.tsx (1)
code-runner/src/lib/utils.ts (1)
cn(4-6)
code-executor/src/server.ts (1)
code-executor/src/executor.ts (1)
executeCode(63-141)
code-runner/src/components/ui/tabs.tsx (1)
code-runner/src/lib/utils.ts (1)
cn(4-6)
🪛 ast-grep (0.40.3)
code-runner/src/components/code-icon.tsx
[warning] 54-54: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
code-runner/src/components/code-runner.tsx
[warning] 159-159: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
code-runner/src/components/code-icon.tsx
[error] 55-55: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
code-runner/src/components/code-runner.tsx
[error] 160-160: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🪛 markdownlint-cli2 (0.18.1)
code-executor/README.md
95-95: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
102-102: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🔇 Additional comments (26)
code-runner/.gitignore (1)
1-31: Standard Next.js .gitignore configuration—approved.The
.gitignorefile is well-organized and covers all essential patterns for a Next.js project: dependencies, build artifacts, environment files, logs, system files, and TypeScript metadata. The categorized comments enhance readability and maintainability.light-token/toolkits/for-streaming-tokens.mdx (1)
68-68: LGTM!The formatting adjustments to the code block delimiters are correct and won't affect rendering.
Also applies to: 140-140
code-runner/tsconfig.json (1)
1-22: LGTM!The TypeScript configuration is well-suited for a Next.js application with appropriate compiler options, path aliases, and Next.js plugin integration.
code-executor/.dockerignore (1)
1-8: LGTM!The Docker ignore patterns appropriately exclude build artifacts, dependencies, temporary files, and sensitive environment configuration from the Docker build context.
code-runner/postcss.config.js (1)
1-6: LGTM!Standard PostCSS configuration for Tailwind CSS and Autoprefixer integration.
code-executor/.gitignore (1)
1-6: LGTM!The ignore patterns appropriately exclude build artifacts, dependencies, temporary files, environment configuration, and logs from version control.
light-token/cookbook/create-mint.mdx (1)
15-15: LGTM! Clean integration of live code example.The CodeRunnerEmbed integration provides an interactive way for users to try the create-mint example directly in the documentation. The height specification is appropriate for the embedded content.
Also applies to: 20-22
code-executor/README.md (1)
1-124: Excellent documentation! Security measures are well-documented.The README provides comprehensive coverage of:
- Security features (API auth, rate limiting, timeouts, resource limits, sandboxing)
- Deployment options across multiple platforms
- Clear integration guidance
This will help users understand and deploy the executor service safely.
code-executor/package.json (1)
11-17: LGTM! Appropriate security-focused dependencies.The dependency selection is appropriate for a security-hardened execution service:
- Express for the HTTP server
- Helmet for security headers
- express-rate-limit for DoS protection
These align well with the security features documented in the README.
code-runner/src/app/page.tsx (1)
1-18: LGTM! Clean and minimal landing page.The home page provides a simple entry point with proper semantic HTML and Next.js best practices (using the Link component for client-side navigation).
code-runner/src/app/embed/layout.tsx (1)
1-8: LGTM! Appropriate minimal layout for iframe embedding.This pass-through layout is correct for embedded content, avoiding unnecessary wrapper elements that could interfere with iframe styling or dimensions.
code-runner/README.md (1)
1-87: Excellent documentation with clear architecture overview.The README provides:
- Clear architecture diagram showing component interaction
- Comprehensive setup instructions for local development
- Guidance for adding new examples
- Deployment instructions for Vercel
This will help developers understand and extend the code runner system effectively.
code-runner/src/lib/utils.ts (1)
1-7: LGTM! Standard Tailwind utility pattern.This is a well-established pattern for merging Tailwind CSS classes, combining
classnamesfor conditional class handling withtailwind-mergefor deduplication and conflict resolution. This pattern is widely used in component libraries like shadcn/ui.code-executor/fly.toml (2)
10-15: Note: Auto-scaling to zero will introduce cold starts.The configuration sets
min_machines_running = 0, which will scale the service down to zero when idle. This is cost-effective but will introduce cold start latency when the first request arrives after a period of inactivity. Consider if this tradeoff is acceptable for the expected usage patterns of the documentation's live code examples.
1-21: LGTM! Deployment configuration is well-sized.The Fly.io configuration is appropriate:
- 512MB memory provides headroom beyond the documented 128MB heap limit
- Shared CPU (1 vCPU) is suitable for code execution workloads
- Port 3040 is consistent across all configurations
- HTTPS enforcement enhances security
code-runner/src/app/globals.css (1)
1-101: LGTM! Well-structured theme configuration.The CSS file provides a comprehensive dark theme with proper Tailwind integration, sensible global resets, and appropriate font stacks. The color variable naming is consistent and the Shiki overrides ensure proper code block rendering.
code-runner/tailwind.config.js (1)
1-19: LGTM! Standard Tailwind configuration.The configuration properly extends the theme with CSS variable-based colors that align with the global styles. The content pattern correctly targets all relevant file types.
code-runner/src/app/embed/create-mint/page.tsx (1)
1-105: LGTM! Clean async page component.The component properly leverages Next.js async server components to pre-highlight code samples. The tab structure is clear and the full-screen layout is appropriate for an embed context.
code-runner/next.config.js (1)
4-15: Verify wildcard CORS is appropriate for your security model.The configuration allows requests from any origin (
Access-Control-Allow-Origin: *). While this may be intentional for a public embed service, ensure this aligns with your security requirements, especially if any endpoints handle sensitive operations or data.Consider restricting to known domains in production:
🔎 Example of domain restriction
async headers() { return [ { source: "/embed/:path*", // Only embed routes need CORS headers: [ { key: "Access-Control-Allow-Origin", value: process.env.ALLOWED_ORIGINS || "https://docs.example.com" }, { key: "Access-Control-Allow-Methods", value: "GET, OPTIONS" }, { key: "Access-Control-Allow-Headers", value: "Content-Type" }, ], }, ]; },If wildcard CORS is required for public embeds, ensure that:
- API endpoints validate authentication tokens (like
EXECUTOR_API_KEY)- Rate limiting is in place
- No sensitive data is exposed without proper authorization
code-runner/src/app/layout.tsx (1)
9-19: LGTM!The layout implementation follows Next.js App Router best practices. The hardcoded dark theme aligns well with the code runner's technical aesthetic.
code-executor/src/server.ts (1)
40-63: LGTM on request validation and execution flow.The validation logic is solid:
- Type checking for
codefield- Length limit enforcement (10KB)
- Proper error handling with try-catch
- Appropriate HTTP status codes
The integration with
executeCodelooks correct based on the executor module's API.code-runner/src/components/ui/resizable.tsx (1)
7-42: LGTM!Clean wrapper implementation around
react-resizable-panelswith appropriate styling and prop forwarding. The conditionalwithHandlerendering and responsive Tailwind classes are well-structured.code-runner/src/components/ui/tabs.tsx (1)
7-49: LGTM!Standard Radix UI wrapper pattern with proper
forwardRefusage anddisplayNamepreservation. The implementation follows React best practices for component composition.code-executor/src/executor.ts (1)
103-122: LGTM on timeout and output size enforcement.The implementation properly enforces both execution timeout (30s) and output size limits (50KB) using:
setTimeoutwithSIGKILLfor hard termination- Stream monitoring with immediate termination on exceeding limits
This provides strong defense against resource exhaustion attacks.
code-runner/src/components/code-runner.tsx (2)
33-68: LGTM on the execution hook and error handling.The
useRunnableCodehook properly:
- Manages loading state during execution
- Clears previous results and errors before each run
- Handles both HTTP errors and network failures
- Extracts error details from the response payload
The error handling logic on Lines 50-55 correctly checks both
res.okanddata.error.
165-243: Well-structured responsive layout with proper state management.The
CodeRunnercomponent demonstrates good architectural decisions:
- Clean separation between desktop (resizable panels) and mobile (stacked) layouts
- Proper state synchronization between tabs and execution context
- Reusable subcomponents (Console, CodeBlock, EmptyConsole)
| COPY package*.json ./ | ||
| RUN npm ci --only=production | ||
|
|
||
| COPY tsconfig.json ./ | ||
| COPY src ./src | ||
|
|
||
| RUN npm run build || npx tsc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Build will fail: TypeScript not available.
The Dockerfile installs only production dependencies (npm ci --only=production) before building, but TypeScript is typically a dev dependency. The build step on Line 17 will fail because the TypeScript compiler won't be installed.
🔎 Recommended fix using multi-stage build
FROM node:20-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
-RUN groupadd -r executor && useradd -r -g executor executor
-
WORKDIR /app
COPY package*.json ./
-RUN npm ci --only=production
+RUN npm ci
COPY tsconfig.json ./
COPY src ./src
-RUN npm run build || npx tsc
+RUN npm run build
+
+# Production stage
+FROM node:20-slim
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ ca-certificates \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN groupadd -r executor && useradd -r -g executor executor
+
+WORKDIR /app
+
+COPY package*.json ./
+RUN npm ci --only=production
+
+COPY --from=0 /app/dist ./dist
RUN mkdir -p .temp && chown -R executor:executor /app
USER executor
ENV NODE_ENV=production
ENV PORT=3040
EXPOSE 3040
CMD ["node", "dist/server.js"]Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In code-executor/Dockerfile around lines 11 to 17, the Dockerfile runs npm ci
--only=production before a TypeScript build so the tsc compiler (a
devDependency) is not installed and the build will fail; fix by using a
multi-stage build or installing devDependencies for the build stage: in the
first stage install all dependencies (no --only=production) or run npm ci, run
the TypeScript build, then in a final runtime stage copy only the produced dist
and production deps (or run npm prune --production) so the final image contains
only production packages while the build stage has tsc.
| COPY tsconfig.json ./ | ||
| COPY src ./src | ||
|
|
||
| RUN npm run build || npx tsc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove the build command fallback.
The fallback pattern || npx tsc masks failures from the primary npm run build command. If the build script fails for any reason (missing dependencies, TypeScript errors, etc.), the fallback will execute and potentially hide the actual error.
🔎 Proposed fix
-RUN npm run build || npx tsc
+RUN npm run build📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| RUN npm run build || npx tsc | |
| RUN npm run build |
🤖 Prompt for AI Agents
In code-executor/Dockerfile around line 17, the RUN step uses "npm run build ||
npx tsc" which masks failures from the primary build; remove the fallback and
change the step to run only "npm run build" so the Docker build fails on real
build errors, and ensure any required dev/build dependencies are installed
earlier in the Dockerfile so the build can run successfully.
| "@lightprotocol/compressed-token": "0.22.1-alpha.7", | ||
| "@lightprotocol/stateless.js": "0.22.1-alpha.6", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
@lightprotocol/compressed-token 0.22.1-alpha.7 production ready stable
💡 Result:
Short answer: No — 0.22.1-alpha.7 is an alpha release and not considered production-stable. Use the published stable release (0.22.0) for production or wait for a non‑alpha 0.22.1. Sources: npm package page (latest stable 0.22.0) and Lightprotocol changelog referencing alpha releases. [1][2]
Sources
- npm: @lightprotocol/compressed-token — version 0.22.0 (latest). [1]
- Lightprotocol changelog / docs showing alpha releases (0.22.1-alpha.* referenced). [2]
🌐 Web query:
Light Protocol packages stable releases 0.22.1 npm
💡 Result:
Short answer: There is no stable 0.22.1 release on npm — the latest published stable npm versions I find are 0.22.0. The Light docs/package pages reference 0.22.1 as alpha (e.g., 0.22.1-alpha.1), not a stable npm release. [1][2][3]
Sources:
- @lightprotocol/compressed-token (npm) — version 0.22.0. [1]
- @lightprotocol/stateless.js docs — shows v0.22.1-alpha.1. [2]
- Package index / metadata (socket.dev) for @lightprotocol/stateless.js showing latest stable 0.22.0. [3]
🏁 Script executed:
# Check the actual package.json file to confirm versions
cat -n code-executor/package.json | head -20Repository: Lightprotocol/docs-v2
Length of output: 721
🏁 Script executed:
# Search for any documentation or comments about these packages
rg "lightprotocol" --type json --type markdown -A 2 -B 2Repository: Lightprotocol/docs-v2
Length of output: 2646
🏁 Script executed:
# Check if there are version specifications elsewhere
fd package.json | xargs grep -l "lightprotocol"Repository: Lightprotocol/docs-v2
Length of output: 91
Upgrade to stable release versions of Light Protocol packages.
The executor service currently depends on alpha versions of Light Protocol packages (@lightprotocol/compressed-token@0.22.1-alpha.7 and @lightprotocol/stateless.js@0.22.1-alpha.6). These alpha releases are not production-ready. Upgrade to the stable 0.22.0 versions available on npm, or wait for stable 0.22.1 releases before deploying to production.
🤖 Prompt for AI Agents
In code-executor/package.json around lines 12 to 13, the dependencies reference
alpha releases of Light Protocol packages; replace
"@lightprotocol/compressed-token": "0.22.1-alpha.7" and
"@lightprotocol/stateless.js": "0.22.1-alpha.6" with the stable "0.22.0"
versions (or pin to the upcoming stable 0.22.1 if/when released), then run npm
install to update package-lock.json (or yarn.lock) and run the test suite to
confirm no breaking changes; commit the updated lockfile alongside package.json.
| return new Promise((resolve) => { | ||
| const child = spawn("npx", ["tsx", filePath], { | ||
| timeout: TIMEOUT_MS, | ||
| env: { | ||
| ...process.env, | ||
| NODE_NO_WARNINGS: "1", | ||
| NODE_OPTIONS: "--max-old-space-size=128", | ||
| }, | ||
| cwd: join(__dirname, ".."), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Environment variables are exposed to executed code.
Line 86 passes process.env to the spawned child process, which means executed user code can access all environment variables including potentially sensitive values like API keys, database credentials, and other secrets.
🔎 Recommended fix: Use empty environment
const child = spawn("npx", ["tsx", filePath], {
timeout: TIMEOUT_MS,
env: {
- ...process.env,
+ PATH: process.env.PATH, // Only pass essential variables
NODE_NO_WARNINGS: "1",
NODE_OPTIONS: "--max-old-space-size=128",
},
cwd: join(__dirname, ".."),
});Alternatively, use an explicit allowlist of safe environment variables or run with an empty environment.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return new Promise((resolve) => { | |
| const child = spawn("npx", ["tsx", filePath], { | |
| timeout: TIMEOUT_MS, | |
| env: { | |
| ...process.env, | |
| NODE_NO_WARNINGS: "1", | |
| NODE_OPTIONS: "--max-old-space-size=128", | |
| }, | |
| cwd: join(__dirname, ".."), | |
| }); | |
| return new Promise((resolve) => { | |
| const child = spawn("npx", ["tsx", filePath], { | |
| timeout: TIMEOUT_MS, | |
| env: { | |
| PATH: process.env.PATH, // Only pass essential variables | |
| NODE_NO_WARNINGS: "1", | |
| NODE_OPTIONS: "--max-old-space-size=128", | |
| }, | |
| cwd: join(__dirname, ".."), | |
| }); |
🤖 Prompt for AI Agents
In code-executor/src/executor.ts around lines 82–91, the spawned child process
currently inherits all of process.env (exposing secrets) — replace the spread of
process.env with an explicit, minimal environment: either an empty env or an
allowlist that only includes non-sensitive keys required to run (e.g.,
NODE_NO_WARNINGS and NODE_OPTIONS), and if you need to run npx keep PATH only;
do not pass process.env wholesale. Ensure the child still receives required keys
(PATH if using npx) and remove any secrets or broad inheritance from
process.env.
| "target": "ES2022", | ||
| "module": "ESNext", | ||
| "moduleResolution": "bundler", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
TypeScript moduleResolution bundler vs node for Node.js server applications
💡 Result:
Short answer
- For a Node.js server app, prefer a Node-targeted moduleResolution (node16/node18/nodenext) so TypeScript enforces the same resolution rules Node will use at runtime. Use "bundler" only if you actually bundle the server code (and understand the trade‑offs). [1][2][3]
Why (key differences)
- Node modes (node16/node18/nodenext) emulate Node’s resolution: they require correct file extensions for ESM, follow package.json "exports"/"imports", and check import/require differences so emitted JS will run in Node. This prevents producing import specifiers that work in bundlers but will fail under Node. [1][2]
- bundler mode lets extensionless relative imports (./utils) and other bundler‑friendly patterns pass typechecking; it assumes a bundler will rewrite/resolve specifiers, so it can allow code that will break when run directly in Node (unless you actually bundle first). [1][2][3]
Practical effects for Node servers
- If you use ESM in Node, node(16/18)/nodenext will make TypeScript require .js/.mjs/.cjs correctness and honor "exports" so runtime import errors are caught at compile time. [1][2]
- Using bundler may hide missing .js extensions, incorrect package "exports" targets, or other issues that will only appear when Node runs the emitted files. [1][2]
- If you publish a library, bundler mode is "infectious": it may produce output that only works for bundler consumers; prefer Node modes for libraries unless you bundle everything (including declarations) and guarantee consumers run in bundlers. [1][3]
Recommendations (concise)
- Node.js server app (not bundled): set moduleResolution to node18/node16/nodenext and set module to matching Node target (e.g., "module": "node18" or "nodenext") and include "type": "module" in package.json if using ESM. This catches Node runtime issues at compile time. [1][2]
- If you build a single bundled artifact for deployment (you run a bundler that resolves all imports and emits final JS you deploy), you can use "bundler" with "module": "esnext"/"preserve" — but ensure the bundler produces correct runtime module specifiers and bundles (or produces compatible) declaration files. [1][3]
- When publishing a library, prefer Node resolution for maximal compatibility unless you control the consumer environment and produce bundled declarations. [3]
Minimal example tsconfig choices
-
For Node ESM server:
- "module": "node18" (or "nodenext")
- "moduleResolution": "node18" (or "nodenext")
- package.json: "type": "module". [1][2]
-
For bundler-built output:
- "module": "esnext" (or "preserve")
- "moduleResolution": "bundler"
- ensure bundler handles extensions/exports and that declarations are compatible. [1][3]
Sources
[1] TypeScript handbook — Choosing compiler options / guidance on bundler vs Node resolution.
[2] TypeScript tsconfig.moduleResolution reference (description of node16/node18/nodenext vs bundler).
[3] TypeScript modules theory / guidance about libraries, bundling and declaration files.
🏁 Script executed:
# Check the tsconfig.json file
cat -n code-executor/tsconfig.json
# Look for Dockerfile
fd Dockerfile
# Check for bundler configuration
fd webpack.config package.json esbuild.configRepository: Lightprotocol/docs-v2
Length of output: 703
🏁 Script executed:
# Check Dockerfile
cat -n code-executor/Dockerfile
# Look for build configuration and package.json
ls -la code-executor/ | grep -E "webpack|esbuild|rollup|tsup|package.json|build"
# Check package.json
cat code-executor/package.json | head -50Repository: Lightprotocol/docs-v2
Length of output: 1428
Use Node.js-compatible moduleResolution for unbundled Node.js server.
The code-executor service is a Node.js application built with only tsc (no bundler) and run directly with node dist/server.js. Using moduleResolution: "bundler" in this setup is incompatible because TypeScript will allow patterns that work in bundlers but will fail under Node.js's module resolution, such as extensionless imports and incorrect package.json "exports" handling. With "type": "module" (ESM) in package.json, change to "moduleResolution": "node18" or "nodenext" to enforce proper Node.js module resolution at compile time, preventing runtime failures.
🤖 Prompt for AI Agents
In code-executor/tsconfig.json around lines 3 to 5, moduleResolution is set to
"bundler" which is incompatible with an unbundled Node.js service; update
"moduleResolution" to a Node-compatible mode such as "node18" or "nodenext"
(depending on whether you need Node.js 18-specific behavior or full Node ESM
resolution) so TypeScript enforces Node semantics for extensionless imports and
package "exports" handling, then recompile to verify there are no now-revealed
import/extension issues.
| const response = await fetch(`${CODE_EXECUTOR_URL}/typescript`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Authorization: `Bearer ${EXECUTOR_API_KEY}`, | ||
| }, | ||
| body: JSON.stringify({ code }), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add timeout to prevent hanging requests.
The fetch call to the code executor lacks a timeout, which could cause the API route to hang indefinitely if the executor becomes unresponsive. This can exhaust server resources and degrade user experience.
🔎 Proposed fix to add timeout using AbortSignal
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
+
const response = await fetch(`${CODE_EXECUTOR_URL}/typescript`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${EXECUTOR_API_KEY}`,
},
body: JSON.stringify({ code }),
+ signal: controller.signal,
});
+
+ clearTimeout(timeoutId);🤖 Prompt for AI Agents
In code-runner/src/app/api/run/route.ts around lines 34 to 41, the fetch to the
code executor has no timeout and can hang indefinitely; fix by creating an
AbortController, pass its signal into fetch, start a timer (e.g., setTimeout)
that calls controller.abort() after a sensible timeout (e.g., 5-10s), and clear
that timer once fetch resolves or rejects; also handle the abort case
specifically (detect AbortError or error.name === 'AbortError') to return a
suitable timeout response and avoid leaking the timer.
| const { svg, color } = getIcon(filename); | ||
| const __html = svg.replace( | ||
| /svg/, | ||
| `svg fill='${color}' height='28' style='margin-left: -6px; margin-top: -6px;'` | ||
| ); | ||
| return ( | ||
| <span className={className}> | ||
| <span | ||
| dangerouslySetInnerHTML={{ __html }} | ||
| style={{ display: "contents" }} | ||
| /> | ||
| </span> | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
seti-icons npm package latest version and maintenance status
💡 Result:
Summary: seti-icons — latest published npm version 0.0.4; last published ~6 years ago and appears unmaintained/low-activity (few maintainers, low weekly downloads). Sources: [1][2].
Sources
- jsDelivr package info for seti-icons (v0.0.4). [1]
- package overview / health (Socket) for seti-icons (latest 0.0.4, last published ~6 years ago, low weekly downloads). [2]
🏁 Script executed:
# Search for seti-icons imports and usage in the codebase
rg "seti-icons" --type ts --type tsx
# Find the getIcon function definition
rg "getIcon" --type ts --type tsx -A 5Repository: Lightprotocol/docs-v2
Length of output: 120
🏁 Script executed:
# Search for seti-icons imports in typescript/tsx files
rg "seti-icons" --type ts
# Also try a broader search without type restriction
rg "seti-icons"
# Find the getIcon function definition
rg "getIcon" -A 5
# Check the code-icon.tsx file directly
cat code-runner/src/components/code-icon.tsxRepository: Lightprotocol/docs-v2
Length of output: 2642
Address unmaintained seti-icons dependency.
The dangerouslySetInnerHTML usage here is a false positive: SVG content comes from the trusted seti-icons library, color values are library-generated, and no user input is directly injected. However, verification reveals that seti-icons is unmaintained—last published ~6 years ago with no active development. The codebase uses v0.0.3 of this abandoned library.
Consider migrating to an actively maintained icon library like lucide-react (already in use elsewhere in this component) or another well-maintained alternative to reduce long-term maintenance risk and potential security vulnerabilities in dependencies.
🧰 Tools
🪛 ast-grep (0.40.3)
[warning] 54-54: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
[error] 55-55: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🤖 Prompt for AI Agents
In code-runner/src/components/code-icon.tsx around lines 47 to 59, the component
relies on the unmaintained seti-icons package and uses dangerouslySetInnerHTML
to inject SVG strings; replace this by removing the seti-icons dependency and
mapping file extensions to React SVG icon components (preferably lucide-react
which is already in the project) instead of inserting raw HTML, pass color/size
via props/styles on the React icon components, eliminate dangerouslySetInnerHTML
usage, update imports and the getIcon mapping to return ReactElements rather
than SVG strings, and run/adjust any tests or types that relied on the previous
getIcon output.
| function CodeBlock({ highlightedHtml }: { highlightedHtml: string }) { | ||
| return ( | ||
| <div | ||
| className="overflow-auto px-0 py-3 m-0 rounded-none bg-ch-background font-mono text-sm max-h-full selection:bg-ch-selection [&_pre]:bg-transparent [&_pre]:p-0 [&_pre]:m-0 [&_pre]:px-4 [&_code]:bg-transparent" | ||
| style={{ direction: "ltr", textAlign: "left" }} | ||
| dangerouslySetInnerHTML={{ __html: highlightedHtml }} | ||
| /> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, find the CodeBlock component usage and where highlightedHtml is passed
rg -n "CodeBlock\|highlightedHtml" code-runner/src/components/code-runner.tsx -A 2 -B 2Repository: Lightprotocol/docs-v2
Length of output: 47
🏁 Script executed:
# Find the highlighter.ts file and examine its implementation
find code-runner/src -name "highlighter.ts" -o -name "highlighter.js" | head -20Repository: Lightprotocol/docs-v2
Length of output: 99
🏁 Script executed:
# Search for all usages of highlightedHtml in the codebase
rg -rn "highlightedHtml" code-runner/src --type ts --type tsxRepository: Lightprotocol/docs-v2
Length of output: 92
🏁 Script executed:
# Check for any other potential sources of HTML rendering in the component
rg -n "dangerouslySetInnerHTML" code-runner/src --type ts --type tsx -B 3 -A 3Repository: Lightprotocol/docs-v2
Length of output: 92
🏁 Script executed:
# List files in code-runner directory structure
find code-runner/src -type f -name "*.ts" -o -name "*.tsx" | head -30Repository: Lightprotocol/docs-v2
Length of output: 519
🏁 Script executed:
# Check the actual content of code-runner.tsx around the CodeBlock component
cat -n code-runner/src/components/code-runner.tsx | sed -n '150,170p'Repository: Lightprotocol/docs-v2
Length of output: 770
🏁 Script executed:
# Find CodeBlock component usage without file type restrictions
rg -n "CodeBlock" code-runner/srcRepository: Lightprotocol/docs-v2
Length of output: 293
🏁 Script executed:
# Examine the highlighter.ts file to see how HTML is generated
cat -n code-runner/src/lib/highlighter.tsRepository: Lightprotocol/docs-v2
Length of output: 760
🏁 Script executed:
# Search for all dangerouslySetInnerHTML usage
rg -n "dangerouslySetInnerHTML" code-runner/srcRepository: Lightprotocol/docs-v2
Length of output: 260
🏁 Script executed:
# Find where current is defined and how highlightedHtml is set in CodeRunner
cat -n code-runner/src/components/code-runner.tsx | sed -n '200,230p'Repository: Lightprotocol/docs-v2
Length of output: 1568
🏁 Script executed:
# Search for CodeTab type definition
rg -n "type CodeTab\|interface CodeTab" code-runner/srcRepository: Lightprotocol/docs-v2
Length of output: 47
🏁 Script executed:
# Find where tabs are populated/initialized with highlightedHtml
rg -n "highlightedHtml.*=" code-runner/srcRepository: Lightprotocol/docs-v2
Length of output: 175
🏁 Script executed:
# Look at the useRunnableCode hook implementation
cat -n code-runner/src/components/code-runner.tsx | sed -n '33,68p'Repository: Lightprotocol/docs-v2
Length of output: 1364
🏁 Script executed:
# Check for CodeTab initialization in the entire component
cat -n code-runner/src/components/code-runner.tsx | sed -n '1,90p'Repository: Lightprotocol/docs-v2
Length of output: 2933
🏁 Script executed:
# Search for where CodeRunner is imported and used
rg -n "import.*CodeRunner\|<CodeRunner" code-runner/srcRepository: Lightprotocol/docs-v2
Length of output: 47
🏁 Script executed:
# Look for where tabs are created/initialized with highlightedHtml
rg -n "highlightedHtml:" code-runner/src -B 3 -A 3Repository: Lightprotocol/docs-v2
Length of output: 1787
🏁 Script executed:
# Search for calls to highlightCode function
rg -n "highlightCode" code-runner/srcRepository: Lightprotocol/docs-v2
Length of output: 459
🏁 Script executed:
# Look at the page.tsx to see how CodeRunner is used
cat -n code-runner/src/app/page.tsxRepository: Lightprotocol/docs-v2
Length of output: 661
🏁 Script executed:
# Search for all usages of CodeRunner component
rg -n "CodeRunner\|CodeTab" code-runner/src --type-list | head -20Repository: Lightprotocol/docs-v2
Length of output: 454
🏁 Script executed:
# Check if there are any other pages that use CodeRunner
find code-runner/src/app -name "page.tsx" -exec grep -l "CodeRunner\|highlightedHtml" {} \;Repository: Lightprotocol/docs-v2
Length of output: 111
🏁 Script executed:
# Look for any direct assignment to highlightedHtml that might bypass highlightCode
rg -n "highlightedHtml\s*[:=]" code-runner/src -B 2 -A 2Repository: Lightprotocol/docs-v2
Length of output: 1696
🏁 Script executed:
# Verify Shiki is properly imported and used
cat -n code-runner/src/lib/highlighter.tsRepository: Lightprotocol/docs-v2
Length of output: 760
🏁 Script executed:
# Check the code-icon.tsx dangerouslySetInnerHTML usage for completeness
cat -n code-runner/src/components/code-icon.tsx | sed -n '50,60p'Repository: Lightprotocol/docs-v2
Length of output: 398
🏁 Script executed:
# See the full context of code-icon.tsx
head -n 60 code-runner/src/components/code-icon.tsxRepository: Lightprotocol/docs-v2
Length of output: 1374
Add a safety comment explaining that highlightedHtml is generated exclusively by Shiki.
The dangerouslySetInnerHTML usage here is safe because highlightedHtml originates exclusively from Shiki's codeToHtml() method, which properly escapes all user input. However, documenting this intent prevents future maintainers from inadvertently passing unsanitized HTML to this component.
Suggested safety comment
function CodeBlock({ highlightedHtml }: { highlightedHtml: string }) {
return (
+ // Safe: highlightedHtml is generated exclusively by Shiki syntax highlighter
+ // which properly escapes all user input. Never pass unsanitized HTML here.
<div
className="overflow-auto px-0 py-3 m-0 rounded-none bg-ch-background font-mono text-sm max-h-full selection:bg-ch-selection [&_pre]:bg-transparent [&_pre]:p-0 [&_pre]:m-0 [&_pre]:px-4 [&_code]:bg-transparent"
style={{ direction: "ltr", textAlign: "left" }}
dangerouslySetInnerHTML={{ __html: highlightedHtml }}
/>
);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function CodeBlock({ highlightedHtml }: { highlightedHtml: string }) { | |
| return ( | |
| <div | |
| className="overflow-auto px-0 py-3 m-0 rounded-none bg-ch-background font-mono text-sm max-h-full selection:bg-ch-selection [&_pre]:bg-transparent [&_pre]:p-0 [&_pre]:m-0 [&_pre]:px-4 [&_code]:bg-transparent" | |
| style={{ direction: "ltr", textAlign: "left" }} | |
| dangerouslySetInnerHTML={{ __html: highlightedHtml }} | |
| /> | |
| ); | |
| } | |
| function CodeBlock({ highlightedHtml }: { highlightedHtml: string }) { | |
| return ( | |
| // Safe: highlightedHtml is generated exclusively by Shiki syntax highlighter | |
| // which properly escapes all user input. Never pass unsanitized HTML here. | |
| <div | |
| className="overflow-auto px-0 py-3 m-0 rounded-none bg-ch-background font-mono text-sm max-h-full selection:bg-ch-selection [&_pre]:bg-transparent [&_pre]:p-0 [&_pre]:m-0 [&_pre]:px-4 [&_code]:bg-transparent" | |
| style={{ direction: "ltr", textAlign: "left" }} | |
| dangerouslySetInnerHTML={{ __html: highlightedHtml }} | |
| /> | |
| ); | |
| } |
🧰 Tools
🪛 ast-grep (0.40.3)
[warning] 159-159: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
[error] 160-160: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🤖 Prompt for AI Agents
In code-runner/src/components/code-runner.tsx around lines 155 to 163, add a
concise safety comment above the CodeBlock component (or directly above the
dangerouslySetInnerHTML usage) stating that highlightedHtml is generated
exclusively by Shiki's codeToHtml() and is properly escaped/escaped-by-Shiki, so
the use of dangerouslySetInnerHTML is intentional and safe; keep the comment
short, include the Shiki source and function name (codeToHtml) and a note to
avoid passing unsanitized HTML into this prop.
| onClick={() => { | ||
| navigator.clipboard.writeText(text); | ||
| setCopied(true); | ||
| setTimeout(() => setCopied(false), 1200); | ||
| }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling and cleanup for clipboard operation.
The clipboard write operation can fail (due to permissions, HTTPS requirement, or browser support) with no error handling. Additionally, the setTimeout is not cleaned up if the component unmounts during the 1.2-second window.
🔎 Proposed fix with error handling and cleanup
export function CopyButton({
text,
className,
}: {
text: string;
className?: string;
}) {
const [copied, setCopied] = useState(false);
+ const [error, setError] = useState(false);
return (
<button
className={cn(
"hover:text-ch-tab-active-foreground -mx-1 p-1 rounded hidden sm:block",
className
)}
- onClick={() => {
- navigator.clipboard.writeText(text);
- setCopied(true);
- setTimeout(() => setCopied(false), 1200);
+ onClick={async () => {
+ try {
+ await navigator.clipboard.writeText(text);
+ setCopied(true);
+ setError(false);
+ setTimeout(() => setCopied(false), 1200);
+ } catch (err) {
+ console.error("Failed to copy:", err);
+ setError(true);
+ setTimeout(() => setError(false), 1200);
+ }
}}
aria-label="Copy to clipboard"
>
- {copied ? (
+ {error ? (
+ <span className="text-red-500">✕</span>
+ ) : copied ? (
<Check size={16} className="block" />
) : (
<Copy size={16} className="block" />
)}
</button>
);
}For the timeout cleanup, consider using useEffect:
useEffect(() => {
if (copied || error) {
const timer = setTimeout(() => {
setCopied(false);
setError(false);
}, 1200);
return () => clearTimeout(timer);
}
}, [copied, error]);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In code-runner/src/components/copy-button.tsx around lines 22 to 26, the
clipboard write is called without error handling and the setTimeout has no
cleanup; wrap navigator.clipboard.writeText(text) in an async try/catch (or
Promise .then/.catch) and only call setCopied(true) on success, set an error
state (e.g., setError(true) or similar) on failure and include a user-friendly
message for unsupported clipboard API, and replace the inline setTimeout with a
cleanupable timer managed by useEffect or a ref so the timer is cleared on
unmount (start the timer when copied or error becomes true and clear it in the
effect’s cleanup).
| const rpc = createRpc(); | ||
| const payer = Keypair.generate(); | ||
| await rpc.requestAirdrop(payer.publicKey, 10e9); | ||
|
|
||
| const owner = Keypair.generate(); | ||
| await rpc.requestAirdrop(owner.publicKey, 1e9); | ||
|
|
||
| const mintAuthority = Keypair.generate(); | ||
| const mintKeypair = Keypair.generate(); | ||
| const { mint } = await createMint( | ||
| rpc, | ||
| payer, | ||
| mintAuthority.publicKey, | ||
| 9, | ||
| mintKeypair, | ||
| ); | ||
|
|
||
| await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); | ||
|
|
||
| // Get c-token ATA address | ||
| const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); | ||
|
|
||
| // Load compressed tokens to hot balance | ||
| // Creates ATA if needed, returns null if nothing to load | ||
| const signature = await loadAta(rpc, ctokenAta, owner, mint, payer); | ||
|
|
||
| if (signature) { | ||
| console.log("Loaded tokens to hot balance"); | ||
| console.log("Transaction:", signature); | ||
| } else { | ||
| console.log("Nothing to load"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
rg -n "requestAirdrop" --type ts -A 3 -B 1Repository: Lightprotocol/docs-v2
Length of output: 871
Add confirmation waits after airdrop transactions to match codebase standards.
The sequential airdrops on lines 35 and 38 are awaited but not explicitly confirmed before subsequent operations. The established pattern in the codebase (seen in code-runner/src/app/embed/create-mint/page.tsx) confirms airdrop transactions immediately:
const sig = await rpc.requestAirdrop(payer.publicKey, 10e9);
await rpc.confirmTransaction(sig);Add similar confirmation waits to prevent potential "insufficient funds" errors during execution. This is especially important if these examples will be executed by the new code runner infrastructure.
🤖 Prompt for AI Agents
In light-token/toolkits/for-streaming-tokens.mdx around lines 33 to 64, the two
awaited requestAirdrop calls are not followed by explicit transaction
confirmations which can lead to "insufficient funds" races; capture each airdrop
signature (e.g., sigPayer, sigOwner) and call await rpc.confirmTransaction(...)
for each signature immediately after its requestAirdrop, ensuring the
confirmations complete before continuing with mint creation and mintTo.
Summary by CodeRabbit
Release Notes
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.