@lightsparkdev/core 1.0.21 → 1.1.0

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.
@@ -137,7 +137,7 @@ class Requester {
137
137
  }
138
138
  }
139
139
  const operation = operationMatch[2];
140
- let bodyData: BodyData = {
140
+ const payload: BodyData = {
141
141
  query: queryPayload,
142
142
  variables,
143
143
  operationName: operation,
@@ -149,16 +149,22 @@ class Requester {
149
149
  "Content-Type": "application/json",
150
150
  "X-Lightspark-SDK": sdkUserAgent,
151
151
  "User-Agent": browserUserAgent || sdkUserAgent,
152
+ "X-GraphQL-Operation": operation,
152
153
  };
153
- const headers = skipAuth
154
+ const headers: { [key: string]: string } = skipAuth
154
155
  ? baseHeaders
155
156
  : await this.authProvider.addAuthHeaders(baseHeaders);
156
- bodyData = await this.addSigningDataIfNeeded(
157
- bodyData,
157
+ let bodyData = await this.addSigningDataIfNeeded(
158
+ payload,
158
159
  headers,
159
160
  signingNodeId,
160
161
  );
161
162
 
163
+ if (bodyData.length > 1024 && typeof CompressionStream != "undefined") {
164
+ bodyData = await compress(bodyData);
165
+ headers["Content-Encoding"] = "deflate";
166
+ }
167
+
162
168
  let urlWithProtocol = this.baseUrl;
163
169
  if (
164
170
  !urlWithProtocol.startsWith("https://") &&
@@ -177,7 +183,7 @@ class Requester {
177
183
  const response = await fetch(url, {
178
184
  method: "POST",
179
185
  headers: headers,
180
- body: JSON.stringify(bodyData),
186
+ body: bodyData,
181
187
  });
182
188
  if (!response.ok) {
183
189
  throw new LightsparkException(
@@ -214,9 +220,16 @@ class Requester {
214
220
  headers: { [key: string]: string },
215
221
  signingNodeId: string | undefined,
216
222
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any -- LIG-3400 */
217
- ): Promise<BodyData> {
223
+ ): Promise<Uint8Array> {
224
+ let TextEncoderImpl;
225
+ if (typeof TextEncoder === "undefined") {
226
+ TextEncoderImpl = (await import("text-encoding")).TextEncoder;
227
+ } else {
228
+ TextEncoderImpl = TextEncoder;
229
+ }
230
+
218
231
  if (!signingNodeId) {
219
- return queryPayload;
232
+ return new TextEncoderImpl().encode(JSON.stringify(queryPayload));
220
233
  }
221
234
 
222
235
  const query = queryPayload.query;
@@ -241,12 +254,6 @@ class Requester {
241
254
  );
242
255
  }
243
256
 
244
- let TextEncoderImpl;
245
- if (typeof TextEncoder === "undefined") {
246
- TextEncoderImpl = (await import("text-encoding")).TextEncoder;
247
- } else {
248
- TextEncoderImpl = TextEncoder;
249
- }
250
257
  const encodedPayload = new TextEncoderImpl().encode(
251
258
  JSON.stringify(payload),
252
259
  );
@@ -258,8 +265,22 @@ class Requester {
258
265
  v: "1",
259
266
  signature: encodedSignedPayload,
260
267
  });
261
- return payload;
268
+ return encodedPayload;
269
+ }
270
+ }
271
+
272
+ async function compress(data: Uint8Array): Promise<Uint8Array> {
273
+ const stream = new Blob([data]).stream();
274
+ const compressedStream = stream.pipeThrough(new CompressionStream("deflate"));
275
+ const reader = compressedStream.getReader();
276
+ const chunks = [];
277
+ let done, value;
278
+ while (!done) {
279
+ ({ done, value } = await reader.read()); // eslint-disable-line @typescript-eslint/no-unsafe-assignment
280
+ chunks.push(value);
262
281
  }
282
+ const blob = new Blob(chunks);
283
+ return new Uint8Array(await blob.arrayBuffer());
263
284
  }
264
285
 
265
286
  export default Requester;
@@ -7,75 +7,35 @@ import { isNumber, round } from "./numbers.js";
7
7
 
8
8
  export const defaultCurrencyCode = "USD";
9
9
 
10
- /**
11
- * This enum identifies the unit of currency associated with a CurrencyAmount.
12
- * *
13
- */
14
- export enum CurrencyUnit {
15
- /**
16
- * This is an enum value that represents values that could be added in the
17
- * future. Clients should support unknown values as more of them could be
18
- * added without notice.
19
- */
20
- FUTURE_VALUE = "FUTURE_VALUE",
21
- /**
22
- * Bitcoin is the cryptocurrency native to the Bitcoin network.
23
- * It is used as the native medium for value transfer for the Lightning
24
- * Network. *
25
- */
26
- BITCOIN = "BITCOIN",
27
- /**
28
- * 0.00000001 (10e-8) Bitcoin or one hundred millionth of a Bitcoin.
29
- * This is the unit most commonly used in Lightning transactions.
30
- * *
31
- */
32
- SATOSHI = "SATOSHI",
33
- /**
34
- * 0.001 Satoshi, or 10e-11 Bitcoin. We recommend using the Satoshi unit
35
- * instead when possible. *
36
- */
37
- MILLISATOSHI = "MILLISATOSHI",
38
- /** United States Dollar. **/
39
- USD = "USD",
40
- /**
41
- * 0.000000001 (10e-9) Bitcoin or a billionth of a Bitcoin.
42
- * We recommend using the Satoshi unit instead when possible.
43
- * *
44
- */
45
- NANOBITCOIN = "NANOBITCOIN",
46
- /**
47
- * 0.000001 (10e-6) Bitcoin or a millionth of a Bitcoin.
48
- * We recommend using the Satoshi unit instead when possible.
49
- * *
50
- */
51
- MICROBITCOIN = "MICROBITCOIN",
52
- /**
53
- * 0.001 (10e-3) Bitcoin or a thousandth of a Bitcoin.
54
- * We recommend using the Satoshi unit instead when possible.
55
- * *
56
- */
57
- MILLIBITCOIN = "MILLIBITCOIN",
58
- }
59
-
60
- /** This object represents the value and unit for an amount of currency. **/
61
- export type CurrencyAmountType = {
62
- /** The original numeric value for this CurrencyAmount. **/
10
+ /* This const identifies the unit of currency associated with a CurrencyAmount as created by the SDK
11
+ * writer and used in JS SDKs. The schema version uses camel case for the keys so we convert
12
+ * arguments of that type to this format for use in the functions below. */
13
+ export const CurrencyUnit = {
14
+ FUTURE_VALUE: "FUTURE_VALUE",
15
+ BITCOIN: "BITCOIN",
16
+ SATOSHI: "SATOSHI",
17
+ MILLISATOSHI: "MILLISATOSHI",
18
+ USD: "USD",
19
+ NANOBITCOIN: "NANOBITCOIN",
20
+ MICROBITCOIN: "MICROBITCOIN",
21
+ MILLIBITCOIN: "MILLIBITCOIN",
22
+
23
+ Bitcoin: "BITCOIN",
24
+ Microbitcoin: "MICROBITCOIN",
25
+ Millibitcoin: "MILLIBITCOIN",
26
+ Millisatoshi: "MILLISATOSHI",
27
+ Nanobitcoin: "NANOBITCOIN",
28
+ Satoshi: "SATOSHI",
29
+ Usd: "USD",
30
+ } as const;
31
+
32
+ export type CurrencyUnitType = (typeof CurrencyUnit)[keyof typeof CurrencyUnit];
33
+
34
+ export type SDKCurrencyAmountType = {
63
35
  originalValue: number;
64
- /** The original unit of currency for this CurrencyAmount. **/
65
- originalUnit: CurrencyUnit;
66
- /** The unit of user's preferred currency. **/
67
- preferredCurrencyUnit: CurrencyUnit;
68
- /**
69
- * The rounded numeric value for this CurrencyAmount in the very base level
70
- * of user's preferred currency. For example, for USD, the value will be in
71
- * cents.
72
- **/
36
+ originalUnit: CurrencyUnitType;
37
+ preferredCurrencyUnit: CurrencyUnitType;
73
38
  preferredCurrencyValueRounded: number;
74
- /**
75
- * The approximate float value for this CurrencyAmount in the very base level
76
- * of user's preferred currency. For example, for USD, the value will be in
77
- * cents.
78
- **/
79
39
  preferredCurrencyValueApprox: number;
80
40
  };
81
41
 
@@ -163,8 +123,8 @@ const CONVERSION_MAP = {
163
123
  };
164
124
 
165
125
  export function convertCurrencyAmountValue(
166
- fromUnit: CurrencyUnit,
167
- toUnit: CurrencyUnit,
126
+ fromUnit: CurrencyUnitType,
127
+ toUnit: CurrencyUnitType,
168
128
  amount: number,
169
129
  centsPerBtc = 1,
170
130
  ): number {
@@ -191,9 +151,9 @@ export function convertCurrencyAmountValue(
191
151
  }
192
152
 
193
153
  export const convertCurrencyAmount = (
194
- from: CurrencyAmountType,
195
- toUnit: CurrencyUnit,
196
- ): CurrencyAmountType => {
154
+ from: SDKCurrencyAmountType,
155
+ toUnit: CurrencyUnitType,
156
+ ): SDKCurrencyAmountType => {
197
157
  const value = convertCurrencyAmountValue(
198
158
  from.originalUnit,
199
159
  toUnit,
@@ -240,21 +200,17 @@ export type CurrencyMap = {
240
200
  };
241
201
 
242
202
  export type CurrencyAmountObj = {
243
- /*
244
- * Technically the generated graphql schema has value as `any` but it's
245
- * always a number.
246
- * We are intentionally widening the type here to allow for more forgiving
247
- * input:
248
- */
203
+ /* Technically the generated graphql schema has value as `any` but it's always a number.
204
+ * We are intentionally widening the type here to allow for more forgiving input: */
249
205
  value?: number | string | null;
250
206
  /* assume satoshi if not provided */
251
- unit?: CurrencyUnit;
252
- __typename?: "CurrencyAmount";
207
+ unit?: CurrencyUnitType;
208
+ __typename?: "CurrencyAmount" | undefined;
253
209
  };
254
210
 
255
211
  export type CurrencyAmountArg =
256
212
  | CurrencyAmountObj
257
- | CurrencyAmountType
213
+ | SDKCurrencyAmountType
258
214
  | undefined
259
215
  | null;
260
216
 
@@ -264,10 +220,13 @@ export function isCurrencyAmountObj(arg: unknown): arg is CurrencyAmountObj {
264
220
  );
265
221
  }
266
222
 
267
- export function isCurrencyAmount(arg: unknown): arg is CurrencyAmountType {
223
+ export function isSDKCurrencyAmount(
224
+ arg: unknown,
225
+ ): arg is SDKCurrencyAmountType {
268
226
  return (
269
227
  typeof arg === "object" &&
270
228
  arg !== null &&
229
+ /* We can expect all SDK CurrencyAmount types to always have these exact properties: */
271
230
  "originalValue" in arg &&
272
231
  "originalUnit" in arg &&
273
232
  "preferredCurrencyUnit" in arg &&
@@ -275,7 +234,6 @@ export function isCurrencyAmount(arg: unknown): arg is CurrencyAmountType {
275
234
  "preferredCurrencyValueApprox" in arg
276
235
  );
277
236
  }
278
-
279
237
  function asNumber(value: string | number | null | undefined) {
280
238
  if (typeof value === "string") {
281
239
  return Number(value);
@@ -287,12 +245,12 @@ function getCurrencyAmount(currencyAmountArg: CurrencyAmountArg) {
287
245
  let value = 0;
288
246
  let unit = undefined;
289
247
 
290
- if (isCurrencyAmountObj(currencyAmountArg)) {
291
- value = asNumber(currencyAmountArg.value);
292
- unit = currencyAmountArg.unit;
293
- } else if (isCurrencyAmount(currencyAmountArg)) {
248
+ if (isSDKCurrencyAmount(currencyAmountArg)) {
294
249
  value = currencyAmountArg.originalValue;
295
250
  unit = currencyAmountArg.originalUnit;
251
+ } else if (isCurrencyAmountObj(currencyAmountArg)) {
252
+ value = asNumber(currencyAmountArg.value);
253
+ unit = currencyAmountArg.unit;
296
254
  }
297
255
 
298
256
  return {
@@ -410,7 +368,7 @@ export const isCurrencyMap = (
410
368
  typeof currencyMap.type === "string" &&
411
369
  currencyMap.type === "CurrencyMap";
412
370
 
413
- export const abbrCurrencyUnit = (unit: CurrencyUnit) => {
371
+ export const abbrCurrencyUnit = (unit: CurrencyUnitType) => {
414
372
  switch (unit) {
415
373
  case CurrencyUnit.BITCOIN:
416
374
  return "BTC";
@@ -424,35 +382,54 @@ export const abbrCurrencyUnit = (unit: CurrencyUnit) => {
424
382
  return "Unsupported CurrencyUnit";
425
383
  };
426
384
 
385
+ const defaultOptions = {
386
+ /* undefined indicates to use default precision for unit defined below */
387
+ precision: undefined,
388
+ compact: false,
389
+ showBtcSymbol: false,
390
+ };
391
+
392
+ type FormatCurrencyStrOptions = {
393
+ /* undefined indicates to use default precision for unit defined below */
394
+ precision?: number | "full" | undefined;
395
+ compact?: boolean | undefined;
396
+ showBtcSymbol?: boolean | undefined;
397
+ };
398
+
427
399
  export function formatCurrencyStr(
428
400
  amount: CurrencyAmountArg,
429
- maxFractionDigits?: number,
430
- compact?: boolean,
431
- showBtcSymbol = false,
432
- options: Intl.NumberFormatOptions = {},
401
+ options?: FormatCurrencyStrOptions,
433
402
  ) {
403
+ const { precision, compact, showBtcSymbol } = {
404
+ ...defaultOptions,
405
+ ...options,
406
+ };
407
+
434
408
  const currencyAmount = getCurrencyAmount(amount);
435
409
  let { value: num } = currencyAmount;
436
410
  const { unit } = currencyAmount;
437
411
 
438
- /**
439
- * Currencies should always be represented in the smallest unit, e.g.
440
- * cents for USD:
441
- */
412
+ /* Currencies should always be represented in the smallest unit, e.g. cents for USD: */
442
413
  if (unit === CurrencyUnit.USD) {
443
414
  num = num / 100;
444
415
  }
445
416
 
446
- function getDefaultMaxFractionDigits(defaultDigits: number) {
447
- return typeof maxFractionDigits === "undefined"
448
- ? compact
449
- ? 1
450
- : defaultDigits
451
- : maxFractionDigits;
417
+ function getDefaultMaxFractionDigits(
418
+ defaultDigits: number,
419
+ fullPrecisionDigits: number,
420
+ ) {
421
+ let digits = defaultDigits;
422
+ if (precision === "full") {
423
+ digits = fullPrecisionDigits;
424
+ } else if (typeof precision === "number") {
425
+ digits = precision;
426
+ } else if (compact) {
427
+ digits = 1;
428
+ }
429
+ return digits;
452
430
  }
453
431
 
454
- // Symbol handled by toLocaleString for USD.
455
- // These rely on the LightsparkIcons font
432
+ /* Symbol handled by toLocaleString for USD. These rely on the LightsparkIcons font: */
456
433
  const symbol = !showBtcSymbol
457
434
  ? ""
458
435
  : unit === CurrencyUnit.BITCOIN
@@ -465,35 +442,41 @@ export function formatCurrencyStr(
465
442
 
466
443
  switch (unit) {
467
444
  case CurrencyUnit.BITCOIN:
445
+ /* In most cases product prefers 4 precision digtis for BTC. In a few places
446
+ full precision (8 digits) are preferred, e.g. for a transaction details page: */
468
447
  return `${symbol}${num.toLocaleString(currentLocale, {
469
448
  notation: compact ? ("compact" as const) : undefined,
470
- maximumFractionDigits: getDefaultMaxFractionDigits(4),
471
- ...options,
449
+ maximumFractionDigits: getDefaultMaxFractionDigits(4, 8),
472
450
  })}`;
473
- case CurrencyUnit.MILLISATOSHI:
474
451
  case CurrencyUnit.SATOSHI:
452
+ /* In most cases product prefers hiding sub sat precision (msats). In a few
453
+ places full precision (3 digits) are preferred, e.g. for Lightning fees
454
+ paid on a transaction details page: */
455
+ return `${symbol}${num.toLocaleString(currentLocale, {
456
+ notation: compact ? ("compact" as const) : undefined,
457
+ maximumFractionDigits: getDefaultMaxFractionDigits(0, 3),
458
+ })}`;
459
+ case CurrencyUnit.MILLISATOSHI:
475
460
  case CurrencyUnit.MICROBITCOIN:
476
461
  case CurrencyUnit.MILLIBITCOIN:
477
462
  case CurrencyUnit.NANOBITCOIN:
478
463
  default:
479
464
  return `${symbol}${num.toLocaleString(currentLocale, {
480
465
  notation: compact ? ("compact" as const) : undefined,
481
- maximumFractionDigits: getDefaultMaxFractionDigits(0),
482
- ...options,
466
+ maximumFractionDigits: getDefaultMaxFractionDigits(0, 0),
483
467
  })}`;
484
468
  case CurrencyUnit.USD:
485
469
  return num.toLocaleString(currentLocale, {
486
470
  style: "currency",
487
471
  currency: defaultCurrencyCode,
488
472
  notation: compact ? ("compact" as const) : undefined,
489
- maximumFractionDigits: getDefaultMaxFractionDigits(2),
490
- ...options,
473
+ maximumFractionDigits: getDefaultMaxFractionDigits(2, 2),
491
474
  });
492
475
  }
493
476
  }
494
477
 
495
478
  export function separateCurrencyStrParts(currencyStr: string) {
496
- // split the currency string into the symbol and the amount
479
+ /* split the currency string into the symbol and the amount */
497
480
  const symbol = currencyStr.replace(/[0-9\s\u00a0.,]/g, "");
498
481
  const amount = currencyStr.replace(/[^\d.,-]/g, "");
499
482
  return { symbol, amount };
@@ -509,8 +492,7 @@ export function localeToCurrencySymbol(locale: string) {
509
492
  maximumFractionDigits: 0,
510
493
  }).format(0);
511
494
 
512
- // Remove numeric and non-breaking space characters to extract the currency
513
- // symbol
495
+ /* Remove numeric and non-breaking space characters to extract the currency symbol */
514
496
  const { symbol } = separateCurrencyStrParts(formatted);
515
497
  return symbol;
516
498
  }
@@ -39,3 +39,9 @@ export function notNullUndefined<TValue>(
39
39
  ): value is TValue {
40
40
  return value !== null && value !== undefined;
41
41
  }
42
+
43
+ /* Make specific properties on object optional: */
44
+ export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
45
+
46
+ /* Opposite of Partial - make all keys required with NonNullable values */
47
+ export type Complete<T> = { [P in keyof T]-?: NonNullable<T[P]> };