@workos-inc/node 7.59.0 → 7.61.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.
@@ -0,0 +1,8 @@
1
+ import { RequestException } from '../interfaces/request-exception.interface';
2
+ export declare class ParseError extends Error implements RequestException {
3
+ readonly name = "ParseError";
4
+ readonly status = 500;
5
+ readonly rawBody: string;
6
+ readonly requestID: string;
7
+ constructor(message: string, rawBody: string, requestID: string);
8
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParseError = void 0;
4
+ class ParseError extends Error {
5
+ constructor(message, rawBody, requestID) {
6
+ super(message);
7
+ this.name = 'ParseError';
8
+ this.status = 500;
9
+ this.rawBody = rawBody;
10
+ this.requestID = requestID;
11
+ }
12
+ }
13
+ exports.ParseError = ParseError;
@@ -12,6 +12,7 @@ export interface UserManagementAuthorizationURLOptions {
12
12
  domainHint?: string;
13
13
  loginHint?: string;
14
14
  provider?: string;
15
+ providerQueryParams?: Record<string, string | boolean | number>;
15
16
  providerScopes?: string[];
16
17
  prompt?: string;
17
18
  redirectUri: string;
@@ -107,7 +107,7 @@ export declare class UserManagement {
107
107
  acceptInvitation(invitationId: string): Promise<Invitation>;
108
108
  revokeInvitation(invitationId: string): Promise<Invitation>;
109
109
  revokeSession(payload: RevokeSessionOptions): Promise<void>;
110
- getAuthorizationUrl({ connectionId, codeChallenge, codeChallengeMethod, context, clientId, domainHint, loginHint, organizationId, provider, providerScopes, prompt, redirectUri, state, screenHint, }: UserManagementAuthorizationURLOptions): string;
110
+ getAuthorizationUrl({ connectionId, codeChallenge, codeChallengeMethod, context, clientId, domainHint, loginHint, organizationId, provider, providerQueryParams, providerScopes, prompt, redirectUri, state, screenHint, }: UserManagementAuthorizationURLOptions): string;
111
111
  getLogoutUrl({ sessionId, returnTo, }: {
112
112
  sessionId: string;
113
113
  returnTo?: string;
@@ -19,9 +19,13 @@ var __rest = (this && this.__rest) || function (s, e) {
19
19
  }
20
20
  return t;
21
21
  };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
22
25
  Object.defineProperty(exports, "__esModule", { value: true });
23
26
  exports.UserManagement = void 0;
24
27
  const jose_1 = require("jose");
28
+ const qs_1 = __importDefault(require("qs"));
25
29
  const oauth_exception_1 = require("../common/exceptions/oauth.exception");
26
30
  const fetch_and_deserialize_1 = require("../common/utils/fetch-and-deserialize");
27
31
  const pagination_1 = require("../common/utils/pagination");
@@ -44,20 +48,13 @@ const send_invitation_options_serializer_1 = require("./serializers/send-invitat
44
48
  const update_organization_membership_options_serializer_1 = require("./serializers/update-organization-membership-options.serializer");
45
49
  const session_1 = require("./session");
46
50
  const toQueryString = (options) => {
47
- const searchParams = new URLSearchParams();
48
- const keys = Object.keys(options).sort();
49
- for (const key of keys) {
50
- const value = options[key];
51
- if (Array.isArray(value)) {
52
- value.forEach((item) => {
53
- searchParams.append(key, item);
54
- });
55
- }
56
- if (typeof value === 'string') {
57
- searchParams.append(key, value);
58
- }
59
- }
60
- return searchParams.toString();
51
+ return qs_1.default.stringify(options, {
52
+ arrayFormat: 'repeat',
53
+ // sorts the keys alphabetically to maintain backwards compatibility
54
+ sort: (a, b) => a.localeCompare(b),
55
+ // encodes space as + instead of %20 to maintain backwards compatibility
56
+ format: 'RFC1738',
57
+ });
61
58
  };
62
59
  class UserManagement {
63
60
  constructor(workos, ironSessionProvider) {
@@ -535,7 +532,7 @@ class UserManagement {
535
532
  yield this.workos.post('/user_management/sessions/revoke', (0, revoke_session_options_interface_1.serializeRevokeSessionOptions)(payload));
536
533
  });
537
534
  }
538
- getAuthorizationUrl({ connectionId, codeChallenge, codeChallengeMethod, context, clientId, domainHint, loginHint, organizationId, provider, providerScopes, prompt, redirectUri, state, screenHint, }) {
535
+ getAuthorizationUrl({ connectionId, codeChallenge, codeChallengeMethod, context, clientId, domainHint, loginHint, organizationId, provider, providerQueryParams, providerScopes, prompt, redirectUri, state, screenHint, }) {
539
536
  if (!provider && !connectionId && !organizationId) {
540
537
  throw new TypeError(`Incomplete arguments. Need to specify either a 'connectionId', 'organizationId', or 'provider'.`);
541
538
  }
@@ -555,6 +552,7 @@ class UserManagement {
555
552
  domain_hint: domainHint,
556
553
  login_hint: loginHint,
557
554
  provider,
555
+ provider_query_params: providerQueryParams,
558
556
  provider_scopes: providerScopes,
559
557
  prompt,
560
558
  client_id: clientId,
@@ -1671,6 +1671,22 @@ describe('UserManagement', () => {
1671
1671
  });
1672
1672
  expect(url).toMatchSnapshot();
1673
1673
  });
1674
+ describe('with providerQueryParams', () => {
1675
+ it('generates an authorize url that includes the specified query params', () => {
1676
+ const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
1677
+ const url = workos.userManagement.getAuthorizationUrl({
1678
+ provider: 'GoogleOAuth',
1679
+ clientId: 'proj_123',
1680
+ redirectUri: 'example.com/auth/workos/callback',
1681
+ providerQueryParams: {
1682
+ foo: 'bar',
1683
+ baz: 123,
1684
+ bool: true,
1685
+ },
1686
+ });
1687
+ expect(url).toMatchSnapshot();
1688
+ });
1689
+ });
1674
1690
  });
1675
1691
  });
1676
1692
  describe('with a connectionId', () => {
package/lib/workos.d.ts CHANGED
@@ -56,5 +56,6 @@ export declare class WorkOS {
56
56
  }>;
57
57
  delete(path: string, query?: any): Promise<void>;
58
58
  emitWarning(warning: string): void;
59
+ private handleParseError;
59
60
  private handleHttpError;
60
61
  }
package/lib/workos.js CHANGED
@@ -31,7 +31,8 @@ const widgets_1 = require("./widgets/widgets");
31
31
  const actions_1 = require("./actions/actions");
32
32
  const vault_1 = require("./vault/vault");
33
33
  const conflict_exception_1 = require("./common/exceptions/conflict.exception");
34
- const VERSION = '7.59.0';
34
+ const parse_error_1 = require("./common/exceptions/parse-error");
35
+ const VERSION = '7.61.0';
35
36
  const DEFAULT_HOSTNAME = 'api.workos.com';
36
37
  const HEADER_AUTHORIZATION = 'Authorization';
37
38
  const HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
@@ -115,17 +116,24 @@ class WorkOS {
115
116
  if (options.warrantToken) {
116
117
  requestHeaders[HEADER_WARRANT_TOKEN] = options.warrantToken;
117
118
  }
119
+ let res;
118
120
  try {
119
- const res = yield this.client.post(path, entity, {
121
+ res = yield this.client.post(path, entity, {
120
122
  params: options.query,
121
123
  headers: requestHeaders,
122
124
  });
123
- return { data: yield res.toJSON() };
124
125
  }
125
126
  catch (error) {
126
127
  this.handleHttpError({ path, error });
127
128
  throw error;
128
129
  }
130
+ try {
131
+ return { data: yield res.toJSON() };
132
+ }
133
+ catch (error) {
134
+ yield this.handleParseError(error, res);
135
+ throw error;
136
+ }
129
137
  });
130
138
  }
131
139
  get(path, options = {}) {
@@ -137,17 +145,24 @@ class WorkOS {
137
145
  if (options.warrantToken) {
138
146
  requestHeaders[HEADER_WARRANT_TOKEN] = options.warrantToken;
139
147
  }
148
+ let res;
140
149
  try {
141
- const res = yield this.client.get(path, {
150
+ res = yield this.client.get(path, {
142
151
  params: options.query,
143
152
  headers: requestHeaders,
144
153
  });
145
- return { data: yield res.toJSON() };
146
154
  }
147
155
  catch (error) {
148
156
  this.handleHttpError({ path, error });
149
157
  throw error;
150
158
  }
159
+ try {
160
+ return { data: yield res.toJSON() };
161
+ }
162
+ catch (error) {
163
+ yield this.handleParseError(error, res);
164
+ throw error;
165
+ }
151
166
  });
152
167
  }
153
168
  put(path, entity, options = {}) {
@@ -156,17 +171,24 @@ class WorkOS {
156
171
  if (options.idempotencyKey) {
157
172
  requestHeaders[HEADER_IDEMPOTENCY_KEY] = options.idempotencyKey;
158
173
  }
174
+ let res;
159
175
  try {
160
- const res = yield this.client.put(path, entity, {
176
+ res = yield this.client.put(path, entity, {
161
177
  params: options.query,
162
178
  headers: requestHeaders,
163
179
  });
164
- return { data: yield res.toJSON() };
165
180
  }
166
181
  catch (error) {
167
182
  this.handleHttpError({ path, error });
168
183
  throw error;
169
184
  }
185
+ try {
186
+ return { data: yield res.toJSON() };
187
+ }
188
+ catch (error) {
189
+ yield this.handleParseError(error, res);
190
+ throw error;
191
+ }
170
192
  });
171
193
  }
172
194
  delete(path, query) {
@@ -186,6 +208,17 @@ class WorkOS {
186
208
  // tslint:disable-next-line:no-console
187
209
  console.warn(`WorkOS: ${warning}`);
188
210
  }
211
+ handleParseError(error, res) {
212
+ var _a;
213
+ return __awaiter(this, void 0, void 0, function* () {
214
+ if (error instanceof SyntaxError) {
215
+ const rawResponse = res.getRawResponse();
216
+ const requestID = (_a = rawResponse.headers.get('X-Request-ID')) !== null && _a !== void 0 ? _a : '';
217
+ const rawBody = yield rawResponse.text();
218
+ throw new parse_error_1.ParseError(error.message, rawBody, requestID);
219
+ }
220
+ });
221
+ }
189
222
  handleHttpError({ path, error }) {
190
223
  var _a;
191
224
  if (!(error instanceof http_client_1.HttpClientError)) {
@@ -16,6 +16,7 @@ const jest_fetch_mock_1 = __importDefault(require("jest-fetch-mock"));
16
16
  const test_utils_1 = require("./common/utils/test-utils");
17
17
  const promises_1 = __importDefault(require("fs/promises"));
18
18
  const exceptions_1 = require("./common/exceptions");
19
+ const parse_error_1 = require("./common/exceptions/parse-error");
19
20
  const index_1 = require("./index");
20
21
  const index_worker_1 = require("./index.worker");
21
22
  const rate_limit_exceeded_exception_1 = require("./common/exceptions/rate-limit-exceeded.exception");
@@ -218,6 +219,70 @@ describe('WorkOS', () => {
218
219
  expect((0, test_utils_1.fetchBody)({ raw: true })).toBe('');
219
220
  }));
220
221
  });
222
+ describe('when the api responds with invalid JSON', () => {
223
+ it('throws a ParseError', () => __awaiter(void 0, void 0, void 0, function* () {
224
+ const mockResponse = {
225
+ ok: true,
226
+ status: 200,
227
+ headers: new Headers({
228
+ 'X-Request-ID': 'a-request-id',
229
+ 'content-type': 'application/json',
230
+ }),
231
+ text: () => Promise.resolve('invalid json{'),
232
+ json: () => Promise.reject(new SyntaxError('Invalid JSON')),
233
+ };
234
+ jest_fetch_mock_1.default.mockResolvedValueOnce(mockResponse);
235
+ const workos = new index_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
236
+ const error = yield workos.post('/path', {}).catch((e) => e);
237
+ expect(error).toBeInstanceOf(parse_error_1.ParseError);
238
+ expect(error.rawBody).toBe('invalid json{');
239
+ expect(error.requestID).toBe('a-request-id');
240
+ }));
241
+ });
242
+ });
243
+ describe('get', () => {
244
+ describe('when the api responds with invalid JSON', () => {
245
+ it('throws a ParseError', () => __awaiter(void 0, void 0, void 0, function* () {
246
+ const mockResponse = {
247
+ ok: true,
248
+ status: 200,
249
+ headers: new Headers({
250
+ 'X-Request-ID': 'a-request-id',
251
+ 'content-type': 'application/json',
252
+ }),
253
+ text: () => Promise.resolve('malformed json}'),
254
+ json: () => Promise.reject(new SyntaxError('Invalid JSON')),
255
+ };
256
+ jest_fetch_mock_1.default.mockResolvedValueOnce(mockResponse);
257
+ const workos = new index_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
258
+ const error = yield workos.get('/path').catch((e) => e);
259
+ expect(error).toBeInstanceOf(parse_error_1.ParseError);
260
+ expect(error.rawBody).toBe('malformed json}');
261
+ expect(error.requestID).toBe('a-request-id');
262
+ }));
263
+ });
264
+ });
265
+ describe('put', () => {
266
+ describe('when the api responds with invalid JSON', () => {
267
+ it('throws a ParseError', () => __awaiter(void 0, void 0, void 0, function* () {
268
+ const mockResponse = {
269
+ ok: true,
270
+ status: 200,
271
+ headers: new Headers({
272
+ 'X-Request-ID': 'a-request-id',
273
+ 'content-type': 'application/json',
274
+ }),
275
+ text: () => Promise.resolve('broken json['),
276
+ json: () => Promise.reject(new SyntaxError('Invalid JSON')),
277
+ };
278
+ jest_fetch_mock_1.default.mockResolvedValueOnce(mockResponse);
279
+ const workos = new index_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
280
+ const error = yield workos.put('/path', {}).catch((e) => e);
281
+ expect(error).toBeInstanceOf(parse_error_1.ParseError);
282
+ expect(error.rawBody).toBe('broken json[');
283
+ expect(error.requestID).toBe('a-request-id');
284
+ }));
285
+ });
221
286
  });
222
287
  describe('when in an environment that does not support fetch', () => {
223
288
  const fetchFn = globalThis.fetch;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "7.59.0",
2
+ "version": "7.61.0",
3
3
  "name": "@workos-inc/node",
4
4
  "author": "WorkOS",
5
5
  "description": "A Node wrapper for the WorkOS API",
@@ -41,7 +41,8 @@
41
41
  "iron-session": "~6.3.1",
42
42
  "jose": "~5.6.3",
43
43
  "leb": "^1.0.0",
44
- "pluralize": "8.0.0"
44
+ "pluralize": "8.0.0",
45
+ "qs": "6.14.0"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@peculiar/webcrypto": "^1.4.5",