@calimero-network/mero-js 22.0.0 → 22.2.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/README.md +7 -5
- package/dist/admin-api/admin-types.d.ts +32 -1
- package/dist/cloud/cloud-client.d.ts +44 -14
- package/dist/cloud/cloud-client.js +82 -37
- package/dist/cloud/connect.d.ts +2 -1
- package/dist/cloud/connect.js +23 -12
- package/dist/events/group.d.ts +2 -1
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +85 -42
- package/dist/index.cjs.map +2 -2
- package/dist/index.mjs +85 -42
- package/dist/index.mjs.map +2 -2
- package/dist/namespace-op/namespace-op.d.ts +8 -2
- package/dist/namespace-op/namespace-op.js +8 -2
- package/dist/relay/relay-client.d.ts +7 -4
- package/dist/relay/relay-client.js +7 -4
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -5256,6 +5256,15 @@ function base642(bytes) {
|
|
|
5256
5256
|
|
|
5257
5257
|
// src/cloud/cloud-client.ts
|
|
5258
5258
|
var DEFAULT_CLOUD_BASE_URL = "https://cloud.calimero.network";
|
|
5259
|
+
function canExecuteOf(row, legacy) {
|
|
5260
|
+
return typeof row.can_execute === "boolean" ? row.can_execute : legacy;
|
|
5261
|
+
}
|
|
5262
|
+
function teeRoleOf(row) {
|
|
5263
|
+
return typeof row.tee_role === "string" && row.tee_role !== "" ? row.tee_role : null;
|
|
5264
|
+
}
|
|
5265
|
+
function pickExecutingRelay(relays) {
|
|
5266
|
+
return relays.find((r) => r.canExecute && r.relayUrl !== null && r.executorAccount !== null) ?? null;
|
|
5267
|
+
}
|
|
5259
5268
|
var REFRESH_HEADER = "x-mdma-session-refresh";
|
|
5260
5269
|
var REFRESH_EXPIRES_HEADER = "x-mdma-session-expires";
|
|
5261
5270
|
var CloudClient = class {
|
|
@@ -5404,37 +5413,48 @@ var CloudClient = class {
|
|
|
5404
5413
|
* The relays serving one namespace: where to present an intent, and as whom.
|
|
5405
5414
|
*
|
|
5406
5415
|
* Everything a client needs in order to mint a warrant it can actually
|
|
5407
|
-
* spend. Filter on `
|
|
5408
|
-
*
|
|
5409
|
-
*
|
|
5416
|
+
* spend. Filter on `canExecute` before signing — a warrant presented to a
|
|
5417
|
+
* relay that does not relay (a `ReadOnlyTee` replica, or a node with no role
|
|
5418
|
+
* and no grant) is refused *after* the author has burned a nonce from its
|
|
5419
|
+
* monotonic sequence on it.
|
|
5410
5420
|
*/
|
|
5411
5421
|
async getNamespaceRelays(namespaceId) {
|
|
5412
5422
|
const body = await this.request(
|
|
5413
5423
|
"GET",
|
|
5414
5424
|
`/api/cloud/me/namespaces/${encodeURIComponent(namespaceId)}/relays`
|
|
5415
5425
|
);
|
|
5416
|
-
return (body.relays ?? []).map((row) =>
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5426
|
+
return (body.relays ?? []).map((row) => {
|
|
5427
|
+
const relayUrl = row.relay_url ?? null;
|
|
5428
|
+
const executorAccount = row.executor_account ?? null;
|
|
5429
|
+
const authorshipReady = row.authorship_ready === true;
|
|
5430
|
+
return {
|
|
5431
|
+
peerId: String(row.peer_id ?? ""),
|
|
5432
|
+
relayUrl,
|
|
5433
|
+
executorAccount,
|
|
5434
|
+
status: String(row.status ?? ""),
|
|
5435
|
+
authorshipReady,
|
|
5436
|
+
teeRole: teeRoleOf(row),
|
|
5437
|
+
canExecute: canExecuteOf(
|
|
5438
|
+
row,
|
|
5439
|
+
authorshipReady && relayUrl !== null && executorAccount !== null
|
|
5440
|
+
),
|
|
5441
|
+
lastSeenAt: row.last_seen_at ?? null,
|
|
5442
|
+
confirmedAt: row.confirmed_at ?? null
|
|
5443
|
+
};
|
|
5444
|
+
});
|
|
5425
5445
|
}
|
|
5426
5446
|
/**
|
|
5427
5447
|
* The first relay for `namespaceId` that can actually run an intent, or
|
|
5428
5448
|
* `null`.
|
|
5429
5449
|
*
|
|
5430
|
-
* "Can actually run one" is
|
|
5431
|
-
* gets a refusal it cannot explain: the cloud must know a URL, the
|
|
5432
|
-
* have reported the executor account a warrant has to name, and
|
|
5433
|
-
* must
|
|
5450
|
+
* "Can actually run one" is several conditions, and a client that checks
|
|
5451
|
+
* fewer gets a refusal it cannot explain: the cloud must know a URL, the
|
|
5452
|
+
* relay must have reported the executor account a warrant has to name, and
|
|
5453
|
+
* the node must relay — which the cloud folds into `canExecute`.
|
|
5434
5454
|
*/
|
|
5435
5455
|
async findExecutingRelay(namespaceId) {
|
|
5436
5456
|
const relays = await this.getNamespaceRelays(namespaceId);
|
|
5437
|
-
return relays
|
|
5457
|
+
return pickExecutingRelay(relays);
|
|
5438
5458
|
}
|
|
5439
5459
|
/**
|
|
5440
5460
|
* Where to reach a namespace without owning it: one read, both verbs.
|
|
@@ -5462,17 +5482,27 @@ var CloudClient = class {
|
|
|
5462
5482
|
});
|
|
5463
5483
|
return {
|
|
5464
5484
|
namespaceId: String(body.namespace_id ?? namespaceId),
|
|
5465
|
-
nodes: (body.admitters ?? []).map((row) =>
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5485
|
+
nodes: (body.admitters ?? []).map((row) => {
|
|
5486
|
+
const account = row.account ?? null;
|
|
5487
|
+
const relayUrl = row.relay_url ?? null;
|
|
5488
|
+
const fresh = row.fresh === true;
|
|
5489
|
+
const authorshipReady = row.authorship_ready === true;
|
|
5490
|
+
return {
|
|
5491
|
+
peerId: String(row.peer_id ?? ""),
|
|
5492
|
+
account,
|
|
5493
|
+
relayUrl,
|
|
5494
|
+
admitUrl: row.admit_url ?? null,
|
|
5495
|
+
status: String(row.status ?? ""),
|
|
5496
|
+
fresh,
|
|
5497
|
+
canAdmit: row.can_admit === true,
|
|
5498
|
+
authorshipReady,
|
|
5499
|
+
teeRole: teeRoleOf(row),
|
|
5500
|
+
canExecute: canExecuteOf(
|
|
5501
|
+
row,
|
|
5502
|
+
authorshipReady && relayUrl !== null && account !== null && fresh
|
|
5503
|
+
)
|
|
5504
|
+
};
|
|
5505
|
+
}),
|
|
5476
5506
|
servable: body.servable === true,
|
|
5477
5507
|
writable: body.writable === true
|
|
5478
5508
|
};
|
|
@@ -5603,23 +5633,32 @@ var CloudClient = class {
|
|
|
5603
5633
|
"GET",
|
|
5604
5634
|
"/api/cloud/me/machines"
|
|
5605
5635
|
);
|
|
5606
|
-
return (body.machines ?? []).map((row) =>
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
canExecute: row.can_execute === true,
|
|
5611
|
-
namespaces: (Array.isArray(row.namespaces) ? row.namespaces : []).map((n) => {
|
|
5636
|
+
return (body.machines ?? []).map((row) => {
|
|
5637
|
+
const relayUrl = row.relay_url ?? null;
|
|
5638
|
+
const executorAccount = row.executor_account ?? null;
|
|
5639
|
+
const namespaces = (Array.isArray(row.namespaces) ? row.namespaces : []).map((n) => {
|
|
5612
5640
|
const ns = n;
|
|
5613
5641
|
return {
|
|
5614
5642
|
namespaceId: String(ns.namespace_id ?? ""),
|
|
5615
5643
|
status: String(ns.status ?? ""),
|
|
5616
5644
|
authorshipReady: ns.authorship_ready === true,
|
|
5645
|
+
teeRole: teeRoleOf(ns),
|
|
5617
5646
|
fresh: ns.fresh === true,
|
|
5618
5647
|
confirmedAt: ns.confirmed_at ?? null,
|
|
5619
5648
|
lastSeenAt: ns.last_seen_at ?? null
|
|
5620
5649
|
};
|
|
5621
|
-
})
|
|
5622
|
-
|
|
5650
|
+
});
|
|
5651
|
+
return {
|
|
5652
|
+
peerId: String(row.peer_id ?? ""),
|
|
5653
|
+
relayUrl,
|
|
5654
|
+
executorAccount,
|
|
5655
|
+
canExecute: canExecuteOf(
|
|
5656
|
+
row,
|
|
5657
|
+
relayUrl !== null && executorAccount !== null && namespaces.some((n) => n.authorshipReady)
|
|
5658
|
+
),
|
|
5659
|
+
namespaces
|
|
5660
|
+
};
|
|
5661
|
+
});
|
|
5623
5662
|
}
|
|
5624
5663
|
/**
|
|
5625
5664
|
* Claim a namespace under this account, proving ownership.
|
|
@@ -7159,12 +7198,12 @@ async function connectCloud(options) {
|
|
|
7159
7198
|
const namespaces = await cloud.getMyNamespaces();
|
|
7160
7199
|
const namespace = pickNamespace(namespaces, options.namespaceId);
|
|
7161
7200
|
const relays = await cloud.getNamespaceRelays(namespace.namespaceId);
|
|
7162
|
-
const relayInfo = relays
|
|
7201
|
+
const relayInfo = pickExecutingRelay(relays);
|
|
7163
7202
|
if (!relayInfo) {
|
|
7164
7203
|
throw new Error(explainNoRelay(namespace, relays));
|
|
7165
7204
|
}
|
|
7166
7205
|
const relay = new RelayClient({
|
|
7167
|
-
// Checked by `
|
|
7206
|
+
// Checked by `pickExecutingRelay`, which is the only thing that returns a
|
|
7168
7207
|
// relay — narrowed here rather than re-validated, so the invariant lives in
|
|
7169
7208
|
// one place.
|
|
7170
7209
|
relayUrl: relayInfo.relayUrl,
|
|
@@ -7218,11 +7257,15 @@ function explainNoRelay(namespace, relays) {
|
|
|
7218
7257
|
if (relays.length === 0) {
|
|
7219
7258
|
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`;
|
|
7220
7259
|
}
|
|
7221
|
-
const
|
|
7222
|
-
if (
|
|
7260
|
+
const replicas = relays.filter((r) => r.teeRole !== null && r.teeRole !== "RelayTee");
|
|
7261
|
+
if (replicas.length === relays.length) {
|
|
7262
|
+
return `every node on namespace ${namespace.namespaceId} is a read-only TEE replica (ReadOnlyTee), which never relays delegated writes: an admin of the namespace must set its TEE admission policy with mode: 'relay' \u2014 AdminClient.setTeeAdmissionPolicy(namespaceId, { ..., mode: 'relay' }) \u2014 so fleet nodes are admitted as RelayTee`;
|
|
7263
|
+
}
|
|
7264
|
+
const waitingOnGrant = relays.filter((r) => r.teeRole === null && !r.authorshipReady);
|
|
7265
|
+
if (waitingOnGrant.length > 0 && waitingOnGrant.length + replicas.length === relays.length) {
|
|
7223
7266
|
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`;
|
|
7224
7267
|
}
|
|
7225
|
-
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none
|
|
7268
|
+
return `namespace ${namespace.namespaceId} has ${relays.length} relay(s), but none is ready to relay yet (a URL, an executor account and a recent heartbeat) \u2014 retry shortly`;
|
|
7226
7269
|
}
|
|
7227
7270
|
async function defaultNonceSource(deviceSecret) {
|
|
7228
7271
|
const key = `mero-js:warrant-nonce:${hex(await derivePublicKey(deviceSecret))}`;
|
|
@@ -7894,7 +7937,7 @@ function splitVersion2(version) {
|
|
|
7894
7937
|
}
|
|
7895
7938
|
|
|
7896
7939
|
// src/namespace-op/namespace-op.ts
|
|
7897
|
-
var SIGNED_NAMESPACE_OP_SCHEMA_VERSION =
|
|
7940
|
+
var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 12;
|
|
7898
7941
|
var NAMESPACE_SIGN_DOMAIN = new TextEncoder().encode("calimero.namespace.v1");
|
|
7899
7942
|
var NAMESPACE_OP_ROOT = 0;
|
|
7900
7943
|
var ROOT_OP = {
|