@teamvortexsoftware/vortex-node-22-sdk 0.8.0 โ†’ 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -37,6 +37,8 @@ module.exports = __toCommonJS(index_exports);
37
37
  // src/vortex.ts
38
38
  var import_node_crypto = __toESM(require("crypto"));
39
39
  var import_uuid = require("uuid");
40
+ var SDK_NAME = "vortex-node-sdk";
41
+ var SDK_VERSION = true ? "0.8.1" : "0.8.1";
40
42
  var Vortex = class {
41
43
  constructor(apiKey) {
42
44
  this.apiKey = apiKey;
@@ -126,7 +128,9 @@ var Vortex = class {
126
128
  method,
127
129
  headers: {
128
130
  "Content-Type": "application/json",
129
- "x-api-key": this.apiKey
131
+ "x-api-key": this.apiKey,
132
+ "x-vortex-sdk-name": SDK_NAME,
133
+ "x-vortex-sdk-version": SDK_VERSION
130
134
  },
131
135
  body: body ? JSON.stringify(body) : void 0
132
136
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/vortex.ts"],"sourcesContent":["export * from './vortex';\nexport * from './types';","import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\n CreateInvitationRequest,\n CreateInvitationResponse,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐Ÿ” Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐Ÿงฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add userName if present\n if (user.userName) {\n payload.userName = user.userName;\n }\n\n // Add userAvatarUrl if present\n if (user.userAvatarUrl) {\n payload.userAvatarUrl = user.userAvatarUrl;\n }\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add allowedEmailDomains if present (for domain-restricted invitations)\n if (user.allowedEmailDomains && user.allowedEmailDomains.length > 0) {\n payload.allowedEmailDomains = user.allowedEmailDomains;\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐Ÿงฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐Ÿงพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n /**\n * Accept invitations using the new User format (preferred)\n * @param invitationIds - Array of invitation IDs to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;\n\n /**\n * Accept invitations using legacy target format (deprecated)\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param target - Legacy target object with type and value\n * @returns Invitation result\n */\n async acceptInvitations(\n invitationIds: string[],\n target: InvitationTarget\n ): Promise<InvitationResult>;\n\n /**\n * Accept invitations using multiple legacy targets (deprecated)\n * Will call the accept endpoint once per target\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param targets - Array of legacy target objects\n * @returns Invitation result from the last acceptance\n */\n async acceptInvitations(\n invitationIds: string[],\n targets: InvitationTarget[]\n ): Promise<InvitationResult>;\n\n // Implementation\n async acceptInvitations(\n invitationIds: string[],\n userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]\n ): Promise<InvitationResult> {\n // Handle array of targets (legacy, call once per target)\n if (Array.isArray(userOrTarget)) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n let lastResult: InvitationResult | undefined;\n for (const target of userOrTarget) {\n lastResult = await this.acceptInvitations(invitationIds, target);\n }\n if (!lastResult) {\n throw new Error('No targets provided');\n }\n return lastResult;\n }\n\n // Check if it's a legacy target format (has 'type' and 'value' properties)\n const isLegacyTarget = 'type' in userOrTarget && 'value' in userOrTarget;\n\n if (isLegacyTarget) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n\n // Convert target to User format\n const target = userOrTarget as InvitationTarget;\n const user: AcceptUser = {};\n\n if (target.type === 'email') {\n user.email = target.value;\n } else if (target.type === 'phone') {\n user.phone = target.value;\n } else {\n throw new Error(`Unsupported target type for accept: ${target.type}`);\n }\n\n // Make request with User format\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n // New User format\n const user = userOrTarget as AcceptUser;\n\n // Validate that either email or phone is provided\n if (!user.email && !user.phone) {\n throw new Error('User must have either email or phone');\n }\n\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n /**\n * Accept a single invitation\n * This is the recommended method for accepting invitations.\n * @param invitationId - Single invitation ID to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitation('inv-123', { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitation(invitationId: string, user: AcceptUser): Promise<InvitationResult> {\n return this.acceptInvitations([invitationId], user);\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Create an invitation from your backend\n *\n * This method allows you to create invitations programmatically using your API key,\n * without requiring a user JWT token. This is useful for server-side invitation\n * creation, such as \"People You May Know\" flows or admin-initiated invitations.\n *\n * @param params - Invitation parameters\n * @param params.widgetConfigurationId - The widget configuration ID to use\n * @param params.target - The target of the invitation (who is being invited)\n * @param params.target.type - 'email', 'phone', or 'internal'\n * @param params.target.value - Email address, phone number, or internal user ID\n * @param params.inviter - Information about the user creating the invitation\n * @param params.inviter.userId - Your internal user ID for the inviter\n * @param params.inviter.userEmail - Optional email of the inviter\n * @param params.inviter.name - Optional display name of the inviter\n * @param params.groups - Optional groups/scopes to associate with the invitation\n * @param params.source - Optional source for analytics (defaults to 'api')\n * @param params.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation with custom link preview\n * const invitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'email', value: 'invitee@example.com' },\n * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },\n * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],\n * unfurlConfig: {\n * title: 'Join the Engineering team!',\n * description: 'John Doe invited you to collaborate on Engineering',\n * image: 'https://example.com/og-image.png',\n * type: 'website',\n * siteName: 'Acme App',\n * },\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * // Use subtype for analytics segmentation\n * const pymkInvitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'internal', value: 'internal-user-id-abc' },\n * inviter: { userId: 'user-456' },\n * source: 'internal',\n * subtype: 'pymk', // Track this as a \"People You May Know\" invitation\n * });\n * ```\n */\n async createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations',\n body: params as unknown as ApiRequestBody,\n }) as Promise<CreateInvitationResponse>;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,yBAAmB;AACnB,kBAA2C;AAepC,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,SAAK,YAAAA,WAAc,OAAO,KAAK,WAAW,WAAW,CAAC;AAE5D,UAAM,UAAU,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;AAGhD,UAAM,aAAa,mBAAAC,QAAO,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,UAAU;AACjB,cAAQ,WAAW,KAAK;AAAA,IAC1B;AAGA,QAAI,KAAK,eAAe;AACtB,cAAQ,gBAAgB,KAAK;AAAA,IAC/B;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,KAAK,uBAAuB,KAAK,oBAAoB,SAAS,GAAG;AACnE,cAAQ,sBAAsB,KAAK;AAAA,IACrC;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,mBAAAA,QAAO,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;AAAA,EAwCA,MAAM,kBACJ,eACA,cAC2B;AAE3B,QAAI,MAAM,QAAQ,YAAY,GAAG;AAC/B,cAAQ;AAAA,QACN;AAAA,MACF;AACA,UAAI;AACJ,iBAAW,UAAU,cAAc;AACjC,qBAAa,MAAM,KAAK,kBAAkB,eAAe,MAAM;AAAA,MACjE;AACA,UAAI,CAAC,YAAY;AACf,cAAM,IAAI,MAAM,qBAAqB;AAAA,MACvC;AACA,aAAO;AAAA,IACT;AAGA,UAAM,iBAAiB,UAAU,gBAAgB,WAAW;AAE5D,QAAI,gBAAgB;AAClB,cAAQ;AAAA,QACN;AAAA,MACF;AAGA,YAAM,SAAS;AACf,YAAMC,QAAmB,CAAC;AAE1B,UAAI,OAAO,SAAS,SAAS;AAC3B,QAAAA,MAAK,QAAQ,OAAO;AAAA,MACtB,WAAW,OAAO,SAAS,SAAS;AAClC,QAAAA,MAAK,QAAQ,OAAO;AAAA,MACtB,OAAO;AACL,cAAM,IAAI,MAAM,uCAAuC,OAAO,IAAI,EAAE;AAAA,MACtE;AAGA,YAAMC,YAAY,MAAM,KAAK,iBAAiB;AAAA,QAC5C,QAAQ;AAAA,QACR,MAAM;AAAA,UACJ;AAAA,UACA,MAAAD;AAAA,QACF;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AACD,aAAOC;AAAA,IACT;AAGA,UAAM,OAAO;AAGb,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,OAAO;AAC9B,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AAEA,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,iBAAiB,cAAsB,MAA6C;AACxF,WAAO,KAAK,kBAAkB,CAAC,YAAY,GAAG,IAAI;AAAA,EACpD;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;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;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,EAsDA,MAAM,iBAAiB,QAAoE;AACzF,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;","names":["uuidStringify","crypto","user","response"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/vortex.ts"],"sourcesContent":["export * from './vortex';\nexport * from './types';","import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\n CreateInvitationRequest,\n CreateInvitationResponse,\n} from './types';\n\n// SDK identification for request tracking\n// __SDK_VERSION__ is injected at build time by tsup (see tsup.config.ts)\ndeclare const __SDK_VERSION__: string;\nconst SDK_NAME = 'vortex-node-sdk';\nconst SDK_VERSION = typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : '0.8.1';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐Ÿ” Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐Ÿงฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add userName if present\n if (user.userName) {\n payload.userName = user.userName;\n }\n\n // Add userAvatarUrl if present\n if (user.userAvatarUrl) {\n payload.userAvatarUrl = user.userAvatarUrl;\n }\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add allowedEmailDomains if present (for domain-restricted invitations)\n if (user.allowedEmailDomains && user.allowedEmailDomains.length > 0) {\n payload.allowedEmailDomains = user.allowedEmailDomains;\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 'x-vortex-sdk-name': SDK_NAME,\n 'x-vortex-sdk-version': SDK_VERSION,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n /**\n * Accept invitations using the new User format (preferred)\n * @param invitationIds - Array of invitation IDs to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;\n\n /**\n * Accept invitations using legacy target format (deprecated)\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param target - Legacy target object with type and value\n * @returns Invitation result\n */\n async acceptInvitations(\n invitationIds: string[],\n target: InvitationTarget\n ): Promise<InvitationResult>;\n\n /**\n * Accept invitations using multiple legacy targets (deprecated)\n * Will call the accept endpoint once per target\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param targets - Array of legacy target objects\n * @returns Invitation result from the last acceptance\n */\n async acceptInvitations(\n invitationIds: string[],\n targets: InvitationTarget[]\n ): Promise<InvitationResult>;\n\n // Implementation\n async acceptInvitations(\n invitationIds: string[],\n userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]\n ): Promise<InvitationResult> {\n // Handle array of targets (legacy, call once per target)\n if (Array.isArray(userOrTarget)) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n let lastResult: InvitationResult | undefined;\n for (const target of userOrTarget) {\n lastResult = await this.acceptInvitations(invitationIds, target);\n }\n if (!lastResult) {\n throw new Error('No targets provided');\n }\n return lastResult;\n }\n\n // Check if it's a legacy target format (has 'type' and 'value' properties)\n const isLegacyTarget = 'type' in userOrTarget && 'value' in userOrTarget;\n\n if (isLegacyTarget) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n\n // Convert target to User format\n const target = userOrTarget as InvitationTarget;\n const user: AcceptUser = {};\n\n if (target.type === 'email') {\n user.email = target.value;\n } else if (target.type === 'phone') {\n user.phone = target.value;\n } else {\n throw new Error(`Unsupported target type for accept: ${target.type}`);\n }\n\n // Make request with User format\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n // New User format\n const user = userOrTarget as AcceptUser;\n\n // Validate that either email or phone is provided\n if (!user.email && !user.phone) {\n throw new Error('User must have either email or phone');\n }\n\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n /**\n * Accept a single invitation\n * This is the recommended method for accepting invitations.\n * @param invitationId - Single invitation ID to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitation('inv-123', { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitation(invitationId: string, user: AcceptUser): Promise<InvitationResult> {\n return this.acceptInvitations([invitationId], user);\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Create an invitation from your backend\n *\n * This method allows you to create invitations programmatically using your API key,\n * without requiring a user JWT token. This is useful for server-side invitation\n * creation, such as \"People You May Know\" flows or admin-initiated invitations.\n *\n * @param params - Invitation parameters\n * @param params.widgetConfigurationId - The widget configuration ID to use\n * @param params.target - The target of the invitation (who is being invited)\n * @param params.target.type - 'email', 'phone', or 'internal'\n * @param params.target.value - Email address, phone number, or internal user ID\n * @param params.inviter - Information about the user creating the invitation\n * @param params.inviter.userId - Your internal user ID for the inviter\n * @param params.inviter.userEmail - Optional email of the inviter\n * @param params.inviter.name - Optional display name of the inviter\n * @param params.groups - Optional groups/scopes to associate with the invitation\n * @param params.source - Optional source for analytics (defaults to 'api')\n * @param params.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation with custom link preview\n * const invitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'email', value: 'invitee@example.com' },\n * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },\n * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],\n * unfurlConfig: {\n * title: 'Join the Engineering team!',\n * description: 'John Doe invited you to collaborate on Engineering',\n * image: 'https://example.com/og-image.png',\n * type: 'website',\n * siteName: 'Acme App',\n * },\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * // Use subtype for analytics segmentation\n * const pymkInvitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'internal', value: 'internal-user-id-abc' },\n * inviter: { userId: 'user-456' },\n * source: 'internal',\n * subtype: 'pymk', // Track this as a \"People You May Know\" invitation\n * });\n * ```\n */\n async createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations',\n body: params as unknown as ApiRequestBody,\n }) as Promise<CreateInvitationResponse>;\n }\n}\n"],"mappings":"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","names":["uuidStringify","crypto","user","response"]}
package/dist/index.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  // src/vortex.ts
2
2
  import crypto from "crypto";
3
3
  import { stringify as uuidStringify } from "uuid";
4
+ var SDK_NAME = "vortex-node-sdk";
5
+ var SDK_VERSION = true ? "0.8.1" : "0.8.1";
4
6
  var Vortex = class {
5
7
  constructor(apiKey) {
6
8
  this.apiKey = apiKey;
@@ -90,7 +92,9 @@ var Vortex = class {
90
92
  method,
91
93
  headers: {
92
94
  "Content-Type": "application/json",
93
- "x-api-key": this.apiKey
95
+ "x-api-key": this.apiKey,
96
+ "x-vortex-sdk-name": SDK_NAME,
97
+ "x-vortex-sdk-version": SDK_VERSION
94
98
  },
95
99
  body: body ? JSON.stringify(body) : void 0
96
100
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vortex.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\n CreateInvitationRequest,\n CreateInvitationResponse,\n} from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐Ÿ” Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐Ÿงฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add userName if present\n if (user.userName) {\n payload.userName = user.userName;\n }\n\n // Add userAvatarUrl if present\n if (user.userAvatarUrl) {\n payload.userAvatarUrl = user.userAvatarUrl;\n }\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add allowedEmailDomains if present (for domain-restricted invitations)\n if (user.allowedEmailDomains && user.allowedEmailDomains.length > 0) {\n payload.allowedEmailDomains = user.allowedEmailDomains;\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐Ÿงฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐Ÿงพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE';\n path: string;\n body?: ApiRequestBody;\n queryParams?: Record<string, string | number | boolean>;\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(\n `${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`\n );\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n /**\n * Accept invitations using the new User format (preferred)\n * @param invitationIds - Array of invitation IDs to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;\n\n /**\n * Accept invitations using legacy target format (deprecated)\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param target - Legacy target object with type and value\n * @returns Invitation result\n */\n async acceptInvitations(\n invitationIds: string[],\n target: InvitationTarget\n ): Promise<InvitationResult>;\n\n /**\n * Accept invitations using multiple legacy targets (deprecated)\n * Will call the accept endpoint once per target\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param targets - Array of legacy target objects\n * @returns Invitation result from the last acceptance\n */\n async acceptInvitations(\n invitationIds: string[],\n targets: InvitationTarget[]\n ): Promise<InvitationResult>;\n\n // Implementation\n async acceptInvitations(\n invitationIds: string[],\n userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]\n ): Promise<InvitationResult> {\n // Handle array of targets (legacy, call once per target)\n if (Array.isArray(userOrTarget)) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n let lastResult: InvitationResult | undefined;\n for (const target of userOrTarget) {\n lastResult = await this.acceptInvitations(invitationIds, target);\n }\n if (!lastResult) {\n throw new Error('No targets provided');\n }\n return lastResult;\n }\n\n // Check if it's a legacy target format (has 'type' and 'value' properties)\n const isLegacyTarget = 'type' in userOrTarget && 'value' in userOrTarget;\n\n if (isLegacyTarget) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n\n // Convert target to User format\n const target = userOrTarget as InvitationTarget;\n const user: AcceptUser = {};\n\n if (target.type === 'email') {\n user.email = target.value;\n } else if (target.type === 'phone') {\n user.phone = target.value;\n } else {\n throw new Error(`Unsupported target type for accept: ${target.type}`);\n }\n\n // Make request with User format\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n // New User format\n const user = userOrTarget as AcceptUser;\n\n // Validate that either email or phone is provided\n if (!user.email && !user.phone) {\n throw new Error('User must have either email or phone');\n }\n\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n /**\n * Accept a single invitation\n * This is the recommended method for accepting invitations.\n * @param invitationId - Single invitation ID to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitation('inv-123', { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitation(invitationId: string, user: AcceptUser): Promise<InvitationResult> {\n return this.acceptInvitations([invitationId], user);\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Create an invitation from your backend\n *\n * This method allows you to create invitations programmatically using your API key,\n * without requiring a user JWT token. This is useful for server-side invitation\n * creation, such as \"People You May Know\" flows or admin-initiated invitations.\n *\n * @param params - Invitation parameters\n * @param params.widgetConfigurationId - The widget configuration ID to use\n * @param params.target - The target of the invitation (who is being invited)\n * @param params.target.type - 'email', 'phone', or 'internal'\n * @param params.target.value - Email address, phone number, or internal user ID\n * @param params.inviter - Information about the user creating the invitation\n * @param params.inviter.userId - Your internal user ID for the inviter\n * @param params.inviter.userEmail - Optional email of the inviter\n * @param params.inviter.name - Optional display name of the inviter\n * @param params.groups - Optional groups/scopes to associate with the invitation\n * @param params.source - Optional source for analytics (defaults to 'api')\n * @param params.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation with custom link preview\n * const invitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'email', value: 'invitee@example.com' },\n * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },\n * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],\n * unfurlConfig: {\n * title: 'Join the Engineering team!',\n * description: 'John Doe invited you to collaborate on Engineering',\n * image: 'https://example.com/og-image.png',\n * type: 'website',\n * siteName: 'Acme App',\n * },\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * // Use subtype for analytics segmentation\n * const pymkInvitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'internal', value: 'internal-user-id-abc' },\n * inviter: { userId: 'user-456' },\n * source: 'internal',\n * subtype: 'pymk', // Track this as a \"People You May Know\" invitation\n * });\n * ```\n */\n async createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations',\n body: params as unknown as ApiRequestBody,\n }) as Promise<CreateInvitationResponse>;\n }\n}\n"],"mappings":"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","names":["user","response"]}
1
+ {"version":3,"sources":["../src/vortex.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport {\n ApiRequestBody,\n ApiResponseJson,\n InvitationResult,\n AcceptInvitationRequest,\n User,\n AcceptUser,\n AutojoinDomainsResponse,\n ConfigureAutojoinRequest,\n InvitationTarget,\n CreateInvitationRequest,\n CreateInvitationResponse,\n} from './types';\n\n// SDK identification for request tracking\n// __SDK_VERSION__ is injected at build time by tsup (see tsup.config.ts)\ndeclare const __SDK_VERSION__: string;\nconst SDK_NAME = 'vortex-node-sdk';\nconst SDK_VERSION = typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : '0.8.1';\n\nexport class Vortex {\n constructor(private apiKey: string) {}\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autojoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐Ÿ” Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐Ÿงฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n // Include identifiers array for widget compatibility (VrtxAutojoin checks this)\n identifiers: user.email ? [{ type: 'email', value: user.email }] : [],\n };\n\n // Add userName if present\n if (user.userName) {\n payload.userName = user.userName;\n }\n\n // Add userAvatarUrl if present\n if (user.userAvatarUrl) {\n payload.userAvatarUrl = user.userAvatarUrl;\n }\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n // Add widget compatibility fields for autojoin admin\n if (user.adminScopes.includes('autojoin')) {\n payload.userIsAutojoinAdmin = true;\n payload.role = 'admin'; // VrtxAutojoin checks parsedJwt.role === 'admin'\n }\n }\n\n // Add allowedEmailDomains if present (for domain-restricted invitations)\n if (user.allowedEmailDomains && user.allowedEmailDomains.length > 0) {\n payload.allowedEmailDomains = user.allowedEmailDomains;\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 'x-vortex-sdk-name': SDK_NAME,\n 'x-vortex-sdk-version': SDK_VERSION,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(\n `Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`\n );\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(\n targetType: 'email' | 'username' | 'phoneNumber',\n targetValue: string\n ): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations',\n queryParams: {\n targetType,\n targetValue,\n },\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n /**\n * Accept invitations using the new User format (preferred)\n * @param invitationIds - Array of invitation IDs to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitations(['inv-123'], { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitations(invitationIds: string[], user: AcceptUser): Promise<InvitationResult>;\n\n /**\n * Accept invitations using legacy target format (deprecated)\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param target - Legacy target object with type and value\n * @returns Invitation result\n */\n async acceptInvitations(\n invitationIds: string[],\n target: InvitationTarget\n ): Promise<InvitationResult>;\n\n /**\n * Accept invitations using multiple legacy targets (deprecated)\n * Will call the accept endpoint once per target\n * @deprecated Use the User format instead: acceptInvitations(invitationIds, { email: 'user@example.com' })\n * @param invitationIds - Array of invitation IDs to accept\n * @param targets - Array of legacy target objects\n * @returns Invitation result from the last acceptance\n */\n async acceptInvitations(\n invitationIds: string[],\n targets: InvitationTarget[]\n ): Promise<InvitationResult>;\n\n // Implementation\n async acceptInvitations(\n invitationIds: string[],\n userOrTarget: AcceptUser | InvitationTarget | InvitationTarget[]\n ): Promise<InvitationResult> {\n // Handle array of targets (legacy, call once per target)\n if (Array.isArray(userOrTarget)) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing an array of targets is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n let lastResult: InvitationResult | undefined;\n for (const target of userOrTarget) {\n lastResult = await this.acceptInvitations(invitationIds, target);\n }\n if (!lastResult) {\n throw new Error('No targets provided');\n }\n return lastResult;\n }\n\n // Check if it's a legacy target format (has 'type' and 'value' properties)\n const isLegacyTarget = 'type' in userOrTarget && 'value' in userOrTarget;\n\n if (isLegacyTarget) {\n console.warn(\n '[Vortex SDK] DEPRECATED: Passing a target object is deprecated. Use the User format instead: acceptInvitations(invitationIds, { email: \"user@example.com\" })'\n );\n\n // Convert target to User format\n const target = userOrTarget as InvitationTarget;\n const user: AcceptUser = {};\n\n if (target.type === 'email') {\n user.email = target.value;\n } else if (target.type === 'phone') {\n user.phone = target.value;\n } else {\n throw new Error(`Unsupported target type for accept: ${target.type}`);\n }\n\n // Make request with User format\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n // New User format\n const user = userOrTarget as AcceptUser;\n\n // Validate that either email or phone is provided\n if (!user.email && !user.phone) {\n throw new Error('User must have either email or phone');\n }\n\n const response = (await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n user,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n })) as InvitationResult;\n return response;\n }\n\n /**\n * Accept a single invitation\n * This is the recommended method for accepting invitations.\n * @param invitationId - Single invitation ID to accept\n * @param user - User object with email or phone (and optional name)\n * @returns Invitation result\n * @example\n * ```typescript\n * await vortex.acceptInvitation('inv-123', { email: 'user@example.com', name: 'John' });\n * ```\n */\n async acceptInvitation(invitationId: string, user: AcceptUser): Promise<InvitationResult> {\n return this.acceptInvitations([invitationId], user);\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = (await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n })) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n\n /**\n * Get autojoin domains configured for a specific scope\n *\n * @param scopeType - The type of scope (e.g., \"organization\", \"team\", \"project\")\n * @param scope - The scope identifier (customer's group ID)\n * @returns Autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.getAutojoinDomains('organization', 'acme-org');\n * console.log(result.autojoinDomains); // [{ id: '...', domain: 'acme.com' }]\n * ```\n */\n async getAutojoinDomains(scopeType: string, scope: string): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-scope/${encodeURIComponent(scopeType)}/${encodeURIComponent(scope)}/autojoin`,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Configure autojoin domains for a specific scope\n *\n * This endpoint syncs autojoin domains - it will add new domains, remove domains\n * not in the provided list, and deactivate the autojoin invitation if all domains\n * are removed (empty array).\n *\n * @param params - Configuration parameters\n * @param params.scope - The scope identifier (customer's group ID)\n * @param params.scopeType - The type of scope (e.g., \"organization\", \"team\")\n * @param params.scopeName - Optional display name for the scope\n * @param params.domains - Array of domains to configure for autojoin\n * @param params.widgetId - The widget configuration ID\n * @returns Updated autojoin domains and associated invitation\n *\n * @example\n * ```typescript\n * const result = await vortex.configureAutojoin({\n * scope: 'acme-org',\n * scopeType: 'organization',\n * scopeName: 'Acme Corporation',\n * domains: ['acme.com', 'acme.org'],\n * widgetId: 'widget-123',\n * });\n * ```\n */\n async configureAutojoin(params: ConfigureAutojoinRequest): Promise<AutojoinDomainsResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations/autojoin',\n body: params as unknown as ApiRequestBody,\n }) as Promise<AutojoinDomainsResponse>;\n }\n\n /**\n * Create an invitation from your backend\n *\n * This method allows you to create invitations programmatically using your API key,\n * without requiring a user JWT token. This is useful for server-side invitation\n * creation, such as \"People You May Know\" flows or admin-initiated invitations.\n *\n * @param params - Invitation parameters\n * @param params.widgetConfigurationId - The widget configuration ID to use\n * @param params.target - The target of the invitation (who is being invited)\n * @param params.target.type - 'email', 'phone', or 'internal'\n * @param params.target.value - Email address, phone number, or internal user ID\n * @param params.inviter - Information about the user creating the invitation\n * @param params.inviter.userId - Your internal user ID for the inviter\n * @param params.inviter.userEmail - Optional email of the inviter\n * @param params.inviter.name - Optional display name of the inviter\n * @param params.groups - Optional groups/scopes to associate with the invitation\n * @param params.source - Optional source for analytics (defaults to 'api')\n * @param params.subtype - Optional subtype for analytics segmentation (e.g., 'pymk', 'find-friends')\n * @param params.templateVariables - Optional template variables for email customization\n * @param params.metadata - Optional metadata passed through to webhooks\n * @param params.unfurlConfig - Optional link unfurl (Open Graph) configuration\n * @returns Created invitation with ID, short link, status, and creation timestamp\n *\n * @example\n * ```typescript\n * // Create an email invitation with custom link preview\n * const invitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'email', value: 'invitee@example.com' },\n * inviter: { userId: 'user-456', userEmail: 'inviter@example.com', name: 'John Doe' },\n * groups: [{ type: 'team', groupId: 'team-789', name: 'Engineering' }],\n * unfurlConfig: {\n * title: 'Join the Engineering team!',\n * description: 'John Doe invited you to collaborate on Engineering',\n * image: 'https://example.com/og-image.png',\n * type: 'website',\n * siteName: 'Acme App',\n * },\n * });\n *\n * // Create an internal invitation (PYMK flow - no email sent)\n * // Use subtype for analytics segmentation\n * const pymkInvitation = await vortex.createInvitation({\n * widgetConfigurationId: 'widget-config-123',\n * target: { type: 'internal', value: 'internal-user-id-abc' },\n * inviter: { userId: 'user-456' },\n * source: 'internal',\n * subtype: 'pymk', // Track this as a \"People You May Know\" invitation\n * });\n * ```\n */\n async createInvitation(params: CreateInvitationRequest): Promise<CreateInvitationResponse> {\n return this.vortexApiRequest({\n method: 'POST',\n path: '/api/v1/invitations',\n body: params as unknown as ApiRequestBody,\n }) as Promise<CreateInvitationResponse>;\n }\n}\n"],"mappings":";AAAA,OAAO,YAAY;AACnB,SAAS,aAAa,qBAAqB;AAkB3C,IAAM,WAAW;AACjB,IAAM,cAAc,OAAyC,UAAkB;AAExE,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,UAAU;AACjB,cAAQ,WAAW,KAAK;AAAA,IAC1B;AAGA,QAAI,KAAK,eAAe;AACtB,cAAQ,gBAAgB,KAAK;AAAA,IAC/B;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,KAAK,uBAAuB,KAAK,oBAAoB,SAAS,GAAG;AACnE,cAAQ,sBAAsB,KAAK;AAAA,IACrC;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,QAClB,qBAAqB;AAAA,QACrB,wBAAwB;AAAA,MAC1B;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;AAAA,EAwCA,MAAM,kBACJ,eACA,cAC2B;AAE3B,QAAI,MAAM,QAAQ,YAAY,GAAG;AAC/B,cAAQ;AAAA,QACN;AAAA,MACF;AACA,UAAI;AACJ,iBAAW,UAAU,cAAc;AACjC,qBAAa,MAAM,KAAK,kBAAkB,eAAe,MAAM;AAAA,MACjE;AACA,UAAI,CAAC,YAAY;AACf,cAAM,IAAI,MAAM,qBAAqB;AAAA,MACvC;AACA,aAAO;AAAA,IACT;AAGA,UAAM,iBAAiB,UAAU,gBAAgB,WAAW;AAE5D,QAAI,gBAAgB;AAClB,cAAQ;AAAA,QACN;AAAA,MACF;AAGA,YAAM,SAAS;AACf,YAAMA,QAAmB,CAAC;AAE1B,UAAI,OAAO,SAAS,SAAS;AAC3B,QAAAA,MAAK,QAAQ,OAAO;AAAA,MACtB,WAAW,OAAO,SAAS,SAAS;AAClC,QAAAA,MAAK,QAAQ,OAAO;AAAA,MACtB,OAAO;AACL,cAAM,IAAI,MAAM,uCAAuC,OAAO,IAAI,EAAE;AAAA,MACtE;AAGA,YAAMC,YAAY,MAAM,KAAK,iBAAiB;AAAA,QAC5C,QAAQ;AAAA,QACR,MAAM;AAAA,UACJ;AAAA,UACA,MAAAD;AAAA,QACF;AAAA,QACA,MAAM;AAAA,MACR,CAAC;AACD,aAAOC;AAAA,IACT;AAGA,UAAM,OAAO;AAGb,QAAI,CAAC,KAAK,SAAS,CAAC,KAAK,OAAO;AAC9B,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AAEA,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,iBAAiB,cAAsB,MAA6C;AACxF,WAAO,KAAK,kBAAkB,CAAC,YAAY,GAAG,IAAI;AAAA,EACpD;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;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;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,EAsDA,MAAM,iBAAiB,QAAoE;AACzF,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,IACR,CAAC;AAAA,EACH;AACF;","names":["user","response"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@teamvortexsoftware/vortex-node-22-sdk",
3
3
  "description": "Vortex Node 22 SDK",
4
4
  "author": "@teamvortexsoftware",
5
- "version": "0.8.0",
5
+ "version": "0.8.1",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",