@toon-protocol/client-mcp 0.15.0 → 0.17.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.
@@ -30,7 +30,7 @@ import {
30
30
  utf8ToBytes
31
31
  } from "./chunk-VA7XC4FD.js";
32
32
 
33
- // ../../node_modules/.pnpm/@toon-protocol+core@1.6.0_@toon-protocol+connector@3.13.0_typescript@5.9.3/node_modules/@toon-protocol/core/dist/chunk-5WT7ISKC.js
33
+ // ../../node_modules/.pnpm/@toon-protocol+core@2.0.1_typescript@5.9.3/node_modules/@toon-protocol/core/dist/chunk-5WT7ISKC.js
34
34
  import { encode } from "@toon-format/toon";
35
35
  import { decode } from "@toon-format/toon";
36
36
  import { decode as decode2 } from "@toon-format/toon";
@@ -54,8 +54,99 @@ var PeerDiscoveryError = class extends ToonError {
54
54
  this.name = "PeerDiscoveryError";
55
55
  }
56
56
  };
57
+ var ToonEncodeError = class extends ToonError {
58
+ constructor(message, cause) {
59
+ super(message, "TOON_ENCODE_ERROR", cause);
60
+ this.name = "ToonEncodeError";
61
+ }
62
+ };
63
+ function encodeEventToToon(event) {
64
+ try {
65
+ const toonString = encode(event);
66
+ return new TextEncoder().encode(toonString);
67
+ } catch (error) {
68
+ throw new ToonEncodeError(
69
+ `Failed to encode event to TOON: ${error instanceof Error ? error.message : String(error)}`,
70
+ error instanceof Error ? error : void 0
71
+ );
72
+ }
73
+ }
74
+ function isValidHex(value, length) {
75
+ if (typeof value !== "string") return false;
76
+ if (value.length !== length) return false;
77
+ return /^[0-9a-f]+$/i.test(value);
78
+ }
79
+ var ToonDecodeError = class extends ToonError {
80
+ constructor(message, cause) {
81
+ super(message, "TOON_DECODE_ERROR", cause);
82
+ this.name = "ToonDecodeError";
83
+ }
84
+ };
85
+ function validateNostrEvent(obj) {
86
+ if (typeof obj !== "object" || obj === null) {
87
+ throw new ToonDecodeError("Decoded value is not an object");
88
+ }
89
+ const event = obj;
90
+ if (!isValidHex(event["id"], 64)) {
91
+ throw new ToonDecodeError(
92
+ "Invalid event id: must be a 64-character hex string"
93
+ );
94
+ }
95
+ if (!isValidHex(event["pubkey"], 64)) {
96
+ throw new ToonDecodeError(
97
+ "Invalid event pubkey: must be a 64-character hex string"
98
+ );
99
+ }
100
+ if (typeof event["kind"] !== "number" || !Number.isInteger(event["kind"])) {
101
+ throw new ToonDecodeError("Invalid event kind: must be an integer");
102
+ }
103
+ if (typeof event["content"] !== "string") {
104
+ throw new ToonDecodeError("Invalid event content: must be a string");
105
+ }
106
+ const tags = event["tags"];
107
+ if (!Array.isArray(tags)) {
108
+ throw new ToonDecodeError("Invalid event tags: must be an array");
109
+ }
110
+ for (let i = 0; i < tags.length; i++) {
111
+ const tag = tags[i];
112
+ if (!Array.isArray(tag)) {
113
+ throw new ToonDecodeError(`Invalid event tags[${i}]: must be an array`);
114
+ }
115
+ for (let j = 0; j < tag.length; j++) {
116
+ if (typeof tag[j] !== "string") {
117
+ throw new ToonDecodeError(
118
+ `Invalid event tags[${i}][${j}]: must be a string`
119
+ );
120
+ }
121
+ }
122
+ }
123
+ if (typeof event["created_at"] !== "number" || !Number.isInteger(event["created_at"])) {
124
+ throw new ToonDecodeError("Invalid event created_at: must be an integer");
125
+ }
126
+ if (!isValidHex(event["sig"], 128)) {
127
+ throw new ToonDecodeError(
128
+ "Invalid event sig: must be a 128-character hex string"
129
+ );
130
+ }
131
+ }
132
+ function decodeEventFromToon(data) {
133
+ try {
134
+ const toonString = new TextDecoder().decode(data);
135
+ const decoded = decode(toonString);
136
+ validateNostrEvent(decoded);
137
+ return decoded;
138
+ } catch (error) {
139
+ if (error instanceof ToonDecodeError) {
140
+ throw error;
141
+ }
142
+ throw new ToonDecodeError(
143
+ `Failed to decode TOON data: ${error instanceof Error ? error.message : String(error)}`,
144
+ error instanceof Error ? error : void 0
145
+ );
146
+ }
147
+ }
57
148
 
58
- // ../../node_modules/.pnpm/@toon-protocol+core@1.6.0_@toon-protocol+connector@3.13.0_typescript@5.9.3/node_modules/@toon-protocol/core/dist/index.js
149
+ // ../../node_modules/.pnpm/@toon-protocol+core@2.0.1_typescript@5.9.3/node_modules/@toon-protocol/core/dist/index.js
59
150
  import { finalizeEvent } from "nostr-tools/pure";
60
151
  import { finalizeEvent as finalizeEvent2 } from "nostr-tools/pure";
61
152
  import { finalizeEvent as finalizeEvent3 } from "nostr-tools/pure";
@@ -398,6 +489,17 @@ function parseIlpPeerInfo(event) {
398
489
  };
399
490
  }
400
491
  var EXPIRATION_TAG = "expiration";
492
+ function getEventExpiration(event) {
493
+ const tag = event.tags.find((t) => t[0] === EXPIRATION_TAG);
494
+ if (!tag || tag[1] === void 0) return void 0;
495
+ const ts = Number(tag[1]);
496
+ if (!Number.isFinite(ts) || ts < 0) return void 0;
497
+ return ts;
498
+ }
499
+ function isEventExpired(event, nowSeconds = Math.floor(Date.now() / 1e3)) {
500
+ const exp = getEventExpiration(event);
501
+ return exp !== void 0 && exp <= nowSeconds;
502
+ }
401
503
  function buildIlpPeerInfoEvent(info, secretKey, options = {}) {
402
504
  if (info.feePerByte !== void 0) {
403
505
  if (typeof info.feePerByte !== "string" || !/^\d+$/.test(info.feePerByte)) {
@@ -493,7 +595,14 @@ function buildBlobStorageRequest(params, secretKey) {
493
595
  secretKey
494
596
  );
495
597
  }
496
- var genesis_peers_default = [];
598
+ var genesis_peers_default = [
599
+ {
600
+ pubkey: "2813187eb66741f9509de2055161f328a0f04e01e1fc20188610b8dbd0591ea5",
601
+ relayUrl: "wss://relay-ws.devnet.toonprotocol.dev",
602
+ ilpAddress: "g.proxy",
603
+ btpEndpoint: "wss://proxy.devnet.toonprotocol.dev:443"
604
+ }
605
+ ];
497
606
  var PUBKEY_REGEX3 = /^[0-9a-f]{64}$/;
498
607
  var ILP_ADDRESS_REGEX = /^g\.[a-zA-Z0-9.-]+$/;
499
608
  function isValidPubkey2(pubkey) {
@@ -1857,6 +1966,13 @@ function resolveClientNetwork(network) {
1857
1966
  status
1858
1967
  };
1859
1968
  }
1969
+ function buildIlpPrepare(params) {
1970
+ return {
1971
+ destination: params.destination,
1972
+ amount: String(params.amount),
1973
+ data: Buffer.from(params.data).toString("base64")
1974
+ };
1975
+ }
1860
1976
 
1861
1977
  // ../client/dist/chunk-QEMD5EAI.js
1862
1978
  import { verifyEvent as verifyEvent2 } from "nostr-tools/pure";
@@ -1959,9 +2075,9 @@ function anumArr(label, input) {
1959
2075
  function chain(...args) {
1960
2076
  const id = (a) => a;
1961
2077
  const wrap = (a, b) => (c) => a(b(c));
1962
- const encode3 = args.map((x) => x.encode).reduceRight(wrap, id);
1963
- const decode4 = args.map((x) => x.decode).reduce(wrap, id);
1964
- return { encode: encode3, decode: decode4 };
2078
+ const encode2 = args.map((x) => x.encode).reduceRight(wrap, id);
2079
+ const decode3 = args.map((x) => x.decode).reduce(wrap, id);
2080
+ return { encode: encode2, decode: decode3 };
1965
2081
  }
1966
2082
  // @__NO_SIDE_EFFECTS__
1967
2083
  function alphabet(letters) {
@@ -5734,12 +5850,12 @@ function ecdh(Point2, ecdhOpts = {}) {
5734
5850
  function randomSecretKey(seed = randomBytes_(lengths.seed)) {
5735
5851
  return mapHashToField(_abytes2(seed, lengths.seed, "seed"), Fn.ORDER);
5736
5852
  }
5737
- function getPublicKey8(secretKey, isCompressed = true) {
5853
+ function getPublicKey7(secretKey, isCompressed = true) {
5738
5854
  return Point2.BASE.multiply(_normFnElement(Fn, secretKey)).toBytes(isCompressed);
5739
5855
  }
5740
5856
  function keygen(seed) {
5741
5857
  const secretKey = randomSecretKey(seed);
5742
- return { secretKey, publicKey: getPublicKey8(secretKey) };
5858
+ return { secretKey, publicKey: getPublicKey7(secretKey) };
5743
5859
  }
5744
5860
  function isProbPub(item) {
5745
5861
  if (typeof item === "bigint")
@@ -5773,7 +5889,7 @@ function ecdh(Point2, ecdhOpts = {}) {
5773
5889
  return point.precompute(windowSize, false);
5774
5890
  }
5775
5891
  };
5776
- return Object.freeze({ getPublicKey: getPublicKey8, getSharedSecret, keygen, Point: Point2, utils: utils2, lengths });
5892
+ return Object.freeze({ getPublicKey: getPublicKey7, getSharedSecret, keygen, Point: Point2, utils: utils2, lengths });
5777
5893
  }
5778
5894
  function ecdsa(Point2, hash, ecdsaOpts = {}) {
5779
5895
  ahash(hash);
@@ -5788,7 +5904,7 @@ function ecdsa(Point2, hash, ecdsaOpts = {}) {
5788
5904
  const hmac2 = ecdsaOpts.hmac || ((key, ...msgs) => hmac(hash, key, concatBytes(...msgs)));
5789
5905
  const { Fp, Fn } = Point2;
5790
5906
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
5791
- const { keygen, getPublicKey: getPublicKey8, getSharedSecret, utils: utils2, lengths } = ecdh(Point2, ecdsaOpts);
5907
+ const { keygen, getPublicKey: getPublicKey7, getSharedSecret, utils: utils2, lengths } = ecdh(Point2, ecdsaOpts);
5792
5908
  const defaultSigOpts = {
5793
5909
  prehash: false,
5794
5910
  lowS: typeof ecdsaOpts.lowS === "boolean" ? ecdsaOpts.lowS : false,
@@ -6034,7 +6150,7 @@ function ecdsa(Point2, hash, ecdsaOpts = {}) {
6034
6150
  }
6035
6151
  return Object.freeze({
6036
6152
  keygen,
6037
- getPublicKey: getPublicKey8,
6153
+ getPublicKey: getPublicKey7,
6038
6154
  getSharedSecret,
6039
6155
  utils: utils2,
6040
6156
  lengths,
@@ -7330,9 +7446,13 @@ function deserializeIlpPacket(buf) {
7330
7446
  }
7331
7447
  function deserializeIlpFulfill(buf) {
7332
7448
  let offset = 1;
7449
+ if (buf.length < offset + 32) {
7450
+ throw new Error("Buffer underflow reading FULFILL fulfillment");
7451
+ }
7452
+ const fulfillment = buf.slice(offset, offset + 32);
7333
7453
  offset += 32;
7334
7454
  const { value: data } = decodeVarOctetString(buf, offset);
7335
- return { type: ILPPacketType.FULFILL, data };
7455
+ return { type: ILPPacketType.FULFILL, fulfillment, data };
7336
7456
  }
7337
7457
  function deserializeIlpReject(buf) {
7338
7458
  let offset = 1;
@@ -7641,8 +7761,10 @@ var IsomorphicBtpClient = class {
7641
7761
  this.pendingRequests.delete(id);
7642
7762
  if (json["type"] === "FULFILL") {
7643
7763
  const responseData = json["data"] ? this.base64ToUint8Array(json["data"]) : new Uint8Array(0);
7764
+ const fulfillment = json["fulfillment"] ? this.base64ToUint8Array(json["fulfillment"]) : new Uint8Array(32);
7644
7765
  pending.resolve({
7645
7766
  type: ILPPacketType.FULFILL,
7767
+ fulfillment,
7646
7768
  data: responseData
7647
7769
  });
7648
7770
  } else {
@@ -7702,6 +7824,64 @@ var IsomorphicBtpClient = class {
7702
7824
  return this.requestIdCounter;
7703
7825
  }
7704
7826
  };
7827
+ var CONDITION_LENGTH = 32;
7828
+ function mintExecutionCondition() {
7829
+ const preimage = randomBytes(CONDITION_LENGTH);
7830
+ return { preimage, condition: sha2562(preimage) };
7831
+ }
7832
+ function isZeroCondition(condition) {
7833
+ if (condition === void 0) return true;
7834
+ for (const byte of condition) {
7835
+ if (byte !== 0) return false;
7836
+ }
7837
+ return true;
7838
+ }
7839
+ function assertValidCondition(condition) {
7840
+ if (condition.length !== CONDITION_LENGTH) {
7841
+ throw new Error(
7842
+ `executionCondition must be exactly ${CONDITION_LENGTH} bytes, got ${condition.length}`
7843
+ );
7844
+ }
7845
+ }
7846
+ function fulfillmentMatchesCondition(fulfillment, condition) {
7847
+ if (fulfillment === void 0 || fulfillment.length !== CONDITION_LENGTH) {
7848
+ return false;
7849
+ }
7850
+ const digest = sha2562(fulfillment);
7851
+ for (let i = 0; i < CONDITION_LENGTH; i++) {
7852
+ if (digest[i] !== condition[i]) return false;
7853
+ }
7854
+ return true;
7855
+ }
7856
+ var FULFILLMENT_MISMATCH_CODE = "F99";
7857
+ var FULFILLMENT_MISMATCH_MESSAGE = "FULFILL fulfillment does not match execution condition (sha256(fulfillment) != executionCondition) \u2014 packet counted failed";
7858
+ function mapIlpResponse(packet, sentCondition) {
7859
+ if (packet.type === ILPPacketType.FULFILL) {
7860
+ const dataField = packet.data.length > 0 ? { data: toBase64(packet.data) } : {};
7861
+ if (sentCondition === void 0 || isZeroCondition(sentCondition)) {
7862
+ return { accepted: true, ...dataField };
7863
+ }
7864
+ if (!fulfillmentMatchesCondition(packet.fulfillment, sentCondition)) {
7865
+ return {
7866
+ accepted: false,
7867
+ code: FULFILLMENT_MISMATCH_CODE,
7868
+ message: FULFILLMENT_MISMATCH_MESSAGE,
7869
+ ...dataField
7870
+ };
7871
+ }
7872
+ return {
7873
+ accepted: true,
7874
+ fulfillment: toBase64(packet.fulfillment),
7875
+ ...dataField
7876
+ };
7877
+ }
7878
+ return {
7879
+ accepted: false,
7880
+ code: packet.code,
7881
+ message: packet.message,
7882
+ ...packet.data.length > 0 ? { data: toBase64(packet.data) } : {}
7883
+ };
7884
+ }
7705
7885
  function isConnectionError(error) {
7706
7886
  const msg = error.message.toLowerCase();
7707
7887
  return msg.includes("not connected") || msg.includes("connection") || msg.includes("websocket") || msg.includes("econnrefused") || msg.includes("econnreset") || msg.includes("socket hang up") || msg.includes("timeout");
@@ -7756,6 +7936,12 @@ var BtpRuntimeClient = class {
7756
7936
  /**
7757
7937
  * Sends an ILP packet via BTP with auto-reconnect on connection errors.
7758
7938
  * Satisfies IlpClient interface.
7939
+ *
7940
+ * `params` may carry a sender-chosen `executionCondition` and an explicit
7941
+ * `expiresAt` (toon-client#350); omitting both is the legacy zero-condition
7942
+ * path, unchanged. With a non-zero condition the FULFILL preimage is
7943
+ * verified (`sha256(fulfillment) == condition`) and a mismatch is surfaced
7944
+ * as a failed result — see `mapIlpResponse`.
7759
7945
  */
7760
7946
  async sendIlpPacket(params) {
7761
7947
  return withRetry(() => this._sendIlpPacketOnce(params), {
@@ -7771,6 +7957,9 @@ var BtpRuntimeClient = class {
7771
7957
  /**
7772
7958
  * Sends a balance proof claim via BTP protocol data, then sends an ILP packet.
7773
7959
  * Auto-reconnects on connection errors.
7960
+ *
7961
+ * Sender-chosen `executionCondition` / explicit `expiresAt` semantics are
7962
+ * identical to {@link sendIlpPacket}.
7774
7963
  */
7775
7964
  async sendIlpPacketWithClaim(params, claim) {
7776
7965
  return withRetry(() => this._sendIlpPacketWithClaimOnce(params, claim), {
@@ -7813,6 +8002,30 @@ var BtpRuntimeClient = class {
7813
8002
  encodeUtf8(JSON.stringify(claim))
7814
8003
  );
7815
8004
  }
8005
+ /**
8006
+ * Build the ILP PREPARE for a send, applying the sender-chosen condition /
8007
+ * explicit expiry when provided (toon-client#350) and validating that a
8008
+ * non-zero condition is exactly 32 bytes (the OER serializer would
8009
+ * otherwise silently zero-fill it, downgrading the packet to the legacy
8010
+ * unverified class).
8011
+ */
8012
+ buildPrepare(params) {
8013
+ const condition = params.executionCondition;
8014
+ if (condition !== void 0 && !isZeroCondition(condition)) {
8015
+ assertValidCondition(condition);
8016
+ }
8017
+ return {
8018
+ prepare: {
8019
+ type: 12,
8020
+ amount: BigInt(params.amount),
8021
+ destination: params.destination,
8022
+ executionCondition: condition ?? new Uint8Array(32),
8023
+ expiresAt: params.expiresAt ?? new Date(Date.now() + (params.timeout ?? 3e4)),
8024
+ data: fromBase64(params.data)
8025
+ },
8026
+ condition
8027
+ };
8028
+ }
7816
8029
  /**
7817
8030
  * Single-attempt ILP packet send. Reconnects if not connected.
7818
8031
  */
@@ -7820,26 +8033,9 @@ var BtpRuntimeClient = class {
7820
8033
  if (!this._isConnected) {
7821
8034
  await this.reconnect();
7822
8035
  }
7823
- const response = await this.btpClient.sendPacket({
7824
- type: 12,
7825
- amount: BigInt(params.amount),
7826
- destination: params.destination,
7827
- executionCondition: new Uint8Array(32),
7828
- expiresAt: new Date(Date.now() + (params.timeout ?? 3e4)),
7829
- data: fromBase64(params.data)
7830
- });
7831
- if (response.type === ILPPacketType.FULFILL) {
7832
- return {
7833
- accepted: true,
7834
- data: response.data.length > 0 ? toBase64(response.data) : void 0
7835
- };
7836
- }
7837
- return {
7838
- accepted: false,
7839
- code: response.code,
7840
- message: response.message,
7841
- data: response.data.length > 0 ? toBase64(response.data) : void 0
7842
- };
8036
+ const { prepare, condition } = this.buildPrepare(params);
8037
+ const response = await this.btpClient.sendPacket(prepare);
8038
+ return mapIlpResponse(response, condition);
7843
8039
  }
7844
8040
  /**
7845
8041
  * Single-attempt claim + ILP packet send. Reconnects if not connected.
@@ -7858,29 +8054,9 @@ var BtpRuntimeClient = class {
7858
8054
  data: encodeUtf8(JSON.stringify(claim))
7859
8055
  }
7860
8056
  ];
7861
- const response = await this.btpClient.sendPacket(
7862
- {
7863
- type: 12,
7864
- amount: BigInt(params.amount),
7865
- destination: params.destination,
7866
- executionCondition: new Uint8Array(32),
7867
- expiresAt: new Date(Date.now() + (params.timeout ?? 3e4)),
7868
- data: fromBase64(params.data)
7869
- },
7870
- protocolData
7871
- );
7872
- if (response.type === ILPPacketType.FULFILL) {
7873
- return {
7874
- accepted: true,
7875
- data: response.data.length > 0 ? toBase64(response.data) : void 0
7876
- };
7877
- }
7878
- return {
7879
- accepted: false,
7880
- code: response.code,
7881
- message: response.message,
7882
- data: response.data.length > 0 ? toBase64(response.data) : void 0
7883
- };
8057
+ const { prepare, condition } = this.buildPrepare(params);
8058
+ const response = await this.btpClient.sendPacket(prepare, protocolData);
8059
+ return mapIlpResponse(response, condition);
7884
8060
  }
7885
8061
  };
7886
8062
  var ILP_CLAIM_HEADER = "ILP-Payment-Channel-Claim";
@@ -7909,6 +8085,12 @@ var HttpIlpClient = class {
7909
8085
  * Send an ILP PREPARE via `POST /ilp` WITHOUT a claim. The connector accepts
7910
8086
  * this only on free/zero-amount routes; paid writes must use
7911
8087
  * {@link sendIlpPacketWithClaim}. Satisfies the IlpClient interface.
8088
+ *
8089
+ * `params` may carry a sender-chosen `executionCondition` and an explicit
8090
+ * `expiresAt` (toon-client#350); omitting both is the legacy zero-condition
8091
+ * path, unchanged. With a non-zero condition the FULFILL preimage is
8092
+ * verified (`sha256(fulfillment) == condition`) and a mismatch is surfaced
8093
+ * as a failed result — see {@link mapIlpResponse}.
7912
8094
  */
7913
8095
  async sendIlpPacket(params) {
7914
8096
  return withRetry(() => this.postPrepare(params), {
@@ -7923,6 +8105,9 @@ var HttpIlpClient = class {
7923
8105
  * as the `ILP-Payment-Channel-Claim` header. `claim` is the SAME JSON object
7924
8106
  * the BTP path attaches as the `payment-channel-claim` protocolData entry —
7925
8107
  * we base64(JSON.stringify(claim)) it, byte-for-byte identical to BTP.
8108
+ *
8109
+ * Sender-chosen `executionCondition` / explicit `expiresAt` semantics are
8110
+ * identical to {@link sendIlpPacket}.
7926
8111
  */
7927
8112
  async sendIlpPacketWithClaim(params, claim) {
7928
8113
  return withRetry(() => this.postPrepare(params, claim), {
@@ -7975,12 +8160,16 @@ var HttpIlpClient = class {
7975
8160
  */
7976
8161
  async postPrepare(params, claim) {
7977
8162
  const requestTimeout = params.timeout ?? this.timeout;
8163
+ const condition = params.executionCondition;
8164
+ if (condition !== void 0 && !isZeroCondition(condition)) {
8165
+ assertValidCondition(condition);
8166
+ }
7978
8167
  const prepare = serializeIlpPrepare({
7979
8168
  type: ILPPacketType.PREPARE,
7980
8169
  amount: BigInt(params.amount),
7981
8170
  destination: params.destination,
7982
- executionCondition: new Uint8Array(32),
7983
- expiresAt: new Date(Date.now() + requestTimeout),
8171
+ executionCondition: condition ?? new Uint8Array(32),
8172
+ expiresAt: params.expiresAt ?? new Date(Date.now() + requestTimeout),
7984
8173
  data: fromBase64(params.data)
7985
8174
  });
7986
8175
  const headers = {
@@ -8003,7 +8192,7 @@ var HttpIlpClient = class {
8003
8192
  signal: controller.signal
8004
8193
  });
8005
8194
  clearTimeout(timeoutId);
8006
- return await this.mapResponse(response);
8195
+ return await this.mapResponse(response, condition);
8007
8196
  } catch (error) {
8008
8197
  clearTimeout(timeoutId);
8009
8198
  throw this.mapTransportError(error, requestTimeout);
@@ -8013,26 +8202,18 @@ var HttpIlpClient = class {
8013
8202
  * Map a `200 OK` body (OER FULFILL/REJECT) to an IlpSendResult; map a non-2xx
8014
8203
  * to a transport error. Per the wire contract, ILP-level rejects arrive as a
8015
8204
  * 200 + REJECT body — only HTTP non-2xx means a transport-layer failure.
8205
+ *
8206
+ * When `sentCondition` is non-zero the FULFILL preimage is verified against
8207
+ * it; a mismatch yields `accepted: false` (shared `mapIlpResponse` logic,
8208
+ * identical to the BTP path).
8016
8209
  */
8017
- async mapResponse(response) {
8210
+ async mapResponse(response, sentCondition) {
8018
8211
  if (response.ok) {
8019
8212
  const buf = new Uint8Array(await response.arrayBuffer());
8020
8213
  if (buf.length === 0) {
8021
8214
  throw new ConnectorError("Empty 200 body from /ilp (expected OER ILP response)");
8022
8215
  }
8023
- const ilp = deserializeIlpPacket(buf);
8024
- if (ilp.type === ILPPacketType.FULFILL) {
8025
- return {
8026
- accepted: true,
8027
- data: ilp.data.length > 0 ? toBase64(ilp.data) : void 0
8028
- };
8029
- }
8030
- return {
8031
- accepted: false,
8032
- code: ilp.code,
8033
- message: ilp.message,
8034
- data: ilp.data.length > 0 ? toBase64(ilp.data) : void 0
8035
- };
8216
+ return mapIlpResponse(deserializeIlpPacket(buf), sentCondition);
8036
8217
  }
8037
8218
  const body = await response.text().catch(() => "");
8038
8219
  const detail = body ? `: ${body}` : "";
@@ -11266,6 +11447,14 @@ var ToonClient = class {
11266
11447
  * matching `destination`,
11267
11448
  * (c) neither -> throw MISSING_CLAIM.
11268
11449
  *
11450
+ * A caller may supply a sender-chosen 32-byte `executionCondition`
11451
+ * (`C = sha256(P)`, one fresh preimage per packet — toon-client#350,
11452
+ * rolling-swap spec §3 R1/R2) and an explicit `expiresAt` (R7). Both are
11453
+ * set on the wire by either transport (HTTP `POST /ilp` and BTP), and on
11454
+ * FULFILL the transport verifies `sha256(fulfillment) == condition` —
11455
+ * a mismatch comes back as `accepted: false` (code F99), never a silent
11456
+ * accept. Omitting them keeps today's zero-condition legacy packet.
11457
+ *
11269
11458
  * @throws {ToonClientError} INVALID_STATE / NO_ILP_TRANSPORT / MISSING_CLAIM
11270
11459
  */
11271
11460
  async sendSwapPacket(params) {
@@ -11286,7 +11475,9 @@ var ToonClient = class {
11286
11475
  destination: params.destination,
11287
11476
  amount: String(params.amount),
11288
11477
  data: toBase64(params.toonData),
11289
- timeout: params.timeout ?? 3e4
11478
+ timeout: params.timeout ?? 3e4,
11479
+ ...params.executionCondition ? { executionCondition: params.executionCondition } : {},
11480
+ ...params.expiresAt ? { expiresAt: params.expiresAt } : {}
11290
11481
  },
11291
11482
  claimMessage
11292
11483
  );
@@ -12050,591 +12241,39 @@ function arweaveUrls(txId, gateways = ARWEAVE_GATEWAYS) {
12050
12241
  import { readFileSync as readFileSync3 } from "fs";
12051
12242
  import { homedir } from "os";
12052
12243
  import { join as join2, resolve } from "path";
12053
-
12054
- // ../../node_modules/.pnpm/@toon-protocol+core@1.4.2_@toon-protocol+connector@3.13.0_typescript@5.9.3/node_modules/@toon-protocol/core/dist/chunk-5WT7ISKC.js
12055
- import { encode as encode2 } from "@toon-format/toon";
12056
- import { decode as decode3 } from "@toon-format/toon";
12057
- import { decode as decode22 } from "@toon-format/toon";
12058
- var ToonError2 = class extends Error {
12059
- code;
12060
- constructor(message, code, cause) {
12061
- super(message, { cause });
12062
- this.name = "ToonError";
12063
- this.code = code;
12064
- }
12065
- };
12066
- var InvalidEventError2 = class extends ToonError2 {
12067
- constructor(message, cause) {
12068
- super(message, "INVALID_EVENT", cause);
12069
- this.name = "InvalidEventError";
12070
- }
12071
- };
12072
- var ToonEncodeError2 = class extends ToonError2 {
12073
- constructor(message, cause) {
12074
- super(message, "TOON_ENCODE_ERROR", cause);
12075
- this.name = "ToonEncodeError";
12076
- }
12077
- };
12078
- function encodeEventToToon2(event) {
12079
- try {
12080
- const toonString = encode2(event);
12081
- return new TextEncoder().encode(toonString);
12082
- } catch (error) {
12083
- throw new ToonEncodeError2(
12084
- `Failed to encode event to TOON: ${error instanceof Error ? error.message : String(error)}`,
12085
- error instanceof Error ? error : void 0
12086
- );
12087
- }
12244
+ var DEFAULT_KEYSTORE_PASSWORD = "toon-client-default";
12245
+ function configDir() {
12246
+ return process.env["TOON_CLIENT_HOME"] ?? join2(homedir(), ".toon-client");
12088
12247
  }
12089
- function isValidHex(value, length) {
12090
- if (typeof value !== "string") return false;
12091
- if (value.length !== length) return false;
12092
- return /^[0-9a-f]+$/i.test(value);
12248
+ function defaultConfigPath() {
12249
+ return join2(configDir(), "config.json");
12093
12250
  }
12094
- var ToonDecodeError2 = class extends ToonError2 {
12095
- constructor(message, cause) {
12096
- super(message, "TOON_DECODE_ERROR", cause);
12097
- this.name = "ToonDecodeError";
12098
- }
12099
- };
12100
- function validateNostrEvent(obj) {
12101
- if (typeof obj !== "object" || obj === null) {
12102
- throw new ToonDecodeError2("Decoded value is not an object");
12103
- }
12104
- const event = obj;
12105
- if (!isValidHex(event["id"], 64)) {
12106
- throw new ToonDecodeError2(
12107
- "Invalid event id: must be a 64-character hex string"
12108
- );
12109
- }
12110
- if (!isValidHex(event["pubkey"], 64)) {
12111
- throw new ToonDecodeError2(
12112
- "Invalid event pubkey: must be a 64-character hex string"
12251
+ function readConfigFile(path) {
12252
+ try {
12253
+ return JSON.parse(readFileSync3(path, "utf8"));
12254
+ } catch (err) {
12255
+ if (err.code === "ENOENT") return {};
12256
+ throw new Error(
12257
+ `Failed to read daemon config at ${path}: ${err instanceof Error ? err.message : String(err)}`
12113
12258
  );
12114
12259
  }
12115
- if (typeof event["kind"] !== "number" || !Number.isInteger(event["kind"])) {
12116
- throw new ToonDecodeError2("Invalid event kind: must be an integer");
12117
- }
12118
- if (typeof event["content"] !== "string") {
12119
- throw new ToonDecodeError2("Invalid event content: must be a string");
12120
- }
12121
- const tags = event["tags"];
12122
- if (!Array.isArray(tags)) {
12123
- throw new ToonDecodeError2("Invalid event tags: must be an array");
12124
- }
12125
- for (let i = 0; i < tags.length; i++) {
12126
- const tag = tags[i];
12127
- if (!Array.isArray(tag)) {
12128
- throw new ToonDecodeError2(`Invalid event tags[${i}]: must be an array`);
12129
- }
12130
- for (let j = 0; j < tag.length; j++) {
12131
- if (typeof tag[j] !== "string") {
12132
- throw new ToonDecodeError2(
12133
- `Invalid event tags[${i}][${j}]: must be a string`
12134
- );
12135
- }
12260
+ }
12261
+ function resolveMnemonic(file) {
12262
+ const envMnemonic = process.env["TOON_CLIENT_MNEMONIC"];
12263
+ if (envMnemonic) return envMnemonic.trim();
12264
+ if (file.keystorePath) {
12265
+ const password = process.env["TOON_CLIENT_KEYSTORE_PASSWORD"] ?? (file.keystoreAutoPassword ? DEFAULT_KEYSTORE_PASSWORD : void 0);
12266
+ if (!password) {
12267
+ throw new Error(
12268
+ "keystorePath is set but TOON_CLIENT_KEYSTORE_PASSWORD is not provided"
12269
+ );
12136
12270
  }
12271
+ return loadKeystore(file.keystorePath, password);
12137
12272
  }
12138
- if (typeof event["created_at"] !== "number" || !Number.isInteger(event["created_at"])) {
12139
- throw new ToonDecodeError2("Invalid event created_at: must be an integer");
12140
- }
12141
- if (!isValidHex(event["sig"], 128)) {
12142
- throw new ToonDecodeError2(
12143
- "Invalid event sig: must be a 128-character hex string"
12144
- );
12145
- }
12146
- }
12147
- function decodeEventFromToon2(data) {
12148
- try {
12149
- const toonString = new TextDecoder().decode(data);
12150
- const decoded = decode3(toonString);
12151
- validateNostrEvent(decoded);
12152
- return decoded;
12153
- } catch (error) {
12154
- if (error instanceof ToonDecodeError2) {
12155
- throw error;
12156
- }
12157
- throw new ToonDecodeError2(
12158
- `Failed to decode TOON data: ${error instanceof Error ? error.message : String(error)}`,
12159
- error instanceof Error ? error : void 0
12160
- );
12161
- }
12162
- }
12163
-
12164
- // ../../node_modules/.pnpm/@toon-protocol+core@1.4.2_@toon-protocol+connector@3.13.0_typescript@5.9.3/node_modules/@toon-protocol/core/dist/index.js
12165
- import { finalizeEvent as finalizeEvent12 } from "nostr-tools/pure";
12166
- import { finalizeEvent as finalizeEvent23 } from "nostr-tools/pure";
12167
- import { finalizeEvent as finalizeEvent32 } from "nostr-tools/pure";
12168
- import { finalizeEvent as finalizeEvent42 } from "nostr-tools/pure";
12169
- import { finalizeEvent as finalizeEvent52 } from "nostr-tools/pure";
12170
- import { finalizeEvent as finalizeEvent62 } from "nostr-tools/pure";
12171
- import { finalizeEvent as finalizeEvent72 } from "nostr-tools/pure";
12172
- import { finalizeEvent as finalizeEvent82 } from "nostr-tools/pure";
12173
- import { finalizeEvent as finalizeEvent92 } from "nostr-tools/pure";
12174
- import { finalizeEvent as finalizeEvent102 } from "nostr-tools/pure";
12175
- import { SimplePool as SimplePool4 } from "nostr-tools/pool";
12176
- import { SimplePool as SimplePool22 } from "nostr-tools/pool";
12177
- import { getPublicKey as getPublicKey7 } from "nostr-tools/pure";
12178
- import WebSocket3 from "ws";
12179
- import { getPublicKey as getPublicKey23, verifyEvent as verifyEvent3 } from "nostr-tools/pure";
12180
- import { SimplePool as SimplePool32 } from "nostr-tools/pool";
12181
- import { getPublicKey as getPublicKey33 } from "nostr-tools/pure";
12182
- import WebSocket23 from "ws";
12183
- import { getPublicKey as getPublicKey42 } from "nostr-tools/pure";
12184
- import { getPublicKey as getPublicKey52 } from "nostr-tools/pure";
12185
- var ILP_PEER_INFO_KIND2 = 10032;
12186
- var ILP_SEGMENT_PATTERN2 = /^[a-z0-9-]+$/;
12187
- var MAX_ILP_ADDRESS_LENGTH2 = 1023;
12188
- function isValidIlpAddressStructure2(address) {
12189
- if (!address) return false;
12190
- if (address.length > MAX_ILP_ADDRESS_LENGTH2) return false;
12191
- const segments = address.split(".");
12192
- for (const segment of segments) {
12193
- if (segment.length === 0) return false;
12194
- if (!ILP_SEGMENT_PATTERN2.test(segment)) return false;
12195
- }
12196
- return true;
12197
- }
12198
- function validateChainId2(chainId) {
12199
- if (!chainId) return false;
12200
- const segments = chainId.split(":");
12201
- if (segments.length < 2 || segments.length > 3) return false;
12202
- return segments.every((s) => s.length > 0);
12203
- }
12204
- var RATE_REGEX2 = /^(0|[1-9]\d*)(\.\d+)?$/;
12205
- var AMOUNT_REGEX2 = /^\d+$/;
12206
- var MAX_NUMERIC_STRING_LENGTH2 = 80;
12207
- function isObject3(value) {
12208
- return typeof value === "object" && value !== null && !Array.isArray(value);
12209
- }
12210
- function isNonEmptyString2(value) {
12211
- return typeof value === "string" && value.length > 0;
12212
- }
12213
- function isNonNegativeInteger2(value) {
12214
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
12215
- }
12216
- function validateAsset2(asset, side) {
12217
- if (!isObject3(asset)) {
12218
- return {
12219
- valid: false,
12220
- reason: `${side} must be an object`,
12221
- field: side
12222
- };
12223
- }
12224
- if (!isNonEmptyString2(asset.assetCode)) {
12225
- return {
12226
- valid: false,
12227
- reason: `${side}.assetCode must be a non-empty string`,
12228
- field: `${side}.assetCode`
12229
- };
12230
- }
12231
- if (!isNonNegativeInteger2(asset.assetScale)) {
12232
- return {
12233
- valid: false,
12234
- reason: `${side}.assetScale must be a non-negative integer`,
12235
- field: `${side}.assetScale`
12236
- };
12237
- }
12238
- if (typeof asset.chain !== "string" || !validateChainId2(asset.chain)) {
12239
- return {
12240
- valid: false,
12241
- reason: `${side}.chain must be a valid chain identifier (e.g., "evm:base:8453")`,
12242
- field: `${side}.chain`
12243
- };
12244
- }
12245
- return { valid: true };
12246
- }
12247
- function isValidSwapPair2(pair) {
12248
- if (!isObject3(pair)) {
12249
- return { valid: false, reason: "pair must be an object", field: "" };
12250
- }
12251
- const fromResult = validateAsset2(pair.from, "from");
12252
- if (!fromResult.valid) return fromResult;
12253
- const toResult = validateAsset2(pair.to, "to");
12254
- if (!toResult.valid) return toResult;
12255
- if (typeof pair.rate !== "string" || pair.rate.length > MAX_NUMERIC_STRING_LENGTH2 || !RATE_REGEX2.test(pair.rate)) {
12256
- return {
12257
- valid: false,
12258
- reason: `rate must be a non-negative decimal string (no leading zeros, no exponent, max ${MAX_NUMERIC_STRING_LENGTH2} chars)`,
12259
- field: "rate"
12260
- };
12261
- }
12262
- if (pair.minAmount !== void 0) {
12263
- if (typeof pair.minAmount !== "string" || pair.minAmount.length > MAX_NUMERIC_STRING_LENGTH2 || !AMOUNT_REGEX2.test(pair.minAmount)) {
12264
- return {
12265
- valid: false,
12266
- reason: `minAmount must be a non-negative integer string (max ${MAX_NUMERIC_STRING_LENGTH2} digits)`,
12267
- field: "minAmount"
12268
- };
12269
- }
12270
- }
12271
- if (pair.maxAmount !== void 0) {
12272
- if (typeof pair.maxAmount !== "string" || pair.maxAmount.length > MAX_NUMERIC_STRING_LENGTH2 || !AMOUNT_REGEX2.test(pair.maxAmount)) {
12273
- return {
12274
- valid: false,
12275
- reason: `maxAmount must be a non-negative integer string (max ${MAX_NUMERIC_STRING_LENGTH2} digits)`,
12276
- field: "maxAmount"
12277
- };
12278
- }
12279
- }
12280
- if (pair.minAmount !== void 0 && pair.maxAmount !== void 0) {
12281
- if (BigInt(pair.minAmount) > BigInt(pair.maxAmount)) {
12282
- return {
12283
- valid: false,
12284
- reason: "minAmount must not exceed maxAmount",
12285
- field: "minAmount/maxAmount"
12286
- };
12287
- }
12288
- }
12289
- return { valid: true };
12290
- }
12291
- function formatMessage2(index, result) {
12292
- return `swapPairs[${index}]: ${result.reason} (field: ${result.field})`;
12293
- }
12294
- function assertSwapPairForParse2(pair, index) {
12295
- const result = isValidSwapPair2(pair);
12296
- if (!result.valid) {
12297
- throw new InvalidEventError2(formatMessage2(index, result));
12298
- }
12299
- }
12300
- function isObject22(value) {
12301
- return typeof value === "object" && value !== null && !Array.isArray(value);
12302
- }
12303
- function parseIlpPeerInfo2(event) {
12304
- if (event.kind !== ILP_PEER_INFO_KIND2) {
12305
- throw new InvalidEventError2(
12306
- `Expected event kind ${ILP_PEER_INFO_KIND2}, got ${event.kind}`
12307
- );
12308
- }
12309
- let parsed;
12310
- try {
12311
- parsed = JSON.parse(event.content);
12312
- } catch (err) {
12313
- throw new InvalidEventError2(
12314
- "Failed to parse event content as JSON",
12315
- err instanceof Error ? err : void 0
12316
- );
12317
- }
12318
- if (!isObject22(parsed)) {
12319
- throw new InvalidEventError2("Event content must be a JSON object");
12320
- }
12321
- const {
12322
- ilpAddress,
12323
- btpEndpoint,
12324
- blsHttpEndpoint,
12325
- settlementEngine,
12326
- assetCode,
12327
- assetScale,
12328
- ilpAddresses: rawIlpAddresses
12329
- } = parsed;
12330
- if (typeof ilpAddress !== "string" || ilpAddress.length === 0) {
12331
- throw new InvalidEventError2(
12332
- "Missing or invalid required field: ilpAddress"
12333
- );
12334
- }
12335
- if (btpEndpoint !== void 0 && typeof btpEndpoint !== "string") {
12336
- throw new InvalidEventError2("Invalid field: btpEndpoint must be a string");
12337
- }
12338
- if (typeof assetCode !== "string" || assetCode.length === 0) {
12339
- throw new InvalidEventError2("Missing or invalid required field: assetCode");
12340
- }
12341
- if (typeof assetScale !== "number" || !Number.isInteger(assetScale)) {
12342
- throw new InvalidEventError2(
12343
- "Missing or invalid required field: assetScale"
12344
- );
12345
- }
12346
- if (settlementEngine !== void 0 && typeof settlementEngine !== "string") {
12347
- throw new InvalidEventError2(
12348
- "Invalid optional field: settlementEngine must be a string"
12349
- );
12350
- }
12351
- const {
12352
- supportedChains,
12353
- settlementAddresses,
12354
- preferredTokens,
12355
- tokenNetworks
12356
- } = parsed;
12357
- if (supportedChains !== void 0) {
12358
- if (!Array.isArray(supportedChains)) {
12359
- throw new InvalidEventError2("supportedChains must be an array");
12360
- }
12361
- if (supportedChains.length === 0) {
12362
- throw new InvalidEventError2(
12363
- "supportedChains must be a non-empty array when provided"
12364
- );
12365
- }
12366
- for (const chainId of supportedChains) {
12367
- if (typeof chainId !== "string" || !validateChainId2(chainId)) {
12368
- throw new InvalidEventError2(
12369
- `Invalid chain identifier: ${String(chainId)}`
12370
- );
12371
- }
12372
- }
12373
- }
12374
- if (settlementAddresses !== void 0) {
12375
- if (!isObject22(settlementAddresses)) {
12376
- throw new InvalidEventError2("settlementAddresses must be an object");
12377
- }
12378
- for (const [key, value] of Object.entries(settlementAddresses)) {
12379
- if (!validateChainId2(key)) {
12380
- throw new InvalidEventError2(
12381
- `Invalid chain identifier in settlementAddresses: ${key}`
12382
- );
12383
- }
12384
- if (typeof value !== "string" || value.length === 0) {
12385
- throw new InvalidEventError2(
12386
- "settlementAddresses values must be non-empty strings"
12387
- );
12388
- }
12389
- }
12390
- if (Array.isArray(supportedChains)) {
12391
- const chainSet = new Set(supportedChains);
12392
- for (const key of Object.keys(settlementAddresses)) {
12393
- if (!chainSet.has(key)) {
12394
- throw new InvalidEventError2(
12395
- `settlementAddresses key '${key}' is not in supportedChains`
12396
- );
12397
- }
12398
- }
12399
- }
12400
- }
12401
- if (preferredTokens !== void 0) {
12402
- if (!isObject22(preferredTokens)) {
12403
- throw new InvalidEventError2("preferredTokens must be an object");
12404
- }
12405
- }
12406
- if (tokenNetworks !== void 0) {
12407
- if (!isObject22(tokenNetworks)) {
12408
- throw new InvalidEventError2("tokenNetworks must be an object");
12409
- }
12410
- }
12411
- const { feePerByte: rawFeePerByte } = parsed;
12412
- let feePerByte;
12413
- if (rawFeePerByte === void 0) {
12414
- feePerByte = "0";
12415
- } else if (typeof rawFeePerByte !== "string" || !/^\d+$/.test(rawFeePerByte)) {
12416
- throw new InvalidEventError2(
12417
- `Invalid feePerByte: "${String(rawFeePerByte)}" must be a non-negative integer string`
12418
- );
12419
- } else {
12420
- feePerByte = rawFeePerByte;
12421
- }
12422
- const { prefixPricing: rawPrefixPricing } = parsed;
12423
- let prefixPricing;
12424
- if (rawPrefixPricing !== void 0) {
12425
- if (!isObject22(rawPrefixPricing)) {
12426
- throw new InvalidEventError2("prefixPricing must be an object");
12427
- }
12428
- const { basePrice } = rawPrefixPricing;
12429
- if (typeof basePrice !== "string" || !/^\d+$/.test(basePrice)) {
12430
- throw new InvalidEventError2(
12431
- `Invalid prefixPricing.basePrice: "${String(basePrice)}" must be a non-negative integer string`
12432
- );
12433
- }
12434
- prefixPricing = { basePrice };
12435
- }
12436
- const { swapPairs: rawSwapPairs } = parsed;
12437
- let swapPairs;
12438
- if (rawSwapPairs !== void 0) {
12439
- if (!Array.isArray(rawSwapPairs)) {
12440
- throw new InvalidEventError2("swapPairs must be an array");
12441
- }
12442
- rawSwapPairs.forEach((pair, index) => {
12443
- assertSwapPairForParse2(pair, index);
12444
- });
12445
- swapPairs = rawSwapPairs;
12446
- }
12447
- let ilpAddresses;
12448
- if (rawIlpAddresses !== void 0) {
12449
- if (!Array.isArray(rawIlpAddresses)) {
12450
- throw new InvalidEventError2("ilpAddresses must be an array");
12451
- }
12452
- for (const addr of rawIlpAddresses) {
12453
- if (typeof addr !== "string" || addr.length === 0) {
12454
- throw new InvalidEventError2(
12455
- "ilpAddresses elements must be non-empty strings"
12456
- );
12457
- }
12458
- if (!isValidIlpAddressStructure2(addr)) {
12459
- throw new InvalidEventError2(
12460
- `Invalid ILP address in ilpAddresses: "${addr}"`
12461
- );
12462
- }
12463
- }
12464
- ilpAddresses = rawIlpAddresses;
12465
- } else {
12466
- ilpAddresses = [ilpAddress];
12467
- }
12468
- return {
12469
- ilpAddress,
12470
- btpEndpoint: typeof btpEndpoint === "string" ? btpEndpoint : "",
12471
- ...blsHttpEndpoint !== void 0 && typeof blsHttpEndpoint === "string" && { blsHttpEndpoint },
12472
- assetCode,
12473
- assetScale,
12474
- ...settlementEngine !== void 0 && { settlementEngine },
12475
- supportedChains: supportedChains !== void 0 ? supportedChains : [],
12476
- settlementAddresses: settlementAddresses !== void 0 ? settlementAddresses : {},
12477
- ...preferredTokens !== void 0 && {
12478
- preferredTokens
12479
- },
12480
- ...tokenNetworks !== void 0 && {
12481
- tokenNetworks
12482
- },
12483
- ilpAddresses,
12484
- feePerByte,
12485
- ...prefixPricing !== void 0 && { prefixPricing },
12486
- ...swapPairs !== void 0 && { swapPairs }
12487
- };
12488
- }
12489
- var EXPIRATION_TAG2 = "expiration";
12490
- function getEventExpiration(event) {
12491
- const tag = event.tags.find((t) => t[0] === EXPIRATION_TAG2);
12492
- if (!tag || tag[1] === void 0) return void 0;
12493
- const ts = Number(tag[1]);
12494
- if (!Number.isFinite(ts) || ts < 0) return void 0;
12495
- return ts;
12496
- }
12497
- function isEventExpired(event, nowSeconds = Math.floor(Date.now() / 1e3)) {
12498
- const exp = getEventExpiration(event);
12499
- return exp !== void 0 && exp <= nowSeconds;
12500
- }
12501
- var genesis_peers_default2 = [];
12502
- var PUBKEY_REGEX32 = /^[0-9a-f]{64}$/;
12503
- var ILP_ADDRESS_REGEX2 = /^g\.[a-zA-Z0-9.-]+$/;
12504
- function isValidPubkey22(pubkey) {
12505
- return PUBKEY_REGEX32.test(pubkey);
12506
- }
12507
- function isValidRelayUrl2(url) {
12508
- return url.startsWith("wss://") || url.startsWith("ws://");
12509
- }
12510
- function isValidIlpAddress2(address) {
12511
- return ILP_ADDRESS_REGEX2.test(address);
12512
- }
12513
- function isValidBtpEndpoint2(url) {
12514
- return url.startsWith("wss://") || url.startsWith("ws://");
12515
- }
12516
- function isValidGenesisPeer2(entry) {
12517
- if (typeof entry !== "object" || entry === null) return false;
12518
- const obj = entry;
12519
- return typeof obj["pubkey"] === "string" && typeof obj["relayUrl"] === "string" && typeof obj["ilpAddress"] === "string" && typeof obj["btpEndpoint"] === "string" && isValidPubkey22(obj["pubkey"]) && isValidRelayUrl2(obj["relayUrl"]) && isValidIlpAddress2(obj["ilpAddress"]) && isValidBtpEndpoint2(obj["btpEndpoint"]);
12520
- }
12521
- function deduplicateByPubkey2(peers) {
12522
- const map = /* @__PURE__ */ new Map();
12523
- for (const peer of peers) {
12524
- map.set(peer.pubkey, peer);
12525
- }
12526
- return [...map.values()];
12527
- }
12528
- function loadGenesisPeers2() {
12529
- const raw = genesis_peers_default2;
12530
- const valid = [];
12531
- for (const entry of raw) {
12532
- if (isValidGenesisPeer2(entry)) {
12533
- valid.push(entry);
12534
- } else {
12535
- console.warn("Skipping invalid genesis peer entry:", entry);
12536
- }
12537
- }
12538
- return deduplicateByPubkey2(valid);
12539
- }
12540
- function loadAdditionalPeers2(json) {
12541
- let parsed;
12542
- try {
12543
- parsed = JSON.parse(json);
12544
- } catch {
12545
- console.warn("Failed to parse additional peers JSON:", json);
12546
- return [];
12547
- }
12548
- if (!Array.isArray(parsed)) {
12549
- console.warn("Additional peers JSON is not an array");
12550
- return [];
12551
- }
12552
- const valid = [];
12553
- for (const entry of parsed) {
12554
- if (isValidGenesisPeer2(entry)) {
12555
- valid.push(entry);
12556
- } else {
12557
- console.warn("Skipping invalid additional peer entry:", entry);
12558
- }
12559
- }
12560
- return valid;
12561
- }
12562
- function loadAllPeers2(additionalPeersJson) {
12563
- const genesis = loadGenesisPeers2();
12564
- if (!additionalPeersJson) {
12565
- return genesis;
12566
- }
12567
- const additional = loadAdditionalPeers2(additionalPeersJson);
12568
- return deduplicateByPubkey2([...genesis, ...additional]);
12569
- }
12570
- var GenesisPeerLoader2 = {
12571
- loadGenesisPeers: loadGenesisPeers2,
12572
- loadAdditionalPeers: loadAdditionalPeers2,
12573
- loadAllPeers: loadAllPeers2
12574
- };
12575
- var ILP_TO_SEMANTIC2 = Object.freeze({
12576
- T00: "internal_error",
12577
- T04: "insufficient_funds",
12578
- F00: "invalid_request",
12579
- // F01 ("Invalid Packet" in ILP terms — emitted by the swap handler for
12580
- // "Invalid gift wrap" / "Invalid amount") has NO dedicated entry in the
12581
- // connector's REJECT_CODE_MAP (accepted semantics are: insufficient_funds,
12582
- // expired, unreachable, invalid_request, invalid_amount,
12583
- // insufficient_destination_amount, unexpected_payment, application_error,
12584
- // internal_error, timeout). The closest faithful reason is `invalid_request`,
12585
- // which the connector re-encodes to wire code F00. We map F01 EXPLICITLY
12586
- // (rather than relying on the fallback below) so the F01 -> F00 normalization
12587
- // is intentional and test-pinned, not a silent collapse that misleads callers
12588
- // into thinking they hit a different failure class. See issue #86.
12589
- F01: "invalid_request",
12590
- F02: "unreachable",
12591
- F03: "invalid_amount",
12592
- F04: "insufficient_destination_amount",
12593
- F06: "unexpected_payment",
12594
- R00: "expired"
12595
- });
12596
- function buildIlpPrepare(params) {
12597
- return {
12598
- destination: params.destination,
12599
- amount: String(params.amount),
12600
- data: Buffer.from(params.data).toString("base64")
12601
- };
12602
- }
12603
-
12604
- // src/daemon/config.ts
12605
- var DEFAULT_KEYSTORE_PASSWORD = "toon-client-default";
12606
- function configDir() {
12607
- return process.env["TOON_CLIENT_HOME"] ?? join2(homedir(), ".toon-client");
12608
- }
12609
- function defaultConfigPath() {
12610
- return join2(configDir(), "config.json");
12611
- }
12612
- function readConfigFile(path) {
12613
- try {
12614
- return JSON.parse(readFileSync3(path, "utf8"));
12615
- } catch (err) {
12616
- if (err.code === "ENOENT") return {};
12617
- throw new Error(
12618
- `Failed to read daemon config at ${path}: ${err instanceof Error ? err.message : String(err)}`
12619
- );
12620
- }
12621
- }
12622
- function resolveMnemonic(file) {
12623
- const envMnemonic = process.env["TOON_CLIENT_MNEMONIC"];
12624
- if (envMnemonic) return envMnemonic.trim();
12625
- if (file.keystorePath) {
12626
- const password = process.env["TOON_CLIENT_KEYSTORE_PASSWORD"] ?? (file.keystoreAutoPassword ? DEFAULT_KEYSTORE_PASSWORD : void 0);
12627
- if (!password) {
12628
- throw new Error(
12629
- "keystorePath is set but TOON_CLIENT_KEYSTORE_PASSWORD is not provided"
12630
- );
12631
- }
12632
- return loadKeystore(file.keystorePath, password);
12633
- }
12634
- if (file.mnemonic) return file.mnemonic.trim();
12635
- throw new Error(
12636
- "No mnemonic configured. Set TOON_CLIENT_MNEMONIC, configure a keystorePath (+ TOON_CLIENT_KEYSTORE_PASSWORD), or add `mnemonic` to the config file."
12637
- );
12273
+ if (file.mnemonic) return file.mnemonic.trim();
12274
+ throw new Error(
12275
+ "No mnemonic configured. Set TOON_CLIENT_MNEMONIC, configure a keystorePath (+ TOON_CLIENT_KEYSTORE_PASSWORD), or add `mnemonic` to the config file."
12276
+ );
12638
12277
  }
12639
12278
  function parseCsvEnv(value) {
12640
12279
  if (!value) return void 0;
@@ -12657,7 +12296,7 @@ function resolveConfig(file) {
12657
12296
  const faucetTimeoutMs = faucetTimeoutEnv && Number.isFinite(Number(faucetTimeoutEnv)) ? Number(faucetTimeoutEnv) : file.faucetTimeoutMs;
12658
12297
  const btpUrl = process.env["TOON_CLIENT_BTP_URL"] ?? file.btpUrl;
12659
12298
  const hasUplink = Boolean(btpUrl || proxyUrl);
12660
- const genesisSeed = GenesisPeerLoader2.loadGenesisPeers()[0];
12299
+ const genesisSeed = GenesisPeerLoader.loadGenesisPeers()[0];
12661
12300
  const relayUrl = process.env["TOON_CLIENT_RELAY_URL"] ?? file.relayUrl ?? genesisSeed?.relayUrl ?? "ws://localhost:7100";
12662
12301
  const httpPort = Number(
12663
12302
  process.env["TOON_CLIENT_HTTP_PORT"] ?? file.httpPort ?? 8787
@@ -12691,8 +12330,8 @@ function resolveConfig(file) {
12691
12330
  assetCode: "USD",
12692
12331
  assetScale: 6
12693
12332
  },
12694
- toonEncoder: encodeEventToToon2,
12695
- toonDecoder: decodeEventFromToon2,
12333
+ toonEncoder: encodeEventToToon,
12334
+ toonDecoder: decodeEventFromToon,
12696
12335
  ...btpUrl ? { btpUrl, btpAuthToken: "" } : {},
12697
12336
  destinationAddress: destination,
12698
12337
  relayUrl: "",
@@ -13084,18 +12723,19 @@ function delay(ms) {
13084
12723
  }
13085
12724
 
13086
12725
  export {
12726
+ ToonError,
12727
+ encodeEventToToon,
12728
+ decodeEventFromToon,
12729
+ ILP_PEER_INFO_KIND,
12730
+ parseIlpPeerInfo,
12731
+ isEventExpired,
12732
+ buildIlpPrepare,
13087
12733
  deriveFullIdentity,
12734
+ mintExecutionCondition,
13088
12735
  extractArweaveTxId,
13089
12736
  ToonClient,
13090
12737
  fundWallet,
13091
12738
  generateKeystore,
13092
- ToonError2 as ToonError,
13093
- encodeEventToToon2 as encodeEventToToon,
13094
- decodeEventFromToon2 as decodeEventFromToon,
13095
- ILP_PEER_INFO_KIND2 as ILP_PEER_INFO_KIND,
13096
- parseIlpPeerInfo2 as parseIlpPeerInfo,
13097
- isEventExpired,
13098
- buildIlpPrepare,
13099
12739
  ARWEAVE_GATEWAYS,
13100
12740
  arweaveUrls,
13101
12741
  DEFAULT_KEYSTORE_PASSWORD,
@@ -13134,4 +12774,4 @@ export {
13134
12774
  @scure/bip32/lib/esm/index.js:
13135
12775
  (*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) *)
13136
12776
  */
13137
- //# sourceMappingURL=chunk-P7YF72JB.js.map
12777
+ //# sourceMappingURL=chunk-UDHZEABQ.js.map