@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.
Files changed (79) hide show
  1. package/.turbo/turbo-build.log +6 -9
  2. package/CHANGELOG.md +27 -0
  3. package/dist/index.js +993 -3956
  4. package/package.json +15 -37
  5. package/src/README.md +126 -0
  6. package/src/cli.tsx +45 -0
  7. package/src/commands/help.tsx +79 -0
  8. package/src/commands/login.tsx +92 -0
  9. package/src/commands/start.tsx +411 -0
  10. package/src/index.tsx +8 -0
  11. package/src/utils/auth.ts +218 -21
  12. package/src/utils/bundle.ts +177 -0
  13. package/src/utils/config.ts +123 -0
  14. package/src/utils/esbuild.ts +541 -0
  15. package/tsconfig.json +10 -15
  16. package/tsup.config.ts +7 -7
  17. package/CLAUDE_CLI.md +0 -265
  18. package/README.md +0 -711
  19. package/__tests__/mocks/console.ts +0 -22
  20. package/__tests__/mocks/core.ts +0 -137
  21. package/__tests__/mocks/index.ts +0 -4
  22. package/__tests__/mocks/inquirer.ts +0 -16
  23. package/__tests__/mocks/progress.ts +0 -19
  24. package/dist/index.d.ts +0 -1
  25. package/src/__tests__/helpers/cli-test-helper.ts +0 -281
  26. package/src/__tests__/mocks/index.ts +0 -142
  27. package/src/actions/component.actions.ts +0 -278
  28. package/src/actions/function.actions.ts +0 -220
  29. package/src/actions/project.actions.ts +0 -131
  30. package/src/actions/version.actions.ts +0 -233
  31. package/src/commands/__tests__/component-validation.test.ts +0 -250
  32. package/src/commands/__tests__/component.test.ts +0 -318
  33. package/src/commands/__tests__/function-validation.test.ts +0 -220
  34. package/src/commands/__tests__/function.test.ts +0 -286
  35. package/src/commands/__tests__/store-version-validation.test.ts +0 -414
  36. package/src/commands/__tests__/store-version.test.ts +0 -402
  37. package/src/commands/component.ts +0 -178
  38. package/src/commands/docs.ts +0 -24
  39. package/src/commands/function.ts +0 -201
  40. package/src/commands/help.ts +0 -18
  41. package/src/commands/index.ts +0 -27
  42. package/src/commands/login.ts +0 -267
  43. package/src/commands/project.ts +0 -107
  44. package/src/commands/store-version.ts +0 -242
  45. package/src/commands/version.ts +0 -51
  46. package/src/commands/whoami.ts +0 -46
  47. package/src/index.ts +0 -116
  48. package/src/prompts/component.prompts.ts +0 -94
  49. package/src/prompts/function.prompts.ts +0 -168
  50. package/src/schemas/command.schema.ts +0 -644
  51. package/src/types/index.ts +0 -183
  52. package/src/utils/__tests__/command-parser.test.ts +0 -159
  53. package/src/utils/__tests__/command-suggestions.test.ts +0 -185
  54. package/src/utils/__tests__/console.test.ts +0 -192
  55. package/src/utils/__tests__/context-detector.test.ts +0 -258
  56. package/src/utils/__tests__/enhanced-error-handler.test.ts +0 -137
  57. package/src/utils/__tests__/error-handler.test.ts +0 -107
  58. package/src/utils/__tests__/rich-progress.test.ts +0 -181
  59. package/src/utils/__tests__/validation-error-formatter.test.ts +0 -175
  60. package/src/utils/__tests__/validation-helpers.test.ts +0 -125
  61. package/src/utils/cli-progress-reporter.ts +0 -84
  62. package/src/utils/command-builder.ts +0 -390
  63. package/src/utils/command-helpers.ts +0 -83
  64. package/src/utils/command-parser.ts +0 -245
  65. package/src/utils/command-suggestions.ts +0 -176
  66. package/src/utils/console.ts +0 -320
  67. package/src/utils/constants.ts +0 -39
  68. package/src/utils/context-detector.ts +0 -177
  69. package/src/utils/deploy-helpers.ts +0 -357
  70. package/src/utils/enhanced-error-handler.ts +0 -264
  71. package/src/utils/error-handler.ts +0 -60
  72. package/src/utils/errors.ts +0 -256
  73. package/src/utils/interactive-builder.ts +0 -325
  74. package/src/utils/rich-progress.ts +0 -331
  75. package/src/utils/store.ts +0 -23
  76. package/src/utils/validation-error-formatter.ts +0 -337
  77. package/src/utils/validation-helpers.ts +0 -325
  78. package/vitest.config.ts +0 -35
  79. 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
- }