@calimero-network/mero-js 19.16.0 → 19.18.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.
package/dist/index.mjs CHANGED
@@ -420,9 +420,9 @@ function createHttpClient(transport) {
420
420
  }
421
421
  function createBrowserHttpClient(options) {
422
422
  const transport = {
423
- // Wrap fetch in arrow function to prevent "Illegal invocation" error
424
- // This preserves the correct 'this' context when fetch is called
425
- fetch: (url, init) => globalThis.fetch(url, init),
423
+ // globalThis.fetch must be called as a method on globalThis, not through a
424
+ // variable, or browsers throw "Illegal invocation".
425
+ fetch: (url, init) => options.fetch ? options.fetch(url, init) : globalThis.fetch(url, init),
426
426
  baseUrl: options.baseUrl,
427
427
  getAuthToken: options.getAuthToken,
428
428
  getProof: options.getProof,
@@ -1913,6 +1913,196 @@ function buildAuthLoginUrl(nodeUrl, opts) {
1913
1913
  return `${base}/auth/login?${params.toString()}`;
1914
1914
  }
1915
1915
 
1916
+ // src/crypto/internal.ts
1917
+ var PKCS8_ED25519_PREFIX = new Uint8Array([
1918
+ 48,
1919
+ 46,
1920
+ 2,
1921
+ 1,
1922
+ 0,
1923
+ 48,
1924
+ 5,
1925
+ 6,
1926
+ 3,
1927
+ 43,
1928
+ 101,
1929
+ 112,
1930
+ 4,
1931
+ 34,
1932
+ 4,
1933
+ 32
1934
+ ]);
1935
+ function u32le(value) {
1936
+ const out = new Uint8Array(4);
1937
+ new DataView(out.buffer).setUint32(0, value, true);
1938
+ return out;
1939
+ }
1940
+ function hex(bytes) {
1941
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1942
+ }
1943
+ function fromHex(value, label, expectedBytes) {
1944
+ const clean = value.trim();
1945
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
1946
+ throw new Error(
1947
+ `${label} must be ${expectedBytes * 2} hex characters, got ${clean.length}`
1948
+ );
1949
+ }
1950
+ const bytes = new Uint8Array(expectedBytes);
1951
+ for (let i = 0; i < expectedBytes; i += 1) {
1952
+ bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
1953
+ }
1954
+ return bytes;
1955
+ }
1956
+ function fromHexUnsized(value, label) {
1957
+ const clean = value.trim();
1958
+ if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
1959
+ throw new Error(`${label} must be an even number of hex characters`);
1960
+ }
1961
+ const bytes = new Uint8Array(clean.length / 2);
1962
+ for (let i = 0; i < bytes.length; i += 1) {
1963
+ bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
1964
+ }
1965
+ return bytes;
1966
+ }
1967
+ function u64le(value) {
1968
+ const out = new Uint8Array(8);
1969
+ new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
1970
+ return out;
1971
+ }
1972
+ function concat(...parts) {
1973
+ const out = new Uint8Array(parts.reduce((total, p) => total + p.length, 0));
1974
+ let offset = 0;
1975
+ for (const part of parts) {
1976
+ out.set(part, offset);
1977
+ offset += part.length;
1978
+ }
1979
+ return out;
1980
+ }
1981
+ async function domainHash(domain, parts) {
1982
+ const chunks = [u64le(domain.length), domain];
1983
+ for (const part of parts) {
1984
+ chunks.push(u64le(part.length), part);
1985
+ }
1986
+ return new Uint8Array(
1987
+ await crypto.subtle.digest("SHA-256", concat(...chunks))
1988
+ );
1989
+ }
1990
+ async function importSigningKey(secretHex) {
1991
+ const seed = fromHex(secretHex, "deviceSecret", 32);
1992
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
1993
+ try {
1994
+ return await crypto.subtle.importKey(
1995
+ "pkcs8",
1996
+ pkcs8,
1997
+ { name: "Ed25519" },
1998
+ false,
1999
+ ["sign"]
2000
+ );
2001
+ } catch (cause) {
2002
+ const detail = cause instanceof Error ? `: ${cause.message}` : "";
2003
+ throw new Error(
2004
+ "this runtime has no WebCrypto Ed25519, so a warrant cannot be signed here. Node 18.4+, Safari 17+, Firefox 129+ and Chrome 137+ have it; otherwise mint the warrant with `merod account warrant`" + detail
2005
+ );
2006
+ }
2007
+ }
2008
+ async function derivePublicKey(secretHex) {
2009
+ const seed = fromHex(secretHex, "deviceSecret", 32);
2010
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2011
+ const key = await crypto.subtle.importKey(
2012
+ "pkcs8",
2013
+ pkcs8,
2014
+ { name: "Ed25519" },
2015
+ true,
2016
+ ["sign"]
2017
+ );
2018
+ const jwk = await crypto.subtle.exportKey("jwk", key);
2019
+ const raw = jwk.x;
2020
+ return Uint8Array.from(
2021
+ atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2022
+ (c) => c.charCodeAt(0)
2023
+ );
2024
+ }
2025
+
2026
+ // src/request/request-sig.ts
2027
+ var SIGN_DOMAIN = new TextEncoder().encode("calimero.auth.request.v1");
2028
+ var BODY_DOMAIN = new TextEncoder().encode("calimero.auth.request.body.v1");
2029
+ function borshString(value) {
2030
+ const bytes = new TextEncoder().encode(value);
2031
+ return concat(u32le(bytes.length), bytes);
2032
+ }
2033
+ async function requestBodyHash(body = new Uint8Array(0)) {
2034
+ const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body;
2035
+ return domainHash(BODY_DOMAIN, [bytes]);
2036
+ }
2037
+ async function signRequest(input) {
2038
+ const key = await importSigningKey(input.signerSecret);
2039
+ const bodyHash = await requestBodyHash(input.body);
2040
+ const issuedAt = u64le(input.issuedAt);
2041
+ const expiresAt = u64le(input.expiresAt);
2042
+ const encoder = new TextEncoder();
2043
+ const preimage = await domainHash(SIGN_DOMAIN, [
2044
+ encoder.encode(input.method),
2045
+ encoder.encode(input.path),
2046
+ bodyHash,
2047
+ issuedAt,
2048
+ expiresAt
2049
+ ]);
2050
+ const signature = new Uint8Array(
2051
+ await crypto.subtle.sign({ name: "Ed25519" }, key, preimage)
2052
+ );
2053
+ return hex(
2054
+ concat(
2055
+ borshString(input.method),
2056
+ borshString(input.path),
2057
+ bodyHash,
2058
+ issuedAt,
2059
+ expiresAt,
2060
+ signature
2061
+ )
2062
+ );
2063
+ }
2064
+
2065
+ // src/request/caller-proof.ts
2066
+ function callerProof(input) {
2067
+ const credential = fromHexUnsized(input.credential, "credential");
2068
+ const request = fromHexUnsized(input.request, "request signature");
2069
+ const session = input.session === void 0 ? new Uint8Array([0]) : concat(new Uint8Array([1]), fromHexUnsized(input.session, "session statement"));
2070
+ return hex(concat(credential, session, request));
2071
+ }
2072
+
2073
+ // src/request/proof-signer.ts
2074
+ function bodyBytes(body) {
2075
+ if (body === void 0 || body === null) return new Uint8Array(0);
2076
+ if (typeof body === "string") return body;
2077
+ if (body instanceof Uint8Array) return body;
2078
+ if (body instanceof ArrayBuffer) return new Uint8Array(body);
2079
+ if (ArrayBuffer.isView(body)) {
2080
+ return new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
2081
+ }
2082
+ throw new Error(
2083
+ `a request-carried proof cannot commit to a ${body?.constructor?.name ?? typeof body} body: pass a string or bytes, or use a session for this call`
2084
+ );
2085
+ }
2086
+ function createProofSigner(options) {
2087
+ const ttl = options.ttlSeconds ?? 120;
2088
+ const clock = options.now ?? (() => Math.floor(Date.now() / 1e3));
2089
+ return async (request) => {
2090
+ const issuedAt = clock();
2091
+ return callerProof({
2092
+ credential: options.credential,
2093
+ session: options.session,
2094
+ request: await signRequest({
2095
+ method: request.method,
2096
+ path: request.path,
2097
+ body: bodyBytes(request.body),
2098
+ issuedAt,
2099
+ expiresAt: issuedAt + ttl,
2100
+ signerSecret: options.signerSecret
2101
+ })
2102
+ });
2103
+ };
2104
+ }
2105
+
1916
2106
  // src/rpc/index.ts
1917
2107
  var RpcError = class extends Error {
1918
2108
  constructor(code, message, data, type) {
@@ -1998,6 +2188,7 @@ var SseClient = class {
1998
2188
  this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
1999
2189
  this.getAuthToken = opts.getAuthToken;
2000
2190
  this.reconnectDelayMs = opts.reconnectDelayMs ?? 3e3;
2191
+ this.fetchImpl = (url, init) => opts.fetch ? opts.fetch(url, init) : globalThis.fetch(url, init);
2001
2192
  }
2002
2193
  on(event, handler) {
2003
2194
  const key = event;
@@ -2064,7 +2255,7 @@ var SseClient = class {
2064
2255
  try {
2065
2256
  const token = await this.getAuthToken();
2066
2257
  const url = `${this.baseUrl}/sse`;
2067
- const response = await fetch(url, {
2258
+ const response = await this.fetchImpl(url, {
2068
2259
  headers: {
2069
2260
  "Authorization": `Bearer ${token}`,
2070
2261
  "Accept": "text/event-stream"
@@ -2223,7 +2414,7 @@ var SseClient = class {
2223
2414
  const params = {};
2224
2415
  if (ids.contextIds && ids.contextIds.length > 0) params.contextIds = ids.contextIds;
2225
2416
  if (ids.groupIds && ids.groupIds.length > 0) params.groupIds = ids.groupIds;
2226
- const response = await fetch(`${this.baseUrl}/sse/subscription`, {
2417
+ const response = await this.fetchImpl(`${this.baseUrl}/sse/subscription`, {
2227
2418
  method: "POST",
2228
2419
  headers: {
2229
2420
  "Authorization": `Bearer ${token}`,
@@ -2552,116 +2743,6 @@ var EphemeralClient = class {
2552
2743
  }
2553
2744
  };
2554
2745
 
2555
- // src/crypto/internal.ts
2556
- var PKCS8_ED25519_PREFIX = new Uint8Array([
2557
- 48,
2558
- 46,
2559
- 2,
2560
- 1,
2561
- 0,
2562
- 48,
2563
- 5,
2564
- 6,
2565
- 3,
2566
- 43,
2567
- 101,
2568
- 112,
2569
- 4,
2570
- 34,
2571
- 4,
2572
- 32
2573
- ]);
2574
- function u32le(value) {
2575
- const out = new Uint8Array(4);
2576
- new DataView(out.buffer).setUint32(0, value, true);
2577
- return out;
2578
- }
2579
- function hex(bytes) {
2580
- return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
2581
- }
2582
- function fromHex(value, label, expectedBytes) {
2583
- const clean = value.trim();
2584
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
2585
- throw new Error(
2586
- `${label} must be ${expectedBytes * 2} hex characters, got ${clean.length}`
2587
- );
2588
- }
2589
- const bytes = new Uint8Array(expectedBytes);
2590
- for (let i = 0; i < expectedBytes; i += 1) {
2591
- bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
2592
- }
2593
- return bytes;
2594
- }
2595
- function fromHexUnsized(value, label) {
2596
- const clean = value.trim();
2597
- if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length % 2 !== 0) {
2598
- throw new Error(`${label} must be an even number of hex characters`);
2599
- }
2600
- const bytes = new Uint8Array(clean.length / 2);
2601
- for (let i = 0; i < bytes.length; i += 1) {
2602
- bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);
2603
- }
2604
- return bytes;
2605
- }
2606
- function u64le(value) {
2607
- const out = new Uint8Array(8);
2608
- new DataView(out.buffer).setBigUint64(0, BigInt(value), true);
2609
- return out;
2610
- }
2611
- function concat(...parts) {
2612
- const out = new Uint8Array(parts.reduce((total, p) => total + p.length, 0));
2613
- let offset = 0;
2614
- for (const part of parts) {
2615
- out.set(part, offset);
2616
- offset += part.length;
2617
- }
2618
- return out;
2619
- }
2620
- async function domainHash(domain, parts) {
2621
- const chunks = [u64le(domain.length), domain];
2622
- for (const part of parts) {
2623
- chunks.push(u64le(part.length), part);
2624
- }
2625
- return new Uint8Array(
2626
- await crypto.subtle.digest("SHA-256", concat(...chunks))
2627
- );
2628
- }
2629
- async function importSigningKey(secretHex) {
2630
- const seed = fromHex(secretHex, "deviceSecret", 32);
2631
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2632
- try {
2633
- return await crypto.subtle.importKey(
2634
- "pkcs8",
2635
- pkcs8,
2636
- { name: "Ed25519" },
2637
- false,
2638
- ["sign"]
2639
- );
2640
- } catch (cause) {
2641
- const detail = cause instanceof Error ? `: ${cause.message}` : "";
2642
- throw new Error(
2643
- "this runtime has no WebCrypto Ed25519, so a warrant cannot be signed here. Node 18.4+, Safari 17+, Firefox 129+ and Chrome 137+ have it; otherwise mint the warrant with `merod account warrant`" + detail
2644
- );
2645
- }
2646
- }
2647
- async function derivePublicKey(secretHex) {
2648
- const seed = fromHex(secretHex, "deviceSecret", 32);
2649
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2650
- const key = await crypto.subtle.importKey(
2651
- "pkcs8",
2652
- pkcs8,
2653
- { name: "Ed25519" },
2654
- true,
2655
- ["sign"]
2656
- );
2657
- const jwk = await crypto.subtle.exportKey("jwk", key);
2658
- const raw = jwk.x;
2659
- return Uint8Array.from(
2660
- atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2661
- (c) => c.charCodeAt(0)
2662
- );
2663
- }
2664
-
2665
2746
  // src/device-cert/device-cert.ts
2666
2747
  var CERT_DOMAIN = new TextEncoder().encode("calimero.device.cert.v1");
2667
2748
  var ACCOUNT_ID_DOMAIN = new TextEncoder().encode("calimero.account.genesis.v1");
@@ -5659,6 +5740,10 @@ var CloudClient = class {
5659
5740
  };
5660
5741
 
5661
5742
  // src/mero-js.ts
5743
+ function resolveProofSigner(config) {
5744
+ if (!config) return void 0;
5745
+ return typeof config === "function" ? config : createProofSigner(config);
5746
+ }
5662
5747
  var REFRESH_LOCK_NAME = "mero-js:token-refresh";
5663
5748
  function getLockManager() {
5664
5749
  const nav = globalThis.navigator;
@@ -5697,8 +5782,11 @@ var MeroJs = class {
5697
5782
  this.tokenData = this.tokenStore.getTokens();
5698
5783
  }
5699
5784
  const isTauri = typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
5785
+ const getProof = resolveProofSigner(this.config.requestProof);
5700
5786
  this.httpClient = createBrowserHttpClient({
5701
5787
  baseUrl: this.config.baseUrl,
5788
+ fetch: this.config.fetch,
5789
+ getProof,
5702
5790
  getAuthToken: async () => {
5703
5791
  const token = await this.getValidToken();
5704
5792
  return token?.access_token || "";
@@ -5768,6 +5856,7 @@ var MeroJs = class {
5768
5856
  if (!this.sseClient) {
5769
5857
  this.sseClient = new SseClient({
5770
5858
  baseUrl: this.config.baseUrl,
5859
+ fetch: this.config.fetch,
5771
5860
  getAuthToken: async () => {
5772
5861
  const token = await this.getValidToken();
5773
5862
  return token?.access_token || "";
@@ -6052,7 +6141,7 @@ var LocalStorageTokenStore = class {
6052
6141
  };
6053
6142
 
6054
6143
  // src/warrant/warrant.ts
6055
- var SIGN_DOMAIN = new TextEncoder().encode("calimero.warrant.v2");
6144
+ var SIGN_DOMAIN2 = new TextEncoder().encode("calimero.warrant.v2");
6056
6145
  var INTENT_DOMAIN = new TextEncoder().encode("calimero.warrant.intent.v1");
6057
6146
  var MAX_CITED_HEADS = 64;
6058
6147
  var UNPINNED_APP = "00".repeat(32);
@@ -6077,7 +6166,7 @@ async function signWarrant(input) {
6077
6166
  const commitment = await intentHash(input.method, input.argsJson);
6078
6167
  const nonce = u64le(input.nonce);
6079
6168
  const notAfter = u64le(input.notAfter);
6080
- const preimage = await domainHash(SIGN_DOMAIN, [
6169
+ const preimage = await domainHash(SIGN_DOMAIN2, [
6081
6170
  context,
6082
6171
  authorAccount,
6083
6172
  publicKey,
@@ -6396,7 +6485,7 @@ function optionalBytes32(value, label) {
6396
6485
  }
6397
6486
  return concat(new Uint8Array([1]), new Uint8Array(value));
6398
6487
  }
6399
- function borshString(value) {
6488
+ function borshString2(value) {
6400
6489
  const bytes = new TextEncoder().encode(value);
6401
6490
  return concat(u32le(bytes.length), bytes);
6402
6491
  }
@@ -6420,7 +6509,7 @@ function encodeSignedInvitation(signed) {
6420
6509
  // ...then the enclosing SignedGroupOpenInvitation, in declaration order.
6421
6510
  // `inviter_account` sits between the signature and the addresses; putting it
6422
6511
  // anywhere else still encodes 32 bytes and still verifies as nothing.
6423
- borshString(signed.inviter_signature),
6512
+ borshString2(signed.inviter_signature),
6424
6513
  signed.inviter_account ? concat(new Uint8Array([1]), fromHex(signed.inviter_account, "inviter_account", 32)) : new Uint8Array([0]),
6425
6514
  // A `Vec<String>`, so each entry is a bare borsh string. This carried an
6426
6515
  // enum until core dropped the URL variant, and an enum writes a
@@ -6428,7 +6517,7 @@ function encodeSignedInvitation(signed) {
6428
6517
  // every following byte and fail as `invalid invitation signature`, pointing
6429
6518
  // nowhere near this line.
6430
6519
  u32le(admitterAddrs.length),
6431
- ...admitterAddrs.map(borshString),
6520
+ ...admitterAddrs.map(borshString2),
6432
6521
  optionalBytes32(signed.application_id, "application_id"),
6433
6522
  // Borsh order follows core's field order, where this is `bytecode_id`; the
6434
6523
  // JSON key is `app_key`, a rename covering both directions that core pins in
@@ -6473,86 +6562,6 @@ async function signMemberJoinOp(input) {
6473
6562
  return hex(concat(signable, signature, unendorsed));
6474
6563
  }
6475
6564
 
6476
- // src/request/request-sig.ts
6477
- var SIGN_DOMAIN2 = new TextEncoder().encode("calimero.auth.request.v1");
6478
- var BODY_DOMAIN = new TextEncoder().encode("calimero.auth.request.body.v1");
6479
- function borshString2(value) {
6480
- const bytes = new TextEncoder().encode(value);
6481
- return concat(u32le(bytes.length), bytes);
6482
- }
6483
- async function requestBodyHash(body = new Uint8Array(0)) {
6484
- const bytes = typeof body === "string" ? new TextEncoder().encode(body) : body;
6485
- return domainHash(BODY_DOMAIN, [bytes]);
6486
- }
6487
- async function signRequest(input) {
6488
- const key = await importSigningKey(input.signerSecret);
6489
- const bodyHash = await requestBodyHash(input.body);
6490
- const issuedAt = u64le(input.issuedAt);
6491
- const expiresAt = u64le(input.expiresAt);
6492
- const encoder = new TextEncoder();
6493
- const preimage = await domainHash(SIGN_DOMAIN2, [
6494
- encoder.encode(input.method),
6495
- encoder.encode(input.path),
6496
- bodyHash,
6497
- issuedAt,
6498
- expiresAt
6499
- ]);
6500
- const signature = new Uint8Array(
6501
- await crypto.subtle.sign({ name: "Ed25519" }, key, preimage)
6502
- );
6503
- return hex(
6504
- concat(
6505
- borshString2(input.method),
6506
- borshString2(input.path),
6507
- bodyHash,
6508
- issuedAt,
6509
- expiresAt,
6510
- signature
6511
- )
6512
- );
6513
- }
6514
-
6515
- // src/request/caller-proof.ts
6516
- function callerProof(input) {
6517
- const credential = fromHexUnsized(input.credential, "credential");
6518
- const request = fromHexUnsized(input.request, "request signature");
6519
- const session = input.session === void 0 ? new Uint8Array([0]) : concat(new Uint8Array([1]), fromHexUnsized(input.session, "session statement"));
6520
- return hex(concat(credential, session, request));
6521
- }
6522
-
6523
- // src/request/proof-signer.ts
6524
- function bodyBytes(body) {
6525
- if (body === void 0 || body === null) return new Uint8Array(0);
6526
- if (typeof body === "string") return body;
6527
- if (body instanceof Uint8Array) return body;
6528
- if (body instanceof ArrayBuffer) return new Uint8Array(body);
6529
- if (ArrayBuffer.isView(body)) {
6530
- return new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
6531
- }
6532
- throw new Error(
6533
- `a request-carried proof cannot commit to a ${body?.constructor?.name ?? typeof body} body: pass a string or bytes, or use a session for this call`
6534
- );
6535
- }
6536
- function createProofSigner(options) {
6537
- const ttl = options.ttlSeconds ?? 120;
6538
- const clock = options.now ?? (() => Math.floor(Date.now() / 1e3));
6539
- return async (request) => {
6540
- const issuedAt = clock();
6541
- return callerProof({
6542
- credential: options.credential,
6543
- session: options.session,
6544
- request: await signRequest({
6545
- method: request.method,
6546
- path: request.path,
6547
- body: bodyBytes(request.body),
6548
- issuedAt,
6549
- expiresAt: issuedAt + ttl,
6550
- signerSecret: options.signerSecret
6551
- })
6552
- });
6553
- };
6554
- }
6555
-
6556
6565
  // src/login/login.ts
6557
6566
  var SIGN_DOMAIN3 = new TextEncoder().encode("calimero.auth.login.v1");
6558
6567
  var AUDIENCE_TAG = {