spaps-sdk 1.1.5 → 1.1.7

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/README.md CHANGED
@@ -153,6 +153,7 @@ NEXT_PUBLIC_SPAPS_API_URL=https://api.sweetpotato.dev
153
153
  - **Auto-detects local mode** - No API key needed for localhost
154
154
  - **Auto-refreshes tokens** - Handles expired tokens automatically
155
155
  - **TypeScript support** - Full type definitions included
156
+ - **Crypto payments ready** - Create invoices, poll status, verify webhooks, trigger reconciliation
156
157
 
157
158
  ### 🔐 Authentication Methods
158
159
  ```javascript
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
- import { CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, AuthResponse, User as User$1, CheckoutSession, Subscription, UsageBalance } from 'spaps-types';
2
- export { AdminPermission, AdminRole, AdminUser, ApiResponse, AuthResponse, CheckoutSession, CreatePriceRequest, CreateProductRequest, Price, Product, ProductSyncResult, Subscription, TokenPair, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet } from 'spaps-types';
1
+ import * as spaps_types from 'spaps-types';
2
+ import { CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
3
+ export { AdminPermission, AdminRole, AdminUser, ApiResponse, AuthResponse, CheckoutSession, CreateCryptoInvoiceRequest, CreatePriceRequest, CreateProductRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, Price, Product, ProductSyncResult, Subscription, TokenPair, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
3
4
 
4
5
  /**
5
6
  * Permission checking utilities for SPAPS SDK
@@ -84,11 +85,6 @@ declare class PermissionChecker {
84
85
  declare function createPermissionChecker(customAdmins?: (string | AdminConfig)[]): PermissionChecker;
85
86
  declare const defaultPermissionChecker: PermissionChecker;
86
87
 
87
- /**
88
- * @spaps/sdk - Sweet Potato Authentication & Payment Service SDK
89
- * Zero-config client for SPAPS authentication and payments
90
- */
91
-
92
88
  interface SPAPSConfig {
93
89
  apiUrl?: string;
94
90
  apiKey?: string;
@@ -102,6 +98,7 @@ declare class SPAPSClient {
102
98
  private accessToken?;
103
99
  private refreshToken?;
104
100
  private _isLocalMode;
101
+ private unwrapApiResponse;
105
102
  admin: {
106
103
  createProduct: (productData: CreateProductRequest) => Promise<{
107
104
  data: Product;
@@ -129,6 +126,11 @@ declare class SPAPSClient {
129
126
  adminMetadata?: any;
130
127
  };
131
128
  }>;
129
+ triggerCryptoReconcile: (opts?: CryptoReconcileRequest) => Promise<{
130
+ job_id: string;
131
+ scheduled_at: string;
132
+ cursor?: Record<string, unknown>;
133
+ }>;
132
134
  };
133
135
  constructor(config?: SPAPSConfig);
134
136
  /** Raw API request helper that returns an ApiResponse-like shape */
@@ -250,6 +252,16 @@ declare class SPAPSClient {
250
252
  isAuthenticated: () => boolean;
251
253
  };
252
254
  payments: {
255
+ crypto: {
256
+ createInvoice: (payload: CreateCryptoInvoiceRequest) => Promise<spaps_types.CryptoInvoice>;
257
+ getInvoice: (invoiceId: string) => Promise<spaps_types.CryptoInvoice>;
258
+ getInvoiceStatus: (invoiceId: string) => Promise<CryptoInvoiceStatusSnapshot>;
259
+ reconcile: (options?: CryptoReconcileRequest) => Promise<{
260
+ job_id: string;
261
+ scheduled_at: string;
262
+ cursor?: Record<string, unknown>;
263
+ }>;
264
+ };
253
265
  createCheckoutSession: (payload: any) => Promise<CheckoutSession>;
254
266
  createPaymentCheckout: (params: {
255
267
  price_id: string;
@@ -397,6 +409,7 @@ declare class SPAPSClient {
397
409
  data: any;
398
410
  }>;
399
411
  }
412
+ declare function verifyCryptoWebhookSignature(options: VerifyCryptoWebhookSignatureOptions): boolean;
400
413
 
401
414
  declare class TokenManager {
402
415
  private static readonly ACCESS_TOKEN_KEY;
@@ -416,4 +429,4 @@ declare class WalletUtils {
416
429
  static isValidAddress(address: string, chainType?: 'solana' | 'ethereum' | 'bitcoin' | 'base'): boolean;
417
430
  }
418
431
 
419
- export { type AdminConfig, DEFAULT_ADMIN_ACCOUNTS, type PermissionCheckResult, PermissionChecker, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, TokenManager, WalletUtils, canAccessAdmin, createPermissionChecker, SPAPSClient as default, defaultPermissionChecker, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount };
432
+ export { type AdminConfig, DEFAULT_ADMIN_ACCOUNTS, type PermissionCheckResult, PermissionChecker, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, TokenManager, WalletUtils, canAccessAdmin, createPermissionChecker, SPAPSClient as default, defaultPermissionChecker, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, verifyCryptoWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, AuthResponse, User as User$1, CheckoutSession, Subscription, UsageBalance } from 'spaps-types';
2
- export { AdminPermission, AdminRole, AdminUser, ApiResponse, AuthResponse, CheckoutSession, CreatePriceRequest, CreateProductRequest, Price, Product, ProductSyncResult, Subscription, TokenPair, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet } from 'spaps-types';
1
+ import * as spaps_types from 'spaps-types';
2
+ import { CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
3
+ export { AdminPermission, AdminRole, AdminUser, ApiResponse, AuthResponse, CheckoutSession, CreateCryptoInvoiceRequest, CreatePriceRequest, CreateProductRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, Price, Product, ProductSyncResult, Subscription, TokenPair, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
3
4
 
4
5
  /**
5
6
  * Permission checking utilities for SPAPS SDK
@@ -84,11 +85,6 @@ declare class PermissionChecker {
84
85
  declare function createPermissionChecker(customAdmins?: (string | AdminConfig)[]): PermissionChecker;
85
86
  declare const defaultPermissionChecker: PermissionChecker;
86
87
 
87
- /**
88
- * @spaps/sdk - Sweet Potato Authentication & Payment Service SDK
89
- * Zero-config client for SPAPS authentication and payments
90
- */
91
-
92
88
  interface SPAPSConfig {
93
89
  apiUrl?: string;
94
90
  apiKey?: string;
@@ -102,6 +98,7 @@ declare class SPAPSClient {
102
98
  private accessToken?;
103
99
  private refreshToken?;
104
100
  private _isLocalMode;
101
+ private unwrapApiResponse;
105
102
  admin: {
106
103
  createProduct: (productData: CreateProductRequest) => Promise<{
107
104
  data: Product;
@@ -129,6 +126,11 @@ declare class SPAPSClient {
129
126
  adminMetadata?: any;
130
127
  };
131
128
  }>;
129
+ triggerCryptoReconcile: (opts?: CryptoReconcileRequest) => Promise<{
130
+ job_id: string;
131
+ scheduled_at: string;
132
+ cursor?: Record<string, unknown>;
133
+ }>;
132
134
  };
133
135
  constructor(config?: SPAPSConfig);
134
136
  /** Raw API request helper that returns an ApiResponse-like shape */
@@ -250,6 +252,16 @@ declare class SPAPSClient {
250
252
  isAuthenticated: () => boolean;
251
253
  };
252
254
  payments: {
255
+ crypto: {
256
+ createInvoice: (payload: CreateCryptoInvoiceRequest) => Promise<spaps_types.CryptoInvoice>;
257
+ getInvoice: (invoiceId: string) => Promise<spaps_types.CryptoInvoice>;
258
+ getInvoiceStatus: (invoiceId: string) => Promise<CryptoInvoiceStatusSnapshot>;
259
+ reconcile: (options?: CryptoReconcileRequest) => Promise<{
260
+ job_id: string;
261
+ scheduled_at: string;
262
+ cursor?: Record<string, unknown>;
263
+ }>;
264
+ };
253
265
  createCheckoutSession: (payload: any) => Promise<CheckoutSession>;
254
266
  createPaymentCheckout: (params: {
255
267
  price_id: string;
@@ -397,6 +409,7 @@ declare class SPAPSClient {
397
409
  data: any;
398
410
  }>;
399
411
  }
412
+ declare function verifyCryptoWebhookSignature(options: VerifyCryptoWebhookSignatureOptions): boolean;
400
413
 
401
414
  declare class TokenManager {
402
415
  private static readonly ACCESS_TOKEN_KEY;
@@ -416,4 +429,4 @@ declare class WalletUtils {
416
429
  static isValidAddress(address: string, chainType?: 'solana' | 'ethereum' | 'bitcoin' | 'base'): boolean;
417
430
  }
418
431
 
419
- export { type AdminConfig, DEFAULT_ADMIN_ACCOUNTS, type PermissionCheckResult, PermissionChecker, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, TokenManager, WalletUtils, canAccessAdmin, createPermissionChecker, SPAPSClient as default, defaultPermissionChecker, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount };
432
+ export { type AdminConfig, DEFAULT_ADMIN_ACCOUNTS, type PermissionCheckResult, PermissionChecker, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, TokenManager, WalletUtils, canAccessAdmin, createPermissionChecker, SPAPSClient as default, defaultPermissionChecker, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, verifyCryptoWebhookSignature };
package/dist/index.js CHANGED
@@ -208,9 +208,11 @@ __export(index_exports, {
208
208
  getUserDisplay: () => getUserDisplay,
209
209
  getUserRole: () => getUserRole,
210
210
  hasPermission: () => hasPermission,
211
- isAdminAccount: () => isAdminAccount
211
+ isAdminAccount: () => isAdminAccount,
212
+ verifyCryptoWebhookSignature: () => verifyCryptoWebhookSignature
212
213
  });
213
214
  module.exports = __toCommonJS(index_exports);
215
+ var import_crypto = __toESM(require("crypto"));
214
216
  var import_axios = __toESM(require("axios"));
215
217
  init_permissions();
216
218
  if (typeof globalThis.fetch === "undefined") {
@@ -222,6 +224,13 @@ var SPAPSClient = class {
222
224
  accessToken;
223
225
  refreshToken;
224
226
  _isLocalMode = false;
227
+ unwrapApiResponse(response, fallback) {
228
+ const body = response?.data ?? response;
229
+ if (body?.success === false) {
230
+ throw new Error(body?.error?.message || fallback);
231
+ }
232
+ return body?.data ?? body;
233
+ }
225
234
  // Admin namespace for cleaner API
226
235
  admin = {
227
236
  createProduct: (productData) => this.createProduct(productData),
@@ -229,7 +238,8 @@ var SPAPSClient = class {
229
238
  deleteProduct: (productId) => this.deleteProduct(productId),
230
239
  createPrice: (priceData) => this.createPrice(priceData),
231
240
  syncProducts: () => this.syncProducts(),
232
- getProducts: () => this.getProducts()
241
+ getProducts: () => this.getProducts(),
242
+ triggerCryptoReconcile: (opts) => this.payments.crypto.reconcile(opts || {})
233
243
  };
234
244
  constructor(config = {}) {
235
245
  const apiUrl = config.apiUrl || process.env.SPAPS_API_URL || process.env.NEXT_PUBLIC_SPAPS_API_URL;
@@ -507,6 +517,48 @@ var SPAPSClient = class {
507
517
  isAuthenticated: () => !!this.accessToken
508
518
  };
509
519
  payments = {
520
+ crypto: {
521
+ createInvoice: async (payload) => {
522
+ const body = {
523
+ asset: payload.asset,
524
+ network: payload.network,
525
+ amount: payload.amount
526
+ };
527
+ if (typeof payload.expiresInSeconds === "number") {
528
+ body.expires_in_seconds = payload.expiresInSeconds;
529
+ }
530
+ if (payload.beneficiary) {
531
+ body.beneficiary = payload.beneficiary;
532
+ }
533
+ if (payload.metadata) {
534
+ body.metadata = payload.metadata;
535
+ }
536
+ const res = await this.client.post("/api/payments/crypto/invoices", body);
537
+ const data = this.unwrapApiResponse(res, "Failed to create crypto invoice");
538
+ return data.invoice;
539
+ },
540
+ getInvoice: async (invoiceId) => {
541
+ const res = await this.client.get(`/api/payments/crypto/invoices/${invoiceId}`);
542
+ const data = this.unwrapApiResponse(res, "Failed to fetch crypto invoice");
543
+ return data.invoice;
544
+ },
545
+ getInvoiceStatus: async (invoiceId) => {
546
+ const res = await this.client.get(`/api/payments/crypto/invoices/${invoiceId}/status`);
547
+ return this.unwrapApiResponse(res, "Failed to fetch crypto invoice status");
548
+ },
549
+ reconcile: async (options = {}) => {
550
+ const headers = {};
551
+ if (options.reconToken) {
552
+ headers["X-Recon-Token"] = options.reconToken;
553
+ }
554
+ const payload = {};
555
+ if (options.cursor) {
556
+ payload.cursor = options.cursor;
557
+ }
558
+ const res = await this.client.post("/api/payments/crypto/reconcile", payload, { headers });
559
+ return this.unwrapApiResponse(res, "Failed to trigger crypto reconciliation");
560
+ }
561
+ },
510
562
  createCheckoutSession: async (payload) => {
511
563
  const headers = {};
512
564
  if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
@@ -802,6 +854,37 @@ var SPAPSClient = class {
802
854
  return this.client.get("/health");
803
855
  }
804
856
  };
857
+ function verifyCryptoWebhookSignature(options) {
858
+ const { body, signature, secret, toleranceSeconds = 300 } = options;
859
+ if (!signature) {
860
+ throw new Error("Missing webhook signature");
861
+ }
862
+ const parts = signature.split(",").reduce((acc, part) => {
863
+ const [key, value] = part.split("=");
864
+ if (key && value) acc[key.trim()] = value.trim();
865
+ return acc;
866
+ }, {});
867
+ const timestamp = parts["t"];
868
+ const expected = parts["v1"];
869
+ if (!timestamp || !expected) {
870
+ throw new Error("Invalid webhook signature format");
871
+ }
872
+ const rawBody = typeof body === "string" ? body : body instanceof Uint8Array ? Buffer.from(body).toString("utf8") : JSON.stringify(body ?? {});
873
+ const computed = import_crypto.default.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
874
+ const expectedBuffer = Buffer.from(expected, "hex");
875
+ const computedBuffer = Buffer.from(computed, "hex");
876
+ if (expectedBuffer.length !== computedBuffer.length || !import_crypto.default.timingSafeEqual(expectedBuffer, computedBuffer)) {
877
+ throw new Error("Invalid webhook signature");
878
+ }
879
+ const ts = Number(timestamp);
880
+ if (Number.isFinite(ts) && toleranceSeconds > 0) {
881
+ const ageSeconds = Math.abs(Date.now() / 1e3 - ts);
882
+ if (ageSeconds > toleranceSeconds) {
883
+ throw new Error("Webhook signature timestamp outside tolerance window");
884
+ }
885
+ }
886
+ return true;
887
+ }
805
888
  var index_default = SPAPSClient;
806
889
  var TokenManager = class _TokenManager {
807
890
  static ACCESS_TOKEN_KEY = "sweet_potato_access_token";
@@ -911,5 +994,6 @@ var WalletUtils = class _WalletUtils {
911
994
  getUserDisplay,
912
995
  getUserRole,
913
996
  hasPermission,
914
- isAdminAccount
997
+ isAdminAccount,
998
+ verifyCryptoWebhookSignature
915
999
  });
package/dist/index.mjs CHANGED
@@ -188,6 +188,7 @@ var init_permissions = __esm({
188
188
 
189
189
  // src/index.ts
190
190
  init_permissions();
191
+ import crypto from "crypto";
191
192
  import axios from "axios";
192
193
  if (typeof globalThis.fetch === "undefined") {
193
194
  __require("cross-fetch/polyfill");
@@ -198,6 +199,13 @@ var SPAPSClient = class {
198
199
  accessToken;
199
200
  refreshToken;
200
201
  _isLocalMode = false;
202
+ unwrapApiResponse(response, fallback) {
203
+ const body = response?.data ?? response;
204
+ if (body?.success === false) {
205
+ throw new Error(body?.error?.message || fallback);
206
+ }
207
+ return body?.data ?? body;
208
+ }
201
209
  // Admin namespace for cleaner API
202
210
  admin = {
203
211
  createProduct: (productData) => this.createProduct(productData),
@@ -205,7 +213,8 @@ var SPAPSClient = class {
205
213
  deleteProduct: (productId) => this.deleteProduct(productId),
206
214
  createPrice: (priceData) => this.createPrice(priceData),
207
215
  syncProducts: () => this.syncProducts(),
208
- getProducts: () => this.getProducts()
216
+ getProducts: () => this.getProducts(),
217
+ triggerCryptoReconcile: (opts) => this.payments.crypto.reconcile(opts || {})
209
218
  };
210
219
  constructor(config = {}) {
211
220
  const apiUrl = config.apiUrl || process.env.SPAPS_API_URL || process.env.NEXT_PUBLIC_SPAPS_API_URL;
@@ -483,6 +492,48 @@ var SPAPSClient = class {
483
492
  isAuthenticated: () => !!this.accessToken
484
493
  };
485
494
  payments = {
495
+ crypto: {
496
+ createInvoice: async (payload) => {
497
+ const body = {
498
+ asset: payload.asset,
499
+ network: payload.network,
500
+ amount: payload.amount
501
+ };
502
+ if (typeof payload.expiresInSeconds === "number") {
503
+ body.expires_in_seconds = payload.expiresInSeconds;
504
+ }
505
+ if (payload.beneficiary) {
506
+ body.beneficiary = payload.beneficiary;
507
+ }
508
+ if (payload.metadata) {
509
+ body.metadata = payload.metadata;
510
+ }
511
+ const res = await this.client.post("/api/payments/crypto/invoices", body);
512
+ const data = this.unwrapApiResponse(res, "Failed to create crypto invoice");
513
+ return data.invoice;
514
+ },
515
+ getInvoice: async (invoiceId) => {
516
+ const res = await this.client.get(`/api/payments/crypto/invoices/${invoiceId}`);
517
+ const data = this.unwrapApiResponse(res, "Failed to fetch crypto invoice");
518
+ return data.invoice;
519
+ },
520
+ getInvoiceStatus: async (invoiceId) => {
521
+ const res = await this.client.get(`/api/payments/crypto/invoices/${invoiceId}/status`);
522
+ return this.unwrapApiResponse(res, "Failed to fetch crypto invoice status");
523
+ },
524
+ reconcile: async (options = {}) => {
525
+ const headers = {};
526
+ if (options.reconToken) {
527
+ headers["X-Recon-Token"] = options.reconToken;
528
+ }
529
+ const payload = {};
530
+ if (options.cursor) {
531
+ payload.cursor = options.cursor;
532
+ }
533
+ const res = await this.client.post("/api/payments/crypto/reconcile", payload, { headers });
534
+ return this.unwrapApiResponse(res, "Failed to trigger crypto reconciliation");
535
+ }
536
+ },
486
537
  createCheckoutSession: async (payload) => {
487
538
  const headers = {};
488
539
  if (this.accessToken) headers["Authorization"] = `Bearer ${this.accessToken}`;
@@ -778,6 +829,37 @@ var SPAPSClient = class {
778
829
  return this.client.get("/health");
779
830
  }
780
831
  };
832
+ function verifyCryptoWebhookSignature(options) {
833
+ const { body, signature, secret, toleranceSeconds = 300 } = options;
834
+ if (!signature) {
835
+ throw new Error("Missing webhook signature");
836
+ }
837
+ const parts = signature.split(",").reduce((acc, part) => {
838
+ const [key, value] = part.split("=");
839
+ if (key && value) acc[key.trim()] = value.trim();
840
+ return acc;
841
+ }, {});
842
+ const timestamp = parts["t"];
843
+ const expected = parts["v1"];
844
+ if (!timestamp || !expected) {
845
+ throw new Error("Invalid webhook signature format");
846
+ }
847
+ const rawBody = typeof body === "string" ? body : body instanceof Uint8Array ? Buffer.from(body).toString("utf8") : JSON.stringify(body ?? {});
848
+ const computed = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
849
+ const expectedBuffer = Buffer.from(expected, "hex");
850
+ const computedBuffer = Buffer.from(computed, "hex");
851
+ if (expectedBuffer.length !== computedBuffer.length || !crypto.timingSafeEqual(expectedBuffer, computedBuffer)) {
852
+ throw new Error("Invalid webhook signature");
853
+ }
854
+ const ts = Number(timestamp);
855
+ if (Number.isFinite(ts) && toleranceSeconds > 0) {
856
+ const ageSeconds = Math.abs(Date.now() / 1e3 - ts);
857
+ if (ageSeconds > toleranceSeconds) {
858
+ throw new Error("Webhook signature timestamp outside tolerance window");
859
+ }
860
+ }
861
+ return true;
862
+ }
781
863
  var index_default = SPAPSClient;
782
864
  var TokenManager = class _TokenManager {
783
865
  static ACCESS_TOKEN_KEY = "sweet_potato_access_token";
@@ -887,5 +969,6 @@ export {
887
969
  getUserDisplay,
888
970
  getUserRole,
889
971
  hasPermission,
890
- isAdminAccount
972
+ isAdminAccount,
973
+ verifyCryptoWebhookSignature
891
974
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spaps-sdk",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Sweet Potato Authentication & Payment Service SDK - Zero-config client with built-in permission checking and role-based access control",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -44,7 +44,7 @@
44
44
  "email": "buildooor@gmail.com"
45
45
  },
46
46
  "dependencies": {
47
- "spaps-types": "^1.0.10",
47
+ "spaps-types": "^1.0.12",
48
48
  "axios": "^1.6.0",
49
49
  "cross-fetch": "^4.0.0"
50
50
  },