| /** | |
| * Exercise 1: Build a Simple Logging Callback | |
| * | |
| * Goal: Understand the basic callback lifecycle | |
| * | |
| * In this exercise, you'll: | |
| * 1. Extend BaseCallback to create a custom logger | |
| * 2. Implement onStart, onEnd, and onError methods | |
| * 3. Format output with emoji indicators | |
| * 4. See how callbacks observe Runnable execution | |
| * | |
| * This is the foundation of observability in your framework! | |
| */ | |
| import {Runnable} from '../../../../src/index.js'; | |
| import {BaseCallback} from '../../../../src/utils/callbacks.js'; | |
| // TODO: Create SimpleLoggerCallback that extends BaseCallback | |
| class SimpleLoggerCallback extends BaseCallback { | |
| constructor(options = {}) { | |
| super(); | |
| this.showTimestamp = options.showTimestamp ?? false; | |
| } | |
| // TODO: Implement onStart - log when runnable starts | |
| // Use emoji: ▶️ | |
| // Log to console: runnable name and input | |
| async onStart(runnable, input, config) { | |
| // Your code here | |
| } | |
| // TODO: Implement onEnd - log when runnable completes | |
| // Use emoji: ✔️ | |
| // Log to console: runnable name and output | |
| async onEnd(runnable, output, config) { | |
| // Your code here | |
| } | |
| // TODO: Implement onError - log when runnable errors | |
| // Use emoji: ❌ | |
| // Log to console: runnable name and error message | |
| async onError(runnable, error, config) { | |
| // Your code here | |
| } | |
| } | |
| // Test Runnables | |
| class GreeterRunnable extends Runnable { | |
| async _call(input, config) { | |
| return `Hello, ${input}!`; | |
| } | |
| } | |
| class UpperCaseRunnable extends Runnable { | |
| async _call(input, config) { | |
| if (typeof input !== 'string') { | |
| throw new Error('Input must be a string'); | |
| } | |
| return input.toUpperCase(); | |
| } | |
| } | |
| class ErrorRunnable extends Runnable { | |
| async _call(input, config) { | |
| throw new Error('Intentional error for testing'); | |
| } | |
| } | |
| // TODO: Test your callback | |
| async function exercise1() { | |
| console.log('=== Exercise 1: Simple Logging Callback ===\n'); | |
| // TODO: Create an instance of your SimpleLoggerCallback | |
| const logger = null; // Replace with your code | |
| const config = { | |
| callbacks: [logger] | |
| }; | |
| // Test 1: Normal execution | |
| console.log('--- Test 1: Normal Execution ---'); | |
| const greeter = new GreeterRunnable(); | |
| // TODO: Invoke greeter with "World" and config | |
| const result1 = null; // Replace with your code | |
| console.log('Final result:', result1); | |
| console.log(); | |
| // Test 2: Pipeline | |
| console.log('--- Test 2: Pipeline ---'); | |
| const upper = new UpperCaseRunnable(); | |
| const pipeline = greeter.pipe(upper); | |
| // TODO: Invoke pipeline with "claude" and config | |
| const result2 = null; // Replace with your code | |
| console.log('Final result:', result2); | |
| console.log(); | |
| // Test 3: Error handling | |
| console.log('--- Test 3: Error Handling ---'); | |
| const errorRunnable = new ErrorRunnable(); | |
| try { | |
| // TODO: Invoke errorRunnable with "test" and config | |
| // Replace with your code | |
| } catch (error) { | |
| console.log('Caught error (expected):', error.message); | |
| } | |
| console.log('\n✓ Exercise 1 complete!'); | |
| } | |
| // Run the exercise | |
| exercise1().catch(console.error); | |
| /** | |
| * Expected Output: | |
| * | |
| * --- Test 1: Normal Execution --- | |
| * ▶️ Starting: GreeterRunnable | |
| * Input: World | |
| * ✔️ Completed: GreeterRunnable | |
| * Output: Hello, World! | |
| * Final result: Hello, World! | |
| * | |
| * --- Test 2: Pipeline --- | |
| * ▶️ Starting: RunnableSequence | |
| * Input: claude | |
| * ▶️ Starting: GreeterRunnable | |
| * Input: claude | |
| * ✔️ Completed: GreeterRunnable | |
| * Output: Hello, claude! | |
| * ▶️ Starting: UpperCaseRunnable | |
| * Input: Hello, claude! | |
| * ✔️ Completed: UpperCaseRunnable | |
| * Output: HELLO, CLAUDE! | |
| * ✔️ Completed: RunnableSequence | |
| * Output: HELLO, CLAUDE! | |
| * Final result: HELLO, CLAUDE! | |
| * | |
| * --- Test 3: Error Handling --- | |
| * ▶️ Starting: ErrorRunnable | |
| * Input: test | |
| * ❌ ErrorRunnable: Intentional error for testing | |
| * Caught error (expected): Intentional error for testing | |
| * | |
| * Learning Points: | |
| * 1. Callbacks see every step in execution | |
| * 2. onStart fires before _call() | |
| * 3. onEnd fires after successful _call() | |
| * 4. onError fires when _call() throws error | |
| * 5. Callbacks don't change the output - they just observe | |
| */ |