@hsuite/smart-engines-sdk 3.4.1 → 3.6.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.
@@ -230,7 +230,7 @@ var CreateAccountRequestSchema = zod.z.object({
230
230
  * Smart node security mode for the account key structure.
231
231
  * - 'partial': threshold(2, [appOwnerKey, tssKeyList]) — co-control
232
232
  * - 'full': TSS KeyList only — full validator network control
233
- * Default: 'full' (Arc 5 §7.5.2: 'none' removed).
233
+ * @defaultValue 'full'
234
234
  */
235
235
  securityMode: zod.z.enum(["partial", "full"]).default("full"),
236
236
  /**
@@ -1100,6 +1100,187 @@ var ClusterDiscoveryClient = class {
1100
1100
  }
1101
1101
  };
1102
1102
 
1103
+ // src/http/index.ts
1104
+ var SdkHttpError = class extends Error {
1105
+ constructor(message, statusCode, details) {
1106
+ super(message);
1107
+ this.statusCode = statusCode;
1108
+ this.details = details;
1109
+ this.name = "SdkHttpError";
1110
+ }
1111
+ statusCode;
1112
+ details;
1113
+ };
1114
+ function createHttpClient(config) {
1115
+ const timeout = config.timeout ?? 3e4;
1116
+ function getHeaders(contentType) {
1117
+ const headers = {};
1118
+ if (contentType) {
1119
+ headers["Content-Type"] = contentType;
1120
+ }
1121
+ if (config.authToken) {
1122
+ headers["Authorization"] = `Bearer ${config.authToken}`;
1123
+ }
1124
+ if (config.apiKey) {
1125
+ headers["X-API-Key"] = config.apiKey;
1126
+ }
1127
+ return headers;
1128
+ }
1129
+ function setAuthToken(token) {
1130
+ config.authToken = token;
1131
+ }
1132
+ function getAuthToken() {
1133
+ return config.authToken;
1134
+ }
1135
+ async function request(method, path, body) {
1136
+ const url = `${config.baseUrl}${path}`;
1137
+ const controller = new AbortController();
1138
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1139
+ try {
1140
+ const init = {
1141
+ method,
1142
+ headers: getHeaders("application/json"),
1143
+ signal: controller.signal
1144
+ };
1145
+ if (body !== void 0) {
1146
+ init.body = JSON.stringify(body);
1147
+ }
1148
+ const response = await fetch(url, init);
1149
+ clearTimeout(timeoutId);
1150
+ if (!response.ok) {
1151
+ const errorData = await response.json().catch(() => ({}));
1152
+ throw new SdkHttpError(
1153
+ errorData.message || `API error: ${response.status} ${response.statusText}`,
1154
+ response.status,
1155
+ errorData
1156
+ );
1157
+ }
1158
+ const text = await response.text();
1159
+ if (!text) return void 0;
1160
+ return JSON.parse(text);
1161
+ } catch (error) {
1162
+ clearTimeout(timeoutId);
1163
+ if (error instanceof SdkHttpError) throw error;
1164
+ const err = error;
1165
+ if (err.name === "AbortError") {
1166
+ throw new SdkHttpError("Request timeout", 408);
1167
+ }
1168
+ throw new SdkHttpError(`Network error: ${err.message}`, 0, error);
1169
+ }
1170
+ }
1171
+ async function getText(path) {
1172
+ const url = `${config.baseUrl}${path}`;
1173
+ const controller = new AbortController();
1174
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
1175
+ try {
1176
+ const response = await fetch(url, {
1177
+ method: "GET",
1178
+ headers: getHeaders(),
1179
+ signal: controller.signal
1180
+ });
1181
+ clearTimeout(timeoutId);
1182
+ if (!response.ok) {
1183
+ const errBody = await response.json().catch(() => ({}));
1184
+ throw new SdkHttpError(
1185
+ errBody.message || `API error: ${response.status} ${response.statusText}`,
1186
+ response.status,
1187
+ errBody
1188
+ );
1189
+ }
1190
+ return await response.text();
1191
+ } catch (error) {
1192
+ clearTimeout(timeoutId);
1193
+ if (error instanceof SdkHttpError) throw error;
1194
+ const err = error;
1195
+ if (err.name === "AbortError") {
1196
+ throw new SdkHttpError("Request timeout", 408);
1197
+ }
1198
+ throw new SdkHttpError(`Network error: ${err.message}`, 0, error);
1199
+ }
1200
+ }
1201
+ async function upload(path, file, filename, metadata, fieldName = "file") {
1202
+ const url = `${config.baseUrl}${path}`;
1203
+ const controller = new AbortController();
1204
+ const timeoutId = setTimeout(() => controller.abort(), timeout * 2);
1205
+ try {
1206
+ const formData = new FormData();
1207
+ const blob = file instanceof Blob ? file : new Blob([new Uint8Array(file)]);
1208
+ formData.append(fieldName, blob, filename);
1209
+ if (metadata) {
1210
+ for (const [key, value] of Object.entries(metadata)) {
1211
+ formData.append(key, value);
1212
+ }
1213
+ }
1214
+ const headers = {};
1215
+ if (config.authToken) {
1216
+ headers["Authorization"] = `Bearer ${config.authToken}`;
1217
+ }
1218
+ if (config.apiKey) {
1219
+ headers["X-API-Key"] = config.apiKey;
1220
+ }
1221
+ const response = await fetch(url, {
1222
+ method: "POST",
1223
+ headers,
1224
+ body: formData,
1225
+ signal: controller.signal
1226
+ });
1227
+ clearTimeout(timeoutId);
1228
+ if (!response.ok) {
1229
+ const errorData = await response.json().catch(() => ({}));
1230
+ throw new SdkHttpError(
1231
+ errorData.message || `Upload error: ${response.status} ${response.statusText}`,
1232
+ response.status,
1233
+ errorData
1234
+ );
1235
+ }
1236
+ return response.json();
1237
+ } catch (error) {
1238
+ clearTimeout(timeoutId);
1239
+ if (error instanceof SdkHttpError) throw error;
1240
+ const err = error;
1241
+ if (err.name === "AbortError") {
1242
+ throw new SdkHttpError("Upload timeout", 408);
1243
+ }
1244
+ throw new SdkHttpError(`Upload error: ${err.message}`, 0, error);
1245
+ }
1246
+ }
1247
+ let reauthInFlight = null;
1248
+ async function withAuthRetry(path, op) {
1249
+ try {
1250
+ return await op();
1251
+ } catch (error) {
1252
+ const refreshable = !!config.onUnauthorized && !path.startsWith("/api/auth/") && error instanceof SdkHttpError && error.statusCode === 401;
1253
+ if (!refreshable) throw error;
1254
+ if (!reauthInFlight) {
1255
+ reauthInFlight = Promise.resolve(config.onUnauthorized()).finally(() => {
1256
+ reauthInFlight = null;
1257
+ });
1258
+ }
1259
+ try {
1260
+ await reauthInFlight;
1261
+ } catch {
1262
+ throw error;
1263
+ }
1264
+ return await op();
1265
+ }
1266
+ }
1267
+ const client = {
1268
+ post: (path, body) => withAuthRetry(path, () => request("POST", path, body)),
1269
+ get: (path) => withAuthRetry(path, () => request("GET", path)),
1270
+ put: (path, body) => withAuthRetry(path, () => request("PUT", path, body)),
1271
+ patch: (path, body) => withAuthRetry(path, () => request("PATCH", path, body)),
1272
+ delete: (path) => withAuthRetry(path, () => request("DELETE", path)),
1273
+ getText: (path) => withAuthRetry(path, () => getText(path)),
1274
+ upload: ((path, file, filename, metadata, fieldName) => withAuthRetry(path, () => upload(path, file, filename, metadata, fieldName))),
1275
+ setAuthToken,
1276
+ getAuthToken
1277
+ };
1278
+ return client;
1279
+ }
1280
+ function encodePathParam(param) {
1281
+ return encodeURIComponent(param).replace(/%2F/gi, "");
1282
+ }
1283
+
1103
1284
  // src/discovery/discovery-client.ts
1104
1285
  var DiscoveryClient = class {
1105
1286
  constructor(http) {
@@ -1133,7 +1314,7 @@ var DiscoveryClient = class {
1133
1314
  * try/catch.
1134
1315
  */
1135
1316
  async getClusterByNode(nodeId) {
1136
- return this.http.get(`/discovery/clusters/${encodeURIComponent(nodeId)}`);
1317
+ return this.http.get(`/discovery/clusters/${encodePathParam(nodeId)}`);
1137
1318
  }
1138
1319
  };
1139
1320
  var PlatformImagesClient = class {
@@ -1163,7 +1344,7 @@ var PlatformImagesClient = class {
1163
1344
  * authorized?" queries.
1164
1345
  */
1165
1346
  async get(imageName) {
1166
- return this.http.get(`/discovery/platform-images/${encodeURIComponent(imageName)}`);
1347
+ return this.http.get(`/discovery/platform-images/${encodePathParam(imageName)}`);
1167
1348
  }
1168
1349
  /**
1169
1350
  * `GET /api/v3/discovery/platform-images/:imageName/:version` —
@@ -1172,7 +1353,7 @@ var PlatformImagesClient = class {
1172
1353
  */
1173
1354
  async getVersion(imageName, version) {
1174
1355
  return this.http.get(
1175
- `/discovery/platform-images/${encodeURIComponent(imageName)}/${encodeURIComponent(version)}`
1356
+ `/discovery/platform-images/${encodePathParam(imageName)}/${encodePathParam(version)}`
1176
1357
  );
1177
1358
  }
1178
1359
  /**
@@ -1186,13 +1367,63 @@ var PlatformImagesClient = class {
1186
1367
  */
1187
1368
  async verify(imageName, version, digest) {
1188
1369
  return this.http.get(
1189
- `/discovery/platform-images/${encodeURIComponent(imageName)}/${encodeURIComponent(
1370
+ `/discovery/platform-images/${encodePathParam(imageName)}/${encodePathParam(
1190
1371
  version
1191
1372
  )}/verify?digest=${encodeURIComponent(digest)}`
1192
1373
  );
1193
1374
  }
1194
1375
  };
1195
1376
 
1377
+ // src/network-presets.ts
1378
+ var KNOWN_NETWORKS = {
1379
+ testnet: {
1380
+ bootstrap: ["https://gateway.testnet.hsuite.network"]
1381
+ },
1382
+ mainnet: {
1383
+ // Mainnet entrypoint reserved. The SDK still resolves the name so
1384
+ // upgrade-by-flipping-NETWORK works; the bootstrap URL is the
1385
+ // pre-allocated DNS we own. If mainnet isn't deployed yet, the discovery
1386
+ // fetch will fail at runtime with the same "no seed reachable" error
1387
+ // any unreachable URL produces — the caller learns the network is not
1388
+ // live, rather than getting a baffling "unknown network" TypeScript
1389
+ // error at compile time.
1390
+ bootstrap: ["https://gateway.hsuite.network"]
1391
+ }
1392
+ };
1393
+ function resolveNetwork(name) {
1394
+ const preset = KNOWN_NETWORKS[name];
1395
+ if (!preset) {
1396
+ throw new Error(
1397
+ `Unknown network: "${name}". Known networks: ${Object.keys(KNOWN_NETWORKS).join(", ")}`
1398
+ );
1399
+ }
1400
+ return preset;
1401
+ }
1402
+
1403
+ // src/discovery/resolve-cluster.ts
1404
+ async function resolveClusterEndpoint(config) {
1405
+ const allowInsecure = config.allowInsecure ?? false;
1406
+ const bootstrap = config.bootstrap ? [...config.bootstrap] : config.network ? [...resolveNetwork(config.network).bootstrap] : [];
1407
+ if (bootstrap.length === 0) {
1408
+ return { ok: false, reason: "no-seeds" };
1409
+ }
1410
+ const discovery = new ClusterDiscoveryClient({
1411
+ bootstrap,
1412
+ allowInsecure,
1413
+ trustAnchor: config.trustAnchor ? {
1414
+ network: config.trustAnchor.network,
1415
+ registryTopicId: config.trustAnchor.registryTopicId,
1416
+ mirrorNodeUrl: config.trustAnchor.mirrorNodeUrl,
1417
+ allowInsecure
1418
+ } : void 0
1419
+ });
1420
+ const cluster = await discovery.getRandomCluster();
1421
+ if (!cluster) {
1422
+ return { ok: false, reason: "no-clusters" };
1423
+ }
1424
+ return { ok: true, cluster };
1425
+ }
1426
+
1196
1427
  // src/auth/validator-auth.ts
1197
1428
  var SUPPORTED_AUTH_CHAINS = [
1198
1429
  "hedera",
@@ -1201,6 +1432,10 @@ var SUPPORTED_AUTH_CHAINS = [
1201
1432
  "stellar",
1202
1433
  "solana"
1203
1434
  ];
1435
+ function toBytes(value) {
1436
+ if (value instanceof Uint8Array) return value;
1437
+ return value.toBytes();
1438
+ }
1204
1439
  function validateValidatorUrl(url, allowInsecure = false) {
1205
1440
  try {
1206
1441
  const parsed = new URL(url);
@@ -1400,30 +1635,42 @@ var ValidatorAuthClient = class {
1400
1635
  }
1401
1636
  }
1402
1637
  /**
1403
- * Sign challenge with Hedera private key
1638
+ * Sign challenge with Hedera private key.
1639
+ *
1640
+ * Structurally typed against the surface of `@hashgraph/sdk`'s
1641
+ * `PrivateKey`. We don't `import` the SDK directly — pulling it into
1642
+ * the smart-engine SDK's runtime would bloat browser bundles and bring
1643
+ * peer-dep churn — but any object with a compatible `sign(...)` method
1644
+ * works.
1404
1645
  *
1405
1646
  * @param challenge - Challenge string from validator
1406
- * @param privateKey - Hedera PrivateKey instance from @hashgraph/sdk
1647
+ * @param privateKey - Hedera PrivateKey instance (or compatible signer)
1407
1648
  * @returns Hex-encoded signature
1408
1649
  */
1409
1650
  signChallengeHedera(challenge, privateKey) {
1410
- const messageBytes = Buffer.from(challenge, "utf-8");
1651
+ const messageBytes = new Uint8Array(Buffer.from(challenge, "utf-8"));
1411
1652
  const signature = privateKey.sign(messageBytes);
1412
- return Buffer.from(signature).toString("hex");
1653
+ return Buffer.from(toBytes(signature)).toString("hex");
1413
1654
  }
1414
1655
  /**
1415
- * Sign challenge with XRPL wallet
1656
+ * Sign challenge with XRPL wallet.
1657
+ *
1658
+ * Structurally typed against the surface of xrpl's `Wallet` — see the
1659
+ * comment on {@link HederaSigner} for the "no direct import" rationale.
1660
+ * Accepts both the `{ signedTransaction }` envelope and the bare-string
1661
+ * return shapes that xrpl signer libraries expose.
1416
1662
  *
1417
1663
  * @param challenge - Challenge string from validator
1418
- * @param wallet - XRPL Wallet instance from xrpl library
1664
+ * @param wallet - XRPL Wallet instance (or compatible signer)
1419
1665
  * @returns Hex-encoded signature
1420
1666
  */
1421
1667
  signChallengeXRPL(challenge, wallet) {
1422
1668
  const signature = wallet.sign(challenge);
1423
- if (typeof signature === "string" && /^[0-9A-Fa-f]+$/.test(signature)) {
1424
- return signature;
1669
+ if (typeof signature === "string") {
1670
+ if (/^[0-9A-Fa-f]+$/.test(signature)) return signature;
1671
+ return Buffer.from(signature).toString("hex");
1425
1672
  }
1426
- return Buffer.from(signature).toString("hex");
1673
+ return signature.signedTransaction;
1427
1674
  }
1428
1675
  /**
1429
1676
  * Complete authentication flow in one call
@@ -1460,189 +1707,6 @@ var ValidatorAuthError = class extends Error {
1460
1707
  details;
1461
1708
  };
1462
1709
 
1463
- // src/http/index.ts
1464
- var SdkHttpError = class extends Error {
1465
- constructor(message, statusCode, details) {
1466
- super(message);
1467
- this.statusCode = statusCode;
1468
- this.details = details;
1469
- this.name = "SdkHttpError";
1470
- }
1471
- statusCode;
1472
- details;
1473
- };
1474
- function createHttpClient(config) {
1475
- const timeout = config.timeout ?? 3e4;
1476
- function getHeaders(contentType) {
1477
- const headers = {};
1478
- if (contentType) {
1479
- headers["Content-Type"] = contentType;
1480
- }
1481
- if (config.authToken) {
1482
- headers["Authorization"] = `Bearer ${config.authToken}`;
1483
- }
1484
- if (config.apiKey) {
1485
- headers["X-API-Key"] = config.apiKey;
1486
- }
1487
- return headers;
1488
- }
1489
- function setAuthToken(token) {
1490
- config.authToken = token;
1491
- }
1492
- async function request(method, path, body) {
1493
- const url = `${config.baseUrl}${path}`;
1494
- const controller = new AbortController();
1495
- const timeoutId = setTimeout(() => controller.abort(), timeout);
1496
- try {
1497
- const init = {
1498
- method,
1499
- headers: getHeaders("application/json"),
1500
- signal: controller.signal
1501
- };
1502
- if (body !== void 0) {
1503
- init.body = JSON.stringify(body);
1504
- }
1505
- const response = await fetch(url, init);
1506
- clearTimeout(timeoutId);
1507
- if (!response.ok) {
1508
- const errorData = await response.json().catch(() => ({}));
1509
- throw new SdkHttpError(
1510
- errorData.message || `API error: ${response.status} ${response.statusText}`,
1511
- response.status,
1512
- errorData
1513
- );
1514
- }
1515
- const text = await response.text();
1516
- if (!text) return void 0;
1517
- return JSON.parse(text);
1518
- } catch (error) {
1519
- clearTimeout(timeoutId);
1520
- if (error instanceof SdkHttpError) throw error;
1521
- const err = error;
1522
- if (err.name === "AbortError") {
1523
- throw new SdkHttpError("Request timeout", 408);
1524
- }
1525
- throw new SdkHttpError(`Network error: ${err.message}`, 0, error);
1526
- }
1527
- }
1528
- async function getText(path) {
1529
- const url = `${config.baseUrl}${path}`;
1530
- const controller = new AbortController();
1531
- const timeoutId = setTimeout(() => controller.abort(), timeout);
1532
- try {
1533
- const response = await fetch(url, {
1534
- method: "GET",
1535
- headers: getHeaders(),
1536
- signal: controller.signal
1537
- });
1538
- clearTimeout(timeoutId);
1539
- if (!response.ok) {
1540
- const errBody = await response.json().catch(() => ({}));
1541
- throw new SdkHttpError(
1542
- errBody.message || `API error: ${response.status} ${response.statusText}`,
1543
- response.status,
1544
- errBody
1545
- );
1546
- }
1547
- return await response.text();
1548
- } catch (error) {
1549
- clearTimeout(timeoutId);
1550
- if (error instanceof SdkHttpError) throw error;
1551
- const err = error;
1552
- if (err.name === "AbortError") {
1553
- throw new SdkHttpError("Request timeout", 408);
1554
- }
1555
- throw new SdkHttpError(`Network error: ${err.message}`, 0, error);
1556
- }
1557
- }
1558
- async function upload(path, file, filename, metadata, fieldName = "file") {
1559
- const url = `${config.baseUrl}${path}`;
1560
- const controller = new AbortController();
1561
- const timeoutId = setTimeout(() => controller.abort(), timeout * 2);
1562
- try {
1563
- const formData = new FormData();
1564
- const blob = file instanceof Blob ? file : new Blob([new Uint8Array(file)]);
1565
- formData.append(fieldName, blob, filename);
1566
- if (metadata) {
1567
- for (const [key, value] of Object.entries(metadata)) {
1568
- formData.append(key, value);
1569
- }
1570
- }
1571
- const headers = {};
1572
- if (config.authToken) {
1573
- headers["Authorization"] = `Bearer ${config.authToken}`;
1574
- }
1575
- if (config.apiKey) {
1576
- headers["X-API-Key"] = config.apiKey;
1577
- }
1578
- const response = await fetch(url, {
1579
- method: "POST",
1580
- headers,
1581
- body: formData,
1582
- signal: controller.signal
1583
- });
1584
- clearTimeout(timeoutId);
1585
- if (!response.ok) {
1586
- const errorData = await response.json().catch(() => ({}));
1587
- throw new SdkHttpError(
1588
- errorData.message || `Upload error: ${response.status} ${response.statusText}`,
1589
- response.status,
1590
- errorData
1591
- );
1592
- }
1593
- return response.json();
1594
- } catch (error) {
1595
- clearTimeout(timeoutId);
1596
- if (error instanceof SdkHttpError) throw error;
1597
- const err = error;
1598
- if (err.name === "AbortError") {
1599
- throw new SdkHttpError("Upload timeout", 408);
1600
- }
1601
- throw new SdkHttpError(`Upload error: ${err.message}`, 0, error);
1602
- }
1603
- }
1604
- const client = {
1605
- post: (path, body) => request("POST", path, body),
1606
- get: (path) => request("GET", path),
1607
- put: (path, body) => request("PUT", path, body),
1608
- patch: (path, body) => request("PATCH", path, body),
1609
- delete: (path) => request("DELETE", path),
1610
- getText,
1611
- upload: ((path, file, filename, metadata, fieldName) => upload(path, file, filename, metadata, fieldName)),
1612
- setAuthToken
1613
- };
1614
- return client;
1615
- }
1616
- function encodePathParam(param) {
1617
- return encodeURIComponent(param).replace(/%2F/gi, "");
1618
- }
1619
-
1620
- // src/network-presets.ts
1621
- var KNOWN_NETWORKS = {
1622
- testnet: {
1623
- bootstrap: ["https://gateway.testnet.hsuite.network"]
1624
- },
1625
- mainnet: {
1626
- // Mainnet entrypoint reserved. The SDK still resolves the name so
1627
- // upgrade-by-flipping-NETWORK works; the bootstrap URL is the
1628
- // pre-allocated DNS we own. If mainnet isn't deployed yet, the discovery
1629
- // fetch will fail at runtime with the same "no seed reachable" error
1630
- // any unreachable URL produces — the caller learns the network is not
1631
- // live, rather than getting a baffling "unknown network" TypeScript
1632
- // error at compile time.
1633
- bootstrap: ["https://gateway.hsuite.network"]
1634
- }
1635
- };
1636
- function resolveNetwork(name) {
1637
- const preset = KNOWN_NETWORKS[name];
1638
- if (!preset) {
1639
- throw new Error(
1640
- `Unknown network: "${name}". Known networks: ${Object.keys(KNOWN_NETWORKS).join(", ")}`
1641
- );
1642
- }
1643
- return preset;
1644
- }
1645
-
1646
1710
  // src/subscription/index.ts
1647
1711
  var SubscriptionClient = class {
1648
1712
  constructor(http) {
@@ -1660,13 +1724,13 @@ var SubscriptionClient = class {
1660
1724
  * Get subscription status by app ID
1661
1725
  */
1662
1726
  async getStatus(appId) {
1663
- return this.http.get(`/subscription/status/${encodeURIComponent(appId)}`);
1727
+ return this.http.get(`/subscription/status/${encodePathParam(appId)}`);
1664
1728
  }
1665
1729
  /**
1666
1730
  * Mint subscription NFT after deposit is confirmed
1667
1731
  */
1668
1732
  async mintNft(appId) {
1669
- return this.http.post(`/subscription/mint/${encodeURIComponent(appId)}`, {});
1733
+ return this.http.post(`/subscription/mint/${encodePathParam(appId)}`, {});
1670
1734
  }
1671
1735
  /**
1672
1736
  * Renew subscription by extending period
@@ -1735,13 +1799,13 @@ var SubscriptionClient = class {
1735
1799
  * List subscriptions by status
1736
1800
  */
1737
1801
  async listByStatus(status) {
1738
- return this.http.get(`/subscription/list/status/${encodeURIComponent(status)}`);
1802
+ return this.http.get(`/subscription/list/status/${encodePathParam(status)}`);
1739
1803
  }
1740
1804
  /**
1741
1805
  * Get subscription balance
1742
1806
  */
1743
1807
  async getBalance(appId) {
1744
- return this.http.get(`/subscription/balance/${encodeURIComponent(appId)}`);
1808
+ return this.http.get(`/subscription/balance/${encodePathParam(appId)}`);
1745
1809
  }
1746
1810
  // ─── Tier-Change Endpoints ────────────────────────────────────────────────
1747
1811
  /**
@@ -1773,19 +1837,19 @@ var SubscriptionClient = class {
1773
1837
  */
1774
1838
  async getActiveFor(walletAddress) {
1775
1839
  return this.http.get(
1776
- `/subscription/active-for/${encodeURIComponent(walletAddress)}`
1840
+ `/subscription/active-for/${encodePathParam(walletAddress)}`
1777
1841
  );
1778
1842
  }
1779
1843
  /** Today's consumption breakdown by category for an app. Owner-only. */
1780
1844
  async getUsage(appId) {
1781
- return this.http.get(`/subscription/usage/${encodeURIComponent(appId)}`);
1845
+ return this.http.get(`/subscription/usage/${encodePathParam(appId)}`);
1782
1846
  }
1783
1847
  /**
1784
1848
  * Current usage with overage info: usage percent, grace status,
1785
1849
  * upgrade suggestion, projected days until limit. Owner-only.
1786
1850
  */
1787
1851
  async getUsageStatus(appId) {
1788
- return this.http.get(`/subscription/usage/status/${encodeURIComponent(appId)}`);
1852
+ return this.http.get(`/subscription/usage/status/${encodePathParam(appId)}`);
1789
1853
  }
1790
1854
  /**
1791
1855
  * Subscription balance deduction history. Pagination + ISO-8601 range
@@ -1799,7 +1863,7 @@ var SubscriptionClient = class {
1799
1863
  if (options?.to) params.append("to", options.to);
1800
1864
  const qs = params.toString();
1801
1865
  return this.http.get(
1802
- `/subscription/billing/${encodeURIComponent(appId)}${qs ? `?${qs}` : ""}`
1866
+ `/subscription/billing/${encodePathParam(appId)}${qs ? `?${qs}` : ""}`
1803
1867
  );
1804
1868
  }
1805
1869
  /**
@@ -1819,6 +1883,40 @@ var SubscriptionClient = class {
1819
1883
  }
1820
1884
  };
1821
1885
 
1886
+ // src/faucet/index.ts
1887
+ var FaucetClient = class {
1888
+ constructor(http) {
1889
+ this.http = http;
1890
+ }
1891
+ http;
1892
+ /**
1893
+ * Request a signing challenge for a recipient address. The returned
1894
+ * `message` must be signed by the key controlling `recipientAddress`.
1895
+ */
1896
+ async requestChallenge(chain, recipientAddress) {
1897
+ return this.http.post("/faucet/hsuite/challenge", { chain, recipientAddress });
1898
+ }
1899
+ /**
1900
+ * Submit a signed challenge to dispense HST. The result is a discriminated
1901
+ * union on `status` — branch on `'dispensed' | 'trustline_required' |
1902
+ * 'rate_limited'`. On `'trustline_required'`, set the returned trust line on
1903
+ * the recipient and re-dispense with a fresh challenge.
1904
+ */
1905
+ async dispense(req) {
1906
+ return this.http.post("/faucet/hsuite", req);
1907
+ }
1908
+ /**
1909
+ * Get today's dispense status for a recipient (e.g. amount already
1910
+ * dispensed today).
1911
+ */
1912
+ async getStatus(chain, recipientAddress) {
1913
+ const params = new URLSearchParams();
1914
+ params.append("chain", chain);
1915
+ params.append("recipientAddress", recipientAddress);
1916
+ return this.http.get(`/faucet/hsuite/status?${params.toString()}`);
1917
+ }
1918
+ };
1919
+
1822
1920
  // src/tss/index.ts
1823
1921
  var TSSClient = class {
1824
1922
  constructor(http) {
@@ -1826,80 +1924,105 @@ var TSSClient = class {
1826
1924
  }
1827
1925
  http;
1828
1926
  /**
1829
- * Create a multi-sig entity with TSS
1927
+ * Create a multi-sig entity via a synchronous DKG ceremony.
1928
+ *
1929
+ * @param options Entity-creation parameters (chain, threshold, participants).
1930
+ * @returns The created entity's identity (ids + group public keys).
1830
1931
  */
1831
1932
  async createEntity(options) {
1832
1933
  return this.http.post("/tss/entity/create", options);
1833
1934
  }
1834
1935
  /**
1835
- * Reshare keys when cluster membership changes.
1836
- * Redistributes secret shares WITHOUT changing public keys.
1936
+ * Reshare keys when cluster membership changes. Redistributes secret shares
1937
+ * WITHOUT changing public keys.
1938
+ *
1939
+ * @param request The new membership / threshold to reshare to.
1940
+ * @returns The reshare outcome.
1837
1941
  */
1838
1942
  async reshareCluster(request) {
1839
1943
  return this.http.post("/tss/cluster/reshare", request);
1840
1944
  }
1841
1945
  /**
1842
- * Get entity details by ID
1946
+ * Get entity details by id.
1947
+ *
1948
+ * @param entityId The entity id to look up.
1949
+ * @returns The entity's details.
1843
1950
  */
1844
1951
  async getEntity(entityId) {
1845
- return this.http.get(`/tss/entity/${encodeURIComponent(entityId)}`);
1952
+ return this.http.get(`/tss/entity/${encodePathParam(entityId)}`);
1846
1953
  }
1847
1954
  /**
1848
1955
  * Sign a transaction using MPC.
1849
1956
  *
1850
1957
  * Routes to `POST /api/v3/tss/hedera/sign-mpc`. Only `'hedera'` is wired
1851
- * server-side today (see
1852
- * `apps/smart-validator/src/tss/tss.controller.ts:279`); other chain
1853
- * signing paths run via their own controllers (XRPL multisig, Polkadot
1854
- * MPC) and are not exposed through this sub-client. The `chain` field is
1855
- * carried into the request body so the validator can log + route, but
1856
- * any non-`'hedera'` value will 404.
1958
+ * server-side; other chain signing paths run via their own controllers (XRPL
1959
+ * multisig, Polkadot MPC) and are not exposed through this sub-client. The
1960
+ * `chain` field is carried into the request body so the validator can log +
1961
+ * route, but any non-`'hedera'` value will 404.
1962
+ *
1963
+ * @param request The MPC signing request; `chain` is forced to `'hedera'`.
1964
+ * @returns The MPC signing result.
1857
1965
  */
1858
1966
  async signMPC(request) {
1859
1967
  const chain = "hedera";
1860
1968
  return this.http.post(`/tss/${chain}/sign-mpc`, { ...request, chain });
1861
1969
  }
1862
1970
  /**
1863
- * Get known validators and their public keys
1971
+ * Get known validators and their public keys.
1972
+ *
1973
+ * @returns The validator list with public keys.
1864
1974
  */
1865
1975
  async getValidators() {
1866
1976
  return this.http.get("/tss/validators");
1867
1977
  }
1868
1978
  /**
1869
- * Force announcement of this node's public key
1979
+ * Force announcement of this node's public key.
1980
+ *
1981
+ * @returns Whether the announcement was accepted, plus a status message.
1870
1982
  */
1871
1983
  async announceKey() {
1872
1984
  return this.http.post("/tss/announce", {});
1873
1985
  }
1874
1986
  /**
1875
- * Get TSS statistics
1987
+ * Get TSS statistics.
1988
+ *
1989
+ * @returns Aggregate TSS statistics.
1876
1990
  */
1877
1991
  async getStats() {
1878
1992
  return this.http.get("/tss/stats");
1879
1993
  }
1880
1994
  /**
1881
- * List all TSS entities
1995
+ * List all TSS entities.
1996
+ *
1997
+ * @returns The full entity list.
1882
1998
  */
1883
1999
  async listEntities() {
1884
2000
  return this.http.get("/tss/entities");
1885
2001
  }
1886
2002
  /**
1887
- * TSS health check
2003
+ * TSS health check.
2004
+ *
2005
+ * @returns The TSS subsystem health report.
1888
2006
  */
1889
2007
  async getHealth() {
1890
2008
  return this.http.get("/tss/health");
1891
2009
  }
1892
2010
  /**
1893
- * List DKG ceremonies and their statistics
2011
+ * List DKG ceremonies and their statistics.
2012
+ *
2013
+ * @returns The ceremony list.
1894
2014
  */
1895
2015
  async listCeremonies() {
1896
2016
  return this.http.get("/tss/multisig/ceremonies");
1897
2017
  }
1898
2018
  /**
1899
- * Get multi-sig transaction status by transaction ID
2019
+ * Get multi-sig transaction status by transaction id.
2020
+ *
2021
+ * @param txId The multi-sig transaction id.
2022
+ * @returns The current status of that transaction.
1900
2023
  */
1901
2024
  async getMultiSigStatus(txId) {
1902
- return this.http.get(`/tss/multisig/transactions/${encodeURIComponent(txId)}`);
2025
+ return this.http.get(`/tss/multisig/transactions/${encodePathParam(txId)}`);
1903
2026
  }
1904
2027
  /**
1905
2028
  * Async-job variant of {@link createEntity}.
@@ -1907,6 +2030,9 @@ var TSSClient = class {
1907
2030
  * Server returns 202 + `{ jobId, statusUrl, status: 'pending' }` immediately;
1908
2031
  * the DKG ceremony runs in the background. Poll {@link getJob} until the
1909
2032
  * status reaches `'success'` or `'failed'`.
2033
+ *
2034
+ * @param options Entity-creation parameters.
2035
+ * @returns A job descriptor (`jobId`, `statusUrl`, initial status).
1910
2036
  */
1911
2037
  async createEntityAsync(options) {
1912
2038
  return this.http.post("/tss/entity/create/async", options);
@@ -1914,6 +2040,9 @@ var TSSClient = class {
1914
2040
  /**
1915
2041
  * Async-job variant of {@link reshareCluster}. Returns 202 + a polling
1916
2042
  * descriptor; resharing runs in the background.
2043
+ *
2044
+ * @param request The new membership / threshold to reshare to.
2045
+ * @returns A job descriptor to poll via {@link getJob}.
1917
2046
  */
1918
2047
  async reshareClusterAsync(request) {
1919
2048
  return this.http.post("/tss/cluster/reshare/async", request);
@@ -1921,9 +2050,12 @@ var TSSClient = class {
1921
2050
  /**
1922
2051
  * Poll the status of an async TSS-ceremony job kicked off via
1923
2052
  * {@link createEntityAsync} or {@link reshareClusterAsync}.
2053
+ *
2054
+ * @param jobId The job id returned by the async kickoff call.
2055
+ * @returns The job's current status (and result once terminal).
1924
2056
  */
1925
2057
  async getJob(jobId) {
1926
- return this.http.get(`/tss/jobs/${encodeURIComponent(jobId)}`);
2058
+ return this.http.get(`/tss/jobs/${encodePathParam(jobId)}`);
1927
2059
  }
1928
2060
  /**
1929
2061
  * Sign a hex payload as smart-app entity `appId` via the cluster's TSS
@@ -1933,9 +2065,13 @@ var TSSClient = class {
1933
2065
  * Payload constraints (enforced server-side):
1934
2066
  * - even-length lowercase hex
1935
2067
  * - ≥32 bytes, ≤8KB
2068
+ *
2069
+ * @param appId The smart-app entity id to sign as.
2070
+ * @param request The hex payload to sign.
2071
+ * @returns The aggregate signature over the payload.
1936
2072
  */
1937
2073
  async signForApp(appId, request) {
1938
- return this.http.post(`/tss/entity/${encodeURIComponent(appId)}/sign`, request);
2074
+ return this.http.post(`/tss/entity/${encodePathParam(appId)}/sign`, request);
1939
2075
  }
1940
2076
  };
1941
2077
 
@@ -1960,31 +2096,31 @@ var IPFSClient = class {
1960
2096
  * Pin content by CID to ensure it persists
1961
2097
  */
1962
2098
  async pin(cid) {
1963
- return this.http.post(`/ipfs/pin/${encodeURIComponent(cid)}`, {});
2099
+ return this.http.post(`/ipfs/pin/${encodePathParam(cid)}`, {});
1964
2100
  }
1965
2101
  /**
1966
2102
  * Unpin content by CID
1967
2103
  */
1968
2104
  async unpin(cid) {
1969
- return this.http.delete(`/ipfs/unpin/${encodeURIComponent(cid)}`);
2105
+ return this.http.delete(`/ipfs/unpin/${encodePathParam(cid)}`);
1970
2106
  }
1971
2107
  /**
1972
2108
  * Get a file by CID
1973
2109
  */
1974
2110
  async getFile(cid) {
1975
- return this.http.get(`/ipfs/file/${encodeURIComponent(cid)}`);
2111
+ return this.http.get(`/ipfs/file/${encodePathParam(cid)}`);
1976
2112
  }
1977
2113
  /**
1978
2114
  * Get raw content by CID
1979
2115
  */
1980
2116
  async getContent(cid) {
1981
- return this.http.get(`/ipfs/${encodeURIComponent(cid)}`);
2117
+ return this.http.get(`/ipfs/${encodePathParam(cid)}`);
1982
2118
  }
1983
2119
  /**
1984
2120
  * Get file metadata by CID
1985
2121
  */
1986
2122
  async getMetadata(cid) {
1987
- return this.http.get(`/ipfs/metadata/${encodeURIComponent(cid)}`);
2123
+ return this.http.get(`/ipfs/metadata/${encodePathParam(cid)}`);
1988
2124
  }
1989
2125
  /**
1990
2126
  * List all pinned content
@@ -2042,7 +2178,7 @@ var HederaTssClient = class {
2042
2178
  var HederaTransactionsClient = class {
2043
2179
  constructor(http, tssHttp) {
2044
2180
  this.http = http;
2045
- this.tss = new HederaTssClient(tssHttp ?? http);
2181
+ this.tss = new HederaTssClient(tssHttp);
2046
2182
  }
2047
2183
  http;
2048
2184
  /**
@@ -2053,10 +2189,10 @@ var HederaTransactionsClient = class {
2053
2189
  * - `client.hedera.tss.createTopic(...)` makes the cluster sign+submit in one call.
2054
2190
  *
2055
2191
  * `tssHttp` is the validator's `/api/v3`-rooted HTTP client (different from
2056
- * the `/api/transactions` one this class uses for prepare paths). When the
2057
- * legacy single-arg constructor is used (no tssHttp passed), `tss` falls
2058
- * back to the same `http` instance for backwards compatibility — callers
2059
- * outside `SmartEngineClient` rarely use the TSS surface.
2192
+ * the `/api/transactions` one this class uses for prepare paths). Both
2193
+ * clients are required the previous single-arg fallback to `http` was
2194
+ * unreachable through `SmartEngineClient` (the only call site always
2195
+ * passes both).
2060
2196
  */
2061
2197
  tss;
2062
2198
  /** Prepare an HCS topic creation transaction. */
@@ -2237,7 +2373,7 @@ var SnapshotsClient = class {
2237
2373
  */
2238
2374
  async generate(tokenId, options) {
2239
2375
  return this.http.post(
2240
- `/snapshots/generate/${encodeURIComponent(tokenId)}`,
2376
+ `/snapshots/generate/${encodePathParam(tokenId)}`,
2241
2377
  options || {}
2242
2378
  );
2243
2379
  }
@@ -2245,7 +2381,7 @@ var SnapshotsClient = class {
2245
2381
  * Get snapshot details by ID
2246
2382
  */
2247
2383
  async get(snapshotId) {
2248
- return this.http.get(`/snapshots/${encodeURIComponent(snapshotId)}`);
2384
+ return this.http.get(`/snapshots/${encodePathParam(snapshotId)}`);
2249
2385
  }
2250
2386
  /**
2251
2387
  * List snapshots for a token
@@ -2256,7 +2392,7 @@ var SnapshotsClient = class {
2256
2392
  if (pagination?.limit !== void 0) params.set("limit", String(pagination.limit));
2257
2393
  const qs = params.toString();
2258
2394
  return this.http.get(
2259
- `/snapshots/token/${encodeURIComponent(tokenId)}${qs ? `?${qs}` : ""}`
2395
+ `/snapshots/token/${encodePathParam(tokenId)}${qs ? `?${qs}` : ""}`
2260
2396
  );
2261
2397
  }
2262
2398
  /**
@@ -2268,7 +2404,7 @@ var SnapshotsClient = class {
2268
2404
  async download(snapshotId, format) {
2269
2405
  const params = format ? `?format=${encodeURIComponent(format)}` : "";
2270
2406
  return this.http.get(
2271
- `/snapshots/${encodeURIComponent(snapshotId)}/download${params}`
2407
+ `/snapshots/${encodePathParam(snapshotId)}/download${params}`
2272
2408
  );
2273
2409
  }
2274
2410
  };
@@ -2451,20 +2587,20 @@ var SettlementClient = class {
2451
2587
  * Get the current status of a settlement by ID.
2452
2588
  */
2453
2589
  async getStatus(settlementId) {
2454
- return this.http.get(`/settlement/${encodeURIComponent(settlementId)}/status`);
2590
+ return this.http.get(`/settlement/${encodePathParam(settlementId)}/status`);
2455
2591
  }
2456
2592
  /**
2457
2593
  * Confirm that XRP has landed on the destination address.
2458
2594
  * Advances the settlement to the next processing step.
2459
2595
  */
2460
2596
  async confirmXrpLanded(settlementId) {
2461
- return this.http.post(`/settlement/${encodeURIComponent(settlementId)}/confirm-xrp`, {});
2597
+ return this.http.post(`/settlement/${encodePathParam(settlementId)}/confirm-xrp`, {});
2462
2598
  }
2463
2599
  /**
2464
2600
  * Get settlement history for a given entity.
2465
2601
  */
2466
2602
  async getHistory(entityId) {
2467
- return this.http.get(`/settlement/history/${encodeURIComponent(entityId)}`);
2603
+ return this.http.get(`/settlement/history/${encodePathParam(entityId)}`);
2468
2604
  }
2469
2605
  };
2470
2606
 
@@ -2507,39 +2643,39 @@ var DaoDashboardClient = class {
2507
2643
  /** Aggregated dashboard counters for a user's governance home screen. */
2508
2644
  async getStats(userAddress) {
2509
2645
  return this.http.get(
2510
- `/dao/dashboard/stats/${encodeURIComponent(userAddress)}`
2646
+ `/dao/dashboard/stats/${encodePathParam(userAddress)}`
2511
2647
  );
2512
2648
  }
2513
2649
  /** Active proposals across every DAO the user belongs to, with vote state. */
2514
2650
  async getActiveProposals(userAddress) {
2515
2651
  return this.http.get(
2516
- `/dao/dashboard/active-proposals/${encodeURIComponent(userAddress)}`
2652
+ `/dao/dashboard/active-proposals/${encodePathParam(userAddress)}`
2517
2653
  );
2518
2654
  }
2519
2655
  /** Paginated vote history across DAOs. */
2520
2656
  async getVoteHistory(userAddress, opts = {}) {
2521
2657
  const qs = buildQuery({ limit: opts.limit, offset: opts.offset });
2522
2658
  return this.http.get(
2523
- `/dao/dashboard/vote-history/${encodeURIComponent(userAddress)}${qs}`
2659
+ `/dao/dashboard/vote-history/${encodePathParam(userAddress)}${qs}`
2524
2660
  );
2525
2661
  }
2526
2662
  /** Activity feed (proposal-created/vote-cast/etc.) across user's DAOs. */
2527
2663
  async getActivity(userAddress, opts = {}) {
2528
2664
  const qs = buildQuery({ limit: opts.limit, daoId: opts.daoId });
2529
2665
  return this.http.get(
2530
- `/dao/dashboard/activity/${encodeURIComponent(userAddress)}${qs}`
2666
+ `/dao/dashboard/activity/${encodePathParam(userAddress)}${qs}`
2531
2667
  );
2532
2668
  }
2533
2669
  /** Items the user must act on (sign prepared messages, claim NFTs, …). */
2534
2670
  async getPendingActions(userAddress) {
2535
2671
  return this.http.get(
2536
- `/dao/dashboard/pending-actions/${encodeURIComponent(userAddress)}`
2672
+ `/dao/dashboard/pending-actions/${encodePathParam(userAddress)}`
2537
2673
  );
2538
2674
  }
2539
2675
  /** Governance impact metrics — weight delivered, success rate, streak. */
2540
2676
  async getImpact(userAddress) {
2541
2677
  return this.http.get(
2542
- `/dao/dashboard/impact/${encodeURIComponent(userAddress)}`
2678
+ `/dao/dashboard/impact/${encodePathParam(userAddress)}`
2543
2679
  );
2544
2680
  }
2545
2681
  };
@@ -2555,13 +2691,13 @@ function buildQuery2(params) {
2555
2691
  return qs ? `?${qs}` : "";
2556
2692
  }
2557
2693
  function encodeDaoId(daoId) {
2558
- return encodeURIComponent(daoId);
2694
+ return encodePathParam(daoId);
2559
2695
  }
2560
2696
  function encodeProposalId(proposalId) {
2561
- return encodeURIComponent(proposalId);
2697
+ return encodePathParam(proposalId);
2562
2698
  }
2563
2699
  function encodeAddress(address) {
2564
- return encodeURIComponent(address);
2700
+ return encodePathParam(address);
2565
2701
  }
2566
2702
  var DaoClient = class {
2567
2703
  constructor(http) {
@@ -2767,7 +2903,7 @@ var AgentsClient = class {
2767
2903
  }
2768
2904
  /** Get agent details */
2769
2905
  async get(agentId) {
2770
- return this.http.get(`/api/agents/${encodeURIComponent(agentId)}`);
2906
+ return this.http.get(`/api/agents/${encodePathParam(agentId)}`);
2771
2907
  }
2772
2908
  /** List all agents */
2773
2909
  async list() {
@@ -2779,33 +2915,33 @@ var AgentsClient = class {
2779
2915
  * the caller is expected to sign and submit the prepared bytes.
2780
2916
  */
2781
2917
  async fund(agentId, request) {
2782
- return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/fund`, request);
2918
+ return this.http.post(`/api/agents/${encodePathParam(agentId)}/fund`, request);
2783
2919
  }
2784
2920
  /**
2785
2921
  * Execute a trade (agent-wallet OR owner). Returns a
2786
2922
  * `PreparedTransactionResponse` wrapped in a `success: true` envelope.
2787
2923
  */
2788
2924
  async trade(agentId, request) {
2789
- return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/trade`, request);
2925
+ return this.http.post(`/api/agents/${encodePathParam(agentId)}/trade`, request);
2790
2926
  }
2791
2927
  /**
2792
2928
  * Withdraw from agent treasury (owner-only). Returns a
2793
2929
  * `PreparedTransactionResponse` wrapped in a `success: true` envelope.
2794
2930
  */
2795
2931
  async withdraw(agentId, request) {
2796
- return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/withdraw`, request);
2932
+ return this.http.post(`/api/agents/${encodePathParam(agentId)}/withdraw`, request);
2797
2933
  }
2798
2934
  /** Pause an agent */
2799
2935
  async pause(agentId) {
2800
- return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/pause`, {});
2936
+ return this.http.post(`/api/agents/${encodePathParam(agentId)}/pause`, {});
2801
2937
  }
2802
2938
  /** Resume a paused agent */
2803
2939
  async resume(agentId) {
2804
- return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/resume`, {});
2940
+ return this.http.post(`/api/agents/${encodePathParam(agentId)}/resume`, {});
2805
2941
  }
2806
2942
  /** Revoke an agent (permanent) */
2807
2943
  async revoke(agentId) {
2808
- return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/revoke`, {});
2944
+ return this.http.post(`/api/agents/${encodePathParam(agentId)}/revoke`, {});
2809
2945
  }
2810
2946
  /**
2811
2947
  * Update agent rules.
@@ -2816,23 +2952,23 @@ var AgentsClient = class {
2816
2952
  * the verb mismatch.
2817
2953
  */
2818
2954
  async updateRules(agentId, rules) {
2819
- return this.http.patch(`/api/agents/${encodeURIComponent(agentId)}/rules`, rules);
2955
+ return this.http.patch(`/api/agents/${encodePathParam(agentId)}/rules`, rules);
2820
2956
  }
2821
2957
  /** Get agent events */
2822
2958
  async getEvents(agentId) {
2823
- return this.http.get(`/api/agents/${encodeURIComponent(agentId)}/events`);
2959
+ return this.http.get(`/api/agents/${encodePathParam(agentId)}/events`);
2824
2960
  }
2825
2961
  /** Get agent balances across chains */
2826
2962
  async getBalances(agentId) {
2827
- return this.http.get(`/api/agents/${encodeURIComponent(agentId)}/balances`);
2963
+ return this.http.get(`/api/agents/${encodePathParam(agentId)}/balances`);
2828
2964
  }
2829
2965
  /** Approve a pending agent operation */
2830
2966
  async approve(agentId, operationId) {
2831
- return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/approve/${encodeURIComponent(operationId)}`, {});
2967
+ return this.http.post(`/api/agents/${encodePathParam(agentId)}/approve/${encodePathParam(operationId)}`, {});
2832
2968
  }
2833
2969
  /** Reject a pending agent operation */
2834
2970
  async reject(agentId, operationId) {
2835
- return this.http.post(`/api/agents/${encodeURIComponent(agentId)}/reject/${encodeURIComponent(operationId)}`, {});
2971
+ return this.http.post(`/api/agents/${encodePathParam(agentId)}/reject/${encodePathParam(operationId)}`, {});
2836
2972
  }
2837
2973
  };
2838
2974
 
@@ -2871,7 +3007,7 @@ var DeploymentClient = class {
2871
3007
  */
2872
3008
  async uploadFrontend(appId, bundle, filename = "bundle.tar.gz") {
2873
3009
  return this.http.upload(
2874
- `/api/deployment/apps/${encodeURIComponent(appId)}/frontend`,
3010
+ `/api/deployment/apps/${encodePathParam(appId)}/frontend`,
2875
3011
  bundle,
2876
3012
  filename,
2877
3013
  void 0,
@@ -2885,20 +3021,20 @@ var DeploymentClient = class {
2885
3021
  * until `runtime.runtimeState === 'RUNNING'` for the URL to be live.
2886
3022
  */
2887
3023
  async deploy(appId, request) {
2888
- return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/deploy`, request);
3024
+ return this.http.post(`/api/deployment/apps/${encodePathParam(appId)}/deploy`, request);
2889
3025
  }
2890
3026
  /**
2891
3027
  * Roll back to a previously-deployed image tag (must exist in
2892
3028
  * `runtime.deploymentHistory[]`).
2893
3029
  */
2894
3030
  async rollback(appId, request) {
2895
- return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/rollback`, request);
3031
+ return this.http.post(`/api/deployment/apps/${encodePathParam(appId)}/rollback`, request);
2896
3032
  }
2897
3033
  /**
2898
3034
  * Live combined lifecycle + runtime status of an app.
2899
3035
  */
2900
3036
  async status(appId) {
2901
- return this.http.get(`/api/deployment/apps/${encodeURIComponent(appId)}/status`);
3037
+ return this.http.get(`/api/deployment/apps/${encodePathParam(appId)}/status`);
2902
3038
  }
2903
3039
  /**
2904
3040
  * List all deployed apps for the authenticated developer.
@@ -2910,31 +3046,35 @@ var DeploymentClient = class {
2910
3046
  * Get app details.
2911
3047
  */
2912
3048
  async get(appId) {
2913
- return this.http.get(`/api/deployment/apps/${encodeURIComponent(appId)}`);
3049
+ return this.http.get(`/api/deployment/apps/${encodePathParam(appId)}`);
2914
3050
  }
2915
3051
  /**
2916
- * Update app configuration. Runtime effect lands in PR-H.
3052
+ * Update app configuration.
3053
+ *
3054
+ * @param appId - The app to update.
3055
+ * @param updates - Partial deploy-request fields to apply.
3056
+ * @returns The updated app info.
2917
3057
  */
2918
3058
  async update(appId, updates) {
2919
- return this.http.put(`/api/deployment/apps/${encodeURIComponent(appId)}`, updates);
3059
+ return this.http.put(`/api/deployment/apps/${encodePathParam(appId)}`, updates);
2920
3060
  }
2921
3061
  /**
2922
- * Delete an app. Runtime effect (namespace teardown) lands in PR-H.
3062
+ * Delete an app (runtime effect: namespace teardown).
2923
3063
  */
2924
3064
  async delete(appId) {
2925
- return this.http.delete(`/api/deployment/apps/${encodeURIComponent(appId)}`);
3065
+ return this.http.delete(`/api/deployment/apps/${encodePathParam(appId)}`);
2926
3066
  }
2927
3067
  /**
2928
- * Suspend an app. Runtime effect (scale to zero) lands in PR-H.
3068
+ * Suspend an app (runtime effect: scale to zero).
2929
3069
  */
2930
3070
  async suspend(appId) {
2931
- return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/suspend`, {});
3071
+ return this.http.post(`/api/deployment/apps/${encodePathParam(appId)}/suspend`, {});
2932
3072
  }
2933
3073
  /**
2934
- * Resume a suspended app. Runtime effect (scale back up) lands in PR-H.
3074
+ * Resume a suspended app (runtime effect: scale back up).
2935
3075
  */
2936
3076
  async resume(appId) {
2937
- return this.http.post(`/api/deployment/apps/${encodeURIComponent(appId)}/resume`, {});
3077
+ return this.http.post(`/api/deployment/apps/${encodePathParam(appId)}/resume`, {});
2938
3078
  }
2939
3079
  /**
2940
3080
  * Get deployment statistics.
@@ -2961,7 +3101,7 @@ var DeploymentClient = class {
2961
3101
  * CGNAT / cloud metadata destinations (SSRF guard).
2962
3102
  */
2963
3103
  async setWebhook(appId, webhookUrl) {
2964
- return this.http.put(`/api/deployment/apps/${encodeURIComponent(appId)}/webhook`, {
3104
+ return this.http.put(`/api/deployment/apps/${encodePathParam(appId)}/webhook`, {
2965
3105
  webhookUrl
2966
3106
  });
2967
3107
  }
@@ -2977,10 +3117,10 @@ var DeploymentClient = class {
2977
3117
  * exposition format.
2978
3118
  */
2979
3119
  async getMetrics(appId) {
2980
- return this.http.getText(`/api/deployment/apps/${encodeURIComponent(appId)}/metrics`);
3120
+ return this.http.getText(`/api/deployment/apps/${encodePathParam(appId)}/metrics`);
2981
3121
  }
2982
3122
  /**
2983
- * Rotate the smart-app's tenant-secret KEK (ADR-011 Phase 6).
3123
+ * Rotate the smart-app's tenant-secret KEK.
2984
3124
  *
2985
3125
  * Re-encrypts every `runtime.env` envelope at the new KEK version
2986
3126
  * transparently. Previous versions remain valid until explicitly
@@ -2988,12 +3128,12 @@ var DeploymentClient = class {
2988
3128
  */
2989
3129
  async rotateKek(appId) {
2990
3130
  return this.http.post(
2991
- `/api/deployment/apps/${encodeURIComponent(appId)}/credentials/rotate-kek`,
3131
+ `/api/deployment/apps/${encodePathParam(appId)}/credentials/rotate-kek`,
2992
3132
  {}
2993
3133
  );
2994
3134
  }
2995
3135
  /**
2996
- * Revoke a tenant-secret KEK version (ADR-011 Phase 6 emergency burn).
3136
+ * Revoke a tenant-secret KEK version (emergency burn).
2997
3137
  *
2998
3138
  * Envelopes at the revoked version become operationally dead —
2999
3139
  * decryption inside the smart-app pod fails. Owner-only and
@@ -3002,7 +3142,7 @@ var DeploymentClient = class {
3002
3142
  */
3003
3143
  async revokeKek(appId, version) {
3004
3144
  return this.http.post(
3005
- `/api/deployment/apps/${encodeURIComponent(appId)}/credentials/revoke-kek`,
3145
+ `/api/deployment/apps/${encodePathParam(appId)}/credentials/revoke-kek`,
3006
3146
  { version }
3007
3147
  );
3008
3148
  }
@@ -3182,16 +3322,6 @@ var TokensClient = class {
3182
3322
  async getMigrationsForToken(tokenId) {
3183
3323
  return this.http.get(`/tokens/${encodePathParam(tokenId)}/migrations`);
3184
3324
  }
3185
- /**
3186
- * Get token details. NOTE: collides with `client.getTokenInfo(...)` —
3187
- * see the class JSDoc above for the route-order details. Prefer
3188
- * `client.getTokenInfo(...)` unless you have a reason to call this one.
3189
- */
3190
- async getDetails(chain, tokenId) {
3191
- return this.http.get(
3192
- `/tokens/${encodePathParam(chain)}/${encodePathParam(tokenId)}`
3193
- );
3194
- }
3195
3325
  };
3196
3326
 
3197
3327
  // src/operator/operator-client.ts
@@ -3226,6 +3356,8 @@ var SmartEngineClient = class _SmartEngineClient {
3226
3356
  // ========== Sub-Clients ==========
3227
3357
  /** Application subscription management */
3228
3358
  subscription;
3359
+ /** Testnet HST faucet (challenge -> sign -> dispense) */
3360
+ faucet;
3229
3361
  /** Threshold Signature Scheme — chain-agnostic MPC operations */
3230
3362
  tss;
3231
3363
  /** IPFS decentralized file storage */
@@ -3284,6 +3416,7 @@ var SmartEngineClient = class _SmartEngineClient {
3284
3416
  timeout: config.timeout
3285
3417
  });
3286
3418
  this.subscription = new SubscriptionClient(this.http);
3419
+ this.faucet = new FaucetClient(this.http);
3287
3420
  this.tss = new TSSClient(this.http);
3288
3421
  this.ipfs = new IPFSClient(this.http);
3289
3422
  this.transactions = new TransactionsClient(this.txHttp);
@@ -3339,13 +3472,17 @@ var SmartEngineClient = class _SmartEngineClient {
3339
3472
  });
3340
3473
  }
3341
3474
  /**
3342
- * Connect to the smart-engines network with auto-discovery and authentication
3475
+ * Connect to the smart-engines network with auto-discovery and authentication.
3343
3476
  *
3344
- * This method:
3345
- * 1. Discovers validators via HCS registry topic
3346
- * 2. Selects a random validator with API endpoint
3347
- * 3. Authenticates with Web3-style challenge-response
3348
- * 4. Returns a configured client ready to use
3477
+ * Steps:
3478
+ * 1. Discovers validators via the HCS registry topic.
3479
+ * 2. Selects a random validator with an API endpoint.
3480
+ * 3. Authenticates with Web3-style challenge-response.
3481
+ * 4. Returns a configured client ready to use.
3482
+ *
3483
+ * @param config - Network, registry topic, and auth signer config.
3484
+ * @returns The configured client, the chosen validator, and the auth session.
3485
+ * @throws SmartEngineError 503 if no validator with an API endpoint is found.
3349
3486
  */
3350
3487
  static async connectToNetwork(config) {
3351
3488
  const allowInsecure = config.allowInsecure ?? false;
@@ -3382,18 +3519,22 @@ var SmartEngineClient = class _SmartEngineClient {
3382
3519
  return { client, validator, session };
3383
3520
  }
3384
3521
  /**
3385
- * Connect to the smart-engines network via the **service-registry**
3386
- * (PR-1 of the cluster-discovery arc). Preferred over
3387
- * {@link connectToNetwork} once the validator pods in the target network
3388
- * have published their cluster endpoints the SDK auto-balances across
3389
- * the active cluster set and rides permissionless cluster join/leave
3390
- * without code edits.
3522
+ * Connect to the smart-engines network via the **service-registry**.
3523
+ * Preferred over {@link connectToNetwork} once the validator pods in the
3524
+ * target network have published their cluster endpoints the SDK
3525
+ * auto-balances across the active cluster set and rides permissionless
3526
+ * cluster join/leave without code edits.
3391
3527
  *
3392
- * Fallback ladder (per `docs/ops/HANDOFF-service-registry-distribution-layer.md` §6):
3528
+ * Resolution ladder:
3393
3529
  * 1. HTTP fetch `/api/v3/discovery/clusters` from each bootstrap seed.
3394
3530
  * 2. (Optional) HCS trust-anchor membership cross-check.
3395
3531
  * 3. Random-pick over the verified set.
3396
3532
  *
3533
+ * @param config - Seed + auth config. See {@link ClusterConnectionConfig}.
3534
+ * @returns The configured client, the selected cluster, and the auth session.
3535
+ * @throws SmartEngineError 400 if neither `bootstrap` nor `network` is given.
3536
+ * @throws SmartEngineError 503 if no active cluster can be reached.
3537
+ *
3397
3538
  * @example Zero-config (recommended for smart-app callers)
3398
3539
  * ```ts
3399
3540
  * const { client, cluster, session } = await SmartEngineClient.connectToCluster({
@@ -3418,30 +3559,25 @@ var SmartEngineClient = class _SmartEngineClient {
3418
3559
  */
3419
3560
  static async connectToCluster(config) {
3420
3561
  const allowInsecure = config.allowInsecure ?? false;
3421
- const bootstrap = config.bootstrap ? [...config.bootstrap] : [...resolveNetwork(config.network).bootstrap];
3422
- if (bootstrap.length === 0) {
3423
- throw new SmartEngineError(
3424
- "connectToCluster requires either a non-empty `bootstrap` list or a known `network` name.",
3425
- 400
3426
- );
3427
- }
3428
- const discovery = new ClusterDiscoveryClient({
3429
- bootstrap,
3562
+ const resolved = await resolveClusterEndpoint({
3563
+ bootstrap: config.bootstrap,
3564
+ network: config.network,
3430
3565
  allowInsecure,
3431
- trustAnchor: config.trustAnchor ? {
3432
- network: config.trustAnchor.network,
3433
- registryTopicId: config.trustAnchor.registryTopicId,
3434
- mirrorNodeUrl: config.trustAnchor.mirrorNodeUrl,
3435
- allowInsecure
3436
- } : void 0
3566
+ trustAnchor: config.trustAnchor
3437
3567
  });
3438
- const cluster = await discovery.getRandomCluster();
3439
- if (!cluster) {
3568
+ if (!resolved.ok) {
3569
+ if (resolved.reason === "no-seeds") {
3570
+ throw new SmartEngineError(
3571
+ "connectToCluster requires either a non-empty `bootstrap` list or a known `network` name.",
3572
+ 400
3573
+ );
3574
+ }
3440
3575
  throw new SmartEngineError(
3441
3576
  "No active clusters available via bootstrap seeds. Check bootstrap URLs and network reachability.",
3442
3577
  503
3443
3578
  );
3444
3579
  }
3580
+ const cluster = resolved.cluster;
3445
3581
  const gatewayUrl = cluster.endpoints.gatewayUrl;
3446
3582
  validateClientUrl(gatewayUrl, allowInsecure);
3447
3583
  const auth = new ValidatorAuthClient({ security: { allowInsecure } });
@@ -3466,7 +3602,7 @@ var SmartEngineClient = class _SmartEngineClient {
3466
3602
  }
3467
3603
  /** Check if client has an auth token */
3468
3604
  isAuthenticated() {
3469
- return !!this.http.authToken;
3605
+ return this.http.getAuthToken() !== void 0;
3470
3606
  }
3471
3607
  /**
3472
3608
  * Get HTTP resilience health information
@@ -3527,17 +3663,11 @@ var SmartEngineClient = class _SmartEngineClient {
3527
3663
  return this.http.post("/tokens/mint", validated);
3528
3664
  }
3529
3665
  /**
3530
- * Get token information.
3666
+ * Get token information for a token on the given chain.
3531
3667
  *
3532
- * Route `GET /api/v3/tokens/:chain/:tokenId` is registered twice on the
3533
- * validator: by `ValidatorController` at
3534
- * `apps/smart-validator/src/validator.controller.ts:497` and by
3535
- * `TokenMigrationController` at
3536
- * `apps/smart-validator/src/token-migration/token-migration.controller.ts:173`.
3537
- * Nest resolves routes in `controllers: [...]` order — `ValidatorController`
3538
- * is registered first (`apps/smart-validator/src/smart-validator.module.ts:1222`),
3539
- * so `multiChain.getTokenInfo(chain, tokenId)` wins and the
3540
- * token-migration handler is unreachable via this path.
3668
+ * @param chain - Chain identifier (e.g. `'hedera'`, `'xrpl'`).
3669
+ * @param tokenId - Chain-native token identifier.
3670
+ * @returns Token metadata and supply information.
3541
3671
  */
3542
3672
  async getTokenInfo(chain, tokenId) {
3543
3673
  return this.http.get(`/tokens/${encodePathParam(chain)}/${encodePathParam(tokenId)}`);
@@ -3697,7 +3827,7 @@ var RoutingClient = class {
3697
3827
  }
3698
3828
  /** Unregister a host. */
3699
3829
  async unregisterHost(hostId) {
3700
- return this.http.delete(`/routing/hosts/${encodeURIComponent(hostId)}`);
3830
+ return this.http.delete(`/routing/hosts/${encodePathParam(hostId)}`);
3701
3831
  }
3702
3832
  /** Get all registered hosts. */
3703
3833
  async getAllHosts() {
@@ -3709,19 +3839,19 @@ var RoutingClient = class {
3709
3839
  }
3710
3840
  /** Get a specific host by ID. */
3711
3841
  async getHost(hostId) {
3712
- return this.http.get(`/routing/hosts/${encodeURIComponent(hostId)}`);
3842
+ return this.http.get(`/routing/hosts/${encodePathParam(hostId)}`);
3713
3843
  }
3714
3844
  /** Trigger host re-verification. */
3715
3845
  async verifyHost(hostId) {
3716
- return this.http.post(`/routing/hosts/${encodeURIComponent(hostId)}/verify`, {});
3846
+ return this.http.post(`/routing/hosts/${encodePathParam(hostId)}/verify`, {});
3717
3847
  }
3718
3848
  /** Set routing configuration for an app. */
3719
3849
  async setRoutingConfig(appId, config) {
3720
- return this.http.put(`/routing/config/${encodeURIComponent(appId)}`, config);
3850
+ return this.http.put(`/routing/config/${encodePathParam(appId)}`, config);
3721
3851
  }
3722
3852
  /** Get routing configuration for an app. */
3723
3853
  async getRoutingConfig(appId) {
3724
- return this.http.get(`/routing/config/${encodeURIComponent(appId)}`);
3854
+ return this.http.get(`/routing/config/${encodePathParam(appId)}`);
3725
3855
  }
3726
3856
  /** Proxy a request through the gateway. */
3727
3857
  async proxyRequest(request) {
@@ -3737,7 +3867,7 @@ var RoutingClient = class {
3737
3867
  * field; treat `appId` as the success signal.
3738
3868
  */
3739
3869
  async mapDomainToApp(domain, appId) {
3740
- return this.http.post(`/routing/domains/${encodeURIComponent(domain)}/map`, { appId });
3870
+ return this.http.post(`/routing/domains/${encodePathParam(domain)}/map`, { appId });
3741
3871
  }
3742
3872
  };
3743
3873
 
@@ -3753,11 +3883,11 @@ var DomainsClient = class {
3753
3883
  }
3754
3884
  /** Check domain availability */
3755
3885
  async checkAvailability(domain) {
3756
- return this.http.get(`/domains/check/${encodeURIComponent(domain)}`);
3886
+ return this.http.get(`/domains/check/${encodePathParam(domain)}`);
3757
3887
  }
3758
3888
  /** Get domain information */
3759
3889
  async getInfo(domain) {
3760
- return this.http.get(`/domains/${encodeURIComponent(domain)}`);
3890
+ return this.http.get(`/domains/${encodePathParam(domain)}`);
3761
3891
  }
3762
3892
  /** List domains, optionally filtered by owner */
3763
3893
  async list(owner) {
@@ -3766,51 +3896,50 @@ var DomainsClient = class {
3766
3896
  }
3767
3897
  /**
3768
3898
  * Generate a verification token. Server accepts one of `dns-txt`,
3769
- * `dns-cname`, `http-file`, `email` (see controller Swagger enum at
3770
- * `apps/smart-gateway/src/domains/domains.controller.ts:226-234`).
3899
+ * `dns-cname`, `http-file`, `email`.
3771
3900
  */
3772
3901
  async generateVerificationToken(domain, method) {
3773
- return this.http.post(`/domains/${encodeURIComponent(domain)}/verification`, { method });
3902
+ return this.http.post(`/domains/${encodePathParam(domain)}/verification`, { method });
3774
3903
  }
3775
3904
  /** Verify domain ownership */
3776
3905
  async verifyOwnership(domain, token) {
3777
- return this.http.post(`/domains/${encodeURIComponent(domain)}/verify`, { token });
3906
+ return this.http.post(`/domains/${encodePathParam(domain)}/verify`, { token });
3778
3907
  }
3779
3908
  /** Configure DNS records for a domain */
3780
3909
  async configureDns(domain, records) {
3781
- return this.http.post(`/domains/${encodeURIComponent(domain)}/dns`, { records });
3910
+ return this.http.post(`/domains/${encodePathParam(domain)}/dns`, { records });
3782
3911
  }
3783
3912
  /** Enable DNSSEC for a domain */
3784
3913
  async enableDnssec(domain) {
3785
- return this.http.post(`/domains/${encodeURIComponent(domain)}/dnssec/enable`, {});
3914
+ return this.http.post(`/domains/${encodePathParam(domain)}/dnssec/enable`, {});
3786
3915
  }
3787
3916
  /** Disable DNSSEC for a domain */
3788
3917
  async disableDnssec(domain) {
3789
- return this.http.post(`/domains/${encodeURIComponent(domain)}/dnssec/disable`, {});
3918
+ return this.http.post(`/domains/${encodePathParam(domain)}/dnssec/disable`, {});
3790
3919
  }
3791
3920
  /** Renew a domain */
3792
3921
  async renew(domain, years) {
3793
- return this.http.post(`/domains/${encodeURIComponent(domain)}/renew`, { years: years ?? 1 });
3922
+ return this.http.post(`/domains/${encodePathParam(domain)}/renew`, { years: years ?? 1 });
3794
3923
  }
3795
3924
  /** Initiate a domain transfer */
3796
3925
  async transfer(domain, request) {
3797
- return this.http.post(`/domains/${encodeURIComponent(domain)}/transfer`, request);
3926
+ return this.http.post(`/domains/${encodePathParam(domain)}/transfer`, request);
3798
3927
  }
3799
3928
  /** Approve a pending domain transfer */
3800
3929
  async approveTransfer(domain) {
3801
- return this.http.post(`/domains/${encodeURIComponent(domain)}/transfer/approve`, {});
3930
+ return this.http.post(`/domains/${encodePathParam(domain)}/transfer/approve`, {});
3802
3931
  }
3803
3932
  /** Reject a pending domain transfer */
3804
3933
  async rejectTransfer(domain) {
3805
- return this.http.post(`/domains/${encodeURIComponent(domain)}/transfer/reject`, {});
3934
+ return this.http.post(`/domains/${encodePathParam(domain)}/transfer/reject`, {});
3806
3935
  }
3807
3936
  /** Suspend a domain */
3808
3937
  async suspend(domain, reason) {
3809
- return this.http.post(`/domains/${encodeURIComponent(domain)}/suspend`, { reason });
3938
+ return this.http.post(`/domains/${encodePathParam(domain)}/suspend`, { reason });
3810
3939
  }
3811
3940
  /** Unsuspend a domain */
3812
3941
  async unsuspend(domain) {
3813
- return this.http.post(`/domains/${encodeURIComponent(domain)}/unsuspend`, {});
3942
+ return this.http.post(`/domains/${encodePathParam(domain)}/unsuspend`, {});
3814
3943
  }
3815
3944
  };
3816
3945
 
@@ -3837,7 +3966,7 @@ var DnsClient = class {
3837
3966
  }
3838
3967
  /** Get a specific zone */
3839
3968
  async getZone(zoneName) {
3840
- return this.http.get(`/dns/zones/${encodeURIComponent(zoneName)}`);
3969
+ return this.http.get(`/dns/zones/${encodePathParam(zoneName)}`);
3841
3970
  }
3842
3971
  /** Create a new DNS zone */
3843
3972
  async createZone(request) {
@@ -3845,38 +3974,38 @@ var DnsClient = class {
3845
3974
  }
3846
3975
  /** Delete a DNS zone */
3847
3976
  async deleteZone(zoneName) {
3848
- return this.http.delete(`/dns/zones/${encodeURIComponent(zoneName)}`);
3977
+ return this.http.delete(`/dns/zones/${encodePathParam(zoneName)}`);
3849
3978
  }
3850
3979
  /** Add a record to a zone */
3851
3980
  async addRecord(zoneName, record) {
3852
- return this.http.post(`/dns/zones/${encodeURIComponent(zoneName)}/records`, record);
3981
+ return this.http.post(`/dns/zones/${encodePathParam(zoneName)}/records`, record);
3853
3982
  }
3854
3983
  /** Update a record in a zone */
3855
3984
  async updateRecord(zoneName, recordId, updates) {
3856
3985
  return this.http.put(
3857
- `/dns/zones/${encodeURIComponent(zoneName)}/records/${encodeURIComponent(recordId)}`,
3986
+ `/dns/zones/${encodePathParam(zoneName)}/records/${encodePathParam(recordId)}`,
3858
3987
  updates
3859
3988
  );
3860
3989
  }
3861
3990
  /** Delete a record from a zone */
3862
3991
  async deleteRecord(zoneName, recordId) {
3863
3992
  return this.http.delete(
3864
- `/dns/zones/${encodeURIComponent(zoneName)}/records/${encodeURIComponent(recordId)}`
3993
+ `/dns/zones/${encodePathParam(zoneName)}/records/${encodePathParam(recordId)}`
3865
3994
  );
3866
3995
  }
3867
3996
  /** Generate DNSSEC keys for a zone */
3868
3997
  async generateDnssecKeys(zoneName, algorithm) {
3869
- return this.http.post(`/dns/zones/${encodeURIComponent(zoneName)}/dnssec/keys`, {
3998
+ return this.http.post(`/dns/zones/${encodePathParam(zoneName)}/dnssec/keys`, {
3870
3999
  algorithm
3871
4000
  });
3872
4001
  }
3873
4002
  /** Get DNSSEC keys for a zone */
3874
4003
  async getDnssecKeys(zoneName) {
3875
- return this.http.get(`/dns/zones/${encodeURIComponent(zoneName)}/dnssec/keys`);
4004
+ return this.http.get(`/dns/zones/${encodePathParam(zoneName)}/dnssec/keys`);
3876
4005
  }
3877
4006
  /** Get DS record for a zone (for registrar configuration) */
3878
4007
  async getDsRecord(zoneName) {
3879
- return this.http.get(`/dns/zones/${encodeURIComponent(zoneName)}/dnssec/ds`);
4008
+ return this.http.get(`/dns/zones/${encodePathParam(zoneName)}/dnssec/ds`);
3880
4009
  }
3881
4010
  /** Clear DNS cache */
3882
4011
  async clearCache() {
@@ -3891,10 +4020,8 @@ var HealthClient = class {
3891
4020
  }
3892
4021
  http;
3893
4022
  /**
3894
- * Per-cluster aggregate health probe. Wraps
3895
- * `GET /api/v3/cluster/health` see
3896
- * `apps/smart-gateway/src/health/health.controller.ts:213-263`. Returns
3897
- * local validator + host + genesis state in a single payload.
4023
+ * Per-cluster aggregate health probe. Wraps `GET /api/v3/cluster/health`.
4024
+ * Returns local validator + host + genesis state in a single payload.
3898
4025
  */
3899
4026
  async getCluster() {
3900
4027
  return this.http.get("/cluster/health");
@@ -3977,7 +4104,7 @@ var DatabaseClient = class {
3977
4104
  async insert(collection, document) {
3978
4105
  const appId = this.getAppId();
3979
4106
  return this.http.post(
3980
- `/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(collection)}`,
4107
+ `/api/db/${encodePathParam(appId)}/${encodePathParam(collection)}`,
3981
4108
  document
3982
4109
  );
3983
4110
  }
@@ -3995,7 +4122,7 @@ var DatabaseClient = class {
3995
4122
  if (options?.sort) params.set("sort", options.sort);
3996
4123
  const qs = params.toString();
3997
4124
  return this.http.get(
3998
- `/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(collection)}${qs ? `?${qs}` : ""}`
4125
+ `/api/db/${encodePathParam(appId)}/${encodePathParam(collection)}${qs ? `?${qs}` : ""}`
3999
4126
  );
4000
4127
  }
4001
4128
  /**
@@ -4004,7 +4131,7 @@ var DatabaseClient = class {
4004
4131
  async update(collection, documentId, updates) {
4005
4132
  const appId = this.getAppId();
4006
4133
  return this.http.put(
4007
- `/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(collection)}/${encodeURIComponent(documentId)}`,
4134
+ `/api/db/${encodePathParam(appId)}/${encodePathParam(collection)}/${encodePathParam(documentId)}`,
4008
4135
  updates
4009
4136
  );
4010
4137
  }
@@ -4014,7 +4141,7 @@ var DatabaseClient = class {
4014
4141
  async delete(collection, documentId) {
4015
4142
  const appId = this.getAppId();
4016
4143
  return this.http.delete(
4017
- `/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(collection)}/${encodeURIComponent(documentId)}`
4144
+ `/api/db/${encodePathParam(appId)}/${encodePathParam(collection)}/${encodePathParam(documentId)}`
4018
4145
  );
4019
4146
  }
4020
4147
  /**
@@ -4026,7 +4153,7 @@ var DatabaseClient = class {
4026
4153
  */
4027
4154
  async listCollections() {
4028
4155
  const appId = this.getAppId();
4029
- return this.http.get(`/api/db/${encodeURIComponent(appId)}/collections`);
4156
+ return this.http.get(`/api/db/${encodePathParam(appId)}/collections`);
4030
4157
  }
4031
4158
  /**
4032
4159
  * Create a new collection in the database.
@@ -4036,7 +4163,7 @@ var DatabaseClient = class {
4036
4163
  */
4037
4164
  async createCollection(name) {
4038
4165
  const appId = this.getAppId();
4039
- return this.http.post(`/api/db/${encodeURIComponent(appId)}/collections`, { name });
4166
+ return this.http.post(`/api/db/${encodePathParam(appId)}/collections`, { name });
4040
4167
  }
4041
4168
  /**
4042
4169
  * Drop a collection and all its documents.
@@ -4047,7 +4174,7 @@ var DatabaseClient = class {
4047
4174
  async dropCollection(name) {
4048
4175
  const appId = this.getAppId();
4049
4176
  return this.http.delete(
4050
- `/api/db/${encodeURIComponent(appId)}/collections/${encodeURIComponent(name)}`
4177
+ `/api/db/${encodePathParam(appId)}/collections/${encodePathParam(name)}`
4051
4178
  );
4052
4179
  }
4053
4180
  // ========== State Proofs ==========
@@ -4056,7 +4183,7 @@ var DatabaseClient = class {
4056
4183
  */
4057
4184
  async getStateRoot() {
4058
4185
  const appId = this.getAppId();
4059
- return this.http.get(`/api/db/${encodeURIComponent(appId)}/state/root`);
4186
+ return this.http.get(`/api/db/${encodePathParam(appId)}/state/root`);
4060
4187
  }
4061
4188
  /**
4062
4189
  * Get a Merkle proof for a specific document
@@ -4064,7 +4191,7 @@ var DatabaseClient = class {
4064
4191
  async getDocumentProof(documentId) {
4065
4192
  const appId = this.getAppId();
4066
4193
  return this.http.get(
4067
- `/api/db/${encodeURIComponent(appId)}/${encodeURIComponent(documentId)}/proof`
4194
+ `/api/db/${encodePathParam(appId)}/${encodePathParam(documentId)}/proof`
4068
4195
  );
4069
4196
  }
4070
4197
  /**
@@ -4078,7 +4205,7 @@ var DatabaseClient = class {
4078
4205
  if (options?.limit !== void 0) params.set("limit", String(options.limit));
4079
4206
  const qs = params.toString();
4080
4207
  return this.http.get(
4081
- `/api/db/${encodeURIComponent(appId)}/state/transitions${qs ? `?${qs}` : ""}`
4208
+ `/api/db/${encodePathParam(appId)}/state/transitions${qs ? `?${qs}` : ""}`
4082
4209
  );
4083
4210
  }
4084
4211
  /**
@@ -4086,7 +4213,7 @@ var DatabaseClient = class {
4086
4213
  */
4087
4214
  async getDbStats() {
4088
4215
  const appId = this.getAppId();
4089
- return this.http.get(`/api/db/${encodeURIComponent(appId)}/stats`);
4216
+ return this.http.get(`/api/db/${encodePathParam(appId)}/stats`);
4090
4217
  }
4091
4218
  };
4092
4219
 
@@ -4103,47 +4230,35 @@ var StorageClient = class {
4103
4230
  */
4104
4231
  async upload(file, filename, metadata) {
4105
4232
  const appId = this.getAppId();
4106
- return this.http.upload(`/api/storage/${encodeURIComponent(appId)}/upload`, file, filename, metadata);
4233
+ return this.http.upload(`/api/storage/${encodePathParam(appId)}/upload`, file, filename, metadata);
4107
4234
  }
4108
4235
  /**
4109
4236
  * Download a file by CID
4110
4237
  */
4111
4238
  async download(cid) {
4112
4239
  const appId = this.getAppId();
4113
- return this.http.get(`/api/storage/${encodeURIComponent(appId)}/download/${encodeURIComponent(cid)}`);
4240
+ return this.http.get(`/api/storage/${encodePathParam(appId)}/download/${encodePathParam(cid)}`);
4114
4241
  }
4115
4242
  /**
4116
4243
  * Get file metadata
4117
4244
  */
4118
4245
  async getMetadata(cid) {
4119
4246
  const appId = this.getAppId();
4120
- return this.http.get(`/api/storage/${encodeURIComponent(appId)}/metadata/${encodeURIComponent(cid)}`);
4247
+ return this.http.get(`/api/storage/${encodePathParam(appId)}/metadata/${encodePathParam(cid)}`);
4121
4248
  }
4122
4249
  /**
4123
4250
  * Delete a file
4124
4251
  */
4125
4252
  async delete(cid) {
4126
4253
  const appId = this.getAppId();
4127
- return this.http.delete(`/api/storage/${encodeURIComponent(appId)}/${encodeURIComponent(cid)}`);
4254
+ return this.http.delete(`/api/storage/${encodePathParam(appId)}/${encodePathParam(cid)}`);
4128
4255
  }
4129
4256
  /**
4130
- * Get file info.
4257
+ * List all files for the app.
4131
4258
  *
4132
- * @deprecated The smart-host storage controller does not expose a
4133
- * bare-CID metadata route — every metadata lookup must go through
4134
- * `getMetadata(cid)` (`/api/storage/:appId/metadata/:cid`) or the
4135
- * stream body via `download(cid)`. This alias forwards to `download`
4136
- * for back-compat; remove in the next major SDK release.
4137
- */
4138
- async getFile(cid) {
4139
- return this.download(cid);
4140
- }
4141
- /**
4142
- * List all files for the app
4143
- *
4144
- * @param pagination.offset Server reads `offset`; the legacy `skip`
4145
- * option was a client-only synonym that the server silently ignored.
4146
- * Use `offset` going forward.
4259
+ * @param pagination - Optional `limit` and `offset` (the server reads
4260
+ * `offset` for pagination).
4261
+ * @returns The file list and total count.
4147
4262
  */
4148
4263
  async listFiles(pagination) {
4149
4264
  const appId = this.getAppId();
@@ -4151,21 +4266,21 @@ var StorageClient = class {
4151
4266
  if (pagination?.limit !== void 0) params.set("limit", String(pagination.limit));
4152
4267
  if (pagination?.offset !== void 0) params.set("offset", String(pagination.offset));
4153
4268
  const qs = params.toString();
4154
- return this.http.get(`/api/storage/${encodeURIComponent(appId)}/files${qs ? `?${qs}` : ""}`);
4269
+ return this.http.get(`/api/storage/${encodePathParam(appId)}/files${qs ? `?${qs}` : ""}`);
4155
4270
  }
4156
4271
  /**
4157
4272
  * Get storage usage for the current app
4158
4273
  */
4159
4274
  async getUsage() {
4160
4275
  const appId = this.getAppId();
4161
- return this.http.get(`/api/storage/${encodeURIComponent(appId)}/usage`);
4276
+ return this.http.get(`/api/storage/${encodePathParam(appId)}/usage`);
4162
4277
  }
4163
4278
  /**
4164
4279
  * Check if a file exists
4165
4280
  */
4166
4281
  async exists(cid) {
4167
4282
  const appId = this.getAppId();
4168
- return this.http.get(`/api/storage/${encodeURIComponent(appId)}/exists/${encodeURIComponent(cid)}`);
4283
+ return this.http.get(`/api/storage/${encodePathParam(appId)}/exists/${encodePathParam(cid)}`);
4169
4284
  }
4170
4285
  };
4171
4286
 
@@ -4182,7 +4297,7 @@ var FunctionsClient = class {
4182
4297
  */
4183
4298
  async deploy(request) {
4184
4299
  const appId = this.getAppId();
4185
- return this.http.post(`/api/functions/${encodeURIComponent(appId)}`, request);
4300
+ return this.http.post(`/api/functions/${encodePathParam(appId)}`, request);
4186
4301
  }
4187
4302
  /**
4188
4303
  * Invoke a function
@@ -4190,7 +4305,7 @@ var FunctionsClient = class {
4190
4305
  async invoke(functionId, payload) {
4191
4306
  const appId = this.getAppId();
4192
4307
  return this.http.post(
4193
- `/api/functions/${encodeURIComponent(appId)}/${encodeURIComponent(functionId)}/invoke`,
4308
+ `/api/functions/${encodePathParam(appId)}/${encodePathParam(functionId)}/invoke`,
4194
4309
  payload ?? {}
4195
4310
  );
4196
4311
  }
@@ -4199,7 +4314,7 @@ var FunctionsClient = class {
4199
4314
  */
4200
4315
  async list() {
4201
4316
  const appId = this.getAppId();
4202
- return this.http.get(`/api/functions/${encodeURIComponent(appId)}`);
4317
+ return this.http.get(`/api/functions/${encodePathParam(appId)}`);
4203
4318
  }
4204
4319
  /**
4205
4320
  * Get function details
@@ -4207,7 +4322,7 @@ var FunctionsClient = class {
4207
4322
  async get(functionId) {
4208
4323
  const appId = this.getAppId();
4209
4324
  return this.http.get(
4210
- `/api/functions/${encodeURIComponent(appId)}/${encodeURIComponent(functionId)}`
4325
+ `/api/functions/${encodePathParam(appId)}/${encodePathParam(functionId)}`
4211
4326
  );
4212
4327
  }
4213
4328
  /**
@@ -4216,7 +4331,7 @@ var FunctionsClient = class {
4216
4331
  async update(functionId, updates) {
4217
4332
  const appId = this.getAppId();
4218
4333
  return this.http.put(
4219
- `/api/functions/${encodeURIComponent(appId)}/${encodeURIComponent(functionId)}`,
4334
+ `/api/functions/${encodePathParam(appId)}/${encodePathParam(functionId)}`,
4220
4335
  updates
4221
4336
  );
4222
4337
  }
@@ -4226,7 +4341,7 @@ var FunctionsClient = class {
4226
4341
  async delete(functionId) {
4227
4342
  const appId = this.getAppId();
4228
4343
  return this.http.delete(
4229
- `/api/functions/${encodeURIComponent(appId)}/${encodeURIComponent(functionId)}`
4344
+ `/api/functions/${encodePathParam(appId)}/${encodePathParam(functionId)}`
4230
4345
  );
4231
4346
  }
4232
4347
  /**
@@ -4240,7 +4355,7 @@ var FunctionsClient = class {
4240
4355
  if (options?.level) params.set("level", options.level);
4241
4356
  const qs = params.toString();
4242
4357
  return this.http.get(
4243
- `/api/functions/${encodeURIComponent(appId)}/${encodeURIComponent(functionId)}/logs${qs ? `?${qs}` : ""}`
4358
+ `/api/functions/${encodePathParam(appId)}/${encodePathParam(functionId)}/logs${qs ? `?${qs}` : ""}`
4244
4359
  );
4245
4360
  }
4246
4361
  /**
@@ -4248,7 +4363,7 @@ var FunctionsClient = class {
4248
4363
  */
4249
4364
  async getStats() {
4250
4365
  const appId = this.getAppId();
4251
- return this.http.get(`/api/functions/${encodeURIComponent(appId)}/stats`);
4366
+ return this.http.get(`/api/functions/${encodePathParam(appId)}/stats`);
4252
4367
  }
4253
4368
  };
4254
4369
 
@@ -4265,28 +4380,28 @@ var MessagingClient = class {
4265
4380
  */
4266
4381
  async createChannel(config) {
4267
4382
  const appId = this.getAppId();
4268
- return this.http.post(`/api/messaging/${encodeURIComponent(appId)}/channels`, config);
4383
+ return this.http.post(`/api/messaging/${encodePathParam(appId)}/channels`, config);
4269
4384
  }
4270
4385
  /**
4271
4386
  * Delete a channel
4272
4387
  */
4273
4388
  async deleteChannel(channelId) {
4274
4389
  const appId = this.getAppId();
4275
- return this.http.delete(`/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channelId)}`);
4390
+ return this.http.delete(`/api/messaging/${encodePathParam(appId)}/channels/${encodePathParam(channelId)}`);
4276
4391
  }
4277
4392
  /**
4278
4393
  * Get a channel by ID
4279
4394
  */
4280
4395
  async getChannel(channelId) {
4281
4396
  const appId = this.getAppId();
4282
- return this.http.get(`/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channelId)}`);
4397
+ return this.http.get(`/api/messaging/${encodePathParam(appId)}/channels/${encodePathParam(channelId)}`);
4283
4398
  }
4284
4399
  /**
4285
4400
  * List all channels for the app
4286
4401
  */
4287
4402
  async listChannels() {
4288
4403
  const appId = this.getAppId();
4289
- return this.http.get(`/api/messaging/${encodeURIComponent(appId)}/channels`);
4404
+ return this.http.get(`/api/messaging/${encodePathParam(appId)}/channels`);
4290
4405
  }
4291
4406
  /**
4292
4407
  * Publish a message to a channel
@@ -4294,7 +4409,7 @@ var MessagingClient = class {
4294
4409
  async publish(channel, message, metadata) {
4295
4410
  const appId = this.getAppId();
4296
4411
  return this.http.post(
4297
- `/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channel)}/publish`,
4412
+ `/api/messaging/${encodePathParam(appId)}/channels/${encodePathParam(channel)}/publish`,
4298
4413
  { data: message, metadata }
4299
4414
  );
4300
4415
  }
@@ -4309,7 +4424,7 @@ var MessagingClient = class {
4309
4424
  if (options?.after) params.set("after", options.after);
4310
4425
  const qs = params.toString();
4311
4426
  return this.http.get(
4312
- `/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channel)}/history${qs ? `?${qs}` : ""}`
4427
+ `/api/messaging/${encodePathParam(appId)}/channels/${encodePathParam(channel)}/history${qs ? `?${qs}` : ""}`
4313
4428
  );
4314
4429
  }
4315
4430
  /**
@@ -4323,7 +4438,7 @@ var MessagingClient = class {
4323
4438
  async setPresence(channel, member) {
4324
4439
  const appId = this.getAppId();
4325
4440
  return this.http.post(
4326
- `/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channel)}/presence`,
4441
+ `/api/messaging/${encodePathParam(appId)}/channels/${encodePathParam(channel)}/presence`,
4327
4442
  member
4328
4443
  );
4329
4444
  }
@@ -4339,7 +4454,7 @@ var MessagingClient = class {
4339
4454
  async removePresence(channel, clientId) {
4340
4455
  const appId = this.getAppId();
4341
4456
  return this.http.delete(
4342
- `/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channel)}/presence/${encodeURIComponent(clientId)}`
4457
+ `/api/messaging/${encodePathParam(appId)}/channels/${encodePathParam(channel)}/presence/${encodePathParam(clientId)}`
4343
4458
  );
4344
4459
  }
4345
4460
  /**
@@ -4348,7 +4463,7 @@ var MessagingClient = class {
4348
4463
  async getPresence(channel) {
4349
4464
  const appId = this.getAppId();
4350
4465
  return this.http.get(
4351
- `/api/messaging/${encodeURIComponent(appId)}/channels/${encodeURIComponent(channel)}/presence`
4466
+ `/api/messaging/${encodePathParam(appId)}/channels/${encodePathParam(channel)}/presence`
4352
4467
  );
4353
4468
  }
4354
4469
  /**
@@ -4356,7 +4471,7 @@ var MessagingClient = class {
4356
4471
  */
4357
4472
  async getStats() {
4358
4473
  const appId = this.getAppId();
4359
- return this.http.get(`/api/messaging/${encodeURIComponent(appId)}/stats`);
4474
+ return this.http.get(`/api/messaging/${encodePathParam(appId)}/stats`);
4360
4475
  }
4361
4476
  };
4362
4477
 
@@ -4444,7 +4559,7 @@ var RulesClient = class {
4444
4559
  }
4445
4560
  /** Fetch a published rule by its HCS consensus timestamp. */
4446
4561
  async get(consensusTimestamp) {
4447
- return this.http.get(`/api/rules/${encodeURIComponent(consensusTimestamp)}`);
4562
+ return this.http.get(`/api/rules/${encodePathParam(consensusTimestamp)}`);
4448
4563
  }
4449
4564
  /** List rules owned by the authenticated entity, optionally filtered by type. */
4450
4565
  async listByOwner(filter) {
@@ -4461,13 +4576,13 @@ var RulesClient = class {
4461
4576
  /** Walk the version history of a published rule. */
4462
4577
  async getVersionHistory(consensusTimestamp) {
4463
4578
  return this.http.get(
4464
- `/api/rules/${encodeURIComponent(consensusTimestamp)}/versions`
4579
+ `/api/rules/${encodePathParam(consensusTimestamp)}/versions`
4465
4580
  );
4466
4581
  }
4467
4582
  /** Deprecate a published rule (owner-only). */
4468
4583
  async deprecate(consensusTimestamp) {
4469
4584
  return this.http.post(
4470
- `/api/rules/${encodeURIComponent(consensusTimestamp)}/deprecate`,
4585
+ `/api/rules/${encodePathParam(consensusTimestamp)}/deprecate`,
4471
4586
  {}
4472
4587
  );
4473
4588
  }
@@ -4505,7 +4620,7 @@ var EntitiesClient = class {
4505
4620
  }
4506
4621
  /** Fetch an entity by its canonical `entityId`. */
4507
4622
  async get(entityId) {
4508
- return this.http.get(`/api/entities/${encodeURIComponent(entityId)}`);
4623
+ return this.http.get(`/api/entities/${encodePathParam(entityId)}`);
4509
4624
  }
4510
4625
  /** List entities owned by the authenticated wallet, optionally filtered by type. */
4511
4626
  async listByOwner(filter) {
@@ -4521,10 +4636,16 @@ var BaasClient = class _BaasClient {
4521
4636
  appId;
4522
4637
  timeout;
4523
4638
  allowInsecure;
4524
- authToken = null;
4525
4639
  http;
4526
4640
  /** Last HTTP error (for getHttpHealth) */
4527
4641
  lastHttpError;
4642
+ /**
4643
+ * Auth options from the last {@link authenticate} call, retained so the
4644
+ * client can transparently re-authenticate when the session token expires
4645
+ * (the http client invokes {@link reauthenticate} on a 401). Undefined until
4646
+ * the first successful authenticate.
4647
+ */
4648
+ authContext;
4528
4649
  // ========== Sub-Clients ==========
4529
4650
  /** Trustless database with state proofs and Merkle verification */
4530
4651
  db;
@@ -4554,7 +4675,11 @@ var BaasClient = class _BaasClient {
4554
4675
  const baseUrlWithPrefix = this.pathPrefix ? this.hostUrl.replace(/\/$/, "") + this.pathPrefix : this.hostUrl;
4555
4676
  this.http = createHttpClient({
4556
4677
  baseUrl: baseUrlWithPrefix,
4557
- timeout: this.timeout
4678
+ timeout: this.timeout,
4679
+ // Transparent session refresh: on a 401, re-run the challenge-response
4680
+ // with the retained signer and retry once. No-op until authenticate() has
4681
+ // been called (authContext set). Excludes /api/auth/* (see http client).
4682
+ onUnauthorized: () => this.reauthenticate()
4558
4683
  });
4559
4684
  const getAppId = () => this.requireAppId();
4560
4685
  this.db = new DatabaseClient(this.http, getAppId);
@@ -4608,30 +4733,25 @@ var BaasClient = class _BaasClient {
4608
4733
  */
4609
4734
  static async connectToCluster(config) {
4610
4735
  const allowInsecure = config.allowInsecure ?? false;
4611
- const bootstrap = config.bootstrap ? [...config.bootstrap] : [...resolveNetwork(config.network).bootstrap];
4612
- if (bootstrap.length === 0) {
4613
- throw new BaasError(
4614
- "connectToCluster requires either a non-empty `bootstrap` list or a known `network` name.",
4615
- 400
4616
- );
4617
- }
4618
- const discovery = new ClusterDiscoveryClient({
4619
- bootstrap,
4736
+ const resolved = await resolveClusterEndpoint({
4737
+ bootstrap: config.bootstrap,
4738
+ network: config.network,
4620
4739
  allowInsecure,
4621
- trustAnchor: config.trustAnchor ? {
4622
- network: config.trustAnchor.network,
4623
- registryTopicId: config.trustAnchor.registryTopicId,
4624
- mirrorNodeUrl: config.trustAnchor.mirrorNodeUrl,
4625
- allowInsecure
4626
- } : void 0
4740
+ trustAnchor: config.trustAnchor
4627
4741
  });
4628
- const cluster = await discovery.getRandomCluster();
4629
- if (!cluster) {
4742
+ if (!resolved.ok) {
4743
+ if (resolved.reason === "no-seeds") {
4744
+ throw new BaasError(
4745
+ "connectToCluster requires either a non-empty `bootstrap` list or a known `network` name.",
4746
+ 400
4747
+ );
4748
+ }
4630
4749
  throw new BaasError(
4631
4750
  "No active clusters available via bootstrap seeds. Check network reachability or bootstrap URLs.",
4632
4751
  503
4633
4752
  );
4634
4753
  }
4754
+ const cluster = resolved.cluster;
4635
4755
  return new _BaasClient({
4636
4756
  hostUrl: cluster.endpoints.gatewayUrl,
4637
4757
  appId: config.appId,
@@ -4649,7 +4769,7 @@ var BaasClient = class _BaasClient {
4649
4769
  }
4650
4770
  /** Check if the client is authenticated */
4651
4771
  isAuthenticated() {
4652
- return this.authToken !== null;
4772
+ return this.http.getAuthToken() !== void 0;
4653
4773
  }
4654
4774
  /** Get the current app ID */
4655
4775
  getAppId() {
@@ -4695,93 +4815,80 @@ var BaasClient = class _BaasClient {
4695
4815
  */
4696
4816
  async authenticate(options) {
4697
4817
  const { chain, walletAddress, publicKey, signFn } = options;
4698
- const challenge = await this.post("/api/auth/challenge", {
4699
- chain,
4700
- walletAddress,
4818
+ this.authContext = options;
4819
+ let challenge;
4820
+ try {
4821
+ challenge = await this.http.post("/api/auth/challenge", {
4822
+ chain,
4823
+ walletAddress,
4824
+ appId: this.appId
4825
+ });
4826
+ } catch (err) {
4827
+ throw asBaasError(err);
4828
+ }
4829
+ const signature = await signFn(challenge.message);
4830
+ let result;
4831
+ try {
4832
+ result = await this.http.post("/api/auth/verify", {
4833
+ challengeId: challenge.challengeId,
4834
+ signature,
4835
+ publicKey
4836
+ });
4837
+ } catch (err) {
4838
+ throw asBaasError(err);
4839
+ }
4840
+ this.http.setAuthToken(result.token);
4841
+ return result;
4842
+ }
4843
+ /**
4844
+ * Re-run the challenge-response with the retained signer to mint a fresh
4845
+ * session token. Invoked by the http client's `onUnauthorized` hook when a
4846
+ * request 401s because the token expired — so long-lived clients keep working
4847
+ * without the caller re-implementing refresh. No-op if {@link authenticate}
4848
+ * was never called. The `/api/auth/*` calls below are excluded from the http
4849
+ * client's 401-retry path, so this can never recurse.
4850
+ */
4851
+ async reauthenticate() {
4852
+ const ctx = this.authContext;
4853
+ if (!ctx) return;
4854
+ const challenge = await this.http.post("/api/auth/challenge", {
4855
+ chain: ctx.chain,
4856
+ walletAddress: ctx.walletAddress,
4701
4857
  appId: this.appId
4702
4858
  });
4703
- const signature = await signFn(challenge.message);
4704
- const result = await this.post("/api/auth/verify", {
4859
+ const signature = await ctx.signFn(challenge.message);
4860
+ const result = await this.http.post("/api/auth/verify", {
4705
4861
  challengeId: challenge.challengeId,
4706
4862
  signature,
4707
- publicKey
4863
+ publicKey: ctx.publicKey
4708
4864
  });
4709
- this.authToken = result.token;
4710
- this.http.setAuthToken?.(result.token);
4711
- return result;
4865
+ this.http.setAuthToken(result.token);
4712
4866
  }
4713
4867
  /** Validate the current session */
4714
4868
  async validateSession() {
4715
4869
  this.requireAuth();
4716
- return this.get("/api/auth/session");
4870
+ try {
4871
+ return await this.http.get("/api/auth/session");
4872
+ } catch (err) {
4873
+ throw asBaasError(err);
4874
+ }
4717
4875
  }
4718
4876
  /** Destroy the current session on server and clear local token */
4719
4877
  async logout() {
4720
- if (this.authToken) {
4878
+ if (this.http.getAuthToken()) {
4721
4879
  try {
4722
- await this.post("/api/auth/logout", {});
4880
+ await this.http.post("/api/auth/logout", {});
4723
4881
  } catch {
4724
4882
  }
4725
4883
  }
4726
- this.authToken = null;
4884
+ this.http.setAuthToken(void 0);
4727
4885
  }
4728
4886
  // ========== HTTP Helpers ==========
4729
4887
  requireAuth() {
4730
- if (!this.authToken) {
4888
+ if (!this.http.getAuthToken()) {
4731
4889
  throw new BaasError("Authentication required. Call authenticate() first.", 401);
4732
4890
  }
4733
4891
  }
4734
- getHeaders() {
4735
- const headers = {
4736
- "Content-Type": "application/json"
4737
- };
4738
- if (this.authToken) {
4739
- headers["Authorization"] = `Bearer ${this.authToken}`;
4740
- }
4741
- return headers;
4742
- }
4743
- async post(path, body) {
4744
- return this.request("POST", path, body);
4745
- }
4746
- async get(path) {
4747
- return this.request("GET", path);
4748
- }
4749
- async request(method, path, body) {
4750
- const url = `${this.hostUrl}${this.pathPrefix}${path}`;
4751
- const controller = new AbortController();
4752
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
4753
- try {
4754
- const options = {
4755
- method,
4756
- headers: this.getHeaders(),
4757
- signal: controller.signal
4758
- };
4759
- if (body !== void 0) {
4760
- options.body = JSON.stringify(body);
4761
- }
4762
- const response = await fetch(url, options);
4763
- clearTimeout(timeoutId);
4764
- if (!response.ok) {
4765
- const errorData = await response.json().catch(() => ({}));
4766
- throw new BaasError(
4767
- errorData.message || `API error: ${response.status} ${response.statusText}`,
4768
- response.status,
4769
- errorData
4770
- );
4771
- }
4772
- const text = await response.text();
4773
- if (!text) return void 0;
4774
- return JSON.parse(text);
4775
- } catch (error) {
4776
- clearTimeout(timeoutId);
4777
- if (error instanceof BaasError) throw error;
4778
- const err = error;
4779
- if (err.name === "AbortError") {
4780
- throw new BaasError("Request timeout", 408);
4781
- }
4782
- throw new BaasError(`Network error: ${err.message}`, 0, { originalError: err.message });
4783
- }
4784
- }
4785
4892
  };
4786
4893
  var BaasError = class extends Error {
4787
4894
  constructor(message, statusCode, details) {
@@ -4793,6 +4900,17 @@ var BaasError = class extends Error {
4793
4900
  statusCode;
4794
4901
  details;
4795
4902
  };
4903
+ function asBaasError(err) {
4904
+ if (err instanceof BaasError) return err;
4905
+ if (err instanceof SdkHttpError) {
4906
+ const details = err.details ?? void 0;
4907
+ return new BaasError(err.message, err.statusCode, details);
4908
+ }
4909
+ const e = err;
4910
+ return new BaasError(`Network error: ${e?.message ?? String(err)}`, 0, {
4911
+ originalError: e?.message ?? String(err)
4912
+ });
4913
+ }
4796
4914
  function validateUrl2(url, allowInsecure = false) {
4797
4915
  try {
4798
4916
  const parsed = new URL(url);
@@ -5016,10 +5134,12 @@ exports.SmartEngineService = __decorateClass([
5016
5134
  // src/nestjs/smart-engine.module.ts
5017
5135
  exports.SmartEngineModule = class SmartEngineModule {
5018
5136
  /**
5019
- * Configure the module with static configuration
5137
+ * Configure the module with static configuration.
5020
5138
  *
5021
- * @param config - SmartEngine service configuration
5022
- * @param isGlobal - Whether to make the module global (default: true)
5139
+ * @param config - SmartEngine service configuration.
5140
+ * @param isGlobal - Whether to make the module global. @defaultValue true
5141
+ * @returns A `DynamicModule` exporting {@link SmartEngineService} plus the
5142
+ * BaaS / rules / entities clients.
5023
5143
  */
5024
5144
  static forRoot(config, isGlobal = true) {
5025
5145
  return {
@@ -5042,11 +5162,15 @@ exports.SmartEngineModule = class SmartEngineModule {
5042
5162
  };
5043
5163
  }
5044
5164
  /**
5045
- * Configure the module with async configuration
5165
+ * Configure the module with async configuration.
5046
5166
  *
5047
5167
  * Supports factory functions, configuration classes, and existing providers.
5048
5168
  *
5049
- * @param options - Async configuration options
5169
+ * @param options - Async configuration options.
5170
+ * @returns A `DynamicModule` exporting {@link SmartEngineService} plus the
5171
+ * BaaS / rules / entities clients.
5172
+ * @throws Error if `options` provides none of `useFactory`, `useClass`, or
5173
+ * `useExisting`.
5050
5174
  */
5051
5175
  static forRootAsync(options) {
5052
5176
  const asyncProviders = this.createAsyncProviders(options);