Welcome to Chapter 3!
In the previous chapter, Lazy Module Loading, we learned how to efficiently fetch the code file only when it is needed. We compared this to a librarian fetching a book from the archive.
Now that we have the "book" (the code file) open in front of us, we need to read it! In this chapter, we will build the Command Execution Handler.
Let's go back to our restaurant analogy.
When the ingredients arrive, the Head Chef doesn't necessarily chop every carrot personally. Instead, the Head Chef coordinates the process. They:
We want to run the heapdump logic. However, saving a snapshot of memory is riskyβit might fail (e.g., if the disk is full).
Our Execution Handler needs to try to run the task, catch any errors if they happen, and format the final message for the user.
The main application needs a consistent way to tell your code to "Start!"
Just like every car has an ignition slot for a key, every command module in our system must export a specific function named call.
// The system looks specifically for a function named "call"
export async function call() {
// Logic goes here
}
If we named it start() or run(), the system wouldn't find it. We must stick to the standard named call.
The Execution Handler is a manager. It shouldn't contain 500 lines of complex math or low-level system operations. Instead, it calls other helper functions (the "Service Layer") to do the heavy lifting.
This keeps our code clean. The Handler focuses on flow control: "Do this, then check that."
Let's build the heapdump.ts file. We will break it down into small steps.
First, we import the actual worker function. We haven't built this yet (we will in the next chapter), but we know we need it.
// heapdump.ts
// Import the "worker" logic (Service Layer)
import { performHeapDump } from '../../utils/heapDumpService.js'
We define our asynchronous call function. It needs to return a Promise because creating a heap dump takes time.
// Define the standard entry point
export async function call(): Promise<{ type: 'text'; value: string }> {
// We invoke the heavy logic here and wait for it
const result = await performHeapDump()
// ... continued below
Explanation:
async: Allows us to pause and wait for the dump to finish.await performHeapDump(): This is the Head Chef telling the line cook to make the food. We wait here until the work is done.
What if the disk is full? The result variable tells us if it succeeded or failed.
// Check if the worker reported a failure
if (!result.success) {
return {
type: 'text',
// We format a nice error message for the user
value: `Failed to create heap dump: ${result.error}`,
}
}
Explanation:
!result.success), we return an error message immediately. We don't crash the app; we just report the problem.If we pass the error check, it means everything went well! We return the success message.
// If we get here, it worked!
return {
type: 'text',
// Combine the file paths into a single string
value: `${result.heapPath}\n${result.diagPath}`,
}
}
Explanation:
heapPath and diagPath).How does the system invoke this handler?
When the load() function (from Chapter 2) finishes, the system gets the module object. It simply assumes there is a .call() property on it and executes it.
You might have noticed the return object looks specific:
{ type: 'text', value: '...' }
This is part of a Standardized Output Protocol. The Handler doesn't console.log directly to the screen. Instead, it returns a data object describing what should be shown. This allows the main application to decide if the text should be printed plain, colored green, or sent to a log file. We will cover this fully in Standardized Output Protocol.
In this chapter, we built the Command Execution Handler.
We learned:
call function: The standard entry point for all commands.
But waitβwe keep calling performHeapDump(), but we haven't written it yet! That is the actual "heavy lifting" logic.
In the next chapter, we will roll up our sleeves and write the code that actually touches the system memory.
Next Chapter: Service Layer Delegation
Generated by Code IQ