@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,201 +0,0 @@
1
- import type { Command } from "@commander-js/extra-typings";
2
- import * as functionActions from "../actions/function.actions";
3
- import {
4
- type FunctionCreateOptions,
5
- FunctionCreateOptionsSchema,
6
- type FunctionDeployOptions,
7
- FunctionDeployOptionsSchema,
8
- FunctionTestOptionsSchema,
9
- FunctionValidateOptionsSchema,
10
- } from "../schemas/command.schema";
11
- import { buildCommand, buildCommandGroup } from "../utils/command-builder";
12
- import { COMMON_OPTIONS } from "../utils/command-parser";
13
-
14
- export function registerFunctionCommands(program: Command): void {
15
- const cmd = buildCommandGroup(
16
- program,
17
- "function",
18
- "Manage Ollie Shop functions",
19
- ["func"],
20
- );
21
-
22
- // Create function command
23
- buildCommand(cmd, {
24
- name: "create",
25
- description: "Create a new function",
26
- options: [
27
- COMMON_OPTIONS.functionName,
28
- COMMON_OPTIONS.functionInvocation,
29
- COMMON_OPTIONS.priority,
30
- {
31
- flags: "--on-error <strategy>",
32
- description: "Error handling strategy (throw|skip)",
33
- defaultValue: "throw",
34
- },
35
- {
36
- flags: "-d, --description <description>",
37
- description: "Function description",
38
- },
39
- {
40
- flags: "--template <template>",
41
- description: "Function template to use",
42
- },
43
- COMMON_OPTIONS.tests,
44
- COMMON_OPTIONS.noTests,
45
- ],
46
- schema: FunctionCreateOptionsSchema,
47
- examples: [
48
- {
49
- description: "Create a function to validate orders",
50
- command:
51
- "ollieshop function create --name validate-order --event order --timing before",
52
- },
53
- {
54
- description: "Create a discount function for cart",
55
- command:
56
- "ollieshop function create --name apply-discount --event cart --description 'Apply bulk discounts'",
57
- },
58
- {
59
- description: "Create a JavaScript function without tests",
60
- command:
61
- "ollieshop function create --name check-inventory --language javascript --no-tests",
62
- },
63
- ],
64
- handler: async (options, console) => {
65
- await functionActions.createFunction(
66
- options as FunctionCreateOptions,
67
- console,
68
- );
69
- },
70
- });
71
-
72
- // Validate function command
73
- buildCommand(cmd, {
74
- name: "validate",
75
- description: "Validate a function",
76
- options: [COMMON_OPTIONS.path],
77
- schema: FunctionValidateOptionsSchema,
78
- examples: [
79
- {
80
- description: "Validate current directory",
81
- command: "ollieshop function validate",
82
- },
83
- {
84
- description: "Validate specific function",
85
- command:
86
- "ollieshop function validate --path ./functions/validate-order",
87
- },
88
- ],
89
- handler: async (options, console) => {
90
- await functionActions.validateFunction(
91
- {
92
- path: options.path || process.cwd(),
93
- strict: options.strict ?? false,
94
- fix: options.fix ?? false,
95
- },
96
- console,
97
- );
98
- },
99
- });
100
-
101
- // Test function command
102
- buildCommand(cmd, {
103
- name: "test",
104
- description: "Test a function locally",
105
- options: [
106
- COMMON_OPTIONS.path,
107
- {
108
- flags: "-w, --watch",
109
- description: "Watch for changes",
110
- defaultValue: false,
111
- },
112
- {
113
- flags: "--payload <json>",
114
- description: "Test payload (JSON string)",
115
- },
116
- {
117
- flags: "--timeout <ms>",
118
- description: "Timeout in milliseconds",
119
- },
120
- ],
121
- schema: FunctionTestOptionsSchema,
122
- examples: [
123
- {
124
- description: "Test function in current directory",
125
- command: "ollieshop function test",
126
- },
127
- {
128
- description: "Test with custom payload",
129
- command: `ollieshop function test --payload '{"items": [{"id": "123", "quantity": 2}]}'`,
130
- },
131
- {
132
- description: "Test in watch mode",
133
- command: "ollieshop function test --watch",
134
- },
135
- ],
136
- handler: async (options, console) => {
137
- await functionActions.testFunction(
138
- {
139
- path: options.path || process.cwd(),
140
- payload: options.payload,
141
- timeout: options.timeout,
142
- watch: options.watch ?? false,
143
- coverage: options.coverage ?? false,
144
- },
145
- console,
146
- );
147
- },
148
- });
149
-
150
- // Deploy function command
151
- buildCommand(cmd, {
152
- name: "deploy",
153
- description: "Build and deploy a function to cloud",
154
- options: [
155
- COMMON_OPTIONS.path,
156
- {
157
- flags: "--id <id>",
158
- description: "Function ID for deployment",
159
- required: true,
160
- },
161
- ],
162
- schema: FunctionDeployOptionsSchema,
163
- examples: [
164
- {
165
- description: "Deploy current directory",
166
- command: "ollieshop function deploy --id func_123abc",
167
- },
168
- {
169
- description: "Deploy specific function",
170
- command:
171
- "ollieshop function deploy --path ./my-function --id func_123abc",
172
- },
173
- ],
174
- handler: async (options: FunctionDeployOptions, console) => {
175
- // Map id to functionId if provided
176
- const deployOptions = {
177
- path: options.path || process.cwd(),
178
- functionId: options.id || options.functionId,
179
- };
180
- await functionActions.deployFunction(deployOptions, console);
181
- },
182
- });
183
-
184
- // List functions command
185
- buildCommand(cmd, {
186
- name: "list",
187
- description: "List all functions",
188
- aliases: ["ls"],
189
- examples: [
190
- {
191
- description: "List all functions",
192
- command: "ollieshop function list",
193
- },
194
- ],
195
- handler: async (_, console) => {
196
- await functionActions.listFunctions(console);
197
- },
198
- });
199
-
200
- // deploy-status command removed - no longer needed since deployments always wait for completion
201
- }
@@ -1,18 +0,0 @@
1
- import type { Command } from "@commander-js/extra-typings";
2
- import { console as cliConsole } from "../utils/console";
3
-
4
- export function configureHelpCommand(program: Command): void {
5
- program
6
- .command("help [command]")
7
- .description("Display help for a specific command or list all commands")
8
- .action((cmdName: string | undefined) => {
9
- if (cmdName) {
10
- // For specific command help, show the program help with the command
11
- cliConsole.info(`\nHelp for command: ${cmdName}`);
12
- cliConsole.info("Use 'ollieshop <command> --help' for detailed help\n");
13
- } else {
14
- // Show general help
15
- program.help();
16
- }
17
- });
18
- }
@@ -1,27 +0,0 @@
1
- import type { Command } from "@commander-js/extra-typings";
2
- import { registerComponentCommands } from "./component";
3
- import { configureDocsCommand } from "./docs";
4
- import { registerFunctionCommands } from "./function";
5
- import { configureHelpCommand } from "./help";
6
- import { configureLoginCommand } from "./login";
7
- import { registerProjectCommands } from "./project";
8
- import { registerStoreVersionCommands } from "./store-version";
9
- import { configureVersionCommand } from "./version";
10
- import { configureWhoamiCommand } from "./whoami";
11
-
12
- export function registerCommands(program: Command): void {
13
- // Register simple commands that don't need heavy services
14
- configureDocsCommand(program);
15
- configureHelpCommand(program);
16
- configureLoginCommand(program);
17
- configureWhoamiCommand(program);
18
-
19
- // Register new commands
20
- registerStoreVersionCommands(program);
21
- configureVersionCommand(program);
22
-
23
- // Register complex commands with lazy service loading
24
- registerComponentCommands(program);
25
- registerFunctionCommands(program);
26
- registerProjectCommands(program);
27
- }
@@ -1,267 +0,0 @@
1
- import { randomBytes } from "node:crypto";
2
- import fs from "node:fs/promises";
3
- import type { IncomingMessage, ServerResponse } from "node:http";
4
- import { createServer } from "node:http";
5
- import { homedir } from "node:os";
6
- import path from "node:path";
7
- import type { Command } from "@commander-js/extra-typings";
8
- import { isNodeError } from "../types";
9
- import { console as cliConsole } from "../utils/console";
10
-
11
- const DEFAULT_CALLBACK_PORT = 7777;
12
- const AUTH_ENDPOINT = "https://admin.ollie.shop/auth/login";
13
-
14
- export function configureLoginCommand(program: Command): Command {
15
- return program
16
- .command("login")
17
- .description("Log in to your Ollie Shop account")
18
- .option(
19
- "-p, --port <port>",
20
- "Port to use for the local callback server",
21
- DEFAULT_CALLBACK_PORT.toString(),
22
- )
23
- .option("--auth-url <url>", "Custom authorization URL", AUTH_ENDPOINT)
24
- .action(async (options: { port: string; authUrl: string }) => {
25
- cliConsole.info("🔐 Initiating Ollie Shop login flow...");
26
-
27
- try {
28
- const token = await startWebAuthFlow(options);
29
-
30
- if (token) {
31
- await saveCredentials(token);
32
- cliConsole.success("✅ Successfully logged in!");
33
- return;
34
- }
35
-
36
- cliConsole.error("❌ Authentication failed. Please try again.");
37
- process.exit(1);
38
- } catch (error) {
39
- cliConsole.error(
40
- `❌ Login failed: ${error instanceof Error ? error.message : "Unknown error"}`,
41
- );
42
- process.exit(1);
43
- }
44
- });
45
- }
46
-
47
- type AuthToken = {
48
- accessToken: string;
49
- refreshToken: string;
50
- expiresAt: string;
51
- };
52
-
53
- async function handleAuthCallback(
54
- req: IncomingMessage,
55
- res: ServerResponse,
56
- state: string,
57
- resolve: (token: AuthToken | null) => void,
58
- reject: (err: Error) => void,
59
- ): Promise<void> {
60
- const socket = req.socket as { localPort?: number };
61
- const url = new URL(
62
- req.url || "/",
63
- `http://localhost:${socket.localPort || 3000}`,
64
- );
65
- const params = url.searchParams;
66
-
67
- const returnedState = params.get("state");
68
- if (returnedState !== state) {
69
- sendErrorResponse(
70
- res,
71
- 400,
72
- "Invalid state parameter",
73
- "Authentication failed. Please try again.",
74
- );
75
- reject(new Error("Invalid state parameter"));
76
- return;
77
- }
78
-
79
- let formData = "";
80
- req.on("data", (chunk) => {
81
- formData += chunk.toString();
82
- });
83
-
84
- await new Promise<void>((formResolve) => {
85
- req.on("end", () => {
86
- formResolve();
87
- });
88
- });
89
-
90
- const formParams = new URLSearchParams(formData);
91
- const accessToken = formParams.get("access_token");
92
- const refreshToken = formParams.get("refresh_token") || "";
93
- const expiresAt =
94
- formParams.get("expires_at") ||
95
- new Date(Date.now() + 3600000).toISOString();
96
-
97
- if (!accessToken) {
98
- sendErrorResponse(
99
- res,
100
- 400,
101
- "Missing authentication token",
102
- "Authentication failed. Please try again.",
103
- );
104
- reject(new Error("Missing authentication token"));
105
- return;
106
- }
107
-
108
- try {
109
- const token: AuthToken = {
110
- accessToken,
111
- refreshToken,
112
- expiresAt,
113
- };
114
-
115
- sendSuccessResponse(res);
116
- resolve(token);
117
- } catch (error) {
118
- const errorMessage =
119
- error instanceof Error ? error.message : "Unknown error";
120
- sendErrorResponse(
121
- res,
122
- 500,
123
- "Authentication failed",
124
- `Error: ${errorMessage}`,
125
- );
126
- reject(new Error(errorMessage));
127
- }
128
- }
129
-
130
- function sendErrorResponse(
131
- res: ServerResponse,
132
- statusCode: number,
133
- title: string,
134
- message: string,
135
- ): void {
136
- res.writeHead(statusCode, { "Content-Type": "text/html" });
137
- res.end(`<h1>${title}</h1><p>${message}</p>`);
138
- }
139
-
140
- function sendSuccessResponse(res: ServerResponse): void {
141
- res.writeHead(200, { "Content-Type": "text/html" });
142
- res.end(
143
- "<h1>Authentication successful!</h1><p>You can now close this window and return to the CLI.</p>",
144
- );
145
- }
146
-
147
- function sendWaitingResponse(res: ServerResponse): void {
148
- res.writeHead(200, { "Content-Type": "text/html" });
149
- res.end(
150
- "<h1>Ollie Shop CLI Authentication</h1><p>Waiting for authentication response...</p>",
151
- );
152
- }
153
-
154
- async function startWebAuthFlow(options: {
155
- port: string;
156
- authUrl: string;
157
- }): Promise<AuthToken | null> {
158
- const state = randomBytes(16).toString("hex");
159
- const port = Number.parseInt(options.port, 10);
160
- const baseUrl = options.authUrl;
161
-
162
- return new Promise<AuthToken | null>((resolve, reject) => {
163
- const server = createServer(async (req, res) => {
164
- try {
165
- const url = new URL(req.url || "/", `http://localhost:${port}`);
166
-
167
- if (url.pathname === "/callback") {
168
- await handleAuthCallback(
169
- req,
170
- res,
171
- state,
172
- (token) => {
173
- server.close(() => {
174
- cliConsole.debug("🔐 Local authentication server closed");
175
- resolve(token);
176
- });
177
- },
178
- (error) => {
179
- server.close(() => {
180
- cliConsole.debug("🔐 Local authentication server closed");
181
- reject(error);
182
- });
183
- },
184
- );
185
- } else {
186
- sendWaitingResponse(res);
187
- }
188
- } catch (error) {
189
- const errorMessage =
190
- error instanceof Error ? error.message : "Unknown error";
191
- sendErrorResponse(res, 500, "Server Error", errorMessage);
192
- server.close(() => {
193
- cliConsole.debug("🔐 Local authentication server closed");
194
- reject(new Error(errorMessage));
195
- });
196
- }
197
- });
198
-
199
- server.listen(port, async () => {
200
- const redirectUrl = `http://localhost:${port}/callback`;
201
- const authUrl = new URL(baseUrl);
202
-
203
- authUrl.searchParams.set("flow", "cli");
204
- authUrl.searchParams.set("state", state);
205
- authUrl.searchParams.set("redirect_to", redirectUrl);
206
-
207
- cliConsole.info("\n🔒 Please authenticate in your browser...\n");
208
- cliConsole.info(`Opening: ${authUrl}\n`);
209
-
210
- const open = (await import("open")).default;
211
- open(authUrl.toString());
212
- });
213
-
214
- server.on("error", (err: Error) => {
215
- if (isNodeError(err) && err.code === "EADDRINUSE") {
216
- reject(
217
- new Error(
218
- `Port ${port} is already in use. Please specify a different port using the --port option.`,
219
- ),
220
- );
221
- } else {
222
- reject(err);
223
- }
224
- server.close();
225
- });
226
-
227
- const timeoutId = setTimeout(
228
- () => {
229
- server.close(() => {
230
- cliConsole.debug(
231
- "🔐 Local authentication server closed due to timeout",
232
- );
233
- reject(new Error("Authentication timed out. Please try again."));
234
- });
235
- },
236
- 5 * 60 * 1000,
237
- );
238
-
239
- server.on("close", () => {
240
- clearTimeout(timeoutId);
241
- });
242
- });
243
- }
244
-
245
- async function saveCredentials(token: {
246
- accessToken: string;
247
- refreshToken: string;
248
- expiresAt: string;
249
- }) {
250
- cliConsole.debug("Saving credentials...");
251
-
252
- const configDir = path.join(homedir(), ".ollie-shop");
253
- const credentialsPath = path.join(configDir, "credentials.json");
254
-
255
- try {
256
- await fs.mkdir(configDir, { recursive: true });
257
- } catch (error) {
258
- if (
259
- !(error instanceof Error && "code" in error && error.code === "EEXIST")
260
- ) {
261
- throw error;
262
- }
263
- }
264
-
265
- await fs.writeFile(credentialsPath, JSON.stringify(token, null, 2));
266
- return true;
267
- }
@@ -1,107 +0,0 @@
1
- import type { Command } from "@commander-js/extra-typings";
2
- import {
3
- buildProject,
4
- initProject,
5
- startDevServer,
6
- validateProject,
7
- } from "../actions/project.actions";
8
- import { console as cliConsole } from "../utils/console";
9
-
10
- export function registerProjectCommands(program: Command): void {
11
- const projectCommand = program
12
- .command("project")
13
- .description("Project management commands");
14
-
15
- projectCommand
16
- .command("init")
17
- .description("Initialize a new project")
18
- .option("-n, --name <name>", "Project name")
19
- .option("-t, --template <template>", "Project template")
20
- .action(async (options) => {
21
- try {
22
- await initProject(
23
- {
24
- name: options.name,
25
- template:
26
- (options.template as
27
- | "default"
28
- | "grocery"
29
- | "sales"
30
- | undefined) || "default",
31
- git: true,
32
- install: true,
33
- },
34
- cliConsole,
35
- );
36
- } catch (error) {
37
- cliConsole.error(
38
- error instanceof Error ? error.message : "An error occurred",
39
- );
40
- process.exit(1);
41
- }
42
- });
43
-
44
- projectCommand
45
- .command("build")
46
- .description("Build project")
47
- .option("-p, --path <path>", "Project path")
48
- .option("--watch", "Watch for changes")
49
- .action(async (options) => {
50
- try {
51
- await buildProject(
52
- {
53
- path: options.path,
54
- watch: options.watch ?? false,
55
- },
56
- cliConsole,
57
- );
58
- } catch (error) {
59
- cliConsole.error(
60
- error instanceof Error ? error.message : "An error occurred",
61
- );
62
- process.exit(1);
63
- }
64
- });
65
-
66
- projectCommand
67
- .command("dev")
68
- .description("Start development server")
69
- .option("-p, --port <port>", "Port number", Number.parseInt)
70
- .option("--open", "Open browser")
71
- .action(async (options) => {
72
- try {
73
- await startDevServer(
74
- {
75
- port: options.port ?? 3000,
76
- open: options.open ?? false,
77
- },
78
- cliConsole,
79
- );
80
- } catch (error) {
81
- cliConsole.error(
82
- error instanceof Error ? error.message : "An error occurred",
83
- );
84
- process.exit(1);
85
- }
86
- });
87
-
88
- projectCommand
89
- .command("validate")
90
- .description("Validate project")
91
- .option("-p, --path <path>", "Project path")
92
- .action(async (options) => {
93
- try {
94
- await validateProject(
95
- {
96
- path: options.path,
97
- },
98
- cliConsole,
99
- );
100
- } catch (error) {
101
- cliConsole.error(
102
- error instanceof Error ? error.message : "An error occurred",
103
- );
104
- process.exit(1);
105
- }
106
- });
107
- }