@ollie-shop/cli 0.1.3 → 0.3.0

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 (181) hide show
  1. package/.turbo/turbo-build.log +2 -11
  2. package/CHANGELOG.md +17 -5
  3. package/CLAUDE_CLI.md +265 -0
  4. package/README.md +704 -8
  5. package/__tests__/mocks/console.ts +22 -0
  6. package/__tests__/mocks/core.ts +137 -0
  7. package/__tests__/mocks/index.ts +4 -0
  8. package/__tests__/mocks/inquirer.ts +16 -0
  9. package/__tests__/mocks/progress.ts +19 -0
  10. package/dist/__tests__/helpers/cli-test-helper.d.ts +89 -0
  11. package/dist/__tests__/helpers/cli-test-helper.d.ts.map +1 -0
  12. package/dist/__tests__/helpers/cli-test-helper.js +220 -0
  13. package/dist/__tests__/mocks/index.d.ts +69 -0
  14. package/dist/__tests__/mocks/index.d.ts.map +1 -0
  15. package/dist/__tests__/mocks/index.js +77 -0
  16. package/dist/actions/component.actions.d.ts +14 -0
  17. package/dist/actions/component.actions.d.ts.map +1 -0
  18. package/dist/actions/component.actions.js +273 -0
  19. package/dist/actions/function.actions.d.ts +15 -0
  20. package/dist/actions/function.actions.d.ts.map +1 -0
  21. package/dist/actions/function.actions.js +254 -0
  22. package/dist/actions/project.actions.d.ts +17 -0
  23. package/dist/actions/project.actions.d.ts.map +1 -0
  24. package/dist/actions/project.actions.js +97 -0
  25. package/dist/actions/version.actions.d.ts +19 -0
  26. package/dist/actions/version.actions.d.ts.map +1 -0
  27. package/dist/actions/version.actions.js +216 -0
  28. package/dist/commands/component.d.ts +3 -0
  29. package/dist/commands/component.d.ts.map +1 -0
  30. package/dist/commands/component.js +192 -0
  31. package/dist/commands/docs.d.ts +3 -0
  32. package/dist/commands/docs.d.ts.map +1 -0
  33. package/dist/commands/docs.js +16 -0
  34. package/dist/commands/function.d.ts +3 -0
  35. package/dist/commands/function.d.ts.map +1 -0
  36. package/dist/commands/function.js +243 -0
  37. package/dist/commands/help.d.ts +3 -0
  38. package/dist/commands/help.d.ts.map +1 -0
  39. package/dist/commands/help.js +20 -0
  40. package/dist/commands/index.d.ts +3 -0
  41. package/dist/commands/index.d.ts.map +1 -0
  42. package/dist/commands/index.js +26 -0
  43. package/dist/commands/login.d.ts +3 -0
  44. package/dist/commands/login.d.ts.map +1 -0
  45. package/dist/commands/login.js +175 -0
  46. package/dist/commands/project.d.ts +3 -0
  47. package/dist/commands/project.d.ts.map +1 -0
  48. package/dist/commands/project.js +78 -0
  49. package/dist/commands/store-version.d.ts +3 -0
  50. package/dist/commands/store-version.d.ts.map +1 -0
  51. package/dist/commands/store-version.js +241 -0
  52. package/dist/commands/version.d.ts +3 -0
  53. package/dist/commands/version.d.ts.map +1 -0
  54. package/dist/commands/version.js +46 -0
  55. package/dist/commands/whoami.d.ts +3 -0
  56. package/dist/commands/whoami.d.ts.map +1 -0
  57. package/dist/commands/whoami.js +41 -0
  58. package/dist/index.d.ts +3 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +93 -226
  61. package/dist/prompts/component.prompts.d.ts +14 -0
  62. package/dist/prompts/component.prompts.d.ts.map +1 -0
  63. package/dist/prompts/component.prompts.js +75 -0
  64. package/dist/prompts/function.prompts.d.ts +21 -0
  65. package/dist/prompts/function.prompts.d.ts.map +1 -0
  66. package/dist/prompts/function.prompts.js +127 -0
  67. package/dist/schemas/command.schema.d.ts +516 -0
  68. package/dist/schemas/command.schema.d.ts.map +1 -0
  69. package/dist/schemas/command.schema.js +267 -0
  70. package/dist/types/index.d.ts +147 -0
  71. package/dist/types/index.d.ts.map +1 -0
  72. package/dist/types/index.js +18 -0
  73. package/dist/utils/auth.d.ts +4 -0
  74. package/dist/utils/auth.d.ts.map +1 -0
  75. package/dist/utils/auth.js +26 -0
  76. package/dist/utils/cli-progress-reporter.d.ts +12 -0
  77. package/dist/utils/cli-progress-reporter.d.ts.map +1 -0
  78. package/dist/utils/cli-progress-reporter.js +77 -0
  79. package/dist/utils/command-builder.d.ts +22 -0
  80. package/dist/utils/command-builder.d.ts.map +1 -0
  81. package/dist/utils/command-builder.js +268 -0
  82. package/dist/utils/command-helpers.d.ts +19 -0
  83. package/dist/utils/command-helpers.d.ts.map +1 -0
  84. package/dist/utils/command-helpers.js +79 -0
  85. package/dist/utils/command-parser.d.ts +146 -0
  86. package/dist/utils/command-parser.d.ts.map +1 -0
  87. package/dist/utils/command-parser.js +179 -0
  88. package/dist/utils/command-suggestions.d.ts +35 -0
  89. package/dist/utils/command-suggestions.d.ts.map +1 -0
  90. package/dist/utils/command-suggestions.js +152 -0
  91. package/dist/utils/console.d.ts +44 -0
  92. package/dist/utils/console.d.ts.map +1 -0
  93. package/dist/utils/console.js +233 -0
  94. package/dist/utils/constants.d.ts +8 -0
  95. package/dist/utils/constants.d.ts.map +1 -0
  96. package/dist/utils/constants.js +10 -0
  97. package/dist/utils/context-detector.d.ts +12 -0
  98. package/dist/utils/context-detector.d.ts.map +1 -0
  99. package/dist/utils/context-detector.js +155 -0
  100. package/dist/utils/enhanced-error-handler.d.ts +47 -0
  101. package/dist/utils/enhanced-error-handler.d.ts.map +1 -0
  102. package/dist/utils/enhanced-error-handler.js +221 -0
  103. package/dist/utils/error-handler.d.ts +3 -0
  104. package/dist/utils/error-handler.d.ts.map +1 -0
  105. package/dist/utils/error-handler.js +55 -0
  106. package/dist/utils/errors.d.ts +44 -0
  107. package/dist/utils/errors.d.ts.map +1 -0
  108. package/dist/utils/errors.js +76 -0
  109. package/dist/utils/interactive-builder.d.ts +22 -0
  110. package/dist/utils/interactive-builder.d.ts.map +1 -0
  111. package/dist/utils/interactive-builder.js +246 -0
  112. package/dist/utils/rich-progress.d.ts +59 -0
  113. package/dist/utils/rich-progress.d.ts.map +1 -0
  114. package/dist/utils/rich-progress.js +234 -0
  115. package/dist/utils/store.d.ts +11 -0
  116. package/dist/utils/store.d.ts.map +1 -0
  117. package/dist/utils/store.js +19 -0
  118. package/dist/utils/validation-error-formatter.d.ts +25 -0
  119. package/dist/utils/validation-error-formatter.d.ts.map +1 -0
  120. package/dist/utils/validation-error-formatter.js +258 -0
  121. package/dist/utils/validation-helpers.d.ts +60 -0
  122. package/dist/utils/validation-helpers.d.ts.map +1 -0
  123. package/dist/utils/validation-helpers.js +152 -0
  124. package/package.json +44 -9
  125. package/src/__tests__/helpers/cli-test-helper.ts +281 -0
  126. package/src/__tests__/mocks/index.ts +142 -0
  127. package/src/actions/component.actions.ts +334 -0
  128. package/src/actions/function.actions.ts +313 -0
  129. package/src/actions/project.actions.ts +126 -0
  130. package/src/actions/version.actions.ts +233 -0
  131. package/src/commands/__tests__/component-validation.test.ts +250 -0
  132. package/src/commands/__tests__/component.test.ts +321 -0
  133. package/src/commands/__tests__/function-validation.test.ts +220 -0
  134. package/src/commands/__tests__/function.test.ts +286 -0
  135. package/src/commands/__tests__/store-version-validation.test.ts +414 -0
  136. package/src/commands/__tests__/store-version.test.ts +405 -0
  137. package/src/commands/__tests__/version.test.ts +71 -0
  138. package/src/commands/component.ts +188 -0
  139. package/src/commands/docs.ts +24 -0
  140. package/src/commands/function.ts +252 -0
  141. package/src/commands/help.ts +18 -0
  142. package/src/commands/index.ts +21 -7
  143. package/src/commands/login.ts +19 -79
  144. package/src/commands/project.ts +107 -0
  145. package/src/commands/store-version.ts +242 -0
  146. package/src/commands/version.ts +51 -0
  147. package/src/commands/whoami.ts +46 -0
  148. package/src/index.ts +110 -15
  149. package/src/prompts/component.prompts.ts +94 -0
  150. package/src/prompts/function.prompts.ts +168 -0
  151. package/src/schemas/command.schema.ts +354 -0
  152. package/src/types/index.ts +183 -0
  153. package/src/utils/__tests__/command-parser.test.ts +159 -0
  154. package/src/utils/__tests__/command-suggestions.test.ts +185 -0
  155. package/src/utils/__tests__/console.test.ts +192 -0
  156. package/src/utils/__tests__/context-detector.test.ts +258 -0
  157. package/src/utils/__tests__/enhanced-error-handler.test.ts +137 -0
  158. package/src/utils/__tests__/error-handler.test.ts +107 -0
  159. package/src/utils/__tests__/rich-progress.test.ts +170 -0
  160. package/src/utils/__tests__/validation-error-formatter.test.ts +175 -0
  161. package/src/utils/__tests__/validation-helpers.test.ts +125 -0
  162. package/src/utils/auth.ts +41 -0
  163. package/src/utils/cli-progress-reporter.ts +84 -0
  164. package/src/utils/command-builder.ts +390 -0
  165. package/src/utils/command-helpers.ts +83 -0
  166. package/src/utils/command-parser.ts +250 -0
  167. package/src/utils/command-suggestions.ts +176 -0
  168. package/src/utils/console.ts +291 -0
  169. package/src/utils/context-detector.ts +177 -0
  170. package/src/utils/enhanced-error-handler.ts +264 -0
  171. package/src/utils/error-handler.ts +60 -0
  172. package/src/utils/errors.ts +125 -0
  173. package/src/utils/interactive-builder.ts +271 -0
  174. package/src/utils/rich-progress.ts +320 -0
  175. package/src/utils/store.ts +23 -0
  176. package/src/utils/validation-error-formatter.ts +337 -0
  177. package/src/utils/validation-helpers.ts +192 -0
  178. package/tsconfig.json +13 -7
  179. package/vitest.config.ts +28 -0
  180. package/vitest.setup.ts +29 -0
  181. package/tsup.config.ts +0 -15
@@ -0,0 +1,252 @@
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
+ COMMON_OPTIONS.wait,
162
+ ],
163
+ schema: FunctionDeployOptionsSchema,
164
+ examples: [
165
+ {
166
+ description: "Deploy current directory",
167
+ command: "ollieshop function deploy",
168
+ },
169
+ {
170
+ description: "Deploy specific function and wait",
171
+ command: "ollieshop function deploy --path ./my-function --wait",
172
+ },
173
+ {
174
+ description: "Deploy with function ID",
175
+ command: "ollieshop function deploy --id func_123abc",
176
+ },
177
+ ],
178
+ handler: async (options: FunctionDeployOptions, console) => {
179
+ // Map id to functionId if provided
180
+ const deployOptions = {
181
+ path: options.path || process.cwd(),
182
+ functionId: options.id || options.functionId,
183
+ wait: options.wait,
184
+ };
185
+ await functionActions.deployFunction(deployOptions, console);
186
+ },
187
+ });
188
+
189
+ // List functions command
190
+ buildCommand(cmd, {
191
+ name: "list",
192
+ description: "List all functions",
193
+ aliases: ["ls"],
194
+ examples: [
195
+ {
196
+ description: "List all functions",
197
+ command: "ollieshop function list",
198
+ },
199
+ ],
200
+ handler: async (_, console) => {
201
+ await functionActions.listFunctions(console);
202
+ },
203
+ });
204
+
205
+ // Deploy status command
206
+ cmd
207
+ .command("deploy-status <buildId>")
208
+ .description("Check the status of a function deployment")
209
+ .action(async (buildId) => {
210
+ try {
211
+ const { getBuildStatus } = await import("@ollie-shop/core");
212
+ const { console: cliConsole } = await import("../utils/console.js");
213
+ const spinner = cliConsole.spinner("Checking deployment status...");
214
+
215
+ const build = await getBuildStatus(buildId);
216
+
217
+ if (!build) {
218
+ spinner.fail("Build not found");
219
+ return;
220
+ }
221
+
222
+ spinner.succeed();
223
+
224
+ cliConsole.info("Deployment Status:");
225
+ cliConsole.log(` Build ID: ${build.id}`);
226
+ cliConsole.log(` Status: ${build.status}`);
227
+ cliConsole.log(
228
+ ` Started: ${new Date(build.startTime).toLocaleString()}`,
229
+ );
230
+
231
+ if (build.endTime) {
232
+ cliConsole.log(
233
+ ` Completed: ${new Date(build.endTime).toLocaleString()}`,
234
+ );
235
+ }
236
+
237
+ if (build.deploymentUrl) {
238
+ cliConsole.success(`\nFunction URL: ${build.deploymentUrl}`);
239
+ }
240
+
241
+ if (build.error) {
242
+ cliConsole.error(`\nError: ${build.error}`);
243
+ }
244
+ } catch (error) {
245
+ const { console: cliConsole } = await import("../utils/console.js");
246
+ cliConsole.error(
247
+ `Error: ${error instanceof Error ? error.message : String(error)}`,
248
+ );
249
+ process.exit(1);
250
+ }
251
+ });
252
+ }
@@ -0,0 +1,18 @@
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,13 +1,27 @@
1
- import type { Command } from "commander";
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";
2
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";
3
11
 
4
- /**
5
- * Register all CLI commands with the program
6
- * @param program The commander program instance
7
- */
8
12
  export function registerCommands(program: Command): void {
9
- // Register individual commands
13
+ // Register simple commands that don't need heavy services
14
+ configureDocsCommand(program);
15
+ configureHelpCommand(program);
10
16
  configureLoginCommand(program);
17
+ configureWhoamiCommand(program);
11
18
 
12
- // Add more commands here as they are implemented
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);
13
27
  }
@@ -4,23 +4,13 @@ import type { IncomingMessage, ServerResponse } from "node:http";
4
4
  import { createServer } from "node:http";
5
5
  import { homedir } from "node:os";
6
6
  import path from "node:path";
7
- import type { Command } from "commander";
7
+ import type { Command } from "@commander-js/extra-typings";
8
+ import { isNodeError } from "../types";
9
+ import { console as cliConsole } from "../utils/console";
8
10
 
9
- /**
10
- * Default port for the local callback server
11
- */
12
11
  const DEFAULT_CALLBACK_PORT = 7777;
13
-
14
- /**
15
- * Default Ollie Shop authorization endpoint
16
- */
17
12
  const AUTH_ENDPOINT = "https://admin.ollie.shop/auth/login";
18
13
 
19
- /**
20
- * Configure the login command
21
- * @param program The commander program instance
22
- * @returns The configured command
23
- */
24
14
  export function configureLoginCommand(program: Command): Command {
25
15
  return program
26
16
  .command("login")
@@ -31,24 +21,22 @@ export function configureLoginCommand(program: Command): Command {
31
21
  DEFAULT_CALLBACK_PORT.toString(),
32
22
  )
33
23
  .option("--auth-url <url>", "Custom authorization URL", AUTH_ENDPOINT)
34
- .action(async (options) => {
35
- console.log("šŸ” Initiating Ollie Shop login flow...");
24
+ .action(async (options: { port: string; authUrl: string }) => {
25
+ cliConsole.info("šŸ” Initiating Ollie Shop login flow...");
36
26
 
37
27
  try {
38
28
  const token = await startWebAuthFlow(options);
39
29
 
40
30
  if (token) {
41
31
  await saveCredentials(token);
42
-
43
- console.log("āœ… Successfully logged in!");
44
- // Process continues normally after login
32
+ cliConsole.success("āœ… Successfully logged in!");
45
33
  return;
46
34
  }
47
35
 
48
- console.error("āŒ Authentication failed. Please try again.");
36
+ cliConsole.error("āŒ Authentication failed. Please try again.");
49
37
  process.exit(1);
50
38
  } catch (error) {
51
- console.error(
39
+ cliConsole.error(
52
40
  `āŒ Login failed: ${error instanceof Error ? error.message : "Unknown error"}`,
53
41
  );
54
42
  process.exit(1);
@@ -56,20 +44,12 @@ export function configureLoginCommand(program: Command): Command {
56
44
  });
57
45
  }
58
46
 
59
- /**
60
- * Authentication response type
61
- */
62
47
  type AuthToken = {
63
48
  accessToken: string;
64
49
  refreshToken: string;
65
50
  expiresAt: string;
66
51
  };
67
52
 
68
- /**
69
- * Handle the callback endpoint for web authentication flow
70
- *
71
- * This now expects to receive tokens directly from the Next.js app with Supabase auth
72
- */
73
53
  async function handleAuthCallback(
74
54
  req: IncomingMessage,
75
55
  res: ServerResponse,
@@ -77,7 +57,6 @@ async function handleAuthCallback(
77
57
  resolve: (token: AuthToken | null) => void,
78
58
  reject: (err: Error) => void,
79
59
  ): Promise<void> {
80
- // TypeScript doesn't know the socket will always have localPort, so we cast
81
60
  const socket = req.socket as { localPort?: number };
82
61
  const url = new URL(
83
62
  req.url || "/",
@@ -85,7 +64,6 @@ async function handleAuthCallback(
85
64
  );
86
65
  const params = url.searchParams;
87
66
 
88
- // Verify state from query parameters to prevent CSRF attacks
89
67
  const returnedState = params.get("state");
90
68
  if (returnedState !== state) {
91
69
  sendErrorResponse(
@@ -98,7 +76,6 @@ async function handleAuthCallback(
98
76
  return;
99
77
  }
100
78
 
101
- // Parse the request body to get the access_token from form data
102
79
  let formData = "";
103
80
  req.on("data", (chunk) => {
104
81
  formData += chunk.toString();
@@ -110,7 +87,6 @@ async function handleAuthCallback(
110
87
  });
111
88
  });
112
89
 
113
- // Parse form data
114
90
  const formParams = new URLSearchParams(formData);
115
91
  const accessToken = formParams.get("access_token");
116
92
  const refreshToken = formParams.get("refresh_token") || "";
@@ -130,17 +106,13 @@ async function handleAuthCallback(
130
106
  }
131
107
 
132
108
  try {
133
- // Create token object
134
109
  const token: AuthToken = {
135
110
  accessToken,
136
111
  refreshToken,
137
112
  expiresAt,
138
113
  };
139
114
 
140
- // Send success response to browser
141
115
  sendSuccessResponse(res);
142
-
143
- // Resolve the promise with the token
144
116
  resolve(token);
145
117
  } catch (error) {
146
118
  const errorMessage =
@@ -155,9 +127,6 @@ async function handleAuthCallback(
155
127
  }
156
128
  }
157
129
 
158
- /**
159
- * Send HTML error response
160
- */
161
130
  function sendErrorResponse(
162
131
  res: ServerResponse,
163
132
  statusCode: number,
@@ -168,9 +137,6 @@ function sendErrorResponse(
168
137
  res.end(`<h1>${title}</h1><p>${message}</p>`);
169
138
  }
170
139
 
171
- /**
172
- * Send HTML success response
173
- */
174
140
  function sendSuccessResponse(res: ServerResponse): void {
175
141
  res.writeHead(200, { "Content-Type": "text/html" });
176
142
  res.end(
@@ -178,9 +144,6 @@ function sendSuccessResponse(res: ServerResponse): void {
178
144
  );
179
145
  }
180
146
 
181
- /**
182
- * Send HTML waiting page response
183
- */
184
147
  function sendWaitingResponse(res: ServerResponse): void {
185
148
  res.writeHead(200, { "Content-Type": "text/html" });
186
149
  res.end(
@@ -188,9 +151,6 @@ function sendWaitingResponse(res: ServerResponse): void {
188
151
  );
189
152
  }
190
153
 
191
- /**
192
- * Start the web-based authentication flow
193
- */
194
154
  async function startWebAuthFlow(options: {
195
155
  port: string;
196
156
  authUrl: string;
@@ -200,35 +160,29 @@ async function startWebAuthFlow(options: {
200
160
  const baseUrl = options.authUrl;
201
161
 
202
162
  return new Promise<AuthToken | null>((resolve, reject) => {
203
- // Create a local server to receive the callback
204
163
  const server = createServer(async (req, res) => {
205
164
  try {
206
- // Parse the URL and query parameters
207
165
  const url = new URL(req.url || "/", `http://localhost:${port}`);
208
166
 
209
- // Check if this is a callback from the Next.js app
210
167
  if (url.pathname === "/callback") {
211
168
  await handleAuthCallback(
212
169
  req,
213
170
  res,
214
171
  state,
215
172
  (token) => {
216
- // Wrap the resolve to ensure server is closed
217
173
  server.close(() => {
218
- console.log("šŸ” Local authentication server closed");
174
+ cliConsole.debug("šŸ” Local authentication server closed");
219
175
  resolve(token);
220
176
  });
221
177
  },
222
178
  (error) => {
223
- // Wrap the reject to ensure server is closed
224
179
  server.close(() => {
225
- console.log("šŸ” Local authentication server closed");
180
+ cliConsole.debug("šŸ” Local authentication server closed");
226
181
  reject(error);
227
182
  });
228
183
  },
229
184
  );
230
185
  } else {
231
- // Serve a simple page for any other path
232
186
  sendWaitingResponse(res);
233
187
  }
234
188
  } catch (error) {
@@ -236,35 +190,29 @@ async function startWebAuthFlow(options: {
236
190
  error instanceof Error ? error.message : "Unknown error";
237
191
  sendErrorResponse(res, 500, "Server Error", errorMessage);
238
192
  server.close(() => {
239
- console.log("šŸ” Local authentication server closed");
193
+ cliConsole.debug("šŸ” Local authentication server closed");
240
194
  reject(new Error(errorMessage));
241
195
  });
242
196
  }
243
197
  });
244
198
 
245
- // Start the server
246
199
  server.listen(port, async () => {
247
- // Build the URL to your Next.js app with necessary parameters
248
200
  const redirectUrl = `http://localhost:${port}/callback`;
249
-
250
201
  const authUrl = new URL(baseUrl);
251
202
 
252
203
  authUrl.searchParams.set("flow", "cli");
253
204
  authUrl.searchParams.set("state", state);
254
205
  authUrl.searchParams.set("redirect_to", redirectUrl);
255
206
 
256
- console.log("\nšŸ”’ Please authenticate in your browser...\n");
257
- console.log(`Opening: ${authUrl}\n`);
207
+ cliConsole.info("\nšŸ”’ Please authenticate in your browser...\n");
208
+ cliConsole.info(`Opening: ${authUrl}\n`);
258
209
 
259
210
  const open = (await import("open")).default;
260
-
261
- // Open the browser with the authorization URL
262
211
  open(authUrl.toString());
263
212
  });
264
213
 
265
- // Handle server errors
266
214
  server.on("error", (err: Error) => {
267
- if ((err as NodeJS.ErrnoException).code === "EADDRINUSE") {
215
+ if (isNodeError(err) && err.code === "EADDRINUSE") {
268
216
  reject(
269
217
  new Error(
270
218
  `Port ${port} is already in use. Please specify a different port using the --port option.`,
@@ -276,44 +224,37 @@ async function startWebAuthFlow(options: {
276
224
  server.close();
277
225
  });
278
226
 
279
- // Set a timeout to prevent hanging indefinitely
280
227
  const timeoutId = setTimeout(
281
228
  () => {
282
229
  server.close(() => {
283
- console.log("šŸ” Local authentication server closed due to timeout");
230
+ cliConsole.debug(
231
+ "šŸ” Local authentication server closed due to timeout",
232
+ );
284
233
  reject(new Error("Authentication timed out. Please try again."));
285
234
  });
286
235
  },
287
236
  5 * 60 * 1000,
288
- ); // 5 minutes timeout
237
+ );
289
238
 
290
- // Clean up the timeout if the server closes for other reasons
291
239
  server.on("close", () => {
292
240
  clearTimeout(timeoutId);
293
241
  });
294
242
  });
295
243
  }
296
244
 
297
- /**
298
- * Save authentication credentials locally
299
- *
300
- * @todo Use a more secure storage method like OS keychain
301
- */
302
245
  async function saveCredentials(token: {
303
246
  accessToken: string;
304
247
  refreshToken: string;
305
248
  expiresAt: string;
306
249
  }) {
307
- console.log("Saving credentials...");
250
+ cliConsole.debug("Saving credentials...");
308
251
 
309
252
  const configDir = path.join(homedir(), ".ollie-shop");
310
253
  const credentialsPath = path.join(configDir, "credentials.json");
311
254
 
312
- // Create the .ollie-shop directory if it doesn't exist
313
255
  try {
314
256
  await fs.mkdir(configDir, { recursive: true });
315
257
  } catch (error) {
316
- // Ignore error if directory already exists
317
258
  if (
318
259
  !(error instanceof Error && "code" in error && error.code === "EEXIST")
319
260
  ) {
@@ -322,6 +263,5 @@ async function saveCredentials(token: {
322
263
  }
323
264
 
324
265
  await fs.writeFile(credentialsPath, JSON.stringify(token, null, 2));
325
-
326
266
  return true;
327
267
  }