@teamvortexsoftware/vortex-node-22-sdk 0.0.7 โ 0.1.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 +95 -31
- package/dist/index.d.mts +99 -10
- package/dist/index.d.ts +99 -10
- package/dist/index.js +94 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -16
- 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
|
|
|
@@ -27,6 +28,53 @@ Let's assume you have an express powered API and a user that is looking to invit
|
|
|
27
28
|
|
|
28
29
|
You could populate the JWT in some set of initial data that your application already provides (recommended) or you could create an endpoint specifically to fetch the JWT on demand for use with the widget. For the purposes of this example, we'll create an endpoint to fetch the JWT.
|
|
29
30
|
|
|
31
|
+
#### Current Format (Recommended)
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const express = require('express');
|
|
35
|
+
const app = express();
|
|
36
|
+
const port = 3000;
|
|
37
|
+
|
|
38
|
+
// Provide your API key however you see fit.
|
|
39
|
+
const vortex = new Vortex(process.env.VORTEX_API_KEY);
|
|
40
|
+
|
|
41
|
+
app.get('/vortex-jwt', (req, res) => {
|
|
42
|
+
res.setHeader('Content-Type', 'application/json');
|
|
43
|
+
|
|
44
|
+
const token = vortex.generateJwt({
|
|
45
|
+
user: {
|
|
46
|
+
id: 'user-123',
|
|
47
|
+
email: 'user@example.com',
|
|
48
|
+
adminScopes: ['autojoin'], // Optional: grants admin privileges for autojoining
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
res.end(JSON.stringify({ jwt: token }));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
app.listen(port, () => {
|
|
56
|
+
console.log(`Example app listening on port ${port}. Fetch example JWT by hitting /vortex-jwt`);
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You can also add extra properties to the JWT payload:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const token = vortex.generateJwt({
|
|
64
|
+
user: {
|
|
65
|
+
id: 'user-123',
|
|
66
|
+
email: 'user@example.com',
|
|
67
|
+
adminScopes: ['autojoin'],
|
|
68
|
+
},
|
|
69
|
+
role: 'admin',
|
|
70
|
+
department: 'Engineering',
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Legacy Format (Deprecated)
|
|
75
|
+
|
|
76
|
+
The legacy format is still supported for backward compatibility but is deprecated. New integrations should use the simplified format above.
|
|
77
|
+
|
|
30
78
|
```ts
|
|
31
79
|
const express = require('express');
|
|
32
80
|
const app = express();
|
|
@@ -39,14 +87,18 @@ const userId = 'users-id-in-my-system';
|
|
|
39
87
|
const identifiers = [
|
|
40
88
|
{ type: 'email', value: 'users@emailaddress.com' },
|
|
41
89
|
{ type: 'email', value: 'someother@address.com' },
|
|
42
|
-
{ type: 'sms', value: '18008675309' }
|
|
90
|
+
{ type: 'sms', value: '18008675309' },
|
|
43
91
|
];
|
|
44
92
|
|
|
45
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
|
|
46
94
|
const groups = [
|
|
47
|
-
{
|
|
48
|
-
|
|
49
|
-
|
|
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' },
|
|
50
102
|
];
|
|
51
103
|
|
|
52
104
|
// If your product has the concept of user roles (admin, guest, member, etc), provide it here
|
|
@@ -57,17 +109,21 @@ const vortex = new Vortex(process.env.VORTEX_API_KEY);
|
|
|
57
109
|
|
|
58
110
|
app.get('/vortex-jwt', (req, res) => {
|
|
59
111
|
res.setHeader('Content-Type', 'application/json');
|
|
60
|
-
res.end(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
112
|
+
res.end(
|
|
113
|
+
JSON.stringify({
|
|
114
|
+
jwt: vortex.generateJwt({
|
|
115
|
+
userId,
|
|
116
|
+
identifiers,
|
|
117
|
+
groups,
|
|
118
|
+
role,
|
|
119
|
+
}),
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
});
|
|
67
123
|
|
|
68
124
|
app.listen(port, () => {
|
|
69
|
-
console.log(`Example app listening on port ${port}. Fetch example JWT by hitting /vortex-jwt`)
|
|
70
|
-
})
|
|
125
|
+
console.log(`Example app listening on port ${port}. Fetch example JWT by hitting /vortex-jwt`);
|
|
126
|
+
});
|
|
71
127
|
```
|
|
72
128
|
|
|
73
129
|
Now, you can utilize that JWT endpoint in conjuction with the Vortex widget
|
|
@@ -75,7 +131,7 @@ Now, you can utilize that JWT endpoint in conjuction with the Vortex widget
|
|
|
75
131
|
Here's an example hook:
|
|
76
132
|
|
|
77
133
|
```ts
|
|
78
|
-
import { useEffect, useState } from
|
|
134
|
+
import { useEffect, useState } from 'react';
|
|
79
135
|
|
|
80
136
|
export function useVortexJwt() {
|
|
81
137
|
const [data, setData] = useState<any>(null);
|
|
@@ -87,7 +143,7 @@ export function useVortexJwt() {
|
|
|
87
143
|
|
|
88
144
|
async function fetchJwt() {
|
|
89
145
|
try {
|
|
90
|
-
const res = await fetch(
|
|
146
|
+
const res = await fetch('/vortex-jwt');
|
|
91
147
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
92
148
|
const json = await res.json();
|
|
93
149
|
if (!cancelled) setData(json);
|
|
@@ -132,6 +188,7 @@ function InviteWrapperComponent() {
|
|
|
132
188
|
/>);
|
|
133
189
|
}
|
|
134
190
|
```
|
|
191
|
+
|
|
135
192
|
### Fetch an invitation by ID
|
|
136
193
|
|
|
137
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
|
|
@@ -157,8 +214,9 @@ app.get('/invite/landing', async (req, res) => {
|
|
|
157
214
|
// For the sake of simplicity, we'll simply return the raw JSON
|
|
158
215
|
res.setHeader('Content-Type', 'application/json');
|
|
159
216
|
res.end(JSON.stringify(invitation));
|
|
160
|
-
})
|
|
217
|
+
});
|
|
161
218
|
```
|
|
219
|
+
|
|
162
220
|
### View invitations by target (email address for example)
|
|
163
221
|
|
|
164
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.
|
|
@@ -182,8 +240,9 @@ app.get('/invitations/by-email', async (req, res) => {
|
|
|
182
240
|
// For the sake of simplicity, we'll simply return the raw JSON
|
|
183
241
|
res.setHeader('Content-Type', 'application/json');
|
|
184
242
|
res.end(JSON.stringify(invitations));
|
|
185
|
-
})
|
|
243
|
+
});
|
|
186
244
|
```
|
|
245
|
+
|
|
187
246
|
### Accept invitations
|
|
188
247
|
|
|
189
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.
|
|
@@ -199,12 +258,12 @@ app.post('/signup', async (req, res) => {
|
|
|
199
258
|
}
|
|
200
259
|
|
|
201
260
|
// YOUR signup logic, whatever it may be
|
|
202
|
-
await myApp.doSignupLogic(email);
|
|
261
|
+
await myApp.doSignupLogic(email);
|
|
203
262
|
|
|
204
263
|
// you may want to do this even if the user is signing up without clicking an invitaiton link
|
|
205
264
|
const invitations = await vortex.getInvitationsByTarget('email', email);
|
|
206
265
|
|
|
207
|
-
// Assume that your application may pass the original invitationId from the
|
|
266
|
+
// Assume that your application may pass the original invitationId from the
|
|
208
267
|
// landing page registered with the configured widget
|
|
209
268
|
const invitationId = req.body.invitationId;
|
|
210
269
|
|
|
@@ -213,16 +272,17 @@ app.post('/signup', async (req, res) => {
|
|
|
213
272
|
uniqueInvitationIds.push(invitationId);
|
|
214
273
|
}
|
|
215
274
|
|
|
216
|
-
const acceptedInvitations = await vortex.acceptInvitations(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
);
|
|
220
|
-
|
|
275
|
+
const acceptedInvitations = await vortex.acceptInvitations(uniqueInvitationIds, {
|
|
276
|
+
type: 'email',
|
|
277
|
+
value: email,
|
|
278
|
+
});
|
|
279
|
+
|
|
221
280
|
// continue with post-signup activity. perhaps redirect to your logged in landing page
|
|
222
281
|
|
|
223
282
|
res.redirect(302, '/app');
|
|
224
|
-
})
|
|
283
|
+
});
|
|
225
284
|
```
|
|
285
|
+
|
|
226
286
|
### Fetch invitations by group
|
|
227
287
|
|
|
228
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.
|
|
@@ -239,8 +299,9 @@ app.get('/invitations/by-group', async (req, res) => {
|
|
|
239
299
|
|
|
240
300
|
res.setHeader('Content-Type', 'application/json');
|
|
241
301
|
res.end(JSON.stringify(invitations));
|
|
242
|
-
})
|
|
302
|
+
});
|
|
243
303
|
```
|
|
304
|
+
|
|
244
305
|
### Reinvite
|
|
245
306
|
|
|
246
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.
|
|
@@ -257,8 +318,9 @@ app.post('/invitations/reinvite', async (req, res) => {
|
|
|
257
318
|
|
|
258
319
|
res.setHeader('Content-Type', 'application/json');
|
|
259
320
|
res.end(JSON.stringify(invitation));
|
|
260
|
-
})
|
|
321
|
+
});
|
|
261
322
|
```
|
|
323
|
+
|
|
262
324
|
### Revoke invitation
|
|
263
325
|
|
|
264
326
|
In addition to reinvite, you may want to present your users (or perhaps just admins) with the ability to revoke outstanding invitations.
|
|
@@ -275,13 +337,15 @@ app.post('/invitations/revoke', async (req, res) => {
|
|
|
275
337
|
|
|
276
338
|
res.setHeader('Content-Type', 'application/json');
|
|
277
339
|
res.end(JSON.stringify({}));
|
|
278
|
-
})
|
|
340
|
+
});
|
|
279
341
|
```
|
|
342
|
+
|
|
280
343
|
### Delete invitations by group
|
|
281
344
|
|
|
282
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.
|
|
283
346
|
|
|
284
347
|
Here is how to clean them up when the workspace is deleted.
|
|
348
|
+
|
|
285
349
|
```ts
|
|
286
350
|
app.delete('/workspace/:workspaceId', async (req, res) => {
|
|
287
351
|
const { workspaceId } = req.params;
|
|
@@ -294,5 +358,5 @@ app.delete('/workspace/:workspaceId', async (req, res) => {
|
|
|
294
358
|
|
|
295
359
|
res.setHeader('Content-Type', 'application/json');
|
|
296
360
|
res.end(JSON.stringify({}));
|
|
297
|
-
})
|
|
298
|
-
```
|
|
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[];
|
|
@@ -60,19 +62,65 @@ type ApiResponseJson = InvitationResult | {
|
|
|
60
62
|
invitations: InvitationResult[];
|
|
61
63
|
} | {};
|
|
62
64
|
type ApiRequestBody = AcceptInvitationRequest | null;
|
|
65
|
+
/**
|
|
66
|
+
* User type for JWT generation
|
|
67
|
+
*/
|
|
68
|
+
type User = {
|
|
69
|
+
id: string;
|
|
70
|
+
email: string;
|
|
71
|
+
adminScopes?: string[];
|
|
72
|
+
[key: string]: any;
|
|
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
|
+
};
|
|
63
99
|
|
|
64
100
|
declare class Vortex {
|
|
65
101
|
private apiKey;
|
|
66
102
|
constructor(apiKey: string);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Generate a JWT token for a user
|
|
105
|
+
*
|
|
106
|
+
* @param params - Object containing user and optional additional properties
|
|
107
|
+
* @param params.user - User object with id, email, and optional adminScopes
|
|
108
|
+
* @returns JWT token string
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const token = vortex.generateJwt({
|
|
113
|
+
* user: {
|
|
114
|
+
* id: "user-123",
|
|
115
|
+
* email: "user@example.com",
|
|
116
|
+
* adminScopes: ['autojoin']
|
|
117
|
+
* }
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
generateJwt(params: {
|
|
122
|
+
user: User;
|
|
123
|
+
[key: string]: any;
|
|
76
124
|
}): string;
|
|
77
125
|
vortexApiRequest(options: {
|
|
78
126
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
@@ -90,6 +138,47 @@ declare class Vortex {
|
|
|
90
138
|
deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>;
|
|
91
139
|
getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>;
|
|
92
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>;
|
|
93
182
|
}
|
|
94
183
|
|
|
95
|
-
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, 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[];
|
|
@@ -60,19 +62,65 @@ type ApiResponseJson = InvitationResult | {
|
|
|
60
62
|
invitations: InvitationResult[];
|
|
61
63
|
} | {};
|
|
62
64
|
type ApiRequestBody = AcceptInvitationRequest | null;
|
|
65
|
+
/**
|
|
66
|
+
* User type for JWT generation
|
|
67
|
+
*/
|
|
68
|
+
type User = {
|
|
69
|
+
id: string;
|
|
70
|
+
email: string;
|
|
71
|
+
adminScopes?: string[];
|
|
72
|
+
[key: string]: any;
|
|
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
|
+
};
|
|
63
99
|
|
|
64
100
|
declare class Vortex {
|
|
65
101
|
private apiKey;
|
|
66
102
|
constructor(apiKey: string);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Generate a JWT token for a user
|
|
105
|
+
*
|
|
106
|
+
* @param params - Object containing user and optional additional properties
|
|
107
|
+
* @param params.user - User object with id, email, and optional adminScopes
|
|
108
|
+
* @returns JWT token string
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const token = vortex.generateJwt({
|
|
113
|
+
* user: {
|
|
114
|
+
* id: "user-123",
|
|
115
|
+
* email: "user@example.com",
|
|
116
|
+
* adminScopes: ['autojoin']
|
|
117
|
+
* }
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
generateJwt(params: {
|
|
122
|
+
user: User;
|
|
123
|
+
[key: string]: any;
|
|
76
124
|
}): string;
|
|
77
125
|
vortexApiRequest(options: {
|
|
78
126
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
@@ -90,6 +138,47 @@ declare class Vortex {
|
|
|
90
138
|
deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}>;
|
|
91
139
|
getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]>;
|
|
92
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>;
|
|
93
182
|
}
|
|
94
183
|
|
|
95
|
-
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, 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
|
@@ -41,13 +41,26 @@ var Vortex = class {
|
|
|
41
41
|
constructor(apiKey) {
|
|
42
42
|
this.apiKey = apiKey;
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Generate a JWT token for a user
|
|
46
|
+
*
|
|
47
|
+
* @param params - Object containing user and optional additional properties
|
|
48
|
+
* @param params.user - User object with id, email, and optional adminScopes
|
|
49
|
+
* @returns JWT token string
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const token = vortex.generateJwt({
|
|
54
|
+
* user: {
|
|
55
|
+
* id: "user-123",
|
|
56
|
+
* email: "user@example.com",
|
|
57
|
+
* adminScopes: ['autojoin']
|
|
58
|
+
* }
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
generateJwt(params) {
|
|
63
|
+
const { user, ...rest } = params;
|
|
51
64
|
const [prefix, encodedId, key] = this.apiKey.split(".");
|
|
52
65
|
if (!prefix || !encodedId || !key) {
|
|
53
66
|
throw new Error("Invalid API key format");
|
|
@@ -65,13 +78,22 @@ var Vortex = class {
|
|
|
65
78
|
kid: id
|
|
66
79
|
};
|
|
67
80
|
const payload = {
|
|
68
|
-
userId,
|
|
69
|
-
|
|
70
|
-
role,
|
|
81
|
+
userId: user.id,
|
|
82
|
+
userEmail: user.email,
|
|
71
83
|
expires,
|
|
72
|
-
identifiers
|
|
73
|
-
|
|
84
|
+
// Include identifiers array for widget compatibility (VrtxAutojoin checks this)
|
|
85
|
+
identifiers: user.email ? [{ type: "email", value: user.email }] : []
|
|
74
86
|
};
|
|
87
|
+
if (user.adminScopes) {
|
|
88
|
+
payload.adminScopes = user.adminScopes;
|
|
89
|
+
if (user.adminScopes.includes("autojoin")) {
|
|
90
|
+
payload.userIsAutojoinAdmin = true;
|
|
91
|
+
payload.role = "admin";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (rest && Object.keys(rest).length > 0) {
|
|
95
|
+
Object.assign(payload, rest);
|
|
96
|
+
}
|
|
75
97
|
const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
|
|
76
98
|
const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
77
99
|
const toSign = `${headerB64}.${payloadB64}`;
|
|
@@ -83,7 +105,9 @@ var Vortex = class {
|
|
|
83
105
|
}
|
|
84
106
|
async vortexApiRequest(options) {
|
|
85
107
|
const { method, path, body, queryParams } = options;
|
|
86
|
-
const url = new URL(
|
|
108
|
+
const url = new URL(
|
|
109
|
+
`${process.env.VORTEX_API_BASE_URL || "https://api.vortexsoftware.com"}${path}`
|
|
110
|
+
);
|
|
87
111
|
if (queryParams) {
|
|
88
112
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
89
113
|
url.searchParams.append(key, String(value));
|
|
@@ -99,11 +123,13 @@ var Vortex = class {
|
|
|
99
123
|
});
|
|
100
124
|
if (!results.ok) {
|
|
101
125
|
const errorBody = await results.text();
|
|
102
|
-
throw new Error(
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`
|
|
128
|
+
);
|
|
103
129
|
}
|
|
104
130
|
const contentLength = results.headers.get("content-length");
|
|
105
131
|
const contentType = results.headers.get("content-type");
|
|
106
|
-
if (contentLength === "0" || !
|
|
132
|
+
if (contentLength === "0" || !contentType?.includes("application/json") && !contentLength) {
|
|
107
133
|
return {};
|
|
108
134
|
}
|
|
109
135
|
const responseText = await results.text();
|
|
@@ -119,7 +145,7 @@ var Vortex = class {
|
|
|
119
145
|
async getInvitationsByTarget(targetType, targetValue) {
|
|
120
146
|
const response = await this.vortexApiRequest({
|
|
121
147
|
method: "GET",
|
|
122
|
-
path: "/api/v1/invitations
|
|
148
|
+
path: "/api/v1/invitations",
|
|
123
149
|
queryParams: {
|
|
124
150
|
targetType,
|
|
125
151
|
targetValue
|
|
@@ -169,6 +195,58 @@ var Vortex = class {
|
|
|
169
195
|
path: `/api/v1/invitations/${invitationId}/reinvite`
|
|
170
196
|
});
|
|
171
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
|
+
}
|
|
172
250
|
};
|
|
173
251
|
// Annotate the CommonJS export names for ESM import in node:
|
|
174
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, GroupInput } from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) { }\n\n generateJwt({\n userId,\n identifiers,\n groups,\n role,\n attributes,\n }: {\n userId: string;\n identifiers: { type: 'email' | 'sms'; value: string }[];\n groups: GroupInput[];\n role?: string;\n attributes?: Record<string, any>;\n }): string {\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 const payload = {\n userId,\n groups,\n role,\n expires,\n identifiers,\n ...(attributes && Object.keys(attributes).length > 0 ? { attributes } : {}),\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
|
@@ -5,13 +5,26 @@ var Vortex = class {
|
|
|
5
5
|
constructor(apiKey) {
|
|
6
6
|
this.apiKey = apiKey;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Generate a JWT token for a user
|
|
10
|
+
*
|
|
11
|
+
* @param params - Object containing user and optional additional properties
|
|
12
|
+
* @param params.user - User object with id, email, and optional adminScopes
|
|
13
|
+
* @returns JWT token string
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const token = vortex.generateJwt({
|
|
18
|
+
* user: {
|
|
19
|
+
* id: "user-123",
|
|
20
|
+
* email: "user@example.com",
|
|
21
|
+
* adminScopes: ['autojoin']
|
|
22
|
+
* }
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
generateJwt(params) {
|
|
27
|
+
const { user, ...rest } = params;
|
|
15
28
|
const [prefix, encodedId, key] = this.apiKey.split(".");
|
|
16
29
|
if (!prefix || !encodedId || !key) {
|
|
17
30
|
throw new Error("Invalid API key format");
|
|
@@ -29,13 +42,22 @@ var Vortex = class {
|
|
|
29
42
|
kid: id
|
|
30
43
|
};
|
|
31
44
|
const payload = {
|
|
32
|
-
userId,
|
|
33
|
-
|
|
34
|
-
role,
|
|
45
|
+
userId: user.id,
|
|
46
|
+
userEmail: user.email,
|
|
35
47
|
expires,
|
|
36
|
-
identifiers
|
|
37
|
-
|
|
48
|
+
// Include identifiers array for widget compatibility (VrtxAutojoin checks this)
|
|
49
|
+
identifiers: user.email ? [{ type: "email", value: user.email }] : []
|
|
38
50
|
};
|
|
51
|
+
if (user.adminScopes) {
|
|
52
|
+
payload.adminScopes = user.adminScopes;
|
|
53
|
+
if (user.adminScopes.includes("autojoin")) {
|
|
54
|
+
payload.userIsAutojoinAdmin = true;
|
|
55
|
+
payload.role = "admin";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (rest && Object.keys(rest).length > 0) {
|
|
59
|
+
Object.assign(payload, rest);
|
|
60
|
+
}
|
|
39
61
|
const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
|
|
40
62
|
const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
41
63
|
const toSign = `${headerB64}.${payloadB64}`;
|
|
@@ -47,7 +69,9 @@ var Vortex = class {
|
|
|
47
69
|
}
|
|
48
70
|
async vortexApiRequest(options) {
|
|
49
71
|
const { method, path, body, queryParams } = options;
|
|
50
|
-
const url = new URL(
|
|
72
|
+
const url = new URL(
|
|
73
|
+
`${process.env.VORTEX_API_BASE_URL || "https://api.vortexsoftware.com"}${path}`
|
|
74
|
+
);
|
|
51
75
|
if (queryParams) {
|
|
52
76
|
Object.entries(queryParams).forEach(([key, value]) => {
|
|
53
77
|
url.searchParams.append(key, String(value));
|
|
@@ -63,11 +87,13 @@ var Vortex = class {
|
|
|
63
87
|
});
|
|
64
88
|
if (!results.ok) {
|
|
65
89
|
const errorBody = await results.text();
|
|
66
|
-
throw new Error(
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`
|
|
92
|
+
);
|
|
67
93
|
}
|
|
68
94
|
const contentLength = results.headers.get("content-length");
|
|
69
95
|
const contentType = results.headers.get("content-type");
|
|
70
|
-
if (contentLength === "0" || !
|
|
96
|
+
if (contentLength === "0" || !contentType?.includes("application/json") && !contentLength) {
|
|
71
97
|
return {};
|
|
72
98
|
}
|
|
73
99
|
const responseText = await results.text();
|
|
@@ -83,7 +109,7 @@ var Vortex = class {
|
|
|
83
109
|
async getInvitationsByTarget(targetType, targetValue) {
|
|
84
110
|
const response = await this.vortexApiRequest({
|
|
85
111
|
method: "GET",
|
|
86
|
-
path: "/api/v1/invitations
|
|
112
|
+
path: "/api/v1/invitations",
|
|
87
113
|
queryParams: {
|
|
88
114
|
targetType,
|
|
89
115
|
targetValue
|
|
@@ -133,6 +159,58 @@ var Vortex = class {
|
|
|
133
159
|
path: `/api/v1/invitations/${invitationId}/reinvite`
|
|
134
160
|
});
|
|
135
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
|
+
}
|
|
136
214
|
};
|
|
137
215
|
export {
|
|
138
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, GroupInput } from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) { }\n\n generateJwt({\n userId,\n identifiers,\n groups,\n role,\n attributes,\n }: {\n userId: string;\n identifiers: { type: 'email' | 'sms'; value: string }[];\n groups: GroupInput[];\n role?: string;\n attributes?: Record<string, any>;\n }): string {\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 const payload = {\n userId,\n groups,\n role,\n expires,\n identifiers,\n ...(attributes && Object.keys(attributes).length > 0 ? { attributes } : {}),\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":";AAAA,OAAO,YAAY;AACnB,SAAS,aAAa,qBAAqB;AAWpC,IAAM,SAAN,MAAa;AAAA,EAClB,YAAoB,QAAgB;AAAhB;AAAA,EAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBrC,YAAY,QAAoD;AAC9D,UAAM,EAAE,MAAM,GAAG,KAAK,IAAI;AAC1B,UAAM,CAAC,QAAQ,WAAW,GAAG,IAAI,KAAK,OAAO,MAAM,GAAG;AACtD,QAAI,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK;AACjC,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AACA,QAAI,WAAW,QAAQ;AACrB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AACA,UAAM,KAAK,cAAc,OAAO,KAAK,WAAW,WAAW,CAAC;AAE5D,UAAM,UAAU,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;AAGhD,UAAM,aAAa,OAAO,WAAW,UAAU,GAAG,EAAE,OAAO,EAAE,EAAE,OAAO;AAGtE,UAAM,SAAS;AAAA,MACb,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,MACjC,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAGA,UAAM,UAAe;AAAA,MACnB,QAAQ,KAAK;AAAA,MACb,WAAW,KAAK;AAAA,MAChB;AAAA;AAAA,MAEA,aAAa,KAAK,QAAQ,CAAC,EAAE,MAAM,SAAS,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC;AAAA,IACtE;AAGA,QAAI,KAAK,aAAa;AACpB,cAAQ,cAAc,KAAK;AAE3B,UAAI,KAAK,YAAY,SAAS,UAAU,GAAG;AACzC,gBAAQ,sBAAsB;AAC9B,gBAAQ,OAAO;AAAA,MACjB;AAAA,IACF;AAGA,QAAI,QAAQ,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AACxC,aAAO,OAAO,SAAS,IAAI;AAAA,IAC7B;AAGA,UAAM,YAAY,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC,EAAE,SAAS,WAAW;AAC1E,UAAM,aAAa,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,EAAE,SAAS,WAAW;AAG5E,UAAM,SAAS,GAAG,SAAS,IAAI,UAAU;AACzC,UAAM,YAAY,OAAO;AAAA,MACvB,OAAO,WAAW,UAAU,UAAU,EAAE,OAAO,MAAM,EAAE,OAAO;AAAA,IAChE,EAAE,SAAS,WAAW;AACtB,UAAM,MAAM,GAAG,MAAM,IAAI,SAAS;AAClC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,iBAAiB,SAKM;AAC3B,UAAM,EAAE,QAAQ,MAAM,MAAM,YAAY,IAAI;AAC5C,UAAM,MAAM,IAAI;AAAA,MACd,GAAG,QAAQ,IAAI,uBAAuB,gCAAgC,GAAG,IAAI;AAAA,IAC/E;AACA,QAAI,aAAa;AACf,aAAO,QAAQ,WAAW,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACpD,YAAI,aAAa,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,MAC5C,CAAC;AAAA,IACH;AACA,UAAM,UAAU,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MAC1C;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,MACpB;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AACD,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,YAAY,MAAM,QAAQ,KAAK;AACrC,YAAM,IAAI;AAAA,QACR,8BAA8B,QAAQ,MAAM,IAAI,QAAQ,UAAU,MAAM,SAAS;AAAA,MACnF;AAAA,IACF;AAGA,UAAM,gBAAgB,QAAQ,QAAQ,IAAI,gBAAgB;AAC1D,UAAM,cAAc,QAAQ,QAAQ,IAAI,cAAc;AAGtD,QAAI,kBAAkB,OAAQ,CAAC,aAAa,SAAS,kBAAkB,KAAK,CAAC,eAAgB;AAC3F,aAAO,CAAC;AAAA,IACV;AAGA,UAAM,eAAe,MAAM,QAAQ,KAAK;AACxC,QAAI,CAAC,aAAa,KAAK,GAAG;AACxB,aAAO,CAAC;AAAA,IACV;AAGA,QAAI;AACF,aAAO,KAAK,MAAM,YAAY;AAAA,IAChC,SAAS,OAAO;AAEd,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,uBACJ,YACA,aAC6B;AAC7B,UAAM,WAAY,MAAM,KAAK,iBAAiB;AAAA,MAC5C,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,QACX;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,cAAc,cAAiD;AACnE,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,uBAAuB,YAAY;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,iBAAiB,cAAmC;AACxD,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,uBAAuB,YAAY;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,kBACJ,eACA,QAC2B;AAC3B,UAAM,WAAY,MAAM,KAAK,iBAAiB;AAAA,MAC5C,QAAQ;AAAA,MACR,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,MACF;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,yBAAyB,WAAmB,SAA8B;AAC9E,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,gCAAgC,SAAS,IAAI,OAAO;AAAA,IAC5D,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,sBAAsB,WAAmB,SAA8C;AAC3F,UAAM,WAAY,MAAM,KAAK,iBAAiB;AAAA,MAC5C,QAAQ;AAAA,MACR,MAAM,gCAAgC,SAAS,IAAI,OAAO;AAAA,IAC5D,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,SAAS,cAAiD;AAC9D,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,uBAAuB,YAAY;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,mBAAmB,WAAmB,OAAiD;AAC3F,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,gCAAgC,mBAAmB,SAAS,CAAC,IAAI,mBAAmB,KAAK,CAAC;AAAA,IAClG,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4BA,MAAM,kBAAkB,QAAoE;AAC1F,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;","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.0
|
|
5
|
+
"version": "0.1.0",
|
|
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
|
}
|