@calimero-network/mero-js 15.3.2 → 15.3.4

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.
@@ -52,10 +52,17 @@ export interface SignMemberJoinInput {
52
52
  /** Anti-replay nonce; a node keeps a window per signer. */
53
53
  readonly nonce: number | bigint;
54
54
  /**
55
- * Use `MemberJoinedAt` instead of `MemberJoined`. The `At` form pins the
56
- * membership to a position in the log.
55
+ * When the join happened, unix seconds. Defaults to now.
56
+ *
57
+ * Carried only by `MemberJoinedAt`, which is the variant used whenever the
58
+ * invitation expires — and since core clamps every invitation to
59
+ * `MAX_INVITATION_VALIDITY_SECS`, that is every invitation. The variant is
60
+ * inferred from the invitation rather than selected by a flag: an expiring
61
+ * invitation joined with plain `MemberJoined` is refused at apply with
62
+ * "expiration is set but joined_at is absent", which is not a mistake worth
63
+ * leaving available.
57
64
  */
58
- readonly at?: boolean;
65
+ readonly joinedAt?: number | bigint;
59
66
  }
60
67
  /**
61
68
  * Sign a `MemberJoined` (or `MemberJoinedAt`) namespace op and return it
@@ -97,7 +97,16 @@ export function encodeSignedInvitation(signed) {
97
97
  export async function signMemberJoinOp(input) {
98
98
  const parents = input.parentOpHashes ?? [];
99
99
  const credential = fromHex(input.credential, 'credential', input.credential.length / 2);
100
- const rootOp = concat(new Uint8Array([input.at ? ROOT_OP.MemberJoinedAt : ROOT_OP.MemberJoined]), fromHex(input.member, 'member', 32), encodeSignedInvitation(input.invitation), credential);
100
+ // An invitation that expires must be claimed with `MemberJoinedAt`, which
101
+ // carries `joined_at` between the invitation and the credential. Choosing the
102
+ // variant from the invitation keeps the two in step: the discriminant and the
103
+ // presence of that field are one decision, and splitting them produces bytes
104
+ // that decode into the wrong shape rather than failing to decode.
105
+ const expires = BigInt(input.invitation.invitation.expiration_timestamp ?? 0) !== 0n;
106
+ const joinedAt = input.joinedAt ?? BigInt(Math.floor(Date.now() / 1000));
107
+ const rootOp = expires
108
+ ? concat(new Uint8Array([ROOT_OP.MemberJoinedAt]), fromHex(input.member, 'member', 32), encodeSignedInvitation(input.invitation), u64le(joinedAt), credential)
109
+ : concat(new Uint8Array([ROOT_OP.MemberJoined]), fromHex(input.member, 'member', 32), encodeSignedInvitation(input.invitation), credential);
101
110
  const op = concat(new Uint8Array([NAMESPACE_OP_ROOT]), rootOp);
102
111
  const key = await importSigningKey(input.deviceSecret);
103
112
  const signer = await derivePublicKey(input.deviceSecret);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calimero-network/mero-js",
3
- "version": "15.3.2",
3
+ "version": "15.3.4",
4
4
  "description": "Pure JavaScript SDK for Calimero",
5
5
  "type": "module",
6
6
  "exports": {