@arkade-os/boltz-swap 0.3.7 → 0.3.8

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/dist/index.cjs CHANGED
@@ -5036,6 +5036,28 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
5036
5036
  };
5037
5037
 
5038
5038
  // src/serviceWorker/arkade-swaps-runtime.ts
5039
+ var import_sdk10 = require("@arkade-os/sdk");
5040
+ function isMessageBusNotInitializedError(error) {
5041
+ return error instanceof Error && error.message.includes(import_sdk10.MESSAGE_BUS_NOT_INITIALIZED);
5042
+ }
5043
+ var DEDUPABLE_REQUEST_TYPES = /* @__PURE__ */ new Set([
5044
+ "GET_FEES",
5045
+ "GET_LIMITS",
5046
+ "GET_SWAP_STATUS",
5047
+ "GET_PENDING_SUBMARINE_SWAPS",
5048
+ "GET_PENDING_REVERSE_SWAPS",
5049
+ "GET_PENDING_CHAIN_SWAPS",
5050
+ "GET_SWAP_HISTORY",
5051
+ "QUOTE_SWAP",
5052
+ "SM-GET_PENDING_SWAPS",
5053
+ "SM-HAS_SWAP",
5054
+ "SM-IS_PROCESSING",
5055
+ "SM-GET_STATS"
5056
+ ]);
5057
+ function getRequestDedupKey(request) {
5058
+ const { id, tag, ...rest } = request;
5059
+ return JSON.stringify(rest);
5060
+ }
5039
5061
  var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5040
5062
  constructor(messageTag, serviceWorker, swapRepository, withSwapManager) {
5041
5063
  this.messageTag = messageTag;
@@ -5050,6 +5072,10 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5050
5072
  actionExecutedListeners = /* @__PURE__ */ new Set();
5051
5073
  wsConnectedListeners = /* @__PURE__ */ new Set();
5052
5074
  wsDisconnectedListeners = /* @__PURE__ */ new Set();
5075
+ initPayload = null;
5076
+ reinitPromise = null;
5077
+ pingPromise = null;
5078
+ inflightRequests = /* @__PURE__ */ new Map();
5053
5079
  static async create(config) {
5054
5080
  const messageTag = config.messageTag ?? DEFAULT_MESSAGE_TAG;
5055
5081
  const swapRepository = config.swapRepository ?? new IndexedDbSwapRepository();
@@ -5059,18 +5085,20 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5059
5085
  swapRepository,
5060
5086
  Boolean(config.swapManager)
5061
5087
  );
5088
+ const initPayload = {
5089
+ network: config.network,
5090
+ arkServerUrl: config.arkServerUrl,
5091
+ swapProvider: { baseUrl: config.swapProvider.getApiUrl() },
5092
+ swapManager: config.swapManager
5093
+ };
5062
5094
  const initMessage = {
5063
5095
  tag: messageTag,
5064
5096
  id: getRandomId(),
5065
5097
  type: "INIT_ARKADE_SWAPS",
5066
- payload: {
5067
- network: config.network,
5068
- arkServerUrl: config.arkServerUrl,
5069
- swapProvider: { baseUrl: config.swapProvider.getApiUrl() },
5070
- swapManager: config.swapManager
5071
- }
5098
+ payload: initPayload
5072
5099
  };
5073
5100
  await svcArkadeSwaps.sendMessage(initMessage);
5101
+ svcArkadeSwaps.initPayload = initPayload;
5074
5102
  return svcArkadeSwaps;
5075
5103
  }
5076
5104
  async startSwapManager() {
@@ -5611,17 +5639,23 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5611
5639
  async [Symbol.asyncDispose]() {
5612
5640
  return this.dispose();
5613
5641
  }
5614
- async sendMessage(request) {
5642
+ sendMessageDirect(request) {
5615
5643
  return new Promise((resolve, reject) => {
5616
- const timeoutMs = 3e4;
5617
- let timeout;
5618
5644
  const cleanup = () => {
5619
- if (timeout) clearTimeout(timeout);
5645
+ clearTimeout(timeoutId);
5620
5646
  navigator.serviceWorker.removeEventListener(
5621
5647
  "message",
5622
5648
  messageHandler
5623
5649
  );
5624
5650
  };
5651
+ const timeoutId = setTimeout(() => {
5652
+ cleanup();
5653
+ reject(
5654
+ new import_sdk10.ServiceWorkerTimeoutError(
5655
+ `Service worker message timed out (${request.type})`
5656
+ )
5657
+ );
5658
+ }, 3e4);
5625
5659
  const messageHandler = (event) => {
5626
5660
  const response = event.data;
5627
5661
  if (!response || response.tag !== this.messageTag || response.id !== request.id) {
@@ -5634,17 +5668,97 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
5634
5668
  resolve(response);
5635
5669
  }
5636
5670
  };
5637
- timeout = setTimeout(() => {
5671
+ navigator.serviceWorker.addEventListener("message", messageHandler);
5672
+ this.serviceWorker.postMessage(request);
5673
+ });
5674
+ }
5675
+ async sendMessage(request) {
5676
+ if (!DEDUPABLE_REQUEST_TYPES.has(request.type)) {
5677
+ return this.sendMessageWithRetry(request);
5678
+ }
5679
+ const key = getRequestDedupKey(request);
5680
+ const existing = this.inflightRequests.get(key);
5681
+ if (existing) return existing;
5682
+ const promise = this.sendMessageWithRetry(request).finally(() => {
5683
+ this.inflightRequests.delete(key);
5684
+ });
5685
+ this.inflightRequests.set(key, promise);
5686
+ return promise;
5687
+ }
5688
+ pingServiceWorker() {
5689
+ if (this.pingPromise) return this.pingPromise;
5690
+ this.pingPromise = new Promise((resolve, reject) => {
5691
+ const pingId = getRandomId();
5692
+ const cleanup = () => {
5693
+ clearTimeout(timeoutId);
5694
+ navigator.serviceWorker.removeEventListener(
5695
+ "message",
5696
+ onMessage
5697
+ );
5698
+ };
5699
+ const timeoutId = setTimeout(() => {
5638
5700
  cleanup();
5639
5701
  reject(
5640
- new Error(
5641
- `Timed out waiting for service worker response: ${request.type}`
5702
+ new import_sdk10.ServiceWorkerTimeoutError(
5703
+ "Service worker ping timed out"
5642
5704
  )
5643
5705
  );
5644
- }, timeoutMs);
5645
- navigator.serviceWorker.addEventListener("message", messageHandler);
5646
- this.serviceWorker.postMessage(request);
5706
+ }, 2e3);
5707
+ const onMessage = (event) => {
5708
+ if (event.data?.id === pingId && event.data?.tag === "PONG") {
5709
+ cleanup();
5710
+ resolve();
5711
+ }
5712
+ };
5713
+ navigator.serviceWorker.addEventListener("message", onMessage);
5714
+ this.serviceWorker.postMessage({
5715
+ id: pingId,
5716
+ tag: "PING"
5717
+ });
5718
+ }).finally(() => {
5719
+ this.pingPromise = null;
5720
+ });
5721
+ return this.pingPromise;
5722
+ }
5723
+ // Send a message, retrying up to 2 times if the service worker was
5724
+ // killed and restarted by the OS (mobile browsers do this aggressively).
5725
+ async sendMessageWithRetry(request) {
5726
+ if (this.initPayload) {
5727
+ try {
5728
+ await this.pingServiceWorker();
5729
+ } catch {
5730
+ await this.reinitialize();
5731
+ }
5732
+ }
5733
+ const maxRetries = 2;
5734
+ for (let attempt = 0; ; attempt++) {
5735
+ try {
5736
+ return await this.sendMessageDirect(request);
5737
+ } catch (error) {
5738
+ if (!isMessageBusNotInitializedError(error) || attempt >= maxRetries) {
5739
+ throw error;
5740
+ }
5741
+ await this.reinitialize();
5742
+ }
5743
+ }
5744
+ }
5745
+ async reinitialize() {
5746
+ if (this.reinitPromise) return this.reinitPromise;
5747
+ this.reinitPromise = (async () => {
5748
+ if (!this.initPayload) {
5749
+ throw new Error("Cannot re-initialize: missing configuration");
5750
+ }
5751
+ const initMessage = {
5752
+ tag: this.messageTag,
5753
+ type: "INIT_ARKADE_SWAPS",
5754
+ id: getRandomId(),
5755
+ payload: this.initPayload
5756
+ };
5757
+ await this.sendMessageDirect(initMessage);
5758
+ })().finally(() => {
5759
+ this.reinitPromise = null;
5647
5760
  });
5761
+ return this.reinitPromise;
5648
5762
  }
5649
5763
  initEventStream() {
5650
5764
  if (this.eventListenerInitialized) return;
package/dist/index.d.cts CHANGED
@@ -562,6 +562,10 @@ declare class ServiceWorkerArkadeSwaps implements IArkadeSwaps {
562
562
  private actionExecutedListeners;
563
563
  private wsConnectedListeners;
564
564
  private wsDisconnectedListeners;
565
+ private initPayload;
566
+ private reinitPromise;
567
+ private pingPromise;
568
+ private inflightRequests;
565
569
  private constructor();
566
570
  static create(config: SvcWrkArkadeSwapsConfig): Promise<ServiceWorkerArkadeSwaps>;
567
571
  startSwapManager(): Promise<void>;
@@ -654,7 +658,11 @@ declare class ServiceWorkerArkadeSwaps implements IArkadeSwaps {
654
658
  refreshSwapsStatus(): Promise<void>;
655
659
  dispose(): Promise<void>;
656
660
  [Symbol.asyncDispose](): Promise<void>;
661
+ private sendMessageDirect;
657
662
  private sendMessage;
663
+ private pingServiceWorker;
664
+ private sendMessageWithRetry;
665
+ private reinitialize;
658
666
  private initEventStream;
659
667
  private handleEventMessage;
660
668
  }
package/dist/index.d.ts CHANGED
@@ -562,6 +562,10 @@ declare class ServiceWorkerArkadeSwaps implements IArkadeSwaps {
562
562
  private actionExecutedListeners;
563
563
  private wsConnectedListeners;
564
564
  private wsDisconnectedListeners;
565
+ private initPayload;
566
+ private reinitPromise;
567
+ private pingPromise;
568
+ private inflightRequests;
565
569
  private constructor();
566
570
  static create(config: SvcWrkArkadeSwapsConfig): Promise<ServiceWorkerArkadeSwaps>;
567
571
  startSwapManager(): Promise<void>;
@@ -654,7 +658,11 @@ declare class ServiceWorkerArkadeSwaps implements IArkadeSwaps {
654
658
  refreshSwapsStatus(): Promise<void>;
655
659
  dispose(): Promise<void>;
656
660
  [Symbol.asyncDispose](): Promise<void>;
661
+ private sendMessageDirect;
657
662
  private sendMessage;
663
+ private pingServiceWorker;
664
+ private sendMessageWithRetry;
665
+ private reinitialize;
658
666
  private initEventStream;
659
667
  private handleEventMessage;
660
668
  }
package/dist/index.js CHANGED
@@ -526,6 +526,31 @@ var ArkadeSwapsMessageHandler = class _ArkadeSwapsMessageHandler {
526
526
  };
527
527
 
528
528
  // src/serviceWorker/arkade-swaps-runtime.ts
529
+ import {
530
+ MESSAGE_BUS_NOT_INITIALIZED,
531
+ ServiceWorkerTimeoutError
532
+ } from "@arkade-os/sdk";
533
+ function isMessageBusNotInitializedError(error) {
534
+ return error instanceof Error && error.message.includes(MESSAGE_BUS_NOT_INITIALIZED);
535
+ }
536
+ var DEDUPABLE_REQUEST_TYPES = /* @__PURE__ */ new Set([
537
+ "GET_FEES",
538
+ "GET_LIMITS",
539
+ "GET_SWAP_STATUS",
540
+ "GET_PENDING_SUBMARINE_SWAPS",
541
+ "GET_PENDING_REVERSE_SWAPS",
542
+ "GET_PENDING_CHAIN_SWAPS",
543
+ "GET_SWAP_HISTORY",
544
+ "QUOTE_SWAP",
545
+ "SM-GET_PENDING_SWAPS",
546
+ "SM-HAS_SWAP",
547
+ "SM-IS_PROCESSING",
548
+ "SM-GET_STATS"
549
+ ]);
550
+ function getRequestDedupKey(request) {
551
+ const { id, tag, ...rest } = request;
552
+ return JSON.stringify(rest);
553
+ }
529
554
  var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
530
555
  constructor(messageTag, serviceWorker, swapRepository, withSwapManager) {
531
556
  this.messageTag = messageTag;
@@ -540,6 +565,10 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
540
565
  actionExecutedListeners = /* @__PURE__ */ new Set();
541
566
  wsConnectedListeners = /* @__PURE__ */ new Set();
542
567
  wsDisconnectedListeners = /* @__PURE__ */ new Set();
568
+ initPayload = null;
569
+ reinitPromise = null;
570
+ pingPromise = null;
571
+ inflightRequests = /* @__PURE__ */ new Map();
543
572
  static async create(config) {
544
573
  const messageTag = config.messageTag ?? DEFAULT_MESSAGE_TAG;
545
574
  const swapRepository = config.swapRepository ?? new IndexedDbSwapRepository();
@@ -549,18 +578,20 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
549
578
  swapRepository,
550
579
  Boolean(config.swapManager)
551
580
  );
581
+ const initPayload = {
582
+ network: config.network,
583
+ arkServerUrl: config.arkServerUrl,
584
+ swapProvider: { baseUrl: config.swapProvider.getApiUrl() },
585
+ swapManager: config.swapManager
586
+ };
552
587
  const initMessage = {
553
588
  tag: messageTag,
554
589
  id: getRandomId(),
555
590
  type: "INIT_ARKADE_SWAPS",
556
- payload: {
557
- network: config.network,
558
- arkServerUrl: config.arkServerUrl,
559
- swapProvider: { baseUrl: config.swapProvider.getApiUrl() },
560
- swapManager: config.swapManager
561
- }
591
+ payload: initPayload
562
592
  };
563
593
  await svcArkadeSwaps.sendMessage(initMessage);
594
+ svcArkadeSwaps.initPayload = initPayload;
564
595
  return svcArkadeSwaps;
565
596
  }
566
597
  async startSwapManager() {
@@ -1101,17 +1132,23 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
1101
1132
  async [Symbol.asyncDispose]() {
1102
1133
  return this.dispose();
1103
1134
  }
1104
- async sendMessage(request) {
1135
+ sendMessageDirect(request) {
1105
1136
  return new Promise((resolve, reject) => {
1106
- const timeoutMs = 3e4;
1107
- let timeout;
1108
1137
  const cleanup = () => {
1109
- if (timeout) clearTimeout(timeout);
1138
+ clearTimeout(timeoutId);
1110
1139
  navigator.serviceWorker.removeEventListener(
1111
1140
  "message",
1112
1141
  messageHandler
1113
1142
  );
1114
1143
  };
1144
+ const timeoutId = setTimeout(() => {
1145
+ cleanup();
1146
+ reject(
1147
+ new ServiceWorkerTimeoutError(
1148
+ `Service worker message timed out (${request.type})`
1149
+ )
1150
+ );
1151
+ }, 3e4);
1115
1152
  const messageHandler = (event) => {
1116
1153
  const response = event.data;
1117
1154
  if (!response || response.tag !== this.messageTag || response.id !== request.id) {
@@ -1124,17 +1161,97 @@ var ServiceWorkerArkadeSwaps = class _ServiceWorkerArkadeSwaps {
1124
1161
  resolve(response);
1125
1162
  }
1126
1163
  };
1127
- timeout = setTimeout(() => {
1164
+ navigator.serviceWorker.addEventListener("message", messageHandler);
1165
+ this.serviceWorker.postMessage(request);
1166
+ });
1167
+ }
1168
+ async sendMessage(request) {
1169
+ if (!DEDUPABLE_REQUEST_TYPES.has(request.type)) {
1170
+ return this.sendMessageWithRetry(request);
1171
+ }
1172
+ const key = getRequestDedupKey(request);
1173
+ const existing = this.inflightRequests.get(key);
1174
+ if (existing) return existing;
1175
+ const promise = this.sendMessageWithRetry(request).finally(() => {
1176
+ this.inflightRequests.delete(key);
1177
+ });
1178
+ this.inflightRequests.set(key, promise);
1179
+ return promise;
1180
+ }
1181
+ pingServiceWorker() {
1182
+ if (this.pingPromise) return this.pingPromise;
1183
+ this.pingPromise = new Promise((resolve, reject) => {
1184
+ const pingId = getRandomId();
1185
+ const cleanup = () => {
1186
+ clearTimeout(timeoutId);
1187
+ navigator.serviceWorker.removeEventListener(
1188
+ "message",
1189
+ onMessage
1190
+ );
1191
+ };
1192
+ const timeoutId = setTimeout(() => {
1128
1193
  cleanup();
1129
1194
  reject(
1130
- new Error(
1131
- `Timed out waiting for service worker response: ${request.type}`
1195
+ new ServiceWorkerTimeoutError(
1196
+ "Service worker ping timed out"
1132
1197
  )
1133
1198
  );
1134
- }, timeoutMs);
1135
- navigator.serviceWorker.addEventListener("message", messageHandler);
1136
- this.serviceWorker.postMessage(request);
1199
+ }, 2e3);
1200
+ const onMessage = (event) => {
1201
+ if (event.data?.id === pingId && event.data?.tag === "PONG") {
1202
+ cleanup();
1203
+ resolve();
1204
+ }
1205
+ };
1206
+ navigator.serviceWorker.addEventListener("message", onMessage);
1207
+ this.serviceWorker.postMessage({
1208
+ id: pingId,
1209
+ tag: "PING"
1210
+ });
1211
+ }).finally(() => {
1212
+ this.pingPromise = null;
1213
+ });
1214
+ return this.pingPromise;
1215
+ }
1216
+ // Send a message, retrying up to 2 times if the service worker was
1217
+ // killed and restarted by the OS (mobile browsers do this aggressively).
1218
+ async sendMessageWithRetry(request) {
1219
+ if (this.initPayload) {
1220
+ try {
1221
+ await this.pingServiceWorker();
1222
+ } catch {
1223
+ await this.reinitialize();
1224
+ }
1225
+ }
1226
+ const maxRetries = 2;
1227
+ for (let attempt = 0; ; attempt++) {
1228
+ try {
1229
+ return await this.sendMessageDirect(request);
1230
+ } catch (error) {
1231
+ if (!isMessageBusNotInitializedError(error) || attempt >= maxRetries) {
1232
+ throw error;
1233
+ }
1234
+ await this.reinitialize();
1235
+ }
1236
+ }
1237
+ }
1238
+ async reinitialize() {
1239
+ if (this.reinitPromise) return this.reinitPromise;
1240
+ this.reinitPromise = (async () => {
1241
+ if (!this.initPayload) {
1242
+ throw new Error("Cannot re-initialize: missing configuration");
1243
+ }
1244
+ const initMessage = {
1245
+ tag: this.messageTag,
1246
+ type: "INIT_ARKADE_SWAPS",
1247
+ id: getRandomId(),
1248
+ payload: this.initPayload
1249
+ };
1250
+ await this.sendMessageDirect(initMessage);
1251
+ })().finally(() => {
1252
+ this.reinitPromise = null;
1137
1253
  });
1254
+ return this.reinitPromise;
1138
1255
  }
1139
1256
  initEventStream() {
1140
1257
  if (this.eventListenerInitialized) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkade-os/boltz-swap",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "type": "module",
5
5
  "description": "A production-ready TypeScript package that brings Boltz submarine-swaps to Arkade.",
6
6
  "main": "./dist/index.js",
@@ -60,7 +60,7 @@
60
60
  "author": "Arkade-OS",
61
61
  "license": "MIT",
62
62
  "dependencies": {
63
- "@arkade-os/sdk": "0.4.9",
63
+ "@arkade-os/sdk": "0.4.10",
64
64
  "@noble/hashes": "2.0.1",
65
65
  "@scure/base": "2.0.0",
66
66
  "@scure/btc-signer": "2.0.1",