spaps 0.2.6 โ†’ 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.
package/bin/spaps.js CHANGED
@@ -15,6 +15,7 @@ const fs = require('fs');
15
15
  const { handleError } = require('../src/error-handler');
16
16
  const { showInteractiveHelp, showQuickHelp } = require('../src/help-system');
17
17
  const { showInteractiveDocs, showQuickReference, searchDocs } = require('../src/docs-system');
18
+ const { getQuickStartInstructions, getServerStatus, runQuickTest } = require('../src/ai-helper');
18
19
 
19
20
  const version = require('../package.json').version;
20
21
 
@@ -93,6 +94,87 @@ program
93
94
  }
94
95
  });
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
+
96
178
  // Init command - Initialize SPAPS in existing project
97
179
  program
98
180
  .command('init')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spaps",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "description": "Sweet Potato Authentication & Payment Service CLI - Zero-config local development and project scaffolding",
5
5
  "main": "bin/spaps.js",
6
6
  "bin": {
@@ -0,0 +1,273 @@
1
+ /**
2
+ * SPAPS AI Agent Helper
3
+ * Provides AI-friendly outputs and quick commands
4
+ */
5
+
6
+ const chalk = require('chalk');
7
+
8
+ function getQuickStartInstructions(port = 3300) {
9
+ return {
10
+ success: true,
11
+ instructions: {
12
+ step1: {
13
+ description: "Install SDK",
14
+ command: "npm install spaps-sdk",
15
+ verify: "npm list spaps-sdk"
16
+ },
17
+ step2: {
18
+ description: "Create test file",
19
+ filename: "test-spaps.js",
20
+ content: `const { SPAPSClient } = require('spaps-sdk');
21
+
22
+ 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
+
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 };
36
+ }
37
+
38
+ test().then(console.log).catch(console.error);`
39
+ },
40
+ step3: {
41
+ description: "Run test",
42
+ command: "node test-spaps.js",
43
+ expected_output: {
44
+ success: true,
45
+ user: {
46
+ id: "local-user-123",
47
+ email: "test@example.com"
48
+ }
49
+ }
50
+ }
51
+ },
52
+ endpoints: [
53
+ {
54
+ method: "POST",
55
+ path: "/api/auth/login",
56
+ body: { email: "string", password: "string" },
57
+ response: { access_token: "string", refresh_token: "string", user: "object" }
58
+ },
59
+ {
60
+ method: "POST",
61
+ path: "/api/auth/register",
62
+ body: { email: "string", password: "string" },
63
+ response: { access_token: "string", refresh_token: "string", user: "object" }
64
+ },
65
+ {
66
+ method: "GET",
67
+ path: "/api/auth/user",
68
+ headers: { Authorization: "Bearer TOKEN" },
69
+ response: { id: "string", email: "string", role: "string" }
70
+ },
71
+ {
72
+ method: "POST",
73
+ path: "/api/stripe/create-checkout-session",
74
+ body: { price_id: "string", success_url: "string" },
75
+ response: { sessionId: "string", url: "string" }
76
+ },
77
+ {
78
+ method: "GET",
79
+ path: "/api/usage/balance",
80
+ headers: { Authorization: "Bearer TOKEN" },
81
+ response: { balance: "number", currency: "string" }
82
+ }
83
+ ],
84
+ test_commands: {
85
+ health_check: `curl http://localhost:${port}/health`,
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)"`
88
+ }
89
+ };
90
+ }
91
+
92
+ function getServerStatus(port = 3300) {
93
+ const http = require('http');
94
+
95
+ return new Promise((resolve) => {
96
+ const options = {
97
+ hostname: 'localhost',
98
+ port: port,
99
+ path: '/health',
100
+ method: 'GET',
101
+ timeout: 1000
102
+ };
103
+
104
+ const req = http.request(options, (res) => {
105
+ let data = '';
106
+ res.on('data', chunk => data += chunk);
107
+ res.on('end', () => {
108
+ try {
109
+ const parsed = JSON.parse(data);
110
+ resolve({
111
+ running: true,
112
+ port: port,
113
+ health: parsed,
114
+ url: `http://localhost:${port}`,
115
+ docs: `http://localhost:${port}/docs`
116
+ });
117
+ } catch {
118
+ resolve({ running: true, port: port, error: 'Invalid response' });
119
+ }
120
+ });
121
+ });
122
+
123
+ req.on('error', () => {
124
+ resolve({
125
+ running: false,
126
+ port: port,
127
+ message: 'Server not running',
128
+ start_command: `npx spaps local --port ${port}`
129
+ });
130
+ });
131
+
132
+ req.on('timeout', () => {
133
+ req.destroy();
134
+ resolve({
135
+ running: false,
136
+ port: port,
137
+ message: 'Server timeout',
138
+ start_command: `npx spaps local --port ${port}`
139
+ });
140
+ });
141
+
142
+ req.end();
143
+ });
144
+ }
145
+
146
+ async function runQuickTest(port = 3300) {
147
+ const results = [];
148
+
149
+ // Check server
150
+ const status = await getServerStatus(port);
151
+ results.push({
152
+ test: 'server_status',
153
+ success: status.running,
154
+ details: status
155
+ });
156
+
157
+ if (!status.running) {
158
+ return {
159
+ success: false,
160
+ message: 'Server not running',
161
+ fix: `npx spaps local --port ${port}`,
162
+ results
163
+ };
164
+ }
165
+
166
+ // Try HTTP request
167
+ try {
168
+ const http = require('http');
169
+ const loginResult = await new Promise((resolve, reject) => {
170
+ const postData = JSON.stringify({
171
+ email: 'test@example.com',
172
+ password: 'password'
173
+ });
174
+
175
+ const options = {
176
+ hostname: 'localhost',
177
+ port: port,
178
+ path: '/api/auth/login',
179
+ method: 'POST',
180
+ headers: {
181
+ 'Content-Type': 'application/json',
182
+ 'Content-Length': Buffer.byteLength(postData)
183
+ }
184
+ };
185
+
186
+ const req = http.request(options, (res) => {
187
+ let data = '';
188
+ res.on('data', chunk => data += chunk);
189
+ res.on('end', () => {
190
+ try {
191
+ resolve(JSON.parse(data));
192
+ } catch {
193
+ reject(new Error('Invalid JSON response'));
194
+ }
195
+ });
196
+ });
197
+
198
+ req.on('error', reject);
199
+ req.write(postData);
200
+ req.end();
201
+ });
202
+
203
+ results.push({
204
+ test: 'login_endpoint',
205
+ success: true,
206
+ response: loginResult
207
+ });
208
+ } catch (error) {
209
+ results.push({
210
+ test: 'login_endpoint',
211
+ success: false,
212
+ error: error.message
213
+ });
214
+ }
215
+
216
+ // Check SDK availability
217
+ try {
218
+ require.resolve('spaps-sdk');
219
+ results.push({
220
+ test: 'sdk_installed',
221
+ success: true,
222
+ message: 'spaps-sdk is installed'
223
+ });
224
+
225
+ // Try SDK login
226
+ try {
227
+ const { SPAPSClient } = require('spaps-sdk');
228
+ const spaps = new SPAPSClient({ apiUrl: `http://localhost:${port}` });
229
+ const { data } = await spaps.login('test@example.com', 'password');
230
+
231
+ results.push({
232
+ test: 'sdk_login',
233
+ success: true,
234
+ user: data.user
235
+ });
236
+ } catch (error) {
237
+ results.push({
238
+ test: 'sdk_login',
239
+ success: false,
240
+ error: error.message
241
+ });
242
+ }
243
+ } catch {
244
+ results.push({
245
+ test: 'sdk_installed',
246
+ success: false,
247
+ message: 'spaps-sdk not installed',
248
+ fix: 'npm install spaps-sdk'
249
+ });
250
+ }
251
+
252
+ const allSuccess = results.every(r => r.success);
253
+
254
+ return {
255
+ success: allSuccess,
256
+ summary: `${results.filter(r => r.success).length}/${results.length} tests passed`,
257
+ results,
258
+ next_steps: allSuccess ? [
259
+ 'Server is running and SDK is working',
260
+ 'You can now use SPAPS in your application',
261
+ 'See docs at http://localhost:' + port + '/docs'
262
+ ] : [
263
+ 'Fix the failing tests above',
264
+ 'Run: npx spaps test --json to retry'
265
+ ]
266
+ };
267
+ }
268
+
269
+ module.exports = {
270
+ getQuickStartInstructions,
271
+ getServerStatus,
272
+ runQuickTest
273
+ };
@@ -0,0 +1,763 @@
1
+ /**
2
+ * SPAPS Local Server Documentation HTML Generator
3
+ * Generates comprehensive documentation page
4
+ */
5
+
6
+ function generateDocsHTML(port = 3300) {
7
+ return `<!DOCTYPE html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="UTF-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
12
+ <title>SPAPS Documentation - Local Development Mode</title>
13
+ <style>
14
+ * {
15
+ margin: 0;
16
+ padding: 0;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ body {
21
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
22
+ line-height: 1.6;
23
+ color: #333;
24
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
25
+ min-height: 100vh;
26
+ }
27
+
28
+ .container {
29
+ max-width: 1200px;
30
+ margin: 0 auto;
31
+ padding: 2rem;
32
+ }
33
+
34
+ .header {
35
+ background: white;
36
+ border-radius: 12px;
37
+ padding: 2rem;
38
+ margin-bottom: 2rem;
39
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
40
+ }
41
+
42
+ .header h1 {
43
+ color: #764ba2;
44
+ font-size: 2.5rem;
45
+ margin-bottom: 0.5rem;
46
+ }
47
+
48
+ .header .subtitle {
49
+ color: #666;
50
+ font-size: 1.1rem;
51
+ }
52
+
53
+ .status-badge {
54
+ display: inline-block;
55
+ background: #48bb78;
56
+ color: white;
57
+ padding: 0.25rem 0.75rem;
58
+ border-radius: 20px;
59
+ font-size: 0.9rem;
60
+ margin-top: 1rem;
61
+ }
62
+
63
+ .nav {
64
+ background: white;
65
+ border-radius: 12px;
66
+ padding: 1rem;
67
+ margin-bottom: 2rem;
68
+ box-shadow: 0 5px 15px rgba(0,0,0,0.08);
69
+ position: sticky;
70
+ top: 1rem;
71
+ z-index: 100;
72
+ }
73
+
74
+ .nav ul {
75
+ list-style: none;
76
+ display: flex;
77
+ gap: 2rem;
78
+ overflow-x: auto;
79
+ padding: 0.5rem;
80
+ }
81
+
82
+ .nav a {
83
+ color: #764ba2;
84
+ text-decoration: none;
85
+ font-weight: 500;
86
+ white-space: nowrap;
87
+ transition: color 0.3s;
88
+ }
89
+
90
+ .nav a:hover {
91
+ color: #667eea;
92
+ }
93
+
94
+ .content {
95
+ background: white;
96
+ border-radius: 12px;
97
+ padding: 2rem;
98
+ margin-bottom: 2rem;
99
+ box-shadow: 0 5px 15px rgba(0,0,0,0.08);
100
+ }
101
+
102
+ h2 {
103
+ color: #764ba2;
104
+ margin-top: 2rem;
105
+ margin-bottom: 1rem;
106
+ padding-bottom: 0.5rem;
107
+ border-bottom: 2px solid #f0f0f0;
108
+ }
109
+
110
+ h3 {
111
+ color: #667eea;
112
+ margin-top: 1.5rem;
113
+ margin-bottom: 0.75rem;
114
+ }
115
+
116
+ pre {
117
+ background: #f7f7f7;
118
+ border: 1px solid #e0e0e0;
119
+ border-radius: 8px;
120
+ padding: 1rem;
121
+ overflow-x: auto;
122
+ margin: 1rem 0;
123
+ position: relative;
124
+ }
125
+
126
+ code {
127
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace;
128
+ font-size: 0.9rem;
129
+ background: #f7f7f7;
130
+ padding: 0.2rem 0.4rem;
131
+ border-radius: 4px;
132
+ }
133
+
134
+ pre code {
135
+ background: none;
136
+ padding: 0;
137
+ }
138
+
139
+ .endpoint {
140
+ background: #f9f9f9;
141
+ border-left: 4px solid #667eea;
142
+ padding: 1rem;
143
+ margin: 1rem 0;
144
+ border-radius: 4px;
145
+ }
146
+
147
+ .endpoint strong {
148
+ color: #764ba2;
149
+ font-family: 'SF Mono', Monaco, monospace;
150
+ font-size: 0.95rem;
151
+ }
152
+
153
+ .endpoint .method {
154
+ display: inline-block;
155
+ padding: 0.2rem 0.5rem;
156
+ border-radius: 4px;
157
+ font-weight: bold;
158
+ font-size: 0.85rem;
159
+ margin-right: 0.5rem;
160
+ }
161
+
162
+ .endpoint .method.get { background: #48bb78; color: white; }
163
+ .endpoint .method.post { background: #4299e1; color: white; }
164
+ .endpoint .method.put { background: #ed8936; color: white; }
165
+ .endpoint .method.delete { background: #f56565; color: white; }
166
+
167
+ .tabs {
168
+ display: flex;
169
+ gap: 1rem;
170
+ margin-bottom: 1rem;
171
+ border-bottom: 2px solid #f0f0f0;
172
+ }
173
+
174
+ .tab {
175
+ padding: 0.5rem 1rem;
176
+ cursor: pointer;
177
+ border: none;
178
+ background: none;
179
+ color: #666;
180
+ font-size: 1rem;
181
+ transition: all 0.3s;
182
+ }
183
+
184
+ .tab.active {
185
+ color: #764ba2;
186
+ border-bottom: 2px solid #764ba2;
187
+ margin-bottom: -2px;
188
+ }
189
+
190
+ .tab-content {
191
+ display: none;
192
+ }
193
+
194
+ .tab-content.active {
195
+ display: block;
196
+ }
197
+
198
+ .feature-grid {
199
+ display: grid;
200
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
201
+ gap: 1.5rem;
202
+ margin: 2rem 0;
203
+ }
204
+
205
+ .feature-card {
206
+ background: #f9f9f9;
207
+ padding: 1.5rem;
208
+ border-radius: 8px;
209
+ transition: transform 0.3s;
210
+ }
211
+
212
+ .feature-card:hover {
213
+ transform: translateY(-2px);
214
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
215
+ }
216
+
217
+ .feature-card h3 {
218
+ margin-top: 0;
219
+ }
220
+
221
+ .copy-button {
222
+ position: absolute;
223
+ top: 0.5rem;
224
+ right: 0.5rem;
225
+ background: #667eea;
226
+ color: white;
227
+ border: none;
228
+ padding: 0.25rem 0.75rem;
229
+ border-radius: 4px;
230
+ cursor: pointer;
231
+ font-size: 0.85rem;
232
+ transition: background 0.3s;
233
+ }
234
+
235
+ .copy-button:hover {
236
+ background: #764ba2;
237
+ }
238
+
239
+ .copy-button.copied {
240
+ background: #48bb78;
241
+ }
242
+
243
+ .alert {
244
+ background: #fef5e7;
245
+ border: 1px solid #f9e79f;
246
+ border-radius: 8px;
247
+ padding: 1rem;
248
+ margin: 1rem 0;
249
+ }
250
+
251
+ .alert.info {
252
+ background: #e8f4fd;
253
+ border-color: #bee5eb;
254
+ }
255
+
256
+ .alert.success {
257
+ background: #d4edda;
258
+ border-color: #c3e6cb;
259
+ }
260
+
261
+ .footer {
262
+ text-align: center;
263
+ color: white;
264
+ padding: 2rem;
265
+ }
266
+
267
+ .footer a {
268
+ color: white;
269
+ text-decoration: underline;
270
+ }
271
+ </style>
272
+ </head>
273
+ <body>
274
+ <div class="container">
275
+ <div class="header">
276
+ <h1>๐Ÿ  SPAPS Documentation</h1>
277
+ <p class="subtitle">Sweet Potato Authentication & Payment Service</p>
278
+ <span class="status-badge">โœ… Local Mode Active - Port ${port}</span>
279
+ </div>
280
+
281
+ <nav class="nav">
282
+ <ul>
283
+ <li><a href="#quickstart">Quick Start</a></li>
284
+ <li><a href="#sdk">SDK Setup</a></li>
285
+ <li><a href="#authentication">Authentication</a></li>
286
+ <li><a href="#payments">Payments</a></li>
287
+ <li><a href="#endpoints">API Endpoints</a></li>
288
+ <li><a href="#examples">Examples</a></li>
289
+ <li><a href="#testing">Testing</a></li>
290
+ </ul>
291
+ </nav>
292
+
293
+ <div class="content">
294
+ <div class="alert info">
295
+ <strong>๐Ÿš€ Local Development Mode</strong><br>
296
+ You're running in local mode. No API keys, database, or external services required!
297
+ All responses are mocked for rapid development.
298
+ </div>
299
+
300
+ <section id="quickstart">
301
+ <h2>Quick Start</h2>
302
+
303
+ <h3>1. Install the SDK</h3>
304
+ <pre><code>npm install spaps-sdk
305
+ # or
306
+ yarn add spaps-sdk</code></pre>
307
+
308
+ <h3>2. Initialize the Client</h3>
309
+ <div class="tabs">
310
+ <button class="tab active" onclick="showTab(event, 'js-init')">JavaScript</button>
311
+ <button class="tab" onclick="showTab(event, 'ts-init')">TypeScript</button>
312
+ <button class="tab" onclick="showTab(event, 'react-init')">React</button>
313
+ </div>
314
+
315
+ <div id="js-init" class="tab-content active">
316
+ <pre><code>const { SPAPSClient } = require('spaps-sdk');
317
+
318
+ // Auto-detects local mode - no config needed!
319
+ const spaps = new SPAPSClient();
320
+
321
+ // Login
322
+ const { data } = await spaps.login('user@example.com', 'password');
323
+ console.log('User:', data.user);
324
+ console.log('Token:', data.access_token);</code></pre>
325
+ </div>
326
+
327
+ <div id="ts-init" class="tab-content">
328
+ <pre><code>import { SPAPSClient } from 'spaps-sdk';
329
+
330
+ // TypeScript types included
331
+ const spaps = new SPAPSClient({
332
+ apiUrl: 'http://localhost:${port}'
333
+ });
334
+
335
+ // Fully typed responses
336
+ const { data } = await spaps.login('user@example.com', 'password');
337
+ console.log('User ID:', data.user.id);</code></pre>
338
+ </div>
339
+
340
+ <div id="react-init" class="tab-content">
341
+ <pre><code>import { SPAPSClient } from 'spaps-sdk';
342
+ import { createContext, useContext } from 'react';
343
+
344
+ const spaps = new SPAPSClient();
345
+ const SpapsContext = createContext(spaps);
346
+
347
+ export function SpapsProvider({ children }) {
348
+ return (
349
+ &lt;SpapsContext.Provider value={spaps}&gt;
350
+ {children}
351
+ &lt;/SpapsContext.Provider&gt;
352
+ );
353
+ }
354
+
355
+ export const useSpaps = () => useContext(SpapsContext);</code></pre>
356
+ </div>
357
+ </section>
358
+
359
+ <section id="sdk">
360
+ <h2>SDK Setup & Configuration</h2>
361
+
362
+ <h3>Installation</h3>
363
+ <pre><code>npm install spaps-sdk</code></pre>
364
+
365
+ <h3>Configuration Options</h3>
366
+ <pre><code>const spaps = new SPAPSClient({
367
+ // API endpoint (auto-detected from env)
368
+ apiUrl: 'http://localhost:${port}',
369
+
370
+ // API key (not needed for localhost)
371
+ apiKey: 'spaps_live_abc123...',
372
+
373
+ // Request timeout in milliseconds
374
+ timeout: 10000,
375
+
376
+ // Auto-detect local mode (default: true)
377
+ autoDetect: true
378
+ });</code></pre>
379
+
380
+ <h3>Environment Variables</h3>
381
+ <pre><code># .env
382
+ SPAPS_API_URL=http://localhost:${port}
383
+ SPAPS_API_KEY=your_api_key_here
384
+
385
+ # Next.js
386
+ NEXT_PUBLIC_SPAPS_API_URL=http://localhost:${port}</code></pre>
387
+
388
+ <div class="feature-grid">
389
+ <div class="feature-card">
390
+ <h3>๐Ÿ”Œ Auto-Detection</h3>
391
+ <p>SDK automatically detects local mode when URL contains localhost or 127.0.0.1</p>
392
+ </div>
393
+ <div class="feature-card">
394
+ <h3>๐Ÿ”‘ No API Key</h3>
395
+ <p>Local mode doesn't require API keys - perfect for rapid development</p>
396
+ </div>
397
+ <div class="feature-card">
398
+ <h3>๐Ÿ“ฆ TypeScript</h3>
399
+ <p>Full TypeScript support with type definitions included</p>
400
+ </div>
401
+ <div class="feature-card">
402
+ <h3>๐Ÿ”„ Auto-Refresh</h3>
403
+ <p>Tokens automatically refresh when expired</p>
404
+ </div>
405
+ </div>
406
+ </section>
407
+
408
+ <section id="authentication">
409
+ <h2>Authentication</h2>
410
+
411
+ <h3>Email/Password Authentication</h3>
412
+ <pre><code>// Register new user
413
+ const { data } = await spaps.register('user@example.com', 'password');
414
+ console.log('New user:', data.user);
415
+
416
+ // Login existing user
417
+ const { data } = await spaps.login('user@example.com', 'password');
418
+ console.log('Access token:', data.access_token);
419
+
420
+ // Get current user
421
+ const user = await spaps.getUser();
422
+ console.log('Current user:', user.data);
423
+
424
+ // Logout
425
+ await spaps.logout();</code></pre>
426
+
427
+ <h3>Wallet Authentication</h3>
428
+ <pre><code>// Solana wallet
429
+ await spaps.walletSignIn(
430
+ walletAddress,
431
+ signature,
432
+ message,
433
+ 'solana'
434
+ );
435
+
436
+ // Ethereum wallet
437
+ await spaps.walletSignIn(
438
+ walletAddress,
439
+ signature,
440
+ message,
441
+ 'ethereum'
442
+ );</code></pre>
443
+
444
+ <h3>Token Management</h3>
445
+ <pre><code>// Check if authenticated
446
+ if (spaps.isAuthenticated()) {
447
+ console.log('User is logged in');
448
+ }
449
+
450
+ // Get access token
451
+ const token = spaps.getAccessToken();
452
+
453
+ // Set token manually
454
+ spaps.setAccessToken(token);
455
+
456
+ // Refresh token
457
+ await spaps.refresh();</code></pre>
458
+ </section>
459
+
460
+ <section id="payments">
461
+ <h2>Payment Integration</h2>
462
+
463
+ <h3>Stripe Checkout</h3>
464
+ <pre><code>// Create checkout session
465
+ const session = await spaps.createCheckoutSession(
466
+ 'price_123abc', // Stripe price ID
467
+ 'http://localhost:3000/success', // Success URL
468
+ 'http://localhost:3000/cancel' // Cancel URL (optional)
469
+ );
470
+
471
+ // Redirect to Stripe
472
+ window.location.href = session.data.url;</code></pre>
473
+
474
+ <h3>Subscription Management</h3>
475
+ <pre><code>// Get current subscription
476
+ const subscription = await spaps.getSubscription();
477
+ console.log('Status:', subscription.data.status);
478
+ console.log('Plan:', subscription.data.plan);
479
+
480
+ // Cancel subscription
481
+ await spaps.cancelSubscription();</code></pre>
482
+
483
+ <h3>Usage Tracking</h3>
484
+ <pre><code>// Check balance
485
+ const balance = await spaps.getUsageBalance();
486
+ console.log('Credits:', balance.data.balance);
487
+
488
+ // Record usage
489
+ await spaps.recordUsage('api-call', 1);
490
+ await spaps.recordUsage('image-generation', 10);</code></pre>
491
+ </section>
492
+
493
+ <section id="endpoints">
494
+ <h2>API Endpoints</h2>
495
+
496
+ <h3>Authentication</h3>
497
+ <div class="endpoint">
498
+ <span class="method post">POST</span>
499
+ <strong>/api/auth/register</strong> - Register new user
500
+ </div>
501
+ <div class="endpoint">
502
+ <span class="method post">POST</span>
503
+ <strong>/api/auth/login</strong> - Login with email/password
504
+ </div>
505
+ <div class="endpoint">
506
+ <span class="method post">POST</span>
507
+ <strong>/api/auth/wallet-sign-in</strong> - Wallet authentication
508
+ </div>
509
+ <div class="endpoint">
510
+ <span class="method post">POST</span>
511
+ <strong>/api/auth/refresh</strong> - Refresh access token
512
+ </div>
513
+ <div class="endpoint">
514
+ <span class="method post">POST</span>
515
+ <strong>/api/auth/logout</strong> - Logout user
516
+ </div>
517
+ <div class="endpoint">
518
+ <span class="method get">GET</span>
519
+ <strong>/api/auth/user</strong> - Get current user
520
+ </div>
521
+
522
+ <h3>Payments</h3>
523
+ <div class="endpoint">
524
+ <span class="method post">POST</span>
525
+ <strong>/api/stripe/create-checkout-session</strong> - Create Stripe checkout
526
+ </div>
527
+ <div class="endpoint">
528
+ <span class="method get">GET</span>
529
+ <strong>/api/stripe/subscription</strong> - Get subscription status
530
+ </div>
531
+ <div class="endpoint">
532
+ <span class="method delete">DELETE</span>
533
+ <strong>/api/stripe/subscription</strong> - Cancel subscription
534
+ </div>
535
+
536
+ <h3>Usage</h3>
537
+ <div class="endpoint">
538
+ <span class="method get">GET</span>
539
+ <strong>/api/usage/balance</strong> - Get usage balance
540
+ </div>
541
+ <div class="endpoint">
542
+ <span class="method post">POST</span>
543
+ <strong>/api/usage/record</strong> - Record usage event
544
+ </div>
545
+
546
+ <h3>Health</h3>
547
+ <div class="endpoint">
548
+ <span class="method get">GET</span>
549
+ <strong>/health</strong> - Health check
550
+ </div>
551
+ <div class="endpoint">
552
+ <span class="method get">GET</span>
553
+ <strong>/health/local-mode</strong> - Local mode status
554
+ </div>
555
+ </section>
556
+
557
+ <section id="examples">
558
+ <h2>Complete Examples</h2>
559
+
560
+ <h3>React Login Component</h3>
561
+ <pre><code>import { useState } from 'react';
562
+ import { useSpaps } from './contexts/SpapsContext';
563
+
564
+ function LoginForm() {
565
+ const spaps = useSpaps();
566
+ const [email, setEmail] = useState('');
567
+ const [password, setPassword] = useState('');
568
+ const [loading, setLoading] = useState(false);
569
+
570
+ const handleSubmit = async (e) => {
571
+ e.preventDefault();
572
+ setLoading(true);
573
+
574
+ try {
575
+ const { data } = await spaps.login(email, password);
576
+ console.log('Logged in:', data.user);
577
+ // Redirect to dashboard
578
+ } catch (error) {
579
+ console.error('Login failed:', error);
580
+ } finally {
581
+ setLoading(false);
582
+ }
583
+ };
584
+
585
+ return (
586
+ &lt;form onSubmit={handleSubmit}&gt;
587
+ &lt;input
588
+ type="email"
589
+ value={email}
590
+ onChange={(e) => setEmail(e.target.value)}
591
+ required
592
+ /&gt;
593
+ &lt;input
594
+ type="password"
595
+ value={password}
596
+ onChange={(e) => setPassword(e.target.value)}
597
+ required
598
+ /&gt;
599
+ &lt;button type="submit" disabled={loading}&gt;
600
+ {loading ? 'Logging in...' : 'Login'}
601
+ &lt;/button&gt;
602
+ &lt;/form&gt;
603
+ );
604
+ }</code></pre>
605
+
606
+ <h3>Express.js Middleware</h3>
607
+ <pre><code>const express = require('express');
608
+ const { SPAPSClient } = require('spaps-sdk');
609
+
610
+ const app = express();
611
+ const spaps = new SPAPSClient();
612
+
613
+ // Auth middleware
614
+ async function requireAuth(req, res, next) {
615
+ const token = req.headers.authorization?.split(' ')[1];
616
+
617
+ if (!token) {
618
+ return res.status(401).json({ error: 'No token' });
619
+ }
620
+
621
+ try {
622
+ spaps.setAccessToken(token);
623
+ const { data } = await spaps.getUser();
624
+ req.user = data;
625
+ next();
626
+ } catch (error) {
627
+ res.status(401).json({ error: 'Invalid token' });
628
+ }
629
+ }
630
+
631
+ // Protected route
632
+ app.get('/api/profile', requireAuth, (req, res) => {
633
+ res.json(req.user);
634
+ });</code></pre>
635
+
636
+ <h3>Next.js Server Action</h3>
637
+ <pre><code>'use server';
638
+
639
+ import { SPAPSClient } from 'spaps-sdk';
640
+ import { cookies } from 'next/headers';
641
+
642
+ const spaps = new SPAPSClient();
643
+
644
+ export async function loginAction(email: string, password: string) {
645
+ try {
646
+ const { data } = await spaps.login(email, password);
647
+
648
+ // Store token in cookie
649
+ cookies().set('spaps_token', data.access_token, {
650
+ httpOnly: true,
651
+ secure: process.env.NODE_ENV === 'production',
652
+ sameSite: 'lax',
653
+ maxAge: 60 * 60 * 24 * 7 // 1 week
654
+ });
655
+
656
+ return { success: true, user: data.user };
657
+ } catch (error) {
658
+ return { success: false, error: 'Invalid credentials' };
659
+ }
660
+ }</code></pre>
661
+ </section>
662
+
663
+ <section id="testing">
664
+ <h2>Testing</h2>
665
+
666
+ <h3>Test with cURL</h3>
667
+ <pre><code># Health check
668
+ curl http://localhost:${port}/health
669
+
670
+ # Login
671
+ curl -X POST http://localhost:${port}/api/auth/login \\
672
+ -H "Content-Type: application/json" \\
673
+ -d '{"email":"test@example.com","password":"password"}'
674
+
675
+ # Get user (with token)
676
+ curl http://localhost:${port}/api/auth/user \\
677
+ -H "Authorization: Bearer YOUR_TOKEN"</code></pre>
678
+
679
+ <h3>Test with SDK</h3>
680
+ <pre><code>// test.js
681
+ const { SPAPSClient } = require('spaps-sdk');
682
+
683
+ async function test() {
684
+ const spaps = new SPAPSClient();
685
+
686
+ // Test login
687
+ const { data } = await spaps.login('test@example.com', 'password');
688
+ console.log('โœ… Login successful:', data.user);
689
+
690
+ // Test authenticated request
691
+ const user = await spaps.getUser();
692
+ console.log('โœ… Got user:', user.data);
693
+
694
+ // Test Stripe
695
+ const session = await spaps.createCheckoutSession(
696
+ 'price_123',
697
+ 'http://localhost:3000/success'
698
+ );
699
+ console.log('โœ… Checkout URL:', session.data.url);
700
+ }
701
+
702
+ test().catch(console.error);</code></pre>
703
+
704
+ <div class="alert success">
705
+ <strong>๐Ÿ’ก Pro Tip:</strong> In local mode, all endpoints return successful mock responses.
706
+ Use any email/password combination for testing.
707
+ </div>
708
+ </section>
709
+ </div>
710
+
711
+ <div class="footer">
712
+ <p>
713
+ <strong>SPAPS v0.2.6</strong> |
714
+ <a href="https://github.com/yourusername/sweet-potato">GitHub</a> |
715
+ <a href="https://sweetpotato.dev">Website</a> |
716
+ <a href="https://discord.gg/sweetpotato">Discord</a>
717
+ </p>
718
+ <p>Made with ๐Ÿ  by the Sweet Potato team</p>
719
+ </div>
720
+ </div>
721
+
722
+ <script>
723
+ function showTab(event, tabId) {
724
+ // Hide all tab contents
725
+ const contents = document.querySelectorAll('.tab-content');
726
+ contents.forEach(content => content.classList.remove('active'));
727
+
728
+ // Remove active from all tabs
729
+ const tabs = document.querySelectorAll('.tab');
730
+ tabs.forEach(tab => tab.classList.remove('active'));
731
+
732
+ // Show selected tab
733
+ document.getElementById(tabId).classList.add('active');
734
+ event.target.classList.add('active');
735
+ }
736
+
737
+ // Add copy buttons to code blocks
738
+ document.addEventListener('DOMContentLoaded', () => {
739
+ const codeBlocks = document.querySelectorAll('pre');
740
+ codeBlocks.forEach(block => {
741
+ const button = document.createElement('button');
742
+ button.className = 'copy-button';
743
+ button.textContent = 'Copy';
744
+ button.onclick = () => {
745
+ const code = block.querySelector('code').textContent;
746
+ navigator.clipboard.writeText(code).then(() => {
747
+ button.textContent = 'Copied!';
748
+ button.classList.add('copied');
749
+ setTimeout(() => {
750
+ button.textContent = 'Copy';
751
+ button.classList.remove('copied');
752
+ }, 2000);
753
+ });
754
+ };
755
+ block.appendChild(button);
756
+ });
757
+ });
758
+ </script>
759
+ </body>
760
+ </html>`;
761
+ }
762
+
763
+ module.exports = { generateDocsHTML };
@@ -8,6 +8,7 @@
8
8
  const express = require('express');
9
9
  const cors = require('cors');
10
10
  const chalk = require('chalk');
11
+ const { generateDocsHTML } = require('./docs-html');
11
12
 
12
13
  class LocalServer {
13
14
  constructor(options = {}) {
@@ -167,100 +168,7 @@ class LocalServer {
167
168
 
168
169
  // Documentation endpoint
169
170
  this.app.get('/docs', (req, res) => {
170
- res.send(`
171
- <!DOCTYPE html>
172
- <html>
173
- <head>
174
- <title>SPAPS Local Mode</title>
175
- <style>
176
- body {
177
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
178
- max-width: 800px;
179
- margin: 50px auto;
180
- padding: 20px;
181
- line-height: 1.6;
182
- }
183
- h1 { color: #ff6b6b; }
184
- code {
185
- background: #f4f4f4;
186
- padding: 2px 6px;
187
- border-radius: 3px;
188
- }
189
- pre {
190
- background: #f4f4f4;
191
- padding: 15px;
192
- border-radius: 5px;
193
- overflow-x: auto;
194
- }
195
- .endpoint {
196
- margin: 20px 0;
197
- padding: 15px;
198
- border-left: 3px solid #ff6b6b;
199
- background: #fff9f9;
200
- }
201
- </style>
202
- </head>
203
- <body>
204
- <h1>๐Ÿ  SPAPS Local Development Mode</h1>
205
- <p>You're running in <strong>local development mode</strong>. No API keys required!</p>
206
-
207
- <h2>Quick Start</h2>
208
- <pre>
209
- // No configuration needed!
210
- const response = await fetch('http://localhost:${this.port}/api/auth/login', {
211
- method: 'POST',
212
- headers: { 'Content-Type': 'application/json' },
213
- body: JSON.stringify({
214
- email: 'test@example.com',
215
- password: 'password'
216
- })
217
- });
218
- const { access_token, user } = await response.json();
219
- </pre>
220
-
221
- <h2>Available Endpoints</h2>
222
-
223
- <div class="endpoint">
224
- <strong>GET /health</strong> - Health check
225
- </div>
226
-
227
- <div class="endpoint">
228
- <strong>POST /api/auth/login</strong> - Email/password login
229
- </div>
230
-
231
- <div class="endpoint">
232
- <strong>POST /api/auth/register</strong> - Create account
233
- </div>
234
-
235
- <div class="endpoint">
236
- <strong>POST /api/auth/wallet-sign-in</strong> - Wallet authentication
237
- </div>
238
-
239
- <div class="endpoint">
240
- <strong>GET /api/auth/user</strong> - Get current user
241
- </div>
242
-
243
- <div class="endpoint">
244
- <strong>POST /api/stripe/create-checkout-session</strong> - Create payment session
245
- </div>
246
-
247
- <div class="endpoint">
248
- <strong>GET /api/usage/balance</strong> - Check usage balance
249
- </div>
250
-
251
- <h2>Test Users</h2>
252
- <p>Switch between test users by adding <code>?_user=admin</code> or <code>?_user=premium</code> to any request.</p>
253
-
254
- <h2>Environment</h2>
255
- <pre>
256
- Mode: ${process.env.NODE_ENV || 'development'}
257
- Port: ${this.port}
258
- Auto-Auth: Enabled
259
- CORS: Enabled (all origins)
260
- </pre>
261
- </body>
262
- </html>
263
- `);
171
+ res.send(generateDocsHTML(this.port));
264
172
  });
265
173
 
266
174
  // Catch-all for unimplemented routes