@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/cloud/connect.d.ts +19 -0
- package/dist/cloud/connect.js +17 -1
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +123 -91
- package/dist/index.cjs.map +3 -3
- package/dist/index.mjs +123 -91
- package/dist/index.mjs.map +3 -3
- package/dist/login/session.js +6 -5
- package/dist/sealed/index.d.ts +2 -2
- package/dist/sealed/index.js +1 -1
- package/dist/sealed/sealed.d.ts +32 -0
- package/dist/sealed/sealed.js +49 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -6467,92 +6467,6 @@ function createLocalStorageNonceSource(key, storage) {
|
|
|
6467
6467
|
};
|
|
6468
6468
|
}
|
|
6469
6469
|
|
|
6470
|
-
// src/cloud/connect.ts
|
|
6471
|
-
async function connectCloud(options) {
|
|
6472
|
-
if (!options.sessionToken && !options.googleIdToken) {
|
|
6473
|
-
throw new Error("connectCloud needs either a sessionToken or a googleIdToken");
|
|
6474
|
-
}
|
|
6475
|
-
const cloud = new CloudClient({
|
|
6476
|
-
cloudBaseUrl: options.cloudBaseUrl,
|
|
6477
|
-
sessionToken: options.sessionToken,
|
|
6478
|
-
onSession: options.onSession,
|
|
6479
|
-
fetch: options.fetch,
|
|
6480
|
-
timeoutMs: options.timeoutMs
|
|
6481
|
-
});
|
|
6482
|
-
if (!options.sessionToken && options.googleIdToken) {
|
|
6483
|
-
await cloud.signInWithGoogle(options.googleIdToken);
|
|
6484
|
-
}
|
|
6485
|
-
const namespaces = await cloud.getMyNamespaces();
|
|
6486
|
-
const namespace = pickNamespace(namespaces, options.namespaceId);
|
|
6487
|
-
const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
|
|
6488
|
-
const relayInfo = relays.find((r) => r.authorshipReady && r.relayUrl !== null && r.executorAccount !== null) ?? null;
|
|
6489
|
-
if (!relayInfo) {
|
|
6490
|
-
throw new Error(explainNoRelay(namespace, relays));
|
|
6491
|
-
}
|
|
6492
|
-
const relay = new RelayClient({
|
|
6493
|
-
// Checked by `findExecutingRelay`, which is the only thing that returns a
|
|
6494
|
-
// relay — narrowed here rather than re-validated, so the invariant lives in
|
|
6495
|
-
// one place.
|
|
6496
|
-
relayUrl: relayInfo.relayUrl,
|
|
6497
|
-
executorAccount: relayInfo.executorAccount,
|
|
6498
|
-
authorAccount: options.authorAccount,
|
|
6499
|
-
authorProof: options.authorProof,
|
|
6500
|
-
deviceSecret: options.deviceSecret,
|
|
6501
|
-
nonces: options.nonces ?? await defaultNonceSource(options.deviceSecret),
|
|
6502
|
-
ttlSeconds: options.ttlSeconds,
|
|
6503
|
-
fetch: options.fetch,
|
|
6504
|
-
timeoutMs: options.timeoutMs
|
|
6505
|
-
});
|
|
6506
|
-
return {
|
|
6507
|
-
cloud,
|
|
6508
|
-
relay,
|
|
6509
|
-
namespaceId: namespace.namespaceId,
|
|
6510
|
-
namespace,
|
|
6511
|
-
relayInfo,
|
|
6512
|
-
execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson)
|
|
6513
|
-
};
|
|
6514
|
-
}
|
|
6515
|
-
function pickNamespace(namespaces, requested) {
|
|
6516
|
-
if (requested) {
|
|
6517
|
-
const found = namespaces.find((n) => n.namespaceId === requested);
|
|
6518
|
-
if (!found) {
|
|
6519
|
-
throw new Error(
|
|
6520
|
-
`this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(", ") || "(none)"}`
|
|
6521
|
-
);
|
|
6522
|
-
}
|
|
6523
|
-
return found;
|
|
6524
|
-
}
|
|
6525
|
-
if (namespaces.length === 0) {
|
|
6526
|
-
throw new Error(
|
|
6527
|
-
"this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned"
|
|
6528
|
-
);
|
|
6529
|
-
}
|
|
6530
|
-
if (namespaces.length > 1) {
|
|
6531
|
-
throw new Error(
|
|
6532
|
-
`this account owns ${namespaces.length} namespaces \u2014 pass namespaceId to say which: ${namespaces.map((n) => n.namespaceId).join(", ")}`
|
|
6533
|
-
);
|
|
6534
|
-
}
|
|
6535
|
-
return namespaces[0];
|
|
6536
|
-
}
|
|
6537
|
-
function explainNoRelay(namespace, relays) {
|
|
6538
|
-
if (relays.length === 0) {
|
|
6539
|
-
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`;
|
|
6540
|
-
}
|
|
6541
|
-
const waitingOnGrant = relays.filter((r) => !r.authorshipReady);
|
|
6542
|
-
if (waitingOnGrant.length === relays.length) {
|
|
6543
|
-
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`;
|
|
6544
|
-
}
|
|
6545
|
-
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none reported both a URL and an executor account yet \u2014 retry shortly`;
|
|
6546
|
-
}
|
|
6547
|
-
async function defaultNonceSource(deviceSecret) {
|
|
6548
|
-
const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
|
|
6549
|
-
try {
|
|
6550
|
-
return createLocalStorageNonceSource(key);
|
|
6551
|
-
} catch {
|
|
6552
|
-
return createMemoryNonceSource(1);
|
|
6553
|
-
}
|
|
6554
|
-
}
|
|
6555
|
-
|
|
6556
6470
|
// src/sealed/noise.ts
|
|
6557
6471
|
var PROTOCOL_NAME = "Noise_NK_25519_AESGCM_SHA256";
|
|
6558
6472
|
var DH_LEN = 32;
|
|
@@ -6772,6 +6686,33 @@ async function fetchAttestedTransportKey(admin, verify, options = {}) {
|
|
|
6772
6686
|
}
|
|
6773
6687
|
return transportKey;
|
|
6774
6688
|
}
|
|
6689
|
+
function createAttestedSealedFetch(options) {
|
|
6690
|
+
const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
6691
|
+
const attestUrl = `${options.baseUrl.replace(/\/+$/, "")}/admin-api/tee/attest`;
|
|
6692
|
+
const admin = {
|
|
6693
|
+
async teeAttest(request) {
|
|
6694
|
+
const response = await baseFetch(attestUrl, {
|
|
6695
|
+
method: "POST",
|
|
6696
|
+
headers: { "content-type": "application/json" },
|
|
6697
|
+
body: JSON.stringify(request)
|
|
6698
|
+
});
|
|
6699
|
+
if (!response.ok) {
|
|
6700
|
+
throw new Error(`The node refused to attest: HTTP ${response.status}`);
|
|
6701
|
+
}
|
|
6702
|
+
const body = await response.json();
|
|
6703
|
+
if (!body.data) throw new Error("The attestation response is malformed");
|
|
6704
|
+
return body.data;
|
|
6705
|
+
}
|
|
6706
|
+
};
|
|
6707
|
+
return createSealedFetch({
|
|
6708
|
+
baseUrl: options.baseUrl,
|
|
6709
|
+
transportPublicKey: () => fetchAttestedTransportKey(admin, options.verify, {
|
|
6710
|
+
applicationId: options.applicationId,
|
|
6711
|
+
applicationHash: options.applicationHash
|
|
6712
|
+
}),
|
|
6713
|
+
fetch: baseFetch
|
|
6714
|
+
});
|
|
6715
|
+
}
|
|
6775
6716
|
function createSealedFetch(options) {
|
|
6776
6717
|
const base = new URL(options.baseUrl);
|
|
6777
6718
|
const basePath = base.pathname.replace(/\/+$/, "");
|
|
@@ -6779,6 +6720,7 @@ function createSealedFetch(options) {
|
|
|
6779
6720
|
const baseFetch = options.fetch ?? ((input, init) => fetch(input, init));
|
|
6780
6721
|
const attest = typeof options.transportPublicKey === "function" ? options.transportPublicKey : void 0;
|
|
6781
6722
|
let transportKey = attest ? Promise.resolve().then(attest) : Promise.resolve(options.transportPublicKey);
|
|
6723
|
+
transportKey.catch(() => void 0);
|
|
6782
6724
|
let session;
|
|
6783
6725
|
const openSession = async () => {
|
|
6784
6726
|
try {
|
|
@@ -7029,6 +6971,98 @@ async function envelopeRefusal(response) {
|
|
|
7029
6971
|
return new SealedTransportError(response.status, code, message, retryAfterMs);
|
|
7030
6972
|
}
|
|
7031
6973
|
|
|
6974
|
+
// src/cloud/connect.ts
|
|
6975
|
+
async function connectCloud(options) {
|
|
6976
|
+
if (!options.sessionToken && !options.googleIdToken) {
|
|
6977
|
+
throw new Error("connectCloud needs either a sessionToken or a googleIdToken");
|
|
6978
|
+
}
|
|
6979
|
+
const cloud = new CloudClient({
|
|
6980
|
+
cloudBaseUrl: options.cloudBaseUrl,
|
|
6981
|
+
sessionToken: options.sessionToken,
|
|
6982
|
+
onSession: options.onSession,
|
|
6983
|
+
fetch: options.fetch,
|
|
6984
|
+
timeoutMs: options.timeoutMs
|
|
6985
|
+
});
|
|
6986
|
+
if (!options.sessionToken && options.googleIdToken) {
|
|
6987
|
+
await cloud.signInWithGoogle(options.googleIdToken);
|
|
6988
|
+
}
|
|
6989
|
+
const namespaces = await cloud.getMyNamespaces();
|
|
6990
|
+
const namespace = pickNamespace(namespaces, options.namespaceId);
|
|
6991
|
+
const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
|
|
6992
|
+
const relayInfo = relays.find((r) => r.authorshipReady && r.relayUrl !== null && r.executorAccount !== null) ?? null;
|
|
6993
|
+
if (!relayInfo) {
|
|
6994
|
+
throw new Error(explainNoRelay(namespace, relays));
|
|
6995
|
+
}
|
|
6996
|
+
const relay = new RelayClient({
|
|
6997
|
+
// Checked by `findExecutingRelay`, which is the only thing that returns a
|
|
6998
|
+
// relay — narrowed here rather than re-validated, so the invariant lives in
|
|
6999
|
+
// one place.
|
|
7000
|
+
relayUrl: relayInfo.relayUrl,
|
|
7001
|
+
executorAccount: relayInfo.executorAccount,
|
|
7002
|
+
authorAccount: options.authorAccount,
|
|
7003
|
+
authorProof: options.authorProof,
|
|
7004
|
+
deviceSecret: options.deviceSecret,
|
|
7005
|
+
nonces: options.nonces ?? await defaultNonceSource(options.deviceSecret),
|
|
7006
|
+
ttlSeconds: options.ttlSeconds,
|
|
7007
|
+
// Only the relay is sealed: a sealed fetch is bound to one node and refuses
|
|
7008
|
+
// any other URL, and the cloud calls above carry no intent.
|
|
7009
|
+
fetch: options.seal ? createAttestedSealedFetch({
|
|
7010
|
+
baseUrl: relayInfo.relayUrl,
|
|
7011
|
+
verify: options.seal,
|
|
7012
|
+
fetch: options.fetch
|
|
7013
|
+
}) : options.fetch,
|
|
7014
|
+
timeoutMs: options.timeoutMs
|
|
7015
|
+
});
|
|
7016
|
+
return {
|
|
7017
|
+
cloud,
|
|
7018
|
+
relay,
|
|
7019
|
+
namespaceId: namespace.namespaceId,
|
|
7020
|
+
namespace,
|
|
7021
|
+
relayInfo,
|
|
7022
|
+
execute: (contextId, method, argsJson) => relay.execute(contextId, method, argsJson)
|
|
7023
|
+
};
|
|
7024
|
+
}
|
|
7025
|
+
function pickNamespace(namespaces, requested) {
|
|
7026
|
+
if (requested) {
|
|
7027
|
+
const found = namespaces.find((n) => n.namespaceId === requested);
|
|
7028
|
+
if (!found) {
|
|
7029
|
+
throw new Error(
|
|
7030
|
+
`this account owns no namespace ${requested}; it owns: ${namespaces.map((n) => n.namespaceId).join(", ") || "(none)"}`
|
|
7031
|
+
);
|
|
7032
|
+
}
|
|
7033
|
+
return found;
|
|
7034
|
+
}
|
|
7035
|
+
if (namespaces.length === 0) {
|
|
7036
|
+
throw new Error(
|
|
7037
|
+
"this account owns no namespaces in the cloud: claim one first, then enable HA on it so a relay is assigned"
|
|
7038
|
+
);
|
|
7039
|
+
}
|
|
7040
|
+
if (namespaces.length > 1) {
|
|
7041
|
+
throw new Error(
|
|
7042
|
+
`this account owns ${namespaces.length} namespaces \u2014 pass namespaceId to say which: ${namespaces.map((n) => n.namespaceId).join(", ")}`
|
|
7043
|
+
);
|
|
7044
|
+
}
|
|
7045
|
+
return namespaces[0];
|
|
7046
|
+
}
|
|
7047
|
+
function explainNoRelay(namespace, relays) {
|
|
7048
|
+
if (relays.length === 0) {
|
|
7049
|
+
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`;
|
|
7050
|
+
}
|
|
7051
|
+
const waitingOnGrant = relays.filter((r) => !r.authorshipReady);
|
|
7052
|
+
if (waitingOnGrant.length === relays.length) {
|
|
7053
|
+
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`;
|
|
7054
|
+
}
|
|
7055
|
+
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none reported both a URL and an executor account yet \u2014 retry shortly`;
|
|
7056
|
+
}
|
|
7057
|
+
async function defaultNonceSource(deviceSecret) {
|
|
7058
|
+
const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
|
|
7059
|
+
try {
|
|
7060
|
+
return createLocalStorageNonceSource(key);
|
|
7061
|
+
} catch {
|
|
7062
|
+
return createMemoryNonceSource(1);
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
|
|
7032
7066
|
// src/sealed/verify.ts
|
|
7033
7067
|
var MEASUREMENT_HEX = /^[0-9a-f]{96}$/;
|
|
7034
7068
|
function createQuoteVerifier(options) {
|
|
@@ -7299,17 +7333,14 @@ function audienceLabel(audience) {
|
|
|
7299
7333
|
}
|
|
7300
7334
|
async function login(config) {
|
|
7301
7335
|
const baseUrl = config.nodeUrl.replace(/\/+$/, "");
|
|
7302
|
-
const doFetch = config.fetch ?? globalThis.fetch;
|
|
7303
7336
|
const timeoutMs = config.timeoutMs ?? 1e4;
|
|
7304
7337
|
const request = async (path, init) => {
|
|
7305
7338
|
const controller = new AbortController();
|
|
7306
7339
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7307
7340
|
let response;
|
|
7308
7341
|
try {
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
signal: controller.signal
|
|
7312
|
-
});
|
|
7342
|
+
const requestInit = { ...init, signal: controller.signal };
|
|
7343
|
+
response = config.fetch ? await config.fetch(`${baseUrl}${path}`, requestInit) : await globalThis.fetch(`${baseUrl}${path}`, requestInit);
|
|
7313
7344
|
} catch (err) {
|
|
7314
7345
|
throw new HTTPError(
|
|
7315
7346
|
0,
|
|
@@ -7468,6 +7499,7 @@ export {
|
|
|
7468
7499
|
connectCloud,
|
|
7469
7500
|
createAdminApiClient,
|
|
7470
7501
|
createAdminApiClientFromHttpClient,
|
|
7502
|
+
createAttestedSealedFetch,
|
|
7471
7503
|
createAuthApiClient,
|
|
7472
7504
|
createAuthApiClientFromHttpClient,
|
|
7473
7505
|
createBrowserAdminApiClient,
|