@peable.to/sdk 0.4.0 → 0.4.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.
@@ -1,4 +1,13 @@
1
1
  "use strict";
2
+ /**
3
+ * Read-only display values. Never use these estimates to authorize a payment.
4
+ *
5
+ * This entry is imported by every Oxy app's startup bundle (through
6
+ * `@oxy.so/services`), and Metro does not tree-shake: it must import nothing.
7
+ * `@fairco.in/core` exposes only its root barrel, which drags in the whole
8
+ * wallet stack, so the Explorer origin is restated here and a test pins it to
9
+ * the core constant.
10
+ */
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.DISPLAY_CURRENCIES = void 0;
4
13
  exports.isDisplayCurrency = isDisplayCurrency;
@@ -6,8 +15,9 @@ exports.displayRatesAreFresh = displayRatesAreFresh;
6
15
  exports.readDisplayRates = readDisplayRates;
7
16
  exports.formatDisplayUnits = formatDisplayUnits;
8
17
  exports.convertDisplayAmount = convertDisplayAmount;
9
- /** Read-only display values. Never use these estimates to authorize a payment. */
10
- const core_1 = require("@fairco.in/core");
18
+ /** Same value as `EXPLORER_BASE_URL` in `@fairco.in/core`. */
19
+ const DISPLAY_RATES_ORIGIN = 'https://explorer.fairco.in';
20
+ const DISPLAY_RATES_TIMEOUT_MS = 8000;
11
21
  exports.DISPLAY_CURRENCIES = ['USD', 'EUR', 'FAIR'];
12
22
  function isDisplayCurrency(value) {
13
23
  return exports.DISPLAY_CURRENCIES.some((currency) => currency === value);
@@ -38,19 +48,45 @@ function displayRatesAreFresh(rates, now = Date.now()) {
38
48
  expires > now &&
39
49
  expires <= observed + 120000);
40
50
  }
51
+ /**
52
+ * A signal that aborts after `ms` or when `parent` does. React Native's
53
+ * AbortSignal (Hermes) lacks `AbortSignal.timeout` and `AbortSignal.any`, which
54
+ * made every native read fail, so this uses only a controller and a timer.
55
+ */
56
+ function timeoutSignal(ms, parent) {
57
+ const controller = new AbortController();
58
+ const onParentAbort = () => controller.abort(parent?.reason);
59
+ const timer = setTimeout(() => controller.abort(new Error('Exchange rate request timed out')), ms);
60
+ if (parent?.aborted)
61
+ onParentAbort();
62
+ else
63
+ parent?.addEventListener('abort', onParentAbort);
64
+ return {
65
+ signal: controller.signal,
66
+ dispose: () => {
67
+ clearTimeout(timer);
68
+ parent?.removeEventListener('abort', onParentAbort);
69
+ },
70
+ };
71
+ }
41
72
  /** Uses the owning FairCoin source; no cached fallback after expiry or failure. */
42
73
  async function readDisplayRates(options = {}) {
43
- const source = `${core_1.EXPLORER_BASE_URL}/api/price`;
44
- const response = await (options.fetch ?? fetch)(source, {
45
- signal: options.signal
46
- ? AbortSignal.any([options.signal, AbortSignal.timeout(8000)])
47
- : AbortSignal.timeout(8000),
48
- redirect: 'error',
49
- headers: { Accept: 'application/json' },
50
- });
51
- if (!response.ok)
52
- throw new Error('Exchange rate unavailable');
53
- const body = await response.json();
74
+ const source = `${DISPLAY_RATES_ORIGIN}/api/price`;
75
+ const request = timeoutSignal(DISPLAY_RATES_TIMEOUT_MS, options.signal);
76
+ let body;
77
+ try {
78
+ const response = await (options.fetch ?? fetch)(source, {
79
+ signal: request.signal,
80
+ redirect: 'error',
81
+ headers: { Accept: 'application/json' },
82
+ });
83
+ if (!response.ok)
84
+ throw new Error('Exchange rate unavailable');
85
+ body = await response.json();
86
+ }
87
+ finally {
88
+ request.dispose();
89
+ }
54
90
  if (!body || typeof body !== 'object' || !('price' in body) || !('updatedAt' in body))
55
91
  throw new Error('Exchange rate unavailable');
56
92
  const price = body.price;
@@ -1,5 +1,15 @@
1
- /** Read-only display values. Never use these estimates to authorize a payment. */
2
- import { EXPLORER_BASE_URL } from '@fairco.in/core';
1
+ /**
2
+ * Read-only display values. Never use these estimates to authorize a payment.
3
+ *
4
+ * This entry is imported by every Oxy app's startup bundle (through
5
+ * `@oxy.so/services`), and Metro does not tree-shake: it must import nothing.
6
+ * `@fairco.in/core` exposes only its root barrel, which drags in the whole
7
+ * wallet stack, so the Explorer origin is restated here and a test pins it to
8
+ * the core constant.
9
+ */
10
+ /** Same value as `EXPLORER_BASE_URL` in `@fairco.in/core`. */
11
+ const DISPLAY_RATES_ORIGIN = 'https://explorer.fairco.in';
12
+ const DISPLAY_RATES_TIMEOUT_MS = 8000;
3
13
  export const DISPLAY_CURRENCIES = ['USD', 'EUR', 'FAIR'];
4
14
  export function isDisplayCurrency(value) {
5
15
  return DISPLAY_CURRENCIES.some((currency) => currency === value);
@@ -30,19 +40,45 @@ export function displayRatesAreFresh(rates, now = Date.now()) {
30
40
  expires > now &&
31
41
  expires <= observed + 120000);
32
42
  }
43
+ /**
44
+ * A signal that aborts after `ms` or when `parent` does. React Native's
45
+ * AbortSignal (Hermes) lacks `AbortSignal.timeout` and `AbortSignal.any`, which
46
+ * made every native read fail, so this uses only a controller and a timer.
47
+ */
48
+ function timeoutSignal(ms, parent) {
49
+ const controller = new AbortController();
50
+ const onParentAbort = () => controller.abort(parent?.reason);
51
+ const timer = setTimeout(() => controller.abort(new Error('Exchange rate request timed out')), ms);
52
+ if (parent?.aborted)
53
+ onParentAbort();
54
+ else
55
+ parent?.addEventListener('abort', onParentAbort);
56
+ return {
57
+ signal: controller.signal,
58
+ dispose: () => {
59
+ clearTimeout(timer);
60
+ parent?.removeEventListener('abort', onParentAbort);
61
+ },
62
+ };
63
+ }
33
64
  /** Uses the owning FairCoin source; no cached fallback after expiry or failure. */
34
65
  export async function readDisplayRates(options = {}) {
35
- const source = `${EXPLORER_BASE_URL}/api/price`;
36
- const response = await (options.fetch ?? fetch)(source, {
37
- signal: options.signal
38
- ? AbortSignal.any([options.signal, AbortSignal.timeout(8000)])
39
- : AbortSignal.timeout(8000),
40
- redirect: 'error',
41
- headers: { Accept: 'application/json' },
42
- });
43
- if (!response.ok)
44
- throw new Error('Exchange rate unavailable');
45
- const body = await response.json();
66
+ const source = `${DISPLAY_RATES_ORIGIN}/api/price`;
67
+ const request = timeoutSignal(DISPLAY_RATES_TIMEOUT_MS, options.signal);
68
+ let body;
69
+ try {
70
+ const response = await (options.fetch ?? fetch)(source, {
71
+ signal: request.signal,
72
+ redirect: 'error',
73
+ headers: { Accept: 'application/json' },
74
+ });
75
+ if (!response.ok)
76
+ throw new Error('Exchange rate unavailable');
77
+ body = await response.json();
78
+ }
79
+ finally {
80
+ request.dispose();
81
+ }
46
82
  if (!body || typeof body !== 'object' || !('price' in body) || !('updatedAt' in body))
47
83
  throw new Error('Exchange rate unavailable');
48
84
  const price = body.price;
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Read-only display values. Never use these estimates to authorize a payment.
3
+ *
4
+ * This entry is imported by every Oxy app's startup bundle (through
5
+ * `@oxy.so/services`), and Metro does not tree-shake: it must import nothing.
6
+ * `@fairco.in/core` exposes only its root barrel, which drags in the whole
7
+ * wallet stack, so the Explorer origin is restated here and a test pins it to
8
+ * the core constant.
9
+ */
1
10
  export declare const DISPLAY_CURRENCIES: readonly ["USD", "EUR", "FAIR"];
2
11
  export type DisplayCurrency = (typeof DISPLAY_CURRENCIES)[number];
3
12
  export interface DisplayRates {
@@ -31,7 +31,13 @@ export interface RegisterMerchantParams {
31
31
  webhookSecret?: string;
32
32
  requiredConfirmations?: number;
33
33
  displayName?: string;
34
- /** A bare Oxy file id, never a URL. */
34
+ /**
35
+ * A bare Oxy file id, never a URL. It must name an active, public or unlisted
36
+ * file owned by your application, by the account that owns it, or by the
37
+ * user your token acts for — anything else is refused with a 422 (a 503 when
38
+ * Oxy cannot be asked). If the file is later deleted or taken down, Peable
39
+ * clears it.
40
+ */
35
41
  avatarFileId?: string;
36
42
  description?: string;
37
43
  }
@@ -46,6 +52,7 @@ export interface UpdateMerchantParams {
46
52
  webhookSecret?: string | null;
47
53
  requiredConfirmations?: number;
48
54
  displayName?: string | null;
55
+ /** Same rule as {@link RegisterMerchantParams.avatarFileId}; `null` removes it. */
49
56
  avatarFileId?: string | null;
50
57
  description?: string | null;
51
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peable.to/sdk",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Peable SDK — a Stripe-ergonomics TypeScript client over the Peable Gateway REST + realtime contract. Server entry (`@peable.to/sdk`) mints Oxy service tokens from an ApplicationCredential and exposes paymentIntents/paymentLinks/checkout.sessions/webhooks; browser entry (`@peable.to/sdk/checkout`) is the anonymous payer-side client core.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -93,10 +93,10 @@
93
93
  "@peable.to/shared-types": "0.5.0",
94
94
  "qrcode-generator": "^2.0.4",
95
95
  "socket.io-client": "^4.8.3",
96
- "@peable.to/contracts": "0.1.0",
97
- "@fairco.in/core": "0.5.0"
96
+ "@peable.to/contracts": "0.1.0"
98
97
  },
99
98
  "devDependencies": {
99
+ "@fairco.in/core": "0.5.0",
100
100
  "typescript": "~5.9.2",
101
101
  "@types/node": "^20.0.0",
102
102
  "@types/bun": "latest"