spaps-sdk 1.1.2 → 1.1.3

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.js CHANGED
@@ -198,7 +198,8 @@ __export(index_exports, {
198
198
  PermissionChecker: () => PermissionChecker,
199
199
  SPAPS: () => SPAPSClient,
200
200
  SPAPSClient: () => SPAPSClient,
201
- SweetPotatoSDK: () => SPAPSClient,
201
+ TokenManager: () => TokenManager,
202
+ WalletUtils: () => WalletUtils,
202
203
  canAccessAdmin: () => canAccessAdmin,
203
204
  createPermissionChecker: () => createPermissionChecker,
204
205
  default: () => index_default,
@@ -282,6 +283,30 @@ var SPAPSClient = class {
282
283
  }
283
284
  );
284
285
  }
286
+ // -------- Helper methods (for tests and convenience) --------
287
+ /** Raw API request helper that returns an ApiResponse-like shape */
288
+ async request(method, url, data, requiresAuth = false) {
289
+ try {
290
+ const config = { method, url, data };
291
+ if (requiresAuth && this.accessToken) {
292
+ config.headers = { ...config.headers || {}, Authorization: `Bearer ${this.accessToken}` };
293
+ }
294
+ const res = await this.client.request(config);
295
+ return { success: true, data: res.data };
296
+ } catch (err) {
297
+ const message = err?.response?.data?.error?.message || err?.message || "Request failed";
298
+ return Promise.reject(new Error(message));
299
+ }
300
+ }
301
+ /** Health check helper that returns boolean */
302
+ async healthCheck() {
303
+ try {
304
+ const res = await this.client.get("/health");
305
+ return Boolean(res.data?.success);
306
+ } catch {
307
+ return false;
308
+ }
309
+ }
285
310
  // Authentication Methods
286
311
  async login(email, password) {
287
312
  const response = await this.client.post("/api/auth/login", {
@@ -328,6 +353,327 @@ var SPAPSClient = class {
328
353
  async getUser() {
329
354
  return this.client.get("/api/auth/user");
330
355
  }
356
+ // -------- Facade namespaces (compat with previous SDK shape) --------
357
+ auth = {
358
+ /**
359
+ * Verify magic link token without mutating token state.
360
+ * Returns a simple success object from the API.
361
+ */
362
+ getNonce: async (walletAddress) => {
363
+ const res = await this.client.post("/api/auth/nonce", { wallet_address: walletAddress });
364
+ const body = res.data;
365
+ if (body?.success === false) throw new Error(body?.error?.message || "Failed to generate nonce");
366
+ return body?.data ?? body;
367
+ },
368
+ signInWithWallet: async (req) => {
369
+ const res = await this.client.post("/api/auth/wallet-sign-in", req);
370
+ const body = res.data;
371
+ if (body?.success === false) throw new Error(body?.error?.message || "Wallet sign-in failed");
372
+ const data = body?.data ?? body;
373
+ this.accessToken = data.access_token;
374
+ this.refreshToken = data.refresh_token;
375
+ return data;
376
+ },
377
+ authenticateWallet: async (walletAddress, signFn, chainType, username) => {
378
+ const nonce = await this.auth.getNonce(walletAddress);
379
+ const signature = await signFn(nonce.message);
380
+ return this.auth.signInWithWallet({ wallet_address: walletAddress, signature, message: nonce.message, chain_type: chainType, username });
381
+ },
382
+ signInWithPassword: async (payload) => {
383
+ const res = await this.client.post("/api/auth/login", payload);
384
+ const body = res.data;
385
+ if (body?.success === false) throw new Error(body?.error?.message || "Login failed");
386
+ const data = body?.data ?? body;
387
+ this.accessToken = data.access_token;
388
+ this.refreshToken = data.refresh_token;
389
+ return data;
390
+ },
391
+ requestMagicLink: async (payload) => {
392
+ await this.client.post("/api/auth/magic-link", payload);
393
+ },
394
+ requestPasswordReset: async (payload) => {
395
+ await this.client.post("/api/auth/password-reset", payload);
396
+ },
397
+ confirmPasswordReset: async (payload) => {
398
+ await this.client.post("/api/auth/reset-password-confirm", payload);
399
+ },
400
+ register: async (payload) => {
401
+ const res = await this.client.post("/api/auth/register", payload);
402
+ const body = res.data;
403
+ if (body?.success === false) throw new Error(body?.error?.message || "Register failed");
404
+ const data = body?.data ?? body;
405
+ this.accessToken = data.access_token;
406
+ this.refreshToken = data.refresh_token;
407
+ return data;
408
+ },
409
+ /**
410
+ * Verify a magic link token. Does not set access/refresh tokens.
411
+ * Consumers should redirect or prompt login based on the returned status.
412
+ */
413
+ verifyMagicLink: async (payload) => {
414
+ const res = await this.client.post("/api/auth/verify-magic-link", payload);
415
+ const body = res.data;
416
+ if (body?.success === false) throw new Error(body?.error?.message || "Failed to verify magic link");
417
+ return body?.data || body || { success: true };
418
+ },
419
+ solana: {
420
+ linkWallet: async (payload) => {
421
+ const res = await this.client.post("/api/auth/solana/link-wallet", payload);
422
+ return res.data;
423
+ },
424
+ verifySignature: async (payload) => {
425
+ const res = await this.client.post("/api/auth/solana/verify-signature", payload);
426
+ return res.data;
427
+ },
428
+ generateMessage: async (wallet_address) => {
429
+ const res = await this.client.get(`/api/auth/solana/generate-message/${wallet_address}`);
430
+ return res.data;
431
+ },
432
+ getWallets: async () => {
433
+ const res = await this.client.get("/api/auth/solana/wallets");
434
+ return res.data;
435
+ },
436
+ networkInfo: async () => {
437
+ const res = await this.client.get("/api/auth/solana/network-info");
438
+ return res.data;
439
+ }
440
+ },
441
+ ethereum: {
442
+ linkWallet: async (payload) => {
443
+ const res = await this.client.post("/api/auth/ethereum/link-wallet", payload);
444
+ return res.data;
445
+ },
446
+ verifySignature: async (payload) => {
447
+ const res = await this.client.post("/api/auth/ethereum/verify-signature", payload);
448
+ return res.data;
449
+ },
450
+ verifyTypedData: async (payload) => {
451
+ const res = await this.client.post("/api/auth/ethereum/verify-typed-data", payload);
452
+ return res.data;
453
+ },
454
+ generateMessage: async (wallet_address) => {
455
+ const res = await this.client.get(`/api/auth/ethereum/generate-message/${wallet_address}`);
456
+ return res.data;
457
+ },
458
+ generateTypedData: async (wallet_address) => {
459
+ const res = await this.client.get(`/api/auth/ethereum/generate-typed-data/${wallet_address}`);
460
+ return res.data;
461
+ },
462
+ getWallets: async () => {
463
+ const res = await this.client.get("/api/auth/ethereum/wallets");
464
+ return res.data;
465
+ },
466
+ networkInfo: async () => {
467
+ const res = await this.client.get("/api/auth/ethereum/network-info");
468
+ return res.data;
469
+ },
470
+ balance: async (wallet_address) => {
471
+ const res = await this.client.get(`/api/auth/ethereum/balance/${wallet_address}`);
472
+ return res.data;
473
+ },
474
+ contractCheck: async (wallet_address, contract_address) => {
475
+ const res = await this.client.get(`/api/auth/ethereum/contract-check/${wallet_address}/${contract_address}`);
476
+ return res.data;
477
+ }
478
+ },
479
+ refreshToken: async (refreshToken) => {
480
+ const res = await this.client.post("/api/auth/refresh", { refresh_token: refreshToken });
481
+ const body = res.data;
482
+ if (body?.success === false) throw new Error(body?.error?.message || "Token refresh failed");
483
+ const data = body?.data ?? body;
484
+ this.accessToken = data.access_token;
485
+ this.refreshToken = data.refresh_token;
486
+ return data;
487
+ },
488
+ /**
489
+ * Logout and clear tokens. Network errors are intentionally swallowed
490
+ * to avoid trapping users in a bad state during sign‑out flows.
491
+ */
492
+ logout: async () => {
493
+ try {
494
+ await this.client.post("/api/auth/logout");
495
+ } catch {
496
+ } finally {
497
+ this.accessToken = void 0;
498
+ this.refreshToken = void 0;
499
+ }
500
+ },
501
+ getCurrentUser: async () => {
502
+ const res = await this.client.get("/api/auth/user");
503
+ const body = res.data;
504
+ if (body?.success === false) throw new Error(body?.error?.message || "Failed to get user profile");
505
+ return body?.data?.user ?? body?.user;
506
+ },
507
+ isAuthenticated: () => !!this.accessToken
508
+ };
509
+ payments = {
510
+ createCheckoutSession: async (payload) => {
511
+ const headers = {};
512
+ if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
513
+ const res = await this.client.post("/api/stripe/checkout-sessions", payload, { headers });
514
+ return res.data;
515
+ },
516
+ createPaymentCheckout: async (params) => {
517
+ const payload = { mode: "payment", line_items: [{ price_id: params.price_id, quantity: params.quantity ?? 1 }], success_url: params.success_url, cancel_url: params.cancel_url };
518
+ return this.payments.createCheckoutSession(payload);
519
+ },
520
+ createSubscriptionCheckout: async (params) => {
521
+ const payload = { mode: "subscription", line_items: [{ price_id: params.price_id, quantity: 1 }], success_url: params.success_url, cancel_url: params.cancel_url };
522
+ if (params.trial_period_days) payload.subscription_data = { trial_period_days: params.trial_period_days };
523
+ return this.payments.createCheckoutSession(payload);
524
+ },
525
+ getCheckoutSession: async (sessionId) => {
526
+ const res = await this.client.get(`/api/stripe/checkout-sessions/${sessionId}`);
527
+ return res.data;
528
+ },
529
+ listCheckoutSessions: async (query = {}) => {
530
+ const q = new URLSearchParams();
531
+ if (query.limit) q.append("limit", String(query.limit));
532
+ if (query.starting_after) q.append("starting_after", query.starting_after);
533
+ const res = await this.client.get(`/api/stripe/checkout-sessions${q.toString() ? `?${q.toString()}` : ""}`);
534
+ return res.data;
535
+ },
536
+ expireCheckoutSession: async (sessionId) => {
537
+ const res = await this.client.post(`/api/stripe/checkout-sessions/${sessionId}/expire`);
538
+ return res.data;
539
+ },
540
+ listProducts: async (query = {}) => {
541
+ const q = new URLSearchParams();
542
+ if (query.category) q.append("category", query.category);
543
+ if (typeof query.active === "boolean") q.append("active", String(query.active));
544
+ if (query.limit) q.append("limit", String(query.limit));
545
+ if (query.starting_after) q.append("starting_after", query.starting_after);
546
+ const headers = {};
547
+ if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
548
+ const res = await this.client.get(`/api/stripe/products${q.toString() ? `?${q.toString()}` : ""}`, { headers });
549
+ return res.data;
550
+ },
551
+ getProduct: async (productId) => {
552
+ const headers = {};
553
+ if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
554
+ const res = await this.client.get(`/api/stripe/products/${productId}`, { headers });
555
+ return res.data;
556
+ },
557
+ createCustomerPortalSession: async (payload) => {
558
+ const headers = {};
559
+ if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
560
+ const res = await this.client.post("/api/stripe/portal-session", payload, { headers });
561
+ return res.data;
562
+ },
563
+ // Guest checkout helpers
564
+ createGuestCheckoutSession: async (payload) => {
565
+ const headers = {};
566
+ if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
567
+ const res = await this.client.post("/api/stripe/guest-checkout-sessions", payload, { headers });
568
+ return res.data;
569
+ },
570
+ getGuestCheckoutSession: async (sessionId) => {
571
+ const headers = {};
572
+ if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
573
+ const res = await this.client.get(`/api/stripe/guest-checkout-sessions/${sessionId}`, { headers });
574
+ return res.data;
575
+ },
576
+ listGuestCheckoutSessions: async (query = {}) => {
577
+ const q = new URLSearchParams();
578
+ if (query.limit) q.append("limit", String(query.limit));
579
+ if (query.starting_after) q.append("starting_after", query.starting_after);
580
+ const headers = {};
581
+ if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
582
+ const res = await this.client.get(`/api/stripe/guest-checkout-sessions${q.toString() ? `?${q.toString()}` : ""}`, { headers });
583
+ return res.data;
584
+ },
585
+ convertGuestCheckoutSession: async (payload) => {
586
+ const headers = {};
587
+ if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
588
+ const res = await this.client.post("/api/stripe/guest-checkout-sessions/convert", payload, { headers });
589
+ return res.data;
590
+ },
591
+ convertGuestCheckout: async (payload) => this.payments.convertGuestCheckoutSession(payload),
592
+ // Super-admin product helpers (admin token required)
593
+ listAllProductsSuperAdmin: async () => {
594
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
595
+ const res = await this.client.get("/api/stripe/products/super-admin/all", { headers: { Authorization: `Bearer ${this.accessToken}` } });
596
+ return res.data;
597
+ },
598
+ updateProductSuperAdmin: async (productId, updates) => {
599
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
600
+ const res = await this.client.put(`/api/stripe/products/super-admin/${productId}`, updates, { headers: { Authorization: `Bearer ${this.accessToken}` } });
601
+ return res.data;
602
+ },
603
+ deleteProductSuperAdmin: async (productId) => {
604
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
605
+ const res = await this.client.delete(`/api/stripe/products/super-admin/${productId}`, { headers: { Authorization: `Bearer ${this.accessToken}` } });
606
+ return res.data;
607
+ },
608
+ createProductWithPrice: async (payload) => {
609
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
610
+ const res = await this.client.post("/api/stripe/products/with-price", payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
611
+ return res.data;
612
+ },
613
+ createProductWithPriceSuperAdmin: async (productId, payload) => {
614
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
615
+ const res = await this.client.post(`/api/stripe/products/super-admin/${productId}/with-price`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
616
+ return res.data;
617
+ },
618
+ setDefaultPrice: async (productId, payload) => {
619
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
620
+ const res = await this.client.post(`/api/stripe/products/${productId}/default-price`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
621
+ return res.data;
622
+ },
623
+ setDefaultPriceSuperAdmin: async (productId, payload) => {
624
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
625
+ const res = await this.client.put(`/api/stripe/products/super-admin/${productId}/default-price`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
626
+ return res.data;
627
+ },
628
+ createDefaultNewPrice: async (productId, payload) => {
629
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
630
+ const res = await this.client.post(`/api/stripe/products/${productId}/prices/default-new`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
631
+ return res.data;
632
+ },
633
+ superAdminListAllProducts: async () => this.payments.listAllProductsSuperAdmin(),
634
+ superAdminUpdateProduct: async (productId, updates) => this.payments.updateProductSuperAdmin(productId, updates),
635
+ superAdminArchiveProduct: async (productId) => this.payments.deleteProductSuperAdmin(productId),
636
+ superAdminCreateProductWithPrice: async (applicationId, payload) => {
637
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
638
+ const res = await this.client.post(`/api/stripe/products/super-admin/${applicationId}/with-price`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
639
+ return res.data;
640
+ },
641
+ superAdminCreatePriceAndSetDefault: async (productId, payload) => {
642
+ if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
643
+ const res = await this.client.post(`/api/stripe/products/super-admin/${productId}/prices/default-new`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
644
+ return res.data;
645
+ },
646
+ superAdminSetDefaultPrice: async (productId, payload) => this.payments.setDefaultPriceSuperAdmin(productId, payload)
647
+ };
648
+ sessions = {
649
+ getCurrent: async () => {
650
+ const res = await this.client.get("/api/sessions/current", this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
651
+ return res.data;
652
+ },
653
+ list: async (params = {}) => {
654
+ const q = new URLSearchParams();
655
+ if (params.limit) q.append("limit", String(params.limit));
656
+ if (params.starting_after) q.append("starting_after", params.starting_after);
657
+ const res = await this.client.get(`/api/sessions${q.toString() ? `?${q.toString()}` : ""}`, this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
658
+ return res.data;
659
+ },
660
+ validate: async () => {
661
+ const res = await this.client.post("/api/sessions/validate", {}, this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
662
+ return res.data || { valid: true };
663
+ },
664
+ revoke: async (sessionId) => {
665
+ const res = await this.client.delete(`/api/sessions/${sessionId}`, this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
666
+ return res.data || { revoked: true };
667
+ },
668
+ revokeAll: async () => {
669
+ const res = await this.client.delete("/api/sessions/all", this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
670
+ return res.data || { revoked: true };
671
+ },
672
+ touch: async () => {
673
+ const res = await this.client.post("/api/sessions/touch", {}, this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0);
674
+ return res.data || { touched: true };
675
+ }
676
+ };
331
677
  // Stripe Methods
332
678
  async createCheckoutSession(priceId, successUrl, cancelUrl) {
333
679
  return this.client.post("/api/stripe/create-checkout-session", {
@@ -457,13 +803,107 @@ var SPAPSClient = class {
457
803
  }
458
804
  };
459
805
  var index_default = SPAPSClient;
806
+ var TokenManager = class _TokenManager {
807
+ static ACCESS_TOKEN_KEY = "sweet_potato_access_token";
808
+ static REFRESH_TOKEN_KEY = "sweet_potato_refresh_token";
809
+ static USER_KEY = "sweet_potato_user";
810
+ static getStorage() {
811
+ if (typeof globalThis !== "undefined" && globalThis.localStorage) return globalThis.localStorage;
812
+ if (typeof globalThis !== "undefined" && globalThis.window?.localStorage) return globalThis.window.localStorage;
813
+ if (typeof global !== "undefined" && global.window?.localStorage) return global.window.localStorage;
814
+ return null;
815
+ }
816
+ static storeTokens(tokens) {
817
+ const s = _TokenManager.getStorage();
818
+ if (s) {
819
+ s.setItem(_TokenManager.ACCESS_TOKEN_KEY, tokens.access_token);
820
+ s.setItem(_TokenManager.REFRESH_TOKEN_KEY, tokens.refresh_token);
821
+ s.setItem(_TokenManager.USER_KEY, JSON.stringify(tokens.user));
822
+ }
823
+ }
824
+ static getAccessToken() {
825
+ const s = _TokenManager.getStorage();
826
+ return s ? s.getItem(_TokenManager.ACCESS_TOKEN_KEY) : null;
827
+ }
828
+ static getRefreshToken() {
829
+ const s = _TokenManager.getStorage();
830
+ return s ? s.getItem(_TokenManager.REFRESH_TOKEN_KEY) : null;
831
+ }
832
+ static getStoredUser() {
833
+ const s = _TokenManager.getStorage();
834
+ if (!s) return null;
835
+ const v = s.getItem(_TokenManager.USER_KEY);
836
+ return v ? JSON.parse(v) : null;
837
+ }
838
+ static clearTokens() {
839
+ const s = _TokenManager.getStorage();
840
+ if (s) {
841
+ s.removeItem(_TokenManager.ACCESS_TOKEN_KEY);
842
+ s.removeItem(_TokenManager.REFRESH_TOKEN_KEY);
843
+ s.removeItem(_TokenManager.USER_KEY);
844
+ }
845
+ }
846
+ static isTokenExpired(token) {
847
+ try {
848
+ const parts = token.split(".");
849
+ if (parts.length !== 3 || !parts[1]) return true;
850
+ const payload = JSON.parse(atob(parts[1]));
851
+ const now = Math.floor(Date.now() / 1e3);
852
+ return payload.exp < now;
853
+ } catch {
854
+ return true;
855
+ }
856
+ }
857
+ static async autoRefreshToken(sdk) {
858
+ const accessToken = _TokenManager.getAccessToken();
859
+ const refreshToken = _TokenManager.getRefreshToken();
860
+ if (!accessToken || !refreshToken) return false;
861
+ if (!_TokenManager.isTokenExpired(accessToken)) {
862
+ sdk.setAccessToken(accessToken);
863
+ return true;
864
+ }
865
+ try {
866
+ const newTokens = await sdk.auth.refreshToken(refreshToken);
867
+ _TokenManager.storeTokens(newTokens);
868
+ return true;
869
+ } catch {
870
+ _TokenManager.clearTokens();
871
+ return false;
872
+ }
873
+ }
874
+ };
875
+ var WalletUtils = class _WalletUtils {
876
+ static detectChainType(address) {
877
+ if (/^0x[a-fA-F0-9]{40}$/.test(address)) return "ethereum";
878
+ if (/^bc1[a-z0-9]{39,59}$/.test(address)) return "bitcoin";
879
+ if (/^[1-9A-HJ-NP-Za-km-z]{32}$/.test(address) || /^[1-9A-HJ-NP-Za-km-z]{44}$/.test(address)) return "solana";
880
+ if (/^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address) && address.length >= 26 && address.length <= 35) return "bitcoin";
881
+ if (/^[1-9A-HJ-NP-Za-km-z]{35,44}$/.test(address)) return "solana";
882
+ return null;
883
+ }
884
+ static isValidAddress(address, chainType) {
885
+ if (!chainType) chainType = _WalletUtils.detectChainType(address) || "ethereum";
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
+ }
897
+ }
898
+ };
460
899
  // Annotate the CommonJS export names for ESM import in node:
461
900
  0 && (module.exports = {
462
901
  DEFAULT_ADMIN_ACCOUNTS,
463
902
  PermissionChecker,
464
903
  SPAPS,
465
904
  SPAPSClient,
466
- SweetPotatoSDK,
905
+ TokenManager,
906
+ WalletUtils,
467
907
  canAccessAdmin,
468
908
  createPermissionChecker,
469
909
  defaultPermissionChecker,