@calimero-network/mero-js 25.0.3 → 25.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -551,6 +551,14 @@ function createRetryableMethod(method, retryOptions = {}) {
551
551
  };
552
552
  }
553
553
 
554
+ // src/http-client/path-segment.ts
555
+ function pathSegment(id) {
556
+ if (id === "" || id === "." || id === "..") {
557
+ throw new TypeError(`invalid request path segment: ${JSON.stringify(id)}`);
558
+ }
559
+ return encodeURIComponent(id);
560
+ }
561
+
554
562
  // src/auth-api/auth-client.ts
555
563
  var AuthApiClient = class {
556
564
  constructor(httpClient) {
@@ -652,7 +660,7 @@ var AuthApiClient = class {
652
660
  return response.data;
653
661
  }
654
662
  async deleteRootKey(keyId) {
655
- return this.httpClient.delete(`/admin/keys/${keyId}`);
663
+ return this.httpClient.delete(`/admin/keys/${pathSegment(keyId)}`);
656
664
  }
657
665
  // Client Management Endpoints
658
666
  async listClientKeys() {
@@ -669,18 +677,18 @@ var AuthApiClient = class {
669
677
  }
670
678
  async deleteClientKey(keyId, clientId) {
671
679
  return this.httpClient.delete(
672
- `/admin/keys/${keyId}/clients/${clientId}`
680
+ `/admin/keys/${pathSegment(keyId)}/clients/${pathSegment(clientId)}`
673
681
  );
674
682
  }
675
683
  // Permission Management Endpoints
676
684
  async getKeyPermissions(keyId) {
677
685
  return this.httpClient.get(
678
- `/admin/keys/${keyId}/permissions`
686
+ `/admin/keys/${pathSegment(keyId)}/permissions`
679
687
  );
680
688
  }
681
689
  async updateKeyPermissions(keyId, changes) {
682
690
  return this.httpClient.put(
683
- `/admin/keys/${keyId}/permissions`,
691
+ `/admin/keys/${pathSegment(keyId)}/permissions`,
684
692
  { add: changes.add, remove: changes.remove }
685
693
  );
686
694
  }
@@ -1102,13 +1110,13 @@ var AdminApiClient = class {
1102
1110
  return unwrap(await this.httpClient.post("/admin-api/install-dev-application", request));
1103
1111
  }
1104
1112
  async uninstallApplication(appId) {
1105
- return unwrap(await this.httpClient.delete(`/admin-api/applications/${appId}`));
1113
+ return unwrap(await this.httpClient.delete(`/admin-api/applications/${pathSegment(appId)}`));
1106
1114
  }
1107
1115
  async listApplications() {
1108
1116
  return unwrap(await this.httpClient.get("/admin-api/applications"));
1109
1117
  }
1110
1118
  async getApplication(appId) {
1111
- return unwrap(await this.httpClient.get(`/admin-api/applications/${appId}`));
1119
+ return unwrap(await this.httpClient.get(`/admin-api/applications/${pathSegment(appId)}`));
1112
1120
  }
1113
1121
  /**
1114
1122
  * Installed-blob inventory for an application — one entry per locally installed
@@ -1117,7 +1125,7 @@ var AdminApiClient = class {
1117
1125
  */
1118
1126
  async listApplicationVersions(applicationId) {
1119
1127
  return unwrap(
1120
- await this.httpClient.get(`/admin-api/applications/${applicationId}/versions`)
1128
+ await this.httpClient.get(`/admin-api/applications/${pathSegment(applicationId)}/versions`)
1121
1129
  );
1122
1130
  }
1123
1131
  /**
@@ -1128,7 +1136,7 @@ var AdminApiClient = class {
1128
1136
  const query = serviceName ? `?service_name=${encodeURIComponent(serviceName)}` : "";
1129
1137
  return unwrap(
1130
1138
  await this.httpClient.get(
1131
- `/admin-api/applications/${applicationId}/abi${query}`
1139
+ `/admin-api/applications/${pathSegment(applicationId)}/abi${query}`
1132
1140
  )
1133
1141
  );
1134
1142
  }
@@ -1141,13 +1149,13 @@ var AdminApiClient = class {
1141
1149
  }
1142
1150
  async listPackageVersions(packageName) {
1143
1151
  const r = await this.httpClient.get(
1144
- `/admin-api/packages/${encodeURIComponent(packageName)}/versions`
1152
+ `/admin-api/packages/${pathSegment(packageName)}/versions`
1145
1153
  );
1146
1154
  return r?.data ?? r;
1147
1155
  }
1148
1156
  async getLatestPackageVersion(packageName) {
1149
1157
  return this.httpClient.get(
1150
- `/admin-api/packages/${encodeURIComponent(packageName)}/latest`
1158
+ `/admin-api/packages/${pathSegment(packageName)}/latest`
1151
1159
  );
1152
1160
  }
1153
1161
  // ---- Context Management ----
@@ -1156,43 +1164,43 @@ var AdminApiClient = class {
1156
1164
  return unwrap(await this.httpClient.post("/admin-api/contexts", body));
1157
1165
  }
1158
1166
  async deleteContext(contextId) {
1159
- return unwrap(await this.httpClient.delete(`/admin-api/contexts/${contextId}`));
1167
+ return unwrap(await this.httpClient.delete(`/admin-api/contexts/${pathSegment(contextId)}`));
1160
1168
  }
1161
1169
  async getContexts() {
1162
1170
  return unwrap(await this.httpClient.get("/admin-api/contexts"));
1163
1171
  }
1164
1172
  async getContext(contextId) {
1165
- return unwrap(await this.httpClient.get(`/admin-api/contexts/${contextId}`));
1173
+ return unwrap(await this.httpClient.get(`/admin-api/contexts/${pathSegment(contextId)}`));
1166
1174
  }
1167
1175
  async getContextsForApplication(applicationId) {
1168
- return unwrap(await this.httpClient.get(`/admin-api/contexts/for-application/${applicationId}`));
1176
+ return unwrap(await this.httpClient.get(`/admin-api/contexts/for-application/${pathSegment(applicationId)}`));
1169
1177
  }
1170
1178
  // ---- Context Identity ----
1171
1179
  async generateContextIdentity() {
1172
1180
  return unwrap(await this.httpClient.post("/admin-api/identity/context", {}));
1173
1181
  }
1174
1182
  async getContextIdentities(contextId) {
1175
- return unwrap(await this.httpClient.get(`/admin-api/contexts/${contextId}/identities`));
1183
+ return unwrap(await this.httpClient.get(`/admin-api/contexts/${pathSegment(contextId)}/identities`));
1176
1184
  }
1177
1185
  async getContextIdentitiesOwned(contextId) {
1178
- return unwrap(await this.httpClient.get(`/admin-api/contexts/${contextId}/identities-owned`));
1186
+ return unwrap(await this.httpClient.get(`/admin-api/contexts/${pathSegment(contextId)}/identities-owned`));
1179
1187
  }
1180
1188
  // ---- Context join (group membership) ----
1181
1189
  async joinContext(contextId) {
1182
1190
  return unwrap(
1183
- await this.httpClient.post(`/admin-api/contexts/${contextId}/join`, {})
1191
+ await this.httpClient.post(`/admin-api/contexts/${pathSegment(contextId)}/join`, {})
1184
1192
  );
1185
1193
  }
1186
1194
  // ---- Context group / storage / sync ----
1187
1195
  async getContextGroup(contextId) {
1188
- return unwrap(await this.httpClient.get(`/admin-api/contexts/${contextId}/group`));
1196
+ return unwrap(await this.httpClient.get(`/admin-api/contexts/${pathSegment(contextId)}/group`));
1189
1197
  }
1190
1198
  async getContextStorage(contextId) {
1191
- return unwrap(await this.httpClient.get(`/admin-api/contexts/${contextId}/storage`));
1199
+ return unwrap(await this.httpClient.get(`/admin-api/contexts/${pathSegment(contextId)}/storage`));
1192
1200
  }
1193
1201
  /** Sync one context, or every context when `contextId` is omitted. */
1194
1202
  async syncContext(contextId) {
1195
- const path = contextId ? `/admin-api/contexts/sync/${contextId}` : "/admin-api/contexts/sync";
1203
+ const path = contextId ? `/admin-api/contexts/sync/${pathSegment(contextId)}` : "/admin-api/contexts/sync";
1196
1204
  await this.httpClient.post(path, {});
1197
1205
  }
1198
1206
  /**
@@ -1202,7 +1210,7 @@ var AdminApiClient = class {
1202
1210
  */
1203
1211
  async resyncContext(contextId, request = {}) {
1204
1212
  const r = await this.httpClient.post(
1205
- `/admin-api/contexts/${contextId}/resync`,
1213
+ `/admin-api/contexts/${pathSegment(contextId)}/resync`,
1206
1214
  request
1207
1215
  );
1208
1216
  return r ?? { contextId, resyncStarted: true };
@@ -1216,10 +1224,10 @@ var AdminApiClient = class {
1216
1224
  );
1217
1225
  }
1218
1226
  async updateContextApplication(contextId, request) {
1219
- await this.httpClient.post(`/admin-api/contexts/${contextId}/application`, request);
1227
+ await this.httpClient.post(`/admin-api/contexts/${pathSegment(contextId)}/application`, request);
1220
1228
  }
1221
1229
  async getContextsWithExecutorsForApplication(applicationId) {
1222
- const r = await this.httpClient.get(`/admin-api/contexts/with-executors/for-application/${applicationId}`);
1230
+ const r = await this.httpClient.get(`/admin-api/contexts/with-executors/for-application/${pathSegment(applicationId)}`);
1223
1231
  if (Array.isArray(r)) return r;
1224
1232
  return r?.contexts ?? r?.data ?? [];
1225
1233
  }
@@ -1241,7 +1249,7 @@ var AdminApiClient = class {
1241
1249
  }
1242
1250
  async deleteBlob(blobId) {
1243
1251
  const body = await this.httpClient.delete(
1244
- `/admin-api/blobs/${blobId}`
1252
+ `/admin-api/blobs/${pathSegment(blobId)}`
1245
1253
  );
1246
1254
  return { blobId: body.blob_id, deleted: body.deleted };
1247
1255
  }
@@ -1259,9 +1267,9 @@ var AdminApiClient = class {
1259
1267
  * side from drifting from the write side.
1260
1268
  */
1261
1269
  blobPath(blobId, options) {
1262
- if (!options?.contextId) return `/admin-api/blobs/${blobId}`;
1270
+ if (!options?.contextId) return `/admin-api/blobs/${pathSegment(blobId)}`;
1263
1271
  const params = new URLSearchParams({ context_id: options.contextId });
1264
- return `/admin-api/blobs/${blobId}?${params.toString()}`;
1272
+ return `/admin-api/blobs/${pathSegment(blobId)}?${params.toString()}`;
1265
1273
  }
1266
1274
  /**
1267
1275
  * Download a blob's raw bytes. `GET /admin-api/blobs/:id` streams the blob
@@ -1326,7 +1334,7 @@ var AdminApiClient = class {
1326
1334
  async lookupContextAlias(name) {
1327
1335
  return unwrap(
1328
1336
  await this.httpClient.post(
1329
- `/admin-api/alias/lookup/context/${encodeURIComponent(name)}`,
1337
+ `/admin-api/alias/lookup/context/${pathSegment(name)}`,
1330
1338
  {}
1331
1339
  )
1332
1340
  );
@@ -1334,7 +1342,7 @@ var AdminApiClient = class {
1334
1342
  async lookupApplicationAlias(name) {
1335
1343
  return unwrap(
1336
1344
  await this.httpClient.post(
1337
- `/admin-api/alias/lookup/application/${encodeURIComponent(name)}`,
1345
+ `/admin-api/alias/lookup/application/${pathSegment(name)}`,
1338
1346
  {}
1339
1347
  )
1340
1348
  );
@@ -1342,7 +1350,7 @@ var AdminApiClient = class {
1342
1350
  async deleteContextAlias(name) {
1343
1351
  return unwrap(
1344
1352
  await this.httpClient.post(
1345
- `/admin-api/alias/delete/context/${encodeURIComponent(name)}`,
1353
+ `/admin-api/alias/delete/context/${pathSegment(name)}`,
1346
1354
  {}
1347
1355
  )
1348
1356
  );
@@ -1350,7 +1358,7 @@ var AdminApiClient = class {
1350
1358
  async deleteApplicationAlias(name) {
1351
1359
  return unwrap(
1352
1360
  await this.httpClient.post(
1353
- `/admin-api/alias/delete/application/${encodeURIComponent(name)}`,
1361
+ `/admin-api/alias/delete/application/${pathSegment(name)}`,
1354
1362
  {}
1355
1363
  )
1356
1364
  );
@@ -1372,7 +1380,7 @@ var AdminApiClient = class {
1372
1380
  async lookupDeviceAlias(name) {
1373
1381
  return unwrap(
1374
1382
  await this.httpClient.post(
1375
- `/admin-api/alias/lookup/device/${encodeURIComponent(name)}`,
1383
+ `/admin-api/alias/lookup/device/${pathSegment(name)}`,
1376
1384
  {}
1377
1385
  )
1378
1386
  );
@@ -1380,7 +1388,7 @@ var AdminApiClient = class {
1380
1388
  async deleteDeviceAlias(name) {
1381
1389
  return unwrap(
1382
1390
  await this.httpClient.post(
1383
- `/admin-api/alias/delete/device/${encodeURIComponent(name)}`,
1391
+ `/admin-api/alias/delete/device/${pathSegment(name)}`,
1384
1392
  {}
1385
1393
  )
1386
1394
  );
@@ -1393,7 +1401,7 @@ var AdminApiClient = class {
1393
1401
  return unwrap(await this.httpClient.get("/admin-api/namespaces"));
1394
1402
  }
1395
1403
  async getNamespace(namespaceId) {
1396
- return unwrap(await this.httpClient.get(`/admin-api/namespaces/${namespaceId}`));
1404
+ return unwrap(await this.httpClient.get(`/admin-api/namespaces/${pathSegment(namespaceId)}`));
1397
1405
  }
1398
1406
  /**
1399
1407
  * Who this node is — the account it writes as, the device it is, the key it
@@ -1458,7 +1466,7 @@ var AdminApiClient = class {
1458
1466
  async performIntent(contextId, request) {
1459
1467
  return unwrap(
1460
1468
  await this.httpClient.post(
1461
- `/admin-api/contexts/${contextId}/intents`,
1469
+ `/admin-api/contexts/${pathSegment(contextId)}/intents`,
1462
1470
  request
1463
1471
  )
1464
1472
  );
@@ -1475,7 +1483,7 @@ var AdminApiClient = class {
1475
1483
  async queryContext(contextId, request) {
1476
1484
  return unwrap(
1477
1485
  await this.httpClient.post(
1478
- `/admin-api/contexts/${contextId}/query`,
1486
+ `/admin-api/contexts/${pathSegment(contextId)}/query`,
1479
1487
  request
1480
1488
  )
1481
1489
  );
@@ -1504,12 +1512,12 @@ var AdminApiClient = class {
1504
1512
  async getIntentRelay(contextId) {
1505
1513
  return unwrap(
1506
1514
  await this.httpClient.get(
1507
- `/admin-api/contexts/${contextId}/intents`
1515
+ `/admin-api/contexts/${pathSegment(contextId)}/intents`
1508
1516
  )
1509
1517
  );
1510
1518
  }
1511
1519
  async listNamespacesForApplication(applicationId) {
1512
- return unwrap(await this.httpClient.get(`/admin-api/namespaces/for-application/${applicationId}`));
1520
+ return unwrap(await this.httpClient.get(`/admin-api/namespaces/for-application/${pathSegment(applicationId)}`));
1513
1521
  }
1514
1522
  async createNamespace(request) {
1515
1523
  return unwrap(
@@ -1518,7 +1526,7 @@ var AdminApiClient = class {
1518
1526
  }
1519
1527
  async deleteNamespace(namespaceId) {
1520
1528
  return unwrap(
1521
- await this.httpClient.request(`/admin-api/namespaces/${namespaceId}`, {
1529
+ await this.httpClient.request(`/admin-api/namespaces/${pathSegment(namespaceId)}`, {
1522
1530
  method: "DELETE",
1523
1531
  body: "{}",
1524
1532
  headers: { "Content-Type": "application/json" }
@@ -1528,7 +1536,7 @@ var AdminApiClient = class {
1528
1536
  async createNamespaceInvitation(namespaceId, request) {
1529
1537
  return unwrap(
1530
1538
  await this.httpClient.post(
1531
- `/admin-api/namespaces/${namespaceId}/invite`,
1539
+ `/admin-api/namespaces/${pathSegment(namespaceId)}/invite`,
1532
1540
  request ?? {}
1533
1541
  )
1534
1542
  );
@@ -1553,7 +1561,7 @@ var AdminApiClient = class {
1553
1561
  async admitJoin(namespaceId, request) {
1554
1562
  return unwrap(
1555
1563
  await this.httpClient.post(
1556
- `/admin-api/namespaces/${namespaceId}/admit`,
1564
+ `/admin-api/namespaces/${pathSegment(namespaceId)}/admit`,
1557
1565
  request,
1558
1566
  { timeoutMs: 65e3 }
1559
1567
  )
@@ -1562,7 +1570,7 @@ var AdminApiClient = class {
1562
1570
  async joinNamespace(namespaceId, request) {
1563
1571
  const data = unwrap(
1564
1572
  await this.httpClient.post(
1565
- `/admin-api/namespaces/${namespaceId}/join`,
1573
+ `/admin-api/namespaces/${pathSegment(namespaceId)}/join`,
1566
1574
  request,
1567
1575
  { timeoutMs: 65e3 }
1568
1576
  )
@@ -1609,13 +1617,13 @@ var AdminApiClient = class {
1609
1617
  async createGroupInNamespace(namespaceId, request) {
1610
1618
  return unwrap(
1611
1619
  await this.httpClient.post(
1612
- `/admin-api/namespaces/${namespaceId}/groups`,
1620
+ `/admin-api/namespaces/${pathSegment(namespaceId)}/groups`,
1613
1621
  request ?? {}
1614
1622
  )
1615
1623
  );
1616
1624
  }
1617
1625
  async listNamespaceGroups(namespaceId) {
1618
- return unwrap(await this.httpClient.get(`/admin-api/namespaces/${namespaceId}/groups`));
1626
+ return unwrap(await this.httpClient.get(`/admin-api/namespaces/${pathSegment(namespaceId)}/groups`));
1619
1627
  }
1620
1628
  // ---- Account Devices & Pairing ----
1621
1629
  /**
@@ -1677,7 +1685,7 @@ var AdminApiClient = class {
1677
1685
  async relinkAccountDevice(deviceId, request) {
1678
1686
  return unwrap(
1679
1687
  await this.httpClient.post(
1680
- `/admin-api/account/devices/${deviceId}/relink`,
1688
+ `/admin-api/account/devices/${pathSegment(deviceId)}/relink`,
1681
1689
  request ?? {}
1682
1690
  )
1683
1691
  );
@@ -1692,7 +1700,7 @@ var AdminApiClient = class {
1692
1700
  async rescopeAccountDevice(deviceId, request) {
1693
1701
  return unwrap(
1694
1702
  await this.httpClient.put(
1695
- `/admin-api/account/devices/${encodeURIComponent(deviceId)}/scope`,
1703
+ `/admin-api/account/devices/${pathSegment(deviceId)}/scope`,
1696
1704
  request
1697
1705
  )
1698
1706
  );
@@ -1743,7 +1751,7 @@ var AdminApiClient = class {
1743
1751
  async labelAccountDevice(deviceId, request) {
1744
1752
  return unwrap(
1745
1753
  await this.httpClient.put(
1746
- `/admin-api/account/devices/${encodeURIComponent(deviceId)}/label`,
1754
+ `/admin-api/account/devices/${pathSegment(deviceId)}/label`,
1747
1755
  request
1748
1756
  )
1749
1757
  );
@@ -1807,7 +1815,7 @@ var AdminApiClient = class {
1807
1815
  async revokeAccountDevice(namespaceId, request) {
1808
1816
  return unwrap(
1809
1817
  await this.httpClient.post(
1810
- `/admin-api/namespaces/${namespaceId}/account/revoke`,
1818
+ `/admin-api/namespaces/${pathSegment(namespaceId)}/account/revoke`,
1811
1819
  request
1812
1820
  )
1813
1821
  );
@@ -1830,14 +1838,14 @@ var AdminApiClient = class {
1830
1838
  async linkAccountDevice(namespaceId, request) {
1831
1839
  return unwrap(
1832
1840
  await this.httpClient.post(
1833
- `/admin-api/namespaces/${namespaceId}/account/link-device`,
1841
+ `/admin-api/namespaces/${pathSegment(namespaceId)}/account/link-device`,
1834
1842
  request
1835
1843
  )
1836
1844
  );
1837
1845
  }
1838
1846
  // ---- Group Management ----
1839
1847
  async getGroupInfo(groupId) {
1840
- return unwrap(await this.httpClient.get(`/admin-api/groups/${groupId}`));
1848
+ return unwrap(await this.httpClient.get(`/admin-api/groups/${pathSegment(groupId)}`));
1841
1849
  }
1842
1850
  /** Thin wrapper over {@link getGroupInfo}: returns the group's `defaultCapabilities` bitmask. */
1843
1851
  async getDefaultCapabilities(groupId) {
@@ -1849,7 +1857,7 @@ var AdminApiClient = class {
1849
1857
  }
1850
1858
  async deleteGroup(groupId) {
1851
1859
  return unwrap(
1852
- await this.httpClient.request(`/admin-api/groups/${groupId}`, {
1860
+ await this.httpClient.request(`/admin-api/groups/${pathSegment(groupId)}`, {
1853
1861
  method: "DELETE",
1854
1862
  body: "{}",
1855
1863
  headers: { "Content-Type": "application/json" }
@@ -1858,7 +1866,7 @@ var AdminApiClient = class {
1858
1866
  }
1859
1867
  async listGroupMembers(groupId) {
1860
1868
  const response = await this.httpClient.get(
1861
- `/admin-api/groups/${groupId}/members`
1869
+ `/admin-api/groups/${pathSegment(groupId)}/members`
1862
1870
  );
1863
1871
  if (!Array.isArray(response?.members)) {
1864
1872
  const safeId = String(groupId).replace(/[\r\n\t\s]/g, "").slice(0, 64);
@@ -1896,7 +1904,7 @@ var AdminApiClient = class {
1896
1904
  if (options?.offset !== void 0) params.set("offset", String(options.offset));
1897
1905
  if (options?.limit !== void 0) params.set("limit", String(options.limit));
1898
1906
  const query = params.toString();
1899
- const base = `/admin-api/groups/${encodeURIComponent(groupId)}/member-devices`;
1907
+ const base = `/admin-api/groups/${pathSegment(groupId)}/member-devices`;
1900
1908
  const response = await this.httpClient.get(
1901
1909
  query ? `${base}?${query}` : base
1902
1910
  );
@@ -1948,7 +1956,7 @@ var AdminApiClient = class {
1948
1956
  * hex (exactly 64). The node accepts either; the two never collide.
1949
1957
  */
1950
1958
  async getWarrantNonce(contextId, authorDeviceKey) {
1951
- const path = `/admin-api/contexts/${encodeURIComponent(contextId)}/warrant-nonce/${encodeURIComponent(authorDeviceKey)}`;
1959
+ const path = `/admin-api/contexts/${pathSegment(contextId)}/warrant-nonce/${pathSegment(authorDeviceKey)}`;
1952
1960
  let raw;
1953
1961
  try {
1954
1962
  raw = await this.httpClient.get(path, { parse: "text" });
@@ -1988,7 +1996,7 @@ var AdminApiClient = class {
1988
1996
  * value passed as `authorProof` to the relay client.
1989
1997
  */
1990
1998
  async getWarrantNonceAsAuthor(contextId, authorProof) {
1991
- const path = `/admin-api/contexts/${encodeURIComponent(contextId)}/warrant-nonce`;
1999
+ const path = `/admin-api/contexts/${pathSegment(contextId)}/warrant-nonce`;
1992
2000
  let raw;
1993
2001
  try {
1994
2002
  raw = await this.httpClient.post(path, { authorProof }, { parse: "text" });
@@ -2010,7 +2018,7 @@ var AdminApiClient = class {
2010
2018
  if (options.offset !== void 0) params.set("offset", String(options.offset));
2011
2019
  if (options.limit !== void 0) params.set("limit", String(options.limit));
2012
2020
  const query = params.toString();
2013
- const path = `/admin-api/groups/${groupId}/member-devices${query ? `?${query}` : ""}`;
2021
+ const path = `/admin-api/groups/${pathSegment(groupId)}/member-devices${query ? `?${query}` : ""}`;
2014
2022
  return this.httpClient.get(path);
2015
2023
  }
2016
2024
  /**
@@ -2021,19 +2029,19 @@ var AdminApiClient = class {
2021
2029
  async sealToAccount(groupId, account, request) {
2022
2030
  return unwrap(
2023
2031
  await this.httpClient.post(
2024
- `/admin-api/groups/${groupId}/accounts/${account}/seal`,
2032
+ `/admin-api/groups/${pathSegment(groupId)}/accounts/${pathSegment(account)}/seal`,
2025
2033
  request
2026
2034
  )
2027
2035
  );
2028
2036
  }
2029
2037
  async listGroupContexts(groupId) {
2030
- return unwrap(await this.httpClient.get(`/admin-api/groups/${groupId}/contexts`));
2038
+ return unwrap(await this.httpClient.get(`/admin-api/groups/${pathSegment(groupId)}/contexts`));
2031
2039
  }
2032
2040
  async addGroupMembers(groupId, request) {
2033
- await this.httpClient.post(`/admin-api/groups/${groupId}/members`, request);
2041
+ await this.httpClient.post(`/admin-api/groups/${pathSegment(groupId)}/members`, request);
2034
2042
  }
2035
2043
  async removeGroupMembers(groupId, request) {
2036
- await this.httpClient.post(`/admin-api/groups/${groupId}/members/remove`, request);
2044
+ await this.httpClient.post(`/admin-api/groups/${pathSegment(groupId)}/members/remove`, request);
2037
2045
  }
2038
2046
  /**
2039
2047
  * `identity` is the member's ACCOUNT (64 hex), as returned by
@@ -2041,7 +2049,7 @@ var AdminApiClient = class {
2041
2049
  * 64 hex, so nothing but the source tells them apart.
2042
2050
  */
2043
2051
  async updateMemberRole(groupId, identity, request) {
2044
- await this.httpClient.put(`/admin-api/groups/${groupId}/members/${identity}/role`, request);
2052
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/members/${pathSegment(identity)}/role`, request);
2045
2053
  }
2046
2054
  /**
2047
2055
  * `identity` is the member's ACCOUNT (64 hex), as returned by
@@ -2051,7 +2059,7 @@ var AdminApiClient = class {
2051
2059
  async getMemberCapabilities(groupId, identity) {
2052
2060
  return unwrap(
2053
2061
  await this.httpClient.get(
2054
- `/admin-api/groups/${groupId}/members/${identity}/capabilities`
2062
+ `/admin-api/groups/${pathSegment(groupId)}/members/${pathSegment(identity)}/capabilities`
2055
2063
  )
2056
2064
  );
2057
2065
  }
@@ -2061,10 +2069,10 @@ var AdminApiClient = class {
2061
2069
  * 64 hex, so nothing but the source tells them apart.
2062
2070
  */
2063
2071
  async setMemberCapabilities(groupId, identity, request) {
2064
- await this.httpClient.put(`/admin-api/groups/${groupId}/members/${identity}/capabilities`, request);
2072
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/members/${pathSegment(identity)}/capabilities`, request);
2065
2073
  }
2066
2074
  async setDefaultCapabilities(groupId, request) {
2067
- await this.httpClient.put(`/admin-api/groups/${groupId}/settings/default-capabilities`, request);
2075
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/settings/default-capabilities`, request);
2068
2076
  }
2069
2077
  /**
2070
2078
  * Make every member admitted to `groupId` from now on able to relay a
@@ -2146,13 +2154,13 @@ var AdminApiClient = class {
2146
2154
  return { changed: true, capabilities: next };
2147
2155
  }
2148
2156
  async setSubgroupVisibility(groupId, request) {
2149
- await this.httpClient.put(`/admin-api/groups/${groupId}/settings/subgroup-visibility`, request);
2157
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/settings/subgroup-visibility`, request);
2150
2158
  }
2151
2159
  async setTeeAdmissionPolicy(groupId, request) {
2152
- await this.httpClient.put(`/admin-api/groups/${groupId}/settings/tee-admission-policy`, request);
2160
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/settings/tee-admission-policy`, request);
2153
2161
  }
2154
2162
  async getTeeAdmissionPolicy(groupId) {
2155
- const response = await this.httpClient.get(`/admin-api/groups/${groupId}/settings/tee-admission-policy`);
2163
+ const response = await this.httpClient.get(`/admin-api/groups/${pathSegment(groupId)}/settings/tee-admission-policy`);
2156
2164
  return response.data ?? response;
2157
2165
  }
2158
2166
  /**
@@ -2162,7 +2170,7 @@ var AdminApiClient = class {
2162
2170
  * route for this policy.
2163
2171
  */
2164
2172
  async setTeeAuthoringPolicy(groupId, request) {
2165
- await this.httpClient.put(`/admin-api/groups/${groupId}/settings/tee-authoring-policy`, request);
2173
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/settings/tee-authoring-policy`, request);
2166
2174
  }
2167
2175
  /**
2168
2176
  * Turns TEE authorship off in the namespace: every TEE loses its authority,
@@ -2174,7 +2182,7 @@ var AdminApiClient = class {
2174
2182
  * (`teeAuthoringPolicyOp([])`); omit it on a node that holds that root.
2175
2183
  */
2176
2184
  async disableTeeAuthoringPolicy(groupId, options = {}) {
2177
- const path = `/admin-api/groups/${groupId}/settings/tee-authoring-policy`;
2185
+ const path = `/admin-api/groups/${pathSegment(groupId)}/settings/tee-authoring-policy`;
2178
2186
  if (options.rootProof === void 0) {
2179
2187
  await this.httpClient.delete(path);
2180
2188
  return;
@@ -2192,14 +2200,14 @@ var AdminApiClient = class {
2192
2200
  * {@link RootGuardedOpRequest}.
2193
2201
  */
2194
2202
  async transferOwnership(groupId, request) {
2195
- await this.httpClient.post(`/admin-api/groups/${groupId}/transfer-ownership`, request);
2203
+ await this.httpClient.post(`/admin-api/groups/${pathSegment(groupId)}/transfer-ownership`, request);
2196
2204
  }
2197
2205
  /**
2198
2206
  * Repoint the namespace's admin pin at `newAdmin`, a member of its root.
2199
2207
  * Owner-only, with the owner account's root proof; see {@link RootGuardedOpRequest}.
2200
2208
  */
2201
2209
  async changeNamespaceAdmin(namespaceId, request) {
2202
- await this.httpClient.post(`/admin-api/namespaces/${namespaceId}/admin`, request);
2210
+ await this.httpClient.post(`/admin-api/namespaces/${pathSegment(namespaceId)}/admin`, request);
2203
2211
  }
2204
2212
  /**
2205
2213
  * Delete a group through the owner-only path: it must hold no contexts, and
@@ -2207,20 +2215,20 @@ var AdminApiClient = class {
2207
2215
  * admin-level cascading delete.
2208
2216
  */
2209
2217
  async ownerDeleteGroup(groupId, request = {}) {
2210
- await this.httpClient.post(`/admin-api/groups/${groupId}/owner-delete`, request);
2218
+ await this.httpClient.post(`/admin-api/groups/${pathSegment(groupId)}/owner-delete`, request);
2211
2219
  }
2212
2220
  // ---- Group / member / context metadata ----
2213
2221
  async setGroupMetadata(groupId, request) {
2214
- await this.httpClient.put(`/admin-api/groups/${groupId}/metadata`, request);
2222
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/metadata`, request);
2215
2223
  }
2216
2224
  async getGroupMetadata(groupId) {
2217
2225
  const response = await this.httpClient.get(
2218
- `/admin-api/groups/${groupId}/metadata`
2226
+ `/admin-api/groups/${pathSegment(groupId)}/metadata`
2219
2227
  );
2220
2228
  return response?.data ?? null;
2221
2229
  }
2222
2230
  async setMemberMetadata(groupId, identity, request) {
2223
- await this.httpClient.put(`/admin-api/groups/${groupId}/members/${identity}/metadata`, request);
2231
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/members/${pathSegment(identity)}/metadata`, request);
2224
2232
  }
2225
2233
  /**
2226
2234
  * `identity` is the member's ACCOUNT (64 hex), as returned by
@@ -2229,28 +2237,28 @@ var AdminApiClient = class {
2229
2237
  */
2230
2238
  async getMemberMetadata(groupId, identity) {
2231
2239
  const response = await this.httpClient.get(
2232
- `/admin-api/groups/${groupId}/members/${identity}/metadata`
2240
+ `/admin-api/groups/${pathSegment(groupId)}/members/${pathSegment(identity)}/metadata`
2233
2241
  );
2234
2242
  return response?.data ?? null;
2235
2243
  }
2236
2244
  async setContextMetadata(groupId, contextId, request) {
2237
- await this.httpClient.put(`/admin-api/groups/${groupId}/contexts/${contextId}/metadata`, request);
2245
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/contexts/${pathSegment(contextId)}/metadata`, request);
2238
2246
  }
2239
2247
  async getContextMetadata(groupId, contextId) {
2240
2248
  const response = await this.httpClient.get(
2241
- `/admin-api/groups/${groupId}/contexts/${contextId}/metadata`
2249
+ `/admin-api/groups/${pathSegment(groupId)}/contexts/${pathSegment(contextId)}/metadata`
2242
2250
  );
2243
2251
  return response?.data ?? null;
2244
2252
  }
2245
2253
  async syncGroup(groupId) {
2246
- return unwrap(await this.httpClient.post(`/admin-api/groups/${groupId}/sync`, {}));
2254
+ return unwrap(await this.httpClient.post(`/admin-api/groups/${pathSegment(groupId)}/sync`, {}));
2247
2255
  }
2248
2256
  async upgradeGroup(groupId, request) {
2249
- return unwrap(await this.httpClient.post(`/admin-api/groups/${groupId}/upgrade`, request));
2257
+ return unwrap(await this.httpClient.post(`/admin-api/groups/${pathSegment(groupId)}/upgrade`, request));
2250
2258
  }
2251
2259
  async getGroupUpgradeStatus(groupId) {
2252
2260
  return unwrap(
2253
- await this.httpClient.get(`/admin-api/groups/${groupId}/upgrade/status`)
2261
+ await this.httpClient.get(`/admin-api/groups/${pathSegment(groupId)}/upgrade/status`)
2254
2262
  );
2255
2263
  }
2256
2264
  /**
@@ -2259,43 +2267,41 @@ var AdminApiClient = class {
2259
2267
  * envelope to unwrap here (unlike most admin reads).
2260
2268
  */
2261
2269
  async getMigrationStatus(namespaceId) {
2262
- const id = encodeURIComponent(namespaceId);
2263
- return this.httpClient.get(`/admin-api/groups/${id}/migration-status`);
2270
+ return this.httpClient.get(`/admin-api/groups/${pathSegment(namespaceId)}/migration-status`);
2264
2271
  }
2265
2272
  /** Per-group cascade-migration snapshots for a namespace. */
2266
2273
  async getCascadeStatus(namespaceId) {
2267
- const id = encodeURIComponent(namespaceId);
2268
2274
  return unwrap(
2269
- await this.httpClient.get(`/admin-api/groups/${id}/cascade-status`)
2275
+ await this.httpClient.get(`/admin-api/groups/${pathSegment(namespaceId)}/cascade-status`)
2270
2276
  );
2271
2277
  }
2272
2278
  async retryGroupUpgrade(groupId) {
2273
2279
  return unwrap(
2274
2280
  await this.httpClient.post(
2275
- `/admin-api/groups/${groupId}/upgrade/retry`,
2281
+ `/admin-api/groups/${pathSegment(groupId)}/upgrade/retry`,
2276
2282
  {}
2277
2283
  )
2278
2284
  );
2279
2285
  }
2280
2286
  /** Move `childGroupId` under `request.newParentId`. */
2281
2287
  async reparentGroup(childGroupId, request) {
2282
- const r = await this.httpClient.post(`/admin-api/groups/${childGroupId}/reparent`, request);
2288
+ const r = await this.httpClient.post(`/admin-api/groups/${pathSegment(childGroupId)}/reparent`, request);
2283
2289
  return r?.data ?? r;
2284
2290
  }
2285
2291
  async listSubgroups(groupId) {
2286
2292
  const response = await this.httpClient.get(
2287
- `/admin-api/groups/${groupId}/subgroups`
2293
+ `/admin-api/groups/${pathSegment(groupId)}/subgroups`
2288
2294
  );
2289
2295
  return response.subgroups ?? response.data ?? [];
2290
2296
  }
2291
2297
  async detachContextFromGroup(groupId, contextId) {
2292
- await this.httpClient.post(`/admin-api/groups/${groupId}/contexts/${contextId}/remove`, {});
2298
+ await this.httpClient.post(`/admin-api/groups/${pathSegment(groupId)}/contexts/${pathSegment(contextId)}/remove`, {});
2293
2299
  }
2294
2300
  // ---- Group Invitation & Join ----
2295
2301
  async createGroupInvitation(groupId, request) {
2296
2302
  return unwrap(
2297
2303
  await this.httpClient.post(
2298
- `/admin-api/groups/${groupId}/invite`,
2304
+ `/admin-api/groups/${pathSegment(groupId)}/invite`,
2299
2305
  request ?? {}
2300
2306
  )
2301
2307
  );
@@ -2308,7 +2314,7 @@ var AdminApiClient = class {
2308
2314
  async joinSubgroupInheritance(groupId) {
2309
2315
  return unwrap(
2310
2316
  await this.httpClient.post(
2311
- `/admin-api/groups/${groupId}/join-via-inheritance`,
2317
+ `/admin-api/groups/${pathSegment(groupId)}/join-via-inheritance`,
2312
2318
  {}
2313
2319
  )
2314
2320
  );
@@ -2350,27 +2356,27 @@ var AdminApiClient = class {
2350
2356
  }
2351
2357
  /** Leave a group (POST /admin-api/groups/:group_id/leave). */
2352
2358
  async leaveGroup(groupId) {
2353
- await this.httpClient.post(`/admin-api/groups/${groupId}/leave`, {});
2359
+ await this.httpClient.post(`/admin-api/groups/${pathSegment(groupId)}/leave`, {});
2354
2360
  }
2355
2361
  /** Leave a context (POST /admin-api/contexts/:context_id/leave). */
2356
2362
  async leaveContext(contextId) {
2357
- await this.httpClient.post(`/admin-api/contexts/${contextId}/leave`, {});
2363
+ await this.httpClient.post(`/admin-api/contexts/${pathSegment(contextId)}/leave`, {});
2358
2364
  }
2359
2365
  /** Leave a namespace (POST /admin-api/namespaces/:namespace_id/leave). */
2360
2366
  async leaveNamespace(namespaceId) {
2361
- await this.httpClient.post(`/admin-api/namespaces/${namespaceId}/leave`, {});
2367
+ await this.httpClient.post(`/admin-api/namespaces/${pathSegment(namespaceId)}/leave`, {});
2362
2368
  }
2363
2369
  /** Issue a group ownership proof (POST /admin-api/groups/:group_id/issue-ownership-proof). */
2364
2370
  async issueOwnershipProof(groupId, request) {
2365
2371
  return this.httpClient.post(
2366
- `/admin-api/groups/${groupId}/issue-ownership-proof`,
2372
+ `/admin-api/groups/${pathSegment(groupId)}/issue-ownership-proof`,
2367
2373
  request
2368
2374
  );
2369
2375
  }
2370
2376
  /** Issue a namespace ownership proof (POST /admin-api/groups/:group_id/issue-namespace-ownership-proof). */
2371
2377
  async issueNamespaceOwnershipProof(groupId, request) {
2372
2378
  return this.httpClient.post(
2373
- `/admin-api/groups/${groupId}/issue-namespace-ownership-proof`,
2379
+ `/admin-api/groups/${pathSegment(groupId)}/issue-namespace-ownership-proof`,
2374
2380
  request
2375
2381
  );
2376
2382
  }
@@ -2383,11 +2389,11 @@ var AdminApiClient = class {
2383
2389
  * 64 hex, so nothing but the source tells them apart.
2384
2390
  */
2385
2391
  async setMemberAutoFollow(groupId, identity, request) {
2386
- await this.httpClient.put(`/admin-api/groups/${groupId}/members/${identity}/auto-follow`, request);
2392
+ await this.httpClient.put(`/admin-api/groups/${pathSegment(groupId)}/members/${pathSegment(identity)}/auto-follow`, request);
2387
2393
  }
2388
2394
  /** Abort a namespace migration (POST /admin-api/groups/:namespace_id/migration/abort). */
2389
2395
  async abortMigration(namespaceId) {
2390
- return this.httpClient.post(`/admin-api/groups/${namespaceId}/migration/abort`, {});
2396
+ return this.httpClient.post(`/admin-api/groups/${pathSegment(namespaceId)}/migration/abort`, {});
2391
2397
  }
2392
2398
  };
2393
2399
 
@@ -7926,7 +7932,7 @@ var RelayClient = class {
7926
7932
  async describe(contextId) {
7927
7933
  const body = await this.json(
7928
7934
  "GET",
7929
- `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`
7935
+ `/admin-api/contexts/${pathSegment(contextId)}/intents`
7930
7936
  );
7931
7937
  return body.data;
7932
7938
  }
@@ -7963,7 +7969,7 @@ var RelayClient = class {
7963
7969
  });
7964
7970
  const body = await this.json(
7965
7971
  "POST",
7966
- `/admin-api/contexts/${encodeURIComponent(contextId)}/intents`,
7972
+ `/admin-api/contexts/${pathSegment(contextId)}/intents`,
7967
7973
  { method, argsJson, warrant, authorProof: this.config.authorProof }
7968
7974
  );
7969
7975
  return { rootHash: body.data.rootHash, returns: body.data.returns ?? null };
@@ -7977,7 +7983,7 @@ var RelayClient = class {
7977
7983
  async presenceIntent(contextId, body) {
7978
7984
  await this.jsonOnce(
7979
7985
  "POST",
7980
- `/admin-api/contexts/${encodeURIComponent(contextId)}/presence-intents`,
7986
+ `/admin-api/contexts/${pathSegment(contextId)}/presence-intents`,
7981
7987
  body
7982
7988
  );
7983
7989
  }
@@ -7991,7 +7997,7 @@ var RelayClient = class {
7991
7997
  const query = opts.author ? `?author=${encodeURIComponent(opts.author)}` : "";
7992
7998
  const body = await this.json(
7993
7999
  "GET",
7994
- `/admin-api/groups/${encodeURIComponent(groupId)}/context-intents${query}`
8000
+ `/admin-api/groups/${pathSegment(groupId)}/context-intents${query}`
7995
8001
  );
7996
8002
  return body.data;
7997
8003
  }
@@ -8055,7 +8061,7 @@ var RelayClient = class {
8055
8061
  });
8056
8062
  const body = await this.json(
8057
8063
  "POST",
8058
- `/admin-api/groups/${encodeURIComponent(input.groupId)}/context-intents`,
8064
+ `/admin-api/groups/${pathSegment(input.groupId)}/context-intents`,
8059
8065
  { warrant, authorProof: this.config.authorProof, initArgs }
8060
8066
  );
8061
8067
  return {
@@ -8071,7 +8077,7 @@ var RelayClient = class {
8071
8077
  async describeGovernance(groupId) {
8072
8078
  const body = await this.json(
8073
8079
  "GET",
8074
- `/admin-api/groups/${encodeURIComponent(groupId)}/governance-intents`
8080
+ `/admin-api/groups/${pathSegment(groupId)}/governance-intents`
8075
8081
  );
8076
8082
  return body.data;
8077
8083
  }
@@ -8203,7 +8209,7 @@ var RelayClient = class {
8203
8209
  return result;
8204
8210
  }
8205
8211
  async postGovernance(groupId, op, warrant) {
8206
- const body = await this.json("POST", `/admin-api/groups/${encodeURIComponent(groupId)}/governance-intents`, {
8212
+ const body = await this.json("POST", `/admin-api/groups/${pathSegment(groupId)}/governance-intents`, {
8207
8213
  warrant,
8208
8214
  authorProof: this.config.authorProof,
8209
8215
  op: hex(op.bytes)
@@ -9906,15 +9912,70 @@ function base643(bytes) {
9906
9912
  return btoa(binary);
9907
9913
  }
9908
9914
 
9915
+ // src/cloud/link-state.ts
9916
+ var KEY2 = "calimero.cloud.link.pending";
9917
+ var STATE_PARAM = "link_state";
9918
+ var LIFETIME_MS = 10 * 60 * 1e3;
9919
+ var ACCOUNT_HEX = /^[0-9a-f]{64}$/;
9920
+ var MAX_PENDING = 20;
9921
+ function readPending(now) {
9922
+ try {
9923
+ const raw = localStorage.getItem(KEY2);
9924
+ const pending = raw ? JSON.parse(raw) : {};
9925
+ return Object.fromEntries(
9926
+ Object.entries(pending).filter(
9927
+ ([, p]) => p && typeof p.expiresAt === "number" && p.expiresAt > now
9928
+ )
9929
+ );
9930
+ } catch {
9931
+ return {};
9932
+ }
9933
+ }
9934
+ function writePending(pending) {
9935
+ try {
9936
+ localStorage.setItem(KEY2, JSON.stringify(pending));
9937
+ } catch {
9938
+ }
9939
+ }
9940
+ function beginLinkState(account, returnTo) {
9941
+ if (!ACCOUNT_HEX.test(account)) {
9942
+ throw new Error("accountId must be 64 lowercase hex characters");
9943
+ }
9944
+ const url = new URL(returnTo);
9945
+ const now = Date.now();
9946
+ const pending = readPending(now);
9947
+ const bytes = crypto.getRandomValues(new Uint8Array(16));
9948
+ const state = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
9949
+ ""
9950
+ );
9951
+ pending[state] = { account, expiresAt: now + LIFETIME_MS };
9952
+ writePending(Object.fromEntries(Object.entries(pending).slice(-MAX_PENDING)));
9953
+ url.searchParams.set(STATE_PARAM, state);
9954
+ return url.toString();
9955
+ }
9956
+ function takeLinkState(search) {
9957
+ const state = new URLSearchParams(search).get(STATE_PARAM);
9958
+ const pending = readPending(Date.now());
9959
+ if (!state || !Object.prototype.hasOwnProperty.call(pending, state))
9960
+ return null;
9961
+ const entry = pending[state];
9962
+ delete pending[state];
9963
+ writePending(pending);
9964
+ return entry;
9965
+ }
9966
+ function withoutLinkState(search) {
9967
+ const params = new URLSearchParams(search);
9968
+ params.delete(STATE_PARAM);
9969
+ const rest = params.toString();
9970
+ return rest ? `?${rest}` : "";
9971
+ }
9972
+
9909
9973
  // src/cloud/link-redirect.ts
9910
9974
  var DEFAULT_CLOUD_WEB_URL = "https://cloud.calimero.network";
9911
9975
  function cloudLinkUrl(options) {
9912
- if (!/^[0-9a-f]{64}$/.test(options.accountId)) {
9913
- throw new Error("accountId must be 64 lowercase hex characters");
9914
- }
9915
9976
  const url = new URL(options.cloudWebUrl ?? DEFAULT_CLOUD_WEB_URL);
9916
9977
  url.searchParams.set("link-account", options.accountId);
9917
- url.searchParams.set("callback-url", options.returnTo);
9978
+ url.searchParams.set("callback-url", beginLinkState(options.accountId, options.returnTo));
9918
9979
  return url.toString();
9919
9980
  }
9920
9981
  function beginCloudLink(options) {
@@ -9925,6 +9986,7 @@ function readCloudLinkCallback(location2 = window.location) {
9925
9986
  const params = new URLSearchParams(location2.hash.replace(/^#/, ""));
9926
9987
  const error = params.get("error");
9927
9988
  if (error) {
9989
+ takeLinkState(location2.search ?? "");
9928
9990
  stripFragment();
9929
9991
  throw new Error(
9930
9992
  error === "denied" ? "the account link was declined at the cloud" : `the cloud refused this link: ${error}`
@@ -9933,12 +9995,18 @@ function readCloudLinkCallback(location2 = window.location) {
9933
9995
  const grant = params.get("grant");
9934
9996
  const account = params.get("account");
9935
9997
  if (!grant || !account) return null;
9998
+ if (takeLinkState(location2.search ?? "")?.account !== account) {
9999
+ stripFragment();
10000
+ throw new Error(
10001
+ "this account link answers a request this browser did not make - start the link again from here"
10002
+ );
10003
+ }
9936
10004
  stripFragment();
9937
10005
  return { grant, account };
9938
10006
  }
9939
10007
  function stripFragment() {
9940
10008
  if (typeof history === "undefined" || !history.replaceState) return;
9941
- history.replaceState(null, "", location.pathname + location.search);
10009
+ history.replaceState(null, "", location.pathname + withoutLinkState(location.search));
9942
10010
  }
9943
10011
  async function completeCloudLink(options) {
9944
10012
  const root = await resolveRoot(options.root);
@@ -9958,7 +10026,7 @@ async function completeCloudLink(options) {
9958
10026
  var DEVICE_PARAM = "enrol-device";
9959
10027
  var KEM_PARAM = "enrol-kem";
9960
10028
  var CALLBACK_PARAM = "callback-url";
9961
- var STATE_PARAM = "state";
10029
+ var STATE_PARAM2 = "state";
9962
10030
  function deviceEnrolmentUrl(options) {
9963
10031
  assertHex(options.devicePublicKey, "devicePublicKey");
9964
10032
  assertHex(options.kemPublicKey, "kemPublicKey");
@@ -9968,7 +10036,7 @@ function deviceEnrolmentUrl(options) {
9968
10036
  url.searchParams.set(DEVICE_PARAM, options.devicePublicKey);
9969
10037
  url.searchParams.set(KEM_PARAM, options.kemPublicKey);
9970
10038
  url.searchParams.set(CALLBACK_PARAM, options.returnTo);
9971
- if (options.state) url.searchParams.set(STATE_PARAM, options.state);
10039
+ if (options.state) url.searchParams.set(STATE_PARAM2, options.state);
9972
10040
  return url.toString();
9973
10041
  }
9974
10042
  function beginDeviceEnrolment(options) {
@@ -10135,7 +10203,7 @@ async function resolveRelayFromInvitation(input) {
10135
10203
  relayUrl: chosen.relayUrl,
10136
10204
  // The cloud's ready-made URL when it gave one, so the path is never rebuilt
10137
10205
  // from a guess; the fallback is the documented route on the same origin.
10138
- admitUrl: chosen.admitUrl ?? `${chosen.relayUrl.replace(/\/+$/, "")}/admin-api/namespaces/${input.namespaceId}/admit`,
10206
+ admitUrl: chosen.admitUrl ?? `${chosen.relayUrl.replace(/\/+$/, "")}/admin-api/namespaces/${pathSegment(input.namespaceId)}/admit`,
10139
10207
  admitterAccount: chosen.account,
10140
10208
  peerId: chosen.peerId,
10141
10209
  writable: chosen.canExecute,
@@ -10373,7 +10441,7 @@ async function joinWithNode(input) {
10373
10441
  };
10374
10442
  }
10375
10443
  const body = { invitation: input.invitation, signedOp };
10376
- const url = input.admitUrl ?? `${origin(input.nodeUrl)}/admin-api/namespaces/${input.namespaceId}/admit`;
10444
+ const url = input.admitUrl ?? `${origin(input.nodeUrl)}/admin-api/namespaces/${pathSegment(input.namespaceId)}/admit`;
10377
10445
  const doFetch = input.deps?.fetch ?? fetch;
10378
10446
  let response;
10379
10447
  try {
@@ -11066,7 +11134,6 @@ function joinOf(row) {
11066
11134
  reportedAt: typeof join.reported_at === "string" ? join.reported_at : null
11067
11135
  };
11068
11136
  }
11069
- var ACCOUNT_HEX = /^[0-9a-f]{64}$/;
11070
11137
  function accountHexOrNull(value) {
11071
11138
  return typeof value === "string" && ACCOUNT_HEX.test(value) ? value : null;
11072
11139
  }
@@ -11286,7 +11353,7 @@ var CloudClient = class {
11286
11353
  async getNamespaceRelays(namespaceId) {
11287
11354
  const body = await this.request(
11288
11355
  "GET",
11289
- `/api/cloud/me/namespaces/${encodeURIComponent(namespaceId)}/relays`
11356
+ `/api/cloud/me/namespaces/${pathSegment(namespaceId)}/relays`
11290
11357
  );
11291
11358
  return (body.relays ?? []).map((row) => {
11292
11359
  const relayUrl = row.relay_url ?? null;
@@ -11344,7 +11411,7 @@ var CloudClient = class {
11344
11411
  * a node holding the namespace, so there is no hosted admitter.
11345
11412
  */
11346
11413
  async getNamespaceRouting(namespaceId) {
11347
- const body = await this.request("GET", `/api/cloud/namespaces/${encodeURIComponent(namespaceId)}/admitters`, {
11414
+ const body = await this.request("GET", `/api/cloud/namespaces/${pathSegment(namespaceId)}/admitters`, {
11348
11415
  anonymous: true,
11349
11416
  headers: await this.routingProof(namespaceId)
11350
11417
  });
@@ -11388,7 +11455,7 @@ var CloudClient = class {
11388
11455
  * challenge somewhere this client cannot reach the key.
11389
11456
  */
11390
11457
  async getRoutingChallenge(namespaceId) {
11391
- const body = await this.request("GET", `/api/cloud/namespaces/${encodeURIComponent(namespaceId)}/challenge`, {
11458
+ const body = await this.request("GET", `/api/cloud/namespaces/${pathSegment(namespaceId)}/challenge`, {
11392
11459
  anonymous: true
11393
11460
  });
11394
11461
  return {
@@ -11419,7 +11486,7 @@ var CloudClient = class {
11419
11486
  * the account from the certificate and refuses a mismatch.
11420
11487
  */
11421
11488
  async getAccountRelaysChallenge(accountId) {
11422
- const body = await this.request("GET", `/api/cloud/accounts/${encodeURIComponent(accountId)}/challenge`, {
11489
+ const body = await this.request("GET", `/api/cloud/accounts/${pathSegment(accountId)}/challenge`, {
11423
11490
  anonymous: true
11424
11491
  });
11425
11492
  return {
@@ -11455,7 +11522,7 @@ var CloudClient = class {
11455
11522
  );
11456
11523
  }
11457
11524
  const challenge = await this.getAccountRelaysChallenge(accountId);
11458
- const body = await this.request("GET", `/api/cloud/accounts/${encodeURIComponent(accountId)}/relays`, {
11525
+ const body = await this.request("GET", `/api/cloud/accounts/${pathSegment(accountId)}/relays`, {
11459
11526
  anonymous: true,
11460
11527
  headers: await routingProofHeaders(challenge, this.routingCredential)
11461
11528
  });
@@ -11579,7 +11646,7 @@ var CloudClient = class {
11579
11646
  const body = options.admitterAddrs === void 0 ? {} : { admitter_addrs: [...options.admitterAddrs] };
11580
11647
  return this.request(
11581
11648
  "POST",
11582
- `/api/cloud/namespaces/${encodeURIComponent(namespaceId)}/enable-ha`,
11649
+ `/api/cloud/namespaces/${pathSegment(namespaceId)}/enable-ha`,
11583
11650
  { body }
11584
11651
  );
11585
11652
  }
@@ -11592,7 +11659,7 @@ var CloudClient = class {
11592
11659
  async enableFoundedNamespaceHa(namespaceId, ownershipProof) {
11593
11660
  return this.request(
11594
11661
  "POST",
11595
- `/api/cloud/me/namespaces/${encodeURIComponent(namespaceId)}/enable-ha`,
11662
+ `/api/cloud/me/namespaces/${pathSegment(namespaceId)}/enable-ha`,
11596
11663
  { body: { groups: [], ownership_proof: ownershipProof } }
11597
11664
  );
11598
11665
  }
@@ -11629,7 +11696,7 @@ var CloudClient = class {
11629
11696
  try {
11630
11697
  return await this.request(
11631
11698
  "POST",
11632
- `/api/cloud/accounts/${encodeURIComponent(account)}/namespaces/${encodeURIComponent(namespace)}/enable-ha`,
11699
+ `/api/cloud/accounts/${pathSegment(account)}/namespaces/${pathSegment(namespace)}/enable-ha`,
11633
11700
  { body: { ownership_proof: proof }, anonymous: true }
11634
11701
  );
11635
11702
  } catch (err) {
@@ -11654,7 +11721,7 @@ var CloudClient = class {
11654
11721
  async disableNamespaceHa(namespaceId) {
11655
11722
  return this.request(
11656
11723
  "POST",
11657
- `/api/cloud/namespaces/${encodeURIComponent(namespaceId)}/disable-ha`,
11724
+ `/api/cloud/namespaces/${pathSegment(namespaceId)}/disable-ha`,
11658
11725
  { body: {} }
11659
11726
  );
11660
11727
  }
@@ -11794,7 +11861,7 @@ var CloudClient = class {
11794
11861
  const portal = options.portalUrl.replace(/\/+$/, "");
11795
11862
  const query = new URLSearchParams({
11796
11863
  "link-account": options.accountId,
11797
- "callback-url": options.callbackUrl
11864
+ "callback-url": beginLinkState(options.accountId, options.callbackUrl)
11798
11865
  });
11799
11866
  return { url: `${portal}/?${query.toString()}`, accountId: options.accountId };
11800
11867
  }
@@ -11804,14 +11871,19 @@ var CloudClient = class {
11804
11871
  * Returns `{}` when there is nothing to read, so it is safe to call on every
11805
11872
  * load — which is how a redirected app finds out it has been sent back.
11806
11873
  *
11874
+ * A grant is only returned when it answers a handoff this browser began, for the
11875
+ * account that handoff named; anything else comes back as
11876
+ * `error: 'unexpected_callback'`, because the redirect URL can be forwarded.
11877
+ *
11807
11878
  * @param href defaults to the current location, when there is one.
11808
11879
  */
11809
11880
  static readAccountLinkCallback(href) {
11810
11881
  const source = href ?? (typeof window === "undefined" ? "" : window.location.href);
11811
11882
  if (!source) return {};
11812
11883
  let hash;
11884
+ let search;
11813
11885
  try {
11814
- hash = new URL(source).hash;
11886
+ ({ hash, search } = new URL(source));
11815
11887
  } catch {
11816
11888
  return {};
11817
11889
  }
@@ -11820,6 +11892,10 @@ var CloudClient = class {
11820
11892
  const accountId = params.get("account");
11821
11893
  const error = params.get("error");
11822
11894
  const out = {};
11895
+ if (error) takeLinkState(search);
11896
+ if (grant && takeLinkState(search)?.account !== accountId) {
11897
+ return { error: "unexpected_callback" };
11898
+ }
11823
11899
  if (grant) out.grant = grant;
11824
11900
  if (accountId) out.accountId = accountId;
11825
11901
  if (error) out.error = error;