diff --git a/.changeset/wise-queens-decide.md b/.changeset/wise-queens-decide.md new file mode 100644 index 000000000..46c2d0deb --- /dev/null +++ b/.changeset/wise-queens-decide.md @@ -0,0 +1,15 @@ +--- +'@openfn/language-kobotoolbox': major +--- + +Update all functions in the main API + +## Migration guide + +- callbacks have removed from all functions. You can use `.then()` or `fn()` to + access the data returned by a function, ie, `getForms().then(state => state)` +- `getForms(params, callback)` is now `getForms()`, and will download download + assets of type survey. Use `http.get('assets/')` to retrieve other assets (and + add query parameters) +- `getSubmissions(params, callback)` is now `getSubmissions(formId, { query })`. +- `getDeploymentInfo(params, callback)` is now `getDeploymentInfo(formId)` diff --git a/packages/kobotoolbox/ast.json b/packages/kobotoolbox/ast.json index a17fea454..df4b851e0 100644 --- a/packages/kobotoolbox/ast.json +++ b/packages/kobotoolbox/ast.json @@ -2,12 +2,9 @@ "operations": [ { "name": "getForms", - "params": [ - "options", - "callback" - ], + "params": [], "docs": { - "description": "Make a request to get the list of forms", + "description": "Make a request to fetch all survey forms accessible to the user's API token. The url is `/assets/?asset_type=survey`.", "tags": [ { "title": "public", @@ -16,35 +13,13 @@ }, { "title": "example", - "description": "getForms({}, state => {\n console.log(state.data);\n return state;\n});" + "description": "getForms();" }, { "title": "function", "description": null, "name": null }, - { - "title": "param", - "description": "Optional headers and query for the request", - "type": { - "type": "OptionalType", - "expression": { - "type": "NameExpression", - "name": "RequestOptions" - } - }, - "name": "options", - "default": "{}" - }, - { - "title": "param", - "description": "(Optional) Callback function to execute after fetching form list", - "type": { - "type": "NameExpression", - "name": "function" - }, - "name": "callback" - }, { "title": "returns", "description": null, @@ -60,15 +35,21 @@ { "name": "getSubmissions", "params": [ - "params", - "callback" + "formId", + "options" ], "docs": { "description": "Get submissions for a specific form", "tags": [ { "title": "example", - "description": "getSubmissions({formId: 'aXecHjmbATuF6iGFmvBLBX'}, state => {\n console.log(state.data);\n return state;\n});" + "description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX');", + "caption": "Get all submissions for a specific form" + }, + { + "title": "example", + "description": "getSubmissions('aXecHjmbATuF6iGFmvBLBX', { query: { _submission_time:{ $gte: \"2022-06-12T21:54:20\" } } });", + "caption": "Get form submissions with a query" }, { "title": "function", @@ -82,21 +63,25 @@ }, { "title": "param", - "description": "Form Id and data to make the fetch or filter", + "description": "Form Id to get the specific submissions", "type": { "type": "NameExpression", - "name": "object" + "name": "string" }, - "name": "params" + "name": "formId" }, { "title": "param", - "description": "(Optional) Callback function to execute after fetching form submissions", + "description": "Optional query params for the request", "type": { - "type": "NameExpression", - "name": "function" + "type": "OptionalType", + "expression": { + "type": "NameExpression", + "name": "object" + } }, - "name": "callback" + "name": "options", + "default": "{}" }, { "title": "returns", @@ -113,15 +98,14 @@ { "name": "getDeploymentInfo", "params": [ - "params", - "callback" + "formId" ], "docs": { "description": "Get deployment information for a specific form", "tags": [ { "title": "example", - "description": "getDeploymentInfo({formId: 'aXecHjmbATuF6iGFmvBLBX'}, state => {\n console.log(state.data);\n return state;\n});" + "description": "getDeploymentInfo('aXecHjmbATuF6iGFmvBLBX');" }, { "title": "function", @@ -135,21 +119,12 @@ }, { "title": "param", - "description": "Form Id and data to make the fetch or filter", - "type": { - "type": "NameExpression", - "name": "object" - }, - "name": "params" - }, - { - "title": "param", - "description": "(Optional) Callback function to execute after fetching form deployment information", + "description": "Form Id to get the deployment information", "type": { "type": "NameExpression", - "name": "function" + "name": "string" }, - "name": "callback" + "name": "formId" }, { "title": "returns", diff --git a/packages/kobotoolbox/package.json b/packages/kobotoolbox/package.json index 15a0948f9..ec7d72d7c 100644 --- a/packages/kobotoolbox/package.json +++ b/packages/kobotoolbox/package.json @@ -33,7 +33,6 @@ "chai": "^3.4.0", "deep-eql": "^0.1.3", "esno": "^0.16.3", - "nock": "^12.0.3", "rimraf": "^3.0.2" }, "type": "module", diff --git a/packages/kobotoolbox/src/Adaptor.js b/packages/kobotoolbox/src/Adaptor.js index 9f602fe97..1f0909b7b 100644 --- a/packages/kobotoolbox/src/Adaptor.js +++ b/packages/kobotoolbox/src/Adaptor.js @@ -3,6 +3,7 @@ import { expandReferences } from '@openfn/language-common/util'; import * as util from './Utils'; + /** * Options object * @typedef {Object} RequestOptions @@ -38,81 +39,84 @@ export function execute(...operations) { } /** - * Make a request to get the list of forms + * Make a request to fetch all survey forms accessible to the user's API token. Calls `/api/v2/assets/?asset_type=survey`. * @public * @example - * getForms({}, state => { - * console.log(state.data); - * return state; - * }); + * getForms(); * @function - * @param {RequestOptions} [options={}] - Optional headers and query for the request - * @param {function} callback - (Optional) Callback function to execute after fetching form list + * @state data - an array of form objects * @returns {Operation} */ -export function getForms(options = {}, callback) { +export function getForms() { return async state => { - const [resolvedOptions] = expandReferences(state, options); + const url = `/assets/?asset_type=survey`; - const url = `/assets/?format=json`; + const response = await util.request(state, 'GET', url, {}); - const response = await util.request(state, 'GET', url, resolvedOptions); - console.log('✓', response.body.count, 'forms fetched.'); - return util.prepareNextState(state, response, callback); + console.log('✓', response.body.results.length, 'forms fetched.'); + return util.prepareNextState(state, response); }; } /** - * Get submissions for a specific form - * @example - * getSubmissions({formId: 'aXecHjmbATuF6iGFmvBLBX'}, state => { - * console.log(state.data); - * return state; - * }); + * Get submissions for a specific form. Calls `/api/v2/assets//data/`. + * @example Get all submissions for a specific form + * getSubmissions('aXecHjmbATuF6iGFmvBLBX'); + * @example Get form submissions with a query + * getSubmissions('aXecHjmbATuF6iGFmvBLBX', { query: { _submission_time:{ $gte: "2022-06-12T21:54:20" } } }); * @function * @public - * @param {object} params - Form Id and data to make the fetch or filter - * @param {function} callback - (Optional) Callback function to execute after fetching form submissions + * @param {string} formId - Form Id to get the specific submissions + * @param {object} [options={}] - Optional query params for the request + * @state data - an array of submission objects * @returns {Operation} */ -export function getSubmissions(params, callback) { +export function getSubmissions(formId, options = {}) { return async state => { - const [resolvedParams] = expandReferences(state, params); - - const { formId } = resolvedParams; - - const url = `/assets/${formId}/data/?format=json`; - - const response = await util.request(state, 'GET', url, resolvedParams); - console.log('✓', response.body.count, 'forms fetched.'); - return util.prepareNextState(state, response, callback); + const [resolvedFormId, resolvedOptions] = expandReferences( + state, + formId, + options + ); + + const url = `/assets/${resolvedFormId}/data/`; + const query = {}; + if (resolvedOptions.query) { + if (typeof resolvedOptions.query == 'string') { + query.query = resolvedOptions.query; + } else { + query.query = JSON.stringify(resolvedOptions.query); + } + } + + const response = await util.request(state, 'GET', url, { + paginate: true, + query, + }); + console.log('✓', response.results.length, 'submissions fetched.'); + return util.prepareNextState(state, response); }; } /** - * Get deployment information for a specific form + * Get deployment information for a specific form. Calls `/api/v2/assets//deployment/`. * @example - * getDeploymentInfo({formId: 'aXecHjmbATuF6iGFmvBLBX'}, state => { - * console.log(state.data); - * return state; - * }); + * getDeploymentInfo('aXecHjmbATuF6iGFmvBLBX'); * @function * @public - * @param {object} params - Form Id and data to make the fetch or filter - * @param {function} callback - (Optional) Callback function to execute after fetching form deployment information + * @param {string} formId - Form Id to get the deployment information + * @state data - an object containing deployment information * @returns {Operation} */ -export function getDeploymentInfo(params, callback) { +export function getDeploymentInfo(formId) { return async state => { - const [resolvedParams] = expandReferences(state, params); - - const { formId } = resolvedParams; + const [resolvedFormId] = expandReferences(state, formId); - const url = `/assets/${formId}/deployment/?format=json`; + const url = `/assets/${resolvedFormId}/deployment/`; - const response = await util.request(state, 'GET', url, resolvedParams); + const response = await util.request(state, 'GET', url, {}); console.log('✓', 'deployment information fetched.'); - return util.prepareNextState(state, response, callback); + return util.prepareNextState(state, response); }; } @@ -126,7 +130,7 @@ export { fields, fn, fnIf, - http, + http, group, lastReferenceValue, merge, diff --git a/packages/kobotoolbox/src/Utils.js b/packages/kobotoolbox/src/Utils.js index b6ab3ac5a..956e45e07 100644 --- a/packages/kobotoolbox/src/Utils.js +++ b/packages/kobotoolbox/src/Utils.js @@ -5,22 +5,30 @@ import { logResponse, } from '@openfn/language-common/util'; -export const prepareNextState = (state, response, callback = s => s) => { +export const prepareNextState = (state, response) => { const { body, ...responseWithoutBody } = response; - const nextState = { + + return { ...composeNextState(state, response.body), response: responseWithoutBody, }; - return callback(nextState); }; export async function request(state, method, path, opts) { const { baseURL, apiVersion, username, password } = state.configuration; - const { data = {}, query = {}, headers = {}, parseAs = 'json' } = opts; + const { + data = {}, + query = {}, + headers = {}, + parseAs = 'json', + paginate = false, + } = opts; const authHeaders = makeBasicAuthHeader(username, password); + let start, limit; + const options = { body: data, headers: { @@ -36,5 +44,36 @@ export async function request(state, method, path, opts) { baseUrl: `${baseURL}/api/${apiVersion}`, }; - return commonRequest(method, path, options).then(logResponse); + if (paginate) { + const results = []; + + do { + const response = await commonRequest(method, path, options).then( + logResponse + ); + results.push(...response.body.results); + if (response.body.next) { + const nextUrl = new URL(response.body.next); + + const startDigit = + nextUrl.searchParams.get('start') !== null + ? nextUrl.searchParams.get('start') + : 0; + + start = Number(startDigit); + limit = nextUrl.searchParams.get('limit'); + + options.query = { + ...options.query, + start, + limit, + }; + } else { + break; + } + } while (true); + return { results }; + } else { + return commonRequest(method, path, options).then(logResponse); + } } diff --git a/packages/kobotoolbox/test/index.js b/packages/kobotoolbox/test/index.js index e612a217c..88ade6da5 100644 --- a/packages/kobotoolbox/test/index.js +++ b/packages/kobotoolbox/test/index.js @@ -1,14 +1,16 @@ import chai from 'chai'; const { expect } = chai; import { enableMockClient } from '@openfn/language-common/util'; +import { request } from '../src/Utils'; -import nock from 'nock'; +import { http } from '../src'; -import Adaptor, { http } from '../src'; - -const { execute } = Adaptor; - -import { getSubmissions, getForms, getDeploymentInfo } from '../src/Adaptor'; +import { + getSubmissions, + getForms, + getDeploymentInfo, + execute, +} from '../src/Adaptor'; const testServer = enableMockClient('https://test.kobotoolbox.org'); const jsonHeaders = { @@ -23,6 +25,77 @@ const configuration = { baseURL: 'https://test.kobotoolbox.org', }; +const defaultObjects = [ + { uid: '1' }, + { uid: '2' }, + { uid: '3' }, + { uid: '4' }, + { uid: '5' }, + { uid: '6' }, +]; + +const paginatedResponse = ( + url, + limit = 30000, + start = 0, + objects = defaultObjects +) => { + const s = parseInt(start); + const l = parseInt(limit); + const results = objects.slice(s, s + l); + if (objects.length > s + l) { + const next = `${url}/?format=json&start=${s + l}&limit=${l}`; + + return { + next, + results, + }; + } else { + return { results }; + } +}; + +describe('paginatedResponse', () => { + const items = [1, 2, 3]; + it('should return the first item', () => { + const start = 0; + const limit = 1; + const { next, results } = paginatedResponse('www', limit, start, items); + expect(results).to.eql([1]); + }); + it('should return 2 items with limit 2', () => { + const start = 0; + const limit = 2; + const { next, results } = paginatedResponse('www', limit, start, items); + expect(results).to.eql([1, 2]); + }); + it('should return 2 items with start 1 and limit 2', () => { + const start = 1; + const limit = 2; + const { next, results } = paginatedResponse('www', limit, start, items); + expect(results).to.eql([2, 3]); + }); + it('should return all items', () => { + const start = 0; + const limit = 3; + const { next, results } = paginatedResponse('www', limit, start, items); + expect(results).to.eql([1, 2, 3]); + }); + it('should return all items if limit is greater', () => { + const start = 0; + const limit = 100; + const { next, results } = paginatedResponse('www', limit, start, items); + expect(results).to.eql([1, 2, 3]); + }); + it('should return the correct next link', () => { + const start = 1; + const limit = 1; + const { next, results } = paginatedResponse('www', limit, start, items); + expect(next).to.eql('www/?format=json&start=2&limit=1'); + expect(results).to.eql([2]); + }); +}); + describe('execute', () => { it('executes each operation in sequence', done => { let state = {}; @@ -57,8 +130,78 @@ describe('execute', () => { }); }); -describe('http', () => { - it('should return responses in JSON format', async () => { +describe('request', () => { + it('handles pagination if paginate is true', async () => { + let callCount = 0; + testServer + .intercept({ + path: /\/api\/v2\/assets\/aDReHdA7UuNBYsiCXQBr43\/data\//, + method: 'GET', + }) + .reply( + 200, + req => { + callCount++; + return paginatedResponse( + `${req.origin}${req.path}`, + req.query.limit, + req.query.start + ); + }, + { ...jsonHeaders } + ) + .times(6); + const state = { configuration }; + const { results } = await request( + state, + 'GET', + '/assets/aDReHdA7UuNBYsiCXQBr43/data/', + { + paginate: true, + query: { start: 0, limit: 1 }, + } + ); + + expect(callCount).to.eql(6); + expect(results).to.eql(defaultObjects); + }); + it('does not handle pagination if paginate is false', async () => { + let callCount = 0; + testServer + .intercept({ + path: /\/api\/v2\/assets\/aDReHdA7UuNBYsiCXQBr43\/data\//, + method: 'GET', + }) + .reply( + 200, + req => { + callCount++; + return paginatedResponse( + `${req.origin}${req.path}`, + req.query.limit, + req.query.start + ); + }, + { ...jsonHeaders } + ) + .times(1); + const state = { configuration }; + const { body } = await request( + state, + 'GET', + '/assets/aDReHdA7UuNBYsiCXQBr43/data/', + { + paginate: false, + } + ); + + expect(callCount).to.eql(1); + expect(body.results).to.eql(defaultObjects); + }); +}); + +describe('http.get', () => { + it('should GET with a query', async () => { testServer .intercept({ path: '/api/v2/assets/', @@ -67,7 +210,9 @@ describe('http', () => { }) .reply( 200, - { results: [{ name: 'Feedback Survey Test', asset_type: 'survey' }] }, + { + results: [{ name: 'Feedback Survey Test', asset_type: 'survey' }], + }, { ...jsonHeaders } ); const state = { configuration }; @@ -128,238 +273,101 @@ describe('http.post', () => { }); }); -describe.skip('getSubmissions', () => { - before(() => { - nock('https://kf.kobotoolbox.org') - .get('/api/v2/assets/aXecHjmbATuF6iGFmvBLBX/data/?format=json') - .basicAuth({ user: 'john', pass: 'doe' }) - .reply(200, { - count: 2, - next: null, - previous: null, - results: [{}, {}], - }); - - nock('https://kf.kobotoolbox.org') - .get('/api/v2/assets/bXecHjmbATuF6iGFmvBLBX/data/?format=json') - .basicAuth({ user: 'john', pass: 'doe' }) - .reply(404, { - body: 'A 404 error.', - }); - - nock('https://kf.kobotoolbox.org') - .get('/api/v2/assets/cXecHjmbATuF6iGFmvBLBX/data/?format=json') - .basicAuth({ user: 'john', pass: 'doe' }) - .reply(500, { - body: 'Another error.', - }); - }); +describe('getSubmissions', () => { + const formId = 'aXecHjmbATuF6iGFmvBLBX'; it('should get a list of submissions', async () => { - let state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - // type: 'process.env.type', - apiVersion: 'v2', - }, - }; - - const nextState = await execute( - getSubmissions({ formId: 'aXecHjmbATuF6iGFmvBLBX' }) - )(state).then(nextState => { - return nextState; - }); - expect(nextState.data).to.deep.eq({ - count: 2, - next: null, - previous: null, - results: [{}, {}], - }); - }).timeout(10 * 1000); - - it('throws an error for a 404 response', async () => { - const state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - apiVersion: 'v2', - }, - }; - - const error = await execute( - getSubmissions({ formId: 'bXecHjmbATuF6iGFmvBLBX' }) - )(state).catch(error => { - return error; - }); - expect(error.message).to.eql('Request failed with status code 404'); + testServer + .intercept({ + path: `/api/v2/assets/${formId}/data/`, + method: 'GET', + query: { format: 'json' }, + }) + .reply( + 200, + { + results: [ + { First_Name_of_Patient: 'Kwothe', Last_Name_of_Patient: 'Ruh' }, + ], + }, + { ...jsonHeaders } + ); + const state = { configuration }; + const { response } = await execute(getSubmissions(formId))(state); + expect(response.results[0]['First_Name_of_Patient']).to.eql('Kwothe'); }); - it('throws different kind of errors', async () => { - const state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - apiVersion: 'v2', - }, - }; - - const error = await execute( - getSubmissions({ formId: 'cXecHjmbATuF6iGFmvBLBX' }) - )(state).catch(error => { - return error; - }); - expect(error.message).to.eql('Request failed with status code 500'); + it('should get a list of submissions with a query', async () => { + testServer + .intercept({ + path: `/api/v2/assets/${formId}/data/`, + method: 'GET', + query: { + format: 'json', + query: '{"_submission_time":{"$gte":"2022-06-12T21:54:20"}}', + }, + }) + .reply( + 200, + { + results: [ + { First_Name_of_Patient: 'Kwothe', Last_Name_of_Patient: 'Ruh' }, + ], + }, + { ...jsonHeaders } + ); + const state = { configuration }; + const { response } = await execute( + getSubmissions(formId, { + query: { _submission_time: { $gte: '2022-06-12T21:54:20' } }, + }) + )(state); + expect(response.results[0]['First_Name_of_Patient']).to.eql('Kwothe'); }); }); -describe.skip('getForms', () => { - before(() => { - nock('https://kf.kobotoolbox.org') - .get('/api/v2/assets/?format=json') - .basicAuth({ user: 'john', pass: 'doe' }) - .reply(200, { - count: 10, - next: null, - previous: null, - results: [{}, {}], - }); +describe('getForms', () => { + beforeEach(() => { + testServer + .intercept({ + path: `/api/v2/assets/`, + method: 'GET', + query: { format: 'json', asset_type: 'survey' }, + }) + .reply( + 200, + { results: [{ name: 'Feedback Survey Test', asset_type: 'survey' }] }, + { ...jsonHeaders } + ); }); it('should get a list of forms', async () => { - let state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - apiVersion: 'v2', - }, - }; - const nextState = await execute(getForms())(state).then(nextState => { - return nextState; - }); - expect(nextState.data).to.deep.eq({ - count: 10, - next: null, - previous: null, - results: [{}, {}], - }); - }).timeout(10 * 1000); - /* - it('throws an error for a 404 response', async () => { - - const state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - apiVersion: 'v2', - }, - }; - - const error = await execute(getForms())(state).catch(error => { - return error; - }); - expect(error.message).to.eql('Request failed with status code 404'); + const state = { configuration }; + const { data } = await execute(getForms())(state); + expect(data.results[0].name).to.eql('Feedback Survey Test'); }); - - it('throws different kind of errors', async () => { - - const state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - apiVersion: 'v2', - }, - }; - - const error = await execute(getForms())(state).catch(error => { - return error; - }); - expect(error.message).to.eql('Request failed with status code 500'); - }); */ }); -describe.skip('getDeploymentInfo', () => { - before(() => { - nock('https://kf.kobotoolbox.org') - .get('/api/v2/assets/aXecHjmbATuF6iGFmvBLBX/deployment/?format=json') - .basicAuth({ user: 'john', pass: 'doe' }) - .reply(200, { - count: 2, - next: null, - previous: null, - results: [{}, {}], - }); - nock('https://kf.kobotoolbox.org') - .get('/api/v2/assets/bXecHjmbATuF6iGFmvBLBX/deployment/?format=json') - .basicAuth({ user: 'john', pass: 'doe' }) - .reply(404, { - body: 'A 404 error.', - }); - nock('https://kf.kobotoolbox.org') - .get('/api/v2/assets/cXecHjmbATuF6iGFmvBLBX/deployment/?format=json') - .basicAuth({ user: 'john', pass: 'doe' }) - .reply(500, { - body: 'Another error.', - }); +describe('getDeploymentInfo', () => { + const formId = 'aXecHjmbATuF6iGFmvBLBX'; + + beforeEach(() => { + testServer + .intercept({ + path: `/api/v2/assets/${formId}/deployment/`, + method: 'GET', + query: { format: 'json' }, + }) + .reply( + 200, + { asset: { name: 'Feedback Survey Test', asset_type: 'survey' } }, + { ...jsonHeaders } + ); }); + it('should get a list of deployment', async () => { - let state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - // type: 'process.env.type', - apiVersion: 'v2', - }, - }; - const nextState = await execute( - getDeploymentInfo({ formId: 'aXecHjmbATuF6iGFmvBLBX' }) - )(state).then(nextState => { - return nextState; - }); - expect(nextState.data).to.deep.eq({ - count: 2, - next: null, - previous: null, - results: [{}, {}], - }); - }).timeout(10 * 1000); - it('throws an error for a 404 response', async () => { - const state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - apiVersion: 'v2', - }, - }; - const error = await execute( - getDeploymentInfo({ formId: 'bXecHjmbATuF6iGFmvBLBX' }) - )(state).catch(error => { - return error; - }); - expect(error.message).to.eql('Request failed with status code 404'); - }); - it('throws different kind of errors', async () => { - const state = { - configuration: { - username: 'john', - password: 'doe', - baseURL: 'https://kf.kobotoolbox.org', - apiVersion: 'v2', - }, - }; - const error = await execute( - getDeploymentInfo({ formId: 'cXecHjmbATuF6iGFmvBLBX' }) - )(state).catch(error => { - return error; - }); - expect(error.message).to.eql('Request failed with status code 500'); + const state = { configuration }; + const { data } = await execute(getDeploymentInfo(formId))(state); + expect(data.asset.name).to.eql('Feedback Survey Test'); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02c8ff721..8c1c8381c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -935,9 +935,6 @@ importers: esno: specifier: ^0.16.3 version: 0.16.3 - nock: - specifier: ^12.0.3 - version: 12.0.3 rimraf: specifier: ^3.0.2 version: 3.0.2 @@ -2952,7 +2949,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.0 '@babel/types': 7.23.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -6789,6 +6786,17 @@ packages: dependencies: ms: 2.1.2 + /debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + /debug@4.4.0(supports-color@8.1.1): resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'}