@teamvortexsoftware/vortex-node-22-sdk 0.0.8 โ 0.1.1
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 +60 -43
- package/dist/index.d.mts +70 -2
- package/dist/index.d.ts +70 -2
- package/dist/index.js +68 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +68 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -9
package/README.md
CHANGED
|
@@ -17,7 +17,8 @@ npm install --save @teamvortexsoftware/vortex-node-22-sdk
|
|
|
17
17
|
|
|
18
18
|
Once you have the SDK, [login](https://admin.vortexsoftware.com/signin) to Vortex and [create an API Key](https://admin.vortexsoftware.com/members/api-keys). Keep your API key safe! Vortex does not store the API key and it is not retrievable once it has been created. Also, it should be noted that the API key you use is scoped to the environment you're targeting. The environment is implied based on the API key used to sign JWTs and to make API requests.
|
|
19
19
|
|
|
20
|
-
Your API key is used to
|
|
20
|
+
Your API key is used to
|
|
21
|
+
|
|
21
22
|
- Sign JWTs for use with the Vortex Widget
|
|
22
23
|
- Make API calls against the [Vortex API](https://api.vortexsoftware.com/api)
|
|
23
24
|
|
|
@@ -42,18 +43,18 @@ app.get('/vortex-jwt', (req, res) => {
|
|
|
42
43
|
|
|
43
44
|
const token = vortex.generateJwt({
|
|
44
45
|
user: {
|
|
45
|
-
id:
|
|
46
|
-
email:
|
|
47
|
-
adminScopes: ['
|
|
48
|
-
}
|
|
46
|
+
id: 'user-123',
|
|
47
|
+
email: 'user@example.com',
|
|
48
|
+
adminScopes: ['autojoin'], // Optional: grants admin privileges for autojoining
|
|
49
|
+
},
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
res.end(JSON.stringify({ jwt: token }));
|
|
52
|
-
})
|
|
53
|
+
});
|
|
53
54
|
|
|
54
55
|
app.listen(port, () => {
|
|
55
|
-
console.log(`Example app listening on port ${port}. Fetch example JWT by hitting /vortex-jwt`)
|
|
56
|
-
})
|
|
56
|
+
console.log(`Example app listening on port ${port}. Fetch example JWT by hitting /vortex-jwt`);
|
|
57
|
+
});
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
You can also add extra properties to the JWT payload:
|
|
@@ -61,12 +62,12 @@ You can also add extra properties to the JWT payload:
|
|
|
61
62
|
```ts
|
|
62
63
|
const token = vortex.generateJwt({
|
|
63
64
|
user: {
|
|
64
|
-
id:
|
|
65
|
-
email:
|
|
66
|
-
adminScopes: ['
|
|
65
|
+
id: 'user-123',
|
|
66
|
+
email: 'user@example.com',
|
|
67
|
+
adminScopes: ['autojoin'],
|
|
67
68
|
},
|
|
68
|
-
role:
|
|
69
|
-
department:
|
|
69
|
+
role: 'admin',
|
|
70
|
+
department: 'Engineering',
|
|
70
71
|
});
|
|
71
72
|
```
|
|
72
73
|
|
|
@@ -86,14 +87,18 @@ const userId = 'users-id-in-my-system';
|
|
|
86
87
|
const identifiers = [
|
|
87
88
|
{ type: 'email', value: 'users@emailaddress.com' },
|
|
88
89
|
{ type: 'email', value: 'someother@address.com' },
|
|
89
|
-
{ type: 'sms', value: '18008675309' }
|
|
90
|
+
{ type: 'sms', value: '18008675309' },
|
|
90
91
|
];
|
|
91
92
|
|
|
92
93
|
// 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
|
|
93
94
|
const groups = [
|
|
94
|
-
{
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
{
|
|
96
|
+
type: 'workspace',
|
|
97
|
+
groupId: 'some-workspace-id',
|
|
98
|
+
name: 'The greatest workspace...pause...in the world',
|
|
99
|
+
},
|
|
100
|
+
{ type: 'document', groupId: 'some-document-id', name: "Ricky's grade 10 word papers" },
|
|
101
|
+
{ type: 'document', groupId: 'another-document-id', name: 'Sunnyvale bylaws' },
|
|
97
102
|
];
|
|
98
103
|
|
|
99
104
|
// If your product has the concept of user roles (admin, guest, member, etc), provide it here
|
|
@@ -104,17 +109,21 @@ const vortex = new Vortex(process.env.VORTEX_API_KEY);
|
|
|
104
109
|
|
|
105
110
|
app.get('/vortex-jwt', (req, res) => {
|
|
106
111
|
res.setHeader('Content-Type', 'application/json');
|
|
107
|
-
res.end(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
res.end(
|
|
113
|
+
JSON.stringify({
|
|
114
|
+
jwt: vortex.generateJwt({
|
|
115
|
+
userId,
|
|
116
|
+
identifiers,
|
|
117
|
+
groups,
|
|
118
|
+
role,
|
|
119
|
+
}),
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
});
|
|
114
123
|
|
|
115
124
|
app.listen(port, () => {
|
|
116
|
-
console.log(`Example app listening on port ${port}. Fetch example JWT by hitting /vortex-jwt`)
|
|
117
|
-
})
|
|
125
|
+
console.log(`Example app listening on port ${port}. Fetch example JWT by hitting /vortex-jwt`);
|
|
126
|
+
});
|
|
118
127
|
```
|
|
119
128
|
|
|
120
129
|
Now, you can utilize that JWT endpoint in conjuction with the Vortex widget
|
|
@@ -122,7 +131,7 @@ Now, you can utilize that JWT endpoint in conjuction with the Vortex widget
|
|
|
122
131
|
Here's an example hook:
|
|
123
132
|
|
|
124
133
|
```ts
|
|
125
|
-
import { useEffect, useState } from
|
|
134
|
+
import { useEffect, useState } from 'react';
|
|
126
135
|
|
|
127
136
|
export function useVortexJwt() {
|
|
128
137
|
const [data, setData] = useState<any>(null);
|
|
@@ -134,7 +143,7 @@ export function useVortexJwt() {
|
|
|
134
143
|
|
|
135
144
|
async function fetchJwt() {
|
|
136
145
|
try {
|
|
137
|
-
const res = await fetch(
|
|
146
|
+
const res = await fetch('/vortex-jwt');
|
|
138
147
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
139
148
|
const json = await res.json();
|
|
140
149
|
if (!cancelled) setData(json);
|
|
@@ -179,6 +188,7 @@ function InviteWrapperComponent() {
|
|
|
179
188
|
/>);
|
|
180
189
|
}
|
|
181
190
|
```
|
|
191
|
+
|
|
182
192
|
### Fetch an invitation by ID
|
|
183
193
|
|
|
184
194
|
When a shared invitation link or an invitaion link sent via email is clicked, the user who clicks it is redirected to the landing page you set in the widget configurator. For instance, if you set http://localhost:3000/invite/landing as the landing page and the invitation being clicked has an id of deadbeef-dead-4bad-8dad-c001d00dc0de, the user who clicks the invitation link will land on http://localhost:3000/invite/landing?invitationId=deadbeef-dead-4bad-8dad-c001d00dc0de
|
|
@@ -204,8 +214,9 @@ app.get('/invite/landing', async (req, res) => {
|
|
|
204
214
|
// For the sake of simplicity, we'll simply return the raw JSON
|
|
205
215
|
res.setHeader('Content-Type', 'application/json');
|
|
206
216
|
res.end(JSON.stringify(invitation));
|
|
207
|
-
})
|
|
217
|
+
});
|
|
208
218
|
```
|
|
219
|
+
|
|
209
220
|
### View invitations by target (email address for example)
|
|
210
221
|
|
|
211
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, sms or others in the future) a new user to your product.
|
|
@@ -229,8 +240,9 @@ app.get('/invitations/by-email', async (req, res) => {
|
|
|
229
240
|
// For the sake of simplicity, we'll simply return the raw JSON
|
|
230
241
|
res.setHeader('Content-Type', 'application/json');
|
|
231
242
|
res.end(JSON.stringify(invitations));
|
|
232
|
-
})
|
|
243
|
+
});
|
|
233
244
|
```
|
|
245
|
+
|
|
234
246
|
### Accept invitations
|
|
235
247
|
|
|
236
248
|
This is how you'd accept one or more invitations with the SDK. You want this as part of your signup flow more than likely. When someone clicks on an invitation link, we redirect to the landing page you specified in the widget configuration. Ultimately, the user will sign up with your service and that is when you create the relationship between the newly created user in your system and whatever grouping defined in the invitation itself.
|
|
@@ -246,12 +258,12 @@ app.post('/signup', async (req, res) => {
|
|
|
246
258
|
}
|
|
247
259
|
|
|
248
260
|
// YOUR signup logic, whatever it may be
|
|
249
|
-
await myApp.doSignupLogic(email);
|
|
261
|
+
await myApp.doSignupLogic(email);
|
|
250
262
|
|
|
251
263
|
// you may want to do this even if the user is signing up without clicking an invitaiton link
|
|
252
264
|
const invitations = await vortex.getInvitationsByTarget('email', email);
|
|
253
265
|
|
|
254
|
-
// Assume that your application may pass the original invitationId from the
|
|
266
|
+
// Assume that your application may pass the original invitationId from the
|
|
255
267
|
// landing page registered with the configured widget
|
|
256
268
|
const invitationId = req.body.invitationId;
|
|
257
269
|
|
|
@@ -260,16 +272,17 @@ app.post('/signup', async (req, res) => {
|
|
|
260
272
|
uniqueInvitationIds.push(invitationId);
|
|
261
273
|
}
|
|
262
274
|
|
|
263
|
-
const acceptedInvitations = await vortex.acceptInvitations(
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
);
|
|
267
|
-
|
|
275
|
+
const acceptedInvitations = await vortex.acceptInvitations(uniqueInvitationIds, {
|
|
276
|
+
type: 'email',
|
|
277
|
+
value: email,
|
|
278
|
+
});
|
|
279
|
+
|
|
268
280
|
// continue with post-signup activity. perhaps redirect to your logged in landing page
|
|
269
281
|
|
|
270
282
|
res.redirect(302, '/app');
|
|
271
|
-
})
|
|
283
|
+
});
|
|
272
284
|
```
|
|
285
|
+
|
|
273
286
|
### Fetch invitations by group
|
|
274
287
|
|
|
275
288
|
Perhaps you want to allow your users to see all outstanding invitations for a group that they are a member of. Or perhaps you want this exclusively for admins of the group. However you choose to do it, this SDK feature will allow you to fetch all outstanding invitations for a group.
|
|
@@ -286,8 +299,9 @@ app.get('/invitations/by-group', async (req, res) => {
|
|
|
286
299
|
|
|
287
300
|
res.setHeader('Content-Type', 'application/json');
|
|
288
301
|
res.end(JSON.stringify(invitations));
|
|
289
|
-
})
|
|
302
|
+
});
|
|
290
303
|
```
|
|
304
|
+
|
|
291
305
|
### Reinvite
|
|
292
306
|
|
|
293
307
|
You may want to allow your users to resend an existing invitation. Allowing for this will increase the conversion chances of a stale invitation. Perhaps you display a list of outstanding invites and allow for a reinvite based on that list.
|
|
@@ -304,8 +318,9 @@ app.post('/invitations/reinvite', async (req, res) => {
|
|
|
304
318
|
|
|
305
319
|
res.setHeader('Content-Type', 'application/json');
|
|
306
320
|
res.end(JSON.stringify(invitation));
|
|
307
|
-
})
|
|
321
|
+
});
|
|
308
322
|
```
|
|
323
|
+
|
|
309
324
|
### Revoke invitation
|
|
310
325
|
|
|
311
326
|
In addition to reinvite, you may want to present your users (or perhaps just admins) with the ability to revoke outstanding invitations.
|
|
@@ -322,13 +337,15 @@ app.post('/invitations/revoke', async (req, res) => {
|
|
|
322
337
|
|
|
323
338
|
res.setHeader('Content-Type', 'application/json');
|
|
324
339
|
res.end(JSON.stringify({}));
|
|
325
|
-
})
|
|
340
|
+
});
|
|
326
341
|
```
|
|
342
|
+
|
|
327
343
|
### Delete invitations by group
|
|
328
344
|
|
|
329
345
|
Your product may allow for your users to delete the underlying resource that is tied to one or more invitations. For instance, say your product has the concept of a 'workspace' and your invitations are created specifying a particular workspace associated with each invitation. Then, at some point in the future, the admin of the workspace decides to delete it. This means all invitations associated with that workspace are now invalid and need to be removed so that reminders don't go out for any outstanding invite to the now deleted workspace.
|
|
330
346
|
|
|
331
347
|
Here is how to clean them up when the workspace is deleted.
|
|
348
|
+
|
|
332
349
|
```ts
|
|
333
350
|
app.delete('/workspace/:workspaceId', async (req, res) => {
|
|
334
351
|
const { workspaceId } = req.params;
|
|
@@ -341,5 +358,5 @@ app.delete('/workspace/:workspaceId', async (req, res) => {
|
|
|
341
358
|
|
|
342
359
|
res.setHeader('Content-Type', 'application/json');
|
|
343
360
|
res.end(JSON.stringify({}));
|
|
344
|
-
})
|
|
345
|
-
```
|
|
361
|
+
});
|
|
362
|
+
```
|
package/dist/index.d.mts
CHANGED
|
@@ -51,6 +51,8 @@ type InvitationResult = {
|
|
|
51
51
|
projectId: string;
|
|
52
52
|
groups: InvitationGroup[];
|
|
53
53
|
accepts: InvitationAcceptance[];
|
|
54
|
+
expired: boolean;
|
|
55
|
+
expires?: string;
|
|
54
56
|
};
|
|
55
57
|
type AcceptInvitationRequest = {
|
|
56
58
|
invitationIds: string[];
|
|
@@ -69,6 +71,31 @@ type User = {
|
|
|
69
71
|
adminScopes?: string[];
|
|
70
72
|
[key: string]: any;
|
|
71
73
|
};
|
|
74
|
+
/**
|
|
75
|
+
* Autojoin domain configuration
|
|
76
|
+
*/
|
|
77
|
+
type AutojoinDomain = {
|
|
78
|
+
id: string;
|
|
79
|
+
domain: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Response from autojoin API endpoints
|
|
83
|
+
*/
|
|
84
|
+
type AutojoinDomainsResponse = {
|
|
85
|
+
autojoinDomains: AutojoinDomain[];
|
|
86
|
+
invitation: InvitationResult | null;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Request body for configuring autojoin domains
|
|
90
|
+
*/
|
|
91
|
+
type ConfigureAutojoinRequest = {
|
|
92
|
+
scope: string;
|
|
93
|
+
scopeType: string;
|
|
94
|
+
scopeName?: string;
|
|
95
|
+
domains: string[];
|
|
96
|
+
widgetId: string;
|
|
97
|
+
metadata?: Record<string, any>;
|
|
98
|
+
};
|
|
72
99
|
|
|
73
100
|
declare class Vortex {
|
|
74
101
|
private apiKey;
|
|
@@ -86,7 +113,7 @@ declare class Vortex {
|
|
|
86
113
|
* user: {
|
|
87
114
|
* id: "user-123",
|
|
88
115
|
* email: "user@example.com",
|
|
89
|
-
* adminScopes: ['
|
|
116
|
+
* adminScopes: ['autojoin']
|
|
90
117
|
* }
|
|
91
118
|
* });
|
|
92
119
|
* ```
|
|
@@ -111,6 +138,47 @@ declare class Vortex {
|
|
|
111
138
|
deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>;
|
|
112
139
|
getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>;
|
|
113
140
|
reinvite(invitationId: string): Promise<InvitationResult>;
|
|
141
|
+
/**
|
|
142
|
+
* Get autojoin domains configured for a specific scope
|
|
143
|
+
*
|
|
144
|
+
* @param scopeType - The type of scope (e.g., "organization", "team", "project")
|
|
145
|
+
* @param scope - The scope identifier (customer's group ID)
|
|
146
|
+
* @returns Autojoin domains and associated invitation
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* const result = await vortex.getAutojoinDomains('organization', 'acme-org');
|
|
151
|
+
* console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse>;
|
|
155
|
+
/**
|
|
156
|
+
* Configure autojoin domains for a specific scope
|
|
157
|
+
*
|
|
158
|
+
* This endpoint syncs autojoin domains - it will add new domains, remove domains
|
|
159
|
+
* not in the provided list, and deactivate the autojoin invitation if all domains
|
|
160
|
+
* are removed (empty array).
|
|
161
|
+
*
|
|
162
|
+
* @param params - Configuration parameters
|
|
163
|
+
* @param params.scope - The scope identifier (customer's group ID)
|
|
164
|
+
* @param params.scopeType - The type of scope (e.g., "organization", "team")
|
|
165
|
+
* @param params.scopeName - Optional display name for the scope
|
|
166
|
+
* @param params.domains - Array of domains to configure for autojoin
|
|
167
|
+
* @param params.widgetId - The widget configuration ID
|
|
168
|
+
* @returns Updated autojoin domains and associated invitation
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const result = await vortex.configureAutojoin({
|
|
173
|
+
* scope: 'acme-org',
|
|
174
|
+
* scopeType: 'organization',
|
|
175
|
+
* scopeName: 'Acme Corporation',
|
|
176
|
+
* domains: ['acme.com', 'acme.org'],
|
|
177
|
+
* widgetId: 'widget-123',
|
|
178
|
+
* });
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse>;
|
|
114
182
|
}
|
|
115
183
|
|
|
116
|
-
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type User, Vortex };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -51,6 +51,8 @@ type InvitationResult = {
|
|
|
51
51
|
projectId: string;
|
|
52
52
|
groups: InvitationGroup[];
|
|
53
53
|
accepts: InvitationAcceptance[];
|
|
54
|
+
expired: boolean;
|
|
55
|
+
expires?: string;
|
|
54
56
|
};
|
|
55
57
|
type AcceptInvitationRequest = {
|
|
56
58
|
invitationIds: string[];
|
|
@@ -69,6 +71,31 @@ type User = {
|
|
|
69
71
|
adminScopes?: string[];
|
|
70
72
|
[key: string]: any;
|
|
71
73
|
};
|
|
74
|
+
/**
|
|
75
|
+
* Autojoin domain configuration
|
|
76
|
+
*/
|
|
77
|
+
type AutojoinDomain = {
|
|
78
|
+
id: string;
|
|
79
|
+
domain: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Response from autojoin API endpoints
|
|
83
|
+
*/
|
|
84
|
+
type AutojoinDomainsResponse = {
|
|
85
|
+
autojoinDomains: AutojoinDomain[];
|
|
86
|
+
invitation: InvitationResult | null;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Request body for configuring autojoin domains
|
|
90
|
+
*/
|
|
91
|
+
type ConfigureAutojoinRequest = {
|
|
92
|
+
scope: string;
|
|
93
|
+
scopeType: string;
|
|
94
|
+
scopeName?: string;
|
|
95
|
+
domains: string[];
|
|
96
|
+
widgetId: string;
|
|
97
|
+
metadata?: Record<string, any>;
|
|
98
|
+
};
|
|
72
99
|
|
|
73
100
|
declare class Vortex {
|
|
74
101
|
private apiKey;
|
|
@@ -86,7 +113,7 @@ declare class Vortex {
|
|
|
86
113
|
* user: {
|
|
87
114
|
* id: "user-123",
|
|
88
115
|
* email: "user@example.com",
|
|
89
|
-
* adminScopes: ['
|
|
116
|
+
* adminScopes: ['autojoin']
|
|
90
117
|
* }
|
|
91
118
|
* });
|
|
92
119
|
* ```
|
|
@@ -111,6 +138,47 @@ declare class Vortex {
|
|
|
111
138
|
deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>;
|
|
112
139
|
getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>;
|
|
113
140
|
reinvite(invitationId: string): Promise<InvitationResult>;
|
|
141
|
+
/**
|
|
142
|
+
* Get autojoin domains configured for a specific scope
|
|
143
|
+
*
|
|
144
|
+
* @param scopeType - The type of scope (e.g., "organization", "team", "project")
|
|
145
|
+
* @param scope - The scope identifier (customer's group ID)
|
|
146
|
+
* @returns Autojoin domains and associated invitation
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* const result = await vortex.getAutojoinDomains('organization', 'acme-org');
|
|
151
|
+
* console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse>;
|
|
155
|
+
/**
|
|
156
|
+
* Configure autojoin domains for a specific scope
|
|
157
|
+
*
|
|
158
|
+
* This endpoint syncs autojoin domains - it will add new domains, remove domains
|
|
159
|
+
* not in the provided list, and deactivate the autojoin invitation if all domains
|
|
160
|
+
* are removed (empty array).
|
|
161
|
+
*
|
|
162
|
+
* @param params - Configuration parameters
|
|
163
|
+
* @param params.scope - The scope identifier (customer's group ID)
|
|
164
|
+
* @param params.scopeType - The type of scope (e.g., "organization", "team")
|
|
165
|
+
* @param params.scopeName - Optional display name for the scope
|
|
166
|
+
* @param params.domains - Array of domains to configure for autojoin
|
|
167
|
+
* @param params.widgetId - The widget configuration ID
|
|
168
|
+
* @returns Updated autojoin domains and associated invitation
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const result = await vortex.configureAutojoin({
|
|
173
|
+
* scope: 'acme-org',
|
|
174
|
+
* scopeType: 'organization',
|
|
175
|
+
* scopeName: 'Acme Corporation',
|
|
176
|
+
* domains: ['acme.com', 'acme.org'],
|
|
177
|
+
* widgetId: 'widget-123',
|
|
178
|
+
* });
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse>;
|
|
114
182
|
}
|
|
115
183
|
|
|
116
|
-
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type User, Vortex };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,7 @@ var Vortex = class {
|
|
|
54
54
|
* user: {
|
|
55
55
|
* id: "user-123",
|
|
56
56
|
* email: "user@example.com",
|
|
57
|
-
* adminScopes: ['
|
|
57
|
+
* adminScopes: ['autojoin']
|
|
58
58
|
* }
|
|
59
59
|
* });
|
|
60
60
|
* ```
|
|
@@ -80,10 +80,16 @@ var Vortex = class {
|
|
|
80
80
|
const payload = {
|
|
81
81
|
userId: user.id,
|
|
82
82
|
userEmail: user.email,
|
|
83
|
-
expires
|
|
83
|
+
expires,
|
|
84
|
+
// Include identifiers array for widget compatibility (VrtxAutojoin checks this)
|
|
85
|
+
identifiers: user.email ? [{ type: "email", value: user.email }] : []
|
|
84
86
|
};
|
|
85
87
|
if (user.adminScopes) {
|
|
86
88
|
payload.adminScopes = user.adminScopes;
|
|
89
|
+
if (user.adminScopes.includes("autojoin")) {
|
|
90
|
+
payload.userIsAutojoinAdmin = true;
|
|
91
|
+
payload.role = "admin";
|
|
92
|
+
}
|
|
87
93
|
}
|
|
88
94
|
if (rest && Object.keys(rest).length > 0) {
|
|
89
95
|
Object.assign(payload, rest);
|
|
@@ -99,7 +105,9 @@ var Vortex = class {
|
|
|
99
105
|
}
|
|
100
106
|
async vortexApiRequest(options) {
|
|
101
107
|
const { method, path, body, queryParams } = options;
|
|
102
|
-
const url = new URL(
|
|
108
|
+
const url = new URL(
|
|
109
|
+
`${process.env.VORTEX_API_BASE_URL || "https://api.vortexsoftware.com"}${path}`
|
|
110
|
+
);
|
|
103
111
|
if (queryParams) {
|
|
104
112
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
105
113
|
url.searchParams.append(key, String(value));
|
|
@@ -115,11 +123,13 @@ var Vortex = class {
|
|
|
115
123
|
});
|
|
116
124
|
if (!results.ok) {
|
|
117
125
|
const errorBody = await results.text();
|
|
118
|
-
throw new Error(
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`
|
|
128
|
+
);
|
|
119
129
|
}
|
|
120
130
|
const contentLength = results.headers.get("content-length");
|
|
121
131
|
const contentType = results.headers.get("content-type");
|
|
122
|
-
if (contentLength === "0" || !
|
|
132
|
+
if (contentLength === "0" || !contentType?.includes("application/json") && !contentLength) {
|
|
123
133
|
return {};
|
|
124
134
|
}
|
|
125
135
|
const responseText = await results.text();
|
|
@@ -135,7 +145,7 @@ var Vortex = class {
|
|
|
135
145
|
async getInvitationsByTarget(targetType, targetValue) {
|
|
136
146
|
const response = await this.vortexApiRequest({
|
|
137
147
|
method: "GET",
|
|
138
|
-
path: "/api/v1/invitations
|
|
148
|
+
path: "/api/v1/invitations",
|
|
139
149
|
queryParams: {
|
|
140
150
|
targetType,
|
|
141
151
|
targetValue
|
|
@@ -185,6 +195,58 @@ var Vortex = class {
|
|
|
185
195
|
path: `/api/v1/invitations/${invitationId}/reinvite`
|
|
186
196
|
});
|
|
187
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Get autojoin domains configured for a specific scope
|
|
200
|
+
*
|
|
201
|
+
* @param scopeType - The type of scope (e.g., "organization", "team", "project")
|
|
202
|
+
* @param scope - The scope identifier (customer's group ID)
|
|
203
|
+
* @returns Autojoin domains and associated invitation
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* const result = await vortex.getAutojoinDomains('organization', 'acme-org');
|
|
208
|
+
* console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
async getAutojoinDomains(scopeType, scope) {
|
|
212
|
+
return this.vortexApiRequest({
|
|
213
|
+
method: "GET",
|
|
214
|
+
path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Configure autojoin domains for a specific scope
|
|
219
|
+
*
|
|
220
|
+
* This endpoint syncs autojoin domains - it will add new domains, remove domains
|
|
221
|
+
* not in the provided list, and deactivate the autojoin invitation if all domains
|
|
222
|
+
* are removed (empty array).
|
|
223
|
+
*
|
|
224
|
+
* @param params - Configuration parameters
|
|
225
|
+
* @param params.scope - The scope identifier (customer's group ID)
|
|
226
|
+
* @param params.scopeType - The type of scope (e.g., "organization", "team")
|
|
227
|
+
* @param params.scopeName - Optional display name for the scope
|
|
228
|
+
* @param params.domains - Array of domains to configure for autojoin
|
|
229
|
+
* @param params.widgetId - The widget configuration ID
|
|
230
|
+
* @returns Updated autojoin domains and associated invitation
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* const result = await vortex.configureAutojoin({
|
|
235
|
+
* scope: 'acme-org',
|
|
236
|
+
* scopeType: 'organization',
|
|
237
|
+
* scopeName: 'Acme Corporation',
|
|
238
|
+
* domains: ['acme.com', 'acme.org'],
|
|
239
|
+
* widgetId: 'widget-123',
|
|
240
|
+
* });
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
async configureAutojoin(params) {
|
|
244
|
+
return this.vortexApiRequest({
|
|
245
|
+
method: "POST",
|
|
246
|
+
path: "/api/v1/invitations/autojoin",
|
|
247
|
+
body: params
|
|
248
|
+
});
|
|
249
|
+
}
|
|
188
250
|
};
|
|
189
251
|
// Annotate the CommonJS export names for ESM import in node:
|
|
190
252
|
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 { ApiRequestBody, ApiResponseJson, InvitationResult, AcceptInvitationRequest, User } 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 };\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\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(`${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`);\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(`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`);\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(targetType: 'email' | 'username' | 'phoneNumber', targetValue: string): Promise<InvitationResult[]> {\n const response = await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations?targetType',\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}"],"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 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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -18,7 +18,7 @@ var Vortex = class {
|
|
|
18
18
|
* user: {
|
|
19
19
|
* id: "user-123",
|
|
20
20
|
* email: "user@example.com",
|
|
21
|
-
* adminScopes: ['
|
|
21
|
+
* adminScopes: ['autojoin']
|
|
22
22
|
* }
|
|
23
23
|
* });
|
|
24
24
|
* ```
|
|
@@ -44,10 +44,16 @@ var Vortex = class {
|
|
|
44
44
|
const payload = {
|
|
45
45
|
userId: user.id,
|
|
46
46
|
userEmail: user.email,
|
|
47
|
-
expires
|
|
47
|
+
expires,
|
|
48
|
+
// Include identifiers array for widget compatibility (VrtxAutojoin checks this)
|
|
49
|
+
identifiers: user.email ? [{ type: "email", value: user.email }] : []
|
|
48
50
|
};
|
|
49
51
|
if (user.adminScopes) {
|
|
50
52
|
payload.adminScopes = user.adminScopes;
|
|
53
|
+
if (user.adminScopes.includes("autojoin")) {
|
|
54
|
+
payload.userIsAutojoinAdmin = true;
|
|
55
|
+
payload.role = "admin";
|
|
56
|
+
}
|
|
51
57
|
}
|
|
52
58
|
if (rest && Object.keys(rest).length > 0) {
|
|
53
59
|
Object.assign(payload, rest);
|
|
@@ -63,7 +69,9 @@ var Vortex = class {
|
|
|
63
69
|
}
|
|
64
70
|
async vortexApiRequest(options) {
|
|
65
71
|
const { method, path, body, queryParams } = options;
|
|
66
|
-
const url = new URL(
|
|
72
|
+
const url = new URL(
|
|
73
|
+
`${process.env.VORTEX_API_BASE_URL || "https://api.vortexsoftware.com"}${path}`
|
|
74
|
+
);
|
|
67
75
|
if (queryParams) {
|
|
68
76
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
69
77
|
url.searchParams.append(key, String(value));
|
|
@@ -79,11 +87,13 @@ var Vortex = class {
|
|
|
79
87
|
});
|
|
80
88
|
if (!results.ok) {
|
|
81
89
|
const errorBody = await results.text();
|
|
82
|
-
throw new Error(
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`
|
|
92
|
+
);
|
|
83
93
|
}
|
|
84
94
|
const contentLength = results.headers.get("content-length");
|
|
85
95
|
const contentType = results.headers.get("content-type");
|
|
86
|
-
if (contentLength === "0" || !
|
|
96
|
+
if (contentLength === "0" || !contentType?.includes("application/json") && !contentLength) {
|
|
87
97
|
return {};
|
|
88
98
|
}
|
|
89
99
|
const responseText = await results.text();
|
|
@@ -99,7 +109,7 @@ var Vortex = class {
|
|
|
99
109
|
async getInvitationsByTarget(targetType, targetValue) {
|
|
100
110
|
const response = await this.vortexApiRequest({
|
|
101
111
|
method: "GET",
|
|
102
|
-
path: "/api/v1/invitations
|
|
112
|
+
path: "/api/v1/invitations",
|
|
103
113
|
queryParams: {
|
|
104
114
|
targetType,
|
|
105
115
|
targetValue
|
|
@@ -149,6 +159,58 @@ var Vortex = class {
|
|
|
149
159
|
path: `/api/v1/invitations/${invitationId}/reinvite`
|
|
150
160
|
});
|
|
151
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Get autojoin domains configured for a specific scope
|
|
164
|
+
*
|
|
165
|
+
* @param scopeType - The type of scope (e.g., "organization", "team", "project")
|
|
166
|
+
* @param scope - The scope identifier (customer's group ID)
|
|
167
|
+
* @returns Autojoin domains and associated invitation
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* const result = await vortex.getAutojoinDomains('organization', 'acme-org');
|
|
172
|
+
* console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
async getAutojoinDomains(scopeType, scope) {
|
|
176
|
+
return this.vortexApiRequest({
|
|
177
|
+
method: "GET",
|
|
178
|
+
path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Configure autojoin domains for a specific scope
|
|
183
|
+
*
|
|
184
|
+
* This endpoint syncs autojoin domains - it will add new domains, remove domains
|
|
185
|
+
* not in the provided list, and deactivate the autojoin invitation if all domains
|
|
186
|
+
* are removed (empty array).
|
|
187
|
+
*
|
|
188
|
+
* @param params - Configuration parameters
|
|
189
|
+
* @param params.scope - The scope identifier (customer's group ID)
|
|
190
|
+
* @param params.scopeType - The type of scope (e.g., "organization", "team")
|
|
191
|
+
* @param params.scopeName - Optional display name for the scope
|
|
192
|
+
* @param params.domains - Array of domains to configure for autojoin
|
|
193
|
+
* @param params.widgetId - The widget configuration ID
|
|
194
|
+
* @returns Updated autojoin domains and associated invitation
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const result = await vortex.configureAutojoin({
|
|
199
|
+
* scope: 'acme-org',
|
|
200
|
+
* scopeType: 'organization',
|
|
201
|
+
* scopeName: 'Acme Corporation',
|
|
202
|
+
* domains: ['acme.com', 'acme.org'],
|
|
203
|
+
* widgetId: 'widget-123',
|
|
204
|
+
* });
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
async configureAutojoin(params) {
|
|
208
|
+
return this.vortexApiRequest({
|
|
209
|
+
method: "POST",
|
|
210
|
+
path: "/api/v1/invitations/autojoin",
|
|
211
|
+
body: params
|
|
212
|
+
});
|
|
213
|
+
}
|
|
152
214
|
};
|
|
153
215
|
export {
|
|
154
216
|
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 { ApiRequestBody, ApiResponseJson, InvitationResult, AcceptInvitationRequest, User } 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 };\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\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(`${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`);\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(`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`);\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(targetType: 'email' | 'username' | 'phoneNumber', targetValue: string): Promise<InvitationResult[]> {\n const response = await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations?targetType',\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}"],"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 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":[]}
|
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.1.1",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build:stg": "pnpm run distclean && pnpm run build && mkdir -p dist.d/stg && mv dist dist.d/stg && echo && echo 'Build in ./dist.d/stg'",
|
|
21
21
|
"prepublish:stg": "cp ./LICENSE ./README.md ./dist.d/stg/ && jq '.name = \"@teamvortexsoftware/vortex-node-22-sdk-stg\" | .description = \"Vortex Node 22 SDK (STG)\" | del(.scripts.prepack)' ./package.json > ./dist.d/stg/package.json",
|
|
22
|
-
"publish:stg": "pnpm run prepublish:stg &&
|
|
22
|
+
"publish:stg": "pnpm run prepublish:stg && pnpm publish --access restricted ./dist.d/stg",
|
|
23
23
|
"build:prod": "pnpm run distclean && pnpm run build && mkdir -p dist.d/prod && mv dist dist.d/prod && echo && echo 'Build in ./dist.d/prod'",
|
|
24
24
|
"prepublish:prod": "cp ./LICENSE ./README.md ./dist.d/prod/ && jq 'del(.scripts.prepack)' ./package.json > ./dist.d/prod/package.json",
|
|
25
|
-
"publish:prod": "pnpm run prepublish:prod &&
|
|
25
|
+
"publish:prod": "pnpm run prepublish:prod && pnpm publish --access public ./dist.d/prod",
|
|
26
26
|
"check-types": "tsc --noEmit",
|
|
27
27
|
"distclean": "rm -rf ./dist ./dist.d",
|
|
28
28
|
"clean": "rm -rf ./dist",
|
|
29
|
-
"dev": "",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
30
|
"test": "jest",
|
|
31
31
|
"package": "pnpm run build && cd dist/vortex-node-22-sdk && npm pack",
|
|
32
32
|
"build": "tsup"
|
|
@@ -44,16 +44,18 @@
|
|
|
44
44
|
"testEnvironment": "node"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@eslint/js": "
|
|
47
|
+
"@eslint/js": "catalog:",
|
|
48
48
|
"@jest/globals": "29.7.0",
|
|
49
49
|
"@teamvortexsoftware/eslint-config": "workspace:*",
|
|
50
50
|
"@teamvortexsoftware/typescript-config": "workspace:*",
|
|
51
|
-
"
|
|
51
|
+
"@types/node": "catalog:",
|
|
52
|
+
"eslint": "catalog:",
|
|
52
53
|
"jest": "29.7.0",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
54
|
+
"ts-jest": "catalog:",
|
|
55
|
+
"tsup": "catalog:",
|
|
56
|
+
"typescript-eslint": "catalog:"
|
|
55
57
|
},
|
|
56
58
|
"dependencies": {
|
|
57
|
-
"uuid": "
|
|
59
|
+
"uuid": "catalog:"
|
|
58
60
|
}
|
|
59
61
|
}
|