@calimero-network/mero-js 25.3.3 → 26.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -82,8 +82,6 @@ __export(index_exports, {
82
82
  SseClient: () => SseClient,
83
83
  StaleTransportKeyError: () => StaleTransportKeyError,
84
84
  UnknownRelayError: () => UnknownRelayError,
85
- WarrantNonceExhaustedError: () => WarrantNonceExhaustedError,
86
- WarrantNonceRouteUnavailableError: () => WarrantNonceRouteUnavailableError,
87
85
  WebHttpClient: () => WebHttpClient,
88
86
  WsClient: () => WsClient,
89
87
  accountForRoot: () => accountForRoot,
@@ -100,7 +98,6 @@ __export(index_exports, {
100
98
  attestAgent: () => attestAgent,
101
99
  attestKeyBinding: () => attestKeyBinding,
102
100
  attestRelayNodeKey: () => attestRelayNodeKey,
103
- authorNonceLookup: () => authorNonceLookup,
104
101
  beginCloudLink: () => beginCloudLink,
105
102
  beginDeviceEnrolment: () => beginDeviceEnrolment,
106
103
  bootstrapFromInvitation: () => bootstrapFromInvitation,
@@ -151,7 +148,6 @@ __export(index_exports, {
151
148
  createProofAuthorizer: () => createProofAuthorizer,
152
149
  createProofSigner: () => createProofSigner,
153
150
  createQuoteVerifier: () => createQuoteVerifier,
154
- createRecoveringNonceSource: () => createRecoveringNonceSource,
155
151
  createRetryableMethod: () => createRetryableMethod,
156
152
  createSealedFetch: () => createSealedFetch,
157
153
  createSignedReleaseSealedFetch: () => createSignedReleaseSealedFetch,
@@ -225,7 +221,6 @@ __export(index_exports, {
225
221
  parseCreationWarrant: () => parseCreationWarrant,
226
222
  parseDeviceCredential: () => parseDeviceCredential,
227
223
  parseGovernanceWarrant: () => parseGovernanceWarrant,
228
- parseWarrantNonce: () => parseWarrantNonce,
229
224
  persistedNonces: () => persistedNonces,
230
225
  pinRelayNodeKey: () => pinRelayNodeKey,
231
226
  presenceStatementBytes: () => presenceStatementBytes,
@@ -1519,50 +1514,6 @@ async function redeemInvitation(parsed, redeemer) {
1519
1514
  }
1520
1515
 
1521
1516
  // src/admin-api/admin-client.ts
1522
- var WarrantNonceRouteUnavailableError = class extends Error {
1523
- constructor(path, cause) {
1524
- super(
1525
- `this node does not serve warrant-nonce discovery (404 on ${path}); it predates the route, so a client must fall back to its own nonce counter`
1526
- );
1527
- this.path = path;
1528
- this.cause = cause;
1529
- this.name = "WarrantNonceRouteUnavailableError";
1530
- }
1531
- };
1532
- function u64FromBody(body, parsed, field) {
1533
- const value = parsed[field];
1534
- if (value === void 0 || value === null) return void 0;
1535
- const digits = new RegExp(`"${field}"\\s*:\\s*(\\d+)`).exec(body);
1536
- if (digits) return BigInt(digits[1]);
1537
- if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
1538
- throw new Error(`warrant-nonce response field \`${field}\` is not a u64: ${JSON.stringify(value)}`);
1539
- }
1540
- function parseWarrantNonce(body) {
1541
- const parsed = JSON.parse(body);
1542
- const data = parsed?.data;
1543
- if (!data || typeof data !== "object") {
1544
- throw new Error(`warrant-nonce response had no \`data\`: ${body.slice(0, 200)}`);
1545
- }
1546
- const common = {
1547
- contextId: String(data.contextId ?? ""),
1548
- authorDeviceKey: String(data.authorDeviceKey ?? ""),
1549
- seen: data.seen === true,
1550
- highWaterNonce: u64FromBody(body, data, "highWaterNonce"),
1551
- // Not defaulted: a node that stopped sending it is a node whose window this
1552
- // client would be guessing at, and the window is not a nonce input anyway.
1553
- windowWidth: u64FromBody(body, data, "windowWidth") ?? 0n
1554
- };
1555
- const nextNonce = u64FromBody(body, data, "nextNonce");
1556
- if (nextNonce === void 0) {
1557
- return {
1558
- ...common,
1559
- kind: "exhausted",
1560
- seen: true,
1561
- highWaterNonce: common.highWaterNonce ?? 0xffffffffffffffffn
1562
- };
1563
- }
1564
- return { ...common, kind: "open", nextNonce };
1565
- }
1566
1517
  function unwrap(response) {
1567
1518
  return response?.data;
1568
1519
  }
@@ -1776,14 +1727,6 @@ var AdminApiClient = class {
1776
1727
  );
1777
1728
  return r ?? { contextId, resyncStarted: true };
1778
1729
  }
1779
- async inviteSpecializedNode(request) {
1780
- return unwrap(
1781
- await this.httpClient.post(
1782
- "/admin-api/contexts/invite-specialized-node",
1783
- request
1784
- )
1785
- );
1786
- }
1787
1730
  async updateContextApplication(contextId, request) {
1788
1731
  await this.httpClient.post(`/admin-api/contexts/${pathSegment(contextId)}/application`, request);
1789
1732
  }
@@ -1975,29 +1918,6 @@ var AdminApiClient = class {
1975
1918
  async getNodeIdentity() {
1976
1919
  return unwrap(await this.httpClient.get("/admin-api/identity"));
1977
1920
  }
1978
- /**
1979
- * @deprecated Use {@link getNodeIdentity} instead. Every field this returns is
1980
- * node-level, so the namespace was only ever decoration — it now delegates to
1981
- * the node-level route and reshapes the answer.
1982
- *
1983
- * Three consequences of that delegation, all visible to a caller who relied on
1984
- * the old route's behaviour:
1985
- *
1986
- * - **No participation gate.** The namespace route answered 404 when this node
1987
- * had never joined the namespace; this answers whatever the node's identity
1988
- * is, regardless. If you were using it to ask "am I in this namespace?", ask
1989
- * {@link listNamespaces} instead — that is the question you were really
1990
- * asking.
1991
- * - **`namespaceId` is echoed verbatim.** The old route resolved a subgroup id
1992
- * to its root namespace and returned that; passing a subgroup id here gives
1993
- * the subgroup id back.
1994
- * - **404 means something else.** Previously "not my namespace", now "this node
1995
- * holds no account root yet".
1996
- */
1997
- async getNamespaceIdentity(namespaceId) {
1998
- const node = await this.getNodeIdentity();
1999
- return { namespaceId, publicKey: node.publicKey, account: node.accountId };
2000
- }
2001
1921
  /**
2002
1922
  * Ask this node to run one method on a member's behalf, under a warrant that
2003
1923
  * member signed.
@@ -2471,104 +2391,6 @@ var AdminApiClient = class {
2471
2391
  );
2472
2392
  return response.members;
2473
2393
  }
2474
- /**
2475
- * Where `authorDeviceKey` stands in its warrant-nonce sequence in `contextId`.
2476
- *
2477
- * # What this is for
2478
- *
2479
- * The nonce is the one input to a warrant that is stateful on the *client*.
2480
- * Everything else an author needs it either holds (its keys, the method, the
2481
- * args) or can read from the relay ({@link RelayClient.describe} gives the
2482
- * executor account and the grant). So an author that has lost its counter —
2483
- * a browser keyholder in partitioned or periodically cleared storage, which
2484
- * is its normal lifecycle rather than a fault — otherwise has exactly one
2485
- * strategy: guess upward and burn a refused round trip per wrong guess, with
2486
- * each refusal indistinguishable at the API from a genuinely spent nonce.
2487
- *
2488
- * Read {@link WarrantNonceOpen.nextNonce} and mint there. If you still hold
2489
- * your own counter, mint at `max(ownNext, nextNonce)`: nonce state folds per
2490
- * peer, so a node can only be BEHIND a client whose warrants it has not
2491
- * applied — never ahead of the truth. An author spread across several relays
2492
- * asks each and takes the highest answer. {@link createRecoveringNonceSource}
2493
- * does all of that.
2494
- *
2495
- * Purely a read. It spends nothing and advances nothing, so it may be called
2496
- * as often as you like.
2497
- *
2498
- * # Forward compatibility — this route is NEW
2499
- *
2500
- * It is not on core `master` yet, so a current node answers **404**, which
2501
- * this method surfaces as {@link WarrantNonceRouteUnavailableError} rather
2502
- * than as an opaque {@link HTTPError}. That distinction matters because the
2503
- * handler itself never 404s: a device with no row is a `200` with
2504
- * `seen: false`, and a malformed id is a `400`. So a 404 here means the node
2505
- * does not serve the route at all — an older build, or a deployment that has
2506
- * not re-mounted it — and the answer is to fall back to a local counter, not
2507
- * to retry.
2508
- *
2509
- * # Authentication
2510
- *
2511
- * On the PROTECTED admin router, unlike the two delegated-execution routes it
2512
- * sits beside: it takes another principal's device key as input and reports
2513
- * that principal's activity. A `--public-intents` deployment does not serve
2514
- * this anonymously, so an admin credential is required.
2515
- *
2516
- * @param authorDeviceKey the device's signing key, base58 (43-44 chars) or
2517
- * hex (exactly 64). The node accepts either; the two never collide.
2518
- */
2519
- async getWarrantNonce(contextId, authorDeviceKey) {
2520
- const path = `/admin-api/contexts/${pathSegment(contextId)}/warrant-nonce/${pathSegment(authorDeviceKey)}`;
2521
- let raw;
2522
- try {
2523
- raw = await this.httpClient.get(path, { parse: "text" });
2524
- } catch (error) {
2525
- if (error instanceof HTTPError && error.status === 404) {
2526
- throw new WarrantNonceRouteUnavailableError(path, error);
2527
- }
2528
- throw error;
2529
- }
2530
- return parseWarrantNonce(raw);
2531
- }
2532
- /**
2533
- * The same answer, asked for the way a delegated client has to ask.
2534
- *
2535
- * {@link getWarrantNonce} is on the protected admin router, so a keyholder
2536
- * writing through a relay — the client this whole facility exists for — is
2537
- * refused it: measured `403` for a session carrying exactly
2538
- * `context:intent`, `context:query`, `context:subscribe`. That client has no
2539
- * admin credential and cannot be given one without defeating the point of the
2540
- * delegated surface.
2541
- *
2542
- * What it does have is the credential proving the device is its own, which it
2543
- * already sends on every write. Presenting it here makes the request
2544
- * self-scoped: you learn a device's position only by proving, at call time,
2545
- * that the device is yours. So there is no oracle to protect against, and no
2546
- * reason for an admin gate.
2547
- *
2548
- * The device key is NOT a parameter. It is read from the verified
2549
- * certificate server-side, which is the only reading that cannot disagree
2550
- * with the signature — a key supplied alongside would be a second spelling of
2551
- * the same fact, and two spellings can differ.
2552
- *
2553
- * Returns the identical shape {@link getWarrantNonce} returns, so a caller can
2554
- * swap one for the other without touching what it does with the answer.
2555
- *
2556
- * @param authorProof hex-encoded borsh `AccountProof<DeviceCert>` — the same
2557
- * value passed as `authorProof` to the relay client.
2558
- */
2559
- async getWarrantNonceAsAuthor(contextId, authorProof) {
2560
- const path = `/admin-api/contexts/${pathSegment(contextId)}/warrant-nonce`;
2561
- let raw;
2562
- try {
2563
- raw = await this.httpClient.post(path, { authorProof }, { parse: "text" });
2564
- } catch (error) {
2565
- if (error instanceof HTTPError && error.status === 404) {
2566
- throw new WarrantNonceRouteUnavailableError(path, error);
2567
- }
2568
- throw error;
2569
- }
2570
- return parseWarrantNonce(raw);
2571
- }
2572
2394
  /**
2573
2395
  * Member accounts in the group and the devices bound to each. An admin node
2574
2396
  * sees every account; a plain member only its own. `403` if this node is not a
@@ -2891,15 +2713,6 @@ var AdminApiClient = class {
2891
2713
  async teeAttest(request) {
2892
2714
  return unwrap(await this.httpClient.post("/admin-api/tee/attest", request));
2893
2715
  }
2894
- /**
2895
- * @deprecated Nodes no longer serve `/admin-api/tee/verify-quote` (removed in
2896
- * core#3262), so this fails. Verify quotes with `createQuoteVerifier`.
2897
- */
2898
- async teeVerifyQuote(request) {
2899
- return unwrap(
2900
- await this.httpClient.post("/admin-api/tee/verify-quote", request)
2901
- );
2902
- }
2903
2716
  // ---- Network ----
2904
2717
  async getPeersCount() {
2905
2718
  return this.httpClient.get("/admin-api/peers");
@@ -10083,81 +9896,6 @@ function createLocalStorageNonceSource(key, storage) {
10083
9896
  }
10084
9897
  };
10085
9898
  }
10086
- function authorNonceLookup(options) {
10087
- return {
10088
- getWarrantNonce: (contextId, _authorDeviceKey) => options.client.getWarrantNonceAsAuthor(contextId, options.authorProof)
10089
- };
10090
- }
10091
- var WarrantNonceExhaustedError = class extends Error {
10092
- constructor(contextId, authorDeviceKey) {
10093
- super(
10094
- `author device ${authorDeviceKey} has spent every nonce in context ${contextId}; it must re-key to write again`
10095
- );
10096
- this.contextId = contextId;
10097
- this.authorDeviceKey = authorDeviceKey;
10098
- this.name = "WarrantNonceExhaustedError";
10099
- }
10100
- };
10101
- function createRecoveringNonceSource(options) {
10102
- const lookups = Array.isArray(options.lookups) ? options.lookups : [options.lookups];
10103
- if (lookups.length === 0) {
10104
- throw new Error("a recovering nonce source needs at least one node to ask");
10105
- }
10106
- const local = options.local ?? createMemoryNonceSource(1);
10107
- const onLookupFailure = options.onLookupFailure ?? "throw";
10108
- let floor = null;
10109
- let pending = null;
10110
- const resolveFloor = async () => {
10111
- const answers = await Promise.allSettled(
10112
- lookups.map(
10113
- (lookup) => lookup.getWarrantNonce(options.contextId, options.authorDeviceKey)
10114
- )
10115
- );
10116
- let highest = null;
10117
- let exhausted = false;
10118
- for (const answer of answers) {
10119
- if (answer.status !== "fulfilled") continue;
10120
- const state = answer.value;
10121
- if (state.kind === "exhausted") {
10122
- exhausted = true;
10123
- continue;
10124
- }
10125
- if (highest === null || state.nextNonce > highest) highest = state.nextNonce;
10126
- }
10127
- if (highest === null && exhausted) {
10128
- throw new WarrantNonceExhaustedError(options.contextId, options.authorDeviceKey);
10129
- }
10130
- if (highest === null) {
10131
- const first = answers.find((a) => a.status === "rejected");
10132
- const reason = first && first.status === "rejected" ? first.reason : void 0;
10133
- if (onLookupFailure === "local") {
10134
- floor = 0n;
10135
- return;
10136
- }
10137
- throw reason instanceof Error ? reason : new Error(`no node could answer where the warrant-nonce sequence stands: ${String(reason)}`);
10138
- }
10139
- floor = highest;
10140
- await local.advanceTo?.(highest);
10141
- };
10142
- return {
10143
- async next() {
10144
- if (floor === null) {
10145
- pending ?? (pending = resolveFloor().finally(() => {
10146
- pending = null;
10147
- }));
10148
- await pending;
10149
- }
10150
- const mine = await local.next();
10151
- const value = floor !== null && floor > mine ? floor : mine;
10152
- floor = value + 1n;
10153
- return value;
10154
- },
10155
- async advanceTo(wanted) {
10156
- if (floor === null || wanted > floor) floor = wanted;
10157
- await local.advanceTo?.(wanted);
10158
- }
10159
- };
10160
- }
10161
9899
 
10162
9900
  // src/cloud/connect.ts
10163
9901
  async function connectCloud(options) {