diff --git a/lib/shared/server-request/src/request.ts b/lib/shared/server-request/src/request.ts index e8cd1b031..c6caf92d7 100644 --- a/lib/shared/server-request/src/request.ts +++ b/lib/shared/server-request/src/request.ts @@ -1,3 +1,6 @@ +// NOTE: This file is duplicated in "sdk/js/src/RequestUtils" because nx:rollup cant build non-external dependencies +// from outside the root directory https://github.com/nrwl/nx/issues/10395 + import fetchWithRetry, { RequestInitWithRetry } from 'fetch-retry' export class ResponseError extends Error { @@ -9,7 +12,9 @@ export class ResponseError extends Error { status: number } -const exponentialBackoff: RequestInitWithRetry['retryDelay'] = (attempt) => { +export const exponentialBackoff: RequestInitWithRetry['retryDelay'] = ( + attempt, +) => { const delay = Math.pow(2, attempt) * 100 const randomSum = delay * 0.2 * Math.random() return delay + randomSum @@ -31,7 +36,7 @@ const retryOnRequestError: retryOnRequestErrorFunc = (retries) => { } } -const handleResponse = async (res: Response) => { +export async function handleResponse(res: Response): Promise { // res.ok only checks for 200-299 status codes if (!res.ok && res.status >= 400) { let error @@ -88,6 +93,26 @@ export async function post( return handleResponse(res) } +export async function patch( + url: string, + requestConfig: RequestInit | RequestInitWithRetry, + sdkKey: string, +): Promise { + const [_fetch, config] = await getFetchAndConfig(requestConfig) + const patchHeaders = { + ...config.headers, + Authorization: sdkKey, + 'Content-Type': 'application/json', + } + + const res = await _fetch(url, { + ...config, + headers: patchHeaders, + method: 'PATCH', + }) + + return handleResponse(res) +} export async function get( url: string, requestConfig: RequestInit | RequestInitWithRetry, diff --git a/nx.json b/nx.json index 73ab78496..53869af0e 100644 --- a/nx.json +++ b/nx.json @@ -21,6 +21,9 @@ "pluginsConfig": { "@nx/js": { "analyzeSourceFiles": true + }, + "@nrwl/rollup": { + "analyzeSourceFiles": true } }, "namedInputs": { diff --git a/package.json b/package.json index 4824dc773..593e8379f 100644 --- a/package.json +++ b/package.json @@ -40,9 +40,9 @@ "class-transformer": "0.5.1", "class-validator": "^0.14.1", "core-js": "^3.6.5", - "cross-fetch": "^3.1.8", + "cross-fetch": "^4.0.0", "eslint-plugin-lodash": "^7.4.0", - "fetch-retry": "^5.0.3", + "fetch-retry": "^5.0.6", "hoist-non-react-statics": "^3.3.2", "iso-639-1": "^2.1.13", "jira-prepare-commit-msg": "^1.6.2", diff --git a/sdk/js-cloud-server/package.json b/sdk/js-cloud-server/package.json index 3ababb0b9..2e69a5ebe 100644 --- a/sdk/js-cloud-server/package.json +++ b/sdk/js-cloud-server/package.json @@ -5,8 +5,8 @@ "license": "MIT", "dependencies": { "@devcycle/types": "^1.9.0", - "cross-fetch": "^3.1.8", - "fetch-retry": "^5.0.3", + "cross-fetch": "^4.0.0", + "fetch-retry": "^5.0.6", "lodash": "^4.17.21" }, "main": "src/index.js", diff --git a/sdk/js/__mocks__/cross-fetch.ts b/sdk/js/__mocks__/cross-fetch.ts new file mode 100644 index 000000000..ef8b92b3d --- /dev/null +++ b/sdk/js/__mocks__/cross-fetch.ts @@ -0,0 +1,6 @@ +const { Request, Response } = jest.requireActual('cross-fetch') + +const fetch = jest.fn() + +export { Request, Response } +export default fetch diff --git a/sdk/js/__mocks__/fetch-retry.ts b/sdk/js/__mocks__/fetch-retry.ts new file mode 100644 index 000000000..6fb1926a0 --- /dev/null +++ b/sdk/js/__mocks__/fetch-retry.ts @@ -0,0 +1,2 @@ +export const fetchWithRetry = (_fetch: unknown): unknown => _fetch +export default fetchWithRetry diff --git a/sdk/js/__tests__/Client.test.ts b/sdk/js/__tests__/Client.test.ts index 69580d356..82abe9197 100644 --- a/sdk/js/__tests__/Client.test.ts +++ b/sdk/js/__tests__/Client.test.ts @@ -1,14 +1,15 @@ -import { DevCycleClient } from '../src/Client' -jest.mock('../src/Request') -jest.mock('../src/StreamingConnection') - +jest.unmock('cross-fetch') +import fetch from 'cross-fetch' +global.fetch = fetch type Variables = { enum_var: 'value1' | 'value2' bool: boolean string: string number: number } - +jest.mock('fetch-retry') +import { DevCycleClient } from '../src/Client' +jest.mock('../src/StreamingConnection') describe('DevCycleClient', () => { it('should prevent invalid variables', () => { const client = new DevCycleClient('test', { diff --git a/sdk/js/__tests__/Request.spec.ts b/sdk/js/__tests__/Request.spec.ts index f3bb4570c..5e055e744 100644 --- a/sdk/js/__tests__/Request.spec.ts +++ b/sdk/js/__tests__/Request.spec.ts @@ -1,54 +1,28 @@ -import { DVCPopulatedUser } from '../src/User' - -jest.mock('axios') -import axios, { AxiosInstance } from 'axios' -import { mocked } from 'jest-mock' - -const axiosRequestMock = jest.fn() -const createMock = mocked(axios.create) - -createMock.mockImplementation((): AxiosInstance => { - return { - request: axiosRequestMock, - interceptors: { - request: { use: jest.fn() }, - response: { use: jest.fn() }, - }, - } as unknown as AxiosInstance -}) +jest.mock('cross-fetch') +import fetch, { Response } from 'cross-fetch' +global.fetch = fetch +import { DVCPopulatedUser } from '../src/User' import * as Request from '../src/Request' import { BucketedUserConfig } from '@devcycle/types' import { dvcDefaultLogger } from '../src/logger' - const defaultLogger = dvcDefaultLogger({ level: 'debug' }) +const fetchRequestMock = fetch as jest.MockedFn describe('Request tests', () => { beforeEach(() => { - axiosRequestMock.mockReset() - }) - - describe('baseRequestParams', () => { - const { baseRequestHeaders } = Request - it('should add sdkKey header if passed in', () => { - const params = baseRequestHeaders('my_sdk_key') - expect(params['Content-Type']).toBe('application/json') - expect(params['Authorization']).toBe('my_sdk_key') - }) - - it('should not add header if no sdkKey passed in', () => { - const params = baseRequestHeaders() - expect(params['Content-Type']).toBe('application/json') - expect(params['Authorization']).toBeUndefined() - }) + fetchRequestMock.mockClear() + fetchRequestMock.mockResolvedValue( + new Response(JSON.stringify({}), { + status: 200, + }), + ) }) describe('getConfigJson', () => { it('should call get with serialized user and SDK key in params', async () => { const user = { user_id: 'my_user', isAnonymous: false } const sdkKey = 'my_sdk_key' - axiosRequestMock.mockResolvedValue({ status: 200, data: {} }) - await Request.getConfigJson( sdkKey, user as DVCPopulatedUser, @@ -61,21 +35,20 @@ describe('Request tests', () => { }, ) - expect(axiosRequestMock).toBeCalledWith({ - headers: { 'Content-Type': 'application/json' }, - method: 'GET', - url: - 'https://sdk-api.devcycle.com/v1/sdkConfig?sdkKey=' + + expect(fetchRequestMock).toBeCalledWith( + 'https://sdk-api.devcycle.com/v1/sdkConfig?sdkKey=' + `${sdkKey}&user_id=${user.user_id}&isAnonymous=false&sse=1&sseLastModified=1234&sseEtag=etag`, - }) + expect.objectContaining({ + headers: { 'Content-Type': 'application/json' }, + method: 'GET', + }), + ) }) it('should call local proxy for apiProxyURL option', async () => { const user = { user_id: 'my_user', isAnonymous: false } const sdkKey = 'my_sdk_key' const dvcOptions = { apiProxyURL: 'http://localhost:4000' } - axiosRequestMock.mockResolvedValue({ status: 200, data: {} }) - await Request.getConfigJson( sdkKey, user as DVCPopulatedUser, @@ -83,13 +56,14 @@ describe('Request tests', () => { dvcOptions, ) - expect(axiosRequestMock).toBeCalledWith({ - headers: { 'Content-Type': 'application/json' }, - method: 'GET', - url: - `${dvcOptions.apiProxyURL}/v1/sdkConfig?sdkKey=` + + expect(fetchRequestMock).toBeCalledWith( + `${dvcOptions.apiProxyURL}/v1/sdkConfig?sdkKey=` + `${sdkKey}&user_id=${user.user_id}&isAnonymous=false`, - }) + expect.objectContaining({ + headers: { 'Content-Type': 'application/json' }, + method: 'GET', + }), + ) }) }) @@ -99,10 +73,11 @@ describe('Request tests', () => { const config = {} as BucketedUserConfig const sdkKey = 'my_sdk_key' const events = [{ type: 'event_1_type' }, { type: 'event_2_type' }] - axiosRequestMock.mockResolvedValue({ - status: 200, - data: 'messages', - }) + fetchRequestMock.mockResolvedValue( + new Response('{}', { + status: 200, + }), + ) await Request.publishEvents( sdkKey, @@ -112,30 +87,25 @@ describe('Request tests', () => { defaultLogger, ) - expect(axiosRequestMock).toBeCalledWith({ - headers: { - Authorization: 'my_sdk_key', - 'Content-Type': 'application/json', - }, - method: 'POST', - url: 'https://events.devcycle.com/v1/events', - data: { - events: [ - expect.objectContaining({ - customType: 'event_1_type', - type: 'customEvent', - user_id: 'my_user', - clientDate: expect.any(Number), - }), - expect.objectContaining({ - customType: 'event_2_type', - type: 'customEvent', - user_id: 'my_user', - clientDate: expect.any(Number), - }), - ], - user, - }, + const call = fetchRequestMock.mock.calls[0] + const requestBody = JSON.parse(call[1]?.body as string) + + expect(requestBody).toEqual({ + events: [ + expect.objectContaining({ + customType: 'event_1_type', + type: 'customEvent', + user_id: 'my_user', + clientDate: expect.any(Number), + }), + expect.objectContaining({ + customType: 'event_2_type', + type: 'customEvent', + user_id: 'my_user', + clientDate: expect.any(Number), + }), + ], + user, }) }) }) @@ -144,26 +114,33 @@ describe('Request tests', () => { it('should send user data to edgedb api with url-encoded id', async () => { const user = { user_id: 'user@example.com', isAnonymous: false } const sdkKey = 'my_sdk_key' - axiosRequestMock.mockResolvedValue({ status: 200, data: {} }) + fetchRequestMock.mockResolvedValue( + new Response('{}', { + status: 200, + }), + ) await Request.saveEntity( user as DVCPopulatedUser, sdkKey, defaultLogger, ) - - expect(axiosRequestMock).toBeCalledWith({ - headers: { - 'Content-Type': 'application/json', - Authorization: 'my_sdk_key', - }, - data: { - user_id: 'user@example.com', - isAnonymous: false, - }, - method: 'PATCH', - url: 'https://sdk-api.devcycle.com/v1/edgedb/user%40example.com', + const call = fetchRequestMock.mock.calls[0] + const requestBody = JSON.parse(call[1]?.body as string) + expect(requestBody).toEqual({ + user_id: 'user@example.com', + isAnonymous: false, }) + expect(fetchRequestMock).toBeCalledWith( + 'https://sdk-api.devcycle.com/v1/edgedb/user%40example.com', + expect.objectContaining({ + headers: { + 'Content-Type': 'application/json', + Authorization: 'my_sdk_key', + }, + method: 'PATCH', + }), + ) }) }) }) diff --git a/sdk/js/jest.config.ts b/sdk/js/jest.config.ts index f976697e8..89a3ce814 100644 --- a/sdk/js/jest.config.ts +++ b/sdk/js/jest.config.ts @@ -2,7 +2,6 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ export default { displayName: 'js-client-sdk', - globals: {}, transform: { '^.+\\.(ts|tsx|js|jsx)?$': [ diff --git a/sdk/js/package.json b/sdk/js/package.json index 9dd47843a..0127d7934 100644 --- a/sdk/js/package.json +++ b/sdk/js/package.json @@ -14,8 +14,8 @@ "types": "./index.cjs.d.ts", "dependencies": { "@devcycle/types": "^1.9.0", - "axios": "^1.0.0", - "axios-retry": "^3.3.1", + "cross-fetch": "^4.0.0", + "fetch-retry": "^5.0.6", "lodash": "^4.17.21", "ua-parser-js": "^1.0.36", "uuid": "^8.3.2" diff --git a/sdk/js/src/Request.ts b/sdk/js/src/Request.ts index df07d8d35..feeb89c95 100644 --- a/sdk/js/src/Request.ts +++ b/sdk/js/src/Request.ts @@ -1,74 +1,27 @@ import { DevCycleEvent, DevCycleOptions } from './types' import { DVCPopulatedUser } from './User' import { serializeUserSearchParams, generateEventPayload } from './utils' -import axios, { AxiosResponse } from 'axios' -import axiosRetry from 'axios-retry' import type { BucketedUserConfig, DVCLogger } from '@devcycle/types' +import { + ResponseError, + exponentialBackoff, + getWithTimeout, + post, + patch, +} from './RequestUtils' +import { RequestInitWithRetry } from 'fetch-retry' -const axiosClient = axios.create({ - timeout: 5 * 1000, - validateStatus: (status: number) => status < 400 && status >= 200, -}) -axiosRetry(axiosClient, { - retries: 5, - // will do exponential retry until axios.timeout - retryDelay: axiosRetry.exponentialDelay, - shouldResetTimeout: true, - retryCondition: (error) => { - return !error.response || (error.response.status || 0) >= 500 - }, -}) - -export const HOST = '.devcycle.com' -export const CLIENT_SDK_URL = 'https://sdk-api' + HOST -export const EVENT_URL = 'https://events' + HOST - -export const CONFIG_PATH = '/v1/sdkConfig' -export const EVENTS_PATH = '/v1/events' -export const SAVE_ENTITY_PATH = '/v1/edgedb' +const HOST = '.devcycle.com' +const CLIENT_SDK_URL = 'https://sdk-api' + HOST +const EVENT_URL = 'https://events' + HOST -export const baseRequestHeaders = (sdkKey?: string): Record => { - return { - 'Content-Type': 'application/json', - ...(sdkKey ? { Authorization: sdkKey } : {}), - } -} +const CONFIG_PATH = '/v1/sdkConfig' +const EVENTS_PATH = '/v1/events' +const SAVE_ENTITY_PATH = '/v1/edgedb' -/** - * Base Requests - */ -export const get = async (url: string): Promise => { - return await axiosClient.request({ - method: 'GET', - url: `${url}`, - headers: baseRequestHeaders(), - }) -} - -export const post = async ( - url: string, - sdkKey: string, - body: Record, -): Promise => { - return await axiosClient.request({ - method: 'POST', - url, - data: body, - headers: baseRequestHeaders(sdkKey), - }) -} - -export const patch = async ( - url: string, - sdkKey: string, - body: Record, -): Promise => { - return await axiosClient.request({ - method: 'PATCH', - url, - data: body, - headers: baseRequestHeaders(sdkKey), - }) +const requestConfig: RequestInitWithRetry = { + retries: 5, + retryDelay: exponentialBackoff, } /** @@ -103,18 +56,15 @@ export const getConfigJson = async ( queryParams.toString() try { - const res = await get(url) - return res.data - } catch (ex: any) { - const errorString = JSON.stringify(ex?.response?.data?.data?.errors) + const res = await getWithTimeout(url, requestConfig, 5000) + return await res.json() + } catch (e) { logger.error( `Request to get config failed for url: ${url}, ` + - `response message: ${ex.message}` + - (errorString ? `, response data: ${errorString}` : ''), + `response message: ${e}`, ) throw new Error( - `Failed to download DevCycle config.` + - (errorString ? ` Error details: ${errorString}` : ''), + `Failed to download DevCycle config. Error details: ${e}`, ) } } @@ -126,7 +76,7 @@ export const publishEvents = async ( events: DevCycleEvent[], logger: DVCLogger, options?: DevCycleOptions, -): Promise => { +): Promise => { if (!sdkKey) { throw new Error('Missing sdkKey to publish events to Events API') } @@ -136,15 +86,19 @@ export const publishEvents = async ( const res = await post( `${options?.apiProxyURL || EVENT_URL}${EVENTS_PATH}`, + { + ...requestConfig, + body: JSON.stringify(payload), + }, sdkKey, - payload as unknown as Record, ) + const data = await res.json() if (res.status >= 400) { logger.error( - `Error posting events, status: ${res.status}, body: ${res.data}`, + `Error posting events, status: ${res.status}, body: ${data}`, ) } else { - logger.info(`Posted Events, status: ${res.status}, body: ${res.data}`) + logger.info(`Posted Events, status: ${res.status}, body: ${data}`) } return res @@ -155,7 +109,7 @@ export const saveEntity = async ( sdkKey: string, logger: DVCLogger, options?: DevCycleOptions, -): Promise => { +): Promise => { if (!sdkKey) { throw new Error('Missing sdkKey to save to Edge DB!') } @@ -165,32 +119,32 @@ export const saveEntity = async ( if (user.isAnonymous) { throw new Error('Cannot save user data for an anonymous user!') } - - const res = await patch( - `${ - options?.apiProxyURL || CLIENT_SDK_URL - }${SAVE_ENTITY_PATH}/${encodeURIComponent(user.user_id)}`, - sdkKey, - user as unknown as Record, - ) - - if (res.status === 403) { - logger.warn('Warning: EdgeDB feature is not enabled for this project') - } else if (res.status >= 400) { - logger.warn( - `Error saving user entity, status: ${res.status}, body: ${res.data}`, - ) - } else { - logger.info( - `Saved user entity, status: ${res.status}, body: ${res.data}`, + try { + return await patch( + `${ + options?.apiProxyURL || CLIENT_SDK_URL + }${SAVE_ENTITY_PATH}/${encodeURIComponent(user.user_id)}`, + { + ...requestConfig, + body: JSON.stringify(user), + }, + sdkKey, ) + } catch (e) { + const error = e as ResponseError + if (error.status === 403) { + logger.warn( + 'Warning: EdgeDB feature is not enabled for this project', + ) + } else if (error.status >= 400) { + logger.warn( + `Error saving user entity, status: ${error.status}, body: ${error.message}`, + ) + } else { + logger.info( + `Saved user entity, status: ${error.status}, body: ${error.message}`, + ) + } + return } - return res -} - -export default { - get, - post, - getConfigJson, - publishEvents, } diff --git a/sdk/js/src/RequestUtils.ts b/sdk/js/src/RequestUtils.ts new file mode 100644 index 000000000..1a29d807e --- /dev/null +++ b/sdk/js/src/RequestUtils.ts @@ -0,0 +1,159 @@ +// NOTE: This file is duplicated from "lib/shared/server-request" because nx:rollup cant build non-external dependencies +// from outside the root directory https://github.com/nrwl/nx/issues/10395 + +import fetchWithRetry, { RequestInitWithRetry } from 'fetch-retry' +export class ResponseError extends Error { + constructor(message: string) { + super(message) + this.name = 'ResponseError' + } + + status: number +} + +export const exponentialBackoff: RequestInitWithRetry['retryDelay'] = ( + attempt, +) => { + const delay = Math.pow(2, attempt) * 100 + const randomSum = delay * 0.2 * Math.random() + return delay + randomSum +} + +type retryOnRequestErrorFunc = ( + retries: number, +) => RequestInitWithRetry['retryOn'] + +const retryOnRequestError: retryOnRequestErrorFunc = (retries) => { + return (attempt, error, response) => { + if (attempt >= retries) { + return false + } else if (response && response?.status < 500) { + return false + } + + return true + } +} + +export async function handleResponse(res: Response): Promise { + // res.ok only checks for 200-299 status codes + if (!res.ok && res.status >= 400) { + let error + try { + const response: any = await res.clone().json() + error = new ResponseError( + response.message || 'Something went wrong', + ) + } catch (e) { + error = new ResponseError('Something went wrong') + } + error.status = res.status + throw error + } + + return res +} + +export async function getWithTimeout( + url: string, + requestConfig: RequestInit | RequestInitWithRetry, + timeout: number, +): Promise { + const controller = new AbortController() + const id = setTimeout(() => { + controller.abort() + }, timeout) + const response = await get(url, { + ...requestConfig, + signal: controller.signal, + }) + clearTimeout(id) + return response +} + +export async function post( + url: string, + requestConfig: RequestInit | RequestInitWithRetry, + sdkKey: string, +): Promise { + const [_fetch, config] = await getFetchAndConfig(requestConfig) + const postHeaders = { + ...config.headers, + Authorization: sdkKey, + 'Content-Type': 'application/json', + } + + const res = await _fetch(url, { + ...config, + headers: postHeaders, + method: 'POST', + }) + + return handleResponse(res) +} + +export async function patch( + url: string, + requestConfig: RequestInit | RequestInitWithRetry, + sdkKey: string, +): Promise { + const [_fetch, config] = await getFetchAndConfig(requestConfig) + const patchHeaders = { + ...config.headers, + Authorization: sdkKey, + 'Content-Type': 'application/json', + } + + const res = await _fetch(url, { + ...config, + headers: patchHeaders, + method: 'PATCH', + }) + + return handleResponse(res) +} +export async function get( + url: string, + requestConfig: RequestInit | RequestInitWithRetry, +): Promise { + const [_fetch, config] = await getFetchAndConfig(requestConfig) + const headers = { ...config.headers, 'Content-Type': 'application/json' } + + const res = await _fetch(url, { + ...config, + headers, + method: 'GET', + }) + + return handleResponse(res) +} + +async function getFetch() { + if (typeof fetch !== 'undefined') { + return fetch + } + + return (await import('cross-fetch')).default +} + +async function getFetchWithRetry() { + const fetch = await getFetch() + return fetchWithRetry(fetch) +} + +type FetchClient = Awaited> +type FetchAndConfig = [FetchClient, RequestInit] + +async function getFetchAndConfig( + requestConfig: RequestInit | RequestInitWithRetry, +): Promise { + const useRetries = 'retries' in requestConfig + if (useRetries && requestConfig.retries) { + const newConfig: RequestInitWithRetry = { ...requestConfig } + newConfig.retryOn = retryOnRequestError(requestConfig.retries) + newConfig.retryDelay = exponentialBackoff + return [await getFetchWithRetry(), newConfig] + } + + return [await getFetch(), requestConfig] +} diff --git a/sdk/js/tsconfig.lib.es5.json b/sdk/js/tsconfig.lib.es5.json index 481dc164a..c98834420 100644 --- a/sdk/js/tsconfig.lib.es5.json +++ b/sdk/js/tsconfig.lib.es5.json @@ -8,5 +8,10 @@ "types": [] }, "include": ["**/*.ts"], - "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"] + "exclude": [ + "**/*.spec.ts", + "**/*.test.ts", + "jest.config.ts", + "**/__mocks__/**/*.ts" + ] } diff --git a/sdk/js/tsconfig.lib.json b/sdk/js/tsconfig.lib.json index aa5c9a522..a28e66784 100644 --- a/sdk/js/tsconfig.lib.json +++ b/sdk/js/tsconfig.lib.json @@ -7,5 +7,10 @@ "types": [] }, "include": ["**/*.ts"], - "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"] + "exclude": [ + "**/*.spec.ts", + "**/*.test.ts", + "jest.config.ts", + "**/__mocks__/**/*.ts" + ] } diff --git a/sdk/js/tsconfig.spec.json b/sdk/js/tsconfig.spec.json index b2e3cf371..2d10e19cf 100644 --- a/sdk/js/tsconfig.spec.json +++ b/sdk/js/tsconfig.spec.json @@ -4,5 +4,11 @@ "outDir": "../../dist/out-tsc", "types": ["jest", "node"] }, - "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts", "jest.config.ts"] + "include": [ + "**/*.test.ts", + "**/*.spec.ts", + "**/*.d.ts", + "jest.config.ts", + "**/__mocks__/*.ts" + ] } diff --git a/sdk/nodejs/package.json b/sdk/nodejs/package.json index 6e4d4924f..639c46f53 100644 --- a/sdk/nodejs/package.json +++ b/sdk/nodejs/package.json @@ -7,8 +7,8 @@ "@devcycle/bucketing-assembly-script": "^1.16.0", "@devcycle/js-cloud-server-sdk": "^1.8.0", "@devcycle/types": "^1.9.0", - "cross-fetch": "^3.1.8", - "fetch-retry": "^5.0.3" + "cross-fetch": "^4.0.0", + "fetch-retry": "^5.0.6" }, "peerDependencies": { "@openfeature/server-sdk": "^1.11.0" diff --git a/yarn.lock b/yarn.lock index fe566477b..0acaef650 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5133,8 +5133,8 @@ __metadata: resolution: "@devcycle/js-client-sdk@workspace:sdk/js" dependencies: "@devcycle/types": ^1.9.0 - axios: ^1.0.0 - axios-retry: ^3.3.1 + cross-fetch: ^4.0.0 + fetch-retry: ^5.0.6 lodash: ^4.17.21 ua-parser-js: ^1.0.36 uuid: ^8.3.2 @@ -5146,8 +5146,8 @@ __metadata: resolution: "@devcycle/js-cloud-server-sdk@workspace:sdk/js-cloud-server" dependencies: "@devcycle/types": ^1.9.0 - cross-fetch: ^3.1.8 - fetch-retry: ^5.0.3 + cross-fetch: ^4.0.0 + fetch-retry: ^5.0.6 lodash: ^4.17.21 languageName: unknown linkType: soft @@ -5200,8 +5200,8 @@ __metadata: "@devcycle/bucketing-assembly-script": ^1.16.0 "@devcycle/js-cloud-server-sdk": ^1.8.0 "@devcycle/types": ^1.9.0 - cross-fetch: ^3.1.8 - fetch-retry: ^5.0.3 + cross-fetch: ^4.0.0 + fetch-retry: ^5.0.6 peerDependencies: "@openfeature/server-sdk": ^1.11.0 languageName: unknown @@ -14860,12 +14860,12 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:^3.1.8": - version: 3.1.8 - resolution: "cross-fetch@npm:3.1.8" +"cross-fetch@npm:^4.0.0": + version: 4.0.0 + resolution: "cross-fetch@npm:4.0.0" dependencies: node-fetch: ^2.6.12 - checksum: 78f993fa099eaaa041122ab037fe9503ecbbcb9daef234d1d2e0b9230a983f64d645d088c464e21a247b825a08dc444a6e7064adfa93536d3a9454b4745b3632 + checksum: ecca4f37ffa0e8283e7a8a590926b66713a7ef7892757aa36c2d20ffa27b0ac5c60dcf453119c809abe5923fc0bae3702a4d896bfb406ef1077b0d0018213e24 languageName: node linkType: hard @@ -15770,7 +15770,7 @@ __metadata: class-validator: ^0.14.1 core-js: ^3.6.5 cross-env: ^7.0.3 - cross-fetch: ^3.1.8 + cross-fetch: ^4.0.0 css-loader: ^6.4.0 cypress: ^13.0.0 detox: 20.13.1 @@ -15793,7 +15793,7 @@ __metadata: eslint-plugin-react-hooks: ~4.6.0 eslint-plugin-standard: ^5.0.0 express: ^4.18.2 - fetch-retry: ^5.0.3 + fetch-retry: ^5.0.6 hoist-non-react-statics: ^3.3.2 husky: ^7.0.0 iso-639-1: ^2.1.13 @@ -17938,10 +17938,10 @@ __metadata: languageName: node linkType: hard -"fetch-retry@npm:^5.0.3": - version: 5.0.3 - resolution: "fetch-retry@npm:5.0.3" - checksum: b4eebc04bd41651417e89ae9287e5b9e5421970ce07058c6e1e22f7d9c1cd5f935fc39a328fd66b433247c0ae1bb8a6b2d48c073d5a9f911992f72c5d311b14d +"fetch-retry@npm:^5.0.6": + version: 5.0.6 + resolution: "fetch-retry@npm:5.0.6" + checksum: 4ad8bca6ec7a7b1212e636bb422a9ae8bb9dce38df0b441c9eb77a29af99b368029d6248ff69427da67e3d43c53808b121135ea395e7fe4f8f383e0ad65b4f27 languageName: node linkType: hard