@openreceive/node 0.4.2 → 0.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.
@@ -177,6 +177,7 @@ import {
177
177
  compact,
178
178
  formatSpendCapabilityRefusedMessage,
179
179
  formatSpendCapabilityWarningMessage,
180
+ OPENRECEIVE_NWC_NOTIFICATION_TYPES,
180
181
  OpenReceiveError as OpenReceiveError2,
181
182
  parseNwcUri
182
183
  } from "@openreceive/core";
@@ -186,6 +187,8 @@ import {
186
187
  OPENRECEIVE_MAX_AMOUNT_MSATS,
187
188
  OPENRECEIVE_MIN_AMOUNT_MSATS,
188
189
  OPENRECEIVE_NWC_METADATA_MAX_BYTES,
190
+ OPENRECEIVE_NWC_REQUIRED_RECEIVE_METHODS,
191
+ OPENRECEIVE_NWC_SPEND_METHODS,
189
192
  nonEmptyString,
190
193
  recordOrEmpty
191
194
  } from "@openreceive/core";
@@ -194,13 +197,8 @@ import {
194
197
  var HEX_64 = /^[0-9a-fA-F]{64}$/;
195
198
 
196
199
  // src/nwc/normalize.ts
197
- var REQUIRED_RECEIVE_METHODS = ["make_invoice", "list_transactions"];
198
- var SPEND_METHODS = [
199
- "pay_invoice",
200
- "multi_pay_invoice",
201
- "pay_keysend",
202
- "multi_pay_keysend"
203
- ];
200
+ var REQUIRED_RECEIVE_METHODS = OPENRECEIVE_NWC_REQUIRED_RECEIVE_METHODS;
201
+ var SPEND_METHODS = OPENRECEIVE_NWC_SPEND_METHODS;
204
202
  function summarizeWalletCapabilities(connection, rawInfo, rawServiceInfo) {
205
203
  const unwrappedInfo = unwrapNwcResult(rawInfo);
206
204
  const info = recordOrEmpty(unwrappedInfo);
@@ -541,6 +539,7 @@ function delay(milliseconds) {
541
539
  }
542
540
 
543
541
  // src/alby-nwc.ts
542
+ var NOTIFICATION_TYPES = new Set(OPENRECEIVE_NWC_NOTIFICATION_TYPES);
544
543
  var AlbyNwcReceiveClient = class {
545
544
  /**
546
545
  * Safe, loggable view of the connection (wallet pubkey, relays, lud16, and
@@ -829,7 +828,7 @@ var AlbyNwcReceiveClient = class {
829
828
  "debug",
830
829
  "nwc.notifications.subscribe.requested",
831
830
  "Subscribing to NWC-02 payment_received notifications.",
832
- { notification_types: ["payment_received"] }
831
+ { notification_types: OPENRECEIVE_NWC_NOTIFICATION_TYPES }
833
832
  );
834
833
  let subscription;
835
834
  try {
@@ -837,7 +836,7 @@ var AlbyNwcReceiveClient = class {
837
836
  client,
838
837
  (rawNotification) => {
839
838
  const notification = normalizeNwcNotification(rawNotification);
840
- if (notification.type !== "payment_received") return;
839
+ if (!NOTIFICATION_TYPES.has(notification.type)) return;
841
840
  this.#log(
842
841
  "debug",
843
842
  "nwc.notifications.received",
@@ -852,7 +851,7 @@ var AlbyNwcReceiveClient = class {
852
851
  } catch {
853
852
  }
854
853
  },
855
- ["payment_received"]
854
+ [...OPENRECEIVE_NWC_NOTIFICATION_TYPES]
856
855
  );
857
856
  } catch (error) {
858
857
  const normalized = normalizeNwcWalletError(error);
@@ -941,7 +940,7 @@ function createNwcReceiveClient(options) {
941
940
  return new AlbyNwcReceiveClient(options);
942
941
  }
943
942
 
944
- // src/swap/assets.ts
943
+ // src/generated/swap-tables.ts
945
944
  var OPENRECEIVE_SWAP_PAY_IN_ASSETS = [
946
945
  "SOL_SOL",
947
946
  "USDT_TRON",
@@ -951,7 +950,7 @@ var OPENRECEIVE_SWAP_PAY_IN_ASSETS = [
951
950
  "USDT_ETH",
952
951
  "USDC_ETH"
953
952
  ];
954
- var ASSET_INFO = {
953
+ var OPENRECEIVE_SWAP_ASSET_INFO = {
955
954
  SOL_SOL: {
956
955
  pay_in_asset: "SOL_SOL",
957
956
  label: "SOL",
@@ -1002,6 +1001,37 @@ var ASSET_INFO = {
1002
1001
  network: "ETH"
1003
1002
  }
1004
1003
  };
1004
+ var OPENRECEIVE_SWAP_PROVIDER_STATES = [
1005
+ "creating_provider_order",
1006
+ "awaiting_deposit",
1007
+ "confirming",
1008
+ "exchanging",
1009
+ "paying_invoice",
1010
+ "completed",
1011
+ "expired",
1012
+ "refund_required",
1013
+ "refund_pending",
1014
+ "refunded",
1015
+ "attention",
1016
+ "failed"
1017
+ ];
1018
+ var OPENRECEIVE_SWAP_STATE_TABLE = {
1019
+ creating_provider_order: { phase: "preparing", terminal: false },
1020
+ awaiting_deposit: { phase: "awaiting_deposit", terminal: false },
1021
+ confirming: { phase: "processing", terminal: false },
1022
+ exchanging: { phase: "processing", terminal: false },
1023
+ paying_invoice: { phase: "processing", terminal: false },
1024
+ completed: { phase: "settling", terminal: false },
1025
+ expired: { phase: "terminal", terminal: true },
1026
+ refund_required: { phase: "refund", terminal: false },
1027
+ refund_pending: { phase: "refund", terminal: false },
1028
+ refunded: { phase: "terminal", terminal: true },
1029
+ attention: { phase: "attention", terminal: true },
1030
+ failed: { phase: "terminal", terminal: true }
1031
+ };
1032
+
1033
+ // src/swap/assets.ts
1034
+ var ASSET_INFO = OPENRECEIVE_SWAP_ASSET_INFO;
1005
1035
  function isSwapPayInAsset(value) {
1006
1036
  return typeof value === "string" && OPENRECEIVE_SWAP_PAY_IN_ASSETS.includes(value);
1007
1037
  }
@@ -1408,6 +1438,124 @@ function readFixedFloatCurrencies(data) {
1408
1438
 
1409
1439
  // src/swap/fixedfloat-orders.ts
1410
1440
  import { recordOrEmpty as recordOrEmpty5 } from "@openreceive/core";
1441
+
1442
+ // src/generated/swap-state-table.ts
1443
+ var OPENRECEIVE_SWAP_STATUS_ROWS = [
1444
+ {
1445
+ status: "DONE",
1446
+ refund_tx_present: true,
1447
+ choice: "any",
1448
+ state: "refunded",
1449
+ refund_reason_from_emergency: false
1450
+ },
1451
+ {
1452
+ status: "FINISHED",
1453
+ refund_tx_present: true,
1454
+ choice: "any",
1455
+ state: "refunded",
1456
+ refund_reason_from_emergency: false
1457
+ },
1458
+ {
1459
+ status: "NEW",
1460
+ refund_tx_present: "any",
1461
+ choice: "any",
1462
+ state: "awaiting_deposit",
1463
+ refund_reason_from_emergency: false
1464
+ },
1465
+ {
1466
+ status: "PENDING",
1467
+ refund_tx_present: "any",
1468
+ choice: "any",
1469
+ state: "confirming",
1470
+ refund_reason_from_emergency: false
1471
+ },
1472
+ {
1473
+ status: "EXCHANGE",
1474
+ refund_tx_present: "any",
1475
+ choice: "any",
1476
+ state: "exchanging",
1477
+ refund_reason_from_emergency: false
1478
+ },
1479
+ {
1480
+ status: "WITHDRAW",
1481
+ refund_tx_present: "any",
1482
+ choice: "any",
1483
+ state: "paying_invoice",
1484
+ refund_reason_from_emergency: false
1485
+ },
1486
+ {
1487
+ status: "DONE",
1488
+ refund_tx_present: "any",
1489
+ choice: "any",
1490
+ state: "completed",
1491
+ refund_reason_from_emergency: false
1492
+ },
1493
+ {
1494
+ status: "EXPIRED",
1495
+ refund_tx_present: "any",
1496
+ choice: "any",
1497
+ state: "expired",
1498
+ refund_reason_from_emergency: false
1499
+ },
1500
+ {
1501
+ status: "EMERGENCY",
1502
+ refund_tx_present: true,
1503
+ choice: "REFUND",
1504
+ state: "refunded",
1505
+ refund_reason_from_emergency: true
1506
+ },
1507
+ {
1508
+ status: "EMERGENCY",
1509
+ refund_tx_present: "any",
1510
+ choice: "REFUND",
1511
+ state: "refund_pending",
1512
+ refund_reason_from_emergency: true
1513
+ },
1514
+ {
1515
+ status: "EMERGENCY",
1516
+ refund_tx_present: "any",
1517
+ choice: "EXCHANGE",
1518
+ state: "attention",
1519
+ attention_reason: "provider_reported_emergency",
1520
+ refund_reason_from_emergency: false
1521
+ },
1522
+ {
1523
+ status: "EMERGENCY",
1524
+ refund_tx_present: "any",
1525
+ choice: "any",
1526
+ state: "refund_required",
1527
+ refund_reason_from_emergency: true
1528
+ },
1529
+ {
1530
+ status: "*",
1531
+ status_contains: "FAIL",
1532
+ refund_tx_present: "any",
1533
+ choice: "any",
1534
+ state: "failed",
1535
+ refund_reason_from_emergency: false
1536
+ },
1537
+ {
1538
+ status: "*",
1539
+ refund_tx_present: "any",
1540
+ choice: "any",
1541
+ state: "attention",
1542
+ attention_reason: "provider_status_unrecognized",
1543
+ refund_reason_from_emergency: false
1544
+ }
1545
+ ];
1546
+ var OPENRECEIVE_SWAP_EMERGENCY_STATUS_ALIASES = {
1547
+ OVER: "MORE",
1548
+ OVERPAID: "MORE"
1549
+ };
1550
+ var OPENRECEIVE_SWAP_REFUND_REASON_ROWS = [
1551
+ { all_of: ["LESS", "EXPIRED"], refund_reason: "underpaid_and_late" },
1552
+ { all_of: ["MORE", "EXPIRED"], refund_reason: "overpaid_and_late" },
1553
+ { all_of: ["LESS"], refund_reason: "underpaid" },
1554
+ { all_of: ["MORE"], refund_reason: "overpaid" },
1555
+ { all_of: ["EXPIRED"], refund_reason: "late_deposit" }
1556
+ ];
1557
+
1558
+ // src/swap/fixedfloat-orders.ts
1411
1559
  function normalizeFixedFloatOrder(data, input) {
1412
1560
  const fields = extractFixedFloatOrderFields(recordOrEmpty5(data), input);
1413
1561
  const { attention, attention_reason } = fields.status;
@@ -1492,58 +1640,28 @@ function extractFixedFloatOrderFields(record, input) {
1492
1640
  }
1493
1641
  function normalizeFixedFloatStatus(status, emergency, refundTxId) {
1494
1642
  const normalized = status.toUpperCase();
1495
- if (refundTxId !== void 0 && (normalized === "DONE" || normalized === "FINISHED")) {
1496
- return { state: "refunded" };
1497
- }
1498
- if (normalized === "NEW") return { state: "awaiting_deposit" };
1499
- if (normalized === "PENDING") return { state: "confirming" };
1500
- if (normalized === "EXCHANGE") return { state: "exchanging" };
1501
- if (normalized === "WITHDRAW") return { state: "paying_invoice" };
1502
- if (normalized === "DONE") return { state: "completed" };
1503
- if (normalized === "EXPIRED") return { state: "expired" };
1504
- if (normalized === "EMERGENCY") {
1505
- const choice = optionalStringField(emergency, "choice")?.toUpperCase();
1506
- const emergencyStatuses = optionalStringArrayField(emergency, "status").map(
1507
- (item) => item.toUpperCase()
1508
- );
1509
- const refundReason = refundReasonFromEmergencyStatuses(emergencyStatuses);
1510
- if (choice === "REFUND" && refundTxId !== void 0) {
1511
- return {
1512
- state: "refunded",
1513
- ...refundReason === void 0 ? {} : { refund_reason: refundReason }
1514
- };
1515
- }
1516
- if (choice === "REFUND") {
1517
- return {
1518
- state: "refund_pending",
1519
- ...refundReason === void 0 ? {} : { refund_reason: refundReason }
1520
- };
1521
- }
1522
- if (choice === "EXCHANGE") {
1523
- return {
1524
- state: "attention",
1525
- attention: true,
1526
- attention_reason: "provider_reported_emergency"
1527
- };
1528
- }
1529
- return {
1530
- state: "refund_required",
1531
- ...refundReason === void 0 ? {} : { refund_reason: refundReason }
1532
- };
1533
- }
1534
- if (normalized.includes("FAIL")) return { state: "failed" };
1535
- return { state: "attention", attention: true, attention_reason: "provider_status_unrecognized" };
1643
+ const refundTxPresent = refundTxId !== void 0;
1644
+ const choice = optionalStringField(emergency, "choice")?.toUpperCase();
1645
+ const row = OPENRECEIVE_SWAP_STATUS_ROWS.find(
1646
+ (candidate) => (candidate.status === "*" ? candidate.status_contains === void 0 || normalized.includes(candidate.status_contains) : candidate.status === normalized) && (candidate.refund_tx_present === "any" || candidate.refund_tx_present === refundTxPresent) && (candidate.choice === "any" || (choice === void 0 ? candidate.choice === "absent" : candidate.choice === choice))
1647
+ );
1648
+ const refundReason = row.refund_reason_from_emergency ? refundReasonFromEmergencyStatuses(optionalStringArrayField(emergency, "status")) : void 0;
1649
+ return {
1650
+ state: row.state,
1651
+ ...row.attention_reason === void 0 ? {} : { attention: true, attention_reason: row.attention_reason },
1652
+ ...refundReason === void 0 ? {} : { refund_reason: refundReason }
1653
+ };
1536
1654
  }
1537
1655
  function refundReasonFromEmergencyStatuses(statuses) {
1538
- const less = statuses.includes("LESS");
1539
- const more = statuses.includes("MORE") || statuses.includes("OVER") || statuses.includes("OVERPAID");
1540
- const expired = statuses.includes("EXPIRED");
1541
- if (less && expired) return "underpaid_and_late";
1542
- if (more && expired) return "overpaid_and_late";
1543
- if (less) return "underpaid";
1544
- if (more) return "overpaid";
1545
- if (expired) return "late_deposit";
1546
- return void 0;
1656
+ const present = new Set(
1657
+ statuses.map((item) => {
1658
+ const upper = item.toUpperCase();
1659
+ return OPENRECEIVE_SWAP_EMERGENCY_STATUS_ALIASES[upper] ?? upper;
1660
+ })
1661
+ );
1662
+ return OPENRECEIVE_SWAP_REFUND_REASON_ROWS.find(
1663
+ (row) => row.all_of.every((item) => present.has(item))
1664
+ )?.refund_reason;
1547
1665
  }
1548
1666
  function isRefundPathState(state) {
1549
1667
  return state === "refund_required" || state === "refund_pending" || state === "refunded";
@@ -2282,6 +2400,16 @@ function createNwcEndpointLogger(options) {
2282
2400
  if (options.onEvent === void 0 && options.logger === void 0) return void 0;
2283
2401
  return (entry) => emitEvent(options, entry);
2284
2402
  }
2403
+ function summarizeReconcilePass(input) {
2404
+ const decided = [
2405
+ [input.settledCount, "settled"],
2406
+ [input.pendingCount, "pending"],
2407
+ [input.notFoundCount, "not found"]
2408
+ ].filter(([count]) => count > 0).map(([count, label]) => `${count} ${label}`);
2409
+ const summary = decided.length === 0 ? "0 decided" : decided.join(", ");
2410
+ const attempts = `${input.attemptCount} ${input.attemptCount === 1 ? "attempt" : "attempts"}`;
2411
+ return input.resultCount === input.attemptCount ? summary : `${summary} of ${attempts}`;
2412
+ }
2285
2413
  function sanitizeEvent(entry) {
2286
2414
  const clean = {};
2287
2415
  for (const [key, value] of Object.entries(entry)) {
@@ -2408,6 +2536,8 @@ export {
2408
2536
  HEX_64,
2409
2537
  createNwcReceiveClient,
2410
2538
  OPENRECEIVE_SWAP_PAY_IN_ASSETS,
2539
+ OPENRECEIVE_SWAP_PROVIDER_STATES,
2540
+ OPENRECEIVE_SWAP_STATE_TABLE,
2411
2541
  isSwapPayInAsset,
2412
2542
  listSwapAssetInfo,
2413
2543
  TransientSwapCache,
@@ -2418,6 +2548,7 @@ export {
2418
2548
  createLscSwapProvidersFromEnvironment,
2419
2549
  emitLog,
2420
2550
  createNwcEndpointLogger,
2551
+ summarizeReconcilePass,
2421
2552
  sanitizeEvent,
2422
2553
  redactSecrets,
2423
2554
  summarizeSwapProviderApiRequest,
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  createNwcReceiveClient,
3
3
  readLscConnectionsFromEnvironment,
4
4
  redactSecrets
5
- } from "./chunk-ZYS6QECW.js";
5
+ } from "./chunk-65UPROSE.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { existsSync } from "fs";
package/dist/index.d.ts CHANGED
@@ -148,7 +148,18 @@ declare class AlbyNwcReceiveClient implements ReceiveNwcClient {
148
148
  declare function createNwcReceiveClient(options: AlbyNwcReceiveClientOptions): AlbyNwcReceiveClient;
149
149
 
150
150
  declare const OPENRECEIVE_SWAP_PAY_IN_ASSETS: readonly ["SOL_SOL", "USDT_TRON", "USDT_SOL", "USDC_SOL", "ETH_ETH", "USDT_ETH", "USDC_ETH"];
151
- type SwapPayInAsset = (typeof OPENRECEIVE_SWAP_PAY_IN_ASSETS)[number];
151
+ type GeneratedSwapPayInAsset = (typeof OPENRECEIVE_SWAP_PAY_IN_ASSETS)[number];
152
+ declare const OPENRECEIVE_SWAP_PROVIDER_STATES: readonly ["creating_provider_order", "awaiting_deposit", "confirming", "exchanging", "paying_invoice", "completed", "expired", "refund_required", "refund_pending", "refunded", "attention", "failed"];
153
+ type GeneratedSwapProviderState = (typeof OPENRECEIVE_SWAP_PROVIDER_STATES)[number];
154
+ type GeneratedSwapPhase = "preparing" | "awaiting_deposit" | "processing" | "settling" | "terminal" | "refund" | "attention";
155
+ declare const OPENRECEIVE_SWAP_ATTENTION_REASONS: readonly ["provider_reported_emergency", "provider_status_unrecognized", "provider_completed_without_wallet_settlement"];
156
+ type GeneratedSwapAttentionReason = (typeof OPENRECEIVE_SWAP_ATTENTION_REASONS)[number];
157
+ declare const OPENRECEIVE_SWAP_REFUND_REASONS: readonly ["underpaid", "overpaid", "late_deposit", "underpaid_and_late", "overpaid_and_late"];
158
+ type GeneratedSwapRefundReason = (typeof OPENRECEIVE_SWAP_REFUND_REASONS)[number];
159
+ declare const OPENRECEIVE_SWAP_AVAILABILITY_REASONS: readonly ["provider_unconfigured", "amount_too_small", "amount_too_large", "pair_temporarily_unavailable", "region_unsupported", "provider_rate_limited", "provider_unreachable"];
160
+ type GeneratedSwapAvailabilityReason = (typeof OPENRECEIVE_SWAP_AVAILABILITY_REASONS)[number];
161
+
162
+ type SwapPayInAsset = GeneratedSwapPayInAsset;
152
163
 
153
164
  interface SwapCacheResolveOptions<T> {
154
165
  readonly refreshSeconds: number;
@@ -169,15 +180,16 @@ declare class TransientSwapCache {
169
180
  resolve<T>(key: string, options: SwapCacheResolveOptions<T>): Promise<T>;
170
181
  }
171
182
 
172
- type SwapProviderState = "creating_provider_order" | "awaiting_deposit" | "confirming" | "exchanging" | "paying_invoice" | "completed" | "expired" | "refund_required" | "refund_pending" | "refunded" | "attention" | "failed";
173
- type SwapAvailabilityReason = "provider_unconfigured" | "amount_too_small" | "amount_too_large" | "pair_temporarily_unavailable" | "region_unsupported" | "provider_rate_limited" | "provider_unreachable";
183
+ type SwapProviderState = GeneratedSwapProviderState;
184
+ type SwapAvailabilityReason = GeneratedSwapAvailabilityReason;
174
185
  /**
175
186
  * Why a swap attempt entered the `attention` state and needs human/support review.
176
187
  * Every code path that sets `attention: true` records one of these so a dashboard or
177
- * runbook can branch on the cause instead of a bare boolean. See the "Attention"
178
- * section of docs/internal/swap-operations.md for the per-reason operator runbook.
188
+ * runbook can branch on the cause instead of a bare boolean. The FixedFloat status
189
+ * mapping that produces them is pinned by spec/test-vectors/swap-state.json;
190
+ * `provider_completed_without_wallet_settlement` is reserved and not emitted yet.
179
191
  */
180
- type SwapAttentionReason = "provider_completed_without_wallet_settlement" | "provider_order_creation_stale" | "provider_order_creation_failed" | "provider_order_creation_needs_reconcile" | "provider_reported_emergency" | "provider_status_unrecognized" | "provider_order_expires_after_shadow_invoice";
192
+ type SwapAttentionReason = GeneratedSwapAttentionReason;
181
193
  /**
182
194
  * Why a swap attempt entered the refund path (`refund_required` → `refunded`).
183
195
  * Mapped from FixedFloat `emergency.status` (LESS / MORE / EXPIRED). An overpay
@@ -185,7 +197,7 @@ type SwapAttentionReason = "provider_completed_without_wallet_settlement" | "pro
185
197
  * fixed-amount bolt11, so there is nothing to exchange the surplus into and
186
198
  * `choice=EXCHANGE` is not a path this client takes.
187
199
  */
188
- type SwapRefundReason = "underpaid" | "overpaid" | "late_deposit" | "underpaid_and_late" | "overpaid_and_late";
200
+ type SwapRefundReason = GeneratedSwapRefundReason;
189
201
  interface SwapQuote {
190
202
  readonly pay_amount?: string;
191
203
  readonly minimum_pay_amount?: string;
@@ -407,8 +419,12 @@ declare function fixedFloatCompatibleSwapProvider(options: FixedFloatCompatibleS
407
419
  * - `refund` — a refund is required, staged, or in flight.
408
420
  * - `attention` — needs operator/support review (funds may be stuck).
409
421
  * - `terminal` — the attempt is over and will not change (expired/refunded/failed).
422
+ *
423
+ * The state → phase/terminal table itself is kernel vocabulary
424
+ * (spec/data/kernel-tables.json) generated into every engine; this module adds the
425
+ * payer-facing copy, which only the JS checkout renders.
410
426
  */
411
- type SwapPhase = "preparing" | "awaiting_deposit" | "processing" | "settling" | "refund" | "attention" | "terminal";
427
+ type SwapPhase = GeneratedSwapPhase;
412
428
  interface SwapStateInfo {
413
429
  /** The provider state this describes. */
414
430
  readonly state: SwapProviderState;
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  HEX_64,
3
3
  OPENRECEIVE_SWAP_PAY_IN_ASSETS,
4
+ OPENRECEIVE_SWAP_PROVIDER_STATES,
5
+ OPENRECEIVE_SWAP_STATE_TABLE,
4
6
  ReceiveCheckoutValidationError,
5
7
  TransientSwapCache,
6
8
  WalletPreflightError,
@@ -14,9 +16,10 @@ import {
14
16
  isSwapPayInAsset,
15
17
  listSwapAssetInfo,
16
18
  sanitizeEvent,
19
+ summarizeReconcilePass,
17
20
  summarizeSwapProviderApiRequest,
18
21
  summarizeSwapProviderApiResponse
19
- } from "./chunk-ZYS6QECW.js";
22
+ } from "./chunk-65UPROSE.js";
20
23
 
21
24
  // src/index.ts
22
25
  import { OpenReceiveError as OpenReceiveError2 } from "@openreceive/core";
@@ -54,6 +57,7 @@ import path from "path";
54
57
  import { compact as compact2 } from "@openreceive/core";
55
58
 
56
59
  // src/console-logger.ts
60
+ import { inspect } from "util";
57
61
  import { compact } from "@openreceive/core";
58
62
 
59
63
  // src/log-level.ts
@@ -104,9 +108,9 @@ function createConsoleLogger(options = {}) {
104
108
  level,
105
109
  prefix,
106
110
  event,
107
- message
108
- }),
109
- compact(fields)
111
+ message,
112
+ fields: compact(fields)
113
+ })
110
114
  );
111
115
  };
112
116
  }
@@ -125,14 +129,26 @@ function createAppConsoleLogger(options) {
125
129
  level,
126
130
  prefix: options.prefix,
127
131
  event,
128
- message
129
- }),
130
- compact(fields)
132
+ message,
133
+ fields: compact(fields)
134
+ })
131
135
  );
132
136
  };
133
137
  }
134
138
  function formatConsoleLogLine(input) {
135
- return `[${input.at}] ${input.level.toUpperCase()} [${input.prefix}] ${input.event}: ${input.message}`;
139
+ const head = `[${input.at}] ${input.level.toUpperCase()} [${input.prefix}] ${input.event}: ${input.message}`;
140
+ const fields = Object.entries(input.fields).map(
141
+ ([key, value]) => `${key}=${formatFieldValue(value)}`
142
+ );
143
+ return fields.length === 0 ? head : `${head} ${fields.join(" ")}`;
144
+ }
145
+ function formatFieldValue(value) {
146
+ if (typeof value === "string")
147
+ return /^[\w.\-:/@+]*$/.test(value) ? value : JSON.stringify(value);
148
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
149
+ return String(value);
150
+ }
151
+ return inspect(value, { breakLength: Number.POSITIVE_INFINITY, compact: true, depth: 4 });
136
152
  }
137
153
 
138
154
  // src/service/file-logger.ts
@@ -764,92 +780,65 @@ function toSafeInteger(value, field) {
764
780
  import { isValidSwapAddressForPayInAsset } from "@openreceive/core";
765
781
 
766
782
  // src/swap/state.ts
767
- var OPENRECEIVE_SWAP_STATES = {
783
+ var SWAP_STATE_COPY = {
768
784
  creating_provider_order: {
769
- state: "creating_provider_order",
770
785
  label: "Preparing payment address",
771
- detail: "Creating a payment address.",
772
- phase: "preparing",
773
- terminal: false
786
+ detail: "Creating a payment address."
774
787
  },
775
788
  awaiting_deposit: {
776
- state: "awaiting_deposit",
777
789
  label: "Waiting for your payment",
778
- detail: "Send exactly the amount shown below.",
779
- phase: "awaiting_deposit",
780
- terminal: false
790
+ detail: "Send exactly the amount shown below."
781
791
  },
782
792
  confirming: {
783
- state: "confirming",
784
793
  label: "Confirming payment",
785
- detail: "Your payment was detected and is confirming on-chain.",
786
- phase: "processing",
787
- terminal: false
794
+ detail: "Your payment was detected and is confirming on-chain."
788
795
  },
789
796
  exchanging: {
790
- state: "exchanging",
791
797
  label: "Converting payment",
792
- detail: "Your payment is confirmed and being converted. This usually finishes within a minute.",
793
- phase: "processing",
794
- terminal: false
798
+ detail: "Your payment is confirmed and being converted. This usually finishes within a minute."
795
799
  },
796
800
  paying_invoice: {
797
- state: "paying_invoice",
798
801
  label: "Finalizing checkout",
799
- detail: "The provider is sending the Lightning payment. This usually takes a few seconds.",
800
- phase: "processing",
801
- terminal: false
802
+ detail: "The provider is sending the Lightning payment. This usually takes a few seconds."
802
803
  },
803
804
  completed: {
804
- state: "completed",
805
805
  label: "Finalizing checkout",
806
- detail: "The provider is sending the Lightning payment. This usually takes a few seconds.",
807
- phase: "settling",
808
- terminal: false
806
+ detail: "The provider is sending the Lightning payment. This usually takes a few seconds."
809
807
  },
810
808
  expired: {
811
- state: "expired",
812
809
  label: "Expired",
813
- detail: "No payment was received before the payment window closed.",
814
- phase: "terminal",
815
- terminal: true
810
+ detail: "No payment was received before the payment window closed."
816
811
  },
817
812
  refund_required: {
818
- state: "refund_required",
819
813
  label: "Refund needed",
820
- detail: "Enter an address you control to request a refund.",
821
- phase: "refund",
822
- terminal: false
814
+ detail: "Enter an address you control to request a refund."
823
815
  },
824
816
  refund_pending: {
825
- state: "refund_pending",
826
817
  label: "Refund pending",
827
- detail: "Your refund request has been sent.",
828
- phase: "refund",
829
- terminal: false
818
+ detail: "Your refund request has been sent."
830
819
  },
831
820
  refunded: {
832
- state: "refunded",
833
821
  label: "Refunded",
834
- detail: "The provider reports the refund was sent.",
835
- phase: "terminal",
836
- terminal: true
822
+ detail: "The provider reports the refund was sent."
837
823
  },
838
824
  attention: {
839
- state: "attention",
840
825
  label: "Needs attention",
841
- detail: "This payment needs support review.",
842
- phase: "attention",
843
- terminal: true
826
+ detail: "This payment needs support review."
844
827
  },
845
828
  failed: {
846
- state: "failed",
847
829
  label: "Failed",
848
- detail: "This payment address can no longer be used.",
849
- phase: "terminal",
850
- terminal: true
830
+ detail: "This payment address can no longer be used."
851
831
  }
852
832
  };
833
+ function swapStateInfo(state) {
834
+ const { phase, terminal } = OPENRECEIVE_SWAP_STATE_TABLE[state];
835
+ return { state, ...SWAP_STATE_COPY[state], phase, terminal };
836
+ }
837
+ var OPENRECEIVE_SWAP_STATES = Object.freeze(
838
+ Object.fromEntries(
839
+ OPENRECEIVE_SWAP_PROVIDER_STATES.map((state) => [state, swapStateInfo(state)])
840
+ )
841
+ );
853
842
 
854
843
  // src/swap/weight-budget.ts
855
844
  import { compact as compact3 } from "@openreceive/core";
@@ -1379,24 +1368,35 @@ async function createOpenReceive(supplied = {}) {
1379
1368
  maxPages: input.maxPages,
1380
1369
  onWalk: (walk) => walks.push(walk)
1381
1370
  });
1371
+ const settledCount = results.filter((result) => result.status === "settled").length;
1372
+ const pendingCount = results.filter((result) => result.status === "pending").length;
1373
+ const notFoundCount = results.filter((result) => result.status === "not_found").length;
1382
1374
  emitLog(
1383
1375
  nodeOptions,
1384
1376
  "info",
1385
1377
  "payment.reconcile.completed",
1386
- "NWC payment reconciliation completed.",
1378
+ summarizeReconcilePass({
1379
+ attemptCount: input.attempts.length,
1380
+ resultCount: results.length,
1381
+ settledCount,
1382
+ pendingCount,
1383
+ notFoundCount
1384
+ }),
1387
1385
  {
1386
+ attempt_count: input.attempts.length,
1387
+ settled_count: settledCount,
1388
+ pending_count: pendingCount,
1389
+ // Zero-valued extras stay off the line; the message already says
1390
+ // what was decided.
1391
+ ...notFoundCount === 0 ? {} : { not_found_count: notFoundCount },
1388
1392
  // Attempts scanned vs hashes decided: results collapse duplicate
1389
1393
  // hashes and omit any the wallet walk could not reach, so a gap
1390
1394
  // between these two is how a truncated scan shows up in the log.
1391
- attempt_count: input.attempts.length,
1392
- result_count: results.length,
1393
- settled_count: results.filter((result) => result.status === "settled").length,
1394
- pending_count: results.filter((result) => result.status === "pending").length,
1395
- not_found_count: results.filter((result) => result.status === "not_found").length,
1395
+ ...results.length === input.attempts.length ? {} : { result_count: results.length },
1396
1396
  // All pending attempts share these walks: one creation-time window,
1397
1397
  // never one wallet call per invoice.
1398
- list_transactions_walks: walks.length,
1399
- ...walks[0] === void 0 ? {} : { window_from: walks[0].from, window_until: walks[0].until }
1398
+ walks: walks.length,
1399
+ ...walks[0] === void 0 ? {} : { window: `${walks[0].from}..${walks[0].until}` }
1400
1400
  }
1401
1401
  );
1402
1402
  return results;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openreceive/node",
3
- "version": "0.4.2",
3
+ "version": "0.4.5",
4
4
  "description": "The OpenReceive Node service: receive-only NWC wallet client, checkout and swap service, pricing, and the openreceive CLI.",
5
5
  "keywords": [
6
6
  "bitcoin",
@@ -17,7 +17,7 @@
17
17
  "types": "./dist/index.d.ts",
18
18
  "dependencies": {
19
19
  "@getalby/sdk": "^8.0.3",
20
- "@openreceive/core": "0.4.2"
20
+ "@openreceive/core": "0.4.5"
21
21
  },
22
22
  "bin": {
23
23
  "openreceive": "./bin/openreceive.mjs"