@ollie-shop/cli 0.3.0 ā 0.3.3
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 +14 -2
- package/CHANGELOG.md +48 -0
- package/__tests__/mocks/core.ts +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +40631 -75
- package/package.json +7 -6
- package/src/actions/component.actions.ts +148 -204
- package/src/actions/function.actions.ts +78 -171
- package/src/actions/project.actions.ts +16 -11
- package/src/commands/__tests__/component.test.ts +4 -7
- package/src/commands/__tests__/function.test.ts +1 -1
- package/src/commands/__tests__/store-version.test.ts +1 -4
- package/src/commands/component.ts +0 -10
- package/src/commands/function.ts +5 -56
- package/src/schemas/command.schema.ts +296 -6
- package/src/utils/__tests__/rich-progress.test.ts +22 -11
- package/src/utils/cli-progress-reporter.ts +2 -2
- package/src/utils/command-parser.ts +0 -5
- package/src/utils/console.ts +33 -4
- package/src/utils/constants.ts +32 -0
- package/src/utils/deploy-helpers.ts +357 -0
- package/src/utils/errors.ts +133 -2
- package/src/utils/interactive-builder.ts +61 -7
- package/src/utils/rich-progress.ts +25 -14
- package/src/utils/validation-helpers.ts +145 -12
- package/tsup.config.ts +15 -0
- package/dist/__tests__/helpers/cli-test-helper.d.ts +0 -89
- package/dist/__tests__/helpers/cli-test-helper.d.ts.map +0 -1
- package/dist/__tests__/helpers/cli-test-helper.js +0 -220
- package/dist/__tests__/mocks/index.d.ts +0 -69
- package/dist/__tests__/mocks/index.d.ts.map +0 -1
- package/dist/__tests__/mocks/index.js +0 -77
- package/dist/actions/component.actions.d.ts +0 -14
- package/dist/actions/component.actions.d.ts.map +0 -1
- package/dist/actions/component.actions.js +0 -273
- package/dist/actions/function.actions.d.ts +0 -15
- package/dist/actions/function.actions.d.ts.map +0 -1
- package/dist/actions/function.actions.js +0 -254
- package/dist/actions/project.actions.d.ts +0 -17
- package/dist/actions/project.actions.d.ts.map +0 -1
- package/dist/actions/project.actions.js +0 -97
- package/dist/actions/version.actions.d.ts +0 -19
- package/dist/actions/version.actions.d.ts.map +0 -1
- package/dist/actions/version.actions.js +0 -216
- package/dist/commands/component.d.ts +0 -3
- package/dist/commands/component.d.ts.map +0 -1
- package/dist/commands/component.js +0 -192
- package/dist/commands/docs.d.ts +0 -3
- package/dist/commands/docs.d.ts.map +0 -1
- package/dist/commands/docs.js +0 -16
- package/dist/commands/function.d.ts +0 -3
- package/dist/commands/function.d.ts.map +0 -1
- package/dist/commands/function.js +0 -243
- package/dist/commands/help.d.ts +0 -3
- package/dist/commands/help.d.ts.map +0 -1
- package/dist/commands/help.js +0 -20
- package/dist/commands/index.d.ts +0 -3
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js +0 -26
- package/dist/commands/login.d.ts +0 -3
- package/dist/commands/login.d.ts.map +0 -1
- package/dist/commands/login.js +0 -175
- package/dist/commands/project.d.ts +0 -3
- package/dist/commands/project.d.ts.map +0 -1
- package/dist/commands/project.js +0 -78
- package/dist/commands/store-version.d.ts +0 -3
- package/dist/commands/store-version.d.ts.map +0 -1
- package/dist/commands/store-version.js +0 -241
- package/dist/commands/version.d.ts +0 -3
- package/dist/commands/version.d.ts.map +0 -1
- package/dist/commands/version.js +0 -46
- package/dist/commands/whoami.d.ts +0 -3
- package/dist/commands/whoami.d.ts.map +0 -1
- package/dist/commands/whoami.js +0 -41
- package/dist/index.d.ts.map +0 -1
- package/dist/prompts/component.prompts.d.ts +0 -14
- package/dist/prompts/component.prompts.d.ts.map +0 -1
- package/dist/prompts/component.prompts.js +0 -75
- package/dist/prompts/function.prompts.d.ts +0 -21
- package/dist/prompts/function.prompts.d.ts.map +0 -1
- package/dist/prompts/function.prompts.js +0 -127
- package/dist/schemas/command.schema.d.ts +0 -516
- package/dist/schemas/command.schema.d.ts.map +0 -1
- package/dist/schemas/command.schema.js +0 -267
- package/dist/types/index.d.ts +0 -147
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -18
- package/dist/utils/auth.d.ts +0 -4
- package/dist/utils/auth.d.ts.map +0 -1
- package/dist/utils/auth.js +0 -26
- package/dist/utils/cli-progress-reporter.d.ts +0 -12
- package/dist/utils/cli-progress-reporter.d.ts.map +0 -1
- package/dist/utils/cli-progress-reporter.js +0 -77
- package/dist/utils/command-builder.d.ts +0 -22
- package/dist/utils/command-builder.d.ts.map +0 -1
- package/dist/utils/command-builder.js +0 -268
- package/dist/utils/command-helpers.d.ts +0 -19
- package/dist/utils/command-helpers.d.ts.map +0 -1
- package/dist/utils/command-helpers.js +0 -79
- package/dist/utils/command-parser.d.ts +0 -146
- package/dist/utils/command-parser.d.ts.map +0 -1
- package/dist/utils/command-parser.js +0 -179
- package/dist/utils/command-suggestions.d.ts +0 -35
- package/dist/utils/command-suggestions.d.ts.map +0 -1
- package/dist/utils/command-suggestions.js +0 -152
- package/dist/utils/console.d.ts +0 -44
- package/dist/utils/console.d.ts.map +0 -1
- package/dist/utils/console.js +0 -233
- package/dist/utils/constants.d.ts +0 -8
- package/dist/utils/constants.d.ts.map +0 -1
- package/dist/utils/constants.js +0 -10
- package/dist/utils/context-detector.d.ts +0 -12
- package/dist/utils/context-detector.d.ts.map +0 -1
- package/dist/utils/context-detector.js +0 -155
- package/dist/utils/enhanced-error-handler.d.ts +0 -47
- package/dist/utils/enhanced-error-handler.d.ts.map +0 -1
- package/dist/utils/enhanced-error-handler.js +0 -221
- package/dist/utils/error-handler.d.ts +0 -3
- package/dist/utils/error-handler.d.ts.map +0 -1
- package/dist/utils/error-handler.js +0 -55
- package/dist/utils/errors.d.ts +0 -44
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -76
- package/dist/utils/interactive-builder.d.ts +0 -22
- package/dist/utils/interactive-builder.d.ts.map +0 -1
- package/dist/utils/interactive-builder.js +0 -246
- package/dist/utils/rich-progress.d.ts +0 -59
- package/dist/utils/rich-progress.d.ts.map +0 -1
- package/dist/utils/rich-progress.js +0 -234
- package/dist/utils/store.d.ts +0 -11
- package/dist/utils/store.d.ts.map +0 -1
- package/dist/utils/store.js +0 -19
- package/dist/utils/validation-error-formatter.d.ts +0 -25
- package/dist/utils/validation-error-formatter.d.ts.map +0 -1
- package/dist/utils/validation-error-formatter.js +0 -258
- package/dist/utils/validation-helpers.d.ts +0 -60
- package/dist/utils/validation-helpers.d.ts.map +0 -1
- package/dist/utils/validation-helpers.js +0 -152
- package/src/commands/__tests__/version.test.ts +0 -71
|
@@ -0,0 +1,357 @@
|
|
|
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
|
+
}
|
package/src/utils/errors.ts
CHANGED
|
@@ -1,15 +1,45 @@
|
|
|
1
1
|
import type { ZodError } from "zod";
|
|
2
2
|
import type { ErrorContext } from "../types";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Recovery action that can be suggested to users when an error occurs
|
|
6
|
+
* @interface RecoveryAction
|
|
7
|
+
* @property {string} description - Human-readable description of the recovery action
|
|
8
|
+
* @property {string} [command] - Optional CLI command that can help resolve the issue
|
|
9
|
+
*/
|
|
4
10
|
export interface RecoveryAction {
|
|
5
11
|
description: string;
|
|
6
12
|
command?: string;
|
|
7
13
|
}
|
|
8
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Base error class for all Ollie Shop CLI errors
|
|
17
|
+
* Provides context and recovery suggestions to help users resolve issues
|
|
18
|
+
*
|
|
19
|
+
* @class OllieShopCLIError
|
|
20
|
+
* @extends {Error}
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* throw new OllieShopCLIError(
|
|
24
|
+
* "Component build failed",
|
|
25
|
+
* { componentId: "123", path: "./src" },
|
|
26
|
+
* [
|
|
27
|
+
* { description: "Check your TypeScript configuration" },
|
|
28
|
+
* { description: "Run validation", command: "ollieshop component validate" }
|
|
29
|
+
* ]
|
|
30
|
+
* );
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
9
33
|
export class OllieShopCLIError extends Error {
|
|
10
34
|
public readonly context: ErrorContext;
|
|
11
35
|
public readonly recoveryActions: RecoveryAction[];
|
|
12
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new OllieShopCLIError instance
|
|
39
|
+
* @param {string} message - The error message
|
|
40
|
+
* @param {ErrorContext} [context={}] - Additional context about the error
|
|
41
|
+
* @param {RecoveryAction[]} [recoveryActions=[]] - Suggested actions to resolve the error
|
|
42
|
+
*/
|
|
13
43
|
constructor(
|
|
14
44
|
message: string,
|
|
15
45
|
context: ErrorContext = {},
|
|
@@ -21,14 +51,41 @@ export class OllieShopCLIError extends Error {
|
|
|
21
51
|
this.recoveryActions = recoveryActions;
|
|
22
52
|
}
|
|
23
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Get the error context details
|
|
56
|
+
* @returns {ErrorContext} The error context object
|
|
57
|
+
*/
|
|
24
58
|
get details(): ErrorContext {
|
|
25
59
|
return this.context;
|
|
26
60
|
}
|
|
27
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Get suggested recovery actions
|
|
64
|
+
* @returns {RecoveryAction[]} Array of recovery actions
|
|
65
|
+
*/
|
|
28
66
|
get suggestions(): RecoveryAction[] {
|
|
29
67
|
return this.recoveryActions;
|
|
30
68
|
}
|
|
31
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Create an OllieShopCLIError from a Zod validation error
|
|
72
|
+
* Extracts the most relevant error message and provides validation-specific suggestions
|
|
73
|
+
*
|
|
74
|
+
* @static
|
|
75
|
+
* @param {ZodError} error - The Zod validation error
|
|
76
|
+
* @param {ErrorContext} [context={}] - Additional context to include
|
|
77
|
+
* @returns {OllieShopCLIError} A new OllieShopCLIError instance
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* try {
|
|
81
|
+
* ComponentNameSchema.parse("Invalid Name!");
|
|
82
|
+
* } catch (error) {
|
|
83
|
+
* if (error instanceof ZodError) {
|
|
84
|
+
* throw OllieShopCLIError.fromZodError(error, { field: "name" });
|
|
85
|
+
* }
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
32
89
|
static fromZodError(
|
|
33
90
|
error: ZodError,
|
|
34
91
|
context: ErrorContext = {},
|
|
@@ -48,6 +105,23 @@ export class OllieShopCLIError extends Error {
|
|
|
48
105
|
]);
|
|
49
106
|
}
|
|
50
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Create an OllieShopCLIError from an unknown error type
|
|
110
|
+
* Safely handles any error type and converts it to OllieShopCLIError
|
|
111
|
+
*
|
|
112
|
+
* @static
|
|
113
|
+
* @param {unknown} error - Any error or value
|
|
114
|
+
* @param {ErrorContext} [context={}] - Additional context to include
|
|
115
|
+
* @returns {OllieShopCLIError} A new OllieShopCLIError instance
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* try {
|
|
119
|
+
* // some operation
|
|
120
|
+
* } catch (error) {
|
|
121
|
+
* throw OllieShopCLIError.fromUnknown(error, { operation: "deploy" });
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
51
125
|
static fromUnknown(
|
|
52
126
|
error: unknown,
|
|
53
127
|
context: ErrorContext = {},
|
|
@@ -68,6 +142,20 @@ export class OllieShopCLIError extends Error {
|
|
|
68
142
|
);
|
|
69
143
|
}
|
|
70
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Create a file not found error with helpful suggestions
|
|
147
|
+
*
|
|
148
|
+
* @static
|
|
149
|
+
* @param {string} path - The file path that was not found
|
|
150
|
+
* @param {ErrorContext} [context={}] - Additional context to include
|
|
151
|
+
* @returns {OllieShopCLIError} A new OllieShopCLIError instance with file-specific suggestions
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* if (!fs.existsSync(configPath)) {
|
|
155
|
+
* throw OllieShopCLIError.fileNotFound(configPath, { operation: "load-config" });
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
71
159
|
static fileNotFound(
|
|
72
160
|
path: string,
|
|
73
161
|
context: ErrorContext = {},
|
|
@@ -86,11 +174,29 @@ export class OllieShopCLIError extends Error {
|
|
|
86
174
|
export const CLIError = OllieShopCLIError;
|
|
87
175
|
|
|
88
176
|
/**
|
|
89
|
-
*
|
|
177
|
+
* Error thrown when a CLI command execution fails
|
|
178
|
+
* Includes an error code for programmatic error handling
|
|
179
|
+
*
|
|
180
|
+
* @class CommandError
|
|
181
|
+
* @extends {OllieShopCLIError}
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* throw new CommandError(
|
|
185
|
+
* "Failed to connect to builder service",
|
|
186
|
+
* "BUILDER_CONNECTION_ERROR",
|
|
187
|
+
* { url: builderUrl, timeout: 5000 }
|
|
188
|
+
* );
|
|
189
|
+
* ```
|
|
90
190
|
*/
|
|
91
191
|
export class CommandError extends OllieShopCLIError {
|
|
92
192
|
public readonly code: string;
|
|
93
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Creates a new CommandError instance
|
|
196
|
+
* @param {string} message - The error message
|
|
197
|
+
* @param {string} code - A unique error code for this type of error
|
|
198
|
+
* @param {ErrorContext} [context={}] - Additional context about the error
|
|
199
|
+
*/
|
|
94
200
|
constructor(message: string, code: string, context: ErrorContext = {}) {
|
|
95
201
|
super(message, context);
|
|
96
202
|
this.name = "CommandError";
|
|
@@ -99,11 +205,32 @@ export class CommandError extends OllieShopCLIError {
|
|
|
99
205
|
}
|
|
100
206
|
|
|
101
207
|
/**
|
|
102
|
-
*
|
|
208
|
+
* Error thrown when validation fails with detailed field-level information
|
|
209
|
+
* Useful for form validation and input checking scenarios
|
|
210
|
+
*
|
|
211
|
+
* @class ValidationError
|
|
212
|
+
* @extends {OllieShopCLIError}
|
|
213
|
+
* @example
|
|
214
|
+
* ```typescript
|
|
215
|
+
* throw new ValidationError(
|
|
216
|
+
* "Component configuration is invalid",
|
|
217
|
+
* [
|
|
218
|
+
* { field: "name", message: "Must be lowercase with hyphens" },
|
|
219
|
+
* { field: "slot", message: "Invalid slot type" }
|
|
220
|
+
* ],
|
|
221
|
+
* { componentPath: "./src/components/header" }
|
|
222
|
+
* );
|
|
223
|
+
* ```
|
|
103
224
|
*/
|
|
104
225
|
export class ValidationError extends OllieShopCLIError {
|
|
105
226
|
public readonly fields: Array<{ field: string; message: string }>;
|
|
106
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Creates a new ValidationError instance
|
|
230
|
+
* @param {string} message - The overall error message
|
|
231
|
+
* @param {Array<{field: string; message: string}>} [fields=[]] - Field-specific validation errors
|
|
232
|
+
* @param {ErrorContext} [context={}] - Additional context about the error
|
|
233
|
+
*/
|
|
107
234
|
constructor(
|
|
108
235
|
message: string,
|
|
109
236
|
fields: Array<{ field: string; message: string }> = [],
|
|
@@ -114,6 +241,10 @@ export class ValidationError extends OllieShopCLIError {
|
|
|
114
241
|
this.fields = fields;
|
|
115
242
|
}
|
|
116
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Get error details including field-level errors
|
|
246
|
+
* @returns {ErrorContext & {fields: Array<{field: string; message: string}>}} Combined context and field errors
|
|
247
|
+
*/
|
|
117
248
|
get details(): ErrorContext & {
|
|
118
249
|
fields: Array<{ field: string; message: string }>;
|
|
119
250
|
} {
|
|
@@ -5,11 +5,40 @@ import type { ComponentDeployOptions } from "../schemas/command.schema";
|
|
|
5
5
|
import type { InteractiveChoice, MockComponent } from "../types";
|
|
6
6
|
import type { CliConsole } from "./console.js";
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Interactive command builder for creating CLI commands through prompts
|
|
10
|
+
* Provides a wizard-like interface for complex commands
|
|
11
|
+
*
|
|
12
|
+
* @class InteractiveCommandBuilder
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const builder = new InteractiveCommandBuilder(console);
|
|
16
|
+
* await builder.buildDeployComponentCommand();
|
|
17
|
+
* // User is guided through deployment options
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
8
20
|
export class InteractiveCommandBuilder {
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new InteractiveCommandBuilder instance
|
|
23
|
+
* @param {CliConsole} console - Console instance for I/O operations
|
|
24
|
+
*/
|
|
9
25
|
constructor(private console: CliConsole) {}
|
|
10
26
|
|
|
11
27
|
/**
|
|
12
28
|
* Build component deploy command interactively
|
|
29
|
+
* Guides user through selecting a component and deployment options
|
|
30
|
+
*
|
|
31
|
+
* @async
|
|
32
|
+
* @throws {Error} If user cancels the wizard
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* await builder.buildDeployComponentCommand();
|
|
36
|
+
* // Shows:
|
|
37
|
+
* // - List of available components
|
|
38
|
+
* // - Deployment options checkboxes
|
|
39
|
+
* // - Command preview
|
|
40
|
+
* // - Execution confirmation
|
|
41
|
+
* ```
|
|
13
42
|
*/
|
|
14
43
|
async buildDeployComponentCommand(): Promise<void> {
|
|
15
44
|
this.console.log(chalk.blue.bold("\nš Component Deployment Wizard\n"));
|
|
@@ -37,7 +66,6 @@ export class InteractiveCommandBuilder {
|
|
|
37
66
|
name: "options",
|
|
38
67
|
message: "Deployment options:",
|
|
39
68
|
choices: [
|
|
40
|
-
{ name: "Wait for completion", value: "wait", checked: true },
|
|
41
69
|
{ name: "Run tests before deploy", value: "test", checked: true },
|
|
42
70
|
{
|
|
43
71
|
name: "Enable verbose logging",
|
|
@@ -51,10 +79,6 @@ export class InteractiveCommandBuilder {
|
|
|
51
79
|
// Build the command
|
|
52
80
|
let command = `ollieshop component deploy --id ${answers.component}`;
|
|
53
81
|
|
|
54
|
-
if (answers.options.includes("wait")) {
|
|
55
|
-
command += " --wait";
|
|
56
|
-
}
|
|
57
|
-
|
|
58
82
|
if (answers.options.includes("verbose")) {
|
|
59
83
|
command += " --verbose";
|
|
60
84
|
}
|
|
@@ -82,7 +106,6 @@ export class InteractiveCommandBuilder {
|
|
|
82
106
|
const deployOptions: ComponentDeployOptions = {
|
|
83
107
|
componentId: answers.component,
|
|
84
108
|
path: process.cwd(),
|
|
85
|
-
wait: answers.options.includes("wait"),
|
|
86
109
|
};
|
|
87
110
|
await deployComponent(deployOptions, this.console);
|
|
88
111
|
} else {
|
|
@@ -99,6 +122,19 @@ export class InteractiveCommandBuilder {
|
|
|
99
122
|
|
|
100
123
|
/**
|
|
101
124
|
* Build component create command interactively
|
|
125
|
+
* Guides user through component creation options with live validation
|
|
126
|
+
*
|
|
127
|
+
* @async
|
|
128
|
+
* @throws {Error} If user cancels the wizard
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* await builder.buildCreateComponentCommand();
|
|
132
|
+
* // Prompts for:
|
|
133
|
+
* // - Component name (with live validation)
|
|
134
|
+
* // - Component slot selection
|
|
135
|
+
* // - Language preference (TypeScript/JavaScript)
|
|
136
|
+
* // - Test file inclusion
|
|
137
|
+
* ```
|
|
102
138
|
*/
|
|
103
139
|
async buildCreateComponentCommand(): Promise<void> {
|
|
104
140
|
this.console.log(chalk.blue.bold("\nšØ Component Creation Wizard\n"));
|
|
@@ -209,6 +245,11 @@ export class InteractiveCommandBuilder {
|
|
|
209
245
|
|
|
210
246
|
/**
|
|
211
247
|
* Get available components (mock for now)
|
|
248
|
+
* TODO: Replace with actual API call to list components
|
|
249
|
+
*
|
|
250
|
+
* @private
|
|
251
|
+
* @async
|
|
252
|
+
* @returns {Promise<MockComponent[]>} Array of mock component data
|
|
212
253
|
*/
|
|
213
254
|
private async getAvailableComponents(): Promise<MockComponent[]> {
|
|
214
255
|
// TODO: Replace with actual API call
|
|
@@ -248,7 +289,20 @@ export class InteractiveCommandBuilder {
|
|
|
248
289
|
}
|
|
249
290
|
|
|
250
291
|
/**
|
|
251
|
-
*
|
|
292
|
+
* Run an interactive command builder for the specified command
|
|
293
|
+
* Creates a wizard-like interface based on the command type
|
|
294
|
+
*
|
|
295
|
+
* @async
|
|
296
|
+
* @param {string} command - The command type to run interactively
|
|
297
|
+
* @param {CliConsole} console - Console instance for I/O
|
|
298
|
+
* @example
|
|
299
|
+
* ```typescript
|
|
300
|
+
* // Run deployment wizard
|
|
301
|
+
* await runInteractiveCommand("deploy", console);
|
|
302
|
+
*
|
|
303
|
+
* // Run component creation wizard
|
|
304
|
+
* await runInteractiveCommand("create", console);
|
|
305
|
+
* ```
|
|
252
306
|
*/
|
|
253
307
|
export async function runInteractiveCommand(
|
|
254
308
|
command: string,
|