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