spaps-sdk 0.1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,154 +1,909 @@
1
- // src/index.ts
2
- import axios from "axios";
3
- var SPAPSClient = class {
4
- client;
5
- apiKey;
6
- accessToken;
7
- refreshToken;
8
- _isLocalMode = false;
9
- constructor(config = {}) {
10
- const apiUrl = config.apiUrl || process.env.SPAPS_API_URL || process.env.NEXT_PUBLIC_SPAPS_API_URL;
11
- if (!apiUrl || apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1")) {
12
- this._isLocalMode = true;
13
- this.client = axios.create({
14
- baseURL: apiUrl || "http://localhost:3300",
15
- timeout: config.timeout || 1e4,
16
- headers: {
17
- "Content-Type": "application/json"
18
- }
19
- });
1
+ // types.ts
2
+ var SweetPotatoAPIError = class extends Error {
3
+ constructor(message, code, status, details) {
4
+ super(message);
5
+ this.name = "SweetPotatoAPIError";
6
+ if (code !== void 0) this.code = code;
7
+ if (status !== void 0) this.status = status;
8
+ if (details !== void 0) this.details = details;
9
+ }
10
+ };
11
+
12
+ // client.ts
13
+ function detectLocalEnvironment() {
14
+ var _a;
15
+ if (typeof process !== "undefined" && process.env) {
16
+ if (process.env.NODE_ENV === "development" || process.env.SPAPS_LOCAL_MODE === "true") {
17
+ return true;
18
+ }
19
+ }
20
+ if (typeof globalThis !== "undefined" && ((_a = globalThis.window) == null ? void 0 : _a.location)) {
21
+ const hostname = globalThis.window.location.hostname;
22
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".local");
23
+ }
24
+ return false;
25
+ }
26
+ var HttpClient = class {
27
+ constructor(config) {
28
+ var _a, _b;
29
+ const localMode = (_b = config.localMode) != null ? _b : ((_a = config.apiUrl) == null ? void 0 : _a.includes("localhost")) || detectLocalEnvironment();
30
+ this.config = {
31
+ timeout: 3e4,
32
+ retries: 3,
33
+ ...config,
34
+ localMode
35
+ };
36
+ this.isLocalMode = this.config.localMode || false;
37
+ this.accessToken = void 0;
38
+ if (this.isLocalMode && typeof console !== "undefined") {
39
+ console.log("[SPAPS SDK] Running in local development mode - authentication will be automatic");
40
+ }
41
+ }
42
+ /**
43
+ * Set the access token for authenticated requests
44
+ */
45
+ setAccessToken(token) {
46
+ this.accessToken = token;
47
+ }
48
+ /**
49
+ * Clear the access token
50
+ */
51
+ clearAccessToken() {
52
+ this.accessToken = void 0;
53
+ }
54
+ /**
55
+ * Make an HTTP request with automatic retries and error handling
56
+ */
57
+ async request(options) {
58
+ var _a, _b, _c, _d;
59
+ const { method, url, data, headers = {}, requiresAuth = false } = options;
60
+ const fullUrl = url.startsWith("http") ? url : `${this.config.apiUrl.replace(/\/$/, "")}${url}`;
61
+ const requestHeaders = {
62
+ "Content-Type": "application/json",
63
+ ...headers
64
+ };
65
+ if (this.config.apiKey) {
66
+ requestHeaders["x-api-key"] = this.config.apiKey;
67
+ } else if (this.isLocalMode) {
68
+ requestHeaders["x-local-mode"] = "true";
20
69
  } else {
21
- if (!config.apiKey && !process.env.SPAPS_API_KEY) {
22
- console.warn("\u26A0\uFE0F SPAPS: No API key provided. Some features may not work.");
23
- }
24
- this.apiKey = config.apiKey || process.env.SPAPS_API_KEY;
25
- this.client = axios.create({
26
- baseURL: apiUrl,
27
- timeout: config.timeout || 1e4,
28
- headers: {
29
- "Content-Type": "application/json",
30
- ...this.apiKey && { "X-API-Key": this.apiKey }
31
- }
32
- });
70
+ console.warn("[SPAPS SDK] No API key provided and not in local mode");
33
71
  }
34
- this.client.interceptors.request.use((config2) => {
35
- if (this.accessToken && !config2.headers.Authorization) {
36
- config2.headers.Authorization = `Bearer ${this.accessToken}`;
37
- }
38
- return config2;
39
- });
40
- this.client.interceptors.response.use(
41
- (response) => response,
42
- async (error) => {
43
- if (error.response?.status === 401 && this.refreshToken) {
44
- try {
45
- const { data } = await this.refresh(this.refreshToken);
46
- this.accessToken = data.access_token;
47
- this.refreshToken = data.refresh_token;
48
- if (error.config) {
49
- error.config.headers.Authorization = `Bearer ${this.accessToken}`;
50
- return this.client.request(error.config);
51
- }
52
- } catch (refreshError) {
53
- this.accessToken = void 0;
54
- this.refreshToken = void 0;
72
+ if (requiresAuth && this.accessToken) {
73
+ requestHeaders["Authorization"] = `Bearer ${this.accessToken}`;
74
+ }
75
+ let lastError = null;
76
+ for (let attempt = 1; attempt <= this.config.retries; attempt++) {
77
+ try {
78
+ const controller = new AbortController();
79
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
80
+ const requestInit = {
81
+ method,
82
+ headers: requestHeaders,
83
+ signal: controller.signal
84
+ };
85
+ if (data && (method === "POST" || method === "PUT" || method === "PATCH")) {
86
+ requestInit.body = JSON.stringify(data);
87
+ }
88
+ const response = await fetch(fullUrl, requestInit);
89
+ clearTimeout(timeoutId);
90
+ if (!response) {
91
+ throw new Error("No response received");
92
+ }
93
+ let responseData;
94
+ const contentType = (_a = response.headers) == null ? void 0 : _a.get("content-type");
95
+ if (contentType && contentType.includes("application/json")) {
96
+ responseData = await response.json();
97
+ } else {
98
+ throw new SweetPotatoAPIError(
99
+ "Invalid response format: expected JSON",
100
+ "INVALID_RESPONSE_FORMAT",
101
+ response.status
102
+ );
103
+ }
104
+ if (!(response == null ? void 0 : response.ok)) {
105
+ throw new SweetPotatoAPIError(
106
+ ((_b = responseData.error) == null ? void 0 : _b.message) || `HTTP ${response == null ? void 0 : response.status}: ${response == null ? void 0 : response.statusText}`,
107
+ ((_c = responseData.error) == null ? void 0 : _c.code) || "HTTP_ERROR",
108
+ response == null ? void 0 : response.status,
109
+ (_d = responseData.error) == null ? void 0 : _d.details
110
+ );
111
+ }
112
+ return responseData;
113
+ } catch (error) {
114
+ lastError = error;
115
+ if (error instanceof SweetPotatoAPIError) {
116
+ if (error.status && error.status >= 400 && error.status < 500) {
117
+ throw error;
55
118
  }
56
119
  }
57
- return Promise.reject(error);
120
+ if (attempt === this.config.retries) {
121
+ break;
122
+ }
123
+ const delayMs = Math.min(1e3 * Math.pow(2, attempt - 1), 1e4);
124
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
58
125
  }
126
+ }
127
+ if (lastError instanceof SweetPotatoAPIError) {
128
+ throw lastError;
129
+ }
130
+ throw new SweetPotatoAPIError(
131
+ (lastError == null ? void 0 : lastError.message) || "Request failed after all retries",
132
+ "REQUEST_FAILED",
133
+ void 0,
134
+ { originalError: lastError }
59
135
  );
60
136
  }
61
- // Authentication Methods
62
- async login(email, password) {
63
- const response = await this.client.post("/api/auth/login", {
64
- email,
65
- password
137
+ /**
138
+ * Convenience method for GET requests
139
+ */
140
+ async get(url, requiresAuth = false) {
141
+ return this.request({
142
+ method: "GET",
143
+ url,
144
+ requiresAuth
66
145
  });
67
- this.accessToken = response.data.access_token;
68
- this.refreshToken = response.data.refresh_token;
69
- return response;
70
- }
71
- async register(email, password) {
72
- const response = await this.client.post("/api/auth/register", {
73
- email,
74
- password
146
+ }
147
+ /**
148
+ * Convenience method for POST requests
149
+ */
150
+ async post(url, data, requiresAuth = false) {
151
+ return this.request({
152
+ method: "POST",
153
+ url,
154
+ data,
155
+ requiresAuth
75
156
  });
76
- this.accessToken = response.data.access_token;
77
- this.refreshToken = response.data.refresh_token;
78
- return response;
79
157
  }
80
- async walletSignIn(walletAddress, signature, message, chainType = "solana") {
81
- const response = await this.client.post("/api/auth/wallet-sign-in", {
158
+ /**
159
+ * Convenience method for PUT requests
160
+ */
161
+ async put(url, data, requiresAuth = false) {
162
+ return this.request({
163
+ method: "PUT",
164
+ url,
165
+ data,
166
+ requiresAuth
167
+ });
168
+ }
169
+ /**
170
+ * Convenience method for DELETE requests
171
+ */
172
+ async delete(url, requiresAuth = false) {
173
+ return this.request({
174
+ method: "DELETE",
175
+ url,
176
+ requiresAuth
177
+ });
178
+ }
179
+ };
180
+
181
+ // auth.ts
182
+ var AuthService = class {
183
+ constructor(httpClient) {
184
+ this.httpClient = httpClient;
185
+ }
186
+ /**
187
+ * Get a nonce for wallet signature
188
+ * @param walletAddress - The wallet address to generate a nonce for
189
+ * @returns Promise<NonceResponse>
190
+ */
191
+ async getNonce(walletAddress) {
192
+ var _a;
193
+ const response = await this.httpClient.post("/api/auth/nonce", {
194
+ wallet_address: walletAddress
195
+ });
196
+ if (!response.success || !response.data) {
197
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to generate nonce");
198
+ }
199
+ return response.data;
200
+ }
201
+ /**
202
+ * Sign in with wallet signature
203
+ * @param request - Wallet sign-in request data
204
+ * @returns Promise<AuthResponse>
205
+ */
206
+ async signInWithWallet(request) {
207
+ var _a;
208
+ const response = await this.httpClient.post("/api/auth/wallet-sign-in", request);
209
+ if (!response.success || !response.data) {
210
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Wallet sign-in failed");
211
+ }
212
+ this.httpClient.setAccessToken(response.data.access_token);
213
+ return response.data;
214
+ }
215
+ /**
216
+ * Complete wallet authentication flow
217
+ * This is a convenience method that combines getNonce and signInWithWallet
218
+ * @param walletAddress - The wallet address
219
+ * @param signatureFunction - Function that signs the auth message
220
+ * @param chainType - Optional chain type (will be auto-detected if not provided)
221
+ * @param username - Optional username for new users
222
+ * @returns Promise<AuthResponse>
223
+ */
224
+ async authenticateWallet(walletAddress, signatureFunction, chainType, username) {
225
+ const nonceData = await this.getNonce(walletAddress);
226
+ const signature = await signatureFunction(nonceData.message);
227
+ const request = {
82
228
  wallet_address: walletAddress,
83
229
  signature,
84
- message,
85
- chain_type: chainType
86
- });
87
- this.accessToken = response.data.access_token;
88
- this.refreshToken = response.data.refresh_token;
89
- return response;
230
+ message: nonceData.message
231
+ };
232
+ if (chainType) {
233
+ request.chain_type = chainType;
234
+ }
235
+ if (username) {
236
+ request.username = username;
237
+ }
238
+ return this.signInWithWallet(request);
90
239
  }
91
- async refresh(refreshToken) {
92
- const response = await this.client.post("/api/auth/refresh", {
93
- refresh_token: refreshToken || this.refreshToken
240
+ /**
241
+ * Sign in with email and password
242
+ * @param request - Traditional login request data
243
+ * @returns Promise<AuthResponse>
244
+ */
245
+ async signInWithPassword(request) {
246
+ var _a;
247
+ const response = await this.httpClient.post("/api/auth/login", request);
248
+ if (!response.success || !response.data) {
249
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Login failed");
250
+ }
251
+ this.httpClient.setAccessToken(response.data.access_token);
252
+ return response.data;
253
+ }
254
+ /**
255
+ * Request a magic link for email authentication
256
+ * @param request - Magic link request data
257
+ * @returns Promise<void>
258
+ */
259
+ async requestMagicLink(request) {
260
+ var _a;
261
+ const response = await this.httpClient.post("/api/auth/magic-link", request);
262
+ if (!response.success) {
263
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to send magic link");
264
+ }
265
+ }
266
+ /**
267
+ * Refresh access token using refresh token
268
+ * @param refreshToken - The refresh token
269
+ * @returns Promise<TokenPair>
270
+ */
271
+ async refreshToken(refreshToken) {
272
+ var _a;
273
+ const response = await this.httpClient.post("/api/auth/refresh", {
274
+ refresh_token: refreshToken
94
275
  });
95
- this.accessToken = response.data.access_token;
96
- this.refreshToken = response.data.refresh_token;
97
- return response;
276
+ if (!response.success || !response.data) {
277
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Token refresh failed");
278
+ }
279
+ this.httpClient.setAccessToken(response.data.access_token);
280
+ return response.data;
98
281
  }
282
+ /**
283
+ * Log out and invalidate tokens
284
+ * @returns Promise<void>
285
+ */
99
286
  async logout() {
100
- await this.client.post("/api/auth/logout");
101
- this.accessToken = void 0;
102
- this.refreshToken = void 0;
287
+ var _a;
288
+ try {
289
+ const response = await this.httpClient.post("/api/auth/logout", {}, true);
290
+ if (!response.success) {
291
+ console.warn("Logout API call failed:", ((_a = response.error) == null ? void 0 : _a.message) || "Unknown error");
292
+ }
293
+ } catch (error) {
294
+ console.warn("Logout API call failed:", error);
295
+ } finally {
296
+ this.httpClient.clearAccessToken();
297
+ }
103
298
  }
104
- async getUser() {
105
- return this.client.get("/api/auth/user");
299
+ /**
300
+ * Get current user profile
301
+ * @returns Promise<User>
302
+ */
303
+ async getCurrentUser() {
304
+ var _a;
305
+ const response = await this.httpClient.get("/api/auth/user", true);
306
+ if (!response.success || !response.data) {
307
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to get user profile");
308
+ }
309
+ return response.data.user;
106
310
  }
107
- // Stripe Methods
108
- async createCheckoutSession(priceId, successUrl, cancelUrl) {
109
- return this.client.post("/api/stripe/create-checkout-session", {
110
- price_id: priceId,
111
- success_url: successUrl,
112
- cancel_url: cancelUrl
113
- });
311
+ /**
312
+ * Check if user is currently authenticated
313
+ * @returns boolean
314
+ */
315
+ isAuthenticated() {
316
+ return this.httpClient["accessToken"] !== void 0;
114
317
  }
115
- async getSubscription() {
116
- return this.client.get("/api/stripe/subscription");
318
+ /**
319
+ * Clear authentication state (useful for client-side logout)
320
+ */
321
+ clearAuth() {
322
+ this.httpClient.clearAccessToken();
117
323
  }
118
- async cancelSubscription() {
119
- await this.client.delete("/api/stripe/subscription");
324
+ };
325
+
326
+ // payments.ts
327
+ var PaymentsService = class {
328
+ constructor(httpClient) {
329
+ this.httpClient = httpClient;
120
330
  }
121
- // Usage Methods
122
- async getUsageBalance() {
123
- return this.client.get("/api/usage/balance");
331
+ // Checkout Sessions
332
+ /**
333
+ * Create a new Stripe checkout session
334
+ * @param request - Checkout session creation parameters
335
+ * @returns Promise<CheckoutSession>
336
+ */
337
+ async createCheckoutSession(request) {
338
+ var _a;
339
+ const response = await this.httpClient.post(
340
+ "/api/stripe/checkout-sessions",
341
+ request,
342
+ true
343
+ );
344
+ if (!response.success || !response.data) {
345
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to create checkout session");
346
+ }
347
+ return response.data;
124
348
  }
125
- async recordUsage(feature, amount) {
126
- await this.client.post("/api/usage/record", {
127
- feature,
128
- amount
129
- });
349
+ /**
350
+ * Retrieve a checkout session by ID
351
+ * @param sessionId - The checkout session ID
352
+ * @returns Promise<CheckoutSession>
353
+ */
354
+ async getCheckoutSession(sessionId) {
355
+ var _a;
356
+ const response = await this.httpClient.get(
357
+ `/api/stripe/checkout-sessions/${sessionId}`,
358
+ true
359
+ );
360
+ if (!response.success || !response.data) {
361
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to retrieve checkout session");
362
+ }
363
+ return response.data;
364
+ }
365
+ /**
366
+ * Expire a checkout session
367
+ * @param sessionId - The checkout session ID to expire
368
+ * @returns Promise<{id: string, status: string, expired: boolean}>
369
+ */
370
+ async expireCheckoutSession(sessionId) {
371
+ var _a;
372
+ const response = await this.httpClient.post(
373
+ `/api/stripe/checkout-sessions/${sessionId}/expire`,
374
+ {},
375
+ true
376
+ );
377
+ if (!response.success || !response.data) {
378
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to expire checkout session");
379
+ }
380
+ return response.data;
381
+ }
382
+ /**
383
+ * List checkout sessions for the current user
384
+ * @param options - List options (limit, pagination)
385
+ * @returns Promise<CheckoutSessionListResponse>
386
+ */
387
+ async listCheckoutSessions(options = {}) {
388
+ var _a;
389
+ const params = new URLSearchParams();
390
+ if (options.limit) params.append("limit", options.limit.toString());
391
+ if (options.starting_after) params.append("starting_after", options.starting_after);
392
+ const url = `/api/stripe/checkout-sessions${params.toString() ? `?${params.toString()}` : ""}`;
393
+ const response = await this.httpClient.get(url, true);
394
+ if (!response.success || !response.data) {
395
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to list checkout sessions");
396
+ }
397
+ return response.data;
398
+ }
399
+ // Products
400
+ /**
401
+ * List available products
402
+ * @param request - Product list filters
403
+ * @returns Promise<ProductsListResponse>
404
+ */
405
+ async listProducts(request = {}) {
406
+ var _a;
407
+ const params = new URLSearchParams();
408
+ if (request.category) params.append("category", request.category);
409
+ if (request.active !== void 0) params.append("active", request.active.toString());
410
+ if (request.limit) params.append("limit", request.limit.toString());
411
+ if (request.starting_after) params.append("starting_after", request.starting_after);
412
+ const url = `/api/stripe/products${params.toString() ? `?${params.toString()}` : ""}`;
413
+ const response = await this.httpClient.get(url, true);
414
+ if (!response.success || !response.data) {
415
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to list products");
416
+ }
417
+ return response.data;
418
+ }
419
+ /**
420
+ * Get a specific product by ID
421
+ * @param productId - The product ID
422
+ * @param includePrices - Whether to include associated prices (default: true)
423
+ * @returns Promise<StripeProduct>
424
+ */
425
+ async getProduct(productId, includePrices = true) {
426
+ var _a;
427
+ const params = new URLSearchParams();
428
+ if (!includePrices) params.append("include_prices", "false");
429
+ const url = `/api/stripe/products/${productId}${params.toString() ? `?${params.toString()}` : ""}`;
430
+ const response = await this.httpClient.get(url, true);
431
+ if (!response.success || !response.data) {
432
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to retrieve product");
433
+ }
434
+ return response.data;
435
+ }
436
+ /**
437
+ * Create a new product (Admin only)
438
+ * @param request - Product creation parameters
439
+ * @returns Promise<StripeProduct>
440
+ */
441
+ async createProduct(request) {
442
+ var _a;
443
+ const response = await this.httpClient.post(
444
+ "/api/stripe/products",
445
+ request,
446
+ true
447
+ );
448
+ if (!response.success || !response.data) {
449
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to create product");
450
+ }
451
+ return response.data;
452
+ }
453
+ /**
454
+ * Update an existing product (Admin only)
455
+ * @param productId - The product ID to update
456
+ * @param request - Product update parameters
457
+ * @returns Promise<StripeProduct>
458
+ */
459
+ async updateProduct(productId, request) {
460
+ var _a;
461
+ const response = await this.httpClient.put(
462
+ `/api/stripe/products/${productId}`,
463
+ request,
464
+ true
465
+ );
466
+ if (!response.success || !response.data) {
467
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to update product");
468
+ }
469
+ return response.data;
470
+ }
471
+ /**
472
+ * Archive a product (Admin only)
473
+ * @param productId - The product ID to archive
474
+ * @returns Promise<{id: string, archived: boolean, active: boolean}>
475
+ */
476
+ async archiveProduct(productId) {
477
+ var _a;
478
+ const response = await this.httpClient.delete(
479
+ `/api/stripe/products/${productId}`,
480
+ true
481
+ );
482
+ if (!response.success || !response.data) {
483
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to archive product");
484
+ }
485
+ return response.data;
486
+ }
487
+ /**
488
+ * Sync products from Stripe (Admin only)
489
+ * @returns Promise<{synced_count: number, message: string}>
490
+ */
491
+ async syncProducts() {
492
+ var _a;
493
+ const response = await this.httpClient.post(
494
+ "/api/stripe/products/sync",
495
+ {},
496
+ true
497
+ );
498
+ if (!response.success || !response.data) {
499
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to sync products");
500
+ }
501
+ return response.data;
502
+ }
503
+ // Prices
504
+ /**
505
+ * Create a new price for a product (Admin only)
506
+ * @param request - Price creation parameters
507
+ * @returns Promise<StripePrice>
508
+ */
509
+ async createPrice(request) {
510
+ var _a;
511
+ const response = await this.httpClient.post(
512
+ "/api/stripe/prices",
513
+ request,
514
+ true
515
+ );
516
+ if (!response.success || !response.data) {
517
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to create price");
518
+ }
519
+ return response.data;
520
+ }
521
+ // Subscriptions
522
+ /**
523
+ * Create a new subscription
524
+ * @param request - Subscription creation parameters
525
+ * @returns Promise<Subscription>
526
+ */
527
+ async createSubscription(request) {
528
+ var _a;
529
+ const response = await this.httpClient.post(
530
+ "/api/stripe/subscriptions",
531
+ request,
532
+ true
533
+ );
534
+ if (!response.success || !response.data) {
535
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to create subscription");
536
+ }
537
+ return response.data;
538
+ }
539
+ /**
540
+ * Get a subscription by ID
541
+ * @param subscriptionId - The subscription ID
542
+ * @returns Promise<Subscription>
543
+ */
544
+ async getSubscription(subscriptionId) {
545
+ var _a;
546
+ const response = await this.httpClient.get(
547
+ `/api/stripe/subscriptions/${subscriptionId}`,
548
+ true
549
+ );
550
+ if (!response.success || !response.data) {
551
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to retrieve subscription");
552
+ }
553
+ return response.data;
554
+ }
555
+ /**
556
+ * List subscriptions for the current user
557
+ * @param options - List options
558
+ * @returns Promise<{subscriptions: Subscription[], has_more: boolean}>
559
+ */
560
+ async listSubscriptions(options = {}) {
561
+ var _a;
562
+ const params = new URLSearchParams();
563
+ if (options.limit) params.append("limit", options.limit.toString());
564
+ if (options.starting_after) params.append("starting_after", options.starting_after);
565
+ if (options.status) params.append("status", options.status);
566
+ const url = `/api/stripe/subscriptions${params.toString() ? `?${params.toString()}` : ""}`;
567
+ const response = await this.httpClient.get(url, true);
568
+ if (!response.success || !response.data) {
569
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to list subscriptions");
570
+ }
571
+ return response.data;
572
+ }
573
+ /**
574
+ * Cancel a subscription
575
+ * @param subscriptionId - The subscription ID to cancel
576
+ * @returns Promise<Subscription>
577
+ */
578
+ async cancelSubscription(subscriptionId) {
579
+ var _a;
580
+ const response = await this.httpClient.post(
581
+ `/api/stripe/subscriptions/${subscriptionId}/cancel`,
582
+ {},
583
+ true
584
+ );
585
+ if (!response.success || !response.data) {
586
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to cancel subscription");
587
+ }
588
+ return response.data;
589
+ }
590
+ /**
591
+ * Update a subscription
592
+ * @param subscriptionId - The subscription ID to update
593
+ * @param request - Update parameters
594
+ * @returns Promise<Subscription>
595
+ */
596
+ async updateSubscription(subscriptionId, request) {
597
+ var _a;
598
+ const response = await this.httpClient.put(
599
+ `/api/stripe/subscriptions/${subscriptionId}`,
600
+ request,
601
+ true
602
+ );
603
+ if (!response.success || !response.data) {
604
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to update subscription");
605
+ }
606
+ return response.data;
607
+ }
608
+ // Customer Portal
609
+ /**
610
+ * Create a customer portal session
611
+ * @param request - Portal session parameters
612
+ * @returns Promise<CustomerPortalSession>
613
+ */
614
+ async createCustomerPortalSession(request) {
615
+ var _a;
616
+ const response = await this.httpClient.post(
617
+ "/api/stripe/customer-portal",
618
+ request,
619
+ true
620
+ );
621
+ if (!response.success || !response.data) {
622
+ throw new Error(((_a = response.error) == null ? void 0 : _a.message) || "Failed to create customer portal session");
623
+ }
624
+ return response.data;
130
625
  }
131
626
  // Utility Methods
132
- isAuthenticated() {
133
- return !!this.accessToken;
627
+ /**
628
+ * Create a one-time payment checkout session
629
+ * Convenience method for simple one-time payments
630
+ * @param params - Payment parameters
631
+ * @returns Promise<CheckoutSession>
632
+ */
633
+ async createPaymentCheckout(params) {
634
+ const lineItems = [];
635
+ if (params.price_id) {
636
+ lineItems.push({
637
+ price_id: params.price_id,
638
+ quantity: params.quantity || 1
639
+ });
640
+ } else if (params.product_name && params.amount && params.currency) {
641
+ lineItems.push({
642
+ quantity: params.quantity || 1,
643
+ price_data: {
644
+ currency: params.currency,
645
+ unit_amount: params.amount,
646
+ product_data: {
647
+ name: params.product_name
648
+ }
649
+ }
650
+ });
651
+ } else {
652
+ throw new Error("Either price_id or (product_name, amount, currency) must be provided");
653
+ }
654
+ return this.createCheckoutSession({
655
+ mode: "payment",
656
+ line_items: lineItems,
657
+ success_url: params.success_url,
658
+ cancel_url: params.cancel_url,
659
+ ...params.metadata && { metadata: params.metadata }
660
+ });
661
+ }
662
+ /**
663
+ * Create a subscription checkout session
664
+ * Convenience method for subscription creation
665
+ * @param params - Subscription parameters
666
+ * @returns Promise<CheckoutSession>
667
+ */
668
+ async createSubscriptionCheckout(params) {
669
+ return this.createCheckoutSession({
670
+ mode: "subscription",
671
+ line_items: [{
672
+ price_id: params.price_id,
673
+ quantity: 1
674
+ }],
675
+ success_url: params.success_url,
676
+ cancel_url: params.cancel_url,
677
+ subscription_data: {
678
+ ...params.trial_period_days !== void 0 && { trial_period_days: params.trial_period_days },
679
+ ...params.metadata && { metadata: params.metadata }
680
+ }
681
+ });
134
682
  }
135
- getAccessToken() {
136
- return this.accessToken;
683
+ };
684
+
685
+ // index.ts
686
+ var SweetPotatoSDK = class {
687
+ constructor(config) {
688
+ if (!config.apiUrl) {
689
+ throw new Error("apiUrl is required");
690
+ }
691
+ this.httpClient = new HttpClient(config);
692
+ this.auth = new AuthService(this.httpClient);
693
+ this.payments = new PaymentsService(this.httpClient);
694
+ this.isLocalMode = this.httpClient["isLocalMode"] || false;
695
+ if (this.isLocalMode) {
696
+ console.log("[SPAPS SDK] Initialized in local development mode");
697
+ console.log("[SPAPS SDK] API URL:", config.apiUrl);
698
+ console.log("[SPAPS SDK] Authentication will be automatic");
699
+ }
137
700
  }
701
+ /**
702
+ * Set access token for authenticated requests
703
+ * @param token - Access token
704
+ */
138
705
  setAccessToken(token) {
139
- this.accessToken = token;
706
+ this.httpClient.setAccessToken(token);
707
+ }
708
+ /**
709
+ * Clear access token
710
+ */
711
+ clearAccessToken() {
712
+ this.httpClient.clearAccessToken();
140
713
  }
141
- isLocalMode() {
142
- return this._isLocalMode;
714
+ /**
715
+ * Get SDK configuration
716
+ */
717
+ getConfig() {
718
+ return {
719
+ apiUrl: this.httpClient["config"].apiUrl,
720
+ timeout: this.httpClient["config"].timeout,
721
+ retries: this.httpClient["config"].retries
722
+ };
143
723
  }
144
- async health() {
145
- return this.client.get("/health");
724
+ /**
725
+ * Health check endpoint
726
+ * @returns Promise<boolean>
727
+ */
728
+ async healthCheck() {
729
+ try {
730
+ const response = await this.httpClient.get("/api/health");
731
+ return response.success;
732
+ } catch (e) {
733
+ return false;
734
+ }
735
+ }
736
+ /**
737
+ * Make a custom authenticated request
738
+ * This allows advanced users to make direct API calls not covered by the SDK
739
+ */
740
+ async request(method, url, data, requiresAuth = false) {
741
+ return this.httpClient.request({
742
+ method,
743
+ url,
744
+ data,
745
+ requiresAuth
746
+ });
747
+ }
748
+ };
749
+ function createSweetPotatoSDK(config) {
750
+ return new SweetPotatoSDK(config);
751
+ }
752
+ var _TokenManager = class _TokenManager {
753
+ /**
754
+ * Get localStorage instance (browser or test environment)
755
+ */
756
+ static getStorage() {
757
+ var _a, _b;
758
+ if (typeof globalThis !== "undefined" && globalThis.localStorage) {
759
+ return globalThis.localStorage;
760
+ }
761
+ if (typeof globalThis !== "undefined" && ((_a = globalThis.window) == null ? void 0 : _a.localStorage)) {
762
+ return globalThis.window.localStorage;
763
+ }
764
+ if (typeof global !== "undefined" && ((_b = global.window) == null ? void 0 : _b.localStorage)) {
765
+ return global.window.localStorage;
766
+ }
767
+ return null;
768
+ }
769
+ /**
770
+ * Store tokens in localStorage (browser only)
771
+ */
772
+ static storeTokens(tokens) {
773
+ const localStorage = _TokenManager.getStorage();
774
+ if (localStorage) {
775
+ localStorage.setItem(_TokenManager.ACCESS_TOKEN_KEY, tokens.access_token);
776
+ localStorage.setItem(_TokenManager.REFRESH_TOKEN_KEY, tokens.refresh_token);
777
+ localStorage.setItem(_TokenManager.USER_KEY, JSON.stringify(tokens.user));
778
+ }
779
+ }
780
+ /**
781
+ * Get stored access token (browser only)
782
+ */
783
+ static getAccessToken() {
784
+ const localStorage = _TokenManager.getStorage();
785
+ return localStorage ? localStorage.getItem(_TokenManager.ACCESS_TOKEN_KEY) : null;
786
+ }
787
+ /**
788
+ * Get stored refresh token (browser only)
789
+ */
790
+ static getRefreshToken() {
791
+ const localStorage = _TokenManager.getStorage();
792
+ return localStorage ? localStorage.getItem(_TokenManager.REFRESH_TOKEN_KEY) : null;
793
+ }
794
+ /**
795
+ * Get stored user data (browser only)
796
+ */
797
+ static getStoredUser() {
798
+ const localStorage = _TokenManager.getStorage();
799
+ if (localStorage) {
800
+ const userData = localStorage.getItem(_TokenManager.USER_KEY);
801
+ return userData ? JSON.parse(userData) : null;
802
+ }
803
+ return null;
804
+ }
805
+ /**
806
+ * Clear all stored tokens and user data (browser only)
807
+ */
808
+ static clearTokens() {
809
+ const localStorage = _TokenManager.getStorage();
810
+ if (localStorage) {
811
+ localStorage.removeItem(_TokenManager.ACCESS_TOKEN_KEY);
812
+ localStorage.removeItem(_TokenManager.REFRESH_TOKEN_KEY);
813
+ localStorage.removeItem(_TokenManager.USER_KEY);
814
+ }
815
+ }
816
+ /**
817
+ * Check if access token is expired (basic check, doesn't verify signature)
818
+ */
819
+ static isTokenExpired(token) {
820
+ try {
821
+ const parts = token.split(".");
822
+ if (parts.length !== 3 || !parts[1]) return true;
823
+ const payload = JSON.parse(atob(parts[1]));
824
+ const currentTime = Math.floor(Date.now() / 1e3);
825
+ return payload.exp < currentTime;
826
+ } catch (e) {
827
+ return true;
828
+ }
829
+ }
830
+ /**
831
+ * Auto-refresh token if needed
832
+ */
833
+ static async autoRefreshToken(sdk) {
834
+ const accessToken = _TokenManager.getAccessToken();
835
+ const refreshToken = _TokenManager.getRefreshToken();
836
+ if (!accessToken || !refreshToken) {
837
+ return false;
838
+ }
839
+ if (!_TokenManager.isTokenExpired(accessToken)) {
840
+ sdk.setAccessToken(accessToken);
841
+ return true;
842
+ }
843
+ try {
844
+ const newTokens = await sdk.auth.refreshToken(refreshToken);
845
+ _TokenManager.storeTokens(newTokens);
846
+ return true;
847
+ } catch (e) {
848
+ _TokenManager.clearTokens();
849
+ return false;
850
+ }
851
+ }
852
+ };
853
+ _TokenManager.ACCESS_TOKEN_KEY = "sweet_potato_access_token";
854
+ _TokenManager.REFRESH_TOKEN_KEY = "sweet_potato_refresh_token";
855
+ _TokenManager.USER_KEY = "sweet_potato_user";
856
+ var TokenManager = _TokenManager;
857
+ var WalletUtils = class _WalletUtils {
858
+ /**
859
+ * Detect chain type from wallet address
860
+ */
861
+ static detectChainType(address) {
862
+ if (/^0x[a-fA-F0-9]{40}$/.test(address)) {
863
+ return "ethereum";
864
+ }
865
+ if (/^bc1[a-z0-9]{39,59}$/.test(address)) {
866
+ return "bitcoin";
867
+ }
868
+ if (/^[1-9A-HJ-NP-Za-km-z]{32}$/.test(address) || /^[1-9A-HJ-NP-Za-km-z]{44}$/.test(address)) {
869
+ return "solana";
870
+ }
871
+ if (/^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address) && address.length >= 26 && address.length <= 35) {
872
+ return "bitcoin";
873
+ }
874
+ if (/^[1-9A-HJ-NP-Za-km-z]{35,44}$/.test(address)) {
875
+ return "solana";
876
+ }
877
+ return null;
878
+ }
879
+ /**
880
+ * Validate wallet address format
881
+ */
882
+ static isValidAddress(address, chainType) {
883
+ if (!chainType) {
884
+ chainType = _WalletUtils.detectChainType(address) || "ethereum";
885
+ }
886
+ switch (chainType) {
887
+ case "solana":
888
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
889
+ case "ethereum":
890
+ case "base":
891
+ return /^0x[a-fA-F0-9]{40}$/.test(address);
892
+ case "bitcoin":
893
+ return /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address) || /^bc1[a-z0-9]{39,59}$/.test(address);
894
+ default:
895
+ return false;
896
+ }
146
897
  }
147
898
  };
148
- var index_default = SPAPSClient;
149
899
  export {
150
- SPAPSClient as SPAPS,
151
- SPAPSClient,
152
- SPAPSClient as SweetPotatoSDK,
153
- index_default as default
900
+ AuthService,
901
+ HttpClient,
902
+ PaymentsService,
903
+ SweetPotatoAPIError,
904
+ SweetPotatoSDK,
905
+ TokenManager,
906
+ WalletUtils,
907
+ createSweetPotatoSDK,
908
+ SweetPotatoSDK as default
154
909
  };