@teamvortexsoftware/vortex-node-22-sdk 0.1.3 โ 0.3.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 +44 -3
- package/dist/index.d.mts +160 -8
- package/dist/index.d.ts +160 -8
- package/dist/index.js +98 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +98 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,6 +4,21 @@ This package provides the Vortex Node/Typescript SDK.
|
|
|
4
4
|
|
|
5
5
|
With this module, you can both generate a JWT for use with the Vortex Widget and make API calls to the Vortex API.
|
|
6
6
|
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
### Invitation Delivery Types
|
|
10
|
+
|
|
11
|
+
Vortex supports multiple delivery methods for invitations:
|
|
12
|
+
|
|
13
|
+
- **`email`** - Email invitations sent by Vortex (includes reminders and nudges)
|
|
14
|
+
- **`phone`** - Phone invitations sent by the user/customer
|
|
15
|
+
- **`share`** - Shareable invitation links for social sharing
|
|
16
|
+
- **`internal`** - Internal invitations managed entirely by your application
|
|
17
|
+
- No email/SMS communication triggered by Vortex
|
|
18
|
+
- Target value can be any customer-defined identifier (UUID, string, number)
|
|
19
|
+
- Useful for in-app invitation flows where you handle the delivery
|
|
20
|
+
- Example use case: In-app notifications, dashboard invites, etc.
|
|
21
|
+
|
|
7
22
|
## Use Cases and Examples
|
|
8
23
|
|
|
9
24
|
### Install the NodeJS SDK
|
|
@@ -45,7 +60,9 @@ app.get('/vortex-jwt', (req, res) => {
|
|
|
45
60
|
user: {
|
|
46
61
|
id: 'user-123',
|
|
47
62
|
email: 'user@example.com',
|
|
48
|
-
|
|
63
|
+
name: 'Jane Doe', // Optional: user's display name
|
|
64
|
+
avatarUrl: 'https://example.com/avatars/jane.jpg', // Optional: user's avatar URL
|
|
65
|
+
adminScopes: ['autojoin'], // Optional: grants admin privileges for autojoining
|
|
49
66
|
},
|
|
50
67
|
});
|
|
51
68
|
|
|
@@ -57,6 +74,30 @@ app.listen(port, () => {
|
|
|
57
74
|
});
|
|
58
75
|
```
|
|
59
76
|
|
|
77
|
+
#### User Profile Information (Optional)
|
|
78
|
+
|
|
79
|
+
You can optionally include the user's name and avatar URL in JWTs. This information will be stored and returned when fetching invitations created by this user.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const token = vortex.generateJwt({
|
|
83
|
+
user: {
|
|
84
|
+
id: 'user-123',
|
|
85
|
+
email: 'user@example.com',
|
|
86
|
+
name: 'Jane Doe', // Optional
|
|
87
|
+
avatarUrl: 'https://example.com/avatars/jane.jpg', // Optional
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Requirements:**
|
|
93
|
+
- `name`: Optional string (max 200 characters)
|
|
94
|
+
- `avatarUrl`: Optional HTTPS URL (max 2000 characters)
|
|
95
|
+
- Both fields are optional and can be omitted
|
|
96
|
+
|
|
97
|
+
**Validation:**
|
|
98
|
+
- Invalid or non-HTTPS avatar URLs will be ignored with a warning
|
|
99
|
+
- Authentication will succeed even with invalid avatar URLs
|
|
100
|
+
|
|
60
101
|
You can also add extra properties to the JWT payload:
|
|
61
102
|
|
|
62
103
|
```ts
|
|
@@ -87,7 +128,7 @@ const userId = 'users-id-in-my-system';
|
|
|
87
128
|
const identifiers = [
|
|
88
129
|
{ type: 'email', value: 'users@emailaddress.com' },
|
|
89
130
|
{ type: 'email', value: 'someother@address.com' },
|
|
90
|
-
{ type: '
|
|
131
|
+
{ type: 'phone', value: '18008675309' },
|
|
91
132
|
];
|
|
92
133
|
|
|
93
134
|
// groups are specific to your product. This list should be the groups that the current requesting user is a part of. It is up to you to define them if you so choose. Based on the values here, we can determine whether or not the user is allowed to invite others to a particular group
|
|
@@ -219,7 +260,7 @@ app.get('/invite/landing', async (req, res) => {
|
|
|
219
260
|
|
|
220
261
|
### View invitations by target (email address for example)
|
|
221
262
|
|
|
222
|
-
Depending on your use case, you may want to accept all outstanding invitations to a given user when they sign up for your service. If you don't want to auto accept, you may want to present the new user with a list of all invitations that target them. Either way, the example below shows how you fetch these invitations once you know how to identify (via email,
|
|
263
|
+
Depending on your use case, you may want to accept all outstanding invitations to a given user when they sign up for your service. If you don't want to auto accept, you may want to present the new user with a list of all invitations that target them. Either way, the example below shows how you fetch these invitations once you know how to identify (via email, phone or others in the future) a new user to your product.
|
|
223
264
|
|
|
224
265
|
```ts
|
|
225
266
|
app.get('/invitations/by-email', async (req, res) => {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
type InvitationTarget = {
|
|
2
|
-
type: 'email' | '
|
|
2
|
+
type: 'email' | 'phone';
|
|
3
3
|
value: string;
|
|
4
4
|
};
|
|
5
5
|
/**
|
|
@@ -40,7 +40,7 @@ type InvitationResult = {
|
|
|
40
40
|
createdAt: string;
|
|
41
41
|
deactivated: boolean;
|
|
42
42
|
deliveryCount: number;
|
|
43
|
-
deliveryTypes: ('email' | '
|
|
43
|
+
deliveryTypes: ('email' | 'phone' | 'share' | 'internal')[];
|
|
44
44
|
foreignCreatorId: string;
|
|
45
45
|
invitationType: 'single_use' | 'multi_use';
|
|
46
46
|
modifiedAt: string | null;
|
|
@@ -53,21 +53,40 @@ type InvitationResult = {
|
|
|
53
53
|
accepts: InvitationAcceptance[];
|
|
54
54
|
expired: boolean;
|
|
55
55
|
expires?: string;
|
|
56
|
+
source?: string;
|
|
57
|
+
creatorName?: string | null;
|
|
58
|
+
creatorAvatarUrl?: string | null;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* User type for accepting invitations
|
|
62
|
+
* Requires either email or phone (or both)
|
|
63
|
+
*/
|
|
64
|
+
type AcceptUser = {
|
|
65
|
+
email?: string;
|
|
66
|
+
phone?: string;
|
|
67
|
+
name?: string;
|
|
56
68
|
};
|
|
57
69
|
type AcceptInvitationRequest = {
|
|
70
|
+
invitationIds: string[];
|
|
71
|
+
user: AcceptUser;
|
|
72
|
+
};
|
|
73
|
+
type AcceptInvitationRequestLegacy = {
|
|
58
74
|
invitationIds: string[];
|
|
59
75
|
target: InvitationTarget;
|
|
60
76
|
};
|
|
61
77
|
type ApiResponseJson = InvitationResult | {
|
|
62
78
|
invitations: InvitationResult[];
|
|
63
79
|
} | {};
|
|
64
|
-
type ApiRequestBody = AcceptInvitationRequest | null;
|
|
80
|
+
type ApiRequestBody = AcceptInvitationRequest | AcceptInvitationRequestLegacy | null;
|
|
65
81
|
/**
|
|
66
82
|
* User type for JWT generation
|
|
83
|
+
* Requires both id and email
|
|
67
84
|
*/
|
|
68
85
|
type User = {
|
|
69
86
|
id: string;
|
|
70
87
|
email: string;
|
|
88
|
+
name?: string;
|
|
89
|
+
avatarUrl?: string;
|
|
71
90
|
adminScopes?: string[];
|
|
72
91
|
[key: string]: any;
|
|
73
92
|
};
|
|
@@ -96,6 +115,73 @@ type ConfigureAutojoinRequest = {
|
|
|
96
115
|
widgetId: string;
|
|
97
116
|
metadata?: Record<string, any>;
|
|
98
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* Target types for creating invitations
|
|
120
|
+
*/
|
|
121
|
+
type CreateInvitationTargetType = 'email' | 'phone' | 'internal';
|
|
122
|
+
/**
|
|
123
|
+
* Target for creating an invitation
|
|
124
|
+
*/
|
|
125
|
+
type CreateInvitationTarget = {
|
|
126
|
+
type: CreateInvitationTargetType;
|
|
127
|
+
value: string;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Information about the user creating the invitation (the inviter)
|
|
131
|
+
*/
|
|
132
|
+
type Inviter = {
|
|
133
|
+
/** The internal user ID of the person creating the invitation (from your system) */
|
|
134
|
+
userId: string;
|
|
135
|
+
/** The email address of the person creating the invitation */
|
|
136
|
+
userEmail?: string;
|
|
137
|
+
/** The display name of the person creating the invitation */
|
|
138
|
+
name?: string;
|
|
139
|
+
/** Avatar URL for the person creating the invitation */
|
|
140
|
+
avatarUrl?: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Group information for creating invitations
|
|
144
|
+
*/
|
|
145
|
+
type CreateInvitationGroup = {
|
|
146
|
+
/** The type of the group/scope (e.g., "team", "organization", "project") */
|
|
147
|
+
type: string;
|
|
148
|
+
/** The ID of the group/scope in your system */
|
|
149
|
+
groupId: string;
|
|
150
|
+
/** The display name of the group/scope */
|
|
151
|
+
name: string;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Request body for creating an invitation via the public API
|
|
155
|
+
*/
|
|
156
|
+
type CreateInvitationRequest = {
|
|
157
|
+
/** The ID of the widget configuration to use for this invitation */
|
|
158
|
+
widgetConfigurationId: string;
|
|
159
|
+
/** The target of the invitation (who is being invited) */
|
|
160
|
+
target: CreateInvitationTarget;
|
|
161
|
+
/** Information about the user creating the invitation */
|
|
162
|
+
inviter: Inviter;
|
|
163
|
+
/** Groups/scopes to associate with this invitation */
|
|
164
|
+
groups?: CreateInvitationGroup[];
|
|
165
|
+
/** The source of the invitation for analytics (e.g., "api", "backend", "pymk") */
|
|
166
|
+
source?: string;
|
|
167
|
+
/** Template variables for email customization */
|
|
168
|
+
templateVariables?: Record<string, string>;
|
|
169
|
+
/** Custom metadata to attach to the invitation (passed through to webhooks) */
|
|
170
|
+
metadata?: Record<string, any>;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Response from creating an invitation
|
|
174
|
+
*/
|
|
175
|
+
type CreateInvitationResponse = {
|
|
176
|
+
/** The ID of the created invitation */
|
|
177
|
+
id: string;
|
|
178
|
+
/** The short link for the invitation */
|
|
179
|
+
shortLink: string;
|
|
180
|
+
/** The status of the invitation */
|
|
181
|
+
status: string;
|
|
182
|
+
/** When the invitation was created */
|
|
183
|
+
createdAt: string;
|
|
184
|
+
};
|
|
99
185
|
|
|
100
186
|
declare class Vortex {
|
|
101
187
|
private apiKey;
|
|
@@ -131,10 +217,34 @@ declare class Vortex {
|
|
|
131
217
|
getInvitationsByTarget(targetType: 'email' | 'username' | 'phoneNumber', targetValue: string): Promise<InvitationResult[]>;
|
|
132
218
|
getInvitation(invitationId: string): Promise<InvitationResult>;
|
|
133
219
|
revokeInvitation(invitationId: string): Promise<{}>;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Accept invitations using the new User format (preferred)
|
|
222
|
+
* @param invitationIds - Array of invitation IDs to accept
|
|
223
|
+
* @param user - User object with email or phone (and optional name)
|
|
224
|
+
* @returns Invitation result
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;
|
|
231
|
+
/**
|
|
232
|
+
* Accept invitations using legacy target format (deprecated)
|
|
233
|
+
* @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })
|
|
234
|
+
* @param invitationIds - Array of invitation IDs to accept
|
|
235
|
+
* @param target - Legacy target object with type and value
|
|
236
|
+
* @returns Invitation result
|
|
237
|
+
*/
|
|
238
|
+
acceptInvitations(invitationIds: string[], target: InvitationTarget): Promise<InvitationResult>;
|
|
239
|
+
/**
|
|
240
|
+
* Accept invitations using multiple legacy targets (deprecated)
|
|
241
|
+
* Will call the accept endpoint once per target
|
|
242
|
+
* @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })
|
|
243
|
+
* @param invitationIds - Array of invitation IDs to accept
|
|
244
|
+
* @param targets - Array of legacy target objects
|
|
245
|
+
* @returns Invitation result from the last acceptance
|
|
246
|
+
*/
|
|
247
|
+
acceptInvitations(invitationIds: string[], targets: InvitationTarget[]): Promise<InvitationResult>;
|
|
138
248
|
deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>;
|
|
139
249
|
getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>;
|
|
140
250
|
reinvite(invitationId: string): Promise<InvitationResult>;
|
|
@@ -179,6 +289,48 @@ declare class Vortex {
|
|
|
179
289
|
* ```
|
|
180
290
|
*/
|
|
181
291
|
configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse>;
|
|
292
|
+
/**
|
|
293
|
+
* Create an invitation from your backend
|
|
294
|
+
*
|
|
295
|
+
* This method allows you to create invitations programmatically using your API key,
|
|
296
|
+
* without requiring a user JWT token. This is useful for server-side invitation
|
|
297
|
+
* creation, such as "People You May Know" flows or admin-initiated invitations.
|
|
298
|
+
*
|
|
299
|
+
* @param params - Invitation parameters
|
|
300
|
+
* @param params.widgetConfigurationId - The widget configuration ID to use
|
|
301
|
+
* @param params.target - The target of the invitation (who is being invited)
|
|
302
|
+
* @param params.target.type - 'email', 'phone', or 'internal'
|
|
303
|
+
* @param params.target.value - Email address, phone number, or internal user ID
|
|
304
|
+
* @param params.inviter - Information about the user creating the invitation
|
|
305
|
+
* @param params.inviter.userId - Your internal user ID for the inviter
|
|
306
|
+
* @param params.inviter.userEmail - Optional email of the inviter
|
|
307
|
+
* @param params.inviter.name - Optional display name of the inviter
|
|
308
|
+
* @param params.groups - Optional groups/scopes to associate with the invitation
|
|
309
|
+
* @param params.source - Optional source for analytics (defaults to 'api')
|
|
310
|
+
* @param params.templateVariables - Optional template variables for email customization
|
|
311
|
+
* @param params.metadata - Optional metadata passed through to webhooks
|
|
312
|
+
* @returns Created invitation with ID, short link, status, and creation timestamp
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```typescript
|
|
316
|
+
* // Create an email invitation
|
|
317
|
+
* const invitation = await vortex.createInvitation({
|
|
318
|
+
* widgetConfigurationId: 'widget-config-123',
|
|
319
|
+
* target: { type: 'email', value: 'invitee@example.com' },
|
|
320
|
+
* inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
|
|
321
|
+
* groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
|
|
322
|
+
* });
|
|
323
|
+
*
|
|
324
|
+
* // Create an internal invitation (PYMK flow - no email sent)
|
|
325
|
+
* const pymkInvitation = await vortex.createInvitation({
|
|
326
|
+
* widgetConfigurationId: 'widget-config-123',
|
|
327
|
+
* target: { type: 'internal', value: 'internal-user-id-abc' },
|
|
328
|
+
* inviter: { userId: 'user-456' },
|
|
329
|
+
* source: 'pymk',
|
|
330
|
+
* });
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse>;
|
|
182
334
|
}
|
|
183
335
|
|
|
184
|
-
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type AutojoinDomain, type AutojoinDomainsResponse, type ConfigureAutojoinRequest, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type User, Vortex };
|
|
336
|
+
export { type AcceptInvitationRequest, type AcceptInvitationRequestLegacy, type AcceptUser, type ApiRequestBody, type ApiResponseJson, type AutojoinDomain, type AutojoinDomainsResponse, type ConfigureAutojoinRequest, type CreateInvitationGroup, type CreateInvitationRequest, type CreateInvitationResponse, type CreateInvitationTarget, type CreateInvitationTargetType, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type Inviter, type User, Vortex };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
type InvitationTarget = {
|
|
2
|
-
type: 'email' | '
|
|
2
|
+
type: 'email' | 'phone';
|
|
3
3
|
value: string;
|
|
4
4
|
};
|
|
5
5
|
/**
|
|
@@ -40,7 +40,7 @@ type InvitationResult = {
|
|
|
40
40
|
createdAt: string;
|
|
41
41
|
deactivated: boolean;
|
|
42
42
|
deliveryCount: number;
|
|
43
|
-
deliveryTypes: ('email' | '
|
|
43
|
+
deliveryTypes: ('email' | 'phone' | 'share' | 'internal')[];
|
|
44
44
|
foreignCreatorId: string;
|
|
45
45
|
invitationType: 'single_use' | 'multi_use';
|
|
46
46
|
modifiedAt: string | null;
|
|
@@ -53,21 +53,40 @@ type InvitationResult = {
|
|
|
53
53
|
accepts: InvitationAcceptance[];
|
|
54
54
|
expired: boolean;
|
|
55
55
|
expires?: string;
|
|
56
|
+
source?: string;
|
|
57
|
+
creatorName?: string | null;
|
|
58
|
+
creatorAvatarUrl?: string | null;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* User type for accepting invitations
|
|
62
|
+
* Requires either email or phone (or both)
|
|
63
|
+
*/
|
|
64
|
+
type AcceptUser = {
|
|
65
|
+
email?: string;
|
|
66
|
+
phone?: string;
|
|
67
|
+
name?: string;
|
|
56
68
|
};
|
|
57
69
|
type AcceptInvitationRequest = {
|
|
70
|
+
invitationIds: string[];
|
|
71
|
+
user: AcceptUser;
|
|
72
|
+
};
|
|
73
|
+
type AcceptInvitationRequestLegacy = {
|
|
58
74
|
invitationIds: string[];
|
|
59
75
|
target: InvitationTarget;
|
|
60
76
|
};
|
|
61
77
|
type ApiResponseJson = InvitationResult | {
|
|
62
78
|
invitations: InvitationResult[];
|
|
63
79
|
} | {};
|
|
64
|
-
type ApiRequestBody = AcceptInvitationRequest | null;
|
|
80
|
+
type ApiRequestBody = AcceptInvitationRequest | AcceptInvitationRequestLegacy | null;
|
|
65
81
|
/**
|
|
66
82
|
* User type for JWT generation
|
|
83
|
+
* Requires both id and email
|
|
67
84
|
*/
|
|
68
85
|
type User = {
|
|
69
86
|
id: string;
|
|
70
87
|
email: string;
|
|
88
|
+
name?: string;
|
|
89
|
+
avatarUrl?: string;
|
|
71
90
|
adminScopes?: string[];
|
|
72
91
|
[key: string]: any;
|
|
73
92
|
};
|
|
@@ -96,6 +115,73 @@ type ConfigureAutojoinRequest = {
|
|
|
96
115
|
widgetId: string;
|
|
97
116
|
metadata?: Record<string, any>;
|
|
98
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* Target types for creating invitations
|
|
120
|
+
*/
|
|
121
|
+
type CreateInvitationTargetType = 'email' | 'phone' | 'internal';
|
|
122
|
+
/**
|
|
123
|
+
* Target for creating an invitation
|
|
124
|
+
*/
|
|
125
|
+
type CreateInvitationTarget = {
|
|
126
|
+
type: CreateInvitationTargetType;
|
|
127
|
+
value: string;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Information about the user creating the invitation (the inviter)
|
|
131
|
+
*/
|
|
132
|
+
type Inviter = {
|
|
133
|
+
/** The internal user ID of the person creating the invitation (from your system) */
|
|
134
|
+
userId: string;
|
|
135
|
+
/** The email address of the person creating the invitation */
|
|
136
|
+
userEmail?: string;
|
|
137
|
+
/** The display name of the person creating the invitation */
|
|
138
|
+
name?: string;
|
|
139
|
+
/** Avatar URL for the person creating the invitation */
|
|
140
|
+
avatarUrl?: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Group information for creating invitations
|
|
144
|
+
*/
|
|
145
|
+
type CreateInvitationGroup = {
|
|
146
|
+
/** The type of the group/scope (e.g., "team", "organization", "project") */
|
|
147
|
+
type: string;
|
|
148
|
+
/** The ID of the group/scope in your system */
|
|
149
|
+
groupId: string;
|
|
150
|
+
/** The display name of the group/scope */
|
|
151
|
+
name: string;
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* Request body for creating an invitation via the public API
|
|
155
|
+
*/
|
|
156
|
+
type CreateInvitationRequest = {
|
|
157
|
+
/** The ID of the widget configuration to use for this invitation */
|
|
158
|
+
widgetConfigurationId: string;
|
|
159
|
+
/** The target of the invitation (who is being invited) */
|
|
160
|
+
target: CreateInvitationTarget;
|
|
161
|
+
/** Information about the user creating the invitation */
|
|
162
|
+
inviter: Inviter;
|
|
163
|
+
/** Groups/scopes to associate with this invitation */
|
|
164
|
+
groups?: CreateInvitationGroup[];
|
|
165
|
+
/** The source of the invitation for analytics (e.g., "api", "backend", "pymk") */
|
|
166
|
+
source?: string;
|
|
167
|
+
/** Template variables for email customization */
|
|
168
|
+
templateVariables?: Record<string, string>;
|
|
169
|
+
/** Custom metadata to attach to the invitation (passed through to webhooks) */
|
|
170
|
+
metadata?: Record<string, any>;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Response from creating an invitation
|
|
174
|
+
*/
|
|
175
|
+
type CreateInvitationResponse = {
|
|
176
|
+
/** The ID of the created invitation */
|
|
177
|
+
id: string;
|
|
178
|
+
/** The short link for the invitation */
|
|
179
|
+
shortLink: string;
|
|
180
|
+
/** The status of the invitation */
|
|
181
|
+
status: string;
|
|
182
|
+
/** When the invitation was created */
|
|
183
|
+
createdAt: string;
|
|
184
|
+
};
|
|
99
185
|
|
|
100
186
|
declare class Vortex {
|
|
101
187
|
private apiKey;
|
|
@@ -131,10 +217,34 @@ declare class Vortex {
|
|
|
131
217
|
getInvitationsByTarget(targetType: 'email' | 'username' | 'phoneNumber', targetValue: string): Promise<InvitationResult[]>;
|
|
132
218
|
getInvitation(invitationId: string): Promise<InvitationResult>;
|
|
133
219
|
revokeInvitation(invitationId: string): Promise<{}>;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Accept invitations using the new User format (preferred)
|
|
222
|
+
* @param invitationIds - Array of invitation IDs to accept
|
|
223
|
+
* @param user - User object with email or phone (and optional name)
|
|
224
|
+
* @returns Invitation result
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;
|
|
231
|
+
/**
|
|
232
|
+
* Accept invitations using legacy target format (deprecated)
|
|
233
|
+
* @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })
|
|
234
|
+
* @param invitationIds - Array of invitation IDs to accept
|
|
235
|
+
* @param target - Legacy target object with type and value
|
|
236
|
+
* @returns Invitation result
|
|
237
|
+
*/
|
|
238
|
+
acceptInvitations(invitationIds: string[], target: InvitationTarget): Promise<InvitationResult>;
|
|
239
|
+
/**
|
|
240
|
+
* Accept invitations using multiple legacy targets (deprecated)
|
|
241
|
+
* Will call the accept endpoint once per target
|
|
242
|
+
* @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })
|
|
243
|
+
* @param invitationIds - Array of invitation IDs to accept
|
|
244
|
+
* @param targets - Array of legacy target objects
|
|
245
|
+
* @returns Invitation result from the last acceptance
|
|
246
|
+
*/
|
|
247
|
+
acceptInvitations(invitationIds: string[], targets: InvitationTarget[]): Promise<InvitationResult>;
|
|
138
248
|
deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>;
|
|
139
249
|
getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>;
|
|
140
250
|
reinvite(invitationId: string): Promise<InvitationResult>;
|
|
@@ -179,6 +289,48 @@ declare class Vortex {
|
|
|
179
289
|
* ```
|
|
180
290
|
*/
|
|
181
291
|
configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse>;
|
|
292
|
+
/**
|
|
293
|
+
* Create an invitation from your backend
|
|
294
|
+
*
|
|
295
|
+
* This method allows you to create invitations programmatically using your API key,
|
|
296
|
+
* without requiring a user JWT token. This is useful for server-side invitation
|
|
297
|
+
* creation, such as "People You May Know" flows or admin-initiated invitations.
|
|
298
|
+
*
|
|
299
|
+
* @param params - Invitation parameters
|
|
300
|
+
* @param params.widgetConfigurationId - The widget configuration ID to use
|
|
301
|
+
* @param params.target - The target of the invitation (who is being invited)
|
|
302
|
+
* @param params.target.type - 'email', 'phone', or 'internal'
|
|
303
|
+
* @param params.target.value - Email address, phone number, or internal user ID
|
|
304
|
+
* @param params.inviter - Information about the user creating the invitation
|
|
305
|
+
* @param params.inviter.userId - Your internal user ID for the inviter
|
|
306
|
+
* @param params.inviter.userEmail - Optional email of the inviter
|
|
307
|
+
* @param params.inviter.name - Optional display name of the inviter
|
|
308
|
+
* @param params.groups - Optional groups/scopes to associate with the invitation
|
|
309
|
+
* @param params.source - Optional source for analytics (defaults to 'api')
|
|
310
|
+
* @param params.templateVariables - Optional template variables for email customization
|
|
311
|
+
* @param params.metadata - Optional metadata passed through to webhooks
|
|
312
|
+
* @returns Created invitation with ID, short link, status, and creation timestamp
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```typescript
|
|
316
|
+
* // Create an email invitation
|
|
317
|
+
* const invitation = await vortex.createInvitation({
|
|
318
|
+
* widgetConfigurationId: 'widget-config-123',
|
|
319
|
+
* target: { type: 'email', value: 'invitee@example.com' },
|
|
320
|
+
* inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
|
|
321
|
+
* groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
|
|
322
|
+
* });
|
|
323
|
+
*
|
|
324
|
+
* // Create an internal invitation (PYMK flow - no email sent)
|
|
325
|
+
* const pymkInvitation = await vortex.createInvitation({
|
|
326
|
+
* widgetConfigurationId: 'widget-config-123',
|
|
327
|
+
* target: { type: 'internal', value: 'internal-user-id-abc' },
|
|
328
|
+
* inviter: { userId: 'user-456' },
|
|
329
|
+
* source: 'pymk',
|
|
330
|
+
* });
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse>;
|
|
182
334
|
}
|
|
183
335
|
|
|
184
|
-
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type AutojoinDomain, type AutojoinDomainsResponse, type ConfigureAutojoinRequest, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type User, Vortex };
|
|
336
|
+
export { type AcceptInvitationRequest, type AcceptInvitationRequestLegacy, type AcceptUser, type ApiRequestBody, type ApiResponseJson, type AutojoinDomain, type AutojoinDomainsResponse, type ConfigureAutojoinRequest, type CreateInvitationGroup, type CreateInvitationRequest, type CreateInvitationResponse, type CreateInvitationTarget, type CreateInvitationTargetType, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type Inviter, type User, Vortex };
|
package/dist/index.js
CHANGED
|
@@ -84,6 +84,12 @@ var Vortex = class {
|
|
|
84
84
|
// Include identifiers array for widget compatibility (VrtxAutojoin checks this)
|
|
85
85
|
identifiers: user.email ? [{ type: "email", value: user.email }] : []
|
|
86
86
|
};
|
|
87
|
+
if (user.name) {
|
|
88
|
+
payload.name = user.name;
|
|
89
|
+
}
|
|
90
|
+
if (user.avatarUrl) {
|
|
91
|
+
payload.avatarUrl = user.avatarUrl;
|
|
92
|
+
}
|
|
87
93
|
if (user.adminScopes) {
|
|
88
94
|
payload.adminScopes = user.adminScopes;
|
|
89
95
|
if (user.adminScopes.includes("autojoin")) {
|
|
@@ -165,12 +171,54 @@ var Vortex = class {
|
|
|
165
171
|
path: `/api/v1/invitations/${invitationId}`
|
|
166
172
|
});
|
|
167
173
|
}
|
|
168
|
-
|
|
174
|
+
// Implementation
|
|
175
|
+
async acceptInvitations(invitationIds, userOrTarget) {
|
|
176
|
+
if (Array.isArray(userOrTarget)) {
|
|
177
|
+
console.warn(
|
|
178
|
+
'[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: "user@example.com" })'
|
|
179
|
+
);
|
|
180
|
+
let lastResult;
|
|
181
|
+
for (const target of userOrTarget) {
|
|
182
|
+
lastResult = await this.acceptInvitations(invitationIds, target);
|
|
183
|
+
}
|
|
184
|
+
if (!lastResult) {
|
|
185
|
+
throw new Error("No targets provided");
|
|
186
|
+
}
|
|
187
|
+
return lastResult;
|
|
188
|
+
}
|
|
189
|
+
const isLegacyTarget = "type" in userOrTarget && "value" in userOrTarget;
|
|
190
|
+
if (isLegacyTarget) {
|
|
191
|
+
console.warn(
|
|
192
|
+
'[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: "user@example.com" })'
|
|
193
|
+
);
|
|
194
|
+
const target = userOrTarget;
|
|
195
|
+
const user2 = {};
|
|
196
|
+
if (target.type === "email") {
|
|
197
|
+
user2.email = target.value;
|
|
198
|
+
} else if (target.type === "phone" || target.type === "phoneNumber") {
|
|
199
|
+
user2.phone = target.value;
|
|
200
|
+
} else {
|
|
201
|
+
user2.email = target.value;
|
|
202
|
+
}
|
|
203
|
+
const response2 = await this.vortexApiRequest({
|
|
204
|
+
method: "POST",
|
|
205
|
+
body: {
|
|
206
|
+
invitationIds,
|
|
207
|
+
user: user2
|
|
208
|
+
},
|
|
209
|
+
path: `/api/v1/invitations/accept`
|
|
210
|
+
});
|
|
211
|
+
return response2;
|
|
212
|
+
}
|
|
213
|
+
const user = userOrTarget;
|
|
214
|
+
if (!user.email && !user.phone) {
|
|
215
|
+
throw new Error("User must have either email or phone");
|
|
216
|
+
}
|
|
169
217
|
const response = await this.vortexApiRequest({
|
|
170
218
|
method: "POST",
|
|
171
219
|
body: {
|
|
172
220
|
invitationIds,
|
|
173
|
-
|
|
221
|
+
user
|
|
174
222
|
},
|
|
175
223
|
path: `/api/v1/invitations/accept`
|
|
176
224
|
});
|
|
@@ -247,6 +295,54 @@ var Vortex = class {
|
|
|
247
295
|
body: params
|
|
248
296
|
});
|
|
249
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Create an invitation from your backend
|
|
300
|
+
*
|
|
301
|
+
* This method allows you to create invitations programmatically using your API key,
|
|
302
|
+
* without requiring a user JWT token. This is useful for server-side invitation
|
|
303
|
+
* creation, such as "People You May Know" flows or admin-initiated invitations.
|
|
304
|
+
*
|
|
305
|
+
* @param params - Invitation parameters
|
|
306
|
+
* @param params.widgetConfigurationId - The widget configuration ID to use
|
|
307
|
+
* @param params.target - The target of the invitation (who is being invited)
|
|
308
|
+
* @param params.target.type - 'email', 'phone', or 'internal'
|
|
309
|
+
* @param params.target.value - Email address, phone number, or internal user ID
|
|
310
|
+
* @param params.inviter - Information about the user creating the invitation
|
|
311
|
+
* @param params.inviter.userId - Your internal user ID for the inviter
|
|
312
|
+
* @param params.inviter.userEmail - Optional email of the inviter
|
|
313
|
+
* @param params.inviter.name - Optional display name of the inviter
|
|
314
|
+
* @param params.groups - Optional groups/scopes to associate with the invitation
|
|
315
|
+
* @param params.source - Optional source for analytics (defaults to 'api')
|
|
316
|
+
* @param params.templateVariables - Optional template variables for email customization
|
|
317
|
+
* @param params.metadata - Optional metadata passed through to webhooks
|
|
318
|
+
* @returns Created invitation with ID, short link, status, and creation timestamp
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* // Create an email invitation
|
|
323
|
+
* const invitation = await vortex.createInvitation({
|
|
324
|
+
* widgetConfigurationId: 'widget-config-123',
|
|
325
|
+
* target: { type: 'email', value: 'invitee@example.com' },
|
|
326
|
+
* inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
|
|
327
|
+
* groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
|
|
328
|
+
* });
|
|
329
|
+
*
|
|
330
|
+
* // Create an internal invitation (PYMK flow - no email sent)
|
|
331
|
+
* const pymkInvitation = await vortex.createInvitation({
|
|
332
|
+
* widgetConfigurationId: 'widget-config-123',
|
|
333
|
+
* target: { type: 'internal', value: 'internal-user-id-abc' },
|
|
334
|
+
* inviter: { userId: 'user-456' },
|
|
335
|
+
* source: 'pymk',
|
|
336
|
+
* });
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
async createInvitation(params) {
|
|
340
|
+
return this.vortexApiRequest({
|
|
341
|
+
method: "POST",
|
|
342
|
+
path: "/api/v1/invitations",
|
|
343
|
+
body: params
|
|
344
|
+
});
|
|
345
|
+
}
|
|
250
346
|
};
|
|
251
347
|
// Annotate the CommonJS export names for ESM import in node:
|
|
252
348
|
0 && (module.exports = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/vortex.ts"],"sourcesContent":["export * from './vortex';\nexport * from './types';","import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐ Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐งฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐งฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐งพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n async acceptInvitations(\n invitationIds: string[],\n target: { type: 'email' | 'username' | 'phoneNumber'; value: string }\n ): Promise<InvitationResult> {\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n target,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n}\n"],"mappings":"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","names":["uuidStringify","crypto"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/vortex.ts"],"sourcesContent":["export * from './vortex';\nexport * from './types';","import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\n CreateInvitationRequest,\n CreateInvitationResponse,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐ Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐งฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add name if present\n if (user.name) {\n payload.name = user.name;\n }\n\n // Add avatarUrl if present\n if (user.avatarUrl) {\n payload.avatarUrl = user.avatarUrl;\n }\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐งฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐งพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n /**\n * Accept invitations using the new User format (preferred)\n * @param invitationIds - Array of invitation IDs to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;\n\n /**\n * Accept invitations using legacy target format (deprecated)\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param target - Legacy target object with type and value\n * @returns Invitation result\n */\n async acceptInvitations(\n invitationIds: string[],\n target: InvitationTarget\n ): Promise<InvitationResult>;\n\n /**\n * Accept invitations using multiple legacy targets (deprecated)\n * Will call the accept endpoint once per target\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param targets - Array of legacy target objects\n * @returns Invitation result from the last acceptance\n */\n async acceptInvitations(\n invitationIds: string[],\n targets: InvitationTarget[]\n ): Promise<InvitationResult>;\n\n // Implementation\n async acceptInvitations(\n invitationIds: string[],\n userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]\n ): Promise<InvitationResult> {\n // Handle array of targets (legacy, call once per target)\n if (Array.isArray(userOrTarget)) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n let lastResult: InvitationResult | undefined;\n for (const target of userOrTarget) {\n lastResult = await this.acceptInvitations(invitationIds, target);\n }\n if (!lastResult) {\n throw new Error('No targets provided');\n }\n return lastResult;\n }\n\n // Check if it's a legacy target format (has 'type' and 'value' properties)\n const isLegacyTarget = 'type' in userOrTarget && 'value' in userOrTarget;\n\n if (isLegacyTarget) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n\n // Convert legacy target to User format\n const target = userOrTarget as InvitationTarget;\n const user: AcceptUser = {};\n\n if (target.type === 'email') {\n user.email = target.value;\n } else if (target.type === 'phone' || target.type === 'phoneNumber') {\n user.phone = target.value;\n } else {\n // For other types (like 'username'), try to use as email\n user.email = target.value;\n }\n\n // Make request with User format\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n // New User format\n const user = userOrTarget as AcceptUser;\n\n // Validate that either email or phone is provided\n if (!user.email && !user.phone) {\n throw new Error('User must have either email or phone');\n }\n\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Create an invitation from your backend\n *\n * This method allows you to create invitations programmatically using your API key,\n * without requiring a user JWT token. This is useful for server-side invitation\n * creation, such as \"People You May Know\" flows or admin-initiated invitations.\n *\n * @param params - Invitation parameters\n * @param params.widgetConfigurationId - The widget configuration ID to use\n * @param params.target - The target of the invitation (who is being invited)\n * @param params.target.type - 'email', 'phone', or 'internal'\n * @param params.target.value - Email address, phone number, or internal user ID\n * @param params.inviter - Information about the user creating the invitation\n * @param params.inviter.userId - Your internal user ID for the inviter\n * @param params.inviter.userEmail - Optional email of the inviter\n * @param params.inviter.name - Optional display name of the inviter\n * @param params.groups - Optional groups/scopes to associate with the invitation\n * @param params.source - Optional source for analytics (defaults to 'api')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation\n * const invitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'email', value: 'invitee@example.com' },\n * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },\n * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * const pymkInvitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'internal', value: 'internal-user-id-abc' },\n * inviter: { userId: 'user-456' },\n * source: 'pymk',\n * });\n * ```\n */\n async createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations',\n body: params as unknown as ApiRequestBody,\n }) as Promise<CreateInvitationResponse>;\n }\n}\n"],"mappings":"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","names":["uuidStringify","crypto","user","response"]}
|
package/dist/index.mjs
CHANGED
|
@@ -48,6 +48,12 @@ var Vortex = class {
|
|
|
48
48
|
// Include identifiers array for widget compatibility (VrtxAutojoin checks this)
|
|
49
49
|
identifiers: user.email ? [{ type: "email", value: user.email }] : []
|
|
50
50
|
};
|
|
51
|
+
if (user.name) {
|
|
52
|
+
payload.name = user.name;
|
|
53
|
+
}
|
|
54
|
+
if (user.avatarUrl) {
|
|
55
|
+
payload.avatarUrl = user.avatarUrl;
|
|
56
|
+
}
|
|
51
57
|
if (user.adminScopes) {
|
|
52
58
|
payload.adminScopes = user.adminScopes;
|
|
53
59
|
if (user.adminScopes.includes("autojoin")) {
|
|
@@ -129,12 +135,54 @@ var Vortex = class {
|
|
|
129
135
|
path: `/api/v1/invitations/${invitationId}`
|
|
130
136
|
});
|
|
131
137
|
}
|
|
132
|
-
|
|
138
|
+
// Implementation
|
|
139
|
+
async acceptInvitations(invitationIds, userOrTarget) {
|
|
140
|
+
if (Array.isArray(userOrTarget)) {
|
|
141
|
+
console.warn(
|
|
142
|
+
'[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: "user@example.com" })'
|
|
143
|
+
);
|
|
144
|
+
let lastResult;
|
|
145
|
+
for (const target of userOrTarget) {
|
|
146
|
+
lastResult = await this.acceptInvitations(invitationIds, target);
|
|
147
|
+
}
|
|
148
|
+
if (!lastResult) {
|
|
149
|
+
throw new Error("No targets provided");
|
|
150
|
+
}
|
|
151
|
+
return lastResult;
|
|
152
|
+
}
|
|
153
|
+
const isLegacyTarget = "type" in userOrTarget && "value" in userOrTarget;
|
|
154
|
+
if (isLegacyTarget) {
|
|
155
|
+
console.warn(
|
|
156
|
+
'[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: "user@example.com" })'
|
|
157
|
+
);
|
|
158
|
+
const target = userOrTarget;
|
|
159
|
+
const user2 = {};
|
|
160
|
+
if (target.type === "email") {
|
|
161
|
+
user2.email = target.value;
|
|
162
|
+
} else if (target.type === "phone" || target.type === "phoneNumber") {
|
|
163
|
+
user2.phone = target.value;
|
|
164
|
+
} else {
|
|
165
|
+
user2.email = target.value;
|
|
166
|
+
}
|
|
167
|
+
const response2 = await this.vortexApiRequest({
|
|
168
|
+
method: "POST",
|
|
169
|
+
body: {
|
|
170
|
+
invitationIds,
|
|
171
|
+
user: user2
|
|
172
|
+
},
|
|
173
|
+
path: `/api/v1/invitations/accept`
|
|
174
|
+
});
|
|
175
|
+
return response2;
|
|
176
|
+
}
|
|
177
|
+
const user = userOrTarget;
|
|
178
|
+
if (!user.email && !user.phone) {
|
|
179
|
+
throw new Error("User must have either email or phone");
|
|
180
|
+
}
|
|
133
181
|
const response = await this.vortexApiRequest({
|
|
134
182
|
method: "POST",
|
|
135
183
|
body: {
|
|
136
184
|
invitationIds,
|
|
137
|
-
|
|
185
|
+
user
|
|
138
186
|
},
|
|
139
187
|
path: `/api/v1/invitations/accept`
|
|
140
188
|
});
|
|
@@ -211,6 +259,54 @@ var Vortex = class {
|
|
|
211
259
|
body: params
|
|
212
260
|
});
|
|
213
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Create an invitation from your backend
|
|
264
|
+
*
|
|
265
|
+
* This method allows you to create invitations programmatically using your API key,
|
|
266
|
+
* without requiring a user JWT token. This is useful for server-side invitation
|
|
267
|
+
* creation, such as "People You May Know" flows or admin-initiated invitations.
|
|
268
|
+
*
|
|
269
|
+
* @param params - Invitation parameters
|
|
270
|
+
* @param params.widgetConfigurationId - The widget configuration ID to use
|
|
271
|
+
* @param params.target - The target of the invitation (who is being invited)
|
|
272
|
+
* @param params.target.type - 'email', 'phone', or 'internal'
|
|
273
|
+
* @param params.target.value - Email address, phone number, or internal user ID
|
|
274
|
+
* @param params.inviter - Information about the user creating the invitation
|
|
275
|
+
* @param params.inviter.userId - Your internal user ID for the inviter
|
|
276
|
+
* @param params.inviter.userEmail - Optional email of the inviter
|
|
277
|
+
* @param params.inviter.name - Optional display name of the inviter
|
|
278
|
+
* @param params.groups - Optional groups/scopes to associate with the invitation
|
|
279
|
+
* @param params.source - Optional source for analytics (defaults to 'api')
|
|
280
|
+
* @param params.templateVariables - Optional template variables for email customization
|
|
281
|
+
* @param params.metadata - Optional metadata passed through to webhooks
|
|
282
|
+
* @returns Created invitation with ID, short link, status, and creation timestamp
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* // Create an email invitation
|
|
287
|
+
* const invitation = await vortex.createInvitation({
|
|
288
|
+
* widgetConfigurationId: 'widget-config-123',
|
|
289
|
+
* target: { type: 'email', value: 'invitee@example.com' },
|
|
290
|
+
* inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
|
|
291
|
+
* groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
|
|
292
|
+
* });
|
|
293
|
+
*
|
|
294
|
+
* // Create an internal invitation (PYMK flow - no email sent)
|
|
295
|
+
* const pymkInvitation = await vortex.createInvitation({
|
|
296
|
+
* widgetConfigurationId: 'widget-config-123',
|
|
297
|
+
* target: { type: 'internal', value: 'internal-user-id-abc' },
|
|
298
|
+
* inviter: { userId: 'user-456' },
|
|
299
|
+
* source: 'pymk',
|
|
300
|
+
* });
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
async createInvitation(params) {
|
|
304
|
+
return this.vortexApiRequest({
|
|
305
|
+
method: "POST",
|
|
306
|
+
path: "/api/v1/invitations",
|
|
307
|
+
body: params
|
|
308
|
+
});
|
|
309
|
+
}
|
|
214
310
|
};
|
|
215
311
|
export {
|
|
216
312
|
Vortex
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vortex.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐ Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐งฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐งฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐งพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n async acceptInvitations(\n invitationIds: string[],\n target: { type: 'email' | 'username' | 'phoneNumber'; value: string }\n ): Promise<InvitationResult> {\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n target,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/vortex.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\n CreateInvitationRequest,\n CreateInvitationResponse,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐ Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐งฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add name if present\n if (user.name) {\n payload.name = user.name;\n }\n\n // Add avatarUrl if present\n if (user.avatarUrl) {\n payload.avatarUrl = user.avatarUrl;\n }\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐งฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐งพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n /**\n * Accept invitations using the new User format (preferred)\n * @param invitationIds - Array of invitation IDs to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;\n\n /**\n * Accept invitations using legacy target format (deprecated)\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param target - Legacy target object with type and value\n * @returns Invitation result\n */\n async acceptInvitations(\n invitationIds: string[],\n target: InvitationTarget\n ): Promise<InvitationResult>;\n\n /**\n * Accept invitations using multiple legacy targets (deprecated)\n * Will call the accept endpoint once per target\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param targets - Array of legacy target objects\n * @returns Invitation result from the last acceptance\n */\n async acceptInvitations(\n invitationIds: string[],\n targets: InvitationTarget[]\n ): Promise<InvitationResult>;\n\n // Implementation\n async acceptInvitations(\n invitationIds: string[],\n userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]\n ): Promise<InvitationResult> {\n // Handle array of targets (legacy, call once per target)\n if (Array.isArray(userOrTarget)) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n let lastResult: InvitationResult | undefined;\n for (const target of userOrTarget) {\n lastResult = await this.acceptInvitations(invitationIds, target);\n }\n if (!lastResult) {\n throw new Error('No targets provided');\n }\n return lastResult;\n }\n\n // Check if it's a legacy target format (has 'type' and 'value' properties)\n const isLegacyTarget = 'type' in userOrTarget && 'value' in userOrTarget;\n\n if (isLegacyTarget) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n\n // Convert legacy target to User format\n const target = userOrTarget as InvitationTarget;\n const user: AcceptUser = {};\n\n if (target.type === 'email') {\n user.email = target.value;\n } else if (target.type === 'phone' || target.type === 'phoneNumber') {\n user.phone = target.value;\n } else {\n // For other types (like 'username'), try to use as email\n user.email = target.value;\n }\n\n // Make request with User format\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n // New User format\n const user = userOrTarget as AcceptUser;\n\n // Validate that either email or phone is provided\n if (!user.email && !user.phone) {\n throw new Error('User must have either email or phone');\n }\n\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Create an invitation from your backend\n *\n * This method allows you to create invitations programmatically using your API key,\n * without requiring a user JWT token. This is useful for server-side invitation\n * creation, such as \"People You May Know\" flows or admin-initiated invitations.\n *\n * @param params - Invitation parameters\n * @param params.widgetConfigurationId - The widget configuration ID to use\n * @param params.target - The target of the invitation (who is being invited)\n * @param params.target.type - 'email', 'phone', or 'internal'\n * @param params.target.value - Email address, phone number, or internal user ID\n * @param params.inviter - Information about the user creating the invitation\n * @param params.inviter.userId - Your internal user ID for the inviter\n * @param params.inviter.userEmail - Optional email of the inviter\n * @param params.inviter.name - Optional display name of the inviter\n * @param params.groups - Optional groups/scopes to associate with the invitation\n * @param params.source - Optional source for analytics (defaults to 'api')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation\n * const invitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'email', value: 'invitee@example.com' },\n * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },\n * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * const pymkInvitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'internal', value: 'internal-user-id-abc' },\n * inviter: { userId: 'user-456' },\n * source: 'pymk',\n * });\n * ```\n */\n async createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations',\n body: params as unknown as ApiRequestBody,\n }) as Promise<CreateInvitationResponse>;\n }\n}\n"],"mappings":"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","names":["user","response"]}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@teamvortexsoftware/vortex-node-22-sdk",
|
|
3
3
|
"description": "Vortex Node 22 SDK",
|
|
4
4
|
"author": "@teamvortexsoftware",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.3.0",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"jest": "29.7.0",
|
|
37
37
|
"ts-jest": "29.4.6",
|
|
38
38
|
"tsup": "8.5.1",
|
|
39
|
-
"typescript-eslint": "8.50.
|
|
39
|
+
"typescript-eslint": "8.50.1"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"uuid": "11.1.0"
|