@calimero-network/mero-js 26.0.1 → 26.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/dist/admin-api/admin-types.d.ts +6 -0
- package/dist/admin-api/redeem-invitation.d.ts +8 -1
- package/dist/admin-api/redeem-invitation.js +64 -1
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +27 -0
- package/dist/index.cjs.map +2 -2
- package/dist/index.mjs +27 -0
- package/dist/index.mjs.map +2 -2
- package/package.json +1 -1
|
@@ -362,6 +362,8 @@ export interface Namespace {
|
|
|
362
362
|
* cannot resolve it (raw-wasm app, legacy key, blob not retained locally).
|
|
363
363
|
*/
|
|
364
364
|
appVersion?: string;
|
|
365
|
+
/** Package of the release the namespace targets; present before that release is installed here. */
|
|
366
|
+
appPackage?: string;
|
|
365
367
|
/**
|
|
366
368
|
* What the namespace id was derived from, on nodes that hold it. Every
|
|
367
369
|
* namespace's id is derived from its founder since core's schema v10, so
|
|
@@ -1373,6 +1375,10 @@ export interface GroupInfo {
|
|
|
1373
1375
|
* schema 18.
|
|
1374
1376
|
*/
|
|
1375
1377
|
ownerOpCounter?: number;
|
|
1378
|
+
/** Package of the release the group targets; present before that release is installed here. */
|
|
1379
|
+
appPackage?: string;
|
|
1380
|
+
/** Version of the release the group targets; absent until governance names one. */
|
|
1381
|
+
appVersion?: string;
|
|
1376
1382
|
}
|
|
1377
1383
|
export type GroupInfoResponseData = GroupInfo;
|
|
1378
1384
|
/**
|
|
@@ -27,7 +27,14 @@ export type InviteFailureReason =
|
|
|
27
27
|
'expired'
|
|
28
28
|
/** The link is malformed or names nothing joinable. Only a new link helps. */
|
|
29
29
|
| 'invalid'
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* This person already joined with this link once, and has since left. A link
|
|
32
|
+
* admits each person once: only a new one helps.
|
|
33
|
+
*/
|
|
34
|
+
| 'already-used'
|
|
35
|
+
/** An admin removed this person. No link readmits them; an admin must re-add them. */
|
|
36
|
+
| 'removed'
|
|
37
|
+
/** The node refused this person for another reason (blocked, not allowed). */
|
|
31
38
|
| 'refused'
|
|
32
39
|
/** This client's session with the node lapsed; signing in finishes it. */
|
|
33
40
|
| 'signed-out'
|
|
@@ -28,9 +28,46 @@ export function isSettled(outcome) {
|
|
|
28
28
|
export function shouldRetain(outcome) {
|
|
29
29
|
return !isSettled(outcome);
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* The refusal codes a join route answers with beside `error` (core's
|
|
33
|
+
* `join_refusal_code`), and what each one tells a person.
|
|
34
|
+
*
|
|
35
|
+
* `join_refused_by_member` is absent on purpose: a member that refused may only
|
|
36
|
+
* be behind, so it says nothing final on its own, and the member's own words
|
|
37
|
+
* (in the message) are read by {@link reasonFromMessage} instead.
|
|
38
|
+
*/
|
|
39
|
+
const REASON_FOR_CODE = {
|
|
40
|
+
invitation_expired: 'expired',
|
|
41
|
+
invitation_invalid: 'invalid',
|
|
42
|
+
invitation_already_used: 'already-used',
|
|
43
|
+
removed_from_group: 'removed',
|
|
44
|
+
reentry_blocked: 'refused',
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* The machine-readable `code` a node put beside its `error`, if any: on
|
|
48
|
+
* mero-js's `HTTPError` it is in the body text, on an axios-style error in
|
|
49
|
+
* `response.data`.
|
|
50
|
+
*/
|
|
51
|
+
function refusalCodeOf(err) {
|
|
52
|
+
if (!err || typeof err !== 'object')
|
|
53
|
+
return undefined;
|
|
54
|
+
const shaped = err;
|
|
55
|
+
if (typeof shaped.response?.data?.code === 'string')
|
|
56
|
+
return shaped.response.data.code;
|
|
57
|
+
if (typeof shaped.bodyText !== 'string')
|
|
58
|
+
return undefined;
|
|
59
|
+
try {
|
|
60
|
+
const body = JSON.parse(shaped.bodyText);
|
|
61
|
+
return typeof body?.code === 'string' ? body.code : undefined;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
31
67
|
/**
|
|
32
68
|
* Which reason the message alone names, for a node that answers without a
|
|
33
|
-
* useful status — older than rc.56, where every refusal was a bare 500
|
|
69
|
+
* useful status — older than rc.56, where every refusal was a bare 500 — or
|
|
70
|
+
* without a `code`, or for a member's refusal quoted inside a retryable answer.
|
|
34
71
|
*
|
|
35
72
|
* Kept narrow on purpose. Anything not recognised here stays retryable, because
|
|
36
73
|
* the cost of retrying a dead link is one more attempt, while the cost of
|
|
@@ -51,8 +88,19 @@ function reasonFromMessage(message) {
|
|
|
51
88
|
/\binvitation for group .+? is invalid:/.test(m)) {
|
|
52
89
|
return 'invalid';
|
|
53
90
|
}
|
|
91
|
+
// Core's own words for a spent link and a removal (`MembershipError`), which a
|
|
92
|
+
// refusing member also quotes back to the joiner.
|
|
93
|
+
if (m.includes('has already used this invitation'))
|
|
94
|
+
return 'already-used';
|
|
95
|
+
if (/\bwas removed from group\b/.test(m))
|
|
96
|
+
return 'removed';
|
|
54
97
|
return null;
|
|
55
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* The reasons that are final whatever status carried them: no later attempt
|
|
101
|
+
* with this link can succeed for this person.
|
|
102
|
+
*/
|
|
103
|
+
const FINAL_FROM_TEXT = new Set(['already-used', 'removed']);
|
|
56
104
|
/**
|
|
57
105
|
* Why a join failed, and whether a later attempt could work.
|
|
58
106
|
*
|
|
@@ -65,6 +113,21 @@ function reasonFromMessage(message) {
|
|
|
65
113
|
function classifyJoinFailure(err, message) {
|
|
66
114
|
const { kind, status } = classifyError(err);
|
|
67
115
|
const fromText = reasonFromMessage(message);
|
|
116
|
+
// A code is the node naming the refusal itself: nothing below can know better.
|
|
117
|
+
const code = refusalCodeOf(err);
|
|
118
|
+
const fromCode = code ? REASON_FOR_CODE[code] : undefined;
|
|
119
|
+
if (fromCode)
|
|
120
|
+
return { reason: fromCode, retryable: false };
|
|
121
|
+
// A spent link or a removal is final even when a member said it inside a
|
|
122
|
+
// retryable answer (a 503 for a join every peer refused): asking again only
|
|
123
|
+
// asks the same members, who will say the same.
|
|
124
|
+
if (fromText && FINAL_FROM_TEXT.has(fromText))
|
|
125
|
+
return { reason: fromText, retryable: false };
|
|
126
|
+
// Any other member refusal is not "no one is online": someone answered, and
|
|
127
|
+
// their words are the message. Kept, since a member behind on the team's
|
|
128
|
+
// history refuses what it has not seen yet.
|
|
129
|
+
if (code === 'join_refused_by_member')
|
|
130
|
+
return { reason: 'unknown', retryable: true };
|
|
68
131
|
if (status === 401)
|
|
69
132
|
return { reason: 'signed-out', retryable: true };
|
|
70
133
|
if (status === 400)
|