@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,181 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type { CliConsole } from "../console";
|
|
3
|
-
import { RichProgressReporter, SimpleProgressReporter } from "../rich-progress";
|
|
4
|
-
|
|
5
|
-
// Mock cli-progress
|
|
6
|
-
vi.mock("cli-progress", () => {
|
|
7
|
-
const mockBar = {
|
|
8
|
-
update: vi.fn(),
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const mockMultiBar = {
|
|
12
|
-
create: vi.fn().mockReturnValue(mockBar),
|
|
13
|
-
stop: vi.fn(),
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
default: {
|
|
18
|
-
MultiBar: vi.fn(() => mockMultiBar),
|
|
19
|
-
Presets: {
|
|
20
|
-
shades_classic: {},
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Mock ora
|
|
27
|
-
vi.mock("ora", () => ({
|
|
28
|
-
default: vi.fn(() => ({
|
|
29
|
-
start: vi.fn().mockReturnThis(),
|
|
30
|
-
succeed: vi.fn(),
|
|
31
|
-
fail: vi.fn(),
|
|
32
|
-
text: "",
|
|
33
|
-
})),
|
|
34
|
-
}));
|
|
35
|
-
|
|
36
|
-
describe("RichProgressReporter", () => {
|
|
37
|
-
let reporter: RichProgressReporter;
|
|
38
|
-
let mockConsole: CliConsole;
|
|
39
|
-
let consoleLogSpy: ReturnType<typeof vi.spyOn>;
|
|
40
|
-
|
|
41
|
-
beforeEach(() => {
|
|
42
|
-
// Create a mock console with all required methods
|
|
43
|
-
mockConsole = {
|
|
44
|
-
info: vi.fn(),
|
|
45
|
-
success: vi.fn(),
|
|
46
|
-
error: vi.fn(),
|
|
47
|
-
warn: vi.fn(),
|
|
48
|
-
debug: vi.fn(),
|
|
49
|
-
log: vi.fn(),
|
|
50
|
-
} as unknown as CliConsole;
|
|
51
|
-
|
|
52
|
-
consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {
|
|
53
|
-
// Intentionally empty for test isolation
|
|
54
|
-
});
|
|
55
|
-
vi.clearAllMocks();
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
afterEach(() => {
|
|
59
|
-
consoleLogSpy.mockRestore();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("should start progress tracking", () => {
|
|
63
|
-
reporter = new RichProgressReporter(mockConsole);
|
|
64
|
-
reporter.start();
|
|
65
|
-
|
|
66
|
-
expect(mockConsole.info).toHaveBeenCalledWith(
|
|
67
|
-
expect.stringContaining("🚀 Starting deployment..."),
|
|
68
|
-
);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("should update progress with build status", () => {
|
|
72
|
-
reporter = new RichProgressReporter(mockConsole);
|
|
73
|
-
|
|
74
|
-
const progress = {
|
|
75
|
-
phase: "build",
|
|
76
|
-
message: "Bundling files...",
|
|
77
|
-
progress: 0.5,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// Just verify no errors
|
|
81
|
-
expect(() => reporter.updateProgress(progress)).not.toThrow();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("should show success summary", () => {
|
|
85
|
-
// Test just the summary method
|
|
86
|
-
const reporter = new RichProgressReporter(mockConsole);
|
|
87
|
-
// Access private method for testing
|
|
88
|
-
const reporterWithPrivate = reporter as unknown as {
|
|
89
|
-
showSuccessSummary(): void;
|
|
90
|
-
};
|
|
91
|
-
reporterWithPrivate.showSuccessSummary();
|
|
92
|
-
|
|
93
|
-
expect(mockConsole.success).toHaveBeenCalledWith(
|
|
94
|
-
expect.stringContaining("✅ Deployment completed successfully!"),
|
|
95
|
-
);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("should show failure message", () => {
|
|
99
|
-
// Create a minimal test that doesn't rely on bars
|
|
100
|
-
const reporter = new RichProgressReporter(mockConsole);
|
|
101
|
-
// Access private properties for testing
|
|
102
|
-
const reporterWithPrivate = reporter as unknown as {
|
|
103
|
-
multibar: { stop: () => void };
|
|
104
|
-
bars: Map<string, unknown>;
|
|
105
|
-
};
|
|
106
|
-
reporterWithPrivate.multibar = { stop: vi.fn() };
|
|
107
|
-
reporterWithPrivate.bars = new Map(); // Empty map
|
|
108
|
-
|
|
109
|
-
reporter.stop(false);
|
|
110
|
-
|
|
111
|
-
expect(mockConsole.error).toHaveBeenCalledWith(
|
|
112
|
-
expect.stringContaining("❌ Deployment failed"),
|
|
113
|
-
);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("should display build stats when available", () => {
|
|
117
|
-
const reporter = new RichProgressReporter(mockConsole);
|
|
118
|
-
|
|
119
|
-
// Set stats directly for testing
|
|
120
|
-
const reporterWithPrivate = reporter as unknown as {
|
|
121
|
-
stats: { bundleSize?: number; gzippedSize?: number };
|
|
122
|
-
};
|
|
123
|
-
reporterWithPrivate.stats = {
|
|
124
|
-
bundleSize: 150000,
|
|
125
|
-
gzippedSize: 50000,
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// Call the summary method
|
|
129
|
-
const reporterWithSummary = reporter as unknown as {
|
|
130
|
-
showSuccessSummary(): void;
|
|
131
|
-
};
|
|
132
|
-
reporterWithSummary.showSuccessSummary();
|
|
133
|
-
|
|
134
|
-
expect(mockConsole.info).toHaveBeenCalledWith(
|
|
135
|
-
expect.stringContaining("📊 Build Stats:"),
|
|
136
|
-
);
|
|
137
|
-
expect(mockConsole.info).toHaveBeenCalledWith(
|
|
138
|
-
expect.stringContaining("Bundle size: 146.5KB"),
|
|
139
|
-
);
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe("SimpleProgressReporter", () => {
|
|
144
|
-
let reporter: SimpleProgressReporter;
|
|
145
|
-
|
|
146
|
-
beforeEach(() => {
|
|
147
|
-
reporter = new SimpleProgressReporter("Testing...");
|
|
148
|
-
vi.clearAllMocks();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("should start spinner", () => {
|
|
152
|
-
reporter.start();
|
|
153
|
-
|
|
154
|
-
// Spinner is mocked, verify no errors
|
|
155
|
-
expect(() => reporter.start()).not.toThrow();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it("should update message", () => {
|
|
159
|
-
reporter.start();
|
|
160
|
-
reporter.update("New message");
|
|
161
|
-
|
|
162
|
-
// Update is mocked, verify no errors
|
|
163
|
-
expect(() => reporter.update("test")).not.toThrow();
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("should succeed with message", () => {
|
|
167
|
-
reporter.start();
|
|
168
|
-
reporter.succeed("Success!");
|
|
169
|
-
|
|
170
|
-
// Success is mocked, verify no errors
|
|
171
|
-
expect(() => reporter.succeed()).not.toThrow();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("should fail with message", () => {
|
|
175
|
-
reporter.start();
|
|
176
|
-
reporter.fail("Failed!");
|
|
177
|
-
|
|
178
|
-
// Fail is mocked, verify no errors
|
|
179
|
-
expect(() => reporter.fail()).not.toThrow();
|
|
180
|
-
});
|
|
181
|
-
});
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import { createMockConsole } from "@tests/mocks";
|
|
2
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import {
|
|
5
|
-
displayErrors,
|
|
6
|
-
formatZodError,
|
|
7
|
-
suggestCommand,
|
|
8
|
-
} from "../validation-error-formatter";
|
|
9
|
-
|
|
10
|
-
describe("validation-error-formatter", () => {
|
|
11
|
-
let mockConsole: ReturnType<typeof createMockConsole>;
|
|
12
|
-
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
mockConsole = createMockConsole();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe("formatZodError", () => {
|
|
18
|
-
it("should format required field errors", () => {
|
|
19
|
-
const schema = z.object({
|
|
20
|
-
name: z.string(),
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const result = schema.safeParse({});
|
|
24
|
-
if (!result.success) {
|
|
25
|
-
const errors = formatZodError(result.error);
|
|
26
|
-
expect(errors).toHaveLength(1);
|
|
27
|
-
expect(errors[0]).toEqual({
|
|
28
|
-
field: "name",
|
|
29
|
-
message: "Name is required",
|
|
30
|
-
});
|
|
31
|
-
} else {
|
|
32
|
-
throw new Error("Expected validation to fail");
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("should format regex validation errors", () => {
|
|
37
|
-
const schema = z.object({
|
|
38
|
-
name: z.string().regex(/^[a-z-]+$/),
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const result = schema.safeParse({ name: "TestComponent" });
|
|
42
|
-
if (!result.success) {
|
|
43
|
-
const errors = formatZodError(result.error);
|
|
44
|
-
expect(errors).toHaveLength(1);
|
|
45
|
-
expect(errors[0]).toEqual({
|
|
46
|
-
field: "name",
|
|
47
|
-
message:
|
|
48
|
-
"Must be lowercase with hyphens only (e.g., 'my-component', 'header-nav')",
|
|
49
|
-
});
|
|
50
|
-
} else {
|
|
51
|
-
throw new Error("Expected validation to fail");
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("should format enum errors with suggestions", () => {
|
|
56
|
-
const schema = z.object({
|
|
57
|
-
slot: z.enum(["header", "main", "footer"]),
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const result = schema.safeParse({ slot: "invalid" });
|
|
61
|
-
if (!result.success) {
|
|
62
|
-
const errors = formatZodError(result.error);
|
|
63
|
-
expect(errors).toHaveLength(1);
|
|
64
|
-
expect(errors[0]).toEqual({
|
|
65
|
-
field: "slot",
|
|
66
|
-
message: "Invalid Slot",
|
|
67
|
-
suggestedValue: "Valid options: header, main, footer",
|
|
68
|
-
});
|
|
69
|
-
} else {
|
|
70
|
-
throw new Error("Expected validation to fail");
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("should include examples when context is provided", () => {
|
|
75
|
-
const schema = z.object({
|
|
76
|
-
name: z.string(),
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const result = schema.safeParse({});
|
|
80
|
-
if (!result.success) {
|
|
81
|
-
const errors = formatZodError(result.error, "component");
|
|
82
|
-
expect(errors[0].examples).toBeDefined();
|
|
83
|
-
expect(errors[0].examples?.[0]).toEqual({
|
|
84
|
-
description: "Create a header component",
|
|
85
|
-
command: "ollieshop component create --name header-nav --slot header",
|
|
86
|
-
});
|
|
87
|
-
} else {
|
|
88
|
-
throw new Error("Expected validation to fail");
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe("displayErrors", () => {
|
|
94
|
-
it("should display formatted errors", () => {
|
|
95
|
-
const errors = [
|
|
96
|
-
{
|
|
97
|
-
field: "name",
|
|
98
|
-
message: "Must be lowercase with hyphens",
|
|
99
|
-
suggestedValue: "Valid format: my-component",
|
|
100
|
-
},
|
|
101
|
-
];
|
|
102
|
-
|
|
103
|
-
displayErrors(mockConsole, errors);
|
|
104
|
-
|
|
105
|
-
expect(mockConsole.error).toHaveBeenCalledWith("❌ Validation failed:");
|
|
106
|
-
expect(mockConsole.error).toHaveBeenCalledWith(
|
|
107
|
-
" • name: Must be lowercase with hyphens",
|
|
108
|
-
);
|
|
109
|
-
expect(mockConsole.log).toHaveBeenCalledWith(
|
|
110
|
-
" 💡 Valid format: my-component",
|
|
111
|
-
);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("should display examples when provided", () => {
|
|
115
|
-
const errors = [
|
|
116
|
-
{
|
|
117
|
-
field: "name",
|
|
118
|
-
message: "Invalid format",
|
|
119
|
-
examples: [
|
|
120
|
-
{
|
|
121
|
-
description: "Create header",
|
|
122
|
-
command: "ollieshop component create --name header",
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
},
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
displayErrors(mockConsole, errors);
|
|
129
|
-
|
|
130
|
-
expect(mockConsole.log).toHaveBeenCalledWith(" 📝 Examples:");
|
|
131
|
-
expect(mockConsole.log).toHaveBeenCalledWith(" Create header:");
|
|
132
|
-
expect(mockConsole.log).toHaveBeenCalledWith(
|
|
133
|
-
" $ ollieshop component create --name header",
|
|
134
|
-
);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it("should show help command when provided", () => {
|
|
138
|
-
const errors = [{ field: "name", message: "Required" }];
|
|
139
|
-
|
|
140
|
-
displayErrors(mockConsole, errors, "component create");
|
|
141
|
-
|
|
142
|
-
expect(mockConsole.info).toHaveBeenCalledWith(
|
|
143
|
-
"💡 For more help, run: ollieshop component create --help",
|
|
144
|
-
);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
describe("suggestCommand", () => {
|
|
149
|
-
const commands = ["create", "list", "deploy", "validate", "build"];
|
|
150
|
-
|
|
151
|
-
it("should return exact matches", () => {
|
|
152
|
-
expect(suggestCommand("create", commands)).toBe("create");
|
|
153
|
-
expect(suggestCommand("CREATE", commands)).toBe("create");
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("should return commands that start with input", () => {
|
|
157
|
-
expect(suggestCommand("cr", commands)).toBe("create");
|
|
158
|
-
expect(suggestCommand("val", commands)).toBe("validate");
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("should return commands that contain input", () => {
|
|
162
|
-
expect(suggestCommand("ploy", commands)).toBe("deploy");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it("should return close matches with small typos", () => {
|
|
166
|
-
expect(suggestCommand("craete", commands)).toBe("create");
|
|
167
|
-
expect(suggestCommand("buidl", commands)).toBe("build");
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("should return undefined for no matches", () => {
|
|
171
|
-
expect(suggestCommand("xyz", commands)).toBeUndefined();
|
|
172
|
-
expect(suggestCommand("random", commands)).toBeUndefined();
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
});
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
validateComponentName,
|
|
4
|
-
validateFunctionName,
|
|
5
|
-
validatePriority,
|
|
6
|
-
validateSemver,
|
|
7
|
-
validateUrl,
|
|
8
|
-
validateVersionName,
|
|
9
|
-
} from "../validation-helpers";
|
|
10
|
-
|
|
11
|
-
describe("Validation Helpers", () => {
|
|
12
|
-
describe("validateComponentName", () => {
|
|
13
|
-
it("should accept valid component names", () => {
|
|
14
|
-
expect(validateComponentName("my-component")).toBe(true);
|
|
15
|
-
expect(validateComponentName("header-123")).toBe(true);
|
|
16
|
-
expect(validateComponentName("a")).toBe(true);
|
|
17
|
-
expect(validateComponentName("component-with-many-parts")).toBe(true);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("should reject invalid component names", () => {
|
|
21
|
-
expect(validateComponentName("My Component")).toContain(
|
|
22
|
-
"Component names must be lowercase with hyphens only",
|
|
23
|
-
);
|
|
24
|
-
expect(validateComponentName("UPPERCASE")).toContain(
|
|
25
|
-
"Component names must be lowercase with hyphens only",
|
|
26
|
-
);
|
|
27
|
-
expect(validateComponentName("special@char")).toContain(
|
|
28
|
-
"Component names must be lowercase with hyphens only",
|
|
29
|
-
);
|
|
30
|
-
expect(validateComponentName("")).toContain("required");
|
|
31
|
-
expect(validateComponentName("_underscore")).toContain(
|
|
32
|
-
"Component names must be lowercase with hyphens only",
|
|
33
|
-
);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe("validateFunctionName", () => {
|
|
38
|
-
it("should accept valid function names", () => {
|
|
39
|
-
expect(validateFunctionName("my-function")).toBe(true);
|
|
40
|
-
expect(validateFunctionName("auth-handler")).toBe(true);
|
|
41
|
-
expect(validateFunctionName("process-order-v2")).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("should reject invalid function names", () => {
|
|
45
|
-
expect(validateFunctionName("My Function")).toContain(
|
|
46
|
-
"Function names must be lowercase with hyphens only",
|
|
47
|
-
);
|
|
48
|
-
expect(validateFunctionName("function!")).toContain(
|
|
49
|
-
"Function names must be lowercase with hyphens only",
|
|
50
|
-
);
|
|
51
|
-
expect(validateFunctionName("")).toContain("required");
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe("validateVersionName", () => {
|
|
56
|
-
it("should accept valid version names", () => {
|
|
57
|
-
expect(validateVersionName("Production")).toBe(true);
|
|
58
|
-
expect(validateVersionName("v1.0")).toBe(true);
|
|
59
|
-
expect(validateVersionName("Development Branch")).toBe(true);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("should reject invalid version names", () => {
|
|
63
|
-
expect(validateVersionName("")).toContain("required");
|
|
64
|
-
expect(validateVersionName("a".repeat(101))).toContain("too long");
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
describe("validatePriority", () => {
|
|
69
|
-
it("should accept valid priorities", () => {
|
|
70
|
-
expect(validatePriority(0)).toBe(true);
|
|
71
|
-
expect(validatePriority(50)).toBe(true);
|
|
72
|
-
expect(validatePriority(100)).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("should reject invalid priorities", () => {
|
|
76
|
-
expect(validatePriority(-1)).toContain("between 0 and 100");
|
|
77
|
-
expect(validatePriority(101)).toContain("between 0 and 100");
|
|
78
|
-
expect(validatePriority(50.5)).toContain("whole number");
|
|
79
|
-
expect(validatePriority(Number.NaN)).toContain(
|
|
80
|
-
"Expected number, received nan",
|
|
81
|
-
);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
describe("validateUrl", () => {
|
|
86
|
-
it("should accept valid URLs", () => {
|
|
87
|
-
expect(validateUrl("https://example.com")).toBe(true);
|
|
88
|
-
expect(validateUrl("http://localhost:3000")).toBe(true);
|
|
89
|
-
expect(validateUrl("https://api.example.com/v1/endpoint")).toBe(true);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it("should accept valid URL patterns", () => {
|
|
93
|
-
expect(validateUrl("/api/v1/*")).toBe(true);
|
|
94
|
-
expect(validateUrl("/*")).toBe(true);
|
|
95
|
-
expect(validateUrl("/users/:id")).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("should reject invalid URLs", () => {
|
|
99
|
-
expect(validateUrl("")).toContain("required");
|
|
100
|
-
expect(validateUrl("not a url")).toContain(
|
|
101
|
-
"URL must be a valid URL or path pattern",
|
|
102
|
-
);
|
|
103
|
-
expect(validateUrl("ftp://example.com")).toContain(
|
|
104
|
-
"URL must be a valid URL or path pattern",
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe("validateSemver", () => {
|
|
110
|
-
it("should accept valid semantic versions", () => {
|
|
111
|
-
expect(validateSemver("1.0.0")).toBe(true);
|
|
112
|
-
expect(validateSemver("2.1.3")).toBe(true);
|
|
113
|
-
expect(validateSemver("10.20.30")).toBe(true);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("should reject invalid versions", () => {
|
|
117
|
-
expect(validateSemver("1.0")).toContain("semantic versioning");
|
|
118
|
-
expect(validateSemver("v1.0.0")).toContain("semantic versioning");
|
|
119
|
-
expect(validateSemver("1.0.0-alpha")).toContain("semantic versioning");
|
|
120
|
-
expect(validateSemver("")).toContain(
|
|
121
|
-
"Version must follow semantic versioning format",
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import type { BuildProgress } from "@ollie-shop/core";
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import cliProgress from "cli-progress";
|
|
4
|
-
|
|
5
|
-
const PHASE_DISPLAY_NAMES: Record<string, string> = {
|
|
6
|
-
SUBMITTED: "Queued",
|
|
7
|
-
PROVISIONING: "Setting up",
|
|
8
|
-
DOWNLOAD_SOURCE: "Downloading",
|
|
9
|
-
INSTALL: "Installing deps",
|
|
10
|
-
PRE_BUILD: "Preparing",
|
|
11
|
-
BUILD: "Building",
|
|
12
|
-
POST_BUILD: "Finalizing",
|
|
13
|
-
UPLOAD_ARTIFACTS: "Uploading",
|
|
14
|
-
FINALIZING: "Completing",
|
|
15
|
-
COMPLETED: "Done",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export class CLIProgressReporter {
|
|
19
|
-
private bar: cliProgress.SingleBar;
|
|
20
|
-
|
|
21
|
-
constructor() {
|
|
22
|
-
this.bar = new cliProgress.SingleBar({
|
|
23
|
-
format: `Building |${chalk.cyan("{bar}")}| {percentage}% | {phase} | ETA: {eta_formatted}`,
|
|
24
|
-
barCompleteChar: "\u2588",
|
|
25
|
-
barIncompleteChar: "\u2591",
|
|
26
|
-
hideCursor: true,
|
|
27
|
-
stopOnComplete: false,
|
|
28
|
-
clearOnComplete: false,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
start(): void {
|
|
33
|
-
this.bar.start(100, 0, {
|
|
34
|
-
phase: "Initializing",
|
|
35
|
-
eta_formatted: "calculating...",
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
updateProgress(progress: BuildProgress): void {
|
|
40
|
-
const phaseDisplay = this.formatPhase(progress.status);
|
|
41
|
-
const etaFormatted = this.formatETA(0); // ETA not available in BuildProgress
|
|
42
|
-
|
|
43
|
-
this.bar.update(progress.percentComplete, {
|
|
44
|
-
phase: phaseDisplay,
|
|
45
|
-
eta_formatted: etaFormatted,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
stop(success: boolean): void {
|
|
50
|
-
this.bar.stop();
|
|
51
|
-
|
|
52
|
-
// Clear the line and show final status
|
|
53
|
-
process.stdout.clearLine(0);
|
|
54
|
-
process.stdout.cursorTo(0);
|
|
55
|
-
|
|
56
|
-
if (success) {
|
|
57
|
-
console.log(chalk.green("✓ Build completed successfully!"));
|
|
58
|
-
} else {
|
|
59
|
-
console.log(chalk.red("✗ Build failed"));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private formatPhase(phase: string): string {
|
|
64
|
-
return PHASE_DISPLAY_NAMES[phase] || phase;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private formatETA(seconds: number): string {
|
|
68
|
-
if (seconds <= 0) {
|
|
69
|
-
return "completing...";
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (seconds < 60) {
|
|
73
|
-
return `${Math.ceil(seconds)}s`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const minutes = Math.floor(seconds / 60);
|
|
77
|
-
const remainingSeconds = Math.ceil(seconds % 60);
|
|
78
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function createCLIProgressReporter(): CLIProgressReporter {
|
|
83
|
-
return new CLIProgressReporter();
|
|
84
|
-
}
|