@calimero-network/mero-js 19.26.0 → 19.27.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
@@ -54,6 +54,7 @@ __export(index_exports, {
54
54
  connectCloud: () => connectCloud,
55
55
  createAdminApiClient: () => createAdminApiClient,
56
56
  createAdminApiClientFromHttpClient: () => createAdminApiClientFromHttpClient,
57
+ createAttestedSealedFetch: () => createAttestedSealedFetch,
57
58
  createAuthApiClient: () => createAuthApiClient,
58
59
  createAuthApiClientFromHttpClient: () => createAuthApiClientFromHttpClient,
59
60
  createBrowserAdminApiClient: () => createBrowserAdminApiClient,
@@ -6574,92 +6575,6 @@ function createLocalStorageNonceSource(key, storage) {
6574
6575
  };
6575
6576
  }
6576
6577
 
6577
- // src/cloud/connect.ts
6578
- async function connectCloud(options) {
6579
- if (!options.sessionToken && !options.googleIdToken) {
6580
- throw new Error("connectCloud needs either a sessionToken or a googleIdToken");
6581
- }
6582
- const cloud = new CloudClient({
6583
- cloudBaseUrl: options.cloudBaseUrl,
6584
- sessionToken: options.sessionToken,
6585
- onSession: options.onSession,
6586
- fetch: options.fetch,
6587
- timeoutMs: options.timeoutMs
6588
- });
6589
- if (!options.sessionToken && options.googleIdToken) {
6590
- await cloud.signInWithGoogle(options.googleIdToken);
6591
- }
6592
- const namespaces = await cloud.getMyNamespaces();
6593
- const namespace = pickNamespace(namespaces, options.namespaceId);
6594
- const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
6595
- const relayInfo = relays.find((r) => r.authorshipReady && r.relayUrl !== null && r.executorAccount !== null) ?? null;
6596
- if (!relayInfo) {
6597
- throw new Error(explainNoRelay(namespace, relays));
6598
- }
6599
- const relay = new RelayClient({
6600
- // Checked by `findExecutingRelay`, which is the only thing that returns a
6601
- // relay — narrowed here rather than re-validated, so the invariant lives in
6602
- // one place.
6603
- relayUrl: relayInfo.relayUrl,
6604
- executorAccount: relayInfo.executorAccount,
6605
- authorAccount: options.authorAccount,
6606
- authorProof: options.authorProof,
6607
- deviceSecret: options.deviceSecret,
6608
- nonces: options.nonces ?? await defaultNonceSource(options.deviceSecret),
6609
- ttlSeconds: options.ttlSeconds,
6610
- fetch: options.fetch,
6611
- timeoutMs: options.timeoutMs
6612
- });
6613
- return {
6614
- cloud,
6615
- relay,
6616
- namespaceId: namespace.namespaceId,
6617
- namespace,
6618
- relayInfo,
6619
- execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson)
6620
- };
6621
- }
6622
- function pickNamespace(namespaces, requested) {
6623
- if (requested) {
6624
- const found = namespaces.find((n) => n.namespaceId === requested);
6625
- if (!found) {
6626
- throw new Error(
6627
- `this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(", ") || "(none)"}`
6628
- );
6629
- }
6630
- return found;
6631
- }
6632
- if (namespaces.length === 0) {
6633
- throw new Error(
6634
- "this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned"
6635
- );
6636
- }
6637
- if (namespaces.length > 1) {
6638
- throw new Error(
6639
- `this account owns ${namespaces.length} namespaces \u2014 pass namespaceId to say which: ${namespaces.map((n) => n.namespaceId).join(", ")}`
6640
- );
6641
- }
6642
- return namespaces[0];
6643
- }
6644
- function explainNoRelay(namespace, relays) {
6645
- if (relays.length === 0) {
6646
- return namespace.haStatus === "enabled" ? `namespace ${namespace.namespaceId} has HA enabled but no relay has been assigned to it yet \u2014 retry shortly` : `namespace ${namespace.namespaceId} has no relays: enable HA on it in the cloud to have an attested node assigned`;
6647
- }
6648
- const waitingOnGrant = relays.filter((r) => !r.authorshipReady);
6649
- if (waitingOnGrant.length === relays.length) {
6650
- return `every relay on namespace ${namespace.namespaceId} is waiting for the authorship grant: an admin of the namespace must grant CAN_AUTHOR_ON_BEHALF to ${waitingOnGrant.map((r) => r.executorAccount ?? r.peerId).join(", ") || "the relay account"} \u2014 with AdminClient.grantAuthorship(namespaceId, account) for these relays, and AdminClient.openToDelegatedExecution(namespaceId) so relays assigned later need no further op`;
6651
- }
6652
- return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none reported both a URL and an executor account yet \u2014 retry shortly`;
6653
- }
6654
- async function defaultNonceSource(deviceSecret) {
6655
- const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
6656
- try {
6657
- return createLocalStorageNonceSource(key);
6658
- } catch {
6659
- return createMemoryNonceSource(1);
6660
- }
6661
- }
6662
-
6663
6578
  // src/sealed/noise.ts
6664
6579
  var PROTOCOL_NAME = "Noise_NK_25519_AESGCM_SHA256";
6665
6580
  var DH_LEN = 32;
@@ -6879,6 +6794,33 @@ async function fetchAttestedTransportKey(admin, verify, options = {}) {
6879
6794
  }
6880
6795
  return transportKey;
6881
6796
  }
6797
+ function createAttestedSealedFetch(options) {
6798
+ const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
6799
+ const attestUrl = `${options.baseUrl.replace(/\/+$/, "")}/admin-api/tee/attest`;
6800
+ const admin = {
6801
+ async teeAttest(request) {
6802
+ const response = await baseFetch(attestUrl, {
6803
+ method: "POST",
6804
+ headers: { "content-type": "application/json" },
6805
+ body: JSON.stringify(request)
6806
+ });
6807
+ if (!response.ok) {
6808
+ throw new Error(`The node refused to attest: HTTP ${response.status}`);
6809
+ }
6810
+ const body = await response.json();
6811
+ if (!body.data) throw new Error("The attestation response is malformed");
6812
+ return body.data;
6813
+ }
6814
+ };
6815
+ return createSealedFetch({
6816
+ baseUrl: options.baseUrl,
6817
+ transportPublicKey: () => fetchAttestedTransportKey(admin, options.verify, {
6818
+ applicationId: options.applicationId,
6819
+ applicationHash: options.applicationHash
6820
+ }),
6821
+ fetch: baseFetch
6822
+ });
6823
+ }
6882
6824
  function createSealedFetch(options) {
6883
6825
  const base = new URL(options.baseUrl);
6884
6826
  const basePath = base.pathname.replace(/\/+$/, "");
@@ -6886,6 +6828,7 @@ function createSealedFetch(options) {
6886
6828
  const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
6887
6829
  const attest = typeof options.transportPublicKey === "function" ? options.transportPublicKey : void 0;
6888
6830
  let transportKey = attest ? Promise.resolve().then(attest) : Promise.resolve(options.transportPublicKey);
6831
+ transportKey.catch(() => void 0);
6889
6832
  let session;
6890
6833
  const openSession = async () => {
6891
6834
  try {
@@ -7136,6 +7079,98 @@ async function envelopeRefusal(response) {
7136
7079
  return new SealedTransportError(response.status, code, message, retryAfterMs);
7137
7080
  }
7138
7081
 
7082
+ // src/cloud/connect.ts
7083
+ async function connectCloud(options) {
7084
+ if (!options.sessionToken && !options.googleIdToken) {
7085
+ throw new Error("connectCloud needs either a sessionToken or a googleIdToken");
7086
+ }
7087
+ const cloud = new CloudClient({
7088
+ cloudBaseUrl: options.cloudBaseUrl,
7089
+ sessionToken: options.sessionToken,
7090
+ onSession: options.onSession,
7091
+ fetch: options.fetch,
7092
+ timeoutMs: options.timeoutMs
7093
+ });
7094
+ if (!options.sessionToken && options.googleIdToken) {
7095
+ await cloud.signInWithGoogle(options.googleIdToken);
7096
+ }
7097
+ const namespaces = await cloud.getMyNamespaces();
7098
+ const namespace = pickNamespace(namespaces, options.namespaceId);
7099
+ const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
7100
+ const relayInfo = relays.find((r) => r.authorshipReady && r.relayUrl !== null && r.executorAccount !== null) ?? null;
7101
+ if (!relayInfo) {
7102
+ throw new Error(explainNoRelay(namespace, relays));
7103
+ }
7104
+ const relay = new RelayClient({
7105
+ // Checked by `findExecutingRelay`, which is the only thing that returns a
7106
+ // relay — narrowed here rather than re-validated, so the invariant lives in
7107
+ // one place.
7108
+ relayUrl: relayInfo.relayUrl,
7109
+ executorAccount: relayInfo.executorAccount,
7110
+ authorAccount: options.authorAccount,
7111
+ authorProof: options.authorProof,
7112
+ deviceSecret: options.deviceSecret,
7113
+ nonces: options.nonces ?? await defaultNonceSource(options.deviceSecret),
7114
+ ttlSeconds: options.ttlSeconds,
7115
+ // Only the relay is sealed: a sealed fetch is bound to one node and refuses
7116
+ // any other URL, and the cloud calls above carry no intent.
7117
+ fetch: options.seal ? createAttestedSealedFetch({
7118
+ baseUrl: relayInfo.relayUrl,
7119
+ verify: options.seal,
7120
+ fetch: options.fetch
7121
+ }) : options.fetch,
7122
+ timeoutMs: options.timeoutMs
7123
+ });
7124
+ return {
7125
+ cloud,
7126
+ relay,
7127
+ namespaceId: namespace.namespaceId,
7128
+ namespace,
7129
+ relayInfo,
7130
+ execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson)
7131
+ };
7132
+ }
7133
+ function pickNamespace(namespaces, requested) {
7134
+ if (requested) {
7135
+ const found = namespaces.find((n) => n.namespaceId === requested);
7136
+ if (!found) {
7137
+ throw new Error(
7138
+ `this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(", ") || "(none)"}`
7139
+ );
7140
+ }
7141
+ return found;
7142
+ }
7143
+ if (namespaces.length === 0) {
7144
+ throw new Error(
7145
+ "this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned"
7146
+ );
7147
+ }
7148
+ if (namespaces.length > 1) {
7149
+ throw new Error(
7150
+ `this account owns ${namespaces.length} namespaces \u2014 pass namespaceId to say which: ${namespaces.map((n) => n.namespaceId).join(", ")}`
7151
+ );
7152
+ }
7153
+ return namespaces[0];
7154
+ }
7155
+ function explainNoRelay(namespace, relays) {
7156
+ if (relays.length === 0) {
7157
+ return namespace.haStatus === "enabled" ? `namespace ${namespace.namespaceId} has HA enabled but no relay has been assigned to it yet \u2014 retry shortly` : `namespace ${namespace.namespaceId} has no relays: enable HA on it in the cloud to have an attested node assigned`;
7158
+ }
7159
+ const waitingOnGrant = relays.filter((r) => !r.authorshipReady);
7160
+ if (waitingOnGrant.length === relays.length) {
7161
+ return `every relay on namespace ${namespace.namespaceId} is waiting for the authorship grant: an admin of the namespace must grant CAN_AUTHOR_ON_BEHALF to ${waitingOnGrant.map((r) => r.executorAccount ?? r.peerId).join(", ") || "the relay account"} \u2014 with AdminClient.grantAuthorship(namespaceId, account) for these relays, and AdminClient.openToDelegatedExecution(namespaceId) so relays assigned later need no further op`;
7162
+ }
7163
+ return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none reported both a URL and an executor account yet \u2014 retry shortly`;
7164
+ }
7165
+ async function defaultNonceSource(deviceSecret) {
7166
+ const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
7167
+ try {
7168
+ return createLocalStorageNonceSource(key);
7169
+ } catch {
7170
+ return createMemoryNonceSource(1);
7171
+ }
7172
+ }
7173
+
7139
7174
  // src/sealed/verify.ts
7140
7175
  var MEASUREMENT_HEX = /^[0-9a-f]{96}$/;
7141
7176
  function createQuoteVerifier(options) {
@@ -7406,17 +7441,14 @@ function audienceLabel(audience) {
7406
7441
  }
7407
7442
  async function login(config) {
7408
7443
  const baseUrl = config.nodeUrl.replace(/\/+$/, "");
7409
- const doFetch = config.fetch ?? globalThis.fetch;
7410
7444
  const timeoutMs = config.timeoutMs ?? 1e4;
7411
7445
  const request = async (path, init) => {
7412
7446
  const controller = new AbortController();
7413
7447
  const timer = setTimeout(() => controller.abort(), timeoutMs);
7414
7448
  let response;
7415
7449
  try {
7416
- response = await doFetch(`${baseUrl}${path}`, {
7417
- ...init,
7418
- signal: controller.signal
7419
- });
7450
+ const requestInit = { ...init, signal: controller.signal };
7451
+ response = config.fetch ? await config.fetch(`${baseUrl}${path}`, requestInit) : await globalThis.fetch(`${baseUrl}${path}`, requestInit);
7420
7452
  } catch (err) {
7421
7453
  throw new HTTPError(
7422
7454
  0,