@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 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
- { type: 'workspace', groupId: 'some-workspace-id', name: 'The greatest workspace...pause...in the world' },
48
- { type: 'document', groupId: 'some-document-id', name: 'Ricky\'s grade 10 word papers' },
49
- { type: 'document', groupId: 'another-document-id', name: 'Sunnyvale bylaws' }
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(JSON.stringify({ jwt: vortex.generateJwt({
61
- userId,
62
- identifiers,
63
- groups,
64
- role
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 "react";
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("/vortex-jwt");
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
- uniqueInvitationIds,
218
- { type: 'email', value: email }
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
- generateJwt({ userId, identifiers, groups, role, attributes, }: {
68
- userId: string;
69
- identifiers: {
70
- type: 'email' | 'sms';
71
- value: string;
72
- }[];
73
- groups: GroupInput[];
74
- role?: string;
75
- attributes?: Record<string, any>;
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
- generateJwt({ userId, identifiers, groups, role, attributes, }: {
68
- userId: string;
69
- identifiers: {
70
- type: 'email' | 'sms';
71
- value: string;
72
- }[];
73
- groups: GroupInput[];
74
- role?: string;
75
- attributes?: Record<string, any>;
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
- generateJwt({
45
- userId,
46
- identifiers,
47
- groups,
48
- role,
49
- attributes
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
- groups,
70
- role,
81
+ userId: user.id,
82
+ userEmail: user.email,
71
83
  expires,
72
- identifiers,
73
- ...attributes && Object.keys(attributes).length > 0 ? { attributes } : {}
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(`${process.env.VORTEX_API_BASE_URL || "https://api.vortexsoftware.com"}${path}`);
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(`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`);
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" || !(contentType == null ? void 0 : contentType.includes("application/json")) && !contentLength) {
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?targetType",
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
- generateJwt({
9
- userId,
10
- identifiers,
11
- groups,
12
- role,
13
- attributes
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
- groups,
34
- role,
45
+ userId: user.id,
46
+ userEmail: user.email,
35
47
  expires,
36
- identifiers,
37
- ...attributes && Object.keys(attributes).length > 0 ? { attributes } : {}
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(`${process.env.VORTEX_API_BASE_URL || "https://api.vortexsoftware.com"}${path}`);
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(`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`);
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" || !(contentType == null ? void 0 : contentType.includes("application/json")) && !contentLength) {
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?targetType",
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
@@ -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":"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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.0.7",
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 && npm publish --userconfig ../../.npmrc --access restricted ./dist.d/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 && npm publish --userconfig ../../.npmrc --access public ./dist.d/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": "^9.24.0",
47
+ "@eslint/js": "catalog:",
48
48
  "@jest/globals": "29.7.0",
49
49
  "@teamvortexsoftware/eslint-config": "workspace:*",
50
50
  "@teamvortexsoftware/typescript-config": "workspace:*",
51
- "eslint": "^9.24.0",
51
+ "@types/node": "catalog:",
52
+ "eslint": "catalog:",
52
53
  "jest": "29.7.0",
53
- "tsup": "^8.5.0",
54
- "typescript-eslint": "^8.30.1"
54
+ "ts-jest": "catalog:",
55
+ "tsup": "catalog:",
56
+ "typescript-eslint": "catalog:"
55
57
  },
56
58
  "dependencies": {
57
- "uuid": "^13.0.0"
59
+ "uuid": "catalog:"
58
60
  }
59
61
  }