@ollie-shop/cli 0.3.4 ā 1.0.1
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/.turbo/turbo-build.log +6 -9
- package/CHANGELOG.md +27 -0
- package/dist/index.js +993 -3956
- package/package.json +15 -37
- package/src/README.md +126 -0
- package/src/cli.tsx +45 -0
- package/src/commands/help.tsx +79 -0
- package/src/commands/login.tsx +92 -0
- package/src/commands/start.tsx +411 -0
- package/src/index.tsx +8 -0
- package/src/utils/auth.ts +218 -21
- package/src/utils/bundle.ts +177 -0
- package/src/utils/config.ts +123 -0
- package/src/utils/esbuild.ts +541 -0
- package/tsconfig.json +10 -15
- package/tsup.config.ts +7 -7
- package/CLAUDE_CLI.md +0 -265
- package/README.md +0 -711
- package/__tests__/mocks/console.ts +0 -22
- package/__tests__/mocks/core.ts +0 -137
- package/__tests__/mocks/index.ts +0 -4
- package/__tests__/mocks/inquirer.ts +0 -16
- package/__tests__/mocks/progress.ts +0 -19
- package/dist/index.d.ts +0 -1
- package/src/__tests__/helpers/cli-test-helper.ts +0 -281
- package/src/__tests__/mocks/index.ts +0 -142
- package/src/actions/component.actions.ts +0 -278
- package/src/actions/function.actions.ts +0 -220
- package/src/actions/project.actions.ts +0 -131
- package/src/actions/version.actions.ts +0 -233
- package/src/commands/__tests__/component-validation.test.ts +0 -250
- package/src/commands/__tests__/component.test.ts +0 -318
- package/src/commands/__tests__/function-validation.test.ts +0 -220
- package/src/commands/__tests__/function.test.ts +0 -286
- package/src/commands/__tests__/store-version-validation.test.ts +0 -414
- package/src/commands/__tests__/store-version.test.ts +0 -402
- package/src/commands/component.ts +0 -178
- package/src/commands/docs.ts +0 -24
- package/src/commands/function.ts +0 -201
- package/src/commands/help.ts +0 -18
- package/src/commands/index.ts +0 -27
- package/src/commands/login.ts +0 -267
- package/src/commands/project.ts +0 -107
- package/src/commands/store-version.ts +0 -242
- package/src/commands/version.ts +0 -51
- package/src/commands/whoami.ts +0 -46
- package/src/index.ts +0 -116
- package/src/prompts/component.prompts.ts +0 -94
- package/src/prompts/function.prompts.ts +0 -168
- package/src/schemas/command.schema.ts +0 -644
- package/src/types/index.ts +0 -183
- package/src/utils/__tests__/command-parser.test.ts +0 -159
- package/src/utils/__tests__/command-suggestions.test.ts +0 -185
- package/src/utils/__tests__/console.test.ts +0 -192
- package/src/utils/__tests__/context-detector.test.ts +0 -258
- package/src/utils/__tests__/enhanced-error-handler.test.ts +0 -137
- package/src/utils/__tests__/error-handler.test.ts +0 -107
- package/src/utils/__tests__/rich-progress.test.ts +0 -181
- package/src/utils/__tests__/validation-error-formatter.test.ts +0 -175
- package/src/utils/__tests__/validation-helpers.test.ts +0 -125
- package/src/utils/cli-progress-reporter.ts +0 -84
- package/src/utils/command-builder.ts +0 -390
- package/src/utils/command-helpers.ts +0 -83
- package/src/utils/command-parser.ts +0 -245
- package/src/utils/command-suggestions.ts +0 -176
- package/src/utils/console.ts +0 -320
- package/src/utils/constants.ts +0 -39
- package/src/utils/context-detector.ts +0 -177
- package/src/utils/deploy-helpers.ts +0 -357
- package/src/utils/enhanced-error-handler.ts +0 -264
- package/src/utils/error-handler.ts +0 -60
- package/src/utils/errors.ts +0 -256
- package/src/utils/interactive-builder.ts +0 -325
- package/src/utils/rich-progress.ts +0 -331
- package/src/utils/store.ts +0 -23
- package/src/utils/validation-error-formatter.ts +0 -337
- package/src/utils/validation-helpers.ts +0 -325
- package/vitest.config.ts +0 -35
- package/vitest.setup.ts +0 -29
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type BuildResponse,
|
|
3
|
-
type BuildResult,
|
|
4
|
-
formatBuildError,
|
|
5
|
-
setBuilderUrl,
|
|
6
|
-
waitForBuildCompletion,
|
|
7
|
-
} from "@ollie-shop/core";
|
|
8
|
-
import { validateBuilderApiUrl } from "../schemas/command.schema";
|
|
9
|
-
import type { CliConsole } from "./console";
|
|
10
|
-
import { DEPLOYMENT_SETTINGS, ERROR_MESSAGES } from "./constants";
|
|
11
|
-
import { RichProgressReporter } from "./rich-progress.js";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Resource types that can be deployed through the CLI
|
|
15
|
-
* @typedef {"component" | "function"} ResourceType
|
|
16
|
-
*/
|
|
17
|
-
export type ResourceType = "component" | "function";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Error types that can occur during deployment operations
|
|
21
|
-
* @typedef {"BuildTimeoutError" | "BuildFailedError" | "BuildValidationError" | "UnknownError"} DeploymentErrorType
|
|
22
|
-
*/
|
|
23
|
-
export type DeploymentErrorType =
|
|
24
|
-
| "BuildTimeoutError"
|
|
25
|
-
| "BuildFailedError"
|
|
26
|
-
| "BuildValidationError"
|
|
27
|
-
| "UnknownError";
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Deployment progress information returned after successful deployment
|
|
31
|
-
* @interface DeploymentProgress
|
|
32
|
-
* @property {BuildResponse} completedBuild - The completed build response from the builder service
|
|
33
|
-
* @property {number} duration - Total deployment duration in seconds
|
|
34
|
-
*/
|
|
35
|
-
export interface DeploymentProgress {
|
|
36
|
-
completedBuild: BuildResponse;
|
|
37
|
-
duration: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Configure the builder URL from environment variable with validation
|
|
42
|
-
* Uses BUILDER_API_URL environment variable if set, validates it's a proper URL
|
|
43
|
-
*
|
|
44
|
-
* @throws {Error} If BUILDER_API_URL is set but invalid
|
|
45
|
-
* @example
|
|
46
|
-
* ```typescript
|
|
47
|
-
* // Set environment variable first
|
|
48
|
-
* process.env.BUILDER_API_URL = "https://builder.ollie.shop";
|
|
49
|
-
*
|
|
50
|
-
* // Configure the builder
|
|
51
|
-
* configureBuilderUrl();
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
export function configureBuilderUrl(): void {
|
|
55
|
-
const builderUrl = process.env.BUILDER_API_URL;
|
|
56
|
-
if (builderUrl) {
|
|
57
|
-
if (!validateBuilderApiUrl(builderUrl)) {
|
|
58
|
-
throw new Error(
|
|
59
|
-
"Invalid BUILDER_API_URL environment variable. Must be a valid HTTP or HTTPS URL.",
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
setBuilderUrl(builderUrl);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Handle deployment errors with proper type safety and user-friendly messages
|
|
68
|
-
* Provides specific error messages and recovery suggestions based on error type
|
|
69
|
-
*
|
|
70
|
-
* @param {unknown} error - The error that occurred during deployment
|
|
71
|
-
* @param {string} _buildId - The build ID (currently unused but kept for future use)
|
|
72
|
-
* @param {ResourceType} resourceType - Type of resource being deployed (component/function)
|
|
73
|
-
* @param {CliConsole} cliConsole - Console instance for output
|
|
74
|
-
* @example
|
|
75
|
-
* ```typescript
|
|
76
|
-
* try {
|
|
77
|
-
* await deployComponent(options);
|
|
78
|
-
* } catch (error) {
|
|
79
|
-
* handleDeploymentError(error, buildId, "component", console);
|
|
80
|
-
* }
|
|
81
|
-
* ```
|
|
82
|
-
*/
|
|
83
|
-
export function handleDeploymentError(
|
|
84
|
-
error: unknown,
|
|
85
|
-
_buildId: string,
|
|
86
|
-
resourceType: ResourceType,
|
|
87
|
-
cliConsole: CliConsole,
|
|
88
|
-
): void {
|
|
89
|
-
const errorType = getErrorType(error);
|
|
90
|
-
const errorMessage = getErrorMessage(error);
|
|
91
|
-
|
|
92
|
-
switch (errorType) {
|
|
93
|
-
case "BuildTimeoutError":
|
|
94
|
-
cliConsole.error(ERROR_MESSAGES.DEPLOYMENT_TIMEOUT);
|
|
95
|
-
cliConsole.suggestions([
|
|
96
|
-
`Retry deployment: ollieshop ${resourceType} deploy --id <${resourceType}-id>`,
|
|
97
|
-
"Check your network connection and try again",
|
|
98
|
-
"Contact support if timeouts persist - this may indicate infrastructure issues",
|
|
99
|
-
]);
|
|
100
|
-
break;
|
|
101
|
-
case "BuildFailedError":
|
|
102
|
-
cliConsole.error(ERROR_MESSAGES.BUILD_FAILED);
|
|
103
|
-
cliConsole.info("Review the build logs above for specific error details");
|
|
104
|
-
if (errorMessage) {
|
|
105
|
-
cliConsole.info(`Error details: ${errorMessage}`);
|
|
106
|
-
}
|
|
107
|
-
cliConsole.suggestions([
|
|
108
|
-
`Validate your ${resourceType} code: ollieshop ${resourceType} validate`,
|
|
109
|
-
"Check your code for syntax errors and missing dependencies",
|
|
110
|
-
"Ensure all imports and exports are correctly defined",
|
|
111
|
-
]);
|
|
112
|
-
break;
|
|
113
|
-
case "BuildValidationError":
|
|
114
|
-
cliConsole.error(ERROR_MESSAGES.BUILD_VALIDATION_FAILED);
|
|
115
|
-
handleBuildValidationError(error, resourceType, cliConsole);
|
|
116
|
-
break;
|
|
117
|
-
default:
|
|
118
|
-
cliConsole.error(`${ERROR_MESSAGES.UNKNOWN_ERROR}: ${errorMessage}`);
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Get the error type from an unknown error with proper type safety
|
|
125
|
-
*/
|
|
126
|
-
function getErrorType(error: unknown): DeploymentErrorType {
|
|
127
|
-
if (error && typeof error === "object" && "name" in error) {
|
|
128
|
-
const errorName = (error as { name: string }).name;
|
|
129
|
-
if (isValidErrorType(errorName)) {
|
|
130
|
-
return errorName;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return "UnknownError";
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Get the error message from an unknown error with proper type safety
|
|
138
|
-
*/
|
|
139
|
-
function getErrorMessage(error: unknown): string {
|
|
140
|
-
if (error && typeof error === "object" && "message" in error) {
|
|
141
|
-
return String((error as { message: unknown }).message);
|
|
142
|
-
}
|
|
143
|
-
return "An unknown error occurred";
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Type guard to check if a string is a valid error type
|
|
148
|
-
*/
|
|
149
|
-
function isValidErrorType(errorName: string): errorName is DeploymentErrorType {
|
|
150
|
-
return [
|
|
151
|
-
"BuildTimeoutError",
|
|
152
|
-
"BuildFailedError",
|
|
153
|
-
"BuildValidationError",
|
|
154
|
-
].includes(errorName);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Show deployment success message with build details
|
|
159
|
-
* Displays build ID, duration, and infrastructure deployment confirmation
|
|
160
|
-
*
|
|
161
|
-
* @param {BuildResponse} completedBuild - The completed build response
|
|
162
|
-
* @param {number} duration - Deployment duration in seconds
|
|
163
|
-
* @param {string} _resourceId - Resource ID (currently unused but kept for future use)
|
|
164
|
-
* @param {ResourceType} _resourceType - Resource type (currently unused but kept for future use)
|
|
165
|
-
* @param {CliConsole} cliConsole - Console instance for output
|
|
166
|
-
* @example
|
|
167
|
-
* ```typescript
|
|
168
|
-
* const { completedBuild, duration } = await waitForDeploymentWithProgress(
|
|
169
|
-
* buildId,
|
|
170
|
-
* "component",
|
|
171
|
-
* console
|
|
172
|
-
* );
|
|
173
|
-
* showDeploymentSuccess(completedBuild, duration, componentId, "component", console);
|
|
174
|
-
* ```
|
|
175
|
-
*/
|
|
176
|
-
export function showDeploymentSuccess(
|
|
177
|
-
completedBuild: BuildResponse,
|
|
178
|
-
duration: number,
|
|
179
|
-
_resourceId: string,
|
|
180
|
-
_resourceType: ResourceType,
|
|
181
|
-
cliConsole: CliConsole,
|
|
182
|
-
): void {
|
|
183
|
-
cliConsole.success("\nā
Deployment completed successfully!");
|
|
184
|
-
cliConsole.info(`Build ID: ${completedBuild.id}`);
|
|
185
|
-
cliConsole.info(`Duration: ${duration.toFixed(1)}s`);
|
|
186
|
-
|
|
187
|
-
// Note: Deployment URLs are dynamically generated by the infrastructure
|
|
188
|
-
// For components: CloudFront URL (managed by SST router)
|
|
189
|
-
// For functions: Lambda ARN (managed by AWS)
|
|
190
|
-
cliConsole.info("⨠Resources deployed to cloud infrastructure");
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Removed showAsyncModeNextSteps as deployments now always wait for completion
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Wait for deployment completion with real-time progress reporting
|
|
197
|
-
* Shows animated progress bar with ETA while polling the builder service
|
|
198
|
-
*
|
|
199
|
-
* @async
|
|
200
|
-
* @param {string} buildId - The build ID to monitor
|
|
201
|
-
* @param {ResourceType} resourceType - Type of resource being deployed
|
|
202
|
-
* @param {CliConsole} cliConsole - Console instance for progress display
|
|
203
|
-
* @returns {Promise<DeploymentProgress>} Deployment details including completed build and duration
|
|
204
|
-
* @throws {Error} If deployment times out or fails
|
|
205
|
-
* @example
|
|
206
|
-
* ```typescript
|
|
207
|
-
* const result = await buildAndDeployComponent(componentId, path);
|
|
208
|
-
* const { completedBuild, duration } = await waitForDeploymentWithProgress(
|
|
209
|
-
* result.buildId,
|
|
210
|
-
* "component",
|
|
211
|
-
* console
|
|
212
|
-
* );
|
|
213
|
-
* console.log(`Deployment completed in ${duration}s`);
|
|
214
|
-
* ```
|
|
215
|
-
*/
|
|
216
|
-
export async function waitForDeploymentWithProgress(
|
|
217
|
-
buildId: string,
|
|
218
|
-
resourceType: ResourceType,
|
|
219
|
-
cliConsole: CliConsole,
|
|
220
|
-
): Promise<DeploymentProgress> {
|
|
221
|
-
const progressReporter = new RichProgressReporter(cliConsole);
|
|
222
|
-
progressReporter.start();
|
|
223
|
-
|
|
224
|
-
const startTime = Date.now();
|
|
225
|
-
const estimatedDuration = DEPLOYMENT_SETTINGS.ESTIMATED_BUILD_DURATION_MS;
|
|
226
|
-
|
|
227
|
-
// Update progress based on elapsed time
|
|
228
|
-
const progressTimer = setInterval(() => {
|
|
229
|
-
const elapsed = Date.now() - startTime;
|
|
230
|
-
const progress = Math.min(
|
|
231
|
-
elapsed / estimatedDuration,
|
|
232
|
-
DEPLOYMENT_SETTINGS.MAX_PROGRESS_BEFORE_COMPLETION,
|
|
233
|
-
);
|
|
234
|
-
const remainingSeconds = Math.max(
|
|
235
|
-
0,
|
|
236
|
-
Math.ceil((estimatedDuration - elapsed) / 1000),
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
progressReporter.updateProgress({
|
|
240
|
-
phase: "building",
|
|
241
|
-
message: `Building ${resourceType}... ETA: ${remainingSeconds}s`,
|
|
242
|
-
progress,
|
|
243
|
-
});
|
|
244
|
-
}, DEPLOYMENT_SETTINGS.PROGRESS_UPDATE_INTERVAL_MS);
|
|
245
|
-
|
|
246
|
-
try {
|
|
247
|
-
const completedBuild = await waitForBuildCompletion(buildId, {
|
|
248
|
-
pollIntervalMs: DEPLOYMENT_SETTINGS.DEFAULT_POLL_INTERVAL_MS,
|
|
249
|
-
maxWaitMs: DEPLOYMENT_SETTINGS.DEFAULT_MAX_WAIT_MS,
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
clearInterval(progressTimer);
|
|
253
|
-
const duration = (Date.now() - startTime) / 1000;
|
|
254
|
-
progressReporter.stop(true);
|
|
255
|
-
|
|
256
|
-
return { completedBuild, duration };
|
|
257
|
-
} catch (error) {
|
|
258
|
-
clearInterval(progressTimer);
|
|
259
|
-
progressReporter.stop(false);
|
|
260
|
-
throw error;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Handle build validation errors with specific suggestions
|
|
266
|
-
* Provides targeted help for common validation issues
|
|
267
|
-
*
|
|
268
|
-
* @param {unknown} error - The validation error
|
|
269
|
-
* @param {ResourceType} resourceType - Type of resource that failed validation
|
|
270
|
-
* @param {CliConsole} cliConsole - Console instance for output
|
|
271
|
-
* @example
|
|
272
|
-
* ```typescript
|
|
273
|
-
* if (error.code === "BUILD_VALIDATION_ERROR") {
|
|
274
|
-
* handleBuildValidationError(error, "component", console);
|
|
275
|
-
* }
|
|
276
|
-
* ```
|
|
277
|
-
*/
|
|
278
|
-
export function handleBuildValidationError(
|
|
279
|
-
error: unknown,
|
|
280
|
-
resourceType: ResourceType,
|
|
281
|
-
cliConsole: CliConsole,
|
|
282
|
-
): void {
|
|
283
|
-
const errorCode = getErrorCode(error);
|
|
284
|
-
|
|
285
|
-
if (errorCode === "BUILD_VALIDATION_ERROR") {
|
|
286
|
-
cliConsole.suggestions([
|
|
287
|
-
`Ensure the ${resourceType} ID is a valid UUID format (e.g., 123e4567-e89b-12d3-a456-426614174000)`,
|
|
288
|
-
`Verify the ${resourceType} directory exists and contains the required files`,
|
|
289
|
-
`Check that your ${resourceType} has a valid package.json with correct entry point`,
|
|
290
|
-
`Run: ollieshop ${resourceType} validate --path <path> to identify specific issues`,
|
|
291
|
-
]);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Get error code from unknown error with proper type safety
|
|
297
|
-
*/
|
|
298
|
-
function getErrorCode(error: unknown): string | undefined {
|
|
299
|
-
if (error && typeof error === "object" && "code" in error) {
|
|
300
|
-
const code = (error as { code: unknown }).code;
|
|
301
|
-
return typeof code === "string" ? code : undefined;
|
|
302
|
-
}
|
|
303
|
-
return undefined;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Handle deployment result with proper error handling and type safety
|
|
308
|
-
* Updates spinner state and throws error if deployment failed
|
|
309
|
-
*
|
|
310
|
-
* @param {BuildResult} result - The build result from the deployment attempt
|
|
311
|
-
* @param {ResourceType} resourceType - Type of resource being deployed
|
|
312
|
-
* @param {CliConsole} cliConsole - Console instance for output
|
|
313
|
-
* @param {ReturnType<CliConsole["spinner"]>} spinner - Active spinner to update
|
|
314
|
-
* @throws {Error} If the deployment failed
|
|
315
|
-
* @example
|
|
316
|
-
* ```typescript
|
|
317
|
-
* const result = await buildAndDeployComponent(componentId, path);
|
|
318
|
-
* handleDeploymentResult(result, "component", console, spinner);
|
|
319
|
-
* // If we get here, deployment was initiated successfully
|
|
320
|
-
* ```
|
|
321
|
-
*/
|
|
322
|
-
export function handleDeploymentResult(
|
|
323
|
-
result: BuildResult,
|
|
324
|
-
resourceType: ResourceType,
|
|
325
|
-
cliConsole: CliConsole,
|
|
326
|
-
spinner: ReturnType<CliConsole["spinner"]>,
|
|
327
|
-
): void {
|
|
328
|
-
if (!result.success) {
|
|
329
|
-
const capitalizedType = capitalizeResourceType(resourceType);
|
|
330
|
-
spinner.fail(`${capitalizedType} deployment failed`);
|
|
331
|
-
|
|
332
|
-
const errorMessage = result.error
|
|
333
|
-
? formatBuildError(result.error)
|
|
334
|
-
: "Unknown error occurred";
|
|
335
|
-
|
|
336
|
-
cliConsole.error(errorMessage);
|
|
337
|
-
handleBuildValidationError(result.error, resourceType, cliConsole);
|
|
338
|
-
|
|
339
|
-
const error = new Error(
|
|
340
|
-
getErrorMessage(result.error) || "Deployment failed",
|
|
341
|
-
);
|
|
342
|
-
error.name = "DeploymentError";
|
|
343
|
-
throw error;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const capitalizedType = capitalizeResourceType(resourceType);
|
|
347
|
-
spinner.succeed(`${capitalizedType} deployment initiated!`);
|
|
348
|
-
cliConsole.success(`Build ID: ${result.buildId}`);
|
|
349
|
-
cliConsole.info(`Status: ${result.status}`);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Capitalize resource type for display purposes
|
|
354
|
-
*/
|
|
355
|
-
function capitalizeResourceType(resourceType: ResourceType): string {
|
|
356
|
-
return resourceType.charAt(0).toUpperCase() + resourceType.slice(1);
|
|
357
|
-
}
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import type { ErrorContext } from "../types";
|
|
3
|
-
import { isNodeError } from "../types";
|
|
4
|
-
import type { CliConsole } from "./console.js";
|
|
5
|
-
import { detectProjectContext } from "./context-detector.js";
|
|
6
|
-
|
|
7
|
-
interface ErrorRecovery {
|
|
8
|
-
message: string;
|
|
9
|
-
suggestions: string[];
|
|
10
|
-
commands?: string[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class EnhancedErrorHandler {
|
|
14
|
-
constructor(private console: CliConsole) {}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Common error patterns with recovery suggestions
|
|
18
|
-
*/
|
|
19
|
-
private readonly errorPatterns = new Map<string | RegExp, ErrorRecovery>([
|
|
20
|
-
[
|
|
21
|
-
"ENOENT",
|
|
22
|
-
{
|
|
23
|
-
message: "File or directory not found",
|
|
24
|
-
suggestions: [
|
|
25
|
-
"Check if the path exists",
|
|
26
|
-
"Use --path to specify the correct location",
|
|
27
|
-
],
|
|
28
|
-
commands: ["ls", "pwd"],
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
[
|
|
32
|
-
"EACCES",
|
|
33
|
-
{
|
|
34
|
-
message: "Permission denied",
|
|
35
|
-
suggestions: [
|
|
36
|
-
"Check file permissions",
|
|
37
|
-
"Try running with appropriate permissions",
|
|
38
|
-
],
|
|
39
|
-
commands: ["ls -la"],
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
[
|
|
43
|
-
"ECONNREFUSED",
|
|
44
|
-
{
|
|
45
|
-
message: "Connection refused",
|
|
46
|
-
suggestions: [
|
|
47
|
-
"Check your internet connection",
|
|
48
|
-
"Verify your authentication",
|
|
49
|
-
"Check if backend services are running",
|
|
50
|
-
],
|
|
51
|
-
commands: ["ollieshop login", "ollieshop status"],
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
[
|
|
55
|
-
/Component .* already exists/,
|
|
56
|
-
{
|
|
57
|
-
message: "Component already exists",
|
|
58
|
-
suggestions: [
|
|
59
|
-
"Use a different name",
|
|
60
|
-
"Delete the existing component first",
|
|
61
|
-
"Update the existing component instead",
|
|
62
|
-
],
|
|
63
|
-
commands: ["ollieshop component list", "rm -rf ./components/{name}"],
|
|
64
|
-
},
|
|
65
|
-
],
|
|
66
|
-
[
|
|
67
|
-
/Invalid component name/,
|
|
68
|
-
{
|
|
69
|
-
message: "Component name validation failed",
|
|
70
|
-
suggestions: [
|
|
71
|
-
"Use lowercase letters and hyphens only",
|
|
72
|
-
"Examples: header-nav, shopping-cart, product-list",
|
|
73
|
-
],
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
]);
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Handle errors with enhanced recovery suggestions
|
|
80
|
-
*/
|
|
81
|
-
handle(error: Error, context?: ErrorContext): void {
|
|
82
|
-
// Display the main error
|
|
83
|
-
this.console.error(`\nā ${error.message}\n`);
|
|
84
|
-
|
|
85
|
-
// Get recovery suggestions
|
|
86
|
-
const recovery = this.getRecoverySuggestions(error);
|
|
87
|
-
|
|
88
|
-
if (recovery) {
|
|
89
|
-
this.displayRecoverySuggestions(recovery, context);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Show contextual information
|
|
93
|
-
this.displayContextualInfo(error, context);
|
|
94
|
-
|
|
95
|
-
// Suggest similar commands if applicable
|
|
96
|
-
if (this.isCommandError(error)) {
|
|
97
|
-
this.suggestSimilarCommands(context);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Get recovery suggestions based on error
|
|
103
|
-
*/
|
|
104
|
-
private getRecoverySuggestions(error: Error): ErrorRecovery | undefined {
|
|
105
|
-
// Check error code
|
|
106
|
-
if (isNodeError(error) && this.errorPatterns.has(error.code)) {
|
|
107
|
-
return this.errorPatterns.get(error.code);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Check error message patterns
|
|
111
|
-
for (const [pattern, recovery] of this.errorPatterns) {
|
|
112
|
-
if (pattern instanceof RegExp && pattern.test(error.message)) {
|
|
113
|
-
return recovery;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Display recovery suggestions
|
|
122
|
-
*/
|
|
123
|
-
private displayRecoverySuggestions(
|
|
124
|
-
recovery: ErrorRecovery,
|
|
125
|
-
context?: ErrorContext,
|
|
126
|
-
): void {
|
|
127
|
-
this.console.warn("š” Possible solutions:\n");
|
|
128
|
-
|
|
129
|
-
for (const [index, suggestion] of recovery.suggestions.entries()) {
|
|
130
|
-
// Replace placeholders in suggestions
|
|
131
|
-
const formatted = this.formatSuggestion(suggestion, context);
|
|
132
|
-
this.console.log(chalk.yellow(` ${index + 1}. ${formatted}`));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (recovery.commands && recovery.commands.length > 0) {
|
|
136
|
-
this.console.log(chalk.dim("\nš Helpful commands:"));
|
|
137
|
-
for (const cmd of recovery.commands) {
|
|
138
|
-
const formatted = this.formatSuggestion(cmd, context);
|
|
139
|
-
this.console.log(chalk.dim(` $ ${formatted}`));
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Display contextual information
|
|
146
|
-
*/
|
|
147
|
-
private displayContextualInfo(_error: Error, context?: ErrorContext): void {
|
|
148
|
-
const projectContext = detectProjectContext();
|
|
149
|
-
|
|
150
|
-
this.console.log(chalk.dim("\nš Context:"));
|
|
151
|
-
this.console.log(chalk.dim(` Current directory: ${process.cwd()}`));
|
|
152
|
-
|
|
153
|
-
if (projectContext.type !== "unknown") {
|
|
154
|
-
this.console.log(chalk.dim(` Project type: ${projectContext.type}`));
|
|
155
|
-
if (projectContext.name) {
|
|
156
|
-
this.console.log(
|
|
157
|
-
chalk.dim(` ${projectContext.type} name: ${projectContext.name}`),
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (context?.path) {
|
|
163
|
-
this.console.log(chalk.dim(` Target path: ${context.path}`));
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Format suggestion with context values
|
|
169
|
-
*/
|
|
170
|
-
private formatSuggestion(suggestion: string, context?: ErrorContext): string {
|
|
171
|
-
if (!context) return suggestion;
|
|
172
|
-
|
|
173
|
-
return suggestion.replace(/{(\w+)}/g, (match, key) => {
|
|
174
|
-
const value = context[key as keyof ErrorContext];
|
|
175
|
-
return typeof value === "string" ? value : match;
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Check if this is a command-related error
|
|
181
|
-
*/
|
|
182
|
-
private isCommandError(error: Error): boolean {
|
|
183
|
-
return (
|
|
184
|
-
error.message.includes("Unknown command") ||
|
|
185
|
-
error.message.includes("not found") ||
|
|
186
|
-
(isNodeError(error) && error.code === "COMMAND_NOT_FOUND")
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Suggest similar commands
|
|
192
|
-
*/
|
|
193
|
-
private suggestSimilarCommands(context?: ErrorContext): void {
|
|
194
|
-
if (!context?.attemptedCommand) return;
|
|
195
|
-
|
|
196
|
-
const suggestions = this.findSimilarCommands(
|
|
197
|
-
String(context.attemptedCommand),
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
if (suggestions.length > 0) {
|
|
201
|
-
this.console.log(chalk.dim("\nš” Did you mean:"));
|
|
202
|
-
for (const cmd of suggestions) {
|
|
203
|
-
this.console.log(chalk.dim(` $ ollieshop ${cmd}`));
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Find similar commands using basic string matching
|
|
210
|
-
*/
|
|
211
|
-
private findSimilarCommands(attempted: string): string[] {
|
|
212
|
-
const commands = [
|
|
213
|
-
"component create",
|
|
214
|
-
"component validate",
|
|
215
|
-
"component build",
|
|
216
|
-
"component deploy",
|
|
217
|
-
"component list",
|
|
218
|
-
"function create",
|
|
219
|
-
"function validate",
|
|
220
|
-
"function build",
|
|
221
|
-
"function test",
|
|
222
|
-
"function deploy",
|
|
223
|
-
"store-version create",
|
|
224
|
-
"store-version list",
|
|
225
|
-
"template list",
|
|
226
|
-
"login",
|
|
227
|
-
"whoami",
|
|
228
|
-
];
|
|
229
|
-
|
|
230
|
-
return commands
|
|
231
|
-
.filter((cmd) => {
|
|
232
|
-
// Simple similarity check
|
|
233
|
-
const cmdParts = cmd.toLowerCase().split(" ");
|
|
234
|
-
const attemptedParts = attempted.toLowerCase().split(" ");
|
|
235
|
-
|
|
236
|
-
// Check if any part matches
|
|
237
|
-
return cmdParts.some((part) =>
|
|
238
|
-
attemptedParts.some(
|
|
239
|
-
(aPart) => part.startsWith(aPart) || aPart.startsWith(part),
|
|
240
|
-
),
|
|
241
|
-
);
|
|
242
|
-
})
|
|
243
|
-
.slice(0, 3); // Show top 3 suggestions
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Create an error with additional context
|
|
249
|
-
*/
|
|
250
|
-
export function createContextualError(
|
|
251
|
-
message: string,
|
|
252
|
-
code?: string,
|
|
253
|
-
context?: Record<string, unknown>,
|
|
254
|
-
): Error {
|
|
255
|
-
const error = new Error(message) as Error & {
|
|
256
|
-
code?: string;
|
|
257
|
-
context?: Record<string, unknown>;
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
if (code) error.code = code;
|
|
261
|
-
if (context) error.context = context;
|
|
262
|
-
|
|
263
|
-
return error;
|
|
264
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { ErrorContext } from "../types";
|
|
2
|
-
import { console as cliConsole } from "./console";
|
|
3
|
-
import { EnhancedErrorHandler } from "./enhanced-error-handler.js";
|
|
4
|
-
import { OllieShopCLIError, type RecoveryAction } from "./errors";
|
|
5
|
-
|
|
6
|
-
function handleOllieShopError(error: OllieShopCLIError): void {
|
|
7
|
-
cliConsole.error(`\n${error.message}`);
|
|
8
|
-
|
|
9
|
-
if (error.details) {
|
|
10
|
-
displayErrorDetails(error.details);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (error.suggestions && error.suggestions.length > 0) {
|
|
14
|
-
displayRecoveryActions(error.suggestions);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function displayErrorDetails(details: ErrorContext): void {
|
|
19
|
-
cliConsole.dim("\nContext:");
|
|
20
|
-
for (const [key, value] of Object.entries(details)) {
|
|
21
|
-
if (key === "fields" && Array.isArray(value)) {
|
|
22
|
-
displayFieldErrors(value);
|
|
23
|
-
} else {
|
|
24
|
-
cliConsole.dim(` ${key}: ${value}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function displayFieldErrors(
|
|
30
|
-
fields: Array<{ field: string; message: string }>,
|
|
31
|
-
): void {
|
|
32
|
-
cliConsole.dim(" fields:");
|
|
33
|
-
for (const field of fields) {
|
|
34
|
-
cliConsole.dim(` ${field.field}: ${field.message}`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function displayRecoveryActions(actions: RecoveryAction[]): void {
|
|
39
|
-
cliConsole.info("\nSuggestions:");
|
|
40
|
-
for (const action of actions) {
|
|
41
|
-
cliConsole.dim(` ⢠${action.description}`);
|
|
42
|
-
if (action.command) {
|
|
43
|
-
cliConsole.dim(` $ ${action.command}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function handleError(error: unknown, context?: ErrorContext): never {
|
|
49
|
-
if (error instanceof OllieShopCLIError) {
|
|
50
|
-
handleOllieShopError(error);
|
|
51
|
-
} else if (error instanceof Error) {
|
|
52
|
-
const enhancedHandler = new EnhancedErrorHandler(cliConsole);
|
|
53
|
-
enhancedHandler.handle(error, context);
|
|
54
|
-
} else {
|
|
55
|
-
cliConsole.error("\nAn unexpected error occurred");
|
|
56
|
-
cliConsole.dim(String(error));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|