@teamvortexsoftware/vortex-node-22-sdk 0.3.0 โ 0.6.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 +4 -4
- package/dist/index.d.mts +55 -10
- package/dist/index.d.ts +55 -10
- package/dist/index.js +19 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +19 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -60,8 +60,8 @@ app.get('/vortex-jwt', (req, res) => {
|
|
|
60
60
|
user: {
|
|
61
61
|
id: 'user-123',
|
|
62
62
|
email: 'user@example.com',
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
userName: 'Jane Doe', // Optional: user's display name
|
|
64
|
+
userAvatarUrl: 'https://example.com/avatars/jane.jpg', // Optional: user's avatar URL
|
|
65
65
|
adminScopes: ['autojoin'], // Optional: grants admin privileges for autojoining
|
|
66
66
|
},
|
|
67
67
|
});
|
|
@@ -83,8 +83,8 @@ const token = vortex.generateJwt({
|
|
|
83
83
|
user: {
|
|
84
84
|
id: 'user-123',
|
|
85
85
|
email: 'user@example.com',
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
userName: 'Jane Doe', // Optional
|
|
87
|
+
userAvatarUrl: 'https://example.com/avatars/jane.jpg', // Optional
|
|
88
88
|
},
|
|
89
89
|
});
|
|
90
90
|
```
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
type InvitationTarget = {
|
|
2
|
-
type: 'email' | 'phone';
|
|
2
|
+
type: 'email' | 'phone' | 'share' | 'internal';
|
|
3
3
|
value: string;
|
|
4
|
+
/** The display name of the person being invited */
|
|
5
|
+
name?: string | null;
|
|
6
|
+
/** Avatar URL for the person being invited (for display in invitation lists) */
|
|
7
|
+
avatarUrl?: string | null;
|
|
4
8
|
};
|
|
5
9
|
/**
|
|
6
10
|
* GroupInput is used when creating JWTs - represents customer's group data
|
|
@@ -42,9 +46,9 @@ type InvitationResult = {
|
|
|
42
46
|
deliveryCount: number;
|
|
43
47
|
deliveryTypes: ('email' | 'phone' | 'share' | 'internal')[];
|
|
44
48
|
foreignCreatorId: string;
|
|
45
|
-
invitationType: 'single_use' | 'multi_use';
|
|
49
|
+
invitationType: 'single_use' | 'multi_use' | 'autojoin';
|
|
46
50
|
modifiedAt: string | null;
|
|
47
|
-
status: 'queued' | 'sending' | 'delivered' | 'accepted' | 'shared' | 'unfurled' | 'accepted_elsewhere';
|
|
51
|
+
status: 'queued' | 'sending' | 'sent' | 'delivered' | 'accepted' | 'shared' | 'unfurled' | 'accepted_elsewhere';
|
|
48
52
|
target: InvitationTarget[];
|
|
49
53
|
views: number;
|
|
50
54
|
widgetConfigurationId: string;
|
|
@@ -54,6 +58,8 @@ type InvitationResult = {
|
|
|
54
58
|
expired: boolean;
|
|
55
59
|
expires?: string;
|
|
56
60
|
source?: string;
|
|
61
|
+
/** Customer-defined subtype for categorizing this invitation (e.g., pymk, find-friends, profile-button) */
|
|
62
|
+
subtype?: string | null;
|
|
57
63
|
creatorName?: string | null;
|
|
58
64
|
creatorAvatarUrl?: string | null;
|
|
59
65
|
};
|
|
@@ -85,8 +91,8 @@ type ApiRequestBody = AcceptInvitationRequest | AcceptInvitationRequestLegacy |
|
|
|
85
91
|
type User = {
|
|
86
92
|
id: string;
|
|
87
93
|
email: string;
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
userName?: string;
|
|
95
|
+
userAvatarUrl?: string;
|
|
90
96
|
adminScopes?: string[];
|
|
91
97
|
[key: string]: any;
|
|
92
98
|
};
|
|
@@ -125,6 +131,10 @@ type CreateInvitationTargetType = 'email' | 'phone' | 'internal';
|
|
|
125
131
|
type CreateInvitationTarget = {
|
|
126
132
|
type: CreateInvitationTargetType;
|
|
127
133
|
value: string;
|
|
134
|
+
/** The display name of the person being invited */
|
|
135
|
+
name?: string;
|
|
136
|
+
/** Avatar URL for the person being invited (for display in invitation lists) */
|
|
137
|
+
avatarUrl?: string;
|
|
128
138
|
};
|
|
129
139
|
/**
|
|
130
140
|
* Information about the user creating the invitation (the inviter)
|
|
@@ -135,9 +145,9 @@ type Inviter = {
|
|
|
135
145
|
/** The email address of the person creating the invitation */
|
|
136
146
|
userEmail?: string;
|
|
137
147
|
/** The display name of the person creating the invitation */
|
|
138
|
-
|
|
148
|
+
userName?: string;
|
|
139
149
|
/** Avatar URL for the person creating the invitation */
|
|
140
|
-
|
|
150
|
+
userAvatarUrl?: string;
|
|
141
151
|
};
|
|
142
152
|
/**
|
|
143
153
|
* Group information for creating invitations
|
|
@@ -150,6 +160,22 @@ type CreateInvitationGroup = {
|
|
|
150
160
|
/** The display name of the group/scope */
|
|
151
161
|
name: string;
|
|
152
162
|
};
|
|
163
|
+
/**
|
|
164
|
+
* Configuration for link unfurl (Open Graph) metadata
|
|
165
|
+
* Controls how the invitation link appears when shared on social platforms or messaging apps
|
|
166
|
+
*/
|
|
167
|
+
type UnfurlConfig = {
|
|
168
|
+
/** The title shown in link previews (og:title) */
|
|
169
|
+
title?: string;
|
|
170
|
+
/** The description shown in link previews (og:description) */
|
|
171
|
+
description?: string;
|
|
172
|
+
/** The image URL shown in link previews (og:image) - must be HTTPS */
|
|
173
|
+
image?: string;
|
|
174
|
+
/** The Open Graph type (og:type) - e.g., 'website', 'article', 'product' */
|
|
175
|
+
type?: 'website' | 'article' | 'video' | 'music' | 'book' | 'profile' | 'product';
|
|
176
|
+
/** The site name shown in link previews (og:site_name) */
|
|
177
|
+
siteName?: string;
|
|
178
|
+
};
|
|
153
179
|
/**
|
|
154
180
|
* Request body for creating an invitation via the public API
|
|
155
181
|
*/
|
|
@@ -168,6 +194,14 @@ type CreateInvitationRequest = {
|
|
|
168
194
|
templateVariables?: Record<string, string>;
|
|
169
195
|
/** Custom metadata to attach to the invitation (passed through to webhooks) */
|
|
170
196
|
metadata?: Record<string, any>;
|
|
197
|
+
/**
|
|
198
|
+
* Customer-defined subtype for categorizing this invitation.
|
|
199
|
+
* Used for analytics segmentation, especially for internal invitations.
|
|
200
|
+
* Examples: "pymk", "find-friends", "profile-button", "post-signup"
|
|
201
|
+
*/
|
|
202
|
+
subtype?: string;
|
|
203
|
+
/** Link unfurl (Open Graph) configuration for social/messaging previews */
|
|
204
|
+
unfurlConfig?: UnfurlConfig;
|
|
171
205
|
};
|
|
172
206
|
/**
|
|
173
207
|
* Response from creating an invitation
|
|
@@ -307,30 +341,41 @@ declare class Vortex {
|
|
|
307
341
|
* @param params.inviter.name - Optional display name of the inviter
|
|
308
342
|
* @param params.groups - Optional groups/scopes to associate with the invitation
|
|
309
343
|
* @param params.source - Optional source for analytics (defaults to 'api')
|
|
344
|
+
* @param params.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')
|
|
310
345
|
* @param params.templateVariables - Optional template variables for email customization
|
|
311
346
|
* @param params.metadata - Optional metadata passed through to webhooks
|
|
347
|
+
* @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration
|
|
312
348
|
* @returns Created invitation with ID, short link, status, and creation timestamp
|
|
313
349
|
*
|
|
314
350
|
* @example
|
|
315
351
|
* ```typescript
|
|
316
|
-
* // Create an email invitation
|
|
352
|
+
* // Create an email invitation with custom link preview
|
|
317
353
|
* const invitation = await vortex.createInvitation({
|
|
318
354
|
* widgetConfigurationId: 'widget-config-123',
|
|
319
355
|
* target: { type: 'email', value: 'invitee@example.com' },
|
|
320
356
|
* inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
|
|
321
357
|
* groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
|
|
358
|
+
* unfurlConfig: {
|
|
359
|
+
* title: 'Join the Engineering team!',
|
|
360
|
+
* description: 'John Doe invited you to collaborate on Engineering',
|
|
361
|
+
* image: 'https://example.com/og-image.png',
|
|
362
|
+
* type: 'website',
|
|
363
|
+
* siteName: 'Acme App',
|
|
364
|
+
* },
|
|
322
365
|
* });
|
|
323
366
|
*
|
|
324
367
|
* // Create an internal invitation (PYMK flow - no email sent)
|
|
368
|
+
* // Use subtype for analytics segmentation
|
|
325
369
|
* const pymkInvitation = await vortex.createInvitation({
|
|
326
370
|
* widgetConfigurationId: 'widget-config-123',
|
|
327
371
|
* target: { type: 'internal', value: 'internal-user-id-abc' },
|
|
328
372
|
* inviter: { userId: 'user-456' },
|
|
329
|
-
* source: '
|
|
373
|
+
* source: 'internal',
|
|
374
|
+
* subtype: 'pymk', // Track this as a "People You May Know" invitation
|
|
330
375
|
* });
|
|
331
376
|
* ```
|
|
332
377
|
*/
|
|
333
378
|
createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse>;
|
|
334
379
|
}
|
|
335
380
|
|
|
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 };
|
|
381
|
+
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 UnfurlConfig, type User, Vortex };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
type InvitationTarget = {
|
|
2
|
-
type: 'email' | 'phone';
|
|
2
|
+
type: 'email' | 'phone' | 'share' | 'internal';
|
|
3
3
|
value: string;
|
|
4
|
+
/** The display name of the person being invited */
|
|
5
|
+
name?: string | null;
|
|
6
|
+
/** Avatar URL for the person being invited (for display in invitation lists) */
|
|
7
|
+
avatarUrl?: string | null;
|
|
4
8
|
};
|
|
5
9
|
/**
|
|
6
10
|
* GroupInput is used when creating JWTs - represents customer's group data
|
|
@@ -42,9 +46,9 @@ type InvitationResult = {
|
|
|
42
46
|
deliveryCount: number;
|
|
43
47
|
deliveryTypes: ('email' | 'phone' | 'share' | 'internal')[];
|
|
44
48
|
foreignCreatorId: string;
|
|
45
|
-
invitationType: 'single_use' | 'multi_use';
|
|
49
|
+
invitationType: 'single_use' | 'multi_use' | 'autojoin';
|
|
46
50
|
modifiedAt: string | null;
|
|
47
|
-
status: 'queued' | 'sending' | 'delivered' | 'accepted' | 'shared' | 'unfurled' | 'accepted_elsewhere';
|
|
51
|
+
status: 'queued' | 'sending' | 'sent' | 'delivered' | 'accepted' | 'shared' | 'unfurled' | 'accepted_elsewhere';
|
|
48
52
|
target: InvitationTarget[];
|
|
49
53
|
views: number;
|
|
50
54
|
widgetConfigurationId: string;
|
|
@@ -54,6 +58,8 @@ type InvitationResult = {
|
|
|
54
58
|
expired: boolean;
|
|
55
59
|
expires?: string;
|
|
56
60
|
source?: string;
|
|
61
|
+
/** Customer-defined subtype for categorizing this invitation (e.g., pymk, find-friends, profile-button) */
|
|
62
|
+
subtype?: string | null;
|
|
57
63
|
creatorName?: string | null;
|
|
58
64
|
creatorAvatarUrl?: string | null;
|
|
59
65
|
};
|
|
@@ -85,8 +91,8 @@ type ApiRequestBody = AcceptInvitationRequest | AcceptInvitationRequestLegacy |
|
|
|
85
91
|
type User = {
|
|
86
92
|
id: string;
|
|
87
93
|
email: string;
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
userName?: string;
|
|
95
|
+
userAvatarUrl?: string;
|
|
90
96
|
adminScopes?: string[];
|
|
91
97
|
[key: string]: any;
|
|
92
98
|
};
|
|
@@ -125,6 +131,10 @@ type CreateInvitationTargetType = 'email' | 'phone' | 'internal';
|
|
|
125
131
|
type CreateInvitationTarget = {
|
|
126
132
|
type: CreateInvitationTargetType;
|
|
127
133
|
value: string;
|
|
134
|
+
/** The display name of the person being invited */
|
|
135
|
+
name?: string;
|
|
136
|
+
/** Avatar URL for the person being invited (for display in invitation lists) */
|
|
137
|
+
avatarUrl?: string;
|
|
128
138
|
};
|
|
129
139
|
/**
|
|
130
140
|
* Information about the user creating the invitation (the inviter)
|
|
@@ -135,9 +145,9 @@ type Inviter = {
|
|
|
135
145
|
/** The email address of the person creating the invitation */
|
|
136
146
|
userEmail?: string;
|
|
137
147
|
/** The display name of the person creating the invitation */
|
|
138
|
-
|
|
148
|
+
userName?: string;
|
|
139
149
|
/** Avatar URL for the person creating the invitation */
|
|
140
|
-
|
|
150
|
+
userAvatarUrl?: string;
|
|
141
151
|
};
|
|
142
152
|
/**
|
|
143
153
|
* Group information for creating invitations
|
|
@@ -150,6 +160,22 @@ type CreateInvitationGroup = {
|
|
|
150
160
|
/** The display name of the group/scope */
|
|
151
161
|
name: string;
|
|
152
162
|
};
|
|
163
|
+
/**
|
|
164
|
+
* Configuration for link unfurl (Open Graph) metadata
|
|
165
|
+
* Controls how the invitation link appears when shared on social platforms or messaging apps
|
|
166
|
+
*/
|
|
167
|
+
type UnfurlConfig = {
|
|
168
|
+
/** The title shown in link previews (og:title) */
|
|
169
|
+
title?: string;
|
|
170
|
+
/** The description shown in link previews (og:description) */
|
|
171
|
+
description?: string;
|
|
172
|
+
/** The image URL shown in link previews (og:image) - must be HTTPS */
|
|
173
|
+
image?: string;
|
|
174
|
+
/** The Open Graph type (og:type) - e.g., 'website', 'article', 'product' */
|
|
175
|
+
type?: 'website' | 'article' | 'video' | 'music' | 'book' | 'profile' | 'product';
|
|
176
|
+
/** The site name shown in link previews (og:site_name) */
|
|
177
|
+
siteName?: string;
|
|
178
|
+
};
|
|
153
179
|
/**
|
|
154
180
|
* Request body for creating an invitation via the public API
|
|
155
181
|
*/
|
|
@@ -168,6 +194,14 @@ type CreateInvitationRequest = {
|
|
|
168
194
|
templateVariables?: Record<string, string>;
|
|
169
195
|
/** Custom metadata to attach to the invitation (passed through to webhooks) */
|
|
170
196
|
metadata?: Record<string, any>;
|
|
197
|
+
/**
|
|
198
|
+
* Customer-defined subtype for categorizing this invitation.
|
|
199
|
+
* Used for analytics segmentation, especially for internal invitations.
|
|
200
|
+
* Examples: "pymk", "find-friends", "profile-button", "post-signup"
|
|
201
|
+
*/
|
|
202
|
+
subtype?: string;
|
|
203
|
+
/** Link unfurl (Open Graph) configuration for social/messaging previews */
|
|
204
|
+
unfurlConfig?: UnfurlConfig;
|
|
171
205
|
};
|
|
172
206
|
/**
|
|
173
207
|
* Response from creating an invitation
|
|
@@ -307,30 +341,41 @@ declare class Vortex {
|
|
|
307
341
|
* @param params.inviter.name - Optional display name of the inviter
|
|
308
342
|
* @param params.groups - Optional groups/scopes to associate with the invitation
|
|
309
343
|
* @param params.source - Optional source for analytics (defaults to 'api')
|
|
344
|
+
* @param params.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')
|
|
310
345
|
* @param params.templateVariables - Optional template variables for email customization
|
|
311
346
|
* @param params.metadata - Optional metadata passed through to webhooks
|
|
347
|
+
* @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration
|
|
312
348
|
* @returns Created invitation with ID, short link, status, and creation timestamp
|
|
313
349
|
*
|
|
314
350
|
* @example
|
|
315
351
|
* ```typescript
|
|
316
|
-
* // Create an email invitation
|
|
352
|
+
* // Create an email invitation with custom link preview
|
|
317
353
|
* const invitation = await vortex.createInvitation({
|
|
318
354
|
* widgetConfigurationId: 'widget-config-123',
|
|
319
355
|
* target: { type: 'email', value: 'invitee@example.com' },
|
|
320
356
|
* inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
|
|
321
357
|
* groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
|
|
358
|
+
* unfurlConfig: {
|
|
359
|
+
* title: 'Join the Engineering team!',
|
|
360
|
+
* description: 'John Doe invited you to collaborate on Engineering',
|
|
361
|
+
* image: 'https://example.com/og-image.png',
|
|
362
|
+
* type: 'website',
|
|
363
|
+
* siteName: 'Acme App',
|
|
364
|
+
* },
|
|
322
365
|
* });
|
|
323
366
|
*
|
|
324
367
|
* // Create an internal invitation (PYMK flow - no email sent)
|
|
368
|
+
* // Use subtype for analytics segmentation
|
|
325
369
|
* const pymkInvitation = await vortex.createInvitation({
|
|
326
370
|
* widgetConfigurationId: 'widget-config-123',
|
|
327
371
|
* target: { type: 'internal', value: 'internal-user-id-abc' },
|
|
328
372
|
* inviter: { userId: 'user-456' },
|
|
329
|
-
* source: '
|
|
373
|
+
* source: 'internal',
|
|
374
|
+
* subtype: 'pymk', // Track this as a "People You May Know" invitation
|
|
330
375
|
* });
|
|
331
376
|
* ```
|
|
332
377
|
*/
|
|
333
378
|
createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse>;
|
|
334
379
|
}
|
|
335
380
|
|
|
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 };
|
|
381
|
+
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 UnfurlConfig, type User, Vortex };
|
package/dist/index.js
CHANGED
|
@@ -84,11 +84,11 @@ 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.
|
|
88
|
-
payload.
|
|
87
|
+
if (user.userName) {
|
|
88
|
+
payload.userName = user.userName;
|
|
89
89
|
}
|
|
90
|
-
if (user.
|
|
91
|
-
payload.
|
|
90
|
+
if (user.userAvatarUrl) {
|
|
91
|
+
payload.userAvatarUrl = user.userAvatarUrl;
|
|
92
92
|
}
|
|
93
93
|
if (user.adminScopes) {
|
|
94
94
|
payload.adminScopes = user.adminScopes;
|
|
@@ -195,10 +195,10 @@ var Vortex = class {
|
|
|
195
195
|
const user2 = {};
|
|
196
196
|
if (target.type === "email") {
|
|
197
197
|
user2.email = target.value;
|
|
198
|
-
} else if (target.type === "phone"
|
|
198
|
+
} else if (target.type === "phone") {
|
|
199
199
|
user2.phone = target.value;
|
|
200
200
|
} else {
|
|
201
|
-
|
|
201
|
+
throw new Error(`Unsupported target type for accept: ${target.type}`);
|
|
202
202
|
}
|
|
203
203
|
const response2 = await this.vortexApiRequest({
|
|
204
204
|
method: "POST",
|
|
@@ -313,26 +313,37 @@ var Vortex = class {
|
|
|
313
313
|
* @param params.inviter.name - Optional display name of the inviter
|
|
314
314
|
* @param params.groups - Optional groups/scopes to associate with the invitation
|
|
315
315
|
* @param params.source - Optional source for analytics (defaults to 'api')
|
|
316
|
+
* @param params.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')
|
|
316
317
|
* @param params.templateVariables - Optional template variables for email customization
|
|
317
318
|
* @param params.metadata - Optional metadata passed through to webhooks
|
|
319
|
+
* @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration
|
|
318
320
|
* @returns Created invitation with ID, short link, status, and creation timestamp
|
|
319
321
|
*
|
|
320
322
|
* @example
|
|
321
323
|
* ```typescript
|
|
322
|
-
* // Create an email invitation
|
|
324
|
+
* // Create an email invitation with custom link preview
|
|
323
325
|
* const invitation = await vortex.createInvitation({
|
|
324
326
|
* widgetConfigurationId: 'widget-config-123',
|
|
325
327
|
* target: { type: 'email', value: 'invitee@example.com' },
|
|
326
328
|
* inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
|
|
327
329
|
* groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
|
|
330
|
+
* unfurlConfig: {
|
|
331
|
+
* title: 'Join the Engineering team!',
|
|
332
|
+
* description: 'John Doe invited you to collaborate on Engineering',
|
|
333
|
+
* image: 'https://example.com/og-image.png',
|
|
334
|
+
* type: 'website',
|
|
335
|
+
* siteName: 'Acme App',
|
|
336
|
+
* },
|
|
328
337
|
* });
|
|
329
338
|
*
|
|
330
339
|
* // Create an internal invitation (PYMK flow - no email sent)
|
|
340
|
+
* // Use subtype for analytics segmentation
|
|
331
341
|
* const pymkInvitation = await vortex.createInvitation({
|
|
332
342
|
* widgetConfigurationId: 'widget-config-123',
|
|
333
343
|
* target: { type: 'internal', value: 'internal-user-id-abc' },
|
|
334
344
|
* inviter: { userId: 'user-456' },
|
|
335
|
-
* source: '
|
|
345
|
+
* source: 'internal',
|
|
346
|
+
* subtype: 'pymk', // Track this as a "People You May Know" invitation
|
|
336
347
|
* });
|
|
337
348
|
* ```
|
|
338
349
|
*/
|
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 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"]}
|
|
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 userName if present\n if (user.userName) {\n payload.userName = user.userName;\n }\n\n // Add userAvatarUrl if present\n if (user.userAvatarUrl) {\n payload.userAvatarUrl = user.userAvatarUrl;\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 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') {\n user.phone = target.value;\n } else {\n throw new Error(`Unsupported target type for accept: ${target.type}`);\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.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation with custom link preview\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 * unfurlConfig: {\n * title: 'Join the Engineering team!',\n * description: 'John Doe invited you to collaborate on Engineering',\n * image: 'https://example.com/og-image.png',\n * type: 'website',\n * siteName: 'Acme App',\n * },\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * // Use subtype for analytics segmentation\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: 'internal',\n * subtype: 'pymk', // Track this as a \"People You May Know\" invitation\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,11 +48,11 @@ 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.
|
|
52
|
-
payload.
|
|
51
|
+
if (user.userName) {
|
|
52
|
+
payload.userName = user.userName;
|
|
53
53
|
}
|
|
54
|
-
if (user.
|
|
55
|
-
payload.
|
|
54
|
+
if (user.userAvatarUrl) {
|
|
55
|
+
payload.userAvatarUrl = user.userAvatarUrl;
|
|
56
56
|
}
|
|
57
57
|
if (user.adminScopes) {
|
|
58
58
|
payload.adminScopes = user.adminScopes;
|
|
@@ -159,10 +159,10 @@ var Vortex = class {
|
|
|
159
159
|
const user2 = {};
|
|
160
160
|
if (target.type === "email") {
|
|
161
161
|
user2.email = target.value;
|
|
162
|
-
} else if (target.type === "phone"
|
|
162
|
+
} else if (target.type === "phone") {
|
|
163
163
|
user2.phone = target.value;
|
|
164
164
|
} else {
|
|
165
|
-
|
|
165
|
+
throw new Error(`Unsupported target type for accept: ${target.type}`);
|
|
166
166
|
}
|
|
167
167
|
const response2 = await this.vortexApiRequest({
|
|
168
168
|
method: "POST",
|
|
@@ -277,26 +277,37 @@ var Vortex = class {
|
|
|
277
277
|
* @param params.inviter.name - Optional display name of the inviter
|
|
278
278
|
* @param params.groups - Optional groups/scopes to associate with the invitation
|
|
279
279
|
* @param params.source - Optional source for analytics (defaults to 'api')
|
|
280
|
+
* @param params.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')
|
|
280
281
|
* @param params.templateVariables - Optional template variables for email customization
|
|
281
282
|
* @param params.metadata - Optional metadata passed through to webhooks
|
|
283
|
+
* @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration
|
|
282
284
|
* @returns Created invitation with ID, short link, status, and creation timestamp
|
|
283
285
|
*
|
|
284
286
|
* @example
|
|
285
287
|
* ```typescript
|
|
286
|
-
* // Create an email invitation
|
|
288
|
+
* // Create an email invitation with custom link preview
|
|
287
289
|
* const invitation = await vortex.createInvitation({
|
|
288
290
|
* widgetConfigurationId: 'widget-config-123',
|
|
289
291
|
* target: { type: 'email', value: 'invitee@example.com' },
|
|
290
292
|
* inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },
|
|
291
293
|
* groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],
|
|
294
|
+
* unfurlConfig: {
|
|
295
|
+
* title: 'Join the Engineering team!',
|
|
296
|
+
* description: 'John Doe invited you to collaborate on Engineering',
|
|
297
|
+
* image: 'https://example.com/og-image.png',
|
|
298
|
+
* type: 'website',
|
|
299
|
+
* siteName: 'Acme App',
|
|
300
|
+
* },
|
|
292
301
|
* });
|
|
293
302
|
*
|
|
294
303
|
* // Create an internal invitation (PYMK flow - no email sent)
|
|
304
|
+
* // Use subtype for analytics segmentation
|
|
295
305
|
* const pymkInvitation = await vortex.createInvitation({
|
|
296
306
|
* widgetConfigurationId: 'widget-config-123',
|
|
297
307
|
* target: { type: 'internal', value: 'internal-user-id-abc' },
|
|
298
308
|
* inviter: { userId: 'user-456' },
|
|
299
|
-
* source: '
|
|
309
|
+
* source: 'internal',
|
|
310
|
+
* subtype: 'pymk', // Track this as a "People You May Know" invitation
|
|
300
311
|
* });
|
|
301
312
|
* ```
|
|
302
313
|
*/
|
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 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"]}
|
|
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 userName if present\n if (user.userName) {\n payload.userName = user.userName;\n }\n\n // Add userAvatarUrl if present\n if (user.userAvatarUrl) {\n payload.userAvatarUrl = user.userAvatarUrl;\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 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') {\n user.phone = target.value;\n } else {\n throw new Error(`Unsupported target type for accept: ${target.type}`);\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.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation with custom link preview\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 * unfurlConfig: {\n * title: 'Join the Engineering team!',\n * description: 'John Doe invited you to collaborate on Engineering',\n * image: 'https://example.com/og-image.png',\n * type: 'website',\n * siteName: 'Acme App',\n * },\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * // Use subtype for analytics segmentation\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: 'internal',\n * subtype: 'pymk', // Track this as a \"People You May Know\" invitation\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