@peers-app/peers-sdk 0.23.14 → 0.24.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/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +4 -1
- package/dist/contracts/system/index.d.ts +2 -0
- package/dist/contracts/system/index.js +5 -1
- package/dist/contracts/system/invites-contract.d.ts +121 -0
- package/dist/contracts/system/invites-contract.js +150 -0
- package/dist/data/create-group.d.ts +24 -0
- package/dist/data/create-group.js +61 -0
- package/dist/data/group-secret.d.ts +10 -0
- package/dist/data/group-secret.js +26 -0
- package/dist/data/group-share.d.ts +0 -20
- package/dist/data/group-share.js +0 -35
- package/dist/data/index.d.ts +2 -0
- package/dist/data/index.js +2 -0
- package/dist/data/invites.d.ts +350 -0
- package/dist/data/invites.js +99 -0
- package/dist/data/users.js +4 -0
- package/dist/device-pairing/constants.d.ts +27 -9
- package/dist/device-pairing/constants.js +28 -10
- package/dist/device-pairing/cpace.d.ts +46 -0
- package/dist/device-pairing/cpace.js +173 -0
- package/dist/device-pairing/cpace.test.js +116 -0
- package/dist/device-pairing/crypto.d.ts +40 -15
- package/dist/device-pairing/crypto.js +74 -33
- package/dist/device-pairing/device-pairing.test.js +156 -119
- package/dist/device-pairing/index.d.ts +3 -1
- package/dist/device-pairing/index.js +3 -1
- package/dist/device-pairing/pairing-code.d.ts +27 -0
- package/dist/device-pairing/pairing-code.js +84 -0
- package/dist/device-pairing/pgp-word-list.d.ts +14 -0
- package/dist/device-pairing/pgp-word-list.js +531 -0
- package/dist/device-pairing/signaling.d.ts +17 -6
- package/dist/device-pairing/signaling.js +31 -8
- package/dist/device-pairing/transcript.d.ts +3 -3
- package/dist/device-pairing/transcript.js +6 -13
- package/dist/device-pairing/types.d.ts +870 -224
- package/dist/device-pairing/types.js +186 -108
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/invites/data-box-schema.d.ts +15 -0
- package/dist/invites/data-box-schema.js +15 -0
- package/dist/invites/group-approval.d.ts +43 -0
- package/dist/invites/group-approval.js +155 -0
- package/dist/invites/group-approval.test.js +221 -0
- package/dist/invites/index.d.ts +5 -0
- package/dist/{group-invite → invites}/index.js +5 -3
- package/dist/invites/invite-epoch.d.ts +18 -0
- package/dist/invites/invite-epoch.js +35 -0
- package/dist/invites/invite-messages.d.ts +1354 -0
- package/dist/invites/invite-messages.js +145 -0
- package/dist/invites/invite-token.d.ts +334 -0
- package/dist/invites/invite-token.js +178 -0
- package/dist/invites/invite-token.test.d.ts +1 -0
- package/dist/invites/invite-token.test.js +123 -0
- package/dist/rpc-types.d.ts +5 -4
- package/dist/rpc-types.js +2 -1
- package/dist/system-ids.d.ts +22 -0
- package/dist/system-ids.js +23 -1
- package/package.json +1 -1
- package/dist/device-pairing/invitation.d.ts +0 -13
- package/dist/device-pairing/invitation.js +0 -85
- package/dist/group-invite/group-invite.d.ts +0 -53
- package/dist/group-invite/group-invite.js +0 -231
- package/dist/group-invite/group-invite.pvars.d.ts +0 -94
- package/dist/group-invite/group-invite.pvars.js +0 -139
- package/dist/group-invite/group-invite.types.d.ts +0 -124
- package/dist/group-invite/group-invite.types.js +0 -8
- package/dist/group-invite/index.d.ts +0 -3
- package/dist/user-connect/connection-code.d.ts +0 -67
- package/dist/user-connect/connection-code.js +0 -176
- package/dist/user-connect/connection-code.test.js +0 -213
- package/dist/user-connect/index.d.ts +0 -3
- package/dist/user-connect/index.js +0 -19
- package/dist/user-connect/user-connect.pvars.d.ts +0 -20
- package/dist/user-connect/user-connect.pvars.js +0 -63
- package/dist/user-connect/user-connect.pvars.test.js +0 -46
- package/dist/user-connect/user-connect.types.d.ts +0 -94
- package/dist/user-connect/user-connect.types.js +0 -68
- /package/dist/{user-connect/connection-code.test.d.ts → device-pairing/cpace.test.d.ts} +0 -0
- /package/dist/{user-connect/user-connect.pvars.test.d.ts → invites/group-approval.test.d.ts} +0 -0
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Persistent variables for group invitation state.
|
|
3
|
-
*
|
|
4
|
-
* These device-level variables track:
|
|
5
|
-
* - Active invitation listeners (admin side)
|
|
6
|
-
* - Pending join requests (admin side)
|
|
7
|
-
* - Status messages for UI feedback
|
|
8
|
-
* - Action triggers (UI sets these, device layer reacts)
|
|
9
|
-
*/
|
|
10
|
-
import type { GroupMemberRole } from "../data/group-member-roles";
|
|
11
|
-
import type { IGroupInviteListener, IGroupInviteListenerInfo, IGroupJoinRequest, IGroupJoinResult } from "./group-invite.types";
|
|
12
|
-
/**
|
|
13
|
-
* Active group invitation listeners.
|
|
14
|
-
* Key is groupId, value contains the password and alias being listened for.
|
|
15
|
-
*
|
|
16
|
-
* Used by admins who are waiting for join requests.
|
|
17
|
-
*/
|
|
18
|
-
export declare const groupInviteListeners: import("../data/persistent-vars").PersistentVar<Record<string, IGroupInviteListener>>;
|
|
19
|
-
/**
|
|
20
|
-
* Pending join requests that need admin approval.
|
|
21
|
-
* Array of requests waiting to be approved or denied.
|
|
22
|
-
*
|
|
23
|
-
* These are shown to the admin in the group details UI.
|
|
24
|
-
*/
|
|
25
|
-
export declare const groupInviteRequests: import("../data/persistent-vars").PersistentVar<IGroupJoinRequest[]>;
|
|
26
|
-
/**
|
|
27
|
-
* Status message for the group invite UI (admin side).
|
|
28
|
-
* Used to show success/error messages to the user.
|
|
29
|
-
*/
|
|
30
|
-
export declare const groupInviteStatus: import("../data/persistent-vars").PersistentVar<string>;
|
|
31
|
-
/**
|
|
32
|
-
* Status message for the join group UI (joiner side).
|
|
33
|
-
* Used to show discovery/join status to the user.
|
|
34
|
-
*/
|
|
35
|
-
export declare const groupJoinStatus: import("../data/persistent-vars").PersistentVar<string>;
|
|
36
|
-
/**
|
|
37
|
-
* Results from discovering group listeners.
|
|
38
|
-
* Updated by the device layer after discovery completes.
|
|
39
|
-
*/
|
|
40
|
-
export declare const groupInviteDiscoveryResult: import("../data/persistent-vars").PersistentVar<IGroupInviteListenerInfo[]>;
|
|
41
|
-
/**
|
|
42
|
-
* Result of joining a group.
|
|
43
|
-
* Updated by the device layer after join completes.
|
|
44
|
-
*/
|
|
45
|
-
export declare const groupInviteJoinResult: import("../data/persistent-vars").PersistentVar<IGroupJoinResult | null>;
|
|
46
|
-
/**
|
|
47
|
-
* Responses to join requests, keyed by requestId.
|
|
48
|
-
* Written by processJoinRequest, read by handleJoinRequest.
|
|
49
|
-
* Entries are cleaned up after being read.
|
|
50
|
-
*/
|
|
51
|
-
export declare const groupInviteResponses: import("../data/persistent-vars").PersistentVar<Record<string, {
|
|
52
|
-
statusCode: number;
|
|
53
|
-
payload: string;
|
|
54
|
-
}>>;
|
|
55
|
-
/**
|
|
56
|
-
* Trigger to start listening for invitations.
|
|
57
|
-
* Set by UI: { groupId, password }
|
|
58
|
-
* Device layer clears after processing.
|
|
59
|
-
*/
|
|
60
|
-
export declare const groupInviteStartListening: import("../data/persistent-vars").PersistentVar<{
|
|
61
|
-
groupId: string;
|
|
62
|
-
password: string;
|
|
63
|
-
} | null>;
|
|
64
|
-
/**
|
|
65
|
-
* Trigger to stop listening for invitations.
|
|
66
|
-
* Set by UI: groupId
|
|
67
|
-
* Device layer clears after processing.
|
|
68
|
-
*/
|
|
69
|
-
export declare const groupInviteStopListening: import("../data/persistent-vars").PersistentVar<string | null>;
|
|
70
|
-
/**
|
|
71
|
-
* Trigger to discover groups with a password.
|
|
72
|
-
* Set by UI: password
|
|
73
|
-
* Device layer updates groupInviteDiscoveryResult and clears this.
|
|
74
|
-
*/
|
|
75
|
-
export declare const groupInviteDiscover: import("../data/persistent-vars").PersistentVar<string | null>;
|
|
76
|
-
/**
|
|
77
|
-
* Trigger to send a join request.
|
|
78
|
-
* Set by UI: { listener, password }
|
|
79
|
-
* Device layer updates groupInviteJoinResult and clears this.
|
|
80
|
-
*/
|
|
81
|
-
export declare const groupInviteSendRequest: import("../data/persistent-vars").PersistentVar<{
|
|
82
|
-
listener: IGroupInviteListenerInfo;
|
|
83
|
-
password: string;
|
|
84
|
-
} | null>;
|
|
85
|
-
/**
|
|
86
|
-
* Trigger to process (approve/deny) a join request.
|
|
87
|
-
* Set by UI: { requestId, approved, role }
|
|
88
|
-
* Device layer processes and clears this.
|
|
89
|
-
*/
|
|
90
|
-
export declare const groupInviteProcessRequest: import("../data/persistent-vars").PersistentVar<{
|
|
91
|
-
requestId: string;
|
|
92
|
-
approved: boolean;
|
|
93
|
-
role: GroupMemberRole;
|
|
94
|
-
} | null>;
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Persistent variables for group invitation state.
|
|
4
|
-
*
|
|
5
|
-
* These device-level variables track:
|
|
6
|
-
* - Active invitation listeners (admin side)
|
|
7
|
-
* - Pending join requests (admin side)
|
|
8
|
-
* - Status messages for UI feedback
|
|
9
|
-
* - Action triggers (UI sets these, device layer reacts)
|
|
10
|
-
*/
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.groupInviteProcessRequest = exports.groupInviteSendRequest = exports.groupInviteDiscover = exports.groupInviteStopListening = exports.groupInviteStartListening = exports.groupInviteResponses = exports.groupInviteJoinResult = exports.groupInviteDiscoveryResult = exports.groupJoinStatus = exports.groupInviteStatus = exports.groupInviteRequests = exports.groupInviteListeners = void 0;
|
|
13
|
-
const persistent_vars_1 = require("../data/persistent-vars");
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// STATE VARIABLES (read by both UI and device layer)
|
|
16
|
-
// ============================================================================
|
|
17
|
-
/**
|
|
18
|
-
* Active group invitation listeners.
|
|
19
|
-
* Key is groupId, value contains the password and alias being listened for.
|
|
20
|
-
*
|
|
21
|
-
* Used by admins who are waiting for join requests.
|
|
22
|
-
*/
|
|
23
|
-
exports.groupInviteListeners = (0, persistent_vars_1.deviceVar)("groupInviteListeners", { defaultValue: {} });
|
|
24
|
-
/**
|
|
25
|
-
* Pending join requests that need admin approval.
|
|
26
|
-
* Array of requests waiting to be approved or denied.
|
|
27
|
-
*
|
|
28
|
-
* These are shown to the admin in the group details UI.
|
|
29
|
-
*/
|
|
30
|
-
exports.groupInviteRequests = (0, persistent_vars_1.deviceVar)("groupInviteRequests", {
|
|
31
|
-
defaultValue: [],
|
|
32
|
-
});
|
|
33
|
-
/**
|
|
34
|
-
* Status message for the group invite UI (admin side).
|
|
35
|
-
* Used to show success/error messages to the user.
|
|
36
|
-
*/
|
|
37
|
-
exports.groupInviteStatus = (0, persistent_vars_1.deviceVar)("groupInviteStatus", { defaultValue: "" });
|
|
38
|
-
/**
|
|
39
|
-
* Status message for the join group UI (joiner side).
|
|
40
|
-
* Used to show discovery/join status to the user.
|
|
41
|
-
*/
|
|
42
|
-
exports.groupJoinStatus = (0, persistent_vars_1.deviceVar)("groupJoinStatus", { defaultValue: "" });
|
|
43
|
-
/**
|
|
44
|
-
* Results from discovering group listeners.
|
|
45
|
-
* Updated by the device layer after discovery completes.
|
|
46
|
-
*/
|
|
47
|
-
exports.groupInviteDiscoveryResult = (0, persistent_vars_1.deviceVar)("groupInviteDiscoveryResult", { defaultValue: [] });
|
|
48
|
-
/**
|
|
49
|
-
* Result of joining a group.
|
|
50
|
-
* Updated by the device layer after join completes.
|
|
51
|
-
*/
|
|
52
|
-
exports.groupInviteJoinResult = (0, persistent_vars_1.deviceVar)("groupInviteJoinResult", {
|
|
53
|
-
defaultValue: null,
|
|
54
|
-
});
|
|
55
|
-
/**
|
|
56
|
-
* Responses to join requests, keyed by requestId.
|
|
57
|
-
* Written by processJoinRequest, read by handleJoinRequest.
|
|
58
|
-
* Entries are cleaned up after being read.
|
|
59
|
-
*/
|
|
60
|
-
exports.groupInviteResponses = (0, persistent_vars_1.deviceVar)("groupInviteResponses", { defaultValue: {} });
|
|
61
|
-
// ============================================================================
|
|
62
|
-
// ACTION TRIGGERS (UI sets these, device layer reacts)
|
|
63
|
-
// ============================================================================
|
|
64
|
-
/**
|
|
65
|
-
* Trigger to start listening for invitations.
|
|
66
|
-
* Set by UI: { groupId, password }
|
|
67
|
-
* Device layer clears after processing.
|
|
68
|
-
*/
|
|
69
|
-
exports.groupInviteStartListening = (0, persistent_vars_1.deviceVar)("groupInviteStartListening", { defaultValue: null });
|
|
70
|
-
/**
|
|
71
|
-
* Trigger to stop listening for invitations.
|
|
72
|
-
* Set by UI: groupId
|
|
73
|
-
* Device layer clears after processing.
|
|
74
|
-
*/
|
|
75
|
-
exports.groupInviteStopListening = (0, persistent_vars_1.deviceVar)("groupInviteStopListening", {
|
|
76
|
-
defaultValue: null,
|
|
77
|
-
});
|
|
78
|
-
/**
|
|
79
|
-
* Trigger to discover groups with a password.
|
|
80
|
-
* Set by UI: password
|
|
81
|
-
* Device layer updates groupInviteDiscoveryResult and clears this.
|
|
82
|
-
*/
|
|
83
|
-
exports.groupInviteDiscover = (0, persistent_vars_1.deviceVar)("groupInviteDiscover", {
|
|
84
|
-
defaultValue: null,
|
|
85
|
-
});
|
|
86
|
-
/**
|
|
87
|
-
* Trigger to send a join request.
|
|
88
|
-
* Set by UI: { listener, password }
|
|
89
|
-
* Device layer updates groupInviteJoinResult and clears this.
|
|
90
|
-
*/
|
|
91
|
-
exports.groupInviteSendRequest = (0, persistent_vars_1.deviceVar)("groupInviteSendRequest", { defaultValue: null });
|
|
92
|
-
/**
|
|
93
|
-
* Trigger to process (approve/deny) a join request.
|
|
94
|
-
* Set by UI: { requestId, approved, role }
|
|
95
|
-
* Device layer processes and clears this.
|
|
96
|
-
*/
|
|
97
|
-
exports.groupInviteProcessRequest = (0, persistent_vars_1.deviceVar)("groupInviteProcessRequest", { defaultValue: null });
|
|
98
|
-
// ============================================================================
|
|
99
|
-
// AUTO-CLEANUP
|
|
100
|
-
// ============================================================================
|
|
101
|
-
// Auto-cleanup of expired listeners (10 minute expiry)
|
|
102
|
-
exports.groupInviteListeners.loadingPromise.then(() => {
|
|
103
|
-
// Check every minute for expired listeners
|
|
104
|
-
setInterval(() => {
|
|
105
|
-
const listeners = (0, exports.groupInviteListeners)();
|
|
106
|
-
const now = Date.now();
|
|
107
|
-
const expiredGroupIds = [];
|
|
108
|
-
for (const [groupId, listener] of Object.entries(listeners)) {
|
|
109
|
-
const startedAt = new Date(listener.startedAt).getTime();
|
|
110
|
-
const elapsed = now - startedAt;
|
|
111
|
-
// Expire after 10 minutes
|
|
112
|
-
if (elapsed > 600_000) {
|
|
113
|
-
expiredGroupIds.push(groupId);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (expiredGroupIds.length > 0) {
|
|
117
|
-
const updated = { ...listeners };
|
|
118
|
-
for (const groupId of expiredGroupIds) {
|
|
119
|
-
delete updated[groupId];
|
|
120
|
-
}
|
|
121
|
-
(0, exports.groupInviteListeners)(updated);
|
|
122
|
-
}
|
|
123
|
-
}, 60_000);
|
|
124
|
-
});
|
|
125
|
-
// Clean up old requests after 10 minutes
|
|
126
|
-
exports.groupInviteRequests.loadingPromise.then(() => {
|
|
127
|
-
setInterval(() => {
|
|
128
|
-
const requests = (0, exports.groupInviteRequests)();
|
|
129
|
-
const now = Date.now();
|
|
130
|
-
const validRequests = requests.filter((req) => {
|
|
131
|
-
const receivedAt = new Date(req.receivedAt).getTime();
|
|
132
|
-
const elapsed = now - receivedAt;
|
|
133
|
-
return elapsed < 600_000; // Keep for 10 minutes
|
|
134
|
-
});
|
|
135
|
-
if (validRequests.length !== requests.length) {
|
|
136
|
-
(0, exports.groupInviteRequests)(validRequests);
|
|
137
|
-
}
|
|
138
|
-
}, 60_000);
|
|
139
|
-
});
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Types for the group invitation flow.
|
|
3
|
-
*
|
|
4
|
-
* This flow allows admins to invite new members to a group using a simple
|
|
5
|
-
* password. Multiple admins can listen for the same password simultaneously.
|
|
6
|
-
*/
|
|
7
|
-
import type { GroupMemberRole } from "../data/group-member-roles";
|
|
8
|
-
import type { IGroupMember } from "../data/group-members";
|
|
9
|
-
import type { IGroupShare } from "../data/group-share";
|
|
10
|
-
/**
|
|
11
|
-
* Information about a group shared during the invitation flow.
|
|
12
|
-
*/
|
|
13
|
-
export interface IGroupInviteGroupInfo {
|
|
14
|
-
groupId: string;
|
|
15
|
-
name: string;
|
|
16
|
-
description?: string;
|
|
17
|
-
iconClassName?: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Information about the inviting admin shared during discovery.
|
|
21
|
-
*/
|
|
22
|
-
export interface IGroupInviteListenerInfo {
|
|
23
|
-
/** The admin's user ID */
|
|
24
|
-
userId: string;
|
|
25
|
-
/** The admin's display name */
|
|
26
|
-
name: string;
|
|
27
|
-
/** The admin's device ID */
|
|
28
|
-
deviceId: string;
|
|
29
|
-
/** Group information */
|
|
30
|
-
group: IGroupInviteGroupInfo;
|
|
31
|
-
/** The alias being listened on */
|
|
32
|
-
alias: string;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Information sent by a user requesting to join a group.
|
|
36
|
-
*/
|
|
37
|
-
export interface IGroupJoinRequestInfo {
|
|
38
|
-
/** Requesting user's ID */
|
|
39
|
-
userId: string;
|
|
40
|
-
/** Requesting user's display name */
|
|
41
|
-
name?: string;
|
|
42
|
-
/** Requesting user's public signing key */
|
|
43
|
-
publicKey: string;
|
|
44
|
-
/** Requesting user's public box key for encryption */
|
|
45
|
-
publicBoxKey: string;
|
|
46
|
-
/** Requesting user's device ID */
|
|
47
|
-
deviceId: string;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* A pending join request shown to the admin.
|
|
51
|
-
*/
|
|
52
|
-
export interface IGroupJoinRequest {
|
|
53
|
-
/** Unique request ID */
|
|
54
|
-
requestId: string;
|
|
55
|
-
/** The group being joined */
|
|
56
|
-
groupId: string;
|
|
57
|
-
/** The alias this request came in on */
|
|
58
|
-
alias: string;
|
|
59
|
-
/** The password used (for encryption) */
|
|
60
|
-
password: string;
|
|
61
|
-
/** Information about the requesting user */
|
|
62
|
-
requester: IGroupJoinRequestInfo;
|
|
63
|
-
/** When the request was received */
|
|
64
|
-
receivedAt: Date;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Response sent by admin when approving a join request.
|
|
68
|
-
*/
|
|
69
|
-
export interface IGroupJoinApprovalInfo {
|
|
70
|
-
/** Whether the request was approved */
|
|
71
|
-
approved: boolean;
|
|
72
|
-
/** The role assigned to the new member (if approved) */
|
|
73
|
-
role?: GroupMemberRole;
|
|
74
|
-
/** Canonical group record plus Admin+ seed memberships */
|
|
75
|
-
groupShare?: IGroupShare;
|
|
76
|
-
/** Newly signed membership for the joiner, including Reader roles */
|
|
77
|
-
newMember?: IGroupMember;
|
|
78
|
-
/** Admin's user info for adding to contacts */
|
|
79
|
-
adminInfo?: IGroupJoinRequestInfo;
|
|
80
|
-
/** Error message if rejected */
|
|
81
|
-
rejectionReason?: string;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Signal that a joiner finished importing approval data and can connect to the admin.
|
|
85
|
-
*/
|
|
86
|
-
export interface IGroupInviteJoinFinished {
|
|
87
|
-
/** Group the joiner just imported */
|
|
88
|
-
groupId: string;
|
|
89
|
-
/** Approving admin device that should be contacted in that group context */
|
|
90
|
-
adminDeviceId: string;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Active invitation listener state.
|
|
94
|
-
*/
|
|
95
|
-
export interface IGroupInviteListener {
|
|
96
|
-
/** The group being invited to */
|
|
97
|
-
groupId: string;
|
|
98
|
-
/** The password being listened for */
|
|
99
|
-
password: string;
|
|
100
|
-
/** The derived alias for network discovery */
|
|
101
|
-
alias: string;
|
|
102
|
-
/** When listening started */
|
|
103
|
-
startedAt: Date;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Result of discovering group invite listeners.
|
|
107
|
-
*/
|
|
108
|
-
export interface IGroupInviteDiscoveryResult {
|
|
109
|
-
/** List of listeners found */
|
|
110
|
-
listeners: IGroupInviteListenerInfo[];
|
|
111
|
-
/** Any error that occurred */
|
|
112
|
-
error?: string;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Result of a join request.
|
|
116
|
-
*/
|
|
117
|
-
export interface IGroupJoinResult {
|
|
118
|
-
/** Whether the join was successful */
|
|
119
|
-
success: boolean;
|
|
120
|
-
/** The group ID if successful */
|
|
121
|
-
groupId?: string;
|
|
122
|
-
/** Error message if failed */
|
|
123
|
-
error?: string;
|
|
124
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Types for the group invitation flow.
|
|
4
|
-
*
|
|
5
|
-
* This flow allows admins to invite new members to a group using a simple
|
|
6
|
-
* password. Multiple admins can listen for the same password simultaneously.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utilities for generating and handling connection codes.
|
|
3
|
-
*
|
|
4
|
-
* Connection codes are 12 characters of Crockford Base32:
|
|
5
|
-
* - First 4 chars: device alias (rendezvous point)
|
|
6
|
-
* - Last 8 chars: shared secret for encryption
|
|
7
|
-
*
|
|
8
|
-
* Displayed format: XXXX-YYYY-ZZZZ
|
|
9
|
-
*/
|
|
10
|
-
import type { IUserConnectInfo } from "./user-connect.types";
|
|
11
|
-
export interface IConnectionCode {
|
|
12
|
-
/** Full 12-character code */
|
|
13
|
-
code: string;
|
|
14
|
-
/** First 4 characters - device alias for rendezvous */
|
|
15
|
-
alias: string;
|
|
16
|
-
/** Last 8 characters - shared secret for encryption */
|
|
17
|
-
secret: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Generate a new connection code.
|
|
21
|
-
* @returns Object with full code, alias, and secret
|
|
22
|
-
*/
|
|
23
|
-
export declare function generateConnectionCode(): IConnectionCode;
|
|
24
|
-
/**
|
|
25
|
-
* Format a connection code for display.
|
|
26
|
-
* @param code 12-character code
|
|
27
|
-
* @returns Formatted as "XXXX-YYYY-ZZZZ"
|
|
28
|
-
*/
|
|
29
|
-
export declare function formatConnectionCode(code: string): string;
|
|
30
|
-
/**
|
|
31
|
-
* Parse a formatted connection code.
|
|
32
|
-
* @param formatted Code in any format (with or without dashes)
|
|
33
|
-
* @returns Object with alias and secret
|
|
34
|
-
*/
|
|
35
|
-
export declare function parseConnectionCode(formatted: string): {
|
|
36
|
-
code: string;
|
|
37
|
-
alias: string;
|
|
38
|
-
secret: string;
|
|
39
|
-
};
|
|
40
|
-
/**
|
|
41
|
-
* Encrypt data using the shared secret.
|
|
42
|
-
* @param data Data to encrypt
|
|
43
|
-
* @param secret 8-character shared secret
|
|
44
|
-
* @returns Base64-encoded encrypted data with nonce
|
|
45
|
-
*/
|
|
46
|
-
export declare function encryptWithSecret(data: any, secret: string): string;
|
|
47
|
-
/**
|
|
48
|
-
* Decrypt data using the shared secret.
|
|
49
|
-
* @param encrypted Base64-encoded encrypted data
|
|
50
|
-
* @param secret 8-character shared secret
|
|
51
|
-
* @returns Decrypted data
|
|
52
|
-
*/
|
|
53
|
-
export declare function decryptWithSecret(encrypted: string, secret: string): any;
|
|
54
|
-
/**
|
|
55
|
-
* Generate a confirmation hash from both users' information.
|
|
56
|
-
* Both parties should see the same hash if the exchange was successful.
|
|
57
|
-
* @param userA First user's info
|
|
58
|
-
* @param userB Second user's info
|
|
59
|
-
* @returns 4-character confirmation code
|
|
60
|
-
*/
|
|
61
|
-
export declare function generateConfirmationHash(userA: IUserConnectInfo, userB: IUserConnectInfo): string;
|
|
62
|
-
/**
|
|
63
|
-
* Validate that a string is a valid connection code format.
|
|
64
|
-
* @param code Code to validate (with or without dashes)
|
|
65
|
-
* @returns true if valid
|
|
66
|
-
*/
|
|
67
|
-
export declare function isValidConnectionCode(code: string): boolean;
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Utilities for generating and handling connection codes.
|
|
4
|
-
*
|
|
5
|
-
* Connection codes are 12 characters of Crockford Base32:
|
|
6
|
-
* - First 4 chars: device alias (rendezvous point)
|
|
7
|
-
* - Last 8 chars: shared secret for encryption
|
|
8
|
-
*
|
|
9
|
-
* Displayed format: XXXX-YYYY-ZZZZ
|
|
10
|
-
*/
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.generateConnectionCode = generateConnectionCode;
|
|
13
|
-
exports.formatConnectionCode = formatConnectionCode;
|
|
14
|
-
exports.parseConnectionCode = parseConnectionCode;
|
|
15
|
-
exports.encryptWithSecret = encryptWithSecret;
|
|
16
|
-
exports.decryptWithSecret = decryptWithSecret;
|
|
17
|
-
exports.generateConfirmationHash = generateConfirmationHash;
|
|
18
|
-
exports.isValidConnectionCode = isValidConnectionCode;
|
|
19
|
-
const sha2_1 = require("@noble/hashes/sha2");
|
|
20
|
-
const nacl = require("tweetnacl");
|
|
21
|
-
const tx_encoding_1 = require("../device/tx-encoding");
|
|
22
|
-
const keys_1 = require("../keys");
|
|
23
|
-
/**
|
|
24
|
-
* Crockford Base32 alphabet.
|
|
25
|
-
* Excludes I, L, O, U to avoid confusion with 1, 1, 0, V respectively.
|
|
26
|
-
*/
|
|
27
|
-
const CROCKFORD_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
28
|
-
/**
|
|
29
|
-
* Generate random bytes using nacl.
|
|
30
|
-
*/
|
|
31
|
-
function randomBytes(length) {
|
|
32
|
-
return nacl.randomBytes(length);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Encode bytes to Crockford Base32 string.
|
|
36
|
-
*/
|
|
37
|
-
function toCrockfordBase32(bytes, length) {
|
|
38
|
-
let result = "";
|
|
39
|
-
let i = 0;
|
|
40
|
-
while (result.length < length) {
|
|
41
|
-
if (i >= bytes.length) {
|
|
42
|
-
// Need more bytes
|
|
43
|
-
const moreBytes = randomBytes(length - result.length);
|
|
44
|
-
for (const b of moreBytes) {
|
|
45
|
-
result += CROCKFORD_ALPHABET[b % 32];
|
|
46
|
-
if (result.length >= length)
|
|
47
|
-
break;
|
|
48
|
-
}
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
// Use 5 bits at a time for base32
|
|
52
|
-
result += CROCKFORD_ALPHABET[bytes[i] % 32];
|
|
53
|
-
i++;
|
|
54
|
-
}
|
|
55
|
-
return result.substring(0, length);
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Derive a 32-byte encryption key from the shared secret.
|
|
59
|
-
*/
|
|
60
|
-
function deriveKeyFromSecret(secret) {
|
|
61
|
-
// Use SHA-256 to derive a proper 32-byte key from the secret
|
|
62
|
-
const secretBytes = new TextEncoder().encode(secret);
|
|
63
|
-
return (0, sha2_1.sha256)(secretBytes);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Generate a new connection code.
|
|
67
|
-
* @returns Object with full code, alias, and secret
|
|
68
|
-
*/
|
|
69
|
-
function generateConnectionCode() {
|
|
70
|
-
const bytes = randomBytes(16);
|
|
71
|
-
const alias = toCrockfordBase32(bytes.slice(0, 4), 4);
|
|
72
|
-
const secret = toCrockfordBase32(bytes.slice(4), 8);
|
|
73
|
-
return {
|
|
74
|
-
code: alias + secret,
|
|
75
|
-
alias,
|
|
76
|
-
secret,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Format a connection code for display.
|
|
81
|
-
* @param code 12-character code
|
|
82
|
-
* @returns Formatted as "XXXX-YYYY-ZZZZ"
|
|
83
|
-
*/
|
|
84
|
-
function formatConnectionCode(code) {
|
|
85
|
-
const normalized = code.toUpperCase().replace(/[^0-9A-Z]/g, "");
|
|
86
|
-
if (normalized.length !== 12) {
|
|
87
|
-
throw new Error(`Connection code must be 12 characters, got ${normalized.length}`);
|
|
88
|
-
}
|
|
89
|
-
return `${normalized.slice(0, 4)}-${normalized.slice(4, 8)}-${normalized.slice(8, 12)}`;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Parse a formatted connection code.
|
|
93
|
-
* @param formatted Code in any format (with or without dashes)
|
|
94
|
-
* @returns Object with alias and secret
|
|
95
|
-
*/
|
|
96
|
-
function parseConnectionCode(formatted) {
|
|
97
|
-
const normalized = formatted.toUpperCase().replace(/[^0-9A-Z]/g, "");
|
|
98
|
-
if (normalized.length !== 12) {
|
|
99
|
-
throw new Error(`Connection code must be 12 characters, got ${normalized.length}`);
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
code: normalized,
|
|
103
|
-
alias: normalized.slice(0, 4),
|
|
104
|
-
secret: normalized.slice(4, 12),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Encrypt data using the shared secret.
|
|
109
|
-
* @param data Data to encrypt
|
|
110
|
-
* @param secret 8-character shared secret
|
|
111
|
-
* @returns Base64-encoded encrypted data with nonce
|
|
112
|
-
*/
|
|
113
|
-
function encryptWithSecret(data, secret) {
|
|
114
|
-
const key = deriveKeyFromSecret(secret);
|
|
115
|
-
const nonce = nacl.randomBytes(24);
|
|
116
|
-
const dataBytes = (0, tx_encoding_1.txEncode)(data);
|
|
117
|
-
const encrypted = nacl.secretbox(dataBytes, nonce, key);
|
|
118
|
-
// Combine nonce + encrypted data
|
|
119
|
-
const combined = new Uint8Array(nonce.length + encrypted.length);
|
|
120
|
-
combined.set(nonce);
|
|
121
|
-
combined.set(encrypted, nonce.length);
|
|
122
|
-
return (0, keys_1.encodeBase64)(combined);
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Decrypt data using the shared secret.
|
|
126
|
-
* @param encrypted Base64-encoded encrypted data
|
|
127
|
-
* @param secret 8-character shared secret
|
|
128
|
-
* @returns Decrypted data
|
|
129
|
-
*/
|
|
130
|
-
function decryptWithSecret(encrypted, secret) {
|
|
131
|
-
const key = deriveKeyFromSecret(secret);
|
|
132
|
-
const combined = (0, keys_1.decodeBase64)(encrypted);
|
|
133
|
-
const nonce = combined.slice(0, 24);
|
|
134
|
-
const ciphertext = combined.slice(24);
|
|
135
|
-
const decrypted = nacl.secretbox.open(ciphertext, nonce, key);
|
|
136
|
-
if (!decrypted) {
|
|
137
|
-
throw new Error("Decryption failed - invalid secret or corrupted data");
|
|
138
|
-
}
|
|
139
|
-
return (0, tx_encoding_1.txDecode)(decrypted);
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Generate a confirmation hash from both users' information.
|
|
143
|
-
* Both parties should see the same hash if the exchange was successful.
|
|
144
|
-
* @param userA First user's info
|
|
145
|
-
* @param userB Second user's info
|
|
146
|
-
* @returns 4-character confirmation code
|
|
147
|
-
*/
|
|
148
|
-
function generateConfirmationHash(userA, userB) {
|
|
149
|
-
// Sort by userId to ensure consistent ordering regardless of who is A or B
|
|
150
|
-
const sorted = [userA, userB].sort((a, b) => a.userId.localeCompare(b.userId));
|
|
151
|
-
const combined = JSON.stringify(sorted);
|
|
152
|
-
const hash = (0, sha2_1.sha256)(new TextEncoder().encode(combined));
|
|
153
|
-
// Take first 4 characters of the hash encoded in Crockford Base32
|
|
154
|
-
return toCrockfordBase32(hash.slice(0, 4), 4);
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Validate that a string is a valid connection code format.
|
|
158
|
-
* @param code Code to validate (with or without dashes)
|
|
159
|
-
* @returns true if valid
|
|
160
|
-
*/
|
|
161
|
-
function isValidConnectionCode(code) {
|
|
162
|
-
try {
|
|
163
|
-
const normalized = code.toUpperCase().replace(/[^0-9A-Z]/g, "");
|
|
164
|
-
if (normalized.length !== 12)
|
|
165
|
-
return false;
|
|
166
|
-
// Check all characters are valid Crockford Base32
|
|
167
|
-
for (const char of normalized) {
|
|
168
|
-
if (!CROCKFORD_ALPHABET.includes(char))
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
}
|