spaps 0.4.3 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/spaps.js CHANGED
@@ -2,20 +2,12 @@
2
2
 
3
3
  /**
4
4
  * SPAPS CLI - Sweet Potato Authentication & Payment Service
5
- *
6
- * This is a minimal implementation to secure the npm package name.
7
- * Full implementation coming soon!
8
5
  */
9
6
 
10
7
  const chalk = require('chalk');
11
- const { program } = require('commander');
12
- const { spawn } = require('child_process');
13
- const path = require('path');
14
8
  const fs = require('fs');
15
- const { handleError } = require('../src/error-handler');
16
- const { showInteractiveHelp, showQuickHelp } = require('../src/help-system');
17
- const { showInteractiveDocs, showQuickReference, searchDocs } = require('../src/docs-system');
18
- const { getQuickStartInstructions, getServerStatus, runQuickTest } = require('../src/ai-helper');
9
+ const { buildProgram } = require('../src/cli-dispatcher');
10
+ const { createHandlers } = require('../src/handlers');
19
11
 
20
12
  const version = require('../package.json').version;
21
13
 
@@ -24,307 +16,8 @@ const logo = `
24
16
  ${chalk.yellow('šŸ  SPAPS')} - Sweet Potato Authentication & Payment Service
25
17
  `;
26
18
 
27
- program
28
- .name('spaps')
29
- .description('CLI for Sweet Potato Authentication & Payment Service')
30
- .version(version)
31
- .option('--json', 'Output in JSON format for machine parsing');
32
-
33
- // Local command - Start local development server
34
- program
35
- .command('local')
36
- .description('Start local SPAPS server (no API keys required!)')
37
- .option('-p, --port <port>', 'Port to run on', '3456')
38
- .option('-o, --open', 'Open browser automatically', false)
39
- .option('--json', 'Output in JSON format')
40
- .action(async (options, command) => {
41
- const isJson = options.json || command.parent.opts().json;
42
-
43
- if (!isJson) {
44
- console.log(logo);
45
- }
46
-
47
- try {
48
- // Import and start the local server
49
- const LocalServer = require('../src/local-server.js');
50
- const server = new LocalServer({ port: options.port, json: isJson });
51
-
52
- if (isJson) {
53
- // For JSON output, start server and return immediately
54
- await server.start();
55
- console.log(JSON.stringify({
56
- success: true,
57
- command: 'local',
58
- server: {
59
- url: `http://localhost:${options.port}`,
60
- docs: `http://localhost:${options.port}/docs`,
61
- mode: 'local-development',
62
- port: parseInt(options.port),
63
- features: {
64
- autoAuth: true,
65
- corsEnabled: true,
66
- testUsers: ['user', 'admin', 'premium'],
67
- apiKeyRequired: false
68
- }
69
- }
70
- }));
71
- } else {
72
- await server.start();
73
-
74
- // Open browser if requested
75
- if (options.open) {
76
- const { exec } = require('child_process');
77
- const url = `http://localhost:${options.port}/docs`;
78
- const start = process.platform === 'darwin' ? 'open' :
79
- process.platform === 'win32' ? 'start' : 'xdg-open';
80
- exec(`${start} ${url}`);
81
- }
82
- }
83
-
84
- // Keep process running
85
- process.on('SIGINT', () => {
86
- if (!isJson) {
87
- console.log(chalk.yellow('\nšŸ‘‹ Shutting down SPAPS local server...'));
88
- }
89
- process.exit(0);
90
- });
91
-
92
- } catch (error) {
93
- handleError(error, { port: options.port, command: 'local' }, { json: isJson });
94
- }
95
- });
96
-
97
- // Quickstart command - For AI agents
98
- program
99
- .command('quickstart')
100
- .description('Get quick start instructions (for AI agents)')
101
- .option('-p, --port <port>', 'Port to check', '3300')
102
- .option('--json', 'Output in JSON format')
103
- .action(async (options) => {
104
- const instructions = getQuickStartInstructions(options.port);
105
-
106
- if (options.json === true) {
107
- console.log(JSON.stringify(instructions, null, 2));
108
- process.exit(0);
109
- } else {
110
- console.log(chalk.yellow('\nšŸ  SPAPS Quick Start Instructions\n'));
111
- console.log('1. Install SDK: npm install spaps-sdk');
112
- console.log('2. Create test file with the code above');
113
- console.log('3. Run: node test-spaps.js');
114
- console.log('\nFor JSON output: npx spaps quickstart --json');
115
- }
116
- });
117
-
118
- // Status command - Check if server is running
119
- program
120
- .command('status')
121
- .description('Check if SPAPS server is running')
122
- .option('-p, --port <port>', 'Port to check', '3300')
123
- .option('--json', 'Output in JSON format')
124
- .action(async (options) => {
125
- const status = await getServerStatus(options.port);
126
-
127
- if (options.json) {
128
- console.log(JSON.stringify(status));
129
- } else {
130
- if (status.running) {
131
- console.log(chalk.green(`āœ… SPAPS is running on port ${options.port}`));
132
- console.log(chalk.blue(` URL: ${status.url}`));
133
- console.log(chalk.blue(` Docs: ${status.docs}`));
134
- } else {
135
- console.log(chalk.red(`āŒ SPAPS is not running on port ${options.port}`));
136
- console.log(chalk.yellow(` Start with: ${status.start_command}`));
137
- }
138
- }
139
- });
140
-
141
- // Test command - Run quick tests
142
- program
143
- .command('test')
144
- .description('Run quick tests to verify SPAPS is working')
145
- .option('-p, --port <port>', 'Port to test', '3300')
146
- .option('--json', 'Output in JSON format')
147
- .action(async (options) => {
148
- const results = await runQuickTest(options.port);
149
-
150
- if (options.json) {
151
- console.log(JSON.stringify(results, null, 2));
152
- } else {
153
- console.log(chalk.yellow('\n🧪 Running SPAPS Tests...\n'));
154
-
155
- results.results.forEach(result => {
156
- const icon = result.success ? 'āœ…' : 'āŒ';
157
- console.log(`${icon} ${result.test}`);
158
- if (!result.success && result.fix) {
159
- console.log(chalk.yellow(` Fix: ${result.fix}`));
160
- }
161
- });
162
-
163
- console.log();
164
- console.log(results.success ?
165
- chalk.green(`✨ ${results.summary}`) :
166
- chalk.red(`āš ļø ${results.summary}`)
167
- );
168
-
169
- if (results.next_steps) {
170
- console.log('\nNext steps:');
171
- results.next_steps.forEach(step => {
172
- console.log(` • ${step}`);
173
- });
174
- }
175
- }
176
- });
177
-
178
- // Init command - Initialize SPAPS in existing project
179
- program
180
- .command('init')
181
- .description('Initialize SPAPS in your project')
182
- .option('--json', 'Output in JSON format')
183
- .action((options, command) => {
184
- const isJson = options.json || command.parent.opts().json;
185
-
186
- if (!isJson) {
187
- console.log(logo);
188
- console.log(chalk.green('šŸ”§ Initializing SPAPS...'));
189
- console.log();
190
- }
191
-
192
- // Create minimal .env.local
193
- const envContent = `# SPAPS Local Development Configuration
194
- # No API keys needed for local development!
195
-
196
- SPAPS_API_URL=http://localhost:3300
197
- NODE_ENV=development
198
-
199
- # When you're ready for production:
200
- # SPAPS_API_KEY=your-api-key-here
201
- `;
202
-
203
- const result = {
204
- success: true,
205
- command: 'init',
206
- files_created: [],
207
- files_skipped: [],
208
- next_steps: [
209
- 'npx spaps local',
210
- 'npm install @spaps/sdk',
211
- 'Start coding!'
212
- ]
213
- };
214
-
215
- if (!fs.existsSync('.env.local')) {
216
- fs.writeFileSync('.env.local', envContent);
217
- result.files_created.push('.env.local');
218
- if (!isJson) {
219
- console.log(chalk.green('āœ… Created .env.local'));
220
- }
221
- } else {
222
- result.files_skipped.push('.env.local');
223
- result.message = '.env.local already exists';
224
- if (!isJson) {
225
- console.log(chalk.yellow('āš ļø .env.local already exists'));
226
- }
227
- }
228
-
229
- if (isJson) {
230
- console.log(JSON.stringify(result));
231
- } else {
232
- console.log();
233
- console.log(chalk.green('✨ SPAPS initialized!'));
234
- console.log();
235
- console.log('Next steps:');
236
- console.log(chalk.cyan(' 1. Run: npx spaps local'));
237
- console.log(chalk.cyan(' 2. Install SDK: npm install @spaps/sdk'));
238
- console.log(chalk.cyan(' 3. Start coding!'));
239
- }
240
- });
241
-
242
- // Create command (placeholder)
243
- program
244
- .command('create <name>')
245
- .description('Create a new project with SPAPS (coming soon)')
246
- .action((name) => {
247
- console.log(logo);
248
- console.log(chalk.yellow(`🚧 'spaps create' coming in v0.3.0!`));
249
- console.log();
250
- console.log('For now, check out our examples:');
251
- console.log(chalk.cyan(' https://github.com/yourusername/sweet-potato/tree/main/examples'));
252
- });
253
-
254
- // Types command (placeholder)
255
- program
256
- .command('types')
257
- .description('Generate TypeScript types (coming soon)')
258
- .action(() => {
259
- console.log(logo);
260
- console.log(chalk.yellow(`🚧 'spaps types' coming in v0.4.0!`));
261
- });
262
-
263
- // Help command - Interactive help system
264
- program
265
- .command('help')
266
- .description('Show help and guides')
267
- .option('-i, --interactive', 'Interactive help mode')
268
- .option('-q, --quick', 'Quick reference')
269
- .action(async (options) => {
270
- if (options.interactive) {
271
- await showInteractiveHelp();
272
- } else if (options.quick) {
273
- showQuickHelp();
274
- } else {
275
- // Default to quick help
276
- showQuickHelp();
277
- }
278
- });
279
-
280
- // Docs command - SDK documentation
281
- program
282
- .command('docs')
283
- .description('Browse SDK documentation')
284
- .option('-i, --interactive', 'Interactive documentation browser')
285
- .option('-s, --search <query>', 'Search documentation')
286
- .option('--json', 'Output in JSON format')
287
- .action(async (options) => {
288
- if (options.search) {
289
- const results = searchDocs(options.search);
290
-
291
- if (options.json) {
292
- console.log(JSON.stringify({ results }, null, 2));
293
- } else {
294
- console.log(chalk.yellow(`\nšŸ” Search results for "${options.search}":\n`));
295
-
296
- if (results.length === 0) {
297
- console.log(chalk.gray(' No results found'));
298
- } else {
299
- results.forEach((result, i) => {
300
- console.log(chalk.green(` ${i + 1}. ${result.title}`));
301
- console.log(chalk.gray(` ${result.preview}`));
302
- console.log();
303
- });
304
- }
305
-
306
- console.log(chalk.blue(' Run: npx spaps docs --interactive'));
307
- console.log(chalk.blue(' to browse full documentation\n'));
308
- }
309
- } else if (options.interactive) {
310
- await showInteractiveDocs();
311
- } else {
312
- // Default to quick reference
313
- showQuickReference();
314
- }
315
- });
316
-
317
- // Help command enhancement
318
- program.on('--help', () => {
319
- console.log();
320
- console.log('Examples:');
321
- console.log();
322
- console.log(' $ spaps local # Start local dev server');
323
- console.log(' $ spaps init # Initialize in current project');
324
- console.log(' $ spaps create my-app # Create new project (soon)');
325
- console.log();
326
- console.log('Learn more at https://sweetpotato.dev');
327
- });
19
+ const handlers = createHandlers(version, logo);
20
+ const program = buildProgram({ handlers, dryRun: false, version, logo });
328
21
 
329
22
  // Show help if no command provided
330
23
  if (!process.argv.slice(2).length) {
@@ -334,4 +27,4 @@ if (!process.argv.slice(2).length) {
334
27
  console.log(chalk.yellow('šŸ’” Try: npx spaps help --interactive'));
335
28
  }
336
29
 
337
- program.parse(process.argv);
30
+ program.parse(process.argv);
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "spaps",
3
- "version": "0.4.3",
4
- "description": "Sweet Potato Authentication & Payment Service CLI - Zero-config local development and project scaffolding",
5
- "main": "bin/spaps.js",
3
+ "version": "0.5.1",
4
+ "description": "Sweet Potato Authentication & Payment Service CLI - Zero-config local development with built-in admin middleware and permission utilities",
5
+ "main": "src/index.js",
6
6
  "bin": {
7
7
  "spaps": "./bin/spaps.js"
8
8
  },
9
9
  "exports": {
10
- ".": "./bin/spaps.js",
11
- "./client": "./client.js"
10
+ ".": "./src/index.js",
11
+ "./cli": "./bin/spaps.js",
12
+ "./client": "./client.js",
13
+ "./middleware": "./src/middleware/admin.js"
12
14
  },
13
15
  "scripts": {
14
16
  "test": "echo \"No tests yet\""
@@ -30,10 +32,10 @@
30
32
  "license": "MIT",
31
33
  "repository": {
32
34
  "type": "git",
33
- "url": "https://github.com/yourusername/sweet-potato"
35
+ "url": "https://github.com/buildooor/sweet-potato"
34
36
  },
35
37
  "bugs": {
36
- "url": "https://github.com/yourusername/sweet-potato/issues"
38
+ "url": "https://github.com/build000r/sweet-potato/issues"
37
39
  },
38
40
  "homepage": "https://sweetpotato.dev",
39
41
  "dependencies": {
@@ -44,7 +46,8 @@
44
46
  "express": "^4.18.2",
45
47
  "ora": "^5.4.1",
46
48
  "prompts": "^2.4.2",
47
- "stripe": "^18.5.0"
49
+ "stripe": "^18.5.0",
50
+ "uuid": "^8.3.2"
48
51
  },
49
52
  "engines": {
50
53
  "node": ">=16.0.0"
package/src/ai-helper.js CHANGED
@@ -5,7 +5,9 @@
5
5
 
6
6
  const chalk = require('chalk');
7
7
 
8
- function getQuickStartInstructions(port = 3300) {
8
+ const { DEFAULT_PORT } = require('./config');
9
+
10
+ function getQuickStartInstructions(port = DEFAULT_PORT) {
9
11
  return {
10
12
  success: true,
11
13
  instructions: {
@@ -17,22 +19,20 @@ function getQuickStartInstructions(port = 3300) {
17
19
  step2: {
18
20
  description: "Create test file",
19
21
  filename: "test-spaps.js",
20
- content: `const { SPAPSClient } = require('spaps-sdk');
22
+ content: `const { SweetPotatoSDK } = require('spaps-sdk');
21
23
 
22
24
  async function test() {
23
- const spaps = new SPAPSClient({
24
- apiUrl: 'http://localhost:${port}'
25
- });
26
-
27
- // Test login
28
- const { data } = await spaps.login('test@example.com', 'password');
29
- console.log('āœ… Login successful:', data.user.email);
30
-
25
+ const sdk = new SweetPotatoSDK({ apiUrl: 'http://localhost:${port}' });
26
+
27
+ // Test email/password auth (local mode accepts any credentials)
28
+ const auth = await sdk.auth.signInWithPassword({ email: 'test@example.com', password: 'password' });
29
+ console.log('āœ… Login successful:', auth.user.email);
30
+
31
31
  // Test authenticated request
32
- const user = await spaps.getUser();
33
- console.log('āœ… Got user:', user.data.email);
34
-
35
- return { success: true, user: user.data };
32
+ const user = await sdk.auth.getCurrentUser();
33
+ console.log('āœ… Got user:', user.email);
34
+
35
+ return { success: true, user };
36
36
  }
37
37
 
38
38
  test().then(console.log).catch(console.error);`
@@ -70,9 +70,9 @@ test().then(console.log).catch(console.error);`
70
70
  },
71
71
  {
72
72
  method: "POST",
73
- path: "/api/stripe/create-checkout-session",
74
- body: { price_id: "string", success_url: "string" },
75
- response: { sessionId: "string", url: "string" }
73
+ path: "/api/stripe/checkout-sessions",
74
+ body: { price_id: "string", success_url: "string", cancel_url: "string" },
75
+ response: { id: "string", url: "string", status: "string" }
76
76
  },
77
77
  {
78
78
  method: "GET",
@@ -84,12 +84,12 @@ test().then(console.log).catch(console.error);`
84
84
  test_commands: {
85
85
  health_check: `curl http://localhost:${port}/health`,
86
86
  login: `curl -X POST http://localhost:${port}/api/auth/login -H "Content-Type: application/json" -d '{"email":"test@example.com","password":"password"}'`,
87
- with_sdk: `node -e "const {SPAPSClient}=require('spaps-sdk');const s=new SPAPSClient();s.login('test@example.com','password').then(r=>console.log(JSON.stringify(r.data))).catch(console.error)"`
87
+ with_sdk: `node -e "const {SweetPotatoSDK}=require('spaps-sdk');const sdk=new SweetPotatoSDK({apiUrl:'http://localhost:${port}'});sdk.auth.signInWithPassword({email:'test@example.com',password:'password'}).then(r=>console.log(JSON.stringify(r.user))).catch(console.error)"`
88
88
  }
89
89
  };
90
90
  }
91
91
 
92
- function getServerStatus(port = 3300) {
92
+ function getServerStatus(port = DEFAULT_PORT) {
93
93
  const http = require('http');
94
94
 
95
95
  return new Promise((resolve) => {
@@ -270,4 +270,4 @@ module.exports = {
270
270
  getQuickStartInstructions,
271
271
  getServerStatus,
272
272
  runQuickTest
273
- };
273
+ };
@@ -0,0 +1,139 @@
1
+ // CLI dispatcher: builds a Commander program with pluggable handlers
2
+ // Enables dry-run parsing for unit tests without executing side effects.
3
+
4
+ const { Command } = require('commander');
5
+ const { DEFAULT_PORT } = require('./config');
6
+
7
+ function defineProgram({ handlers = {}, dryRun = false, version = '0.0.0', logo = null } = {}) {
8
+ const intents = [];
9
+ const program = new Command();
10
+
11
+ if (dryRun) {
12
+ program.allowUnknownOption(true);
13
+ }
14
+
15
+ program
16
+ .name('spaps')
17
+ .description('CLI for Sweet Potato Authentication & Payment Service')
18
+ .version(version)
19
+ .option('--json', 'Output in JSON format for machine parsing');
20
+
21
+ function makeAction(name, shape) {
22
+ return async function actionWrapper(...args) {
23
+ // Commander 11 passes (options, command) for subcommands without args
24
+ // For commands with args, it passes (arg1, arg2, ..., options, command)
25
+ const cmd = args[args.length - 1];
26
+ const options = args[args.length - 2] || {};
27
+ const parentJson = program.opts().json;
28
+ const isJson = Boolean(options.json || parentJson);
29
+
30
+ const intent = { name, options: { ...shape(options, cmd, isJson) } };
31
+ intents.push(intent);
32
+
33
+ if (dryRun) return intent;
34
+ const handler = handlers[name];
35
+ if (typeof handler === 'function') {
36
+ return await handler(intent, { program, cmd });
37
+ }
38
+ return intent;
39
+ };
40
+ }
41
+
42
+ // spaps local
43
+ const cmdLocal = program
44
+ .command('local')
45
+ .description('Start local SPAPS server (no API keys required!)')
46
+ .option('-p, --port <port>', 'Port to run on', String(DEFAULT_PORT))
47
+ .option('-o, --open', 'Open browser automatically', false)
48
+ .option('--json', 'Output in JSON format')
49
+ .action(
50
+ makeAction('local', (opts, _cmd, isJson) => ({
51
+ port: Number(opts.port),
52
+ open: Boolean(opts.open),
53
+ json: isJson,
54
+ }))
55
+ );
56
+ if (dryRun) cmdLocal.allowUnknownOption(true);
57
+
58
+ // spaps quickstart
59
+ const cmdQuick = program
60
+ .command('quickstart')
61
+ .description('Get quick start instructions (for AI agents)')
62
+ .option('-p, --port <port>', 'Port to check', String(DEFAULT_PORT))
63
+ .option('--json', 'Output in JSON format')
64
+ .action(makeAction('quickstart', (opts, _cmd, isJson) => ({ port: Number(opts.port), json: isJson })));
65
+ if (dryRun) cmdQuick.allowUnknownOption(true);
66
+
67
+ // spaps status
68
+ const cmdStatus = program
69
+ .command('status')
70
+ .description('Check if SPAPS server is running')
71
+ .option('-p, --port <port>', 'Port to check', String(DEFAULT_PORT))
72
+ .option('--json', 'Output in JSON format')
73
+ .action(makeAction('status', (opts, _cmd, isJson) => ({ port: Number(opts.port), json: isJson })));
74
+ if (dryRun) cmdStatus.allowUnknownOption(true);
75
+
76
+ // spaps init
77
+ const cmdInit = program
78
+ .command('init')
79
+ .description('Initialize SPAPS in current project')
80
+ .option('--json', 'Output in JSON format')
81
+ .action(makeAction('init', (_opts, _cmd, isJson) => ({ json: isJson })));
82
+ if (dryRun) cmdInit.allowUnknownOption(true);
83
+
84
+ // spaps create <name>
85
+ const cmdCreate = program
86
+ .command('create <name>')
87
+ .description('Create a new project with SPAPS (coming soon)')
88
+ .action(makeAction('create', (optsOrName, cmd) => ({ name: typeof optsOrName === 'string' ? optsOrName : cmd.args[0] })));
89
+ if (dryRun) cmdCreate.allowUnknownOption(true);
90
+
91
+ // spaps types
92
+ const cmdTypes = program
93
+ .command('types')
94
+ .description('Generate TypeScript types (coming soon)')
95
+ .action(makeAction('types', () => ({})));
96
+ if (dryRun) cmdTypes.allowUnknownOption(true);
97
+
98
+ // spaps help
99
+ const cmdHelp = program
100
+ .command('help')
101
+ .description('Show help and guides')
102
+ .option('-i, --interactive', 'Interactive help mode')
103
+ .option('-q, --quick', 'Quick reference')
104
+ .action(
105
+ makeAction('help', (opts) => ({ interactive: Boolean(opts.interactive), quick: Boolean(opts.quick) }))
106
+ );
107
+ if (dryRun) cmdHelp.allowUnknownOption(true);
108
+
109
+ // spaps docs
110
+ const cmdDocs = program
111
+ .command('docs')
112
+ .description('Browse SDK documentation')
113
+ .option('-i, --interactive', 'Interactive documentation browser')
114
+ .option('-s, --search <query>', 'Search documentation')
115
+ .option('--json', 'Output in JSON format')
116
+ .action(
117
+ makeAction('docs', (opts, _cmd, isJson) => ({ interactive: Boolean(opts.interactive), search: opts.search || null, json: isJson }))
118
+ );
119
+ if (dryRun) cmdDocs.allowUnknownOption(true);
120
+
121
+ return { program, getIntents: () => intents };
122
+ }
123
+
124
+ function buildProgram(config = {}) {
125
+ return defineProgram(config).program;
126
+ }
127
+
128
+ function parseArgv(argv, config = {}) {
129
+ const { program, getIntents } = defineProgram({ ...config, dryRun: true });
130
+ program.exitOverride(() => { /* swallow exit in dry-run */ });
131
+ try {
132
+ program.parse(argv, { from: 'user' });
133
+ } catch (err) {
134
+ // Commander throws for help/version; we ignore in parse mode
135
+ }
136
+ return getIntents();
137
+ }
138
+
139
+ module.exports = { buildProgram, parseArgv };
package/src/config.js ADDED
@@ -0,0 +1,5 @@
1
+ // Shared configuration for SPAPS CLI
2
+ module.exports = {
3
+ DEFAULT_PORT: 3300
4
+ };
5
+