@miller-tech/uap 2.36.4 → 2.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/config/model-profiles/qwen38.json +3 -3
  2. package/config/model-registry.json +96 -6
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/bin/cli.js +57 -0
  5. package/dist/bin/cli.js.map +1 -1
  6. package/dist/cli/deliver.d.ts +46 -0
  7. package/dist/cli/deliver.d.ts.map +1 -1
  8. package/dist/cli/deliver.js +155 -36
  9. package/dist/cli/deliver.js.map +1 -1
  10. package/dist/cli/models.d.ts +11 -1
  11. package/dist/cli/models.d.ts.map +1 -1
  12. package/dist/cli/models.js +164 -3
  13. package/dist/cli/models.js.map +1 -1
  14. package/dist/cli/workflow.d.ts +36 -0
  15. package/dist/cli/workflow.d.ts.map +1 -0
  16. package/dist/cli/workflow.js +246 -0
  17. package/dist/cli/workflow.js.map +1 -0
  18. package/dist/delivery/run-state.d.ts +1 -1
  19. package/dist/delivery/run-state.d.ts.map +1 -1
  20. package/dist/delivery/run-state.js +1 -1
  21. package/dist/delivery/run-state.js.map +1 -1
  22. package/dist/delivery/team-solve-deliver.d.ts +83 -0
  23. package/dist/delivery/team-solve-deliver.d.ts.map +1 -0
  24. package/dist/delivery/team-solve-deliver.js +191 -0
  25. package/dist/delivery/team-solve-deliver.js.map +1 -0
  26. package/dist/inference/model-files.d.ts +111 -0
  27. package/dist/inference/model-files.d.ts.map +1 -0
  28. package/dist/inference/model-files.js +172 -0
  29. package/dist/inference/model-files.js.map +1 -0
  30. package/dist/placement/registry.d.ts +650 -0
  31. package/dist/placement/registry.d.ts.map +1 -1
  32. package/dist/placement/registry.js +21 -0
  33. package/dist/placement/registry.js.map +1 -1
  34. package/dist/workflow/loader.d.ts +34 -0
  35. package/dist/workflow/loader.d.ts.map +1 -0
  36. package/dist/workflow/loader.js +232 -0
  37. package/dist/workflow/loader.js.map +1 -0
  38. package/dist/workflow/runner.d.ts +90 -0
  39. package/dist/workflow/runner.d.ts.map +1 -0
  40. package/dist/workflow/runner.js +416 -0
  41. package/dist/workflow/runner.js.map +1 -0
  42. package/dist/workflow/types.d.ts +400 -0
  43. package/dist/workflow/types.d.ts.map +1 -0
  44. package/dist/workflow/types.js +147 -0
  45. package/dist/workflow/types.js.map +1 -0
  46. package/docs/INDEX.md +1 -0
  47. package/docs/architecture/adr/0013-declarative-workflow-layer.md +99 -0
  48. package/docs/architecture/adr/0014-loop-steps-and-team-solve-deliver.md +142 -0
  49. package/docs/benchmarks/LCB_DROID_SMOKE.md +111 -0
  50. package/docs/guides/DELIVER.md +3 -1
  51. package/docs/guides/WORKFLOWS.md +179 -0
  52. package/docs/plans/model-performance-proof-options.md +39 -0
  53. package/docs/reference/CLI.md +33 -0
  54. package/docs/reference/CONFIGURATION.md +1 -0
  55. package/docs/reference/FEATURES.md +18 -0
  56. package/examples/auto-coordinator-example.js +127 -0
  57. package/examples/demo-order-solid.ts +19 -0
  58. package/examples/order-processing-strategy-pattern.ts +186 -0
  59. package/examples/order-processor-refactored.ts +473 -0
  60. package/examples/order-solid-refactor.ts +830 -0
  61. package/examples/refactored-order-processing.ts +106 -0
  62. package/examples/run-order-processing.ts +48 -0
  63. package/examples/solid-order-processing.ts +324 -0
  64. package/examples/strategy-pattern-example.ts +100 -0
  65. package/examples/workflows/team-solve/README.md +85 -0
  66. package/examples/workflows/team-solve/check-samples.sh +50 -0
  67. package/examples/workflows/team-solve/sample/task.md +22 -0
  68. package/examples/workflows/team-solve/sample/tests/1.in +2 -0
  69. package/examples/workflows/team-solve/sample/tests/1.out +1 -0
  70. package/examples/workflows/team-solve/sample/tests/2.in +2 -0
  71. package/examples/workflows/team-solve/sample/tests/2.out +1 -0
  72. package/examples/workflows/team-solve/sample/tests/3.in +2 -0
  73. package/examples/workflows/team-solve/sample/tests/3.out +1 -0
  74. package/examples/workflows/team-solve/sample/tests/4.in +2 -0
  75. package/examples/workflows/team-solve/sample/tests/4.out +1 -0
  76. package/examples/workflows/team-solve/sample/tests/5.in +1 -0
  77. package/examples/workflows/team-solve/sample/tests/5.out +1 -0
  78. package/examples/workflows/team-solve/steps/ideate-a.md +26 -0
  79. package/examples/workflows/team-solve/steps/ideate-b.md +28 -0
  80. package/examples/workflows/team-solve/steps/plan.md +20 -0
  81. package/examples/workflows/team-solve/steps/repair.md +25 -0
  82. package/examples/workflows/team-solve/steps/review.md +29 -0
  83. package/examples/workflows/team-solve/steps/solve.md +26 -0
  84. package/examples/workflows/team-solve/workflow.yaml +78 -0
  85. package/examples/workflows/team-solve-deliver/README.md +84 -0
  86. package/examples/workflows/team-solve-deliver/steps/ideate-a.md +25 -0
  87. package/examples/workflows/team-solve-deliver/steps/ideate-b.md +28 -0
  88. package/examples/workflows/team-solve-deliver/steps/plan.md +25 -0
  89. package/examples/workflows/team-solve-deliver/steps/repair.md +20 -0
  90. package/examples/workflows/team-solve-deliver/steps/review.md +31 -0
  91. package/examples/workflows/team-solve-deliver/steps/solve.md +28 -0
  92. package/examples/workflows/team-solve-deliver/steps/verify.md +9 -0
  93. package/examples/workflows/team-solve-deliver/workflow.yaml +74 -0
  94. package/package.json +2 -1
  95. package/src/policies/enforcers/__pycache__/_common.cpython-311.pyc +0 -0
  96. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-311.pyc +0 -0
  97. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-311.pyc +0 -0
  98. package/templates/hooks/__pycache__/deliver_autoroute.cpython-311.pyc +0 -0
  99. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-311.pyc +0 -0
  100. package/tools/agents/scripts/anthropic_proxy.py +147 -14
@@ -0,0 +1,830 @@
1
+ /**
2
+ * SOLID Principles-Compliant Order Processing System
3
+ *
4
+ * This module demonstrates a refactored order processing system that strictly
5
+ * follows SOLID principles and the project's coding standards.
6
+ *
7
+ * ASSUMPTIONS:
8
+ * - Orders contain a valid 'type' property (string)
9
+ * - Order types are known at runtime or have fallback behavior
10
+ * - Processing is synchronous (no async operations required)
11
+ *
12
+ * EDGE CASES HANDLED:
13
+ * - Invalid/missing order data (throws AppError)
14
+ * - Unknown order types (uses DefaultOrderStrategy)
15
+ * - Null/undefined input (throws AppError)
16
+ * - Invalid type values (falls back to default strategy)
17
+ *
18
+ * EDGE CASES NOT HANDLED:
19
+ * - Concurrent order processing (not thread-safe by default)
20
+ * - Distributed processing across multiple services
21
+ * - Complex validation beyond type checking
22
+ * - Order persistence or database operations
23
+ *
24
+ * @module order-solid-refactor
25
+ */
26
+
27
+ import { z } from 'zod';
28
+
29
+ // ============================================
30
+ // Error Types
31
+ // ============================================
32
+
33
+ /**
34
+ * Custom error class for order processing failures.
35
+ * Extends the built-in Error class with additional context.
36
+ */
37
+ export class OrderProcessingError extends Error {
38
+ /** Error code for programmatic handling */
39
+ readonly code: string;
40
+ /** Additional context about the error */
41
+ readonly context?: Record<string, unknown>;
42
+
43
+ /**
44
+ * Creates a new OrderProcessingError instance.
45
+ *
46
+ * @param message - Human-readable error message
47
+ * @param code - Error code for programmatic handling
48
+ * @param context - Optional additional context
49
+ */
50
+ constructor(
51
+ message: string,
52
+ code: string,
53
+ context?: Record<string, unknown>
54
+ ) {
55
+ super(message);
56
+ this.name = 'OrderProcessingError';
57
+ this.code = code;
58
+ this.context = context;
59
+
60
+ // Maintains proper stack trace (V8 engines)
61
+ if (Error.captureStackTrace) {
62
+ Error.captureStackTrace(this, OrderProcessingError);
63
+ }
64
+ }
65
+ }
66
+
67
+ // ============================================
68
+ // Domain Models & Validation
69
+ // ============================================
70
+
71
+ /**
72
+ * Order status enumeration for type safety.
73
+ */
74
+ export enum OrderStatus {
75
+ DELIVERED = 'delivered',
76
+ SHIPPED = 'shipped',
77
+ ACTIVE = 'active',
78
+ SCHEDULED = 'scheduled',
79
+ PENDING = 'pending',
80
+ }
81
+
82
+ /**
83
+ * Minimum valid order interface.
84
+ * All order types must have at least these properties.
85
+ */
86
+ export interface BaseOrder {
87
+ type: string;
88
+ id: string;
89
+ [key: string]: unknown;
90
+ }
91
+
92
+ /**
93
+ * Processed order with computed status.
94
+ * @template T - Additional order properties
95
+ */
96
+ export interface ProcessedOrder extends BaseOrder {
97
+ status: OrderStatus;
98
+ [key: string]: unknown;
99
+ }
100
+
101
+ /**
102
+ * Zod schema for validating order objects.
103
+ * Ensures order has required 'type' and 'id' properties.
104
+ */
105
+ const orderSchema = z.object({
106
+ type: z.string().min(1, 'Order type must be a non-empty string'),
107
+ id: z.string().min(1, 'Order ID must be a non-empty string'),
108
+ }).passthrough(); // Allow additional properties
109
+
110
+ /**
111
+ * Validates and normalizes an order object.
112
+ *
113
+ * @param order - The order to validate
114
+ * @returns The validated order object
115
+ * @throws OrderProcessingError if validation fails
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * validateOrder({ type: 'digital', id: '123' }); // Success
120
+ * validateOrder({ type: '', id: '123' }); // Throws OrderProcessingError
121
+ * ```
122
+ */
123
+ export function validateOrder(order: unknown): BaseOrder {
124
+ const result = orderSchema.safeParse(order);
125
+
126
+ if (!result.success) {
127
+ throw new OrderProcessingError(
128
+ 'Invalid order data',
129
+ 'INVALID_ORDER',
130
+ { errors: result.error.errors, input: order }
131
+ );
132
+ }
133
+
134
+ return result.data;
135
+ }
136
+
137
+ // ============================================
138
+ // Strategy Pattern (Single Responsibility, Open/Closed)
139
+ // ============================================
140
+
141
+ /**
142
+ * Strategy interface for processing different order types.
143
+ * Each strategy handles one specific order type (Single Responsibility Principle).
144
+ *
145
+ * Implements Strategy Pattern to satisfy:
146
+ * - Open/Closed Principle: New strategies can be added without modifying existing code
147
+ * - Single Responsibility Principle: Each class has one reason to change
148
+ */
149
+ export interface OrderProcessingStrategy {
150
+ /**
151
+ * Processes an order and returns the result with updated status.
152
+ *
153
+ * @param order - The order to process (not mutated)
154
+ * @returns Processed order with status
155
+ * @throws OrderProcessingError if processing fails
156
+ */
157
+ process(order: BaseOrder): ProcessedOrder;
158
+ }
159
+
160
+ /**
161
+ * Processes digital orders (e.g., software licenses, downloads).
162
+ *
163
+ * ASSUMPTIONS:
164
+ * - Order type is 'digital'
165
+ * - Order has valid delivery email or download method
166
+ */
167
+ export class DigitalOrderStrategy implements OrderProcessingStrategy {
168
+ /**
169
+ * Processes a digital order by setting status to delivered.
170
+ *
171
+ * @param order - The digital order to process
172
+ * @returns Order with status set to 'delivered'
173
+ */
174
+ process(order: BaseOrder): ProcessedOrder {
175
+ // Assume: order.type === 'digital' (enforced by factory)
176
+ // Condition: Digital orders are immediately delivered
177
+ // Edge case: Invalid order data already validated before reaching here
178
+
179
+ return {
180
+ ...order,
181
+ status: OrderStatus.DELIVERED,
182
+ processedAt: new Date().toISOString(),
183
+ };
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Processes physical orders (e.g., products requiring shipping).
189
+ *
190
+ * ASSUMPTIONS:
191
+ * - Order type is 'physical'
192
+ * - Order contains shipping information
193
+ */
194
+ export class PhysicalOrderStrategy implements OrderProcessingStrategy {
195
+ /**
196
+ * Processes a physical order by setting status to shipped.
197
+ *
198
+ * @param order - The physical order to process
199
+ * @returns Order with status set to 'shipped'
200
+ */
201
+ process(order: BaseOrder): ProcessedOrder {
202
+ // Assume: order.type === 'physical' (enforced by factory)
203
+ // Condition: Physical orders require shipping label generation
204
+ // Edge case: Missing shipping address would need additional validation
205
+
206
+ return {
207
+ ...order,
208
+ status: OrderStatus.SHIPPED,
209
+ processedAt: new Date().toISOString(),
210
+ };
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Processes subscription orders (e.g., recurring services).
216
+ *
217
+ * ASSUMPTIONS:
218
+ * - Order type is 'subscription'
219
+ * - Order contains billing information
220
+ */
221
+ export class SubscriptionOrderStrategy implements OrderProcessingStrategy {
222
+ /**
223
+ * Processes a subscription order by setting status to active.
224
+ *
225
+ * @param order - The subscription order to process
226
+ * @returns Order with status set to 'active'
227
+ */
228
+ process(order: BaseOrder): ProcessedOrder {
229
+ // Assume: order.type === 'subscription' (enforced by factory)
230
+ // Condition: Subscriptions activate immediately
231
+ // Edge case: Payment processing would occur before activation
232
+
233
+ return {
234
+ ...order,
235
+ status: OrderStatus.ACTIVE,
236
+ processedAt: new Date().toISOString(),
237
+ };
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Processes gift card orders.
243
+ *
244
+ * ASSUMPTIONS:
245
+ * - Order type is 'gift_card'
246
+ * - Order contains recipient information
247
+ */
248
+ export class GiftCardOrderStrategy implements OrderProcessingStrategy {
249
+ /**
250
+ * Processes a gift card order by setting status to delivered.
251
+ *
252
+ * @param order - The gift card order to process
253
+ * @returns Order with status set to 'delivered' (card generated)
254
+ */
255
+ process(order: BaseOrder): ProcessedOrder {
256
+ // Assume: order.type === 'gift_card' (enforced by factory)
257
+ // Condition: Gift cards are immediately available
258
+
259
+ return {
260
+ ...order,
261
+ status: OrderStatus.DELIVERED,
262
+ processedAt: new Date().toISOString(),
263
+ };
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Default strategy for unknown order types.
269
+ * Provides graceful fallback behavior instead of failing.
270
+ *
271
+ * ASSUMPTIONS:
272
+ * - Order type is unknown or unregistered
273
+ * - System should still process the order with a safe default
274
+ */
275
+ export class DefaultOrderStrategy implements OrderProcessingStrategy {
276
+ /**
277
+ * Processes an unknown order type by setting status to pending.
278
+ * This allows the order to be reviewed manually later.
279
+ *
280
+ * @param order - The unknown order type to process
281
+ * @returns Order with status set to 'pending'
282
+ */
283
+ process(order: BaseOrder): ProcessedOrder {
284
+ // Assume: order.type is not recognized
285
+ // Condition: Unknown types default to pending for manual review
286
+ // Edge case: Order might be completely invalid (already validated)
287
+
288
+ return {
289
+ ...order,
290
+ status: OrderStatus.PENDING,
291
+ processedAt: new Date().toISOString(),
292
+ requiresManualReview: true,
293
+ };
294
+ }
295
+ }
296
+
297
+ // ============================================
298
+ // Factory Pattern (Dependency Inversion)
299
+ // ============================================
300
+
301
+ /**
302
+ * Factory for creating and managing order processing strategies.
303
+ * Implements Dependency Inversion Principle by depending on abstractions (interface).
304
+ *
305
+ * BENEFITS:
306
+ * - Central strategy registration
307
+ * - Easy to extend with new strategies
308
+ * - Testable via dependency injection
309
+ * - Type-safe strategy lookup
310
+ */
311
+ export class OrderProcessingStrategyFactory {
312
+ private strategies: Map<string, OrderProcessingStrategy> = new Map();
313
+
314
+ /**
315
+ * Creates a new factory with default strategies registered.
316
+ */
317
+ constructor() {
318
+ this.registerDefaultStrategies();
319
+ }
320
+
321
+ /**
322
+ * Registers all default order processing strategies.
323
+ * This is a template method that can be overridden or extended.
324
+ */
325
+ private registerDefaultStrategies(): void {
326
+ this.registerStrategy('digital', new DigitalOrderStrategy());
327
+ this.registerStrategy('physical', new PhysicalOrderStrategy());
328
+ this.registerStrategy('subscription', new SubscriptionOrderStrategy());
329
+ this.registerStrategy('gift_card', new GiftCardOrderStrategy());
330
+ }
331
+
332
+ /**
333
+ * Registers a new strategy for a specific order type.
334
+ *
335
+ * @param type - The order type this strategy handles
336
+ * @param strategy - The strategy instance to register
337
+ * @throws TypeError if type is empty or strategy is invalid
338
+ *
339
+ * @example
340
+ * ```typescript
341
+ * factory.registerStrategy('service', new ServiceOrderStrategy());
342
+ * ```
343
+ */
344
+ registerStrategy(type: string, strategy: OrderProcessingStrategy): void {
345
+ if (!type || typeof type !== 'string') {
346
+ throw new TypeError('Order type must be a non-empty string');
347
+ }
348
+
349
+ if (!this.isValidStrategy(strategy)) {
350
+ throw new TypeError('Strategy must implement OrderProcessingStrategy interface');
351
+ }
352
+
353
+ this.strategies.set(type, strategy);
354
+ }
355
+
356
+ /**
357
+ * Retrieves a strategy for the given order type.
358
+ * Returns DefaultOrderStrategy if type is not registered.
359
+ *
360
+ * @param type - The order type to get a strategy for
361
+ * @returns The appropriate strategy instance
362
+ */
363
+ getStrategy(type: string): OrderProcessingStrategy {
364
+ // Assume: type is a non-empty string
365
+ // Condition: Returns strategy if found, else returns default
366
+ // Edge case: type might be undefined - handled gracefully
367
+
368
+ if (!type || typeof type !== 'string') {
369
+ return new DefaultOrderStrategy();
370
+ }
371
+
372
+ return this.strategies.get(type) || new DefaultOrderStrategy();
373
+ }
374
+
375
+ /**
376
+ * Checks if a strategy is registered for the given order type.
377
+ *
378
+ * @param type - The order type to check
379
+ * @returns true if a strategy is registered, false otherwise
380
+ */
381
+ hasStrategy(type: string): boolean {
382
+ return this.strategies.has(type);
383
+ }
384
+
385
+ /**
386
+ * Returns all registered order type keys.
387
+ *
388
+ * @returns Array of registered order types
389
+ */
390
+ getRegisteredTypes(): string[] {
391
+ return Array.from(this.strategies.keys());
392
+ }
393
+
394
+ /**
395
+ * Validates that a strategy implements the required interface.
396
+ * Uses duck typing to check for the 'process' method.
397
+ *
398
+ * @param strategy - The strategy to validate
399
+ * @returns true if valid, false otherwise
400
+ */
401
+ private isValidStrategy(strategy: unknown): boolean {
402
+ return (
403
+ strategy !== null &&
404
+ typeof strategy === 'object' &&
405
+ 'process' in strategy &&
406
+ typeof (strategy as OrderProcessingStrategy).process === 'function'
407
+ );
408
+ }
409
+ }
410
+
411
+ // ============================================
412
+ // Context / Processor (Liskov Substitution, Interface Segregation)
413
+ // ============================================
414
+
415
+ /**
416
+ * Primary context class for processing orders.
417
+ * Implements Liskov Substitution Principle by working with
418
+ * OrderProcessingStrategy interface abstractions.
419
+ *
420
+ * Implements Interface Segregation Principle by having focused, cohesive methods.
421
+ */
422
+ export class OrderProcessor {
423
+ private factory: OrderProcessingStrategyFactory;
424
+ private processingLog: Array<{ orderId: string; type: string; status: OrderStatus; timestamp: string }> = [];
425
+
426
+ /**
427
+ * Creates a new OrderProcessor instance.
428
+ *
429
+ * @param factory - Optional custom strategy factory (dependency injection)
430
+ * @throws TypeError if factory is invalid
431
+ */
432
+ constructor(factory?: OrderProcessingStrategyFactory) {
433
+ // Assume: factory is a valid OrderProcessingStrategyFactory instance
434
+ // Condition: Uses provided factory or creates default
435
+ // Edge case: Invalid factory - handled by type checking
436
+
437
+ if (factory && !(factory instanceof OrderProcessingStrategyFactory)) {
438
+ throw new TypeError('Factory must be an instance of OrderProcessingStrategyFactory');
439
+ }
440
+
441
+ this.factory = factory || new OrderProcessingStrategyFactory();
442
+ }
443
+
444
+ /**
445
+ * Processes an order using the appropriate strategy.
446
+ *
447
+ * This method follows the Template Method pattern:
448
+ * 1. Validates the order
449
+ * 2. Selects the appropriate strategy
450
+ * 3. Processes the order
451
+ * 4. Logs the processing result
452
+ * 5. Returns the processed order
453
+ *
454
+ * @param order - The order to process
455
+ * @returns The processed order with updated status
456
+ * @throws OrderProcessingError if order is invalid
457
+ * @throws TypeError if order is null/undefined
458
+ *
459
+ * @example
460
+ * ```typescript
461
+ * const processor = new OrderProcessor();
462
+ * const result = processor.processOrder({ type: 'digital', id: '123' });
463
+ * console.log(result.status); // 'delivered'
464
+ * ```
465
+ */
466
+ processOrder(order: unknown): ProcessedOrder {
467
+ // Assume: order is a valid object with 'type' and 'id'
468
+ // Condition: Processes order using appropriate strategy
469
+ // Edge case: order is undefined/null - handled defensively
470
+
471
+ // Step 1: Input validation
472
+ if (order === null || order === undefined) {
473
+ throw new TypeError('Order cannot be null or undefined');
474
+ }
475
+
476
+ const validatedOrder = validateOrder(order);
477
+
478
+ // Step 2: Select strategy
479
+ const strategy = this.factory.getStrategy(validatedOrder.type);
480
+
481
+ // Step 3: Process order
482
+ const processedOrder = strategy.process(validatedOrder);
483
+
484
+ // Step 4: Log processing (optional, can be removed for pure functional approach)
485
+ this.logProcessing(validatedOrder.id, validatedOrder.type, processedOrder.status);
486
+
487
+ // Step 5: Return result
488
+ return processedOrder;
489
+ }
490
+
491
+ /**
492
+ * Processes multiple orders in batch.
493
+ * Returns results for all orders, even if some fail.
494
+ *
495
+ * @param orders - Array of orders to process
496
+ * @returns Array of processing results with success/failure status
497
+ *
498
+ * @example
499
+ * ```typescript
500
+ * const results = processor.processBatch([
501
+ * { type: 'digital', id: '1' },
502
+ * { type: 'physical', id: '2' },
503
+ * ]);
504
+ * ```
505
+ */
506
+ processBatch(orders: unknown[]): Array<{ success: boolean; order: ProcessedOrder | null; error?: OrderProcessingError }> {
507
+ // Assume: orders is an array of objects
508
+ // Condition: Processes all orders, collecting results
509
+ // Edge case: orders array is empty - returns empty array
510
+
511
+ const results: Array<{ success: boolean; order: ProcessedOrder | null; error?: OrderProcessingError }> = [];
512
+
513
+ for (const order of orders) {
514
+ try {
515
+ const processedOrder = this.processOrder(order);
516
+ results.push({ success: true, order: processedOrder });
517
+ } catch (error) {
518
+ results.push({
519
+ success: false,
520
+ order: null,
521
+ error: error instanceof OrderProcessingError ? error : new OrderProcessingError(
522
+ 'Unknown error during order processing',
523
+ 'UNKNOWN_ERROR',
524
+ { originalError: error }
525
+ ),
526
+ });
527
+ }
528
+ }
529
+
530
+ return results;
531
+ }
532
+
533
+ /**
534
+ * Returns the internal strategy factory.
535
+ * Useful for testing or advanced configuration.
536
+ *
537
+ * @returns The strategy factory instance
538
+ */
539
+ getFactory(): OrderProcessingStrategyFactory {
540
+ return this.factory;
541
+ }
542
+
543
+ /**
544
+ * Returns the processing log.
545
+ *
546
+ * @returns Array of processing log entries
547
+ */
548
+ getProcessingLog(): Array<{ orderId: string; type: string; status: OrderStatus; timestamp: string }> {
549
+ return this.processingLog;
550
+ }
551
+
552
+ /**
553
+ * Clears the processing log.
554
+ * Useful for testing or resetting state.
555
+ */
556
+ clearProcessingLog(): void {
557
+ this.processingLog = [];
558
+ }
559
+
560
+ /**
561
+ * Logs order processing internally.
562
+ * This can be replaced with a proper logging system.
563
+ *
564
+ * @param orderId - The order ID
565
+ * @param type - The order type
566
+ * @param status - The resulting status
567
+ */
568
+ private logProcessing(orderId: string, type: string, status: OrderStatus): void {
569
+ this.processingLog.push({
570
+ orderId,
571
+ type,
572
+ status,
573
+ timestamp: new Date().toISOString(),
574
+ });
575
+ }
576
+ }
577
+
578
+ // ============================================
579
+ // Usage Examples
580
+ // ============================================
581
+
582
+ /**
583
+ * Example 1: Basic usage with default strategies.
584
+ */
585
+ function example1_BasicUsage(): void {
586
+ console.log('=== Example 1: Basic Usage ===');
587
+
588
+ const processor = new OrderProcessor();
589
+
590
+ // Process different order types
591
+ const digitalOrder = processor.processOrder({
592
+ type: 'digital',
593
+ id: 'DIG-001',
594
+ items: ['software license'],
595
+ });
596
+
597
+ const physicalOrder = processor.processOrder({
598
+ type: 'physical',
599
+ id: 'PHY-001',
600
+ items: ['laptop'],
601
+ shippingAddress: '123 Main St',
602
+ });
603
+
604
+ const subscriptionOrder = processor.processOrder({
605
+ type: 'subscription',
606
+ id: 'SUB-001',
607
+ items: ['monthly service'],
608
+ billingCycle: 'monthly',
609
+ });
610
+
611
+ console.log('Digital Order:', digitalOrder); // { type: 'digital', status: 'delivered', ... }
612
+ console.log('Physical Order:', physicalOrder); // { type: 'physical', status: 'shipped', ... }
613
+ console.log('Subscription Order:', subscriptionOrder); // { type: 'subscription', status: 'active', ... }
614
+ console.log('');
615
+ }
616
+
617
+ /**
618
+ * Example 2: Handling unknown order types.
619
+ */
620
+ function example2_UnknownTypes(): void {
621
+ console.log('=== Example 2: Unknown Order Types ===');
622
+
623
+ const processor = new OrderProcessor();
624
+
625
+ // Unknown order type (falls back to DefaultOrderStrategy)
626
+ const unknownOrder = processor.processOrder({
627
+ type: 'unknown',
628
+ id: 'UNK-001',
629
+ items: ['something'],
630
+ });
631
+
632
+ console.log('Unknown Order:', unknownOrder); // { type: 'unknown', status: 'pending', requiresManualReview: true, ... }
633
+ console.log('');
634
+ }
635
+
636
+ /**
637
+ * Example 3: Extending with custom strategies (Open/Closed Principle).
638
+ */
639
+ function example3_CustomStrategies(): void {
640
+ console.log('=== Example 3: Custom Strategies (OCP) ===');
641
+
642
+ // Define a new strategy for service orders
643
+ class ServiceOrderStrategy implements OrderProcessingStrategy {
644
+ process(order: BaseOrder): ProcessedOrder {
645
+ return { ...order, status: OrderStatus.SCHEDULED, processedAt: new Date().toISOString() };
646
+ }
647
+ }
648
+
649
+ // Create custom factory with additional strategy
650
+ const customFactory = new OrderProcessingStrategyFactory();
651
+ customFactory.registerStrategy('service', new ServiceOrderStrategy());
652
+
653
+ // Create processor with custom factory
654
+ const processor = new OrderProcessor(customFactory);
655
+
656
+ // Now we can process service orders
657
+ const serviceOrder = processor.processOrder({
658
+ type: 'service',
659
+ id: 'SRV-001',
660
+ items: ['consultation'],
661
+ });
662
+
663
+ console.log('Service Order:', serviceOrder); // { type: 'service', status: 'scheduled', ... }
664
+ console.log('');
665
+ }
666
+
667
+ /**
668
+ * Example 4: Batch processing with error handling.
669
+ */
670
+ function example4_BatchProcessing(): void {
671
+ console.log('=== Example 4: Batch Processing ===');
672
+
673
+ const processor = new OrderProcessor();
674
+
675
+ const results = processor.processBatch([
676
+ { type: 'digital', id: '1' },
677
+ { type: 'physical', id: '2' },
678
+ { type: 'invalid', id: '' }, // Will fail validation
679
+ null, // Will fail type check
680
+ ]);
681
+
682
+ console.log('Batch Results:', results);
683
+ console.log('Successful:', results.filter(r => r.success).length);
684
+ console.log('Failed:', results.filter(r => !r.success).length);
685
+ console.log('');
686
+ }
687
+
688
+ /**
689
+ * Example 5: Error handling edge cases.
690
+ */
691
+ function example5_ErrorHandling(): void {
692
+ console.log('=== Example 5: Error Handling ===');
693
+
694
+ const processor = new OrderProcessor();
695
+
696
+ // Try invalid order (missing 'type')
697
+ try {
698
+ processor.processOrder({ id: '123' });
699
+ } catch (error) {
700
+ console.log('Caught error for missing type:', error instanceof OrderProcessingError ? error.message : error);
701
+ }
702
+
703
+ // Try null order
704
+ try {
705
+ processor.processOrder(null);
706
+ } catch (error) {
707
+ console.log('Caught error for null order:', error instanceof TypeError ? error.message : error);
708
+ }
709
+
710
+ console.log('');
711
+ }
712
+
713
+ /**
714
+ * Example 6: Processing log inspection.
715
+ */
716
+ function example6_ProcessingLog(): void {
717
+ console.log('=== Example 6: Processing Log ===');
718
+
719
+ const processor = new OrderProcessor();
720
+
721
+ // Process some orders
722
+ processor.processOrder({ type: 'digital', id: '1' });
723
+ processor.processOrder({ type: 'physical', id: '2' });
724
+ processor.processOrder({ type: 'subscription', id: '3' });
725
+
726
+ // Inspect the log
727
+ const log = processor.getProcessingLog();
728
+ console.log('Processing Log:', log);
729
+ console.log('');
730
+ }
731
+
732
+ /**
733
+ * Run all examples.
734
+ */
735
+ export function runExamples(): void {
736
+ example1_BasicUsage();
737
+ example2_UnknownTypes();
738
+ example3_CustomStrategies();
739
+ example4_BatchProcessing();
740
+ example5_ErrorHandling();
741
+ example6_ProcessingLog();
742
+ }
743
+
744
+ // ============================================
745
+ // Comparison with Original Code
746
+ // ============================================
747
+
748
+ /**
749
+ * ORIGINAL CODE (SOLID Violations):
750
+ *
751
+ * function processOrder(order: any) {
752
+ * if (order.type === 'digital') {
753
+ * console.log('Sending email with download link');
754
+ * order.status = 'delivered';
755
+ * } else if (order.type === 'physical') {
756
+ * console.log('Creating shipping label');
757
+ * order.status = 'shipped';
758
+ * } else if (order.type === 'subscription') {
759
+ * console.log('Activating subscription');
760
+ * order.status = 'active';
761
+ * }
762
+ * console.log('Order processed: ' + order.id);
763
+ * return order;
764
+ * }
765
+ *
766
+ * SOLID VIOLATIONS:
767
+ * - Single Responsibility: Handles multiple order types in one function
768
+ * - Open/Closed: Must modify function to add new order types
769
+ * - Liskov Substitution: No abstraction, relies on concrete conditionals
770
+ * - Interface Segregation: Monolithic function with mixed concerns
771
+ * - Dependency Inversion: Directly depends on concrete order structure
772
+ *
773
+ * OTHER ISSUES:
774
+ * - Uses 'any' type (no type safety)
775
+ * - Mutates input object (not idempotent)
776
+ * - No validation of input
777
+ * - No error handling
778
+ * - Console.log for side effects (not testable)
779
+ * - Cannot handle unknown order types
780
+ * - Hard to test individual behaviors
781
+ */
782
+
783
+ /**
784
+ * REFACTORED CODE (SOLID Compliant):
785
+ *
786
+ * SOLID PRINCIPLES SATISFIED:
787
+ *
788
+ * Single Responsibility Principle (SRP):
789
+ * - Each strategy handles one type of order
790
+ * - OrderProcessor coordinates but doesn't implement logic
791
+ * - OrderProcessingStrategyFactory manages registration
792
+ *
793
+ * Open/Closed Principle (OCP):
794
+ * - New order types added via new strategies, no code modification
795
+ * - Factory allows runtime registration of strategies
796
+ * - Interface-based design allows easy extension
797
+ *
798
+ * Liskov Substitution Principle (LSP):
799
+ * - All strategies implement the same interface
800
+ * - Strategies are interchangeable without breaking code
801
+ * - DefaultOrderStrategy provides safe fallback behavior
802
+ *
803
+ * Interface Segregation Principle (ISP):
804
+ * - Each interface has focused methods
805
+ * - OrderProcessingStrategy has single method
806
+ * - OrderProcessor has cohesive, related methods
807
+ *
808
+ * Dependency Inversion Principle (DIP):
809
+ * - OrderProcessor depends on OrderProcessingStrategy interface
810
+ * - Concrete strategies injected via factory
811
+ * - Dependencies can be swapped at runtime
812
+ *
813
+ * ADDITIONAL IMPROVEMENTS:
814
+ * - Runtime validation with Zod schemas
815
+ * - Custom error types with context
816
+ * - Immutable operations (returns new objects)
817
+ * - Comprehensive JSDoc documentation
818
+ * - Type safety throughout
819
+ * - Testable design (dependency injection)
820
+ * - Handles unknown order types gracefully
821
+ * - Processing log for debugging
822
+ * - Batch processing support
823
+ * - Clear assumptions and edge case documentation
824
+ */
825
+
826
+ // Export types for external use
827
+ export type {
828
+ BaseOrder,
829
+ ProcessedOrder,
830
+ };