diff --git a/apps/demo-nextjs-app-router/app/api/script/route.tsx b/apps/demo-nextjs-app-router/app/api/script/route.tsx new file mode 100644 index 0000000..23ebeed --- /dev/null +++ b/apps/demo-nextjs-app-router/app/api/script/route.tsx @@ -0,0 +1,38 @@ +import { NextRequest, NextResponse } from "next/server"; +import { + executeScript, + getQueueStats, +} from "../../../services/bashExecService"; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const { scriptPath, args } = body; + + if (!scriptPath) { + return NextResponse.json( + { error: "Script path is required" }, + { status: 400 }, + ); + } + + const result = await executeScript({ + scriptPath, + args, + }); + + return NextResponse.json(result); + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} + +// Endpoint to get queue statistics +export async function GET() { + try { + const stats = getQueueStats(); + return NextResponse.json(stats); + } catch (error: any) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } +} diff --git a/apps/demo-nextjs-app-router/services/bashExecService.ts b/apps/demo-nextjs-app-router/services/bashExecService.ts new file mode 100644 index 0000000..e6cbf3e --- /dev/null +++ b/apps/demo-nextjs-app-router/services/bashExecService.ts @@ -0,0 +1,79 @@ +import { execa } from "execa"; +import PQueue from "p-queue"; + +// Configure the queue with a concurrency limit +const scriptQueue = new PQueue({ concurrency: 5 }); + +export type ScriptParams = { + scriptPath: string; + args?: string[]; + onProgress?: (data: string) => void; +}; + +export type ScriptResult = { + success: boolean; + stdout: string; + stderr: string; + error?: string; +}; + +/** + * Executes a bash script with queue management + */ +export async function executeScript( + params: ScriptParams, +): Promise { + // Add the script execution to the queue + const result = await scriptQueue.add(async () => { + try { + // Execute the bash script + const { stdout, stderr } = await execa( + "bash", + [params.scriptPath, ...(params.args || [])], + { + buffer: true, // Changed to true to ensure we get the complete output + }, + ); + + // If onProgress is provided, we can use it to stream script output + if (params.onProgress && stdout) { + params.onProgress(stdout.toString()); + } + + return { + success: true, + stdout: stdout ? stdout.toString() : "", + stderr: stderr ? stderr.toString() : "", + }; + } catch (error: any) { + // Handle script execution errors + return { + success: false, + stdout: error.stdout ? error.stdout.toString() : "", + stderr: error.stderr ? error.stderr.toString() : "", + error: error.message || "Failed to execute script", + }; + } + }); + + return result as ScriptResult; +} + +// Get current queue size and pending tasks +export function getQueueStats() { + return { + size: scriptQueue.size, + pending: scriptQueue.pending, + isPaused: scriptQueue.isPaused, + }; +} + +// Pause the queue (no further tasks will be executed until resumed) +export function pauseQueue() { + scriptQueue.pause(); +} + +// Resume the queue +export function resumeQueue() { + scriptQueue.start(); +} diff --git a/package-lock.json b/package-lock.json index 73d41ae..5526870 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "next": "^14.2.5", "open": "^10.0.3", "ora": "^8.0.1", + "p-queue": "^8.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "regenerator-runtime": "0.13.7", @@ -26910,6 +26911,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.0.tgz", + "integrity": "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/p-reduce": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", @@ -26932,6 +26953,17 @@ "node": ">=8" } }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", diff --git a/package.json b/package.json index 3d6231f..86452b4 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "next": "^14.2.5", "open": "^10.0.3", "ora": "^8.0.1", + "p-queue": "^8.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "regenerator-runtime": "0.13.7",