agileflow 2.90.7 → 2.92.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.
- package/CHANGELOG.md +10 -0
- package/README.md +6 -6
- package/lib/README.md +178 -0
- package/lib/codebase-indexer.js +818 -0
- package/lib/colors.js +190 -12
- package/lib/consent.js +232 -0
- package/lib/correlation.js +277 -0
- package/lib/error-codes.js +46 -0
- package/lib/errors.js +48 -6
- package/lib/file-cache.js +182 -0
- package/lib/format-error.js +156 -0
- package/lib/path-resolver.js +155 -7
- package/lib/paths.js +212 -20
- package/lib/placeholder-registry.js +205 -0
- package/lib/registry-di.js +358 -0
- package/lib/result-schema.js +363 -0
- package/lib/result.js +210 -0
- package/lib/session-registry.js +13 -0
- package/lib/session-state-machine.js +465 -0
- package/lib/validate-commands.js +308 -0
- package/lib/validate-names.js +3 -3
- package/lib/validate.js +116 -52
- package/package.json +4 -1
- package/scripts/af +34 -0
- package/scripts/agent-loop.js +63 -9
- package/scripts/agileflow-configure.js +2 -2
- package/scripts/agileflow-welcome.js +435 -23
- package/scripts/archive-completed-stories.sh +57 -11
- package/scripts/claude-tmux.sh +102 -0
- package/scripts/damage-control-bash.js +3 -70
- package/scripts/damage-control-edit.js +3 -20
- package/scripts/damage-control-write.js +3 -20
- package/scripts/dependency-check.js +310 -0
- package/scripts/get-env.js +11 -4
- package/scripts/lib/configure-detect.js +23 -1
- package/scripts/lib/configure-features.js +43 -2
- package/scripts/lib/context-formatter.js +771 -0
- package/scripts/lib/context-loader.js +699 -0
- package/scripts/lib/damage-control-utils.js +107 -0
- package/scripts/lib/json-utils.sh +162 -0
- package/scripts/lib/state-migrator.js +353 -0
- package/scripts/lib/story-state-machine.js +437 -0
- package/scripts/obtain-context.js +118 -1048
- package/scripts/pre-push-check.sh +46 -0
- package/scripts/precompact-context.sh +36 -11
- package/scripts/query-codebase.js +538 -0
- package/scripts/ralph-loop.js +5 -5
- package/scripts/session-manager.js +220 -42
- package/scripts/spawn-parallel.js +651 -0
- package/scripts/tui/blessed/data/watcher.js +180 -0
- package/scripts/tui/blessed/index.js +244 -0
- package/scripts/tui/blessed/panels/output.js +101 -0
- package/scripts/tui/blessed/panels/sessions.js +150 -0
- package/scripts/tui/blessed/panels/trace.js +97 -0
- package/scripts/tui/blessed/ui/help.js +77 -0
- package/scripts/tui/blessed/ui/screen.js +52 -0
- package/scripts/tui/blessed/ui/statusbar.js +47 -0
- package/scripts/tui/blessed/ui/tabbar.js +99 -0
- package/scripts/tui/index.js +38 -30
- package/scripts/validators/README.md +143 -0
- package/scripts/validators/component-validator.js +239 -0
- package/scripts/validators/json-schema-validator.js +186 -0
- package/scripts/validators/markdown-validator.js +152 -0
- package/scripts/validators/migration-validator.js +129 -0
- package/scripts/validators/security-validator.js +380 -0
- package/scripts/validators/story-format-validator.js +197 -0
- package/scripts/validators/test-result-validator.js +114 -0
- package/scripts/validators/workflow-validator.js +247 -0
- package/src/core/agents/accessibility.md +6 -0
- package/src/core/agents/adr-writer.md +6 -0
- package/src/core/agents/analytics.md +6 -0
- package/src/core/agents/api.md +6 -0
- package/src/core/agents/ci.md +6 -0
- package/src/core/agents/codebase-query.md +261 -0
- package/src/core/agents/compliance.md +6 -0
- package/src/core/agents/configuration-damage-control.md +6 -0
- package/src/core/agents/configuration-visual-e2e.md +6 -0
- package/src/core/agents/database.md +10 -0
- package/src/core/agents/datamigration.md +6 -0
- package/src/core/agents/design.md +6 -0
- package/src/core/agents/devops.md +6 -0
- package/src/core/agents/documentation.md +6 -0
- package/src/core/agents/epic-planner.md +6 -0
- package/src/core/agents/integrations.md +6 -0
- package/src/core/agents/mentor.md +6 -0
- package/src/core/agents/mobile.md +6 -0
- package/src/core/agents/monitoring.md +6 -0
- package/src/core/agents/multi-expert.md +6 -0
- package/src/core/agents/performance.md +6 -0
- package/src/core/agents/product.md +6 -0
- package/src/core/agents/qa.md +6 -0
- package/src/core/agents/readme-updater.md +6 -0
- package/src/core/agents/refactor.md +6 -0
- package/src/core/agents/research.md +6 -0
- package/src/core/agents/security.md +6 -0
- package/src/core/agents/testing.md +10 -0
- package/src/core/agents/ui.md +6 -0
- package/src/core/commands/adr.md +114 -0
- package/src/core/commands/agent.md +120 -0
- package/src/core/commands/assign.md +145 -0
- package/src/core/commands/audit.md +401 -0
- package/src/core/commands/babysit.md +32 -5
- package/src/core/commands/board.md +1 -0
- package/src/core/commands/changelog.md +118 -0
- package/src/core/commands/configure.md +42 -6
- package/src/core/commands/diagnose.md +114 -0
- package/src/core/commands/epic.md +205 -1
- package/src/core/commands/handoff.md +128 -0
- package/src/core/commands/help.md +76 -0
- package/src/core/commands/metrics.md +1 -0
- package/src/core/commands/pr.md +96 -0
- package/src/core/commands/research/analyze.md +1 -0
- package/src/core/commands/research/ask.md +2 -0
- package/src/core/commands/research/import.md +1 -0
- package/src/core/commands/research/list.md +2 -0
- package/src/core/commands/research/synthesize.md +584 -0
- package/src/core/commands/research/view.md +2 -0
- package/src/core/commands/roadmap/analyze.md +400 -0
- package/src/core/commands/session/new.md +113 -6
- package/src/core/commands/session/spawn.md +197 -0
- package/src/core/commands/sprint.md +22 -0
- package/src/core/commands/status.md +200 -1
- package/src/core/commands/story/list.md +9 -9
- package/src/core/commands/story/view.md +1 -0
- package/src/core/commands/story.md +143 -4
- package/src/core/experts/codebase-query/expertise.yaml +190 -0
- package/src/core/experts/codebase-query/question.md +73 -0
- package/src/core/experts/codebase-query/self-improve.md +105 -0
- package/src/core/templates/agileflow-metadata.json +55 -2
- package/src/core/templates/plan-template.md +125 -0
- package/src/core/templates/story-lifecycle.md +213 -0
- package/src/core/templates/story-template.md +4 -0
- package/src/core/templates/tdd-test-template.js +241 -0
- package/tools/cli/commands/setup.js +86 -0
- package/tools/cli/installers/core/installer.js +94 -0
- package/tools/cli/installers/ide/_base-ide.js +20 -11
- package/tools/cli/installers/ide/codex.js +29 -47
- package/tools/cli/lib/config-manager.js +17 -2
- package/tools/cli/lib/content-transformer.js +271 -0
- package/tools/cli/lib/error-handler.js +14 -22
- package/tools/cli/lib/ide-error-factory.js +421 -0
- package/tools/cli/lib/ide-health-monitor.js +364 -0
- package/tools/cli/lib/ide-registry.js +114 -1
- package/tools/cli/lib/ui.js +14 -25
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgileFlow CLI - IDE Error Factory
|
|
3
|
+
*
|
|
4
|
+
* Abstract Factory pattern for IDE-specific error handling strategies.
|
|
5
|
+
* Centralizes recovery patterns across all IDE installers.
|
|
6
|
+
*
|
|
7
|
+
* Strategy Types:
|
|
8
|
+
* - RetryStrategy: Retry with exponential backoff
|
|
9
|
+
* - FallbackStrategy: Try alternative approach
|
|
10
|
+
* - AbortStrategy: Fail with helpful message
|
|
11
|
+
* - SilentStrategy: Swallow error and continue
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Base error strategy interface
|
|
16
|
+
*/
|
|
17
|
+
class ErrorStrategy {
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} name - Strategy name
|
|
20
|
+
* @param {Object} options - Strategy options
|
|
21
|
+
*/
|
|
22
|
+
constructor(name, options = {}) {
|
|
23
|
+
this.name = name;
|
|
24
|
+
this.options = options;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Handle an error
|
|
29
|
+
* @param {Error} error - The error to handle
|
|
30
|
+
* @param {Object} context - Execution context
|
|
31
|
+
* @returns {Promise<{ recovered: boolean, result: any, error: Error | null }>}
|
|
32
|
+
*/
|
|
33
|
+
async handle(error, context = {}) {
|
|
34
|
+
throw new Error('Strategy.handle() must be implemented');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Retry strategy with exponential backoff
|
|
40
|
+
*/
|
|
41
|
+
class RetryStrategy extends ErrorStrategy {
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
super('retry', options);
|
|
44
|
+
this.maxRetries = options.maxRetries || 3;
|
|
45
|
+
this.baseDelayMs = options.baseDelayMs || 1000;
|
|
46
|
+
this.maxDelayMs = options.maxDelayMs || 30000;
|
|
47
|
+
this.backoffMultiplier = options.backoffMultiplier || 2;
|
|
48
|
+
this.retryCondition = options.retryCondition || (() => true);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async handle(error, context = {}) {
|
|
52
|
+
const { operation, attempt = 0 } = context;
|
|
53
|
+
|
|
54
|
+
// Check if we should retry
|
|
55
|
+
if (attempt >= this.maxRetries) {
|
|
56
|
+
return { recovered: false, result: null, error };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!this.retryCondition(error, context)) {
|
|
60
|
+
return { recovered: false, result: null, error };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Calculate delay with exponential backoff
|
|
64
|
+
const delay = Math.min(
|
|
65
|
+
this.baseDelayMs * Math.pow(this.backoffMultiplier, attempt),
|
|
66
|
+
this.maxDelayMs
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Wait before retry
|
|
70
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
71
|
+
|
|
72
|
+
// Retry the operation
|
|
73
|
+
if (typeof operation === 'function') {
|
|
74
|
+
try {
|
|
75
|
+
const result = await operation();
|
|
76
|
+
return { recovered: true, result, error: null };
|
|
77
|
+
} catch (retryError) {
|
|
78
|
+
// Recursively retry
|
|
79
|
+
return this.handle(retryError, { ...context, attempt: attempt + 1 });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { recovered: false, result: null, error };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Fallback strategy - try alternative approach
|
|
89
|
+
*/
|
|
90
|
+
class FallbackStrategy extends ErrorStrategy {
|
|
91
|
+
constructor(options = {}) {
|
|
92
|
+
super('fallback', options);
|
|
93
|
+
this.fallbackFn = options.fallbackFn;
|
|
94
|
+
this.fallbackCondition = options.fallbackCondition || (() => true);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async handle(error, context = {}) {
|
|
98
|
+
if (!this.fallbackCondition(error, context)) {
|
|
99
|
+
return { recovered: false, result: null, error };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (typeof this.fallbackFn !== 'function') {
|
|
103
|
+
return { recovered: false, result: null, error };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const result = await this.fallbackFn(error, context);
|
|
108
|
+
return { recovered: true, result, error: null };
|
|
109
|
+
} catch (fallbackError) {
|
|
110
|
+
return {
|
|
111
|
+
recovered: false,
|
|
112
|
+
result: null,
|
|
113
|
+
error: new AggregateError([error, fallbackError], 'Fallback also failed'),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Abort strategy - fail with helpful message
|
|
121
|
+
*/
|
|
122
|
+
class AbortStrategy extends ErrorStrategy {
|
|
123
|
+
constructor(options = {}) {
|
|
124
|
+
super('abort', options);
|
|
125
|
+
this.messageFormatter = options.messageFormatter || (e => e.message);
|
|
126
|
+
this.exitCode = options.exitCode || 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async handle(error, context = {}) {
|
|
130
|
+
const message = this.messageFormatter(error, context);
|
|
131
|
+
const enhancedError = new Error(message);
|
|
132
|
+
enhancedError.originalError = error;
|
|
133
|
+
enhancedError.exitCode = this.exitCode;
|
|
134
|
+
enhancedError.context = context;
|
|
135
|
+
|
|
136
|
+
return { recovered: false, result: null, error: enhancedError };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Silent strategy - swallow error and continue
|
|
142
|
+
*/
|
|
143
|
+
class SilentStrategy extends ErrorStrategy {
|
|
144
|
+
constructor(options = {}) {
|
|
145
|
+
super('silent', options);
|
|
146
|
+
this.defaultValue = options.defaultValue;
|
|
147
|
+
this.logFn = options.logFn;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async handle(error, context = {}) {
|
|
151
|
+
if (typeof this.logFn === 'function') {
|
|
152
|
+
this.logFn(error, context);
|
|
153
|
+
}
|
|
154
|
+
return { recovered: true, result: this.defaultValue, error: null };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Composite strategy - try multiple strategies in order
|
|
160
|
+
*/
|
|
161
|
+
class CompositeStrategy extends ErrorStrategy {
|
|
162
|
+
constructor(strategies = [], options = {}) {
|
|
163
|
+
super('composite', options);
|
|
164
|
+
this.strategies = strategies;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async handle(error, context = {}) {
|
|
168
|
+
for (const strategy of this.strategies) {
|
|
169
|
+
const result = await strategy.handle(error, context);
|
|
170
|
+
if (result.recovered) {
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { recovered: false, result: null, error };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* IDE-specific error categories
|
|
180
|
+
*/
|
|
181
|
+
const ErrorCategories = {
|
|
182
|
+
NETWORK: 'network',
|
|
183
|
+
FILESYSTEM: 'filesystem',
|
|
184
|
+
PERMISSION: 'permission',
|
|
185
|
+
VALIDATION: 'validation',
|
|
186
|
+
TIMEOUT: 'timeout',
|
|
187
|
+
UNKNOWN: 'unknown',
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Categorize an error
|
|
192
|
+
* @param {Error} error - Error to categorize
|
|
193
|
+
* @returns {string} Error category
|
|
194
|
+
*/
|
|
195
|
+
function categorizeError(error) {
|
|
196
|
+
const message = (error.message || '').toLowerCase();
|
|
197
|
+
const code = error.code || '';
|
|
198
|
+
|
|
199
|
+
// Check timeout first (before network, since ETIMEDOUT could be either)
|
|
200
|
+
if (code === 'ETIMEDOUT' || message.includes('timeout') || message.includes('timed out')) {
|
|
201
|
+
return ErrorCategories.TIMEOUT;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (
|
|
205
|
+
code === 'ENOTFOUND' ||
|
|
206
|
+
code === 'ECONNREFUSED' ||
|
|
207
|
+
message.includes('network') ||
|
|
208
|
+
message.includes('fetch')
|
|
209
|
+
) {
|
|
210
|
+
return ErrorCategories.NETWORK;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (
|
|
214
|
+
code === 'ENOENT' ||
|
|
215
|
+
code === 'EISDIR' ||
|
|
216
|
+
code === 'ENOTDIR' ||
|
|
217
|
+
message.includes('no such file') ||
|
|
218
|
+
message.includes('directory')
|
|
219
|
+
) {
|
|
220
|
+
return ErrorCategories.FILESYSTEM;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (code === 'EACCES' || code === 'EPERM' || message.includes('permission')) {
|
|
224
|
+
return ErrorCategories.PERMISSION;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (message.includes('valid') || message.includes('invalid') || message.includes('expected')) {
|
|
228
|
+
return ErrorCategories.VALIDATION;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return ErrorCategories.UNKNOWN;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* IDE Error Factory - produces IDE-specific error strategies
|
|
236
|
+
*/
|
|
237
|
+
class IdeErrorFactory {
|
|
238
|
+
constructor(ideName, options = {}) {
|
|
239
|
+
this.ideName = ideName;
|
|
240
|
+
this.options = options;
|
|
241
|
+
this.strategies = new Map();
|
|
242
|
+
|
|
243
|
+
// Register default strategies
|
|
244
|
+
this.registerDefaults();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Register default strategies for common error categories
|
|
249
|
+
*/
|
|
250
|
+
registerDefaults() {
|
|
251
|
+
// Network errors - retry with backoff
|
|
252
|
+
this.register(
|
|
253
|
+
ErrorCategories.NETWORK,
|
|
254
|
+
() =>
|
|
255
|
+
new CompositeStrategy([
|
|
256
|
+
new RetryStrategy({
|
|
257
|
+
maxRetries: 3,
|
|
258
|
+
baseDelayMs: 1000,
|
|
259
|
+
retryCondition: e => categorizeError(e) === ErrorCategories.NETWORK,
|
|
260
|
+
}),
|
|
261
|
+
new AbortStrategy({
|
|
262
|
+
messageFormatter: e =>
|
|
263
|
+
`Network error while installing ${this.ideName}: ${e.message}\n` +
|
|
264
|
+
'Please check your internet connection and try again.',
|
|
265
|
+
}),
|
|
266
|
+
])
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
// Filesystem errors - try with fallback
|
|
270
|
+
this.register(
|
|
271
|
+
ErrorCategories.FILESYSTEM,
|
|
272
|
+
() =>
|
|
273
|
+
new AbortStrategy({
|
|
274
|
+
messageFormatter: e =>
|
|
275
|
+
`File system error while installing ${this.ideName}: ${e.message}\n` +
|
|
276
|
+
'Please check that the path exists and is writable.',
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// Permission errors - abort with help
|
|
281
|
+
this.register(
|
|
282
|
+
ErrorCategories.PERMISSION,
|
|
283
|
+
() =>
|
|
284
|
+
new AbortStrategy({
|
|
285
|
+
messageFormatter: e =>
|
|
286
|
+
`Permission denied while installing ${this.ideName}: ${e.message}\n` +
|
|
287
|
+
'Please check file permissions or run with appropriate privileges.',
|
|
288
|
+
})
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// Validation errors - abort
|
|
292
|
+
this.register(
|
|
293
|
+
ErrorCategories.VALIDATION,
|
|
294
|
+
() =>
|
|
295
|
+
new AbortStrategy({
|
|
296
|
+
messageFormatter: e => `Validation error while installing ${this.ideName}: ${e.message}`,
|
|
297
|
+
})
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// Timeout errors - retry once
|
|
301
|
+
this.register(
|
|
302
|
+
ErrorCategories.TIMEOUT,
|
|
303
|
+
() =>
|
|
304
|
+
new CompositeStrategy([
|
|
305
|
+
new RetryStrategy({
|
|
306
|
+
maxRetries: 1,
|
|
307
|
+
baseDelayMs: 5000,
|
|
308
|
+
}),
|
|
309
|
+
new AbortStrategy({
|
|
310
|
+
messageFormatter: e =>
|
|
311
|
+
`Operation timed out while installing ${this.ideName}: ${e.message}\n` +
|
|
312
|
+
'Please try again later.',
|
|
313
|
+
}),
|
|
314
|
+
])
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
// Unknown errors - abort
|
|
318
|
+
this.register(
|
|
319
|
+
ErrorCategories.UNKNOWN,
|
|
320
|
+
() =>
|
|
321
|
+
new AbortStrategy({
|
|
322
|
+
messageFormatter: e => `Unexpected error while installing ${this.ideName}: ${e.message}`,
|
|
323
|
+
})
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Register a strategy factory for an error category
|
|
329
|
+
* @param {string} category - Error category
|
|
330
|
+
* @param {Function} factory - Factory function returning a strategy
|
|
331
|
+
*/
|
|
332
|
+
register(category, factory) {
|
|
333
|
+
this.strategies.set(category, factory);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Get strategy for an error
|
|
338
|
+
* @param {Error} error - Error to handle
|
|
339
|
+
* @returns {ErrorStrategy} Appropriate strategy
|
|
340
|
+
*/
|
|
341
|
+
getStrategy(error) {
|
|
342
|
+
const category = categorizeError(error);
|
|
343
|
+
const factory = this.strategies.get(category) || this.strategies.get(ErrorCategories.UNKNOWN);
|
|
344
|
+
return factory ? factory() : new AbortStrategy();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Handle an error using the appropriate strategy
|
|
349
|
+
* @param {Error} error - Error to handle
|
|
350
|
+
* @param {Object} context - Execution context
|
|
351
|
+
* @returns {Promise<{ recovered: boolean, result: any, error: Error | null }>}
|
|
352
|
+
*/
|
|
353
|
+
async handleError(error, context = {}) {
|
|
354
|
+
const strategy = this.getStrategy(error);
|
|
355
|
+
return strategy.handle(error, { ...context, ideName: this.ideName });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Create a wrapped function with error handling
|
|
360
|
+
* @param {Function} fn - Function to wrap
|
|
361
|
+
* @param {Object} context - Default context
|
|
362
|
+
* @returns {Function} Wrapped function
|
|
363
|
+
*/
|
|
364
|
+
wrap(fn, context = {}) {
|
|
365
|
+
return async (...args) => {
|
|
366
|
+
try {
|
|
367
|
+
return await fn(...args);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
const result = await this.handleError(error, { ...context, operation: () => fn(...args) });
|
|
370
|
+
if (result.recovered) {
|
|
371
|
+
return result.result;
|
|
372
|
+
}
|
|
373
|
+
throw result.error;
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Factory registry for all IDEs
|
|
381
|
+
*/
|
|
382
|
+
const factoryRegistry = new Map();
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Get or create factory for an IDE
|
|
386
|
+
* @param {string} ideName - IDE identifier
|
|
387
|
+
* @param {Object} options - Factory options
|
|
388
|
+
* @returns {IdeErrorFactory}
|
|
389
|
+
*/
|
|
390
|
+
function getFactory(ideName, options = {}) {
|
|
391
|
+
if (!factoryRegistry.has(ideName)) {
|
|
392
|
+
factoryRegistry.set(ideName, new IdeErrorFactory(ideName, options));
|
|
393
|
+
}
|
|
394
|
+
return factoryRegistry.get(ideName);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Reset factory registry (for testing)
|
|
399
|
+
*/
|
|
400
|
+
function resetFactories() {
|
|
401
|
+
factoryRegistry.clear();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
module.exports = {
|
|
405
|
+
// Strategy classes
|
|
406
|
+
ErrorStrategy,
|
|
407
|
+
RetryStrategy,
|
|
408
|
+
FallbackStrategy,
|
|
409
|
+
AbortStrategy,
|
|
410
|
+
SilentStrategy,
|
|
411
|
+
CompositeStrategy,
|
|
412
|
+
|
|
413
|
+
// Error categorization
|
|
414
|
+
ErrorCategories,
|
|
415
|
+
categorizeError,
|
|
416
|
+
|
|
417
|
+
// Factory
|
|
418
|
+
IdeErrorFactory,
|
|
419
|
+
getFactory,
|
|
420
|
+
resetFactories,
|
|
421
|
+
};
|