BaseCommand.ts
Complete command architecture for micro-block pattern with CommandMetadata interface, OutputCommand and InputOnlyCommand base classes, and BaseError class for comprehensive error handling
BaseCommand.tsv1.0.05.1 KB
BaseCommand.ts(typescript)
1/**
2 * Base interfaces for Micro-Block Architecture commands
3 * All commands in the system implement one of these interfaces
4 */
5
6/**
7 * Metadata for command discovery and documentation
8 */
9export interface CommandMetadata {
10 name: string;
11 description: string;
12 category: string;
13 inputType: string;
14 outputType: string;
15 errorType: string;
16 version: string; // Command implementation version
17 contractVersion: string; // Input/output contract version
18 permissions?: string[];
19 timeout?: number;
20 dataFlow?: {
21 inputs: string[];
22 outputs: string[];
23 sideEffects: string[];
24 };
25 performance?: {
26 expectedDuration: string;
27 scaling: string;
28 };
29 /**
30 * Dependencies required by this command
31 */
32 dependencies?: {
33 /** Service dependencies (e.g., IQueueService, IDatabaseService) */
34 services?: string[];
35 /** Command dependencies (e.g., CreateProcessingJobCommand) */
36 commands?: string[];
37 /** External package dependencies (e.g., ytdl-core, openai) */
38 external?: string[];
39 };
40}
41
42/**
43 * Base interface that all commands must implement.
44 * Provides metadata access and validation through error throwing.
45 */
46export interface BaseCommand {
47 /**
48 * Validate that the command can execute with current configuration.
49 * Throws detailed validation errors instead of returning boolean.
50 * Use raiseError() method to throw errors with validation details.
51 */
52 validate(): void;
53 execute(): Promise<any>;
54 /** Get the static metadata for this command */
55 getMetadata(): CommandMetadata;
56}
57
58/**
59 * Commands that return data (queries, transformations, calculations)
60 */
61export abstract class OutputCommand<TInput, TOutput, TError extends Error> implements BaseCommand {
62 protected input?: TInput;
63 private errorHandlers: ((error: TError) => void)[] = [];
64
65 constructor(input?: TInput) {
66 this.input = input;
67 }
68
69 setInput(input: TInput): void {
70 this.input = input;
71 }
72
73 abstract execute(): Promise<TOutput>;
74
75 validate(): void {
76 // Default implementation does nothing - subclasses should override
77 // to throw detailed validation errors when validation fails
78 }
79
80 onError(handler: (error: TError) => void): void {
81 this.errorHandlers.push(handler);
82 }
83
84 /**
85 * Calls all registered error handlers and returns the error.
86 * Usage: throw this.raiseError(error)
87 * This pattern ensures error handlers are called before the error is thrown.
88 */
89 protected raiseError(error: TError): TError {
90 for (const handler of this.errorHandlers) {
91 if (handler) {
92 try {
93 handler(error);
94 } catch (e) {
95 console.error("Error in error handler:", e);
96 }
97 }
98 }
99 return error;
100 }
101
102 abstract getMetadata(): CommandMetadata;
103}
104
105/**
106 * Commands that perform side effects without returning data
107 */
108export abstract class InputOnlyCommand<TInput, TError extends Error> implements BaseCommand {
109 protected input?: TInput;
110 private errorHandlers: ((error: TError) => void)[] = [];
111
112 constructor(input?: TInput) {
113 this.input = input;
114 }
115
116 setInput(input: TInput): void {
117 this.input = input;
118 }
119
120 abstract execute(): Promise<void>;
121
122 validate(): void {
123 // Default implementation does nothing - subclasses should override
124 // to throw detailed validation errors when validation fails
125 }
126
127 onError(handler: (error: TError) => void): void {
128 this.errorHandlers.push(handler);
129 }
130
131 /**
132 * Calls all registered error handlers and returns the error.
133 * Usage: throw this.raiseError(error)
134 * This pattern ensures error handlers are called before the error is thrown.
135 */
136 protected raiseError(error: TError): TError {
137 for (const handler of this.errorHandlers) {
138 if (handler) {
139 try {
140 handler(error);
141 } catch (e) {
142 console.error("Error in error handler:", e);
143 }
144 }
145 }
146 return error;
147 }
148
149 abstract getMetadata(): CommandMetadata;
150}
151
152/**
153 * Base error class for all command errors
154 */
155export class BaseError extends Error {
156 public readonly timestamp: Date;
157 public readonly code?: string;
158 public readonly details?: Record<string, any>;
159
160 // Validation error properties - can be set after construction
161 public invalidInput: boolean = false;
162 public invalidInputName: string = '';
163 public invalidInputPath: string = '';
164
165 constructor(
166 message: string,
167 code?: string,
168 details?: Record<string, any>
169 ) {
170 super(message);
171 this.name = this.constructor.name;
172 this.timestamp = new Date();
173 this.code = code;
174 this.details = details;
175
176 // Maintains proper stack trace for where our error was thrown
177 Object.setPrototypeOf(this, new.target.prototype);
178
179 // Captures stack trace
180 if (Error.captureStackTrace) {
181 Error.captureStackTrace(this, this.constructor);
182 }
183 }
184
185 /**
186 * Mark this error as a validation error with specific input details
187 */
188 setValidationError(inputName: string, inputPath: string): this {
189 this.invalidInput = true;
190 this.invalidInputName = inputName;
191 this.invalidInputPath = inputPath;
192 return this;
193 }
194}
Metadata
- Path
- utaba/main/commands/micro-block/BaseCommand.ts
- Namespace
- utaba/main/commands/micro-block
- Author
- utaba
- Category
- commands
- Technology
- typescript
- Contract Version
- 1.0.0
- MIME Type
- application/typescript
- Published
- 18-Jul-2025
- Last Updated
- 18-Jul-2025