@lightsparkdev/core 1.4.3 → 1.4.5

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.
@@ -1 +1 @@
1
- export { A as AppendUnitsOptions, ad as ById, ao as Complete, o as CurrencyAmountArg, k as CurrencyAmountInputObj, m as CurrencyAmountObj, n as CurrencyAmountPreferenceObj, X as CurrencyCodes, W as CurrencyLocales, j as CurrencyMap, f as CurrencyUnit, g as CurrencyUnitType, ah as DeepPartial, D as DeprecatedCurrencyAmountObj, ac as ExpandRecursively, ag as ExtractByTypename, ai as JSONLiteral, ak as JSONObject, aj as JSONType, ab as Maybe, al as NN, ae as OmitTypename, an as PartialBy, ap as RequiredKeys, S as SDKCurrencyAmountType, aq as StateCode, U as UmaCurrency, l as UmaCurrencyAmount, z as abbrCurrencyUnit, b as b64decode, a as b64encode, Q as bytesToHex, a1 as clamp, i as convertCurrencyAmount, h as convertCurrencyAmountValue, V as countryCodesToCurrencyCodes, c as createSha256Hash, d as defaultCurrencyCode, a0 as deleteLocalStorageItem, e as ensureArray, P as errorToJSON, B as formatCurrencyStr, G as formatInviteAmount, w as getCurrencyAmount, T as getCurrentLocale, N as getErrorMsg, _ as getLocalStorageBoolean, Z as getLocalStorageConfigItem, R as hexToBytes, K as isBare, H as isBrowser, p as isCurrencyAmountInputObj, s as isCurrencyAmountObj, t as isCurrencyAmountPreferenceObj, y as isCurrencyMap, r as isDeprecatedCurrencyAmountObj, L as isError, O as isErrorMsg, M as isErrorWithMessage, I as isNode, a4 as isNumber, a9 as isObject, aa as isRecord, v as isSDKCurrencyAmount, J as isTest, af as isType, a8 as isUint8Array, q as isUmaCurrencyAmount, a2 as linearInterpolate, Y as localeToCurrencyCode, F as localeToCurrencySymbol, a7 as lsidToUUID, x as mapCurrencyAmount, am as notNullUndefined, a5 as pollUntil, a3 as round, E as separateCurrencyStrParts, $ as setLocalStorageBoolean, a6 as sleep, u as urlsafe_b64decode, as as zipcodeToState, ar as zipcodeToStateCode } from '../index-gUXiTlhb.js';
1
+ export { A as AppendUnitsOptions, ae as ById, ap as Complete, o as CurrencyAmountArg, k as CurrencyAmountInputObj, m as CurrencyAmountObj, n as CurrencyAmountPreferenceObj, Y as CurrencyCodes, X as CurrencyLocales, j as CurrencyMap, f as CurrencyUnit, g as CurrencyUnitType, ai as DeepPartial, D as DeprecatedCurrencyAmountObj, ad as ExpandRecursively, ah as ExtractByTypename, aj as JSONLiteral, al as JSONObject, ak as JSONType, ac as Maybe, am as NN, af as OmitTypename, ao as PartialBy, aq as RequiredKeys, S as SDKCurrencyAmountType, ar as StateCode, U as UmaCurrency, l as UmaCurrencyAmount, z as abbrCurrencyUnit, b as b64decode, a as b64encode, R as bytesToHex, a2 as clamp, i as convertCurrencyAmount, h as convertCurrencyAmountValue, W as countryCodesToCurrencyCodes, c as createSha256Hash, d as defaultCurrencyCode, a1 as deleteLocalStorageItem, e as ensureArray, Q as errorToJSON, B as formatCurrencyStr, G as formatInviteAmount, w as getCurrencyAmount, V as getCurrentLocale, O as getErrorMsg, $ as getLocalStorageBoolean, _ as getLocalStorageConfigItem, T as hexToBytes, K as isBare, H as isBrowser, p as isCurrencyAmountInputObj, s as isCurrencyAmountObj, t as isCurrencyAmountPreferenceObj, y as isCurrencyMap, r as isDeprecatedCurrencyAmountObj, M as isError, P as isErrorMsg, N as isErrorWithMessage, I as isNode, a5 as isNumber, aa as isObject, L as isReactNative, ab as isRecord, v as isSDKCurrencyAmount, J as isTest, ag as isType, a9 as isUint8Array, q as isUmaCurrencyAmount, a3 as linearInterpolate, Z as localeToCurrencyCode, F as localeToCurrencySymbol, a8 as lsidToUUID, x as mapCurrencyAmount, an as notNullUndefined, a6 as pollUntil, a4 as round, E as separateCurrencyStrParts, a0 as setLocalStorageBoolean, a7 as sleep, u as urlsafe_b64decode, at as zipcodeToState, as as zipcodeToStateCode } from '../index-CpAae9dR.js';
@@ -34,6 +34,7 @@ import {
34
34
  isNode,
35
35
  isNumber,
36
36
  isObject,
37
+ isReactNative,
37
38
  isRecord,
38
39
  isSDKCurrencyAmount,
39
40
  isTest,
@@ -54,7 +55,7 @@ import {
54
55
  urlsafe_b64decode,
55
56
  zipcodeToState,
56
57
  zipcodeToStateCode
57
- } from "../chunk-36QHRQJC.js";
58
+ } from "../chunk-IWYMQNIA.js";
58
59
  export {
59
60
  CurrencyUnit,
60
61
  abbrCurrencyUnit,
@@ -91,6 +92,7 @@ export {
91
92
  isNode,
92
93
  isNumber,
93
94
  isObject,
95
+ isReactNative,
94
96
  isRecord,
95
97
  isSDKCurrencyAmount,
96
98
  isTest,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightsparkdev/core",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "description": "Lightspark JS SDK",
5
5
  "author": "Lightspark Inc.",
6
6
  "keywords": [
@@ -24,6 +24,7 @@
24
24
  "types": "./dist/index.d.ts",
25
25
  "exports": {
26
26
  ".": {
27
+ "react-native": "./dist/react-native/index.js",
27
28
  "import": "./dist/index.js",
28
29
  "require": "./dist/index.cjs"
29
30
  },
@@ -39,6 +40,7 @@
39
40
  "src/*",
40
41
  "dist/*",
41
42
  "dist/utils/*",
43
+ "dist/react-native/*",
42
44
  "CHANGELOG.md"
43
45
  ],
44
46
  "scripts": {
@@ -51,6 +53,7 @@
51
53
  "lint:fix": "eslint --fix .",
52
54
  "lint:watch": "esw ./src -w --ext .ts,.tsx,.js --color",
53
55
  "lint": "eslint .",
56
+ "circular-deps": "madge --circular --extensions ts,tsx src",
54
57
  "package:checks": "yarn publint && yarn attw --pack .",
55
58
  "postversion": "yarn build",
56
59
  "test-cmd": "node --experimental-vm-modules $(yarn bin jest) --no-cache --runInBand --bail",
@@ -81,6 +84,7 @@
81
84
  "eslint-watch": "^8.0.0",
82
85
  "jest": "^29.6.2",
83
86
  "lodash-es": "^4.17.21",
87
+ "madge": "^6.1.0",
84
88
  "prettier": "3.0.3",
85
89
  "prettier-plugin-organize-imports": "^3.2.4",
86
90
  "publint": "^0.3.9",
@@ -89,6 +93,16 @@
89
93
  "tsup": "^8.2.4",
90
94
  "typescript": "^5.6.2"
91
95
  },
96
+ "madge": {
97
+ "detectiveOptions": {
98
+ "ts": {
99
+ "skipTypeImports": true
100
+ },
101
+ "tsx": {
102
+ "skipTypeImports": true
103
+ }
104
+ }
105
+ },
92
106
  "engines": {
93
107
  "node": ">=18"
94
108
  }
package/src/Logger.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { ConfigKeys, getLocalStorageConfigItem } from "./index.js";
1
+ import { ConfigKeys } from "./constants/index.js";
2
2
  import { isBrowser, isTest } from "./utils/environment.js";
3
+ import { getLocalStorageConfigItem } from "./utils/localStorage.js";
3
4
 
4
5
  type GetLoggingEnabled = (() => Promise<boolean> | boolean) | undefined;
5
6
 
@@ -1,6 +1,8 @@
1
1
  import secp256k1 from "secp256k1";
2
- import { SigningKeyType, type CryptoInterface } from "../index.js";
3
- import { createSha256Hash, hexToBytes } from "../utils/index.js";
2
+ import { createSha256Hash } from "../utils/createHash.js";
3
+ import { hexToBytes } from "../utils/hex.js";
4
+ import type { CryptoInterface } from "./crypto.js";
5
+ import { SigningKeyType } from "./types.js";
4
6
 
5
7
  interface Alias {
6
8
  alias: string;
package/src/index.ts CHANGED
@@ -1,13 +1,5 @@
1
1
  // Copyright ©, 2023-present, Lightspark Group, Inc. - All Rights Reserved
2
2
 
3
- export * from "./auth/index.js";
4
- export * from "./constants/index.js";
5
- export * from "./crypto/index.js";
6
- export { default as LightsparkException } from "./LightsparkException.js";
7
- export { Logger, LoggingLevel, logger } from "./Logger.js";
8
- export * from "./requester/index.js";
9
- export {
10
- default as ServerEnvironment,
11
- apiDomainForEnvironment,
12
- } from "./ServerEnvironment.js";
13
- export * from "./utils/index.js";
3
+ export { DefaultRequester as Requester } from "./requester/DefaultRequester.js";
4
+ export { default as Query } from "./requester/Query.js";
5
+ export * from "./shared.js";
@@ -0,0 +1,2 @@
1
+ export * from "../requester/index.js";
2
+ export * from "../shared.js";
@@ -0,0 +1,46 @@
1
+ import type AuthProvider from "../auth/AuthProvider.js";
2
+ import { isBare, isNode } from "../utils/environment.js";
3
+ import Requester from "./Requester.js";
4
+
5
+ export class DefaultRequester extends Requester {
6
+ protected async initWsClient(baseUrl: string, authProvider: AuthProvider) {
7
+ if (!this.resolveWsClient) {
8
+ /* If resolveWsClient is null assume already initialized: */
9
+ return this.wsClient;
10
+ }
11
+
12
+ if (isBare) {
13
+ /* graphql-ws library is currently not supported in Bare environment, see LIG-7942 */
14
+ return null;
15
+ }
16
+
17
+ let websocketImpl;
18
+ if (isNode && typeof WebSocket === "undefined") {
19
+ const wsModule = await import("ws");
20
+ websocketImpl = wsModule.default;
21
+ }
22
+ let websocketProtocol = "wss";
23
+ if (baseUrl.startsWith("http://")) {
24
+ websocketProtocol = "ws";
25
+ }
26
+
27
+ const graphqlWsModule = await import("graphql-ws");
28
+ const { createClient } = graphqlWsModule;
29
+
30
+ const wsClient = createClient({
31
+ url: `${websocketProtocol}://${this.stripProtocol(this.baseUrl)}/${
32
+ this.schemaEndpoint
33
+ }`,
34
+ connectionParams: () => authProvider.addWsConnectionParams({}),
35
+ webSocketImpl: websocketImpl,
36
+ });
37
+
38
+ if (this.resolveWsClient) {
39
+ this.resolveWsClient(wsClient);
40
+ this.resolveWsClient = null;
41
+ }
42
+
43
+ return wsClient;
44
+ }
45
+ }
46
+ export default DefaultRequester;
@@ -17,7 +17,7 @@ import type { SigningKey } from "../crypto/SigningKey.js";
17
17
  import LightsparkException from "../LightsparkException.js";
18
18
  import { logger } from "../Logger.js";
19
19
  import { b64encode } from "../utils/base64.js";
20
- import { isBare, isNode } from "../utils/environment.js";
20
+ import { isNode } from "../utils/environment.js";
21
21
 
22
22
  const DEFAULT_BASE_URL = "api.lightspark.com";
23
23
  dayjs.extend(utc);
@@ -31,14 +31,14 @@ type BodyData = {
31
31
  };
32
32
 
33
33
  class Requester {
34
- private wsClient: Promise<WsClient | null>;
35
- private resolveWsClient: ((value: WsClient | null) => void) | null = null;
34
+ protected wsClient: Promise<WsClient | null>;
35
+ protected resolveWsClient: ((value: WsClient | null) => void) | null = null;
36
36
  constructor(
37
37
  private readonly nodeKeyCache: NodeKeyCache,
38
- private readonly schemaEndpoint: string,
38
+ protected readonly schemaEndpoint: string,
39
39
  private readonly sdkUserAgent: string,
40
40
  private readonly authProvider: AuthProvider = new StubAuthProvider(),
41
- private readonly baseUrl: string = DEFAULT_BASE_URL,
41
+ protected readonly baseUrl: string = DEFAULT_BASE_URL,
42
42
  private readonly cryptoImpl: CryptoInterface = DefaultCrypto,
43
43
  private readonly signingKey?: SigningKey,
44
44
  private readonly fetchImpl: typeof fetch = fetch,
@@ -50,44 +50,11 @@ class Requester {
50
50
  autoBind(this);
51
51
  }
52
52
 
53
- private async initWsClient(baseUrl: string, authProvider: AuthProvider) {
54
- if (!this.resolveWsClient) {
55
- /* If resolveWsClient is null assume already initialized: */
56
- return this.wsClient;
57
- }
58
-
59
- if (isBare) {
60
- /* graphql-ws library is currently not supported in Bare environment, see LIG-7942 */
61
- return null;
62
- }
63
-
64
- let websocketImpl;
65
- if (isNode && typeof WebSocket === "undefined") {
66
- const wsModule = await import("ws");
67
- websocketImpl = wsModule.default;
68
- }
69
- let websocketProtocol = "wss";
70
- if (baseUrl.startsWith("http://")) {
71
- websocketProtocol = "ws";
72
- }
73
-
74
- const graphqlWsModule = await import("graphql-ws");
75
- const { createClient } = graphqlWsModule;
76
-
77
- const wsClient = createClient({
78
- url: `${websocketProtocol}://${this.stripProtocol(this.baseUrl)}/${
79
- this.schemaEndpoint
80
- }`,
81
- connectionParams: () => authProvider.addWsConnectionParams({}),
82
- webSocketImpl: websocketImpl,
83
- });
84
-
85
- if (this.resolveWsClient) {
86
- this.resolveWsClient(wsClient);
87
- this.resolveWsClient = null;
88
- }
89
-
90
- return wsClient;
53
+ protected initWsClient(
54
+ baseUrl: string,
55
+ authProvider: AuthProvider,
56
+ ): Promise<WsClient | null> {
57
+ return Promise.resolve(null);
91
58
  }
92
59
 
93
60
  public async executeQuery<T>(query: Query<T>): Promise<T | null> {
@@ -293,7 +260,7 @@ class Requester {
293
260
  return `${this.sdkUserAgent} ${platform}/${platformVersion}`;
294
261
  }
295
262
 
296
- private stripProtocol(url: string): string {
263
+ protected stripProtocol(url: string): string {
297
264
  return url.replace(/.*?:\/\//g, "");
298
265
  }
299
266
 
@@ -0,0 +1,129 @@
1
+ import { beforeEach, jest } from "@jest/globals";
2
+
3
+ import type { Client as WsClient } from "graphql-ws";
4
+ import type AuthProvider from "../../auth/AuthProvider.js";
5
+ import type { CryptoInterface } from "../../crypto/crypto.js";
6
+ import type NodeKeyCache from "../../crypto/NodeKeyCache.js";
7
+ import type { SigningKey } from "../../crypto/SigningKey.js";
8
+ import { SigningKeyType } from "../../crypto/types.js";
9
+
10
+ /* Mocking ESM modules (when running node with --experimental-vm-modules)
11
+ requires unstable_mockModule, see https://bit.ly/433nRV1 */
12
+ await jest.unstable_mockModule("graphql-ws", () => ({
13
+ __esModule: true,
14
+ createClient: jest.fn(),
15
+ }));
16
+ /* Since Requester uses graphql-ws we need a dynamic import after the above mock */
17
+ const { DefaultRequester } = await import("../DefaultRequester.js");
18
+
19
+ describe("DefaultRequester", () => {
20
+ const schemaEndpoint = "graphql";
21
+ const sdkUserAgent = "test-agent";
22
+ const baseUrl = "https://api.example.com";
23
+
24
+ let nodeKeyCache: NodeKeyCache;
25
+ let authProvider: AuthProvider;
26
+ let signingKey: SigningKey;
27
+ let cryptoImpl: CryptoInterface;
28
+ let fetchImpl: typeof fetch;
29
+
30
+ beforeEach(() => {
31
+ nodeKeyCache = {
32
+ getKey: jest.fn(),
33
+ hasKey: jest.fn(),
34
+ } as unknown as NodeKeyCache;
35
+
36
+ authProvider = {
37
+ addAuthHeaders: jest.fn(async (headers: Record<string, string>) => ({
38
+ ...headers,
39
+ "X-Test": "1",
40
+ })),
41
+ isAuthorized: jest.fn(async () => true),
42
+ addWsConnectionParams: jest.fn(
43
+ async (params: Record<string, unknown>) => ({
44
+ ...params,
45
+ ws: true,
46
+ }),
47
+ ),
48
+ } satisfies AuthProvider;
49
+
50
+ signingKey = {
51
+ type: SigningKeyType.RSASigningKey,
52
+ sign: jest.fn(async (data: Uint8Array) => new Uint8Array([1, 2, 3])),
53
+ } satisfies SigningKey;
54
+
55
+ cryptoImpl = {
56
+ decryptSecretWithNodePassword: jest.fn(async () => new ArrayBuffer(0)),
57
+ generateSigningKeyPair: jest.fn(async () => ({
58
+ publicKey: "",
59
+ privateKey: "",
60
+ })),
61
+ serializeSigningKey: jest.fn(async () => new ArrayBuffer(0)),
62
+ getNonce: jest.fn(async () => 123),
63
+ sign: jest.fn(async () => new ArrayBuffer(0)),
64
+ importPrivateSigningKey: jest.fn(async () => ""),
65
+ } satisfies CryptoInterface;
66
+
67
+ fetchImpl = jest.fn(
68
+ async () =>
69
+ ({
70
+ ok: true,
71
+ json: async () => ({ data: { foo: "bar" }, errors: undefined }),
72
+ statusText: "OK",
73
+ }) as Response,
74
+ );
75
+ });
76
+
77
+ describe("subscribe", () => {
78
+ it("returns an Observable for a valid subscription", async () => {
79
+ // Mock wsClient and its subscribe method
80
+ const wsClient = {
81
+ subscribe: jest.fn(
82
+ (
83
+ _body,
84
+ handlers: { next?: (data: unknown) => void; complete?: () => void },
85
+ ) => {
86
+ setTimeout(() => {
87
+ handlers.next?.({ data: { foo: "bar" } });
88
+ handlers.complete?.();
89
+ }, 10);
90
+ return jest.fn();
91
+ },
92
+ ),
93
+ } as unknown as WsClient;
94
+
95
+ const { createClient } = await import("graphql-ws");
96
+ (createClient as jest.Mock).mockReturnValue(wsClient);
97
+
98
+ const requester = new DefaultRequester(
99
+ nodeKeyCache,
100
+ schemaEndpoint,
101
+ sdkUserAgent,
102
+ authProvider,
103
+ baseUrl,
104
+ cryptoImpl,
105
+ signingKey,
106
+ fetchImpl,
107
+ );
108
+
109
+ const observable = requester.subscribe<{ foo: string }>(
110
+ "subscription TestSub { foo }",
111
+ );
112
+
113
+ const results: { foo: string }[] = [];
114
+ await new Promise<void>((resolve) => {
115
+ observable.subscribe({
116
+ next: (data: { data: { foo: string } }) => {
117
+ results.push(data.data);
118
+ },
119
+ complete: () => {
120
+ expect(results).toEqual([{ foo: "bar" }]);
121
+ resolve();
122
+ },
123
+ });
124
+ });
125
+
126
+ expect(wsClient.subscribe).toHaveBeenCalled();
127
+ });
128
+ });
129
+ });
@@ -1,6 +1,5 @@
1
1
  import { beforeEach, jest } from "@jest/globals";
2
2
 
3
- import type { Client as WsClient } from "graphql-ws";
4
3
  import type AuthProvider from "../../auth/AuthProvider.js";
5
4
  import type { CryptoInterface } from "../../crypto/crypto.js";
6
5
  import type NodeKeyCache from "../../crypto/NodeKeyCache.js";
@@ -16,7 +15,7 @@ await jest.unstable_mockModule("graphql-ws", () => ({
16
15
  createClient: jest.fn(),
17
16
  }));
18
17
  /* Since Requester uses graphql-ws we need a dynamic import after the above mock */
19
- const { Requester } = await import("../index.js");
18
+ const { default: Requester } = await import("../Requester.js");
20
19
 
21
20
  describe("Requester", () => {
22
21
  const schemaEndpoint = "graphql";
@@ -257,26 +256,7 @@ describe("Requester", () => {
257
256
  expect(() => requester.subscribe("invalid")).toThrow(LightsparkException);
258
257
  });
259
258
 
260
- it("returns an Observable for a valid subscription", async () => {
261
- // Mock wsClient and its subscribe method
262
- const wsClient = {
263
- subscribe: jest.fn(
264
- (
265
- _body,
266
- handlers: { next?: (data: unknown) => void; complete?: () => void },
267
- ) => {
268
- setTimeout(() => {
269
- handlers.next?.({ data: { foo: "bar" } });
270
- handlers.complete?.();
271
- }, 10);
272
- return jest.fn();
273
- },
274
- ),
275
- } as unknown as WsClient;
276
-
277
- const { createClient } = await import("graphql-ws");
278
- (createClient as jest.Mock).mockReturnValue(wsClient);
279
-
259
+ it("emits error when wsClient is not initialized", async () => {
280
260
  const requester = new Requester(
281
261
  nodeKeyCache,
282
262
  schemaEndpoint,
@@ -287,25 +267,36 @@ describe("Requester", () => {
287
267
  signingKey,
288
268
  fetchImpl,
289
269
  );
270
+ // Resolve internal wsClient promise to null so the observable emits an error.
271
+ (
272
+ requester as unknown as {
273
+ resolveWsClient: ((v: unknown) => void) | null;
274
+ }
275
+ ).resolveWsClient?.(null);
290
276
 
291
- const observable = requester.subscribe<{ foo: string }>(
292
- "subscription TestSub { foo }",
293
- );
277
+ const observable = requester.subscribe("subscription TestSub { foo }");
294
278
 
295
- const results: { foo: string }[] = [];
296
279
  await new Promise<void>((resolve) => {
297
280
  observable.subscribe({
298
- next: (data) => {
299
- results.push(data.data);
281
+ next: () => {
282
+ throw new Error(
283
+ "Should not emit next when wsClient is uninitialized",
284
+ );
300
285
  },
301
- complete: () => {
302
- expect(results).toEqual([{ foo: "bar" }]);
286
+ error: (err) => {
287
+ expect(err).toBeInstanceOf(LightsparkException);
288
+ expect(String((err as Error).message)).toMatch(
289
+ /WebSocket client is not initialized/,
290
+ );
303
291
  resolve();
304
292
  },
293
+ complete: () => {
294
+ throw new Error(
295
+ "Should not complete when wsClient is uninitialized",
296
+ );
297
+ },
305
298
  });
306
299
  });
307
-
308
- expect(wsClient.subscribe).toHaveBeenCalled();
309
300
  });
310
301
  });
311
302
 
package/src/shared.ts ADDED
@@ -0,0 +1,10 @@
1
+ export * from "./auth/index.js";
2
+ export * from "./constants/index.js";
3
+ export * from "./crypto/index.js";
4
+ export { default as LightsparkException } from "./LightsparkException.js";
5
+ export { Logger, LoggingLevel, logger } from "./Logger.js";
6
+ export {
7
+ default as ServerEnvironment,
8
+ apiDomainForEnvironment,
9
+ } from "./ServerEnvironment.js";
10
+ export * from "./utils/index.js";
@@ -24,6 +24,7 @@ export const CurrencyUnit = {
24
24
  EUR: "EUR",
25
25
  GBP: "GBP",
26
26
  INR: "INR",
27
+ USDT: "USDT",
27
28
 
28
29
  Bitcoin: "BITCOIN",
29
30
  Microbitcoin: "MICROBITCOIN",
@@ -36,6 +37,7 @@ export const CurrencyUnit = {
36
37
  Php: "PHP",
37
38
  Gbp: "GBP",
38
39
  Inr: "INR",
40
+ Usdt: "USDT",
39
41
  } as const;
40
42
 
41
43
  export type CurrencyUnitType = (typeof CurrencyUnit)[keyof typeof CurrencyUnit];
@@ -67,6 +69,7 @@ const standardUnitConversionObj = {
67
69
  [CurrencyUnit.EUR]: (v: number) => v,
68
70
  [CurrencyUnit.GBP]: (v: number) => v,
69
71
  [CurrencyUnit.INR]: (v: number) => v,
72
+ [CurrencyUnit.USDT]: (v: number) => v,
70
73
  };
71
74
 
72
75
  /* Round without decimals since we're returning cents: */
@@ -97,6 +100,7 @@ const CONVERSION_MAP = {
97
100
  [CurrencyUnit.EUR]: toBitcoinConversion,
98
101
  [CurrencyUnit.GBP]: toBitcoinConversion,
99
102
  [CurrencyUnit.INR]: toBitcoinConversion,
103
+ [CurrencyUnit.USDT]: toBitcoinConversion,
100
104
  },
101
105
  [CurrencyUnit.MICROBITCOIN]: {
102
106
  [CurrencyUnit.BITCOIN]: (v: number) => v / 1_000_000,
@@ -111,6 +115,7 @@ const CONVERSION_MAP = {
111
115
  [CurrencyUnit.EUR]: toMicrobitcoinConversion,
112
116
  [CurrencyUnit.GBP]: toMicrobitcoinConversion,
113
117
  [CurrencyUnit.INR]: toMicrobitcoinConversion,
118
+ [CurrencyUnit.USDT]: toMicrobitcoinConversion,
114
119
  },
115
120
  [CurrencyUnit.MILLIBITCOIN]: {
116
121
  [CurrencyUnit.BITCOIN]: (v: number) => v / 1_000,
@@ -125,6 +130,7 @@ const CONVERSION_MAP = {
125
130
  [CurrencyUnit.EUR]: toMillibitcoinConversion,
126
131
  [CurrencyUnit.GBP]: toMillibitcoinConversion,
127
132
  [CurrencyUnit.INR]: toMillibitcoinConversion,
133
+ [CurrencyUnit.USDT]: toMillibitcoinConversion,
128
134
  },
129
135
  [CurrencyUnit.MILLISATOSHI]: {
130
136
  [CurrencyUnit.BITCOIN]: (v: number) => v / 100_000_000_000,
@@ -139,6 +145,7 @@ const CONVERSION_MAP = {
139
145
  [CurrencyUnit.EUR]: toMillisatoshiConversion,
140
146
  [CurrencyUnit.GBP]: toMillisatoshiConversion,
141
147
  [CurrencyUnit.INR]: toMillisatoshiConversion,
148
+ [CurrencyUnit.USDT]: toMillisatoshiConversion,
142
149
  },
143
150
  [CurrencyUnit.NANOBITCOIN]: {
144
151
  [CurrencyUnit.BITCOIN]: (v: number) => v / 1_000_000_000,
@@ -153,6 +160,7 @@ const CONVERSION_MAP = {
153
160
  [CurrencyUnit.EUR]: toNanobitcoinConversion,
154
161
  [CurrencyUnit.GBP]: toNanobitcoinConversion,
155
162
  [CurrencyUnit.INR]: toNanobitcoinConversion,
163
+ [CurrencyUnit.USDT]: toNanobitcoinConversion,
156
164
  },
157
165
  [CurrencyUnit.SATOSHI]: {
158
166
  [CurrencyUnit.BITCOIN]: (v: number) => v / 100_000_000,
@@ -167,6 +175,7 @@ const CONVERSION_MAP = {
167
175
  [CurrencyUnit.EUR]: toSatoshiConversion,
168
176
  [CurrencyUnit.GBP]: toSatoshiConversion,
169
177
  [CurrencyUnit.INR]: toSatoshiConversion,
178
+ [CurrencyUnit.USDT]: toSatoshiConversion,
170
179
  },
171
180
  [CurrencyUnit.USD]: standardUnitConversionObj,
172
181
  [CurrencyUnit.MXN]: standardUnitConversionObj,
@@ -174,6 +183,7 @@ const CONVERSION_MAP = {
174
183
  [CurrencyUnit.EUR]: standardUnitConversionObj,
175
184
  [CurrencyUnit.GBP]: standardUnitConversionObj,
176
185
  [CurrencyUnit.INR]: standardUnitConversionObj,
186
+ [CurrencyUnit.USDT]: standardUnitConversionObj,
177
187
  };
178
188
 
179
189
  export function convertCurrencyAmountValue(
@@ -241,6 +251,7 @@ export type CurrencyMap = {
241
251
  [CurrencyUnit.EUR]: number;
242
252
  [CurrencyUnit.GBP]: number;
243
253
  [CurrencyUnit.INR]: number;
254
+ [CurrencyUnit.USDT]: number;
244
255
  [CurrencyUnit.FUTURE_VALUE]: number;
245
256
  formatted: {
246
257
  sats: string;
@@ -258,6 +269,7 @@ export type CurrencyMap = {
258
269
  [CurrencyUnit.EUR]: string;
259
270
  [CurrencyUnit.GBP]: string;
260
271
  [CurrencyUnit.INR]: string;
272
+ [CurrencyUnit.USDT]: string;
261
273
  [CurrencyUnit.FUTURE_VALUE]: string;
262
274
  };
263
275
  isZero: boolean;
@@ -459,6 +471,7 @@ function convertCurrencyAmountValues(
459
471
  mibtc: CurrencyUnit.MICROBITCOIN,
460
472
  mlbtc: CurrencyUnit.MILLIBITCOIN,
461
473
  nbtc: CurrencyUnit.NANOBITCOIN,
474
+ usdt: CurrencyUnit.USDT,
462
475
  };
463
476
  return Object.entries(namesToUnits).reduce(
464
477
  (acc, [name, unit]) => {
@@ -505,8 +518,21 @@ export function mapCurrencyAmount(
505
518
  * preferred_currency_unit on CurrencyAmount types: */
506
519
  const conversionOverride = getPreferredConversionOverride(currencyAmountArg);
507
520
 
508
- const { sats, msats, btc, usd, mxn, php, mibtc, mlbtc, nbtc, eur, gbp, inr } =
509
- convertCurrencyAmountValues(unit, value, unitsPerBtc, conversionOverride);
521
+ const {
522
+ sats,
523
+ msats,
524
+ btc,
525
+ usd,
526
+ mxn,
527
+ php,
528
+ mibtc,
529
+ mlbtc,
530
+ nbtc,
531
+ eur,
532
+ gbp,
533
+ inr,
534
+ usdt,
535
+ } = convertCurrencyAmountValues(unit, value, unitsPerBtc, conversionOverride);
510
536
 
511
537
  const mapWithCurrencyUnits = {
512
538
  [CurrencyUnit.BITCOIN]: btc,
@@ -521,6 +547,7 @@ export function mapCurrencyAmount(
521
547
  [CurrencyUnit.MICROBITCOIN]: mibtc,
522
548
  [CurrencyUnit.MILLIBITCOIN]: mlbtc,
523
549
  [CurrencyUnit.NANOBITCOIN]: nbtc,
550
+ [CurrencyUnit.USDT]: usdt,
524
551
  [CurrencyUnit.FUTURE_VALUE]: NaN,
525
552
  formatted: {
526
553
  [CurrencyUnit.BITCOIN]: formatCurrencyStr({
@@ -571,6 +598,10 @@ export function mapCurrencyAmount(
571
598
  value: inr,
572
599
  unit: CurrencyUnit.INR,
573
600
  }),
601
+ [CurrencyUnit.USDT]: formatCurrencyStr({
602
+ value: usdt,
603
+ unit: CurrencyUnit.USDT,
604
+ }),
574
605
  [CurrencyUnit.FUTURE_VALUE]: "-",
575
606
  },
576
607
  };
@@ -651,6 +682,8 @@ export const abbrCurrencyUnit = (unit: CurrencyUnitType) => {
651
682
  return "GBP";
652
683
  case CurrencyUnit.INR:
653
684
  return "INR";
685
+ case CurrencyUnit.USDT:
686
+ return "USDT";
654
687
  }
655
688
  return "Unsupported CurrencyUnit";
656
689
  };
@@ -14,3 +14,6 @@ export const isTest = isNode && process.env.NODE_ENV === "test";
14
14
 
15
15
  /* https://github.com/holepunchto/which-runtime/blob/main/index.js */
16
16
  export const isBare = typeof Bare !== "undefined";
17
+
18
+ export const isReactNative =
19
+ typeof navigator !== "undefined" && navigator.product === "ReactNative";