spaps 0.2.3 ā 0.2.5
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/bin/spaps.js +22 -31
- package/package.json +1 -1
- package/src/error-handler.js +271 -0
- package/src/help-system.js +487 -0
package/bin/spaps.js
CHANGED
|
@@ -12,6 +12,8 @@ const { program } = require('commander');
|
|
|
12
12
|
const { spawn } = require('child_process');
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const fs = require('fs');
|
|
15
|
+
const { handleError } = require('../src/error-handler');
|
|
16
|
+
const { showInteractiveHelp, showQuickHelp } = require('../src/help-system');
|
|
15
17
|
|
|
16
18
|
const version = require('../package.json').version;
|
|
17
19
|
|
|
@@ -86,37 +88,7 @@ program
|
|
|
86
88
|
});
|
|
87
89
|
|
|
88
90
|
} catch (error) {
|
|
89
|
-
|
|
90
|
-
console.log(JSON.stringify({
|
|
91
|
-
success: false,
|
|
92
|
-
command: 'local',
|
|
93
|
-
error: {
|
|
94
|
-
message: error.message,
|
|
95
|
-
code: error.code,
|
|
96
|
-
suggestion: error.code === 'EADDRINUSE' ?
|
|
97
|
-
'Try a different port with --port option' :
|
|
98
|
-
'Check error message for details'
|
|
99
|
-
}
|
|
100
|
-
}));
|
|
101
|
-
} else {
|
|
102
|
-
console.error(chalk.red('ā Failed to start server:'), error.message);
|
|
103
|
-
|
|
104
|
-
if (error.code === 'MODULE_NOT_FOUND') {
|
|
105
|
-
console.log(chalk.yellow('\nš” Installing dependencies...'));
|
|
106
|
-
const { execSync } = require('child_process');
|
|
107
|
-
try {
|
|
108
|
-
execSync('npm install express cors', {
|
|
109
|
-
cwd: path.join(__dirname, '..'),
|
|
110
|
-
stdio: 'inherit'
|
|
111
|
-
});
|
|
112
|
-
console.log(chalk.green('ā
Dependencies installed! Please run the command again.'));
|
|
113
|
-
} catch (installError) {
|
|
114
|
-
console.error(chalk.red('Failed to install dependencies. Please run: npm install express cors'));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
process.exit(1);
|
|
91
|
+
handleError(error, { port: options.port, command: 'local' }, { json: isJson });
|
|
120
92
|
}
|
|
121
93
|
});
|
|
122
94
|
|
|
@@ -205,6 +177,23 @@ program
|
|
|
205
177
|
console.log(chalk.yellow(`š§ 'spaps types' coming in v0.4.0!`));
|
|
206
178
|
});
|
|
207
179
|
|
|
180
|
+
// Help command - Interactive help system
|
|
181
|
+
program
|
|
182
|
+
.command('help')
|
|
183
|
+
.description('Show help and guides')
|
|
184
|
+
.option('-i, --interactive', 'Interactive help mode')
|
|
185
|
+
.option('-q, --quick', 'Quick reference')
|
|
186
|
+
.action(async (options) => {
|
|
187
|
+
if (options.interactive) {
|
|
188
|
+
await showInteractiveHelp();
|
|
189
|
+
} else if (options.quick) {
|
|
190
|
+
showQuickHelp();
|
|
191
|
+
} else {
|
|
192
|
+
// Default to quick help
|
|
193
|
+
showQuickHelp();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
208
197
|
// Help command enhancement
|
|
209
198
|
program.on('--help', () => {
|
|
210
199
|
console.log();
|
|
@@ -221,6 +210,8 @@ program.on('--help', () => {
|
|
|
221
210
|
if (!process.argv.slice(2).length) {
|
|
222
211
|
console.log(logo);
|
|
223
212
|
program.outputHelp();
|
|
213
|
+
console.log();
|
|
214
|
+
console.log(chalk.yellow('š” Try: npx spaps help --interactive'));
|
|
224
215
|
}
|
|
225
216
|
|
|
226
217
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPAPS CLI Error Handler
|
|
3
|
+
* Provides verbose error messages with exact fix commands
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
|
|
8
|
+
const ERROR_FIXES = {
|
|
9
|
+
// Port errors
|
|
10
|
+
EADDRINUSE: (error, context = {}) => ({
|
|
11
|
+
title: 'Port Already In Use',
|
|
12
|
+
description: `Port ${context.port || 3300} is being used by another process`,
|
|
13
|
+
causes: [
|
|
14
|
+
'Another SPAPS server is already running',
|
|
15
|
+
'Another application is using this port',
|
|
16
|
+
'A previous SPAPS process didn\'t shut down cleanly'
|
|
17
|
+
],
|
|
18
|
+
fixes: [
|
|
19
|
+
{
|
|
20
|
+
command: `npx spaps local --port ${(parseInt(context.port || 3300) + 1)}`,
|
|
21
|
+
description: 'Use a different port'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
command: `lsof -ti:${context.port || 3300} | xargs kill -9`,
|
|
25
|
+
description: 'Kill the process using this port (macOS/Linux)',
|
|
26
|
+
platform: ['darwin', 'linux']
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
command: `netstat -ano | findstr :${context.port || 3300}`,
|
|
30
|
+
description: 'Find process using this port (Windows)',
|
|
31
|
+
platform: ['win32']
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
command: 'npx spaps local --port 0',
|
|
35
|
+
description: 'Let the system choose an available port'
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}),
|
|
39
|
+
|
|
40
|
+
// Module errors
|
|
41
|
+
MODULE_NOT_FOUND: (error, context = {}) => {
|
|
42
|
+
const module = error.message.match(/Cannot find module '(.+?)'/)?.[1] || 'unknown';
|
|
43
|
+
return {
|
|
44
|
+
title: 'Missing Dependencies',
|
|
45
|
+
description: `Required module "${module}" is not installed`,
|
|
46
|
+
causes: [
|
|
47
|
+
'Dependencies were not installed',
|
|
48
|
+
'Package installation was interrupted',
|
|
49
|
+
'Node modules were deleted or corrupted'
|
|
50
|
+
],
|
|
51
|
+
fixes: [
|
|
52
|
+
{
|
|
53
|
+
command: 'npm install',
|
|
54
|
+
description: 'Install all dependencies'
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
command: `npm install ${module}`,
|
|
58
|
+
description: `Install missing module "${module}"`
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
command: 'rm -rf node_modules package-lock.json && npm install',
|
|
62
|
+
description: 'Clean reinstall all dependencies'
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Permission errors
|
|
69
|
+
EACCES: (error, context = {}) => ({
|
|
70
|
+
title: 'Permission Denied',
|
|
71
|
+
description: 'Insufficient permissions to perform this operation',
|
|
72
|
+
causes: [
|
|
73
|
+
'Trying to bind to a privileged port (< 1024)',
|
|
74
|
+
'File system permissions issue',
|
|
75
|
+
'Node.js doesn\'t have necessary permissions'
|
|
76
|
+
],
|
|
77
|
+
fixes: [
|
|
78
|
+
{
|
|
79
|
+
command: 'npx spaps local --port 3000',
|
|
80
|
+
description: 'Use a non-privileged port (>= 1024)'
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
command: `sudo npx spaps ${process.argv.slice(2).join(' ')}`,
|
|
84
|
+
description: 'Run with elevated permissions (use with caution)',
|
|
85
|
+
warning: true
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
command: 'ls -la ./',
|
|
89
|
+
description: 'Check file permissions in current directory'
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}),
|
|
93
|
+
|
|
94
|
+
// Network errors
|
|
95
|
+
ECONNREFUSED: (error, context = {}) => ({
|
|
96
|
+
title: 'Connection Refused',
|
|
97
|
+
description: `Cannot connect to ${context.url || 'server'}`,
|
|
98
|
+
causes: [
|
|
99
|
+
'Server is not running',
|
|
100
|
+
'Server is running on a different port',
|
|
101
|
+
'Firewall is blocking the connection'
|
|
102
|
+
],
|
|
103
|
+
fixes: [
|
|
104
|
+
{
|
|
105
|
+
command: 'npx spaps local',
|
|
106
|
+
description: 'Start the local SPAPS server'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
command: 'curl http://localhost:3300/health',
|
|
110
|
+
description: 'Check if server is responding'
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
command: 'netstat -an | grep LISTEN',
|
|
114
|
+
description: 'List all listening ports'
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}),
|
|
118
|
+
|
|
119
|
+
// File system errors
|
|
120
|
+
ENOENT: (error, context = {}) => {
|
|
121
|
+
const file = error.path || context.file || 'file';
|
|
122
|
+
return {
|
|
123
|
+
title: 'File or Directory Not Found',
|
|
124
|
+
description: `Cannot find "${file}"`,
|
|
125
|
+
causes: [
|
|
126
|
+
'File does not exist',
|
|
127
|
+
'Wrong directory',
|
|
128
|
+
'File was deleted or moved'
|
|
129
|
+
],
|
|
130
|
+
fixes: [
|
|
131
|
+
{
|
|
132
|
+
command: 'pwd',
|
|
133
|
+
description: 'Check current directory'
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
command: 'ls -la',
|
|
137
|
+
description: 'List files in current directory'
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
command: 'npx spaps init',
|
|
141
|
+
description: 'Initialize SPAPS in this directory'
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
// Generic errors
|
|
148
|
+
DEFAULT: (error, context = {}) => ({
|
|
149
|
+
title: 'Unexpected Error',
|
|
150
|
+
description: error.message || 'An unexpected error occurred',
|
|
151
|
+
causes: [
|
|
152
|
+
'Unknown issue',
|
|
153
|
+
'Corrupted installation',
|
|
154
|
+
'System-specific problem'
|
|
155
|
+
],
|
|
156
|
+
fixes: [
|
|
157
|
+
{
|
|
158
|
+
command: 'npm cache clean --force && npm install',
|
|
159
|
+
description: 'Clear npm cache and reinstall'
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
command: 'node --version && npm --version',
|
|
163
|
+
description: 'Check Node.js and npm versions'
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
command: 'npx spaps@latest --version',
|
|
167
|
+
description: 'Check SPAPS version'
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
command: 'npm update spaps',
|
|
171
|
+
description: 'Update SPAPS to latest version'
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
})
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
function formatError(error, context = {}) {
|
|
178
|
+
const errorCode = error.code || 'DEFAULT';
|
|
179
|
+
const errorInfo = ERROR_FIXES[errorCode]
|
|
180
|
+
? ERROR_FIXES[errorCode](error, context)
|
|
181
|
+
: ERROR_FIXES.DEFAULT(error, context);
|
|
182
|
+
|
|
183
|
+
const output = [];
|
|
184
|
+
|
|
185
|
+
// Title
|
|
186
|
+
output.push(chalk.red.bold(`\nā ${errorInfo.title}`));
|
|
187
|
+
output.push(chalk.white(errorInfo.description));
|
|
188
|
+
|
|
189
|
+
// Possible causes
|
|
190
|
+
if (errorInfo.causes && errorInfo.causes.length > 0) {
|
|
191
|
+
output.push(chalk.yellow('\nš Possible causes:'));
|
|
192
|
+
errorInfo.causes.forEach(cause => {
|
|
193
|
+
output.push(chalk.gray(` ⢠${cause}`));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Fixes
|
|
198
|
+
if (errorInfo.fixes && errorInfo.fixes.length > 0) {
|
|
199
|
+
output.push(chalk.green('\n⨠How to fix:'));
|
|
200
|
+
errorInfo.fixes.forEach((fix, index) => {
|
|
201
|
+
// Filter by platform if specified
|
|
202
|
+
if (fix.platform && !fix.platform.includes(process.platform)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
output.push(chalk.cyan(`\n ${index + 1}. ${fix.description}`));
|
|
207
|
+
output.push(chalk.bgBlack.white(` $ ${fix.command}`));
|
|
208
|
+
|
|
209
|
+
if (fix.warning) {
|
|
210
|
+
output.push(chalk.yellow(` ā ļø Use with caution`));
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Additional help
|
|
216
|
+
output.push(chalk.gray('\nš For more help:'));
|
|
217
|
+
output.push(chalk.gray(' ⢠Documentation: https://sweetpotato.dev/docs'));
|
|
218
|
+
output.push(chalk.gray(' ⢠GitHub Issues: https://github.com/yourusername/sweet-potato/issues'));
|
|
219
|
+
output.push(chalk.gray(' ⢠Discord: https://discord.gg/sweetpotato\n'));
|
|
220
|
+
|
|
221
|
+
return output.join('\n');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function formatJsonError(error, context = {}) {
|
|
225
|
+
const errorCode = error.code || 'DEFAULT';
|
|
226
|
+
const errorInfo = ERROR_FIXES[errorCode]
|
|
227
|
+
? ERROR_FIXES[errorCode](error, context)
|
|
228
|
+
: ERROR_FIXES.DEFAULT(error, context);
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
success: false,
|
|
232
|
+
error: {
|
|
233
|
+
code: errorCode,
|
|
234
|
+
title: errorInfo.title,
|
|
235
|
+
message: errorInfo.description,
|
|
236
|
+
causes: errorInfo.causes,
|
|
237
|
+
fixes: errorInfo.fixes.map(fix => ({
|
|
238
|
+
command: fix.command,
|
|
239
|
+
description: fix.description,
|
|
240
|
+
warning: fix.warning || false
|
|
241
|
+
})),
|
|
242
|
+
stack: process.env.DEBUG ? error.stack : undefined
|
|
243
|
+
},
|
|
244
|
+
help: {
|
|
245
|
+
docs: 'https://sweetpotato.dev/docs',
|
|
246
|
+
issues: 'https://github.com/yourusername/sweet-potato/issues',
|
|
247
|
+
discord: 'https://discord.gg/sweetpotato'
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module.exports = {
|
|
253
|
+
formatError,
|
|
254
|
+
formatJsonError,
|
|
255
|
+
handleError: (error, context = {}, options = {}) => {
|
|
256
|
+
if (options.json) {
|
|
257
|
+
console.log(JSON.stringify(formatJsonError(error, context), null, 2));
|
|
258
|
+
} else {
|
|
259
|
+
console.error(formatError(error, context));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Exit with appropriate code
|
|
263
|
+
const exitCode = error.code === 'EADDRINUSE' ? 98 :
|
|
264
|
+
error.code === 'EACCES' ? 77 :
|
|
265
|
+
error.code === 'ENOENT' ? 66 :
|
|
266
|
+
error.code === 'MODULE_NOT_FOUND' ? 127 :
|
|
267
|
+
1;
|
|
268
|
+
|
|
269
|
+
process.exit(exitCode);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPAPS CLI Interactive Help System
|
|
3
|
+
* Decision trees and structured guidance for developers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const prompts = require('prompts');
|
|
8
|
+
|
|
9
|
+
const HELP_TREE = {
|
|
10
|
+
root: {
|
|
11
|
+
question: 'What would you like to do?',
|
|
12
|
+
options: [
|
|
13
|
+
{
|
|
14
|
+
title: 'š Start developing locally',
|
|
15
|
+
value: 'local-dev',
|
|
16
|
+
description: 'Run SPAPS on your machine'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
title: 'š¦ Set up a new project',
|
|
20
|
+
value: 'new-project',
|
|
21
|
+
description: 'Create a new app with SPAPS'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
title: 'š§ Add SPAPS to existing project',
|
|
25
|
+
value: 'existing-project',
|
|
26
|
+
description: 'Integrate SPAPS into your app'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: 'š Debug an issue',
|
|
30
|
+
value: 'debug',
|
|
31
|
+
description: 'Troubleshoot problems'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
title: 'š Learn about SPAPS',
|
|
35
|
+
value: 'learn',
|
|
36
|
+
description: 'Understand concepts and features'
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
'local-dev': {
|
|
42
|
+
question: 'How do you want to run SPAPS locally?',
|
|
43
|
+
options: [
|
|
44
|
+
{
|
|
45
|
+
title: 'Quick start (default settings)',
|
|
46
|
+
value: 'quick-start',
|
|
47
|
+
command: 'npx spaps local',
|
|
48
|
+
description: 'Start on port 3300'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
title: 'Custom port',
|
|
52
|
+
value: 'custom-port',
|
|
53
|
+
command: 'npx spaps local --port 3001',
|
|
54
|
+
description: 'Choose your own port'
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
title: 'Open browser automatically',
|
|
58
|
+
value: 'auto-open',
|
|
59
|
+
command: 'npx spaps local --open',
|
|
60
|
+
description: 'Opens docs in browser'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
title: 'JSON mode (for CI/CD)',
|
|
64
|
+
value: 'json-mode',
|
|
65
|
+
command: 'npx spaps local --json',
|
|
66
|
+
description: 'Machine-readable output'
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
'new-project': {
|
|
72
|
+
question: 'What type of project?',
|
|
73
|
+
options: [
|
|
74
|
+
{
|
|
75
|
+
title: 'Next.js App',
|
|
76
|
+
value: 'nextjs',
|
|
77
|
+
command: 'npx spaps create my-app --template nextjs',
|
|
78
|
+
description: 'Full-stack React framework',
|
|
79
|
+
available: 'v0.3.0'
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
title: 'React + Vite',
|
|
83
|
+
value: 'react',
|
|
84
|
+
command: 'npx spaps create my-app --template react',
|
|
85
|
+
description: 'Fast, modern React setup',
|
|
86
|
+
available: 'v0.3.0'
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
title: 'Node.js API',
|
|
90
|
+
value: 'node',
|
|
91
|
+
command: 'npx spaps create my-api --template node',
|
|
92
|
+
description: 'Express.js backend',
|
|
93
|
+
available: 'v0.3.0'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
title: 'Vanilla JavaScript',
|
|
97
|
+
value: 'vanilla',
|
|
98
|
+
command: 'npx spaps create my-app --template vanilla',
|
|
99
|
+
description: 'No framework, just JS',
|
|
100
|
+
available: 'v0.3.0'
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
'existing-project': {
|
|
106
|
+
question: 'What framework are you using?',
|
|
107
|
+
options: [
|
|
108
|
+
{
|
|
109
|
+
title: 'Next.js',
|
|
110
|
+
value: 'integrate-nextjs',
|
|
111
|
+
steps: [
|
|
112
|
+
'npm install spaps-sdk',
|
|
113
|
+
'npx spaps init',
|
|
114
|
+
'Add to your app:',
|
|
115
|
+
'',
|
|
116
|
+
'// app/providers.tsx',
|
|
117
|
+
'import { SPAPSClient } from "spaps-sdk"',
|
|
118
|
+
'const spaps = new SPAPSClient()'
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
title: 'React',
|
|
123
|
+
value: 'integrate-react',
|
|
124
|
+
steps: [
|
|
125
|
+
'npm install spaps-sdk',
|
|
126
|
+
'npx spaps init',
|
|
127
|
+
'Create a context:',
|
|
128
|
+
'',
|
|
129
|
+
'// src/contexts/SpapsContext.tsx',
|
|
130
|
+
'import { SPAPSClient } from "spaps-sdk"',
|
|
131
|
+
'const spaps = new SPAPSClient()',
|
|
132
|
+
'export const SpapsContext = React.createContext(spaps)'
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
title: 'Node.js/Express',
|
|
137
|
+
value: 'integrate-node',
|
|
138
|
+
steps: [
|
|
139
|
+
'npm install spaps-sdk',
|
|
140
|
+
'npx spaps init',
|
|
141
|
+
'Add middleware:',
|
|
142
|
+
'',
|
|
143
|
+
'// server.js',
|
|
144
|
+
'const { SPAPSClient } = require("spaps-sdk")',
|
|
145
|
+
'const spaps = new SPAPSClient()',
|
|
146
|
+
'',
|
|
147
|
+
'app.use(async (req, res, next) => {',
|
|
148
|
+
' req.spaps = spaps',
|
|
149
|
+
' next()',
|
|
150
|
+
'})'
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
title: 'Other/Vanilla',
|
|
155
|
+
value: 'integrate-other',
|
|
156
|
+
steps: [
|
|
157
|
+
'npm install spaps-sdk',
|
|
158
|
+
'npx spaps init',
|
|
159
|
+
'Import in your code:',
|
|
160
|
+
'',
|
|
161
|
+
'// Using ES6 modules',
|
|
162
|
+
'import { SPAPSClient } from "spaps-sdk"',
|
|
163
|
+
'',
|
|
164
|
+
'// Using CommonJS',
|
|
165
|
+
'const { SPAPSClient } = require("spaps-sdk")',
|
|
166
|
+
'',
|
|
167
|
+
'// Using CDN',
|
|
168
|
+
'<script src="https://unpkg.com/spaps-sdk"></script>'
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
'debug': {
|
|
175
|
+
question: 'What issue are you experiencing?',
|
|
176
|
+
options: [
|
|
177
|
+
{
|
|
178
|
+
title: 'Port already in use',
|
|
179
|
+
value: 'port-error',
|
|
180
|
+
solution: {
|
|
181
|
+
commands: [
|
|
182
|
+
'npx spaps local --port 3301',
|
|
183
|
+
'lsof -ti:3300 | xargs kill -9',
|
|
184
|
+
'npx spaps local --port 0'
|
|
185
|
+
],
|
|
186
|
+
explanation: 'Another process is using port 3300. Either use a different port or kill the existing process.'
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
title: 'Module not found',
|
|
191
|
+
value: 'module-error',
|
|
192
|
+
solution: {
|
|
193
|
+
commands: [
|
|
194
|
+
'npm install',
|
|
195
|
+
'rm -rf node_modules package-lock.json && npm install',
|
|
196
|
+
'npm install spaps-sdk'
|
|
197
|
+
],
|
|
198
|
+
explanation: 'Dependencies are missing. Reinstall packages to fix this.'
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
title: 'CORS errors',
|
|
203
|
+
value: 'cors-error',
|
|
204
|
+
solution: {
|
|
205
|
+
commands: [
|
|
206
|
+
'npx spaps local',
|
|
207
|
+
'Check your API URL: process.env.SPAPS_API_URL'
|
|
208
|
+
],
|
|
209
|
+
explanation: 'CORS is automatically handled in local mode. Make sure your client is pointing to the local server.'
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
title: 'Authentication failing',
|
|
214
|
+
value: 'auth-error',
|
|
215
|
+
solution: {
|
|
216
|
+
commands: [
|
|
217
|
+
'curl http://localhost:3300/health',
|
|
218
|
+
'npx spaps local --port 3300'
|
|
219
|
+
],
|
|
220
|
+
explanation: 'In local mode, authentication is automatic. Make sure the local server is running.'
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
'learn': {
|
|
227
|
+
question: 'What would you like to learn about?',
|
|
228
|
+
options: [
|
|
229
|
+
{
|
|
230
|
+
title: 'How SPAPS works',
|
|
231
|
+
value: 'how-it-works',
|
|
232
|
+
content: `
|
|
233
|
+
SPAPS (Sweet Potato Authentication & Payment Service) provides:
|
|
234
|
+
|
|
235
|
+
1. **Authentication**: Email/password, magic links, and wallet-based auth
|
|
236
|
+
2. **Payments**: Stripe subscriptions and wallet-based payments
|
|
237
|
+
3. **Local Development**: Zero-config local mode with auto-auth
|
|
238
|
+
4. **Type Safety**: Full TypeScript support with generated types
|
|
239
|
+
5. **Multi-tenant**: API key-based app isolation
|
|
240
|
+
|
|
241
|
+
In local mode, everything is mocked so you can develop without:
|
|
242
|
+
- API keys
|
|
243
|
+
- Database setup
|
|
244
|
+
- External services
|
|
245
|
+
- Configuration files
|
|
246
|
+
`
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
title: 'Local vs Production',
|
|
250
|
+
value: 'environments',
|
|
251
|
+
content: `
|
|
252
|
+
**Local Mode** (http://localhost:3300):
|
|
253
|
+
- No API key required
|
|
254
|
+
- Auto-authentication
|
|
255
|
+
- Mocked responses
|
|
256
|
+
- CORS disabled
|
|
257
|
+
- Perfect for development
|
|
258
|
+
|
|
259
|
+
**Production Mode** (https://api.yourapp.com):
|
|
260
|
+
- API key required
|
|
261
|
+
- Real authentication
|
|
262
|
+
- Database persistence
|
|
263
|
+
- Stripe integration
|
|
264
|
+
- Rate limiting
|
|
265
|
+
|
|
266
|
+
The SDK auto-detects which mode to use based on the URL!
|
|
267
|
+
`
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
title: 'Authentication methods',
|
|
271
|
+
value: 'auth-methods',
|
|
272
|
+
content: `
|
|
273
|
+
SPAPS supports multiple authentication methods:
|
|
274
|
+
|
|
275
|
+
**Traditional Auth**:
|
|
276
|
+
- Email/password login
|
|
277
|
+
- Magic link (passwordless)
|
|
278
|
+
- Social logins (coming soon)
|
|
279
|
+
|
|
280
|
+
**Wallet Auth**:
|
|
281
|
+
- Solana wallets
|
|
282
|
+
- Ethereum wallets
|
|
283
|
+
- Bitcoin (coming soon)
|
|
284
|
+
- Base chain (coming soon)
|
|
285
|
+
|
|
286
|
+
**Code Examples**:
|
|
287
|
+
\`\`\`javascript
|
|
288
|
+
// Email/password
|
|
289
|
+
await spaps.login(email, password)
|
|
290
|
+
|
|
291
|
+
// Wallet
|
|
292
|
+
await spaps.walletSignIn(address, signature, message, 'solana')
|
|
293
|
+
\`\`\`
|
|
294
|
+
`
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
title: 'Payment integration',
|
|
298
|
+
value: 'payments',
|
|
299
|
+
content: `
|
|
300
|
+
SPAPS handles two payment types:
|
|
301
|
+
|
|
302
|
+
**Stripe Subscriptions**:
|
|
303
|
+
- Monthly/annual plans
|
|
304
|
+
- Usage-based billing
|
|
305
|
+
- Customer portal
|
|
306
|
+
- Webhook handling
|
|
307
|
+
|
|
308
|
+
**Wallet Payments**:
|
|
309
|
+
- Direct crypto payments
|
|
310
|
+
- Usage credits
|
|
311
|
+
- Instant settlement
|
|
312
|
+
- Multi-chain support
|
|
313
|
+
|
|
314
|
+
**Code Example**:
|
|
315
|
+
\`\`\`javascript
|
|
316
|
+
// Create Stripe checkout
|
|
317
|
+
const session = await spaps.createCheckoutSession(priceId, successUrl)
|
|
318
|
+
|
|
319
|
+
// Check subscription
|
|
320
|
+
const sub = await spaps.getSubscription()
|
|
321
|
+
\`\`\`
|
|
322
|
+
`
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
async function showInteractiveHelp() {
|
|
329
|
+
console.log(chalk.yellow('\nš SPAPS Interactive Help\n'));
|
|
330
|
+
|
|
331
|
+
let currentNode = 'root';
|
|
332
|
+
const history = [];
|
|
333
|
+
|
|
334
|
+
while (true) {
|
|
335
|
+
const node = HELP_TREE[currentNode];
|
|
336
|
+
|
|
337
|
+
if (!node) {
|
|
338
|
+
// Leaf node - show result
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const choices = node.options.map(opt => ({
|
|
343
|
+
title: opt.title,
|
|
344
|
+
description: opt.description || '',
|
|
345
|
+
value: opt.value
|
|
346
|
+
}));
|
|
347
|
+
|
|
348
|
+
// Add back option if not at root
|
|
349
|
+
if (currentNode !== 'root') {
|
|
350
|
+
choices.push({
|
|
351
|
+
title: chalk.gray('ā Back'),
|
|
352
|
+
value: '__back__'
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
choices.push({
|
|
357
|
+
title: chalk.gray('ā Exit'),
|
|
358
|
+
value: '__exit__'
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
const response = await prompts({
|
|
362
|
+
type: 'select',
|
|
363
|
+
name: 'choice',
|
|
364
|
+
message: node.question,
|
|
365
|
+
choices: choices
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
if (!response.choice || response.choice === '__exit__') {
|
|
369
|
+
console.log(chalk.gray('\nGoodbye! š\n'));
|
|
370
|
+
process.exit(0);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (response.choice === '__back__') {
|
|
374
|
+
currentNode = history.pop() || 'root';
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Find the selected option
|
|
379
|
+
const selected = node.options.find(opt => opt.value === response.choice);
|
|
380
|
+
|
|
381
|
+
if (selected) {
|
|
382
|
+
// Show immediate result if available
|
|
383
|
+
if (selected.command) {
|
|
384
|
+
console.log(chalk.green('\n⨠Run this command:\n'));
|
|
385
|
+
console.log(chalk.bgBlack.white(` $ ${selected.command}`));
|
|
386
|
+
|
|
387
|
+
if (selected.available) {
|
|
388
|
+
console.log(chalk.yellow(`\n ā ļø Available in ${selected.available}`));
|
|
389
|
+
}
|
|
390
|
+
console.log();
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (selected.steps) {
|
|
394
|
+
console.log(chalk.green('\nš Follow these steps:\n'));
|
|
395
|
+
selected.steps.forEach((step, i) => {
|
|
396
|
+
if (step.startsWith('//') || step.startsWith('#')) {
|
|
397
|
+
console.log(chalk.gray(step));
|
|
398
|
+
} else if (step === '') {
|
|
399
|
+
console.log();
|
|
400
|
+
} else if (step.includes('npm') || step.includes('npx')) {
|
|
401
|
+
console.log(chalk.bgBlack.white(` $ ${step}`));
|
|
402
|
+
} else {
|
|
403
|
+
console.log(` ${step}`);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
console.log();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (selected.solution) {
|
|
410
|
+
console.log(chalk.green('\nš” Solution:\n'));
|
|
411
|
+
console.log(chalk.white(selected.solution.explanation));
|
|
412
|
+
console.log(chalk.green('\nTry these commands:\n'));
|
|
413
|
+
selected.solution.commands.forEach(cmd => {
|
|
414
|
+
console.log(chalk.bgBlack.white(` $ ${cmd}`));
|
|
415
|
+
});
|
|
416
|
+
console.log();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (selected.content) {
|
|
420
|
+
console.log(chalk.green('\nš Information:\n'));
|
|
421
|
+
console.log(selected.content);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Navigate to next level if exists
|
|
425
|
+
if (HELP_TREE[response.choice]) {
|
|
426
|
+
history.push(currentNode);
|
|
427
|
+
currentNode = response.choice;
|
|
428
|
+
} else {
|
|
429
|
+
// Ask to continue or exit
|
|
430
|
+
const cont = await prompts({
|
|
431
|
+
type: 'confirm',
|
|
432
|
+
name: 'continue',
|
|
433
|
+
message: 'Continue exploring help?',
|
|
434
|
+
initial: true
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
if (!cont.continue) {
|
|
438
|
+
console.log(chalk.gray('\nGoodbye! š\n'));
|
|
439
|
+
process.exit(0);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Go back to previous menu
|
|
443
|
+
currentNode = history.pop() || 'root';
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function showQuickHelp() {
|
|
450
|
+
console.log(chalk.yellow('\nš SPAPS Quick Reference\n'));
|
|
451
|
+
|
|
452
|
+
console.log(chalk.green('Common Commands:'));
|
|
453
|
+
console.log(' npx spaps local ' + chalk.gray('# Start local server'));
|
|
454
|
+
console.log(' npx spaps init ' + chalk.gray('# Initialize in project'));
|
|
455
|
+
console.log(' npx spaps create <name> ' + chalk.gray('# Create new project (v0.3.0)'));
|
|
456
|
+
console.log(' npx spaps types ' + chalk.gray('# Generate types (v0.4.0)'));
|
|
457
|
+
console.log();
|
|
458
|
+
|
|
459
|
+
console.log(chalk.green('Local Development:'));
|
|
460
|
+
console.log(' npx spaps local --port 3001 ' + chalk.gray('# Custom port'));
|
|
461
|
+
console.log(' npx spaps local --open ' + chalk.gray('# Open browser'));
|
|
462
|
+
console.log(' npx spaps local --json ' + chalk.gray('# JSON output'));
|
|
463
|
+
console.log();
|
|
464
|
+
|
|
465
|
+
console.log(chalk.green('SDK Usage:'));
|
|
466
|
+
console.log(' npm install spaps-sdk ' + chalk.gray('# Install SDK'));
|
|
467
|
+
console.log();
|
|
468
|
+
console.log(chalk.gray(' import { SPAPSClient } from "spaps-sdk"'));
|
|
469
|
+
console.log(chalk.gray(' const spaps = new SPAPSClient()'));
|
|
470
|
+
console.log(chalk.gray(' await spaps.login(email, password)'));
|
|
471
|
+
console.log();
|
|
472
|
+
|
|
473
|
+
console.log(chalk.green('Debugging:'));
|
|
474
|
+
console.log(' curl http://localhost:3300/health ' + chalk.gray('# Check server'));
|
|
475
|
+
console.log(' npx spaps help --interactive ' + chalk.gray('# Interactive help'));
|
|
476
|
+
console.log();
|
|
477
|
+
|
|
478
|
+
console.log(chalk.blue('š Docs: https://sweetpotato.dev'));
|
|
479
|
+
console.log(chalk.blue('š¬ Discord: https://discord.gg/sweetpotato'));
|
|
480
|
+
console.log();
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
module.exports = {
|
|
484
|
+
showInteractiveHelp,
|
|
485
|
+
showQuickHelp,
|
|
486
|
+
HELP_TREE
|
|
487
|
+
};
|