diff --git a/docker/obojobo-pm2-server-src/package.json b/docker/obojobo-pm2-server-src/package.json index c4e622d341..4a18f79834 100644 --- a/docker/obojobo-pm2-server-src/package.json +++ b/docker/obojobo-pm2-server-src/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-pm2-server-app", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "description": "Reference project for deploying and customizing an Obojobo Next server", "main": "./index.js", "private": true, diff --git a/lerna.json b/lerna.json index a8cd6b935c..1609246001 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/**/*" ], - "version": "17.0.0-alpha.0", + "version": "17.0.0", "command": { "command": { "run": { diff --git a/package.json b/package.json index accd85b83d..e6e961aa09 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "obojobo-next", - "version": "16.0.1", + "version": "17.0.0", "repository": "https://github.com/ucfopen/Obojobo.git", "homepage": "https://ucfopen.github.io/Obojobo-Docs/", "license": "AGPL-3.0-only", diff --git a/packages/app/obojobo-document-engine/__tests__/Viewer/stores/assessment-state-helpers.test.js b/packages/app/obojobo-document-engine/__tests__/Viewer/stores/assessment-state-helpers.test.js index 55ba20dd92..a4cd983d7b 100644 --- a/packages/app/obojobo-document-engine/__tests__/Viewer/stores/assessment-state-helpers.test.js +++ b/packages/app/obojobo-document-engine/__tests__/Viewer/stores/assessment-state-helpers.test.js @@ -82,6 +82,44 @@ describe('AssessmentStateHelpers', () => { spy.mockRestore() }) + test('saveAttemptState returns true when good response returns', async () => { + AssessmentAPI.saveAttempt.mockResolvedValue({ status: 'ok' }) + + const mockState = { mockProp: 'mockVal' } + + const result = await AssessmentStateHelpers.saveAttemptState( + 'mockAssessmentId', + 'mockAttemptId', + mockState + ) + + expect(AssessmentAPI.saveAttempt).toHaveBeenCalledWith({ + assessmentId: 'mockAssessmentId', + attemptId: 'mockAttemptId', + draftId: 'mockDraftId', + draftContentId: 'mockDraftId', + state: mockState, + visitId: 'mockVisitId' + }) + expect(result).toBe(true) + }) + + test('saveAttemptState throws an error when bad response returns', async () => { + AssessmentAPI.saveAttempt.mockResolvedValue({ + status: 'error', + value: { message: 'mockErrorMessage' } + }) + + try { + await AssessmentStateHelpers.saveAttemptState('mockAssessmentId', 'mockAttemptId'), + { + mockProp: 'mockVal' + } + } catch (e) { + expect(e).toEqual(Error('mockErrorMessage')) + } + }) + test('resumeAttempt calls AssessmentStateHelpers.onAttemptStarted when good response returned', async () => { AssessmentAPI.resumeAttempt.mockResolvedValue({ status: 'ok', value: { questions: [] } }) const spy = jest.spyOn(AssessmentStateHelpers, 'onAttemptStarted').mockReturnValue(true) @@ -318,7 +356,7 @@ describe('AssessmentStateHelpers', () => { expect.assertions(1) }) - test('onAttemptStarted creates OboModels, updates nav context, rebuilds the nav menu, navigates to the assessment, runs the onStartAttempt trigger and fires the assessment:attemptStarted event', () => { + test('onAttemptStarted creates OboModels, updates nav context, rebuilds the nav menu, navigates to the assessment, runs the onStartAttempt trigger and fires the assessment:attemptStarted event (no question responses)', () => { const res = { value: { assessmentId: 'mockAssessmentId', @@ -340,11 +378,59 @@ describe('AssessmentStateHelpers', () => { expect(navUtilSetContextSpy).toHaveBeenCalledWith('assessment:mockAssessmentId:mockAttemptId') expect(navUtilRebuildMenuSpy).toHaveBeenCalled() expect(navUtilGoToSpy).toHaveBeenCalledWith('mockAssessmentId') + expect(QuestionStore.getOrCreateContextState).not.toHaveBeenCalled() + expect(QuestionStore.updateStateByContext).not.toHaveBeenCalled() + + navUtilSetContextSpy.mockRestore() + navUtilRebuildMenuSpy.mockRestore() + navUtilGoToSpy.mockRestore() + dispatcherTriggerSpy.mockRestore() + }) + + test('onAttemptStarted creates OboModels, updates nav context, rebuilds the nav menu, navigates to the assessment, runs the onStartAttempt trigger and fires the assessment:attemptStarted event (with question responses)', () => { + const res = { + value: { + assessmentId: 'mockAssessmentId', + attemptId: 'mockAttemptId', + questions: [{ id: 'question1' }, { id: 'question2' }], + questionResponses: [ + { questionId: 'question1', response: true }, + { questionId: 'question2', response: { ids: ['mockNodeId1'] } } + ] + } + } + + const navUtilSetContextSpy = jest.spyOn(NavUtil, 'setContext') + const navUtilRebuildMenuSpy = jest.spyOn(NavUtil, 'rebuildMenu') + const navUtilGoToSpy = jest.spyOn(NavUtil, 'goto') + const dispatcherTriggerSpy = jest.spyOn(Dispatcher, 'trigger') + const questionStoreContextStateSpy = jest + .spyOn(QuestionStore, 'getOrCreateContextState') + .mockReturnValue({ + responseMetadata: {}, + responses: {}, + viewedQuestions: {} + }) + + AssessmentStateHelpers.onAttemptStarted(res) + + const assessmentModel = OboModel.models.mockAssessmentId + expect(assessmentModel.children.at(1).children.reset).toHaveBeenCalled() + expect(assessmentModel.children.at(1).children.add).toHaveBeenCalledTimes(2) + expect(navUtilSetContextSpy).toHaveBeenCalledWith('assessment:mockAssessmentId:mockAttemptId') + expect(navUtilRebuildMenuSpy).toHaveBeenCalled() + expect(navUtilGoToSpy).toHaveBeenCalledWith('mockAssessmentId') + expect(QuestionStore.getOrCreateContextState).toHaveBeenCalledWith( + 'assessment:mockAssessmentId:mockAttemptId' + ) + // TODO: check the object specifically to make sure everything was carried over correctly? + expect(QuestionStore.updateStateByContext).toHaveBeenCalledTimes(1) navUtilSetContextSpy.mockRestore() navUtilRebuildMenuSpy.mockRestore() navUtilGoToSpy.mockRestore() dispatcherTriggerSpy.mockRestore() + questionStoreContextStateSpy.mockRestore() }) test('getUpdatedAssessmentData returns the expected object', () => { diff --git a/packages/app/obojobo-document-engine/__tests__/Viewer/stores/assessment-state-machine.test.js b/packages/app/obojobo-document-engine/__tests__/Viewer/stores/assessment-state-machine.test.js index d555ea11ed..3bfe2948df 100644 --- a/packages/app/obojobo-document-engine/__tests__/Viewer/stores/assessment-state-machine.test.js +++ b/packages/app/obojobo-document-engine/__tests__/Viewer/stores/assessment-state-machine.test.js @@ -18,6 +18,7 @@ jest.mock('../../../src/scripts/common/flux/dispatcher') const { PROMPTING_FOR_RESUME, STARTING_ATTEMPT, + SAVING_ATTEMPT, RESUMING_ATTEMPT, IN_ATTEMPT, START_ATTEMPT_FAILED, @@ -38,6 +39,7 @@ const { const { FETCH_ATTEMPT_HISTORY, START_ATTEMPT, + SAVE_ATTEMPT, IMPORT_ATTEMPT, ABANDON_IMPORT, RESUME_ATTEMPT, @@ -66,6 +68,8 @@ Pathways to test: [x] PROMPTING_FOR_RESUME(RESUME_ATTEMPT) -> RESUMING_ATTEMPT -> onError -> RESUME_ATTEMPT_FAILED [x] IN_ATTEMPT(SEND_RESPONSES) -> SENDING_RESPONSES -> onDone -> SEND_RESPONSES_SUCCESSFUL [x] IN_ATTEMPT(SEND_RESPONSES) -> SENDING_RESPONSES -> onError -> SEND_RESPONSES_FAILED +[x] IN_ATTEMPT(SAVE_ATTEMPT) -> SAVING_ATTEMPT -> onDone -> IN_ATTEMPT +[x] IN_ATTEMPT(SAVE_ATTEMPT) -> SAVING_ATTEMPT -> onError -> IN_ATTEMPT [x] START_ATTEMPT_FAILED(ACKNOWLEDGE) -> NOT_IN_ATTEMPT [x] IMPORT_ATTEMPT_FAILED(ACKNOWLEDGE) -> NOT_IN_ATTEMPT [x] RESUME_ATTEMPT_FAILED(ACKNOWLEDGE) -> PROMPTING_FOR_RESUME @@ -120,6 +124,38 @@ describe('AssessmentStateMachine', () => { restoreConsole() }) + const standardAssessmentState = { + chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }], + currentQuestion: 0 + } + + const standardStartAttemptMock = () => { + AssessmentAPI.startAttempt.mockResolvedValue({ + status: 'ok', + value: { + assessmentId: 'mockAssessmentId', + attemptId: 'mockAttemptId', + endTime: null, + questions: [ + { + id: 'question1', + type: 'ObojoboDraft.Chunks.Question', + children: [ + { + id: 'mcAssessment', + type: 'ObojoboDraft.Chunks.MCAssessment', + children: [] + } + ] + } + ], + result: null, + startTime: 'mock-start-time', + state: { ...standardAssessmentState } + } + }) + } + test('State machine does not start if already started', () => { const assessmentStoreState = { importableScores: {}, @@ -183,6 +219,7 @@ describe('AssessmentStateMachine', () => { } } } + const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) @@ -401,32 +438,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() expect(m.getCurrentState()).toBe(NOT_IN_ATTEMPT) m.send(START_ATTEMPT) @@ -799,32 +811,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() const spy = jest.spyOn(AssessmentStateHelpers, 'sendResponses').mockResolvedValue(true) expect(m.getCurrentState()).toBe(NOT_IN_ATTEMPT) @@ -862,32 +849,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() const spy = jest.spyOn(AssessmentStateHelpers, 'sendResponses').mockRejectedValue(false) expect(m.getCurrentState()).toBe(NOT_IN_ATTEMPT) @@ -908,6 +870,96 @@ describe('AssessmentStateMachine', () => { }) }) + test('IN_ATTEMPT(SAVE_ATTEMPT) -> SAVING_ATTEMPT -> onDone -> IN_ATTEMPT', done => { + const assessmentStoreState = { + assessments: { + mockAssessmentId: { + id: 'mockAssessmentId', + attemptHistoryNetworkState: 'none' + } + }, + assessmentSummaries: { + mockAssessmentId: {} + }, + importableScores: {} + } + + const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) + m.start(jest.fn()) + + standardStartAttemptMock() + const spy = jest.spyOn(AssessmentStateHelpers, 'saveAttemptState').mockResolvedValue(true) + + expect(m.getCurrentState()).toBe(NOT_IN_ATTEMPT) + m.send(START_ATTEMPT) + expect(m.getCurrentState()).toBe(STARTING_ATTEMPT) + + setTimeout(() => { + expect(m.getCurrentState()).toBe(IN_ATTEMPT) + m.send(SAVE_ATTEMPT) + expect(m.getCurrentState()).toBe(SAVING_ATTEMPT) + + setTimeout(() => { + expect(m.getCurrentState()).toBe(IN_ATTEMPT) + expect(AssessmentStateHelpers.saveAttemptState).toHaveBeenCalledWith( + 'mockAssessmentId', + 'mockAttemptId', + standardAssessmentState + ) + expect(console.error).not.toHaveBeenCalled() + + spy.mockRestore() + done() + }) + }) + }) + + test('IN_ATTEMPT(SAVE_ATTEMPT) -> SAVING_ATTEMPT -> onError -> IN_ATTEMPT', done => { + const assessmentStoreState = { + assessments: { + mockAssessmentId: { + id: 'mockAssessmentId', + attemptHistoryNetworkState: 'none' + } + }, + assessmentSummaries: { + mockAssessmentId: {} + }, + importableScores: {} + } + + const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) + m.start(jest.fn()) + + standardStartAttemptMock() + const spy = jest + .spyOn(AssessmentStateHelpers, 'saveAttemptState') + .mockRejectedValue('mockError') + + expect(m.getCurrentState()).toBe(NOT_IN_ATTEMPT) + m.send(START_ATTEMPT) + expect(m.getCurrentState()).toBe(STARTING_ATTEMPT) + + setTimeout(() => { + expect(m.getCurrentState()).toBe(IN_ATTEMPT) + m.send(SAVE_ATTEMPT) + expect(m.getCurrentState()).toBe(SAVING_ATTEMPT) + + setTimeout(() => { + expect(m.getCurrentState()).toBe(IN_ATTEMPT) + expect(AssessmentStateHelpers.saveAttemptState).toHaveBeenCalledWith( + 'mockAssessmentId', + 'mockAttemptId', + standardAssessmentState + ) + expect(console.error).toHaveBeenCalledWith('mockError') + + spy.mockRestore() + done() + }) + }) + }) + test('START_ATTEMPT_FAILED(ACKNOWLEDGE) -> NOT_IN_ATTEMPT', done => { const assessmentStoreState = { assessments: { @@ -1065,32 +1117,7 @@ describe('AssessmentStateMachine', () => { message: 'Cannot resume an attempt for a different module' } }) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() m.start(jest.fn()) @@ -1121,32 +1148,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() AssessmentAPI.endAttempt.mockResolvedValue({ status: 'ok' }) @@ -1236,32 +1238,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() AssessmentAPI.endAttempt.mockResolvedValue({ status: 'error' }) @@ -1312,32 +1289,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() const spy = jest.spyOn(AssessmentStateHelpers, 'sendResponses').mockResolvedValue(true) m.send(START_ATTEMPT) @@ -1373,32 +1325,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() const spy = jest.spyOn(AssessmentStateHelpers, 'sendResponses').mockRejectedValue(false) m.send(START_ATTEMPT) @@ -1474,32 +1401,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() AssessmentAPI.endAttempt.mockResolvedValue({ status: 'error', value: { @@ -1553,32 +1455,7 @@ describe('AssessmentStateMachine', () => { const m = new AssessmentStateMachine('mockAssessmentId', assessmentStoreState) m.start(jest.fn()) - AssessmentAPI.startAttempt.mockResolvedValue({ - status: 'ok', - value: { - assessmentId: 'mockAssessmentId', - attemptId: 'mockAttemptId', - endTime: null, - questions: [ - { - id: 'question1', - type: 'ObojoboDraft.Chunks.Question', - children: [ - { - id: 'mcAssessment', - type: 'ObojoboDraft.Chunks.MCAssessment', - children: [] - } - ] - } - ], - result: null, - startTime: 'mock-start-time', - state: { - chosen: [{ id: 'question1', type: 'ObojoboDraft.Chunks.Question' }] - } - } - }) + standardStartAttemptMock() AssessmentAPI.endAttempt.mockResolvedValue({ status: 'error', value: { diff --git a/packages/app/obojobo-document-engine/__tests__/Viewer/util/assessment-api.test.js b/packages/app/obojobo-document-engine/__tests__/Viewer/util/assessment-api.test.js index bd0f2215e7..cf3fd0b5af 100644 --- a/packages/app/obojobo-document-engine/__tests__/Viewer/util/assessment-api.test.js +++ b/packages/app/obojobo-document-engine/__tests__/Viewer/util/assessment-api.test.js @@ -37,6 +37,28 @@ describe('assessment-api', () => { }) }) + test('saveAttempt calls fetch', () => { + expect.hasAssertions() + + const mockAttemptId = 'mockAttemptId' + + const args = { + draftId: 'mockDraftId', + draftContentId: 'mockDraftContentId', + assessmentId: 'mockAssessmentId', + attemptId: mockAttemptId, + state: {}, + visitId: 'mockVisitId' + } + return AssessmentAPI.saveAttempt(args).then(result => { + // this is passed as a query parameter, not in the post body + delete args.attemptId + expect(API.post).toHaveBeenCalledWith(`/api/assessments/attempt/${mockAttemptId}/save`, args) + expect(API.processJsonResults).toHaveBeenCalled() + expect(result).toEqual(mockJsonResult) + }) + }) + test('resumeAttempt calls fetch', () => { expect.hasAssertions() const args = { attemptId: 999 } diff --git a/packages/app/obojobo-document-engine/__tests__/Viewer/util/editor-api.test.js b/packages/app/obojobo-document-engine/__tests__/Viewer/util/editor-api.test.js index 7c2984fa03..d1acfcb3a1 100644 --- a/packages/app/obojobo-document-engine/__tests__/Viewer/util/editor-api.test.js +++ b/packages/app/obojobo-document-engine/__tests__/Viewer/util/editor-api.test.js @@ -115,10 +115,33 @@ describe('EditorAPI', () => { expect(res).toBe(mockJsonResult) }) - test('copyDraft fetches with the correct args', async () => { + test('copyDraft fetches with the correct args - allow default readOnly', async () => { const res = await EditorAPI.copyDraft('draft-id', 'new-title') - expect(post).toHaveBeenCalledWith('/api/drafts/draft-id/copy', { title: 'new-title' }) + expect(post).toHaveBeenCalledWith('/api/drafts/draft-id/copy', { + title: 'new-title', + readOnly: false + }) + expect(res).toBe(mockJsonResult) + }) + + test('copyDraft fetches with the correct args - readOnly true', async () => { + const res = await EditorAPI.copyDraft('draft-id', 'new-title', true) + + expect(post).toHaveBeenCalledWith('/api/drafts/draft-id/copy', { + title: 'new-title', + readOnly: true + }) + expect(res).toBe(mockJsonResult) + }) + + test('copyDraft fetches with the correct args - readOnly false', async () => { + const res = await EditorAPI.copyDraft('draft-id', 'new-title', false) + + expect(post).toHaveBeenCalledWith('/api/drafts/draft-id/copy', { + title: 'new-title', + readOnly: false + }) expect(res).toBe(mockJsonResult) }) @@ -147,7 +170,10 @@ describe('EditorAPI', () => { expect.hasAssertions() return EditorAPI.copyDraft('mock-draft-id', 'new title').then(result => { - expect(post).toHaveBeenCalledWith('/api/drafts/mock-draft-id/copy', { title: 'new title' }) + expect(post).toHaveBeenCalledWith('/api/drafts/mock-draft-id/copy', { + title: 'new title', + readOnly: false + }) expect(result).toEqual(mockJsonResult) }) }) diff --git a/packages/app/obojobo-document-engine/__tests__/oboeditor/components/toolbars/__snapshots__/file-menu.test.js.snap b/packages/app/obojobo-document-engine/__tests__/oboeditor/components/toolbars/__snapshots__/file-menu.test.js.snap index 13f3f96345..7d62fb860d 100644 --- a/packages/app/obojobo-document-engine/__tests__/oboeditor/components/toolbars/__snapshots__/file-menu.test.js.snap +++ b/packages/app/obojobo-document-engine/__tests__/oboeditor/components/toolbars/__snapshots__/file-menu.test.js.snap @@ -2,5 +2,5 @@ exports[`File Menu File Menu node 1`] = ` "
" +CTRL+S
" `; diff --git a/packages/app/obojobo-document-engine/__tests__/oboeditor/components/toolbars/file-menu.test.js b/packages/app/obojobo-document-engine/__tests__/oboeditor/components/toolbars/file-menu.test.js index d3ba4ddb26..f4de074627 100644 --- a/packages/app/obojobo-document-engine/__tests__/oboeditor/components/toolbars/file-menu.test.js +++ b/packages/app/obojobo-document-engine/__tests__/oboeditor/components/toolbars/file-menu.test.js @@ -119,6 +119,20 @@ describe('File Menu', () => { expect(ModalUtil.show).toHaveBeenCalled() }) + test('FileMenu calls Copy (Read-Only)', () => { + const model = { + title: 'mockTitle' + } + + const component = mount() + + component + .findWhere(n => n.type() === 'button' && n.html().includes('Make a read-only copy...')) + .simulate('click') + + expect(ModalUtil.show).toHaveBeenCalled() + }) + test('FileMenu calls Download', done => { // setup const model = { @@ -243,7 +257,49 @@ describe('File Menu', () => { .instance() .copyModule('new title') .then(() => { - expect(EditorAPI.copyDraft).toHaveBeenCalledWith('mockDraftId', 'new title') + expect(EditorAPI.copyDraft).toHaveBeenCalledWith('mockDraftId', 'new title', false) + }) + }) + + test('copyModuleReadOnly calls copyDraft api', () => { + expect.hasAssertions() + const model = { + flatJSON: () => ({ children: [] }), + children: [ + { + get: () => CONTENT_NODE, + flatJSON: () => ({ children: [] }), + children: { models: [{ get: () => 'mockValue' }] } + }, + { + get: () => ASSESSMENT_NODE + } + ] + } + + const exportToJSON = jest.fn() + + const component = mount( + + ) + + EditorAPI.copyDraft.mockResolvedValueOnce({ + status: 'ok', + value: { + draftId: 'new-copy-draft-id' + } + }) + + return component + .instance() + .copyModuleReadOnly('new title') + .then(() => { + expect(EditorAPI.copyDraft).toHaveBeenCalledWith('mockDraftId', 'new title', true) }) }) diff --git a/packages/app/obojobo-document-engine/package.json b/packages/app/obojobo-document-engine/package.json index c02d9e49b6..1bf2824f2a 100644 --- a/packages/app/obojobo-document-engine/package.json +++ b/packages/app/obojobo-document-engine/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-document-engine", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "", "engines": { diff --git a/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/editor-app.scss b/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/editor-app.scss index f724c44dda..3ffca88228 100644 --- a/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/editor-app.scss +++ b/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/editor-app.scss @@ -4,8 +4,8 @@ margin-bottom: 10rem; span::selection { - background-color: highlight; /* stylelint-disable-line sh-waqar/declaration-use-variable */ - color: inherit; /* stylelint-disable-line sh-waqar/declaration-use-variable */ + background-color: highlight; /* stylelint-disable-line scale-unlimited/declaration-strict-value */ + color: inherit; /* stylelint-disable-line scale-unlimited/declaration-strict-value */ } .draft-toolbars { diff --git a/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/navigation/editor-nav.scss b/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/navigation/editor-nav.scss index c09ac7aa29..b7ff5acf63 100644 --- a/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/navigation/editor-nav.scss +++ b/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/navigation/editor-nav.scss @@ -190,7 +190,7 @@ cursor: pointer; padding: 0; margin: 0; - color: inherit; /* stylelint-disable-line sh-waqar/declaration-use-variable */ + color: inherit; /* stylelint-disable-line scale-unlimited/declaration-strict-value */ font-weight: inherit; text-align: left; } diff --git a/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/toolbars/file-menu.js b/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/toolbars/file-menu.js index ceda023c9a..e913af1451 100644 --- a/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/toolbars/file-menu.js +++ b/packages/app/obojobo-document-engine/src/scripts/oboeditor/components/toolbars/file-menu.js @@ -15,6 +15,7 @@ class FileMenu extends React.PureComponent { constructor(props) { super(props) this.copyModule = this.copyModule.bind(this) + this.copyModuleReadOnly = this.copyModuleReadOnly.bind(this) this.deleteModule = this.deleteModule.bind(this) this.buildFileSelector = this.buildFileSelector.bind(this) } @@ -27,13 +28,37 @@ class FileMenu extends React.PureComponent { }) } - copyModule(newTitle) { + copyModule(newTitle, readOnly = false) { ModalUtil.hide() - return EditorAPI.copyDraft(this.props.draftId, newTitle).then(result => { - window.open(window.location.origin + '/editor/visual/' + result.value.draftId, '_blank') + return EditorAPI.copyDraft(this.props.draftId, newTitle, readOnly).then(result => { + if (readOnly) { + const buttons = [ + { + value: 'OK', + onClick: ModalUtil.hide, + default: true + } + ] + ModalUtil.show( + + A read-only copy of this module has been created. +
+ Read-only copies can not be edited directly. +
+ View the read-only copy in the dashboard to optionally synchronize any edits made to + this module. +
+ ) + } else { + window.open(window.location.origin + '/editor/visual/' + result.value.draftId, '_blank') + } }) } + copyModuleReadOnly(newTitle) { + return this.copyModule(newTitle, true) + } + processFileContent(id, content, type) { EditorAPI.postDraft( id, @@ -101,6 +126,19 @@ class FileMenu extends React.PureComponent { /> ) }, + { + name: 'Make a read-only copy...', + type: 'action', + action: () => + ModalUtil.show( + + ) + }, { name: 'Download', type: 'sub-menu', diff --git a/packages/app/obojobo-document-engine/src/scripts/viewer/components/nav.scss b/packages/app/obojobo-document-engine/src/scripts/viewer/components/nav.scss index b8ef98663f..fc347c4bf7 100644 --- a/packages/app/obojobo-document-engine/src/scripts/viewer/components/nav.scss +++ b/packages/app/obojobo-document-engine/src/scripts/viewer/components/nav.scss @@ -270,7 +270,7 @@ cursor: pointer; padding: 0; margin: 0; - color: inherit; /* stylelint-disable-line sh-waqar/declaration-use-variable */ + color: inherit; /* stylelint-disable-line scale-unlimited/declaration-strict-value */ font-weight: inherit; text-align: left; width: 100%; diff --git a/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-state-helpers.js b/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-state-helpers.js index b975428739..2b8f69b464 100644 --- a/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-state-helpers.js +++ b/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-state-helpers.js @@ -6,6 +6,7 @@ import LTINetworkStates from './assessment-store/lti-network-states' import LTIResyncStates from './assessment-store/lti-resync-states' import QuestionStore from './question-store' import QuestionUtil from '../util/question-util' +import QuestionResponseSendStates from './question-store/question-response-send-states' import findItemsWithMaxPropValue from '../../common/util/find-items-with-max-prop-value' import injectKatexIfNeeded from '../../common/util/inject-katex-if-needed' @@ -27,6 +28,19 @@ class AssessmentAPIHelpers { }) } + static sendSaveAttemptRequest(assessmentId, attemptId, state) { + const model = OboModel.models[assessmentId] + + return AssessmentAPI.saveAttempt({ + draftId: model.getRoot().get('draftId'), + draftContentId: model.getRoot().get('contentId'), + assessmentId: model.get('id'), + attemptId, + visitId: NavStore.getState().visitId, + state + }) + } + static sendResumeAttemptRequest(assessmentId, attemptId) { const model = OboModel.models[assessmentId] @@ -85,6 +99,16 @@ class AssessmentStateHelpers { return this.onAttemptStarted(res) } + static async saveAttemptState(assessmentId, attemptId, state) { + const res = await AssessmentAPIHelpers.sendSaveAttemptRequest(assessmentId, attemptId, state) + + if (res.status !== 'ok') { + throw getErrorFromResponse(res) + } + + return true + } + static async resumeAttempt(assessmentId, attemptId) { const res = await AssessmentAPIHelpers.sendResumeAttemptRequest(assessmentId, attemptId) @@ -97,10 +121,10 @@ class AssessmentStateHelpers { return this.onAttemptStarted(res) } - static async endAttempt(assessmentId, attemptId) { + static async endAttempt(assessmentId, attemptId, state) { const model = OboModel.models[assessmentId] - const res = await AssessmentAPIHelpers.sendEndAttemptRequest(assessmentId, attemptId) + const res = await AssessmentAPIHelpers.sendEndAttemptRequest(assessmentId, attemptId, state) if (res.status !== 'ok') { throw getErrorFromResponse(res) @@ -177,6 +201,44 @@ class AssessmentStateHelpers { const assessmentId = assessment.assessmentId const assessmentModel = OboModel.models[assessmentId] + if (assessment.questionResponses && assessment.questionResponses.length > 0) { + const context = `assessment:${assessmentId}:${assessment.attemptId}` + + const contextState = QuestionStore.getOrCreateContextState(context) + + assessment.questionResponses.forEach(resp => { + // targetId is null for numeric questions but the most recently selected answer for MC questions + // we can't tell which answer was most recently chosen, just go with the last one in the responses + let targetId = null + if (resp.response.ids) { + targetId = resp.response.ids[resp.response.ids.length - 1] + } + + contextState.responseMetadata[resp.questionId] = { + // consider replacing this with the current timestamp? + time: resp.created_at, + // consider replacing this with a new state to indicate it was set when the assessment was resumed? + sendState: QuestionResponseSendStates.RECORDED, + details: { + questionId: resp.questionId, + response: resp.response, + // this is null for numeric questions but the id of the MCChoice component(s) for MC questions? + // does this even matter? + targetId, + context, + assessmentId, + attemptId: assessment.attemptId, + sendResponseImmediately: false + } + } + // set existing responses and also that those questions have been viewed + contextState.responses[resp.questionId] = resp.response + // this will skip the 'click to reveal' state for questions already answered + contextState.viewedQuestions[resp.questionId] = true + }) + QuestionStore.updateStateByContext(contextState, context) + } + this.setAssessmentQuestionBank(assessmentModel, assessment.questions) this.updateNavContextAndMenu(assessmentModel, assessment.attemptId) this.signalAttemptStarted(assessmentModel) diff --git a/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-state-machine.js b/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-state-machine.js index 73bb459a6c..6f13f2b28b 100644 --- a/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-state-machine.js +++ b/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-state-machine.js @@ -14,6 +14,7 @@ const { INIT, PROMPTING_FOR_RESUME, STARTING_ATTEMPT, + SAVING_ATTEMPT, RESUMING_ATTEMPT, IN_ATTEMPT, START_ATTEMPT_FAILED, @@ -35,6 +36,7 @@ const { const { FETCH_ATTEMPT_HISTORY, START_ATTEMPT, + SAVE_ATTEMPT, IMPORT_ATTEMPT, ABANDON_IMPORT, RESUME_ATTEMPT, @@ -260,7 +262,8 @@ class AssessmentStateMachine { }, [IN_ATTEMPT]: { on: { - [SEND_RESPONSES]: SENDING_RESPONSES + [SEND_RESPONSES]: SENDING_RESPONSES, + [SAVE_ATTEMPT]: SAVING_ATTEMPT } }, [START_ATTEMPT_FAILED]: { @@ -370,6 +373,25 @@ class AssessmentStateMachine { } ] } + }, + [SAVING_ATTEMPT]: { + invoke: { + id: 'saveAttempt', + src: async context => { + const assessmentContext = getAssessmentContext(context) + + return await AssessmentStateHelpers.saveAttemptState( + context.assessmentId, + assessmentContext.current.attemptId, + assessmentContext.current.state + ) + }, + onDone: IN_ATTEMPT, + onError: { + target: IN_ATTEMPT, + actions: [logError, updateContextWithCurrentAttemptError] + } + } } } }, diff --git a/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-store/assessment-machine-states.js b/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-store/assessment-machine-states.js index 166066ae41..939c014d2f 100644 --- a/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-store/assessment-machine-states.js +++ b/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-store/assessment-machine-states.js @@ -11,6 +11,7 @@ export default { SEND_RESPONSES_FAILED: 'sendResponsesFailed', END_ATTEMPT_FAILED: 'endAttemptFailed', STARTING_ATTEMPT: 'startingAttempt', + SAVING_ATTEMPT: 'savingAttempt', RESUMING_ATTEMPT: 'resumingAttempt', SENDING_RESPONSES: 'sendingResponses', ENDING_ATTEMPT: 'endingAttempt', diff --git a/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-store/assessment-state-actions.js b/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-store/assessment-state-actions.js index e6d48c9878..cd0905ddce 100644 --- a/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-store/assessment-state-actions.js +++ b/packages/app/obojobo-document-engine/src/scripts/viewer/stores/assessment-store/assessment-state-actions.js @@ -1,6 +1,7 @@ export default { FETCH_ATTEMPT_HISTORY: 'fetchAttemptHistory', START_ATTEMPT: 'startAttempt', + SAVE_ATTEMPT: 'saveAttempt', PROMPT_FOR_RESUME: 'promptForResume', PROMPT_FOR_IMPORT: 'promptForImport', IMPORT_ATTEMPT: 'importAttempt', diff --git a/packages/app/obojobo-document-engine/src/scripts/viewer/util/assessment-api.js b/packages/app/obojobo-document-engine/src/scripts/viewer/util/assessment-api.js index deee4a82cc..216f6d58a4 100644 --- a/packages/app/obojobo-document-engine/src/scripts/viewer/util/assessment-api.js +++ b/packages/app/obojobo-document-engine/src/scripts/viewer/util/assessment-api.js @@ -26,7 +26,15 @@ const AssessmentAPI = { visitId }).then(API.processJsonResults) }, - + saveAttempt({ draftId, draftContentId, assessmentId, attemptId, state, visitId }) { + return API.post(`/api/assessments/attempt/${attemptId}/save`, { + draftId, + draftContentId, + assessmentId, + state, + visitId + }).then(API.processJsonResults) + }, endAttempt({ attemptId, draftId, visitId }) { return API.post(`/api/assessments/attempt/${attemptId}/end`, { draftId, visitId }).then( API.processJsonResults diff --git a/packages/app/obojobo-document-engine/src/scripts/viewer/util/editor-api.js b/packages/app/obojobo-document-engine/src/scripts/viewer/util/editor-api.js index 3c29b3fb47..cb4645196c 100644 --- a/packages/app/obojobo-document-engine/src/scripts/viewer/util/editor-api.js +++ b/packages/app/obojobo-document-engine/src/scripts/viewer/util/editor-api.js @@ -40,8 +40,10 @@ const EditorAPI = { return API.delete(`/api/drafts/${draftId}`).then(API.processJsonResults) }, - copyDraft(draftId, newTitle) { - return API.post(`/api/drafts/${draftId}/copy`, { title: newTitle }).then(API.processJsonResults) + copyDraft(draftId, newTitle, readOnly = false) { + return API.post(`/api/drafts/${draftId}/copy`, { title: newTitle, readOnly }).then( + API.processJsonResults + ) }, requestEditLock(draftId, contentId) { diff --git a/packages/app/obojobo-document-json-parser/package.json b/packages/app/obojobo-document-json-parser/package.json index 1ec92e0510..3de200495e 100644 --- a/packages/app/obojobo-document-json-parser/package.json +++ b/packages/app/obojobo-document-json-parser/package.json @@ -3,11 +3,11 @@ "xml-formatter": "^2.4.0" }, "peerDependencies": { - "obojobo-document-engine": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-document-engine": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "name": "obojobo-document-json-parser", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "main": "", "scripts": { diff --git a/packages/app/obojobo-document-xml-parser/package.json b/packages/app/obojobo-document-xml-parser/package.json index b0d5602663..f9a4d34a39 100644 --- a/packages/app/obojobo-document-xml-parser/package.json +++ b/packages/app/obojobo-document-xml-parser/package.json @@ -4,10 +4,10 @@ "xml-js": "^1.0.2" }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "name": "obojobo-document-xml-parser", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "main": "xml2draft.js", "scripts": { diff --git a/packages/app/obojobo-express/__tests__/express_current_document.test.js b/packages/app/obojobo-express/__tests__/express_current_document.test.js index 11c07818a5..d6e0e4ab5b 100644 --- a/packages/app/obojobo-express/__tests__/express_current_document.test.js +++ b/packages/app/obojobo-express/__tests__/express_current_document.test.js @@ -3,8 +3,10 @@ const documentFunctions = ['setCurrentDocument', 'requireCurrentDocument', 'rese jest.mock('test_node') jest.mock('../server/models/draft') +jest.mock('obojobo-repository/server/models/drafts_metadata') const DraftDocument = oboRequire('server/models/draft') +const DraftsMetadata = require('obojobo-repository/server/models/drafts_metadata') describe('current document middleware', () => { beforeAll(() => {}) @@ -32,6 +34,7 @@ describe('current document middleware', () => { mockNext.mockClear() mockStatus.mockClear() mockJson.mockClear() + DraftsMetadata.getByDraftIdAndKey.mockClear() }) test('calls next', () => { @@ -168,4 +171,58 @@ describe('current document middleware', () => { done() }) }) + + test('requireDraftWritable rejects when no draftId is available', done => { + expect.assertions(1) + + return mockArgs.req + .requireDraftWritable() + .then(() => { + expect(false).toBe('never_called') + done() + }) + .catch(err => { + expect(err.message).toBe('DraftDocument Required') + done() + }) + }) + + test('requireDraftWritable rejects when corresponding draft is read-only', done => { + expect.assertions(3) + DraftsMetadata.getByDraftIdAndKey.mockResolvedValue(true) + + const { req } = mockArgs + req.params = { + draftId: 1 + } + + return req + .requireDraftWritable() + .then(() => { + expect(false).toBe('never_called') + done() + }) + .catch(err => { + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledTimes(1) + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledWith(1, 'read_only') + expect(err.message).toBe('Requested document is read-only') + done() + }) + }) + + test('requireDraftWritable resolves when corresponding draft is not read-only', done => { + expect.assertions(2) + DraftsMetadata.getByDraftIdAndKey.mockResolvedValue(false) + + const { req } = mockArgs + req.params = { + draftId: 1 + } + + return req.requireDraftWritable().then(() => { + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledTimes(1) + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledWith(1, 'read_only') + done() + }) + }) }) diff --git a/packages/app/obojobo-express/__tests__/express_validators.test.js b/packages/app/obojobo-express/__tests__/express_validators.test.js index 1bed32b95a..5f4900b71c 100644 --- a/packages/app/obojobo-express/__tests__/express_validators.test.js +++ b/packages/app/obojobo-express/__tests__/express_validators.test.js @@ -2,6 +2,7 @@ jest.mock('test_node') jest.mock('../server/models/user') +jest.mock('../server/logger') let mockRes let mockReq @@ -10,11 +11,14 @@ let mockUser let mockDocument const Validators = oboRequire('server/express_validators') +const logger = oboRequire('server/logger') describe('current user middleware', () => { beforeAll(() => {}) afterAll(() => {}) beforeEach(() => { + logger.error.mockClear() + mockUser = { id: 1, username: 'mock-user', @@ -163,6 +167,33 @@ describe('current user middleware', () => { }) }) + // requireDraftWritable tests + + test('requireDraftWritable resolves', () => { + mockReq.requireDraftWritable = jest.fn().mockResolvedValue() + + return expect( + Validators.requireDraftWritable(mockReq, mockRes, mockNext) + ).resolves.toBeUndefined() + }) + + test('requireDraftWritable calls next when valid', () => { + mockReq.requireDraftWritable = jest.fn().mockResolvedValue() + + return Validators.requireDraftWritable(mockReq, mockRes, mockNext).then(() => { + expect(mockNext).toHaveBeenCalled() + }) + }) + + test('requireDraftWritable calls missing when invalid', () => { + mockReq.requireDraftWritable = jest.fn().mockRejectedValue() + + return Validators.requireDraftWritable(mockReq, mockRes, mockNext).then(() => { + expect(logger.error).toHaveBeenCalledWith('User tried editing read-only module') + expect(mockRes.missing).toHaveBeenCalled() + }) + }) + // requireDraftId tests test('requireDraftId resolves in body', () => { diff --git a/packages/app/obojobo-express/__tests__/routes/editor.test.js b/packages/app/obojobo-express/__tests__/routes/editor.test.js index 337da50c03..2a62a8bd10 100644 --- a/packages/app/obojobo-express/__tests__/routes/editor.test.js +++ b/packages/app/obojobo-express/__tests__/routes/editor.test.js @@ -32,6 +32,9 @@ jest.mock('../../server/express_current_document', () => (req, res, next) => { req.currentDocument = mockCurrentDocument return Promise.resolve(mockCurrentDocument) } + req.requireDraftWritable = () => { + return Promise.resolve(true) + } next() }) diff --git a/packages/app/obojobo-express/package.json b/packages/app/obojobo-express/package.json index 166ef57cbf..5c41728e7c 100644 --- a/packages/app/obojobo-express/package.json +++ b/packages/app/obojobo-express/package.json @@ -1,7 +1,7 @@ { "name": "obojobo-express", "license": "AGPL-3.0-only", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "repository": "https://github.com/ucfopen/Obojobo.git", "homepage": "https://ucfopen.github.io/Obojobo-Docs/", "description": "Obojobo express server middleware.", @@ -64,17 +64,18 @@ "pg-promise": "^10.10.1", "react-transition-group": "^4.4.1", "serve-favicon": "~2.5.0", - "sharp": "^0.30.5", + "sharp": "^0.32.6", "trianglify": "^4.1.1", "uuid": "^8.3.2" }, "peerDependencies": { - "obojobo-document-engine": "^17.0.0-alpha.0", - "obojobo-document-xml-parser": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-document-engine": "^17.0.0", + "obojobo-document-xml-parser": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "devDependencies": { "@svgr/webpack": "^5.5.0", + "autoprefixer": "^10.4.14", "babel-loader": "^8.2.2", "babel-polyfill": "^6.26.0", "bluebird": "^3.7.2", @@ -83,7 +84,7 @@ "mini-css-extract-plugin": "^1.4.0", "node-sass": "^8.0.0", "oauth-signature": "^1.5.0", - "postcss-loader": "^5.2.0", + "postcss-loader": "^7.3.3", "sass-loader": "^11.0.1", "style-loader": "^2.0.0", "supertest": "^6.1.3", diff --git a/packages/app/obojobo-express/server/express_current_document.js b/packages/app/obojobo-express/server/express_current_document.js index 1a9c7e558b..0048c11ad6 100644 --- a/packages/app/obojobo-express/server/express_current_document.js +++ b/packages/app/obojobo-express/server/express_current_document.js @@ -1,6 +1,19 @@ const DraftDocument = oboRequire('server/models/draft') +const DraftsMetadata = require('obojobo-repository/server/models/drafts_metadata') const logger = oboRequire('server/logger') +const _getDraftId = req => { + // Figure out where the draftId is in this request + if (req.params && req.params.draftId) { + return req.params.draftId + } else if (req.body && req.body.draftId) { + return req.body.draftId + } else if (req.body && req.body.event && req.body.event.draft_id) { + return req.body.event.draft_id + } + return null +} + const setCurrentDocument = (req, draftDocument) => { if (!(draftDocument instanceof DraftDocument)) { throw new Error('Invalid DraftDocument for Current draftDocument') @@ -12,20 +25,26 @@ const resetCurrentDocument = req => { req.currentDocument = null } +const requireDraftWritable = req => { + const draftId = _getDraftId(req) + if (draftId === null) { + logger.warn('No Session or Current DraftDocument?', req.currentDocument) + return Promise.reject(new Error('DraftDocument Required')) + } + + return DraftsMetadata.getByDraftIdAndKey(draftId, 'read_only').then(readOnly => { + if (readOnly) return Promise.reject(new Error('Requested document is read-only')) + return Promise.resolve() + }) +} + const requireCurrentDocument = req => { if (req.currentDocument) { return Promise.resolve(req.currentDocument) } // Figure out where the draftId is in this request - let draftId = null - if (req.params && req.params.draftId) { - draftId = req.params.draftId - } else if (req.body && req.body.draftId) { - draftId = req.body.draftId - } else if (req.body && req.body.event && req.body.event.draft_id) { - draftId = req.body.event.draft_id - } + const draftId = _getDraftId(req) if (draftId === null) { logger.warn('No Session or Current DraftDocument?', req.currentDocument) @@ -42,5 +61,6 @@ module.exports = (req, res, next) => { req.setCurrentDocument = setCurrentDocument.bind(this, req) req.requireCurrentDocument = requireCurrentDocument.bind(this, req) req.resetCurrentDocument = resetCurrentDocument.bind(this, req) + req.requireDraftWritable = requireDraftWritable.bind(this, req) next() } diff --git a/packages/app/obojobo-express/server/express_validators.js b/packages/app/obojobo-express/server/express_validators.js index 9b84e8c613..0baaf0bc96 100644 --- a/packages/app/obojobo-express/server/express_validators.js +++ b/packages/app/obojobo-express/server/express_validators.js @@ -41,6 +41,17 @@ exports.requireCurrentVisit = (req, res, next) => requireAndValidateReqMethod(req, res, next, 'getCurrentVisitFromRequest', 'currentVisit') exports.requireCurrentDocument = (req, res, next) => requireAndValidateReqMethod(req, res, next, 'requireCurrentDocument', 'currentDocument') +exports.requireDraftWritable = (req, res, next) => { + return req + .requireDraftWritable() + .then(() => { + next() + }) + .catch(() => { + logger.error('User tried editing read-only module') + res.missing() + }) +} exports.getCurrentUser = (req, res, next) => { return req.getCurrentUser().then(user => { diff --git a/packages/app/obojobo-express/server/routes/editor.js b/packages/app/obojobo-express/server/routes/editor.js index 65ee36a33d..0bbd8d397b 100644 --- a/packages/app/obojobo-express/server/routes/editor.js +++ b/packages/app/obojobo-express/server/routes/editor.js @@ -9,9 +9,13 @@ const { dbLockDurationMinutes } = generalConfig.editLocks const { assetForEnv, webpackAssetPath } = oboRequire('server/asset_resolver') -const { check, requireCanViewEditor, requireCurrentDocument, checkValidationRules } = oboRequire( - 'server/express_validators' -) +const { + check, + requireCanViewEditor, + requireCurrentDocument, + checkValidationRules, + requireDraftWritable +} = oboRequire('server/express_validators') const allowedUploadTypes = mediaConfig.allowedMimeTypesRegex .split('|') .map(i => `.${i}`) @@ -23,6 +27,7 @@ router .route('/visual/:draftId/:page?') .get([ requireCanViewEditor, + requireDraftWritable, requireCurrentDocument, check('revision_id') .optional() diff --git a/packages/app/obojobo-module-selector/.stylelintrc b/packages/app/obojobo-module-selector/.stylelintrc index 3b392761f8..51c9e8696d 100644 --- a/packages/app/obojobo-module-selector/.stylelintrc +++ b/packages/app/obojobo-module-selector/.stylelintrc @@ -2,7 +2,7 @@ "extends": "stylelint-config-obojobo", "rules": { "unit-disallowed-list": "warning", - "sh-waqar/declaration-use-variable": "warning", + "scale-unlimited/declaration-strict-value": "warning", "no-descending-specificity": null } } diff --git a/packages/app/obojobo-module-selector/package.json b/packages/app/obojobo-module-selector/package.json index 231edc463d..384a980e81 100644 --- a/packages/app/obojobo-module-selector/package.json +++ b/packages/app/obojobo-module-selector/package.json @@ -1,7 +1,7 @@ { "name": "obojobo-module-selector", "license": "AGPL-3.0-only", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "repository": "https://github.com/ucfopen/Obojobo.git", "homepage": "https://ucfopen.github.io/Obojobo-Docs/", "description": "Obojobo package responsible for selecting which module you use in a course.", @@ -29,8 +29,8 @@ "express": "~4.18.2" }, "peerDependencies": { - "obojobo-express": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0", - "obojobo-repository": "^17.0.0-alpha.0" + "obojobo-express": "^17.0.0", + "obojobo-lib-utils": "^17.0.0", + "obojobo-repository": "^17.0.0" } } diff --git a/packages/app/obojobo-repository/.stylelintrc b/packages/app/obojobo-repository/.stylelintrc index c9ad9ab7e8..bf338315f0 100644 --- a/packages/app/obojobo-repository/.stylelintrc +++ b/packages/app/obojobo-repository/.stylelintrc @@ -2,6 +2,6 @@ "extends": "stylelint-config-obojobo", "rules": { "unit-disallowed-list": "warning", - "sh-waqar/declaration-use-variable": "warning" + "scale-unlimited/declaration-strict-value": "warning" } } diff --git a/packages/app/obojobo-repository/package.json b/packages/app/obojobo-repository/package.json index 2c72365f38..2215c3ecc3 100644 --- a/packages/app/obojobo-repository/package.json +++ b/packages/app/obojobo-repository/package.json @@ -1,7 +1,7 @@ { "name": "obojobo-repository", "license": "AGPL-3.0-only", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "repository": "https://github.com/ucfopen/Obojobo.git", "homepage": "https://ucfopen.github.io/Obojobo-Docs/", "description": "Obojobo express server middleware.", @@ -46,8 +46,8 @@ "use-debounce": "^7.0.0" }, "peerDependencies": { - "obojobo-express": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-express": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "jest": { "setupFilesAfterEnv": [ diff --git a/packages/app/obojobo-repository/server/models/draft_permissions.js b/packages/app/obojobo-repository/server/models/draft_permissions.js index 7c631f3994..5bc6c016bc 100644 --- a/packages/app/obojobo-repository/server/models/draft_permissions.js +++ b/packages/app/obojobo-repository/server/models/draft_permissions.js @@ -95,14 +95,28 @@ class DraftPermissions { } // returns a boolean - static async userHasPermissionToCopy(userId, draftId) { + static async userHasPermissionToPreview(user, draftId) { try { const results = await Promise.all([ DraftPermissions.draftIsPublic(draftId), - DraftPermissions.getUserAccessLevelToDraft(userId, draftId) + DraftPermissions.getUserAccessLevelToDraft(user.id, draftId) ]) - return results[0] === true || results[1] !== null + return user.perms.includes('canPreviewDrafts') && (results[0] === true || results[1] !== null) + } catch (error) { + throw logger.logError('Error userHasPermissionToPreview', error) + } + } + + // returns a boolean + static async userHasPermissionToCopy(user, draftId) { + try { + const results = await Promise.all([ + DraftPermissions.draftIsPublic(draftId), + DraftPermissions.getUserAccessLevelToDraft(user.id, draftId) + ]) + + return user.perms.includes('canCreateDrafts') && (results[0] === true || results[1] !== null) } catch (error) { throw logger.logError('Error userHasPermissionToCopy', error) } diff --git a/packages/app/obojobo-repository/server/models/draft_permissions.test.js b/packages/app/obojobo-repository/server/models/draft_permissions.test.js index d4d6e5e07e..ba36a1cd8a 100644 --- a/packages/app/obojobo-repository/server/models/draft_permissions.test.js +++ b/packages/app/obojobo-repository/server/models/draft_permissions.test.js @@ -274,21 +274,101 @@ describe('DraftPermissions Model', () => { }) }) + // userHasPermissionToPreview tests + + test.each` + mockUserPerms | draftIsPublic | getUserAccessLevelToDraft | expected + ${['canPreviewDrafts']} | ${null} | ${'mock-db-result'} | ${true} + ${['canPreviewDrafts']} | ${'mock-db-result'} | ${null} | ${true} + ${['canPreviewDrafts']} | ${null} | ${null} | ${false} + ${['canPreviewDrafts']} | ${'mock-db-result'} | ${'mock-db-result'} | ${true} + ${[]} | ${null} | ${'mock-db-result'} | ${false} + ${[]} | ${'mock-db-result'} | ${null} | ${false} + ${[]} | ${null} | ${null} | ${false} + ${[]} | ${'mock-db-result'} | ${'mock-db-result'} | ${false} + `( + 'userHasPermissionToPreview returns $expected with mockUserPerms $mockUserPerms, db results $draftIsPublic and $getUserAccessLevelToDraft', + ({ mockUserPerms, draftIsPublic, getUserAccessLevelToDraft, expected }) => { + expect.hasAssertions() + + const mockUser = { + id: 'MUID', + perms: mockUserPerms + } + + db.oneOrNone.mockResolvedValueOnce(draftIsPublic) // draftIsPublic call + db.oneOrNone.mockResolvedValueOnce(getUserAccessLevelToDraft) // getUserAccessLevelToDraft call + + return DraftPermissions.userHasPermissionToPreview(mockUser, 'MDID').then(hasPermissions => { + expect(hasPermissions).toBe(expected) + + const [isPublicQuery, isPublicOptions] = db.oneOrNone.mock.calls[0] + expect(isPublicQuery).toContain('SELECT') + expect(isPublicQuery).toContain('FROM repository_map_drafts_to_collections') + expect(isPublicOptions).toEqual({ + draftId: 'MDID', + publicLibCollectionId: '00000000-0000-0000-0000-000000000000' + }) + + const [hasPermsQuery, hasPermsOptions] = db.oneOrNone.mock.calls[1] + expect(hasPermsQuery).toContain('SELECT') + expect(hasPermsQuery).toContain('FROM repository_map_user_to_draft') + expect(hasPermsOptions).toEqual({ + draftId: 'MDID', + userId: 'MUID' + }) + }) + } + ) + + test('userHasPermissionToPreview throws and logs error, getUserAccessLeveltoDraft failure', () => { + expect.hasAssertions() + db.oneOrNone.mockResolvedValueOnce('mock-db-results') // draftIsPublic call + db.oneOrNone.mockRejectedValueOnce(mockError) // getUserAccessLevelToDraft call + + return DraftPermissions.userHasPermissionToPreview('MUID', 'MDID').catch(error => { + expect(logger.logError).toHaveBeenCalledWith('Error userHasPermissionToPreview', mockError) + expect(error).toBe(mockError) + }) + }) + + test('userHasPermissionToPreview throws and logs error, draftIsPublic failure', () => { + expect.hasAssertions() + db.oneOrNone.mockRejectedValueOnce(mockError) // draftIsPublic call + db.oneOrNone.mockResolvedValueOnce('mock-db-results') // getUserAccessLevelToDraft call + + return DraftPermissions.userHasPermissionToPreview('MUID', 'MDID').catch(error => { + expect(logger.logError).toHaveBeenCalledWith('Error userHasPermissionToPreview', mockError) + expect(error).toBe(mockError) + }) + }) + + // userHasPermissionToCopy tests + test.each` - draftIsPublic | getUserAccessLevelToDraft | expected - ${null} | ${'mock-db-result'} | ${true} - ${'mock-db-result'} | ${null} | ${true} - ${null} | ${null} | ${false} - ${'mock-db-result'} | ${'mock-db-result'} | ${true} + mockUserPerms | draftIsPublic | getUserAccessLevelToDraft | expected + ${['canCreateDrafts']} | ${null} | ${'mock-db-result'} | ${true} + ${['canCreateDrafts']} | ${'mock-db-result'} | ${null} | ${true} + ${['canCreateDrafts']} | ${null} | ${null} | ${false} + ${['canCreateDrafts']} | ${'mock-db-result'} | ${'mock-db-result'} | ${true} + ${[]} | ${null} | ${'mock-db-result'} | ${false} + ${[]} | ${'mock-db-result'} | ${null} | ${false} + ${[]} | ${null} | ${null} | ${false} + ${[]} | ${'mock-db-result'} | ${'mock-db-result'} | ${false} `( - 'userHasPermissionToCopy returns $expected with db results $draftIsPublic and $getUserAccessLevelToDraft', - ({ draftIsPublic, getUserAccessLevelToDraft, expected }) => { + 'userHasPermissionToCopy returns $expected with mockUserPerms $mockUserPerms, db results $draftIsPublic and $getUserAccessLevelToDraft', + ({ mockUserPerms, draftIsPublic, getUserAccessLevelToDraft, expected }) => { expect.hasAssertions() + const mockUser = { + id: 'MUID', + perms: mockUserPerms + } + db.oneOrNone.mockResolvedValueOnce(draftIsPublic) // draftIsPublic call db.oneOrNone.mockResolvedValueOnce(getUserAccessLevelToDraft) // getUserAccessLevelToDraft call - return DraftPermissions.userHasPermissionToCopy('MUID', 'MDID').then(hasPermissions => { + return DraftPermissions.userHasPermissionToCopy(mockUser, 'MDID').then(hasPermissions => { expect(hasPermissions).toBe(expected) const [isPublicQuery, isPublicOptions] = db.oneOrNone.mock.calls[0] @@ -310,7 +390,7 @@ describe('DraftPermissions Model', () => { } ) - test('userHasPermissionToCopy throws and logs error', () => { + test('userHasPermissionToCopy throws and logs error, getUserAccessLeveltoDraft failure', () => { expect.hasAssertions() db.oneOrNone.mockResolvedValueOnce('mock-db-results') // draftIsPublic call db.oneOrNone.mockRejectedValueOnce(mockError) // getUserAccessLevelToDraft call @@ -321,7 +401,7 @@ describe('DraftPermissions Model', () => { }) }) - test('userHasPermissionToCopy throws and logs error', () => { + test('userHasPermissionToCopy throws and logs error, draftIsPublic failure', () => { expect.hasAssertions() db.oneOrNone.mockRejectedValueOnce(mockError) // draftIsPublic call db.oneOrNone.mockResolvedValueOnce('mock-db-results') // getUserAccessLevelToDraft call diff --git a/packages/app/obojobo-repository/server/models/draft_summary.js b/packages/app/obojobo-repository/server/models/draft_summary.js index 273b12ab25..ec67cb35b3 100644 --- a/packages/app/obojobo-repository/server/models/draft_summary.js +++ b/packages/app/obojobo-repository/server/models/draft_summary.js @@ -18,11 +18,17 @@ const buildQueryWhere = ( count(drafts_content.id) OVER wnd as revision_count, COALESCE(last_value(drafts_content.content->'content'->>'title') OVER wnd, '') as "title", drafts.user_id AS user_id, + drafts_metadata.value AS read_only, ${selectSQL} 'visual' AS editor FROM drafts JOIN drafts_content ON drafts_content.draft_id = drafts.id + LEFT JOIN drafts_metadata + ON ( + drafts_metadata.draft_id = drafts.id + AND drafts_metadata."key" = 'read_only' + ) ${joinSQL} WHERE drafts.deleted = ${deleted} AND ${whereSQL} @@ -49,7 +55,8 @@ class DraftSummary { id, first_name, last_name, - access_level + access_level, + read_only }) { this.draftId = draft_id this.title = title @@ -61,6 +68,7 @@ class DraftSummary { this.editor = editor this.json = content this.revisionId = id + this.readOnly = read_only if (first_name && last_name) this.userFullName = `${first_name} ${last_name}` if (revision_count) this.revisionCount = Number(revision_count) @@ -84,6 +92,22 @@ class DraftSummary { }) } + static fetchByIdMoreRecentThan(id, targetTime) { + return db + .oneOrNone( + buildQueryWhere(`drafts.id = $[id] + AND drafts_content.created_at > $[targetTime]`), + { id, targetTime } + ) + .then(res => { + if (res) return DraftSummary.resultsToObjects(res) + return null + }) + .catch(error => { + throw logger.logError('DraftSummary fetchByIdMoreRecentThan Error', error) + }) + } + static fetchByUserId(userId) { return DraftSummary.fetchAndJoinWhere( `repository_map_user_to_draft.access_level AS access_level,`, diff --git a/packages/app/obojobo-repository/server/models/draft_summary.test.js b/packages/app/obojobo-repository/server/models/draft_summary.test.js index f311179aca..c3edad9281 100644 --- a/packages/app/obojobo-repository/server/models/draft_summary.test.js +++ b/packages/app/obojobo-repository/server/models/draft_summary.test.js @@ -102,11 +102,17 @@ describe('DraftSummary Model', () => { count(drafts_content.id) OVER wnd as revision_count, COALESCE(last_value(drafts_content.content->'content'->>'title') OVER wnd, '') as "title", drafts.user_id AS user_id, + drafts_metadata.value AS read_only, ${selectSQL} 'visual' AS editor FROM drafts JOIN drafts_content ON drafts_content.draft_id = drafts.id + LEFT JOIN drafts_metadata + ON ( + drafts_metadata.draft_id = drafts.id + AND drafts_metadata."key" = 'read_only' + ) ${joinSQL} WHERE drafts.deleted = ${deleted} AND ${whereSQL} @@ -214,6 +220,58 @@ describe('DraftSummary Model', () => { }) }) + test('fetchByIdMoreRecentThan generates the correct query and returns DraftSummary objects correctly', () => { + db.oneOrNone = jest.fn() + db.oneOrNone.mockResolvedValueOnce(mockRawDraftSummaries) + + const mockTargetTime = '1999-01-01 01:00:00.000000+00' + + return DraftSummary.fetchByIdMoreRecentThan('mockDraftId', mockTargetTime).then(summaries => { + const query = queryBuilder(`drafts.id = $[id] + AND drafts_content.created_at > $[targetTime]`) + const [actualQuery, options] = db.oneOrNone.mock.calls[0] + expectQueryToMatch(query, actualQuery) + expect(options).toEqual({ id: 'mockDraftId', targetTime: '1999-01-01 01:00:00.000000+00' }) + expect(summaries.length).toBe(2) + expectIsMockSummary(summaries[0]) + expectIsMockSummary(summaries[1]) + }) + }) + + test('fetchByIdMoreRecentThan generates the correct query and returns for zero results correctly', () => { + db.oneOrNone = jest.fn() + db.oneOrNone.mockResolvedValueOnce() + + const mockTargetTime = '1999-01-01 01:00:00.000000+00' + + return DraftSummary.fetchByIdMoreRecentThan('mockDraftId', mockTargetTime).then(summaries => { + const query = queryBuilder(`drafts.id = $[id] + AND drafts_content.created_at > $[targetTime]`) + const [actualQuery, options] = db.oneOrNone.mock.calls[0] + expectQueryToMatch(query, actualQuery) + expect(options).toEqual({ id: 'mockDraftId', targetTime: '1999-01-01 01:00:00.000000+00' }) + expect(summaries).toBe(null) + }) + }) + + test('fetchByIdMoreRecentThan logs database errors', () => { + expect.hasAssertions() + const mockError = new Error('database error') + logger.logError = jest.fn().mockReturnValueOnce(mockError) + db.oneOrNone.mockRejectedValueOnce(mockError) + + return DraftSummary.fetchByIdMoreRecentThan( + 'mockDraftId', + '1999-01-01 01:00:00.000000+00' + ).catch(err => { + expect(logger.logError).toHaveBeenCalledWith( + 'DraftSummary fetchByIdMoreRecentThan Error', + mockError + ) + expect(err).toBe(mockError) + }) + }) + test('fetchByUserId generates the correct query and returns a DraftSummary object', () => { expect.hasAssertions() diff --git a/packages/app/obojobo-repository/server/models/drafts_metadata.js b/packages/app/obojobo-repository/server/models/drafts_metadata.js index 3d319ee2e1..3b31adc8c0 100644 --- a/packages/app/obojobo-repository/server/models/drafts_metadata.js +++ b/packages/app/obojobo-repository/server/models/drafts_metadata.js @@ -10,6 +10,66 @@ class DraftsMetadata { this.value = value } + static getByDraftId(draftId) { + return db + .manyOrNone( + ` + SELECT * + FROM drafts_metadata + WHERE draft_id = $[draftId] + `, + { draftId } + ) + .then(res => { + if (res) return res.map(r => new DraftsMetadata(r)) + return null + }) + .catch(error => { + logger.logError('DraftMetadata getByDraftId error', error) + throw error + }) + } + + static getByDraftIdAndKey(draftId, key) { + return db + .oneOrNone( + ` + SELECT * + FROM drafts_metadata + WHERE draft_id = $[draftId] AND key = $[key] + `, + { draftId, key } + ) + .then(res => { + if (res) return new DraftsMetadata(res) + return null + }) + .catch(error => { + logger.logError('DraftMetadata getByDraftIdAndKey error', error) + throw error + }) + } + + static getByKeyAndValue(key, value) { + return db + .manyOrNone( + ` + SELECT * + FROM drafts_metadata + WHERE key = $[key] AND value = $[value] + `, + { key, value } + ) + .then(res => { + if (res) return res.map(r => new DraftsMetadata(r)) + return null + }) + .catch(error => { + logger.logError('DraftMetadata getByKeyAndValue error', error) + throw error + }) + } + saveOrCreate() { return db .none( diff --git a/packages/app/obojobo-repository/server/models/drafts_metadata.test.js b/packages/app/obojobo-repository/server/models/drafts_metadata.test.js index 2d52e0f274..ce67bbba87 100644 --- a/packages/app/obojobo-repository/server/models/drafts_metadata.test.js +++ b/packages/app/obojobo-repository/server/models/drafts_metadata.test.js @@ -11,12 +11,26 @@ describe('DraftsMetadata Model', () => { value: 'value' } - const expectMatchesRawMock = draftMetadata => { - expect(draftMetadata.draftId).toBe('mockDraftId') + const mockRawDraftMetadataMultiple = [ + { + draft_id: 'mockDraftId1', + key: 'key', + value: 'value1' + }, + { + draft_id: 'mockDraftId2', + key: 'key', + value: 'value2' + } + ] + + const expectMatchesRawMock = (draftMetadata, number = '') => { + expect(draftMetadata.draftId).toBe(`mockDraftId${number}`) + // these would have dates from the database, but it doesn't really matter here expect(draftMetadata.createdAt).toBeUndefined() expect(draftMetadata.updatedAt).toBeUndefined() expect(draftMetadata.key).toBe('key') - expect(draftMetadata.value).toBe('value') + expect(draftMetadata.value).toBe(`value${number}`) } beforeEach(() => { @@ -76,4 +90,171 @@ describe('DraftsMetadata Model', () => { expect(err).toBe('Error loading DraftsMetadata by query') }) }) + + // getByDraftId tests + + test('getByDraftId generates the correct query and returns DraftsMetadata objects', () => { + expect.hasAssertions() + + db.manyOrNone = jest.fn() + db.manyOrNone.mockResolvedValueOnce(mockRawDraftMetadataMultiple) + + // Trying to match whitespace with the query that's actually running + const query = ` + SELECT * + FROM drafts_metadata + WHERE draft_id = $[draftId] + ` + + return DraftsMetadata.getByDraftId('mockDraftId').then(res => { + expect(db.manyOrNone).toHaveBeenCalledWith(query, { draftId: 'mockDraftId' }) + expect(res.length).toBe(2) + expectMatchesRawMock(res[0], 1) + expectMatchesRawMock(res[1], 2) + }) + }) + + test('getByDraftId generates the correct query and returns for zero results correctly', () => { + expect.hasAssertions() + + db.manyOrNone = jest.fn() + db.manyOrNone.mockResolvedValueOnce() + + // Trying to match whitespace with the query that's actually running + const query = ` + SELECT * + FROM drafts_metadata + WHERE draft_id = $[draftId] + ` + + return DraftsMetadata.getByDraftId('mockDraftId').then(res => { + expect(db.manyOrNone).toHaveBeenCalledWith(query, { draftId: 'mockDraftId' }) + expect(res).toBe(null) + }) + }) + + test('getByDraftId logs database errors', () => { + expect.hasAssertions() + const mockError = new Error('database error') + logger.logError = jest.fn().mockReturnValueOnce(mockError) + db.manyOrNone.mockRejectedValueOnce(mockError) + + return DraftsMetadata.getByDraftId('metaKey', 'metaValue').catch(err => { + expect(logger.logError).toHaveBeenCalledWith('DraftMetadata getByDraftId error', mockError) + expect(err).toBe(mockError) + }) + }) + + // getByDraftIdAndKey tests + + test('getByDraftIdAndKey generates the correct query and returns a DraftsMetadata object', () => { + expect.hasAssertions() + + db.oneOrNone = jest.fn() + db.oneOrNone.mockResolvedValueOnce(mockRawDraftsMetadata) + + // Trying to match whitespace with the query that's actually running + const query = ` + SELECT * + FROM drafts_metadata + WHERE draft_id = $[draftId] AND key = $[key] + ` + + return DraftsMetadata.getByDraftIdAndKey('draftId', 'metaKey').then(res => { + expect(db.oneOrNone).toHaveBeenCalledWith(query, { draftId: 'draftId', key: 'metaKey' }) + expect(res).toBeInstanceOf(DraftsMetadata) + expectMatchesRawMock(res) + }) + }) + + test('getByDraftIdAndKey generates the correct query and returns for zero results correctly', () => { + expect.hasAssertions() + + db.oneOrNone = jest.fn() + db.oneOrNone.mockResolvedValueOnce() + + // Trying to match whitespace with the query that's actually running + const query = ` + SELECT * + FROM drafts_metadata + WHERE draft_id = $[draftId] AND key = $[key] + ` + + return DraftsMetadata.getByDraftIdAndKey('draftId', 'metaKey').then(res => { + expect(db.oneOrNone).toHaveBeenCalledWith(query, { draftId: 'draftId', key: 'metaKey' }) + expect(res).toBe(null) + }) + }) + + test('getByDraftIdAndKey logs database errors', () => { + expect.hasAssertions() + const mockError = new Error('database error') + logger.logError = jest.fn().mockReturnValueOnce(mockError) + db.oneOrNone.mockRejectedValueOnce(mockError) + + return DraftsMetadata.getByDraftIdAndKey('draftId', 'metaKey').catch(err => { + expect(logger.logError).toHaveBeenCalledWith( + 'DraftMetadata getByDraftIdAndKey error', + mockError + ) + expect(err).toBe(mockError) + }) + }) + + // getByKeyAndValue tests + + test('getByKeyAndValue generates the correct query and returns DraftsMetadata objects', () => { + expect.hasAssertions() + + db.manyOrNone = jest.fn() + db.manyOrNone.mockResolvedValueOnce(mockRawDraftMetadataMultiple) + + // Trying to match whitespace with the query that's actually running + const query = ` + SELECT * + FROM drafts_metadata + WHERE key = $[key] AND value = $[value] + ` + + return DraftsMetadata.getByKeyAndValue('metaKey', 'metaValue').then(res => { + expect(db.manyOrNone).toHaveBeenCalledWith(query, { key: 'metaKey', value: 'metaValue' }) + expect(res.length).toBe(2) + expectMatchesRawMock(res[0], 1) + expectMatchesRawMock(res[1], 2) + }) + }) + + test('getByKeyAndValue generates the correct query and returns for zero results correctly', () => { + expect.hasAssertions() + + db.manyOrNone = jest.fn() + db.manyOrNone.mockResolvedValueOnce() + + // Trying to match whitespace with the query that's actually running + const query = ` + SELECT * + FROM drafts_metadata + WHERE key = $[key] AND value = $[value] + ` + + return DraftsMetadata.getByKeyAndValue('metaKey', 'metaValue').then(res => { + expect(db.manyOrNone).toHaveBeenCalledWith(query, { key: 'metaKey', value: 'metaValue' }) + expect(res).toBe(null) + }) + }) + + test('getByKeyAndValue logs database errors', () => { + expect.hasAssertions() + const mockError = new Error('database error') + logger.logError = jest.fn().mockReturnValueOnce(mockError) + db.manyOrNone.mockRejectedValueOnce(mockError) + + return DraftsMetadata.getByKeyAndValue('metaKey', 'metaValue').catch(err => { + expect(logger.logError).toHaveBeenCalledWith( + 'DraftMetadata getByKeyAndValue error', + mockError + ) + expect(err).toBe(mockError) + }) + }) }) diff --git a/packages/app/obojobo-repository/server/routes/api.js b/packages/app/obojobo-repository/server/routes/api.js index aa6f65836d..97cf9a433b 100644 --- a/packages/app/obojobo-repository/server/routes/api.js +++ b/packages/app/obojobo-repository/server/routes/api.js @@ -22,7 +22,12 @@ const { fetchAllCollectionsForDraft } = require('../services/collections') const { getUserModuleCount } = require('../services/count') const publicLibCollectionId = require('../../shared/publicLibCollectionId') -const { levelName, levelNumber, FULL } = require('../../../obojobo-express/server/constants') +const { + levelName, + levelNumber, + FULL, + PARTIAL +} = require('../../../obojobo-express/server/constants') const uuid = require('uuid').v4 @@ -181,7 +186,9 @@ router const userId = req.currentUser.id const draftId = req.currentDocument.draftId - const canCopy = await DraftPermissions.userHasPermissionToCopy(userId, draftId) + const readOnly = req.body.readOnly + + const canCopy = await DraftPermissions.userHasPermissionToCopy(req.currentUser, draftId) if (!canCopy) { res.notAuthorized('Current user has no permissions to copy this draft') return @@ -222,14 +229,25 @@ router // const newDraft = await Draft.createWithContent(userId, draftObject) const newDraft = await Draft.createWithContent(userId, newDraftObject) - const draftMetadata = new DraftsMetadata({ + const copiedDraftMetadata = new DraftsMetadata({ draft_id: newDraft.id, key: 'copied', value: draftId }) + let readOnlyDraftMetadata = null + + if (readOnly) { + readOnlyDraftMetadata = new DraftsMetadata({ + draft_id: newDraft.id, + key: 'read_only', + value: true + }) + } + await Promise.all([ - draftMetadata.saveOrCreate(), + copiedDraftMetadata.saveOrCreate(), + readOnlyDraftMetadata ? readOnlyDraftMetadata.saveOrCreate() : Promise.resolve(), insertEvent({ actorTime: 'now()', action: 'draft:copy', @@ -251,6 +269,59 @@ router } }) +// check for any changes to the draft's original +router + .route('/drafts/:draftId/sync') + .get([requireCurrentUser, requireCurrentDocument, requireCanCreateDrafts]) + .get((req, res) => { + DraftsMetadata.getByDraftIdAndKey(req.currentDocument.draftId, 'copied') + .then(md => { + return DraftSummary.fetchByIdMoreRecentThan(md.value, md.updatedAt) + }) + .then(mostRecentOriginalRevision => { + res.success(mostRecentOriginalRevision) + }) + .catch(e => { + res.unexpected(e) + }) + }) + +// replace a read-only draft's draft_content with its parent's most recent draft_content +router + .route('/drafts/:draftId/sync') + .patch([requireCurrentUser, requireCurrentDocument, requireCanCreateDrafts]) + .patch((req, res) => { + let copyMeta = null + + DraftsMetadata.getByDraftIdAndKey(req.currentDocument.draftId, 'copied') + .then(async md => { + const userAccess = await DraftPermissions.getUserAccessLevelToDraft( + req.currentUser.id, + req.currentDocument.draftId + ) + + if (!(userAccess === FULL || userAccess === PARTIAL)) { + res.notAuthorized('Current User does not have permission to share this draft') + return + } + + copyMeta = md + // use original draft ID from the copy metadata + const oldDraft = await Draft.fetchById(md.value) + const draftObject = oldDraft.root.toObject() + const newTitle = req.body.title ? req.body.title : draftObject.content.title + ' Copy' + draftObject.content.title = newTitle + return Draft.updateContent(req.currentDocument.draftId, req.currentUser.id, draftObject) + }) + .then(() => { + copyMeta.saveOrCreate() + res.success() + }) + .catch(e => { + res.unexpected(e) + }) + }) + // list a draft's permissions router .route('/drafts/:draftId/permission') diff --git a/packages/app/obojobo-repository/server/routes/api.test.js b/packages/app/obojobo-repository/server/routes/api.test.js index 7c5abfcd3a..30133f90aa 100644 --- a/packages/app/obojobo-repository/server/routes/api.test.js +++ b/packages/app/obojobo-repository/server/routes/api.test.js @@ -531,12 +531,13 @@ describe('repository api route', () => { .send({ visitId: 'mockVisitId' }) .then(response => { expect(DraftPermissions.userHasPermissionToCopy).toHaveBeenCalledWith( - mockCurrentUser.id, + mockCurrentUser, mockCurrentDocument.draftId ) expect(Draft.fetchById).toHaveBeenCalledWith(mockCurrentDocument.draftId) expect(mockDraftObject.content.title).toEqual('mockDraftTitle Copy') expect(Draft.createWithContent).toHaveBeenCalledWith(mockCurrentUser.id, mockDraftObject) + expect(DraftsMetadata).toHaveBeenCalledTimes(1) expect(DraftsMetadata).toHaveBeenCalledWith({ draft_id: 'mockNewDraftId', key: 'copied', @@ -595,6 +596,54 @@ describe('repository api route', () => { }) }) + test('post /drafts/:draftId/copy makes the copy read-only if specified', () => { + expect.hasAssertions() + + const mockDraftObject = { + id: 'mockNewDraftId', + content: { + id: 'mockNewDraftContentId', + title: 'mockDraftTitle' + } + } + + const mockDraftRootToObject = jest.fn() + mockDraftRootToObject.mockReturnValueOnce(mockDraftObject) + + const mockDraft = { + root: { + toObject: mockDraftRootToObject + } + } + + DraftPermissions.userHasPermissionToCopy.mockResolvedValueOnce(true) + + Draft.fetchById = jest.fn() + Draft.fetchById.mockResolvedValueOnce(mockDraft) + Draft.createWithContent.mockResolvedValueOnce(mockDraftObject) + + return request(app) + .post('/drafts/mockDraftId/copy') + .send({ visitId: 'mockVisitId', title: 'New Draft Title', readOnly: true }) + .then(() => { + expect(mockDraftObject.content.title).toEqual('New Draft Title') + // everything else is unchanged from above + expect(DraftsMetadata).toHaveBeenCalledTimes(2) + expect(DraftsMetadata).toHaveBeenCalledWith({ + draft_id: 'mockNewDraftId', + key: 'copied', + value: mockCurrentDocument.draftId + }) + expect({ + draft_id: 'mockNewDraftId', + key: 'read_only', + value: true + }) + expect(DraftsMetadata.mock.instances[0].saveOrCreate).toHaveBeenCalledTimes(1) + expect(DraftsMetadata.mock.instances[1].saveOrCreate).toHaveBeenCalledTimes(1) + }) + }) + test('post /drafts/:draftId/copy refreshes all node IDs in a document', () => { expect.hasAssertions() @@ -697,7 +746,7 @@ describe('repository api route', () => { .then(response => { expect(DraftPermissions.userHasPermissionToCopy).toHaveBeenCalledTimes(1) expect(DraftPermissions.userHasPermissionToCopy).toHaveBeenCalledWith( - mockCurrentUser.id, + mockCurrentUser, mockCurrentDocument.draftId ) expect(response.statusCode).toBe(401) @@ -714,7 +763,7 @@ describe('repository api route', () => { .then(response => { expect(DraftPermissions.userHasPermissionToCopy).toHaveBeenCalledTimes(1) expect(DraftPermissions.userHasPermissionToCopy).toHaveBeenCalledWith( - mockCurrentUser.id, + mockCurrentUser, mockCurrentDocument.draftId ) expect(response.statusCode).toBe(500) @@ -722,6 +771,160 @@ describe('repository api route', () => { }) }) + test('get /drafts/:draftId/sync calls the correct functions and returns the expected response', () => { + expect.hasAssertions() + + const mockMetadataResponse = { + value: 'mockOriginalDraftId', + updatedAt: '1999-01-01 01:00:00.000000+00' + } + + const mockDraftResponse = { + draftId: 'originalDraftId' + } + + DraftsMetadata.getByDraftIdAndKey.mockResolvedValueOnce(mockMetadataResponse) + DraftSummary.fetchByIdMoreRecentThan.mockResolvedValueOnce(mockDraftResponse) + + return request(app) + .get('/drafts/mockDraftId/sync') + .then(response => { + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledWith( + mockCurrentDocument.draftId, + 'copied' + ) + expect(DraftSummary.fetchByIdMoreRecentThan).toHaveBeenCalledWith( + mockMetadataResponse.value, + mockMetadataResponse.updatedAt + ) + expect(response.body).toEqual(mockDraftResponse) + expect(response.statusCode).toBe(200) + }) + }) + + test('get /drafts/:draftId/sync returns unexpected when encountering an error', () => { + expect.hasAssertions() + + const mockError = new Error('not found in db') + DraftsMetadata.getByDraftIdAndKey.mockRejectedValue(mockError) + + return request(app) + .get('/drafts/mockDraftId/sync') + .then(response => { + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledWith( + mockCurrentDocument.draftId, + 'copied' + ) + expect(DraftSummary.fetchByIdMoreRecentThan).not.toHaveBeenCalled() + expect(response.statusCode).toBe(500) + }) + }) + + test('patch /drafts/:draftId/sync returns notAuthorized for users with minimal access to the draft', () => { + expect.hasAssertions() + + const mockMetaSaveOrCreate = jest.fn() + DraftsMetadata.getByDraftIdAndKey.mockResolvedValueOnce({ + value: 'mockOriginalDraftId', + updatedAt: '1999-01-01 01:00:00.000000+00', + saveOrCreate: mockMetaSaveOrCreate + }) + + DraftPermissions.getUserAccessLevelToDraft.mockResolvedValueOnce(MINIMAL) + + return request(app) + .patch('/drafts/mockDraftId/sync') + .then(() => { + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledWith('mockDraftId', 'copied') + expect(Draft.fetchById).not.toHaveBeenCalled() + expect(Draft.updateContent).not.toHaveBeenCalled() + expect(mockMetaSaveOrCreate).not.toHaveBeenCalled() + }) + }) + + test('patch /drafts/:draftId/sync returns and calls functions correctly for users with partial access to the draft, no optional title', () => { + expect.hasAssertions() + + const mockMetaSaveOrCreate = jest.fn() + DraftsMetadata.getByDraftIdAndKey.mockResolvedValueOnce({ + value: 'mockOriginalDraftId', + updatedAt: '1999-01-01 01:00:00.000000+00', + saveOrCreate: mockMetaSaveOrCreate + }) + + DraftPermissions.getUserAccessLevelToDraft.mockResolvedValueOnce(PARTIAL) + + const mockDraftObject = { + id: 'mockNewDraftId', + content: { + id: 'mockNewDraftContentId', + title: 'mockDraftTitle' + } + } + + const mockDraftRootToObject = jest.fn() + mockDraftRootToObject.mockReturnValueOnce(mockDraftObject) + + const mockDraft = { + root: { + toObject: mockDraftRootToObject + } + } + Draft.fetchById.mockResolvedValueOnce(mockDraft) + + return request(app) + .patch('/drafts/mockDraftId/sync') + .then(() => { + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledWith('mockDraftId', 'copied') + expect(Draft.fetchById).toHaveBeenCalledWith('mockOriginalDraftId') + expect(mockDraftObject.content.title).toEqual('mockDraftTitle Copy') + expect(Draft.updateContent).toHaveBeenCalled() + expect(mockMetaSaveOrCreate).toHaveBeenCalled() + }) + }) + + test('patch /drafts/:draftId/sync returns and calls functions correctly for users with partial access to the draft, optional title', () => { + expect.hasAssertions() + + const mockMetaSaveOrCreate = jest.fn() + DraftsMetadata.getByDraftIdAndKey.mockResolvedValueOnce({ + value: 'mockOriginalDraftId', + updatedAt: '1999-01-01 01:00:00.000000+00', + saveOrCreate: mockMetaSaveOrCreate + }) + + DraftPermissions.getUserAccessLevelToDraft.mockResolvedValueOnce(PARTIAL) + + const mockDraftObject = { + id: 'mockNewDraftId', + content: { + id: 'mockNewDraftContentId', + title: 'mockDraftTitle' + } + } + + const mockDraftRootToObject = jest.fn() + mockDraftRootToObject.mockReturnValueOnce(mockDraftObject) + + const mockDraft = { + root: { + toObject: mockDraftRootToObject + } + } + Draft.fetchById.mockResolvedValueOnce(mockDraft) + + return request(app) + .patch('/drafts/mockDraftId/sync') + .send({ title: 'specified title' }) + .then(() => { + expect(DraftsMetadata.getByDraftIdAndKey).toHaveBeenCalledWith('mockDraftId', 'copied') + expect(Draft.fetchById).toHaveBeenCalledWith('mockOriginalDraftId') + expect(mockDraftObject.content.title).toEqual('specified title') + expect(Draft.updateContent).toHaveBeenCalled() + expect(mockMetaSaveOrCreate).toHaveBeenCalled() + }) + }) + test('get /drafts/:draftId/permission returns the expected response', () => { expect.hasAssertions() const userToJSON = jest.fn().mockReturnValue('filtered-user') diff --git a/packages/app/obojobo-repository/server/routes/library.js b/packages/app/obojobo-repository/server/routes/library.js index 5facba2bb3..b19265ffc7 100644 --- a/packages/app/obojobo-repository/server/routes/library.js +++ b/packages/app/obojobo-repository/server/routes/library.js @@ -151,8 +151,13 @@ router owner = await UserModel.fetchById(module.userId) } + const canPreview = await DraftPermissions.userHasPermissionToPreview( + req.currentUser, + module.draftId + ) + const canCopy = await DraftPermissions.userHasPermissionToCopy( - req.currentUser.id, + req.currentUser, module.draftId ) @@ -163,6 +168,7 @@ router // must use webpackAssetPath for all webpack assets to work in dev and production! appCSSUrl: webpackAssetPath('repository.css'), appJsUrl: webpackAssetPath('page-module.js'), + canPreview, canCopy } res.render('pages/page-module-server.jsx', props) diff --git a/packages/app/obojobo-repository/server/routes/library.test.js b/packages/app/obojobo-repository/server/routes/library.test.js index 812f2f7639..110a26b54f 100644 --- a/packages/app/obojobo-repository/server/routes/library.test.js +++ b/packages/app/obojobo-repository/server/routes/library.test.js @@ -214,7 +214,7 @@ describe('repository library route', () => { expect(UserModel.fetchById).toHaveBeenCalledWith(99) expect(DraftPermissions.userHasPermissionToCopy).toHaveBeenCalledTimes(1) expect(DraftPermissions.userHasPermissionToCopy).toHaveBeenCalledWith( - mockCurrentUser.id, + mockCurrentUser, 'mockDraftId' ) expect(response.header['content-type']).toContain('text/html') @@ -301,7 +301,7 @@ describe('repository library route', () => { expect(DraftSummary.fetchById).toHaveBeenCalledWith(publicLibCollectionId) expect(UserModel.fetchById).not.toHaveBeenCalled() expect(DraftPermissions.userHasPermissionToCopy).toHaveBeenCalledWith( - mockCurrentUser.id, + mockCurrentUser, 'mockDraftId' ) expect(response.header['content-type']).toContain('text/html') diff --git a/packages/app/obojobo-repository/shared/actions/dashboard-actions.js b/packages/app/obojobo-repository/shared/actions/dashboard-actions.js index c9071c48eb..6426e5f660 100644 --- a/packages/app/obojobo-repository/shared/actions/dashboard-actions.js +++ b/packages/app/obojobo-repository/shared/actions/dashboard-actions.js @@ -210,6 +210,16 @@ const apiGetModuleLock = async draftId => { return data.value } +const apiGetModuleSyncStatus = draftId => { + const options = { ...defaultOptions() } + return fetch(`/api/drafts/${draftId}/sync`, options).then(res => res.json()) +} + +const apiSyncModuleUpdates = draftId => { + const options = { ...defaultOptions(), method: 'PATCH' } + return fetch(`/api/drafts/${draftId}/sync`, options).then(res => res.json()) +} + // ================== ACTIONS =================== const SHOW_MODULE_PERMISSIONS = 'SHOW_MODULE_PERMISSIONS' @@ -445,6 +455,38 @@ const showModuleMore = module => ({ module }) +const SHOW_MODULE_SYNC = 'SHOW_MODULE_SYNC' +const showModuleSync = module => ({ + type: SHOW_MODULE_SYNC, + meta: { module }, + promise: apiGetModuleSyncStatus(module.draftId) +}) + +const SYNC_MODULE_UPDATES = 'SYNC_MODULE_UPDATES' +const syncModuleUpdates = (draftId, options = { ...defaultModuleModeOptions }) => { + let apiModuleGetCall + + switch (options.mode) { + case MODE_COLLECTION: + apiModuleGetCall = () => { + return apiGetModulesForCollection(options.collectionId) + } + break + case MODE_RECENT: + apiModuleGetCall = apiGetMyRecentModules + break + case MODE_ALL: + default: + apiModuleGetCall = apiGetMyModules + break + } + + return { + type: SYNC_MODULE_UPDATES, + promise: apiSyncModuleUpdates(draftId).then(apiModuleGetCall) + } +} + const SHOW_MODULE_MANAGE_COLLECTIONS = 'SHOW_MODULE_MANAGE_COLLECTIONS' const showModuleManageCollections = module => ({ type: SHOW_MODULE_MANAGE_COLLECTIONS, @@ -644,6 +686,8 @@ module.exports = { SELECT_MODULES, DESELECT_MODULES, SHOW_MODULE_MORE, + SHOW_MODULE_SYNC, + SYNC_MODULE_UPDATES, CREATE_NEW_COLLECTION, SHOW_MODULE_MANAGE_COLLECTIONS, LOAD_MODULE_COLLECTIONS, @@ -686,6 +730,8 @@ module.exports = { loadUsersForModule, clearPeopleSearchResults, showModuleMore, + showModuleSync, + syncModuleUpdates, showCollectionManageModules, loadCollectionModules, collectionAddModule, diff --git a/packages/app/obojobo-repository/shared/actions/dashboard-actions.test.js b/packages/app/obojobo-repository/shared/actions/dashboard-actions.test.js index 97c8e4460c..5be6a418c1 100644 --- a/packages/app/obojobo-repository/shared/actions/dashboard-actions.test.js +++ b/packages/app/obojobo-repository/shared/actions/dashboard-actions.test.js @@ -1886,23 +1886,18 @@ describe('Dashboard Actions', () => { }) }) - const assertBulkRestoreModulesRunsWithOptions = (secondaryLookupUrl, fetchBody, options) => { + test('bulkRestoreModules returns expected output and calls other functions', () => { global.fetch.mockResolvedValue(standardFetchResponse) - const actionReply = DashboardActions.bulkRestoreModules( - ['mockDraftId1', 'mockDraftId2'], - options - ) + const actionReply = DashboardActions.bulkRestoreModules(['mockDraftId1', 'mockDraftId2']) expect(global.fetch).toHaveBeenCalledTimes(2) expect(global.fetch).toHaveBeenCalledWith('/api/drafts/restore/mockDraftId1', { ...defaultFetchOptions, - method: 'PUT', - body: fetchBody + method: 'PUT' }) expect(global.fetch).toHaveBeenCalledWith('/api/drafts/restore/mockDraftId2', { ...defaultFetchOptions, - method: 'PUT', - body: fetchBody + method: 'PUT' }) global.fetch.mockReset() global.fetch.mockResolvedValueOnce({ @@ -1916,26 +1911,22 @@ describe('Dashboard Actions', () => { return actionReply.promise.then(finalResponse => { expect(standardFetchResponse.json).toHaveBeenCalled() - expect(global.fetch).toHaveBeenCalledWith(secondaryLookupUrl, defaultFetchOptions) + expect(global.fetch).toHaveBeenCalledWith('/api/drafts-deleted', defaultFetchOptions) expect(finalResponse).toEqual({ value: 'mockSecondaryResponse' }) }) - } - test('bulkRestoreModules returns expected output and calls other functions', () => { - return assertBulkRestoreModulesRunsWithOptions('/api/drafts-deleted') }) - const assertGetMyDeletedModulesRunsWithOptions = (secondaryLookupUrl, fetchBody, options) => { + test('apiGetMyDeletedModules returns the expected output', () => { global.fetch.mockResolvedValue(standardFetchResponse) - const actionReply = DashboardActions.getDeletedModules(options) + const actionReply = DashboardActions.getDeletedModules() expect(global.fetch).toHaveBeenCalledTimes(1) expect(global.fetch).toHaveBeenCalledWith('/api/drafts-deleted', { ...defaultFetchOptions, - method: 'GET', - body: fetchBody + method: 'GET' }) global.fetch.mockReset() global.fetch.mockResolvedValueOnce({ @@ -1952,20 +1943,16 @@ describe('Dashboard Actions', () => { expect(global.fetch).not.toHaveBeenCalled() expect(finalResponse).toEqual({ value: 'mockVal' }) }) - } - test('apiGetMyDeletedModules returns the expected output', () => { - return assertGetMyDeletedModulesRunsWithOptions('/api/drafts-deleted') }) - const assertGetMyModulesRunsWithOptions = (secondaryLookupUrl, fetchBody, options) => { + test('apiGetMyModules returns the expected output', () => { global.fetch.mockResolvedValue(standardFetchResponse) - const actionReply = DashboardActions.getModules(options) + const actionReply = DashboardActions.getModules() expect(global.fetch).toHaveBeenCalledTimes(1) expect(global.fetch).toHaveBeenCalledWith('/api/drafts', { ...defaultFetchOptions, - method: 'GET', - body: fetchBody + method: 'GET' }) global.fetch.mockReset() global.fetch.mockResolvedValueOnce({ @@ -1982,8 +1969,74 @@ describe('Dashboard Actions', () => { expect(global.fetch).not.toHaveBeenCalled() expect(finalResponse).toEqual({ value: 'mockVal' }) }) + }) + + test('showModuleSync returns the expected output', () => { + global.fetch.mockResolvedValue(standardFetchResponse) + + const mockModule = { draftId: 'mockDraftId' } + const actionReply = DashboardActions.showModuleSync(mockModule) + + expect(global.fetch).toHaveBeenCalledWith('/api/drafts/mockDraftId/sync', defaultFetchOptions) + expect(actionReply).toEqual({ + type: DashboardActions.SHOW_MODULE_SYNC, + meta: { + module: mockModule + }, + promise: expect.any(Object) + }) + }) + + // three (plus one default) ways of calling syncModuleUpdates + const assertSyncModuleUpdatesRunsWithOptions = (secondaryLookupUrl, options) => { + global.fetch.mockResolvedValueOnce(standardFetchResponse) + const actionReply = DashboardActions.syncModuleUpdates('mockDraftId', options) + + expect(global.fetch).toHaveBeenCalledWith('/api/drafts/mockDraftId/sync', { + ...defaultFetchOptions, + method: 'PATCH' + }) + global.fetch.mockReset() + // two additional API calls are made following the first + global.fetch.mockResolvedValueOnce({ + json: () => ({ value: 'mockSecondVal1' }) + }) + + expect(actionReply).toEqual({ + type: DashboardActions.SYNC_MODULE_UPDATES, + promise: expect.any(Object) + }) + + return actionReply.promise.then(finalResponse => { + expect(standardFetchResponse.json).toHaveBeenCalled() + expect(global.fetch).toHaveBeenCalledWith(secondaryLookupUrl, defaultFetchOptions) + + expect(finalResponse).toEqual({ + value: 'mockSecondVal1' + }) + }) } - test('apiGetMyModules returns the expected output', () => { - return assertGetMyModulesRunsWithOptions('/api/drafts') + //options will contain mode: MODE_COLLECTION and collectionId + test('syncModuleUpdates returns expected output and calls other functions, mode MODE_COLLECTION', () => { + const options = { + mode: MODE_COLLECTION, + collectionId: 'mockCollectionId' + } + return assertSyncModuleUpdatesRunsWithOptions( + '/api/collections/mockCollectionId/modules', + options + ) + }) + //options will contain mode: MODE_RECENT + test('syncModuleUpdates returns expected output and calls other functions, mode MODE_RECENT', () => { + return assertSyncModuleUpdatesRunsWithOptions('/api/recent/drafts', { mode: MODE_RECENT }) + }) + //options will contain mode: MODE_ALL + test('syncModuleUpdates returns expected output and calls other functions, mode MODE_ALL', () => { + return assertSyncModuleUpdatesRunsWithOptions('/api/drafts', { mode: MODE_ALL }) + }) + // no options, default should be equivalent to MODE_ALL + test('syncModuleUpdates returns expected output and calls other functions, default', () => { + return assertSyncModuleUpdatesRunsWithOptions('/api/drafts') }) }) diff --git a/packages/app/obojobo-repository/shared/api-util.js b/packages/app/obojobo-repository/shared/api-util.js index ae43aad975..5e178c44dd 100644 --- a/packages/app/obojobo-repository/shared/api-util.js +++ b/packages/app/obojobo-repository/shared/api-util.js @@ -1,8 +1,8 @@ const API = require('obojobo-document-engine/src/scripts/viewer/util/api') const ViewerAPI = { - copyModule(draftId) { - return API.post(`/api/drafts/${draftId}/copy`).then(result => { + copyModule(draftId, readOnly = false) { + return API.post(`/api/drafts/${draftId}/copy`, { readOnly }).then(result => { if (result.status === 200) { window.location.assign('/dashboard') } else if (result.status === 401) { diff --git a/packages/app/obojobo-repository/shared/api-util.test.js b/packages/app/obojobo-repository/shared/api-util.test.js index 8a41b70ca1..ff0088c6f8 100644 --- a/packages/app/obojobo-repository/shared/api-util.test.js +++ b/packages/app/obojobo-repository/shared/api-util.test.js @@ -39,7 +39,7 @@ describe('repository apiutil', () => { API.post.mockResolvedValueOnce({ status: 200 }) return APIUtil.copyModule('mockDraftId').then(() => { - expect(API.post).toHaveBeenCalledWith('/api/drafts/mockDraftId/copy') + expect(API.post).toHaveBeenCalledWith('/api/drafts/mockDraftId/copy', { readOnly: false }) expect(global.location.assign).toHaveBeenCalledWith('/dashboard') }) }) @@ -50,7 +50,7 @@ describe('repository apiutil', () => { API.post.mockResolvedValueOnce({ status: 401 }) return APIUtil.copyModule('mockDraftId').then(() => { - expect(API.post).toHaveBeenCalledWith('/api/drafts/mockDraftId/copy') + expect(API.post).toHaveBeenCalledWith('/api/drafts/mockDraftId/copy', { readOnly: false }) expect(global.location.assign).not.toHaveBeenCalled() expect(global.alert).toHaveBeenCalledTimes(1) expect(global.alert).toHaveBeenCalledWith('You are not authorized to copy this module') @@ -63,7 +63,7 @@ describe('repository apiutil', () => { API.post.mockResolvedValueOnce({ status: 500 }) return APIUtil.copyModule('mockDraftId').then(() => { - expect(API.post).toHaveBeenCalledWith('/api/drafts/mockDraftId/copy') + expect(API.post).toHaveBeenCalledWith('/api/drafts/mockDraftId/copy', { readOnly: false }) expect(global.location.assign).not.toHaveBeenCalled() expect(global.alert).toHaveBeenCalledTimes(1) expect(global.alert).toHaveBeenCalledWith('Something went wrong while copying') diff --git a/packages/app/obojobo-repository/shared/components/__snapshots__/module-menu.test.js.snap b/packages/app/obojobo-repository/shared/components/__snapshots__/module-menu.test.js.snap index 2c6438472f..3c52a81cbd 100644 --- a/packages/app/obojobo-repository/shared/components/__snapshots__/module-menu.test.js.snap +++ b/packages/app/obojobo-repository/shared/components/__snapshots__/module-menu.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ModuleMenu ModuleMenu renders correctly with "Full" access level 1`] = ` +exports[`ModuleMenu ModuleMenu renders correctly with "Full" access level, no readOnly 1`] = `
@@ -40,7 +40,137 @@ exports[`ModuleMenu ModuleMenu renders correctly with "Full" access level 1`] =
`; -exports[`ModuleMenu ModuleMenu renders correctly with "Minimal" access level 1`] = ` +exports[`ModuleMenu ModuleMenu renders correctly with "Full" access level, readOnly false 1`] = ` +
+
+ + Preview + + + Edit + + +
+ +
+
+`; + +exports[`ModuleMenu ModuleMenu renders correctly with "Full" access level, readOnly true 1`] = ` +
+
+ + Preview + + + +
+ +
+
+`; + +exports[`ModuleMenu ModuleMenu renders correctly with "Minimal" access level, no readOnly 1`] = ` +
+
+ + Preview + +
+ +
+
+`; + +exports[`ModuleMenu ModuleMenu renders correctly with "Minimal" access level, readOnly false 1`] = ` +
+
+ + Preview + +
+ +
+
+`; + +exports[`ModuleMenu ModuleMenu renders correctly with "Minimal" access level, readOnly true 1`] = `
@@ -66,7 +196,7 @@ exports[`ModuleMenu ModuleMenu renders correctly with "Minimal" access level 1`]
`; -exports[`ModuleMenu ModuleMenu renders correctly with "Partial" access level 1`] = ` +exports[`ModuleMenu ModuleMenu renders correctly with "Partial" access level, no readOnly 1`] = `
@@ -100,12 +230,12 @@ exports[`ModuleMenu ModuleMenu renders correctly with "Partial" access level 1`]
`; -exports[`ModuleMenu ModuleMenu renders correctly with className prop 1`] = ` +exports[`ModuleMenu ModuleMenu renders correctly with "Partial" access level, readOnly false 1`] = `
`; -exports[`ModuleMenu ModuleMenu renders correctly with standard expected props 1`] = ` +exports[`ModuleMenu ModuleMenu renders correctly with "Partial" access level, readOnly true 1`] = `
+ + Preview + + +
+ +
+
+`; + +exports[`ModuleMenu ModuleMenu renders correctly with className prop 1`] = ` +
+
Edit + +
+ +
+
+`; + +exports[`ModuleMenu ModuleMenu renders correctly with standard expected props 1`] = ` +
+
+ + Preview +
`; +exports[`ModuleOptionsDialog renders correctly with Full access level - read-only 1`] = ` +
+
+
+ +
+
+ Mock Module Title +
+ +
+
+

+ Module Options +

+

+ Your Access Level: + Full +

+
+
+ + Preview + +
+ View with preview controls. +
+
+
+ +
+ Update this module with changes to the original. +
+
+
+ +
+ Add or remove collaborators. +
+
+
+ +
+ View scores by student. +
+
+
+ +
+ Add to or remove from private collections. +
+
+
+ +
+ Download a copy in JSON format. +
+
+
+ +
+ Download a copy in XML format. +
+
+
+ + Public Page + +
+ Visit this modules public page. +
+
+
+ +
+ Say farewell. +
+
+
+ +
+
+`; + exports[`ModuleOptionsDialog renders correctly with Minimal access level 1`] = `
`; -exports[`ModuleOptionsDialog renders correctly with Partial access level 1`] = ` +exports[`ModuleOptionsDialog renders correctly with Partial access level - not read-only 1`] = `
@@ -519,6 +717,172 @@ exports[`ModuleOptionsDialog renders correctly with Partial access level 1`] = `
`; +exports[`ModuleOptionsDialog renders correctly with Partial access level - read-only 1`] = ` +
+
+
+ +
+
+ Mock Module Title +
+ +
+
+

+ Module Options +

+

+ Your Access Level: + Partial +

+
+
+ + Preview + +
+ View with preview controls. +
+
+
+ +
+ Update this module with changes to the original. +
+
+
+ +
+ View scores by student. +
+
+
+ +
+ Add to or remove from private collections. +
+
+
+ +
+ Download a copy in JSON format. +
+
+
+ +
+ Download a copy in XML format. +
+
+
+ + Public Page + +
+ Visit this modules public page. +
+
+
+ +
+
+`; + exports[`ModuleOptionsDialog renders correctly with standard expected props 1`] = `
+
+
+ +
+
+ Mock Draft Title +
+ +
+
+

+ Synchronize Updates +

+
+ This dialog will indicate if any changes have been made to the module this copy was created from. +
+ If there have been any changes, you will be given the option to automatically update this copy to match. +
+ Please note that synchronizing changes may also change this copy's title. +
+
+

+ Checking for updates to this module's original... +

+
+
+
+`; + +exports[`ModuleSyncDialog ModuleSyncDialog renders with provided newest as null 1`] = ` +
+
+
+ +
+
+ Mock Draft Title +
+ +
+
+

+ Synchronize Updates +

+
+ This dialog will indicate if any changes have been made to the module this copy was created from. +
+ If there have been any changes, you will be given the option to automatically update this copy to match. +
+ Please note that synchronizing changes may also change this copy's title. +
+
+

+ No changes found, copy is up-to-date. +

+
+
+
+`; + +exports[`ModuleSyncDialog ModuleSyncDialog renders with provided newest module 1`] = ` +
+
+
+ +
+
+ Mock Draft Title +
+ +
+
+

+ Synchronize Updates +

+
+ This dialog will indicate if any changes have been made to the module this copy was created from. +
+ If there have been any changes, you will be given the option to automatically update this copy to match. +
+ Please note that synchronizing changes may also change this copy's title. +
+
+
+
+ +
+
+ Original Mock Draft Title +
+ + Last updated: + 1999-01-01 1:01 AM + +
+ +
+
+
+
+`; diff --git a/packages/app/obojobo-repository/shared/components/dashboard-hoc.js b/packages/app/obojobo-repository/shared/components/dashboard-hoc.js index a54ef88455..600e028688 100644 --- a/packages/app/obojobo-repository/shared/components/dashboard-hoc.js +++ b/packages/app/obojobo-repository/shared/components/dashboard-hoc.js @@ -36,7 +36,9 @@ const { checkModuleLock, getDeletedModules, getModules, - bulkRestoreModules + bulkRestoreModules, + showModuleSync, + syncModuleUpdates } = require('../actions/dashboard-actions') const mapStoreStateToProps = state => state const mapActionsToProps = { @@ -75,7 +77,9 @@ const mapActionsToProps = { checkModuleLock, getDeletedModules, getModules, - bulkRestoreModules + bulkRestoreModules, + showModuleSync, + syncModuleUpdates } module.exports = connect( mapStoreStateToProps, diff --git a/packages/app/obojobo-repository/shared/components/dashboard-hoc.test.js b/packages/app/obojobo-repository/shared/components/dashboard-hoc.test.js index 65bbcf9a53..8a62b0d132 100644 --- a/packages/app/obojobo-repository/shared/components/dashboard-hoc.test.js +++ b/packages/app/obojobo-repository/shared/components/dashboard-hoc.test.js @@ -59,7 +59,9 @@ describe('Dashboard HOC', () => { bulkRestoreModules: DashboardActions.bulkRestoreModules, changeAccessLevel: DashboardActions.changeAccessLevel, getDeletedModules: DashboardActions.getDeletedModules, - getModules: DashboardActions.getModules + getModules: DashboardActions.getModules, + showModuleSync: DashboardActions.showModuleSync, + syncModuleUpdates: DashboardActions.syncModuleUpdates }) expect(mockReduxConnectReturn).toHaveBeenCalledTimes(1) diff --git a/packages/app/obojobo-repository/shared/components/dashboard.jsx b/packages/app/obojobo-repository/shared/components/dashboard.jsx index 5001ae824b..be5490103c 100644 --- a/packages/app/obojobo-repository/shared/components/dashboard.jsx +++ b/packages/app/obojobo-repository/shared/components/dashboard.jsx @@ -8,6 +8,7 @@ const RepositoryBanner = require('./repository-banner') const Module = require('./module') const ModulePermissionsDialog = require('./module-permissions-dialog') const ModuleOptionsDialog = require('./module-options-dialog') +const ModuleSyncDialog = require('./module-sync-dialog') const VersionHistoryDialog = require('./version-history-dialog') const Button = require('./button') const MultiButton = require('./multi-button') @@ -40,6 +41,17 @@ const renderOptionsDialog = (props, extension) => ( startLoadingAnimation={props.startLoadingAnimation} stopLoadingAnimation={props.stopLoadingAnimation} showModuleManageCollections={props.showModuleManageCollections} + showModuleSync={props.showModuleSync} + /> +) + +const renderSyncDialog = (props, extension) => ( + ) @@ -118,6 +130,7 @@ const renderModalDialog = props => { extendedProps.deleteModulePermissions = (draftId, userId) => { props.deleteModulePermissions(draftId, userId, extendedOptions) } + extendedProps.syncModuleUpdates = draftId => props.syncModuleUpdates(draftId, extendedOptions) break default: @@ -125,6 +138,7 @@ const renderModalDialog = props => { extendedProps.deleteModulePermissions = (draftId, userId) => { props.deleteModulePermissions(draftId, userId, extendedOptions) } + extendedProps.syncModuleUpdates = draftId => props.syncModuleUpdates(draftId, extendedOptions) extendedProps.onClear = () => { props.clearSelection() } @@ -140,6 +154,11 @@ const renderModalDialog = props => { dialog = renderOptionsDialog(props, extendedProps) break + case 'module-sync': + title = 'Module Sync' + dialog = renderSyncDialog(props, extendedProps) + break + case 'module-permissions': title = 'Module Access' dialog = renderPermissionsDialog(props, extendedProps) diff --git a/packages/app/obojobo-repository/shared/components/dashboard.test.js b/packages/app/obojobo-repository/shared/components/dashboard.test.js index 1a1801955d..1dbc8f79e0 100644 --- a/packages/app/obojobo-repository/shared/components/dashboard.test.js +++ b/packages/app/obojobo-repository/shared/components/dashboard.test.js @@ -49,6 +49,9 @@ jest.mock('./module-permissions-dialog', () => props => { jest.mock('./module-options-dialog', () => props => { return }) +jest.mock('./module-sync-dialog', () => props => { + return +}) jest.mock('./assessment-score-data-dialog', () => props => { return }) @@ -71,6 +74,7 @@ import CollectionRenameDialog from './collection-rename-dialog' import ModuleManageCollectionsDialog from './module-manage-collections-dialog' import ModulePermissionsDialog from './module-permissions-dialog' import ModuleOptionsDialog from './module-options-dialog' +import ModuleSyncDialog from './module-sync-dialog' import VersionHistoryDialog from './version-history-dialog' import AssessmentScoreDataDialog from './assessment-score-data-dialog' import MessageDialog from './message-dialog' @@ -1725,6 +1729,61 @@ describe('Dashboard', () => { expect(component.root.findAllByProps({ className: isLoadingClass }).length).toBe(0) }) + test('renders "Module Sync" dialog and adjusts callbacks for each mode', () => { + dashboardProps.syncModuleUpdates = jest.fn() + dashboardProps.dialog = 'module-sync' + dashboardProps.mode = MODE_RECENT + let component + act(() => { + component = create() + }) + + expectDialogToBeRendered(component, ModuleSyncDialog, 'Module Sync') + const dialogComponent = component.root.findByType(ModuleSyncDialog) + + // ordinarily the draft ID would be provided inside the dialog + dialogComponent.props.syncModuleUpdates('mockDraftId') + expectMethodToBeCalledOnceWith(dashboardProps.syncModuleUpdates, [ + 'mockDraftId', + { mode: MODE_RECENT } + ]) + + dialogComponent.props.onClose() + expectMethodToBeCalledOnceWith(dashboardProps.closeModal) + + dashboardProps.mode = MODE_ALL + act(() => { + component.update() + }) + + dialogComponent.props.syncModuleUpdates('mockDraftId') + expectMethodToBeCalledOnceWith(dashboardProps.syncModuleUpdates, [ + 'mockDraftId', + { mode: MODE_ALL } + ]) + + dialogComponent.props.onClose() + expectMethodToBeCalledOnceWith(dashboardProps.closeModal) + + dashboardProps.mode = MODE_COLLECTION + dashboardProps.collection = { + id: 'mockCollectionId', + title: 'Mock Collection Title' + } + act(() => { + component.update() + }) + + dialogComponent.props.syncModuleUpdates('mockDraftId') + expectMethodToBeCalledOnceWith(dashboardProps.syncModuleUpdates, [ + 'mockDraftId', + { collectionId: 'mockCollectionId', mode: MODE_COLLECTION } + ]) + + dialogComponent.props.onClose() + expectMethodToBeCalledOnceWith(dashboardProps.closeModal) + }) + test('renders "Module Access" dialog and adjusts callbacks for each mode', () => { dashboardProps.dialog = 'module-permissions' dashboardProps.loadUsersForModule = jest.fn() diff --git a/packages/app/obojobo-repository/shared/components/module-menu-hoc.js b/packages/app/obojobo-repository/shared/components/module-menu-hoc.js index f9a642afcb..e1f15ff9bd 100644 --- a/packages/app/obojobo-repository/shared/components/module-menu-hoc.js +++ b/packages/app/obojobo-repository/shared/components/module-menu-hoc.js @@ -3,9 +3,10 @@ const connect = require('react-redux').connect const { showModulePermissions, deleteModule, - showModuleMore + showModuleMore, + showModuleSync } = require('../actions/dashboard-actions') -const mapActionsToProps = { showModulePermissions, deleteModule, showModuleMore } +const mapActionsToProps = { showModulePermissions, deleteModule, showModuleMore, showModuleSync } module.exports = connect( null, mapActionsToProps diff --git a/packages/app/obojobo-repository/shared/components/module-menu-hoc.test.js b/packages/app/obojobo-repository/shared/components/module-menu-hoc.test.js index 1feae2eaf4..a433b5e98c 100644 --- a/packages/app/obojobo-repository/shared/components/module-menu-hoc.test.js +++ b/packages/app/obojobo-repository/shared/components/module-menu-hoc.test.js @@ -19,7 +19,8 @@ describe('ModuleMenu HOC', () => { expect(ReactRedux.connect).toHaveBeenCalledWith(null, { showModulePermissions: DashboardActions.showModulePermissions, deleteModule: DashboardActions.deleteModule, - showModuleMore: DashboardActions.showModuleMore + showModuleMore: DashboardActions.showModuleMore, + showModuleSync: DashboardActions.showModuleSync }) expect(mockReduxConnectReturn).toHaveBeenCalledTimes(1) diff --git a/packages/app/obojobo-repository/shared/components/module-menu.jsx b/packages/app/obojobo-repository/shared/components/module-menu.jsx index a910cd946c..0f2c1c7beb 100644 --- a/packages/app/obojobo-repository/shared/components/module-menu.jsx +++ b/packages/app/obojobo-repository/shared/components/module-menu.jsx @@ -15,17 +15,25 @@ const ModuleMenu = props => { props.showModuleMore(props) } + const onSyncButtonClick = () => { + props.showModuleSync(props) + } + + // accessLevel should always be set - to be safe, don't show an edit button if it isn't return (
Preview - {props.accessLevel !== MINIMAL && ( + {!props.readOnly && props.accessLevel && props.accessLevel !== MINIMAL && ( Edit )} + {props.readOnly && props.accessLevel && props.accessLevel !== MINIMAL && ( + + )} {props.accessLevel === FULL && }
- {props.accessLevel !== MINIMAL && ( + {!props.readOnly && props.accessLevel !== MINIMAL && (
Edit @@ -51,6 +51,19 @@ const ModuleOptionsDialog = props => (
Write, edit, and update.
)} + {props.readOnly && props.accessLevel !== MINIMAL && ( +
+ +
Update this module with changes to the original.
+
+ )} {props.accessLevel === FULL && (
@@ -79,7 +92,7 @@ const ModuleOptionsDialog = props => (
View scores by student.
- {props.accessLevel !== MINIMAL && ( + {!props.readOnly && props.accessLevel !== MINIMAL && (
+
+ ) + } else if (props.newest === null) { + syncableStatusRender = ( +

No changes found, copy is up-to-date.

+ ) + } + + return ( +
+
+ +
+ {props.title} +
+ +
+
+

Synchronize Updates

+
+ This dialog will indicate if any changes have been made to the module this copy was + created from. +
+ If there have been any changes, you will be given the option to automatically update this + copy to match. +
+ Please note that synchronizing changes may also change this copy's title. +
+
{syncableStatusRender}
+
+
+ ) +} + +module.exports = ModuleSyncDialog diff --git a/packages/app/obojobo-repository/shared/components/module-sync-dialog.scss b/packages/app/obojobo-repository/shared/components/module-sync-dialog.scss new file mode 100644 index 0000000000..d99474450f --- /dev/null +++ b/packages/app/obojobo-repository/shared/components/module-sync-dialog.scss @@ -0,0 +1,94 @@ +@import '../../client/css/defaults'; + +.module-sync-dialog { + min-width: 23em; + + .repository--module-icon--image { + width: 1.5em; + height: 1.75em; + display: block; + } + + .module-title { + display: block; + font-size: 0.625em; + font-weight: bold; + color: black; + margin: 0; + max-width: calc(100% - 8.5em); + word-break: break-word; + } + + .top-bar { + text-align: left; + position: relative; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + box-sizing: border-box; + height: 3em; + + .repository--module-icon--image { + position: absolute; + left: 0.75em; + top: 50%; + transform: translate(0, -50%); + } + + .module-title { + position: absolute; + left: 4.5em; + top: 50%; + transform: translate(0, -50%); + max-height: 1.2em; + } + + .close-button { + position: absolute; + right: 0.5em; + top: 50%; + transform: translate(0, -50%); + } + } + + .wrapper { + padding: 0 1.25em; + + .title { + font-weight: 700; + font-size: 1.3em; + margin-bottom: 0.2em; + text-align: center; + } + + .sub-title { + font-size: 0.7em; + margin-bottom: 0.5em; + text-align: left; + } + + .sync-info-wrapper { + background-color: $color-banner-bg; + margin: 1.25em 0; + padding: 1em; + + .sync-info { + display: flex; + align-items: center; + + .module-title { + flex-grow: 1; + margin-left: 1em; + + .last-update-time { + font-size: 0.65em; + } + } + } + + .sync-info-text-only { + display: block; + width: 100%; + text-align: center; + } + } + } +} diff --git a/packages/app/obojobo-repository/shared/components/module-sync-dialog.test.js b/packages/app/obojobo-repository/shared/components/module-sync-dialog.test.js new file mode 100644 index 0000000000..07b53f70d7 --- /dev/null +++ b/packages/app/obojobo-repository/shared/components/module-sync-dialog.test.js @@ -0,0 +1,91 @@ +import React from 'react' +import { create } from 'react-test-renderer' + +import ModuleSyncDialog from './module-sync-dialog' + +describe('ModuleSyncDialog', () => { + let defaultProps + + beforeEach(() => { + jest.resetAllMocks() + + defaultProps = { + draftId: 'mockDraftId', + title: 'Mock Draft Title', + newest: false + } + }) + + test('ModuleSyncDialog renders with default props', () => { + const component = create() + + // 'Checking for updates' text should appear by default + const syncInfoComponent = component.root.findAllByProps({ className: 'sync-info' }) + expect(syncInfoComponent.length).toBe(0) + + const syncInfoTextOnlyComponent = component.root.findAllByProps({ + className: 'sync-info-text-only' + }) + expect(syncInfoTextOnlyComponent.length).toBe(1) + expect(syncInfoTextOnlyComponent[0].props.children).toBe( + "Checking for updates to this module's original..." + ) + + expect(component.toJSON()).toMatchSnapshot() + }) + + test('ModuleSyncDialog renders with provided newest as null', () => { + defaultProps.newest = null + const component = create() + + // 'No updates' text should appear + const syncInfoComponent = component.root.findAllByProps({ className: 'sync-info' }) + expect(syncInfoComponent.length).toBe(0) + + const syncInfoTextOnlyComponent = component.root.findAllByProps({ + className: 'sync-info-text-only' + }) + expect(syncInfoTextOnlyComponent.length).toBe(1) + expect(syncInfoTextOnlyComponent[0].props.children).toBe( + 'No changes found, copy is up-to-date.' + ) + + expect(component.toJSON()).toMatchSnapshot() + }) + + test('ModuleSyncDialog renders with provided newest module', () => { + defaultProps.newest = { + draftId: 'mockOriginalDraftId', + title: 'Original Mock Draft Title', + updatedAt: '1999/01/01 01:01' + } + const component = create() + + // text-only fields should not appear + const syncInfoTextOnlyComponent = component.root.findAllByProps({ + className: 'sync-info-text-only' + }) + expect(syncInfoTextOnlyComponent.length).toBe(0) + + // updated module info should appear + const syncInfoComponent = component.root.findAllByProps({ className: 'sync-info' }) + expect(syncInfoComponent.length).toBe(1) + + expect(component.toJSON()).toMatchSnapshot() + }) + + test('Synchronize button onClick calls syncModuleUpdates', () => { + defaultProps.syncModuleUpdates = jest.fn() + defaultProps.newest = { + draftId: 'mockOriginalDraftId', + title: 'Original Mock Draft Title', + updatedAt: '1999/01/01 01:01' + } + const component = create() + + const syncButton = component.root.findByProps({ className: 'sync-button' }) + syncButton.props.onClick() + + expect(defaultProps.syncModuleUpdates).toHaveBeenCalledWith('mockDraftId') + }) +}) diff --git a/packages/app/obojobo-repository/shared/components/module.jsx b/packages/app/obojobo-repository/shared/components/module.jsx index 497e205b2d..7dfe8a8876 100644 --- a/packages/app/obojobo-repository/shared/components/module.jsx +++ b/packages/app/obojobo-repository/shared/components/module.jsx @@ -72,6 +72,7 @@ const Module = props => { editor={props.editor} title={props.title} accessLevel={props.accessLevel} + readOnly={props.readOnly} /> ) : null} {props.children} diff --git a/packages/app/obojobo-repository/shared/components/pages/__snapshots__/page-module.test.js.snap b/packages/app/obojobo-repository/shared/components/pages/__snapshots__/page-module.test.js.snap index 16da8fad6c..1f4ec5ed33 100644 --- a/packages/app/obojobo-repository/shared/components/pages/__snapshots__/page-module.test.js.snap +++ b/packages/app/obojobo-repository/shared/components/pages/__snapshots__/page-module.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PageModule renders when given props 1`] = ` +exports[`PageModule renders when given props - current user can copy and preview 1`] = `
+ + Click the "Preview this module" button below to view the module as a learner would! + + + Click one of the buttons below to create a copy of this module (or to cancel the copy process). + + + Note: A read-only copy will not be editable, and can be synchronized with the original from the dashboard. + Preview this module - +
+
+ Module created by + + + firstName + + lastName + + + on + + Jan 1, 1970 + + and updated + + A long time ago + . +
+

+ Use this Module in your Canvas Course +

+

+ This module can be used inside your course as an + + assignment + + or + + module + + . +

+
+ Animated gif showing how to create an Obojobo Assignment in canvas +
+ Creating an Assignment in Canvas +
+
+

+ Create an Assignment in Canvas +

+
    +
  1. + Click Assignments in your course's menu. +
  2. +
  3. + Create a new Assignment +
  4. +
  5. + Set the: +
      +
    • + Assignment Name +
    • +
    • + Points (do not use 0) +
    • +
    • + any other relevant settings +
    • +
    +
  6. +
  7. + Set Submission Type to "External Tool" +
  8. +
  9. + Follow the + + Choosing a Obojobo Module + + instructions below +
  10. +
  11. + Click Select +
  12. +
  13. + Save & Publish +
  14. +
+

+ Create an Ungraded Module in Canvas +

+
    +
  1. + Click Modules in your course's menu. +
  2. +
  3. + Click the "+" in a module +
  4. +
  5. + Type an Assignment Name +
  6. +
  7. + Change the top drop down from "Assignment" to "External Tool". +
  8. +
  9. + Follow the + + Choosing a Obojobo Module + + instructions below +
  10. +
  11. + Click Add Item +
  12. +
  13. + You new module will be named " + mockDraftTitle + (doesn't send scores to grade book)" +
  14. +
  15. + Be sure to + + Publish + + within Canvas when ready +
  16. +
+

+ Choosing a Obojobo Module +

+
    +
  1. + Follow one of the sets of instructions above. +
  2. +
  3. + Click "FIND" next to the input labeled "Enter or find an External Tool URL" +
  4. +
  5. + In the popup that appears, scroll down and select "ObojoboNext Module (gradebook synced)" +
  6. +
  7. + Choose Community Collection +
  8. +
  9. + Search for the module by its title ( + + mockDraftTitle + + ) or its id ( + + mockDraftId + + ) +
  10. +
  11. + Click Embed next to your chosen module +
  12. +
+ +
+`; + +exports[`PageModule renders when given props - current user can not copy or preview 1`] = ` +
+
+ +
+
+
+
+
+ +
+

+ mockDraftTitle +

+
+
+
Module created by diff --git a/packages/app/obojobo-repository/shared/components/pages/page-library.scss b/packages/app/obojobo-repository/shared/components/pages/page-library.scss index a6837178ce..c57baf9011 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-library.scss +++ b/packages/app/obojobo-repository/shared/components/pages/page-library.scss @@ -1,5 +1,6 @@ /* stylelint-disable */ @import '../../../client/css/defaults'; +@import './page-module.scss'; // reduce a larger font-size set on an ancestor in the library page .repository--nav--links--search { @@ -12,8 +13,3 @@ color: $color-text-placeholder; text-align: center; } - -.copy-button { - display: inline-block; - margin: 0.5em; -} diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module.jsx b/packages/app/obojobo-repository/shared/components/pages/page-module.jsx index 1079b656e3..e78d43cc1b 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-module.jsx +++ b/packages/app/obojobo-repository/shared/components/pages/page-module.jsx @@ -1,6 +1,7 @@ require('./page-module.scss') const React = require('react') +const { useState } = require('react') const RepositoryNav = require('../repository-nav') const RepositoryBanner = require('../repository-banner') const ModuleImage = require('../module-image') @@ -12,116 +13,197 @@ const relativeTime = require('dayjs/plugin/relativeTime') dayjs.extend(relativeTime) -const PageModule = props => ( -
- - - - - - -
+const PageModule = props => { + const [copyOptionsVisible, setCopyOptionsVisible] = useState(false) + + const copyModule = readOnly => { + APIUtil.copyModule(props.module.draftId, readOnly) + setCopyOptionsVisible(false) + } + + let expositionRender = [] + + let previewButtonRender = null + if (props.canPreview) { + previewButtonRender = ( Preview this module + ) + expositionRender.push( + 'Click the "Preview this module" button below to view the module as a learner would!' + ) + } + + let copyButtonRender = null + if (props.canCopy) { + if (copyOptionsVisible) { + copyButtonRender = ( +
+ + + +
+ ) + } else { + copyButtonRender = ( +
+ +
+ ) + } + expositionRender.push( + 'Click one of the buttons below to create a copy of this module (or to cancel the copy process).' + ) + expositionRender.push( + 'Note: A read-only copy will not be editable, and can be synchronized with the original from the dashboard.' + ) + } + + if (expositionRender.length) { + expositionRender = expositionRender.map((string, index) => ( + + {string} + + )) + } + + return ( +
+ + + + + + +
+ {expositionRender} + {previewButtonRender} + {copyButtonRender} + +
+ Module created by{' '} + + {props.owner.firstName} {props.owner.lastName} + {' '} + on {dayjs(props.module.createdAt).format('MMM D, YYYY')} and updated{' '} + {dayjs(props.module.updatedAt).fromNow()}. +
+ +

Use this Module in your Canvas Course

+

+ This module can be used inside your course as an assignment or module. +

+ +
+ Animated gif showing how to create an Obojobo Assignment in canvas +
Creating an Assignment in Canvas
+
+ +

Create an Assignment in Canvas

+
    +
  1. Click Assignments in your course's menu.
  2. +
  3. Create a new Assignment
  4. +
  5. + Set the: +
      +
    • Assignment Name
    • +
    • Points (do not use 0)
    • +
    • any other relevant settings
    • +
    +
  6. +
  7. Set Submission Type to "External Tool"
  8. +
  9. + Follow the Choosing a Obojobo Module instructions + below +
  10. +
  11. Click Select
  12. +
  13. Save & Publish
  14. +
+ +

Create an Ungraded Module in Canvas

+
    +
  1. Click Modules in your course's menu.
  2. +
  3. Click the "+" in a module
  4. +
  5. Type an Assignment Name
  6. +
  7. + Change the top drop down from "Assignment" to "External Tool". +
  8. +
  9. + Follow the Choosing a Obojobo Module instructions + below +
  10. +
  11. Click Add Item
  12. +
  13. + You new module will be named "{props.module.title} (doesn't send scores to + grade book)" +
  14. +
  15. + Be sure to Publish within Canvas when ready +
  16. +
- - -
- Module created by{' '} - - {props.owner.firstName} {props.owner.lastName} - {' '} - on {dayjs(props.module.createdAt).format('MMM D, YYYY')} and updated{' '} - {dayjs(props.module.updatedAt).fromNow()}. -
- -

Use this Module in your Canvas Course

-

- This module can be used inside your course as an assignment or module. -

- -
- Animated gif showing how to create an Obojobo Assignment in canvas -
Creating an Assignment in Canvas
-
- -

Create an Assignment in Canvas

-
    -
  1. Click Assignments in your course's menu.
  2. -
  3. Create a new Assignment
  4. -
  5. - Set the: -
      -
    • Assignment Name
    • -
    • Points (do not use 0)
    • -
    • any other relevant settings
    • -
    -
  6. -
  7. Set Submission Type to "External Tool"
  8. -
  9. - Follow the Choosing a Obojobo Module instructions - below -
  10. -
  11. Click Select
  12. -
  13. Save & Publish
  14. -
- -

Create an Ungraded Module in Canvas

-
    -
  1. Click Modules in your course's menu.
  2. -
  3. Click the "+" in a module
  4. -
  5. Type an Assignment Name
  6. -
  7. Change the top drop down from "Assignment" to "External Tool".
  8. -
  9. - Follow the Choosing a Obojobo Module instructions - below -
  10. -
  11. Click Add Item
  12. -
  13. - You new module will be named "{props.module.title} (doesn't send scores to grade - book)" -
  14. -
  15. - Be sure to Publish within Canvas when ready -
  16. -
- -

Choosing a Obojobo Module

-
    -
  1. Follow one of the sets of instructions above.
  2. -
  3. - Click "FIND" next to the input labeled "Enter or find an External Tool - URL" -
  4. -
  5. - In the popup that appears, scroll down and select "ObojoboNext Module (gradebook - synced)" -
  6. -
  7. Choose Community Collection
  8. -
  9. - Search for the module by its title ({props.module.title}) or its id ( - {props.module.draftId}) -
  10. -
  11. Click Embed next to your chosen module
  12. -
-
-
-) +

Choosing a Obojobo Module

+
    +
  1. Follow one of the sets of instructions above.
  2. +
  3. + Click "FIND" next to the input labeled "Enter or find an External Tool + URL" +
  4. +
  5. + In the popup that appears, scroll down and select "ObojoboNext Module (gradebook + synced)" +
  6. +
  7. Choose Community Collection
  8. +
  9. + Search for the module by its title ({props.module.title}) or its id ( + {props.module.draftId}) +
  10. +
  11. Click Embed next to your chosen module
  12. +
+
+
+ ) +} module.exports = PageModule diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module.scss b/packages/app/obojobo-repository/shared/components/pages/page-module.scss index c6390c805a..3e4cc028f9 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-module.scss +++ b/packages/app/obojobo-repository/shared/components/pages/page-module.scss @@ -1,7 +1,17 @@ @import '../layouts/default.scss'; @import '../layouts/footer.scss'; -.copy-button { +.exposition-text { + display: block; + font-size: 0.8em; +} + +.copy-button-container { display: inline-block; - margin: 0.5em; + margin: 0 0.5em; + + .copy-button { + display: inline-block; + margin: 0 0.5em; + } } diff --git a/packages/app/obojobo-repository/shared/components/pages/page-module.test.js b/packages/app/obojobo-repository/shared/components/pages/page-module.test.js index 6e3b1ed2de..57c61106f5 100644 --- a/packages/app/obojobo-repository/shared/components/pages/page-module.test.js +++ b/packages/app/obojobo-repository/shared/components/pages/page-module.test.js @@ -3,10 +3,10 @@ jest.mock('dayjs') import React from 'react' import PageModule from './page-module' -import renderer from 'react-test-renderer' -import { shallow } from 'enzyme' +import { create, act } from 'react-test-renderer' const Button = require('../button') +const ButtonLink = require('../button-link') const APIUtil = require('../../api-util') const dayjs = require('dayjs') @@ -26,6 +26,8 @@ describe('PageModule', () => { }) beforeEach(() => { + jest.clearAllMocks() + mockCurrentUser = { id: 99, avatarUrl: '/path/to/avatar/img', @@ -42,28 +44,189 @@ describe('PageModule', () => { } }) - test('renders when given props', () => { - const component = renderer.create( - + test('renders when given props - current user can copy and preview', () => { + const component = create( + ) + + const previewButton = component.root.findAllByType(ButtonLink) + expect(previewButton.length).toBe(1) + + const copyButtonArea = component.root.findAllByProps({ className: 'copy-button-container' }) + expect(copyButtonArea.length).toBe(1) + + // should only have one button in it + const copyButtons = copyButtonArea[0].findAllByType(Button) + expect(copyButtons.length).toBe(1) + expect(copyButtons[0].props.children).toBe('Copy this module') + const tree = component.toJSON() expect(tree).toMatchSnapshot() }) - test('reacts properly when the copy button is clicked', () => { - const component = shallow( + test('renders when given props - current user can not copy or preview', () => { + const component = create( ) - component.find(Button).simulate('click') + const previewButton = component.root.findAllByType(ButtonLink) + expect(previewButton.length).toBe(0) + + const copyButtonArea = component.root.findAllByProps({ className: 'copy-button-container' }) + expect(copyButtonArea.length).toBe(0) + + const tree = component.toJSON() + expect(tree).toMatchSnapshot() + }) + + test('Shows copy options when the initial copy button is clicked', () => { + let component + act(() => { + component = create( + + ) + }) + + const copyButtonArea = component.root.findByProps({ className: 'copy-button-container' }) + + // should only have one button in it + let copyButtons = copyButtonArea.findAllByType(Button) + expect(copyButtons.length).toBe(1) + expect(copyButtons[0].props.children).toBe('Copy this module') + + act(() => { + copyButtons[0].props.onClick() + }) + + // single button should be replaced with three buttons + copyButtons = copyButtonArea.findAllByType(Button) + expect(copyButtons.length).toBe(3) + + expect(copyButtons[0].props.children).toBe('Normal Copy') + expect(copyButtons[1].props.children).toBe('Read-Only Copy') + expect(copyButtons[2].props.children).toBe('Cancel') + }) + + test('Runs correct functions when the "Normal Copy" button is clicked', () => { + let component + act(() => { + component = create( + + ) + }) + + const copyButtonArea = component.root.findByProps({ className: 'copy-button-container' }) + let copyButtons = copyButtonArea.findAllByType(Button) + + act(() => { + copyButtons[0].props.onClick() + }) + + copyButtons = copyButtonArea.findAllByType(Button) + + act(() => { + copyButtons[0].props.onClick() + }) expect(APIUtil.copyModule).toHaveBeenCalledTimes(1) - expect(APIUtil.copyModule).toHaveBeenCalledWith('mockDraftId') + expect(APIUtil.copyModule).toHaveBeenCalledWith('mockDraftId', false) + }) + + test('Runs correct functions when the "Read-Only Copy" button is clicked', () => { + let component + act(() => { + component = create( + + ) + }) + + const copyButtonArea = component.root.findByProps({ className: 'copy-button-container' }) + let copyButtons = copyButtonArea.findAllByType(Button) + + act(() => { + copyButtons[0].props.onClick() + }) + + copyButtons = copyButtonArea.findAllByType(Button) + + act(() => { + copyButtons[1].props.onClick() + }) + + expect(APIUtil.copyModule).toHaveBeenCalledTimes(1) + expect(APIUtil.copyModule).toHaveBeenCalledWith('mockDraftId', true) + }) + + test('Runs correct functions when the "Cancel" button is clicked', () => { + let component + act(() => { + component = create( + + ) + }) + + const copyButtonArea = component.root.findByProps({ className: 'copy-button-container' }) + + // should only have one button in it + let copyButtons = copyButtonArea.findAllByType(Button) + expect(copyButtons.length).toBe(1) + expect(copyButtons[0].props.children).toBe('Copy this module') + + act(() => { + copyButtons[0].props.onClick() + }) + + // single button should be replaced with three buttons + copyButtons = copyButtonArea.findAllByType(Button) + expect(copyButtons.length).toBe(3) + + expect(copyButtons[0].props.children).toBe('Normal Copy') + expect(copyButtons[1].props.children).toBe('Read-Only Copy') + expect(copyButtons[2].props.children).toBe('Cancel') + + act(() => { + copyButtons[2].props.onClick() + }) + + copyButtons = copyButtonArea.findAllByType(Button) + expect(copyButtons.length).toBe(1) + expect(copyButtons[0].props.children).toBe('Copy this module') }) }) diff --git a/packages/app/obojobo-repository/shared/components/stats/assessment-stats-controls.scss b/packages/app/obojobo-repository/shared/components/stats/assessment-stats-controls.scss index 07858890f7..11300063e0 100644 --- a/packages/app/obojobo-repository/shared/components/stats/assessment-stats-controls.scss +++ b/packages/app/obojobo-repository/shared/components/stats/assessment-stats-controls.scss @@ -5,6 +5,31 @@ margin-bottom: 1em; } + .filter-controls { + display: flex; + flex-direction: column; + width: 100%; + + hr { + margin-top: 1em; + } + + input { + margin-right: 0.5em; + margin-left: 0; + } + + label { + cursor: pointer; + display: flex; + align-items: center; + + span { + white-space: nowrap; + } + } + } + .search-controls { select { @include select-input(); @@ -90,29 +115,4 @@ font-family: $font-monospace; } } - - .filter-controls { - display: flex; - flex-direction: column; - width: 100%; - - hr { - margin-top: 1em; - } - - input { - margin-right: 0.5em; - margin-left: 0; - } - - label { - cursor: pointer; - display: flex; - align-items: center; - - span { - white-space: nowrap; - } - } - } } diff --git a/packages/app/obojobo-repository/shared/reducers/dashboard-reducer.js b/packages/app/obojobo-repository/shared/reducers/dashboard-reducer.js index 00ba884f43..191a13c58a 100644 --- a/packages/app/obojobo-repository/shared/reducers/dashboard-reducer.js +++ b/packages/app/obojobo-repository/shared/reducers/dashboard-reducer.js @@ -18,6 +18,8 @@ const { SELECT_MODULES, DESELECT_MODULES, SHOW_MODULE_MORE, + SHOW_MODULE_SYNC, + SYNC_MODULE_UPDATES, CREATE_NEW_COLLECTION, SHOW_MODULE_MANAGE_COLLECTIONS, LOAD_MODULE_COLLECTIONS, @@ -114,9 +116,10 @@ function DashboardReducer(state, action) { }) }) + case SYNC_MODULE_UPDATES: case DELETE_MODULE: return handle(state, action, { - // close the dialog containing the delete button + // close the dialog start: () => ({ ...state, ...closedDialogState() }), // update myModules and re-apply the filter if one exists success: prevState => { @@ -181,6 +184,24 @@ function DashboardReducer(state, action) { selectedModule: action.module } + case SHOW_MODULE_SYNC: + return handle(state, action, { + // open the dialog while the fetch is in progress + start: () => ({ + ...state, + dialog: 'module-sync', + selectedModule: action.meta.module, + newest: false + }), + // update myModules and re-apply the filter if one exists + success: prevState => { + return { + ...prevState, + newest: action.payload.value + } + } + }) + case SHOW_MODULE_PERMISSIONS: return { ...state, diff --git a/packages/app/obojobo-repository/shared/reducers/dashboard-reducer.test.js b/packages/app/obojobo-repository/shared/reducers/dashboard-reducer.test.js index 2da368adaf..eb4a636c35 100644 --- a/packages/app/obojobo-repository/shared/reducers/dashboard-reducer.test.js +++ b/packages/app/obojobo-repository/shared/reducers/dashboard-reducer.test.js @@ -25,6 +25,8 @@ const { SELECT_MODULES, DESELECT_MODULES, SHOW_MODULE_MORE, + SHOW_MODULE_SYNC, + SYNC_MODULE_UPDATES, CREATE_NEW_COLLECTION, SHOW_MODULE_MANAGE_COLLECTIONS, LOAD_MODULE_COLLECTIONS, @@ -181,8 +183,9 @@ describe('Dashboard Reducer', () => { expect(newState.collection.title).toEqual(expectedCollectionTitle) } - const runCreateOrDeleteModuleActionTest = testAction => { - const isDeleteModuleTest = testAction === DELETE_MODULE + const runCreateOrSyncUpdatesOrDeleteModuleActionTest = testAction => { + const isSyncOrDeleteModuleTest = + testAction === DELETE_MODULE || testAction === SYNC_MODULE_UPDATES const mockModuleList = [ { draftId: 'mockDraftId', @@ -196,7 +199,7 @@ describe('Dashboard Reducer', () => { const initialState = { dialog: 'module-options', - moduleSearchString: isDeleteModuleTest ? 'B' : '', + moduleSearchString: isSyncOrDeleteModuleTest ? 'B' : '', myModules: [ { draftId: 'oldMockDraftId', @@ -226,7 +229,7 @@ describe('Dashboard Reducer', () => { // DELETE_MODULE changes state on start AND success, CREATE_MODULE just on success let newState - if (isDeleteModuleTest) { + if (isSyncOrDeleteModuleTest) { newState = handleStart(handler) expect(newState.dialog).toBe(null) // no module list changes should have happened yet @@ -237,7 +240,7 @@ describe('Dashboard Reducer', () => { newState = handleSuccess(handler) expect(newState.myModules).not.toEqual(initialState.myModules) expect(newState.myModules).toEqual(mockModuleList) - if (isDeleteModuleTest) { + if (isSyncOrDeleteModuleTest) { expect(newState.filteredModules).not.toEqual(initialState.filteredModules) expect(newState.moduleSearchString).toEqual(initialState.moduleSearchString) expect(newState.filteredModules).toEqual([{ ...mockModuleList[1] }]) @@ -436,12 +439,16 @@ describe('Dashboard Reducer', () => { }) test('CREATE_NEW_MODULE action modifies state correctly', () => { - runCreateOrDeleteModuleActionTest(CREATE_NEW_MODULE) + runCreateOrSyncUpdatesOrDeleteModuleActionTest(CREATE_NEW_MODULE) }) //DELETE_MODULE is more or less the same as CREATE_MODULE, but will auto-filter new modules test('DELETE_MODULE action modifies state correctly', () => { - runCreateOrDeleteModuleActionTest(DELETE_MODULE) + runCreateOrSyncUpdatesOrDeleteModuleActionTest(DELETE_MODULE) + }) + //SYNC_MODULE_UPDATES should be identical to DELETE_MODULE + test('SYNC_MODULE_UPDATE action modifies state correctly', () => { + runCreateOrSyncUpdatesOrDeleteModuleActionTest(SYNC_MODULE_UPDATES) }) test('BULK_DELETE_MODULES action modifies state correctly', () => { @@ -586,6 +593,43 @@ describe('Dashboard Reducer', () => { expect(newState.selectedModule).toEqual(mockSelectedModule) }) + test('SHOW_MODULE_SYNC action modifies state correctly', () => { + const initialState = { + dialog: null, + selectedModule: { + draftId: 'someMockDraftId', + title: 'Some Mock Module Title' + } + } + + const mockModule = { + draftId: 'originalMockDraftId', + title: 'Some New Mock Module Title' + } + const action = { + type: SHOW_MODULE_SYNC, + meta: { + module: { + draftId: 'originalMockDraftId', + title: 'Some New Mock Module Title' + } + }, + payload: { + value: mockModule + } + } + + // asynchronous action - state changes on success + const handler = dashboardReducer(initialState, action) + let newState + + newState = handleStart(handler) + expect(newState.newest).toEqual(false) + + newState = handleSuccess(handler) + expect(newState.newest).toEqual(mockModule) + }) + test('SHOW_MODULE_PERMISSIONS action modifies state correctly', () => { const initialState = { dialog: null, diff --git a/packages/obonode/obojobo-chunks-abstract-assessment/package.json b/packages/obonode/obojobo-chunks-abstract-assessment/package.json index 762e6b161a..6c7aee920f 100644 --- a/packages/obonode/obojobo-chunks-abstract-assessment/package.json +++ b/packages/obonode/obojobo-chunks-abstract-assessment/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-abstract-assessment", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "AbstractAssessment chunk for Obojobo", "scripts": { @@ -28,8 +28,8 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-chunks-question": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-chunks-question": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "devDependencies": { "lint-staged": "^10.2.2" diff --git a/packages/obonode/obojobo-chunks-action-button/package.json b/packages/obonode/obojobo-chunks-action-button/package.json index d4c0c186a8..930d71c513 100644 --- a/packages/obonode/obojobo-chunks-action-button/package.json +++ b/packages/obonode/obojobo-chunks-action-button/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-action-button", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "scripts": { "test": "TZ='America/New_York' jest --verbose", @@ -20,7 +20,7 @@ ] }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-break/package.json b/packages/obonode/obojobo-chunks-break/package.json index 856e4e9922..19805c1fb2 100644 --- a/packages/obonode/obojobo-chunks-break/package.json +++ b/packages/obonode/obojobo-chunks-break/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-break", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Break content chunk for Obojobo", "scripts": { @@ -21,7 +21,7 @@ ] }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-code/package.json b/packages/obonode/obojobo-chunks-code/package.json index 3ba905c67d..d97bb24cf8 100644 --- a/packages/obonode/obojobo-chunks-code/package.json +++ b/packages/obonode/obojobo-chunks-code/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-code", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Code content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-code/viewer-component.scss b/packages/obonode/obojobo-chunks-code/viewer-component.scss index 5d8226a8e4..f138bdf736 100644 --- a/packages/obonode/obojobo-chunks-code/viewer-component.scss +++ b/packages/obonode/obojobo-chunks-code/viewer-component.scss @@ -27,7 +27,7 @@ } code { - font-family: inherit; /* stylelint-disable-line sh-waqar/declaration-use-variable */ + font-family: inherit; /* stylelint-disable-line scale-unlimited/declaration-strict-value */ } } diff --git a/packages/obonode/obojobo-chunks-excerpt/components/edge-controls.scss b/packages/obonode/obojobo-chunks-excerpt/components/edge-controls.scss index 704e587709..7b15ed13fb 100644 --- a/packages/obonode/obojobo-chunks-excerpt/components/edge-controls.scss +++ b/packages/obonode/obojobo-chunks-excerpt/components/edge-controls.scss @@ -42,6 +42,10 @@ $lighter-blue: lighten($color-obojobo-blue, 60%); transform: translate(-4px); } + &:nth-child(4) { + transform: translate(-6px); + } + &.is-selected { background-color: $lighter-blue; border: 2px solid $color-obojobo-blue; @@ -79,6 +83,14 @@ $lighter-blue: lighten($color-obojobo-blue, 60%); &.is-edge-jagged::after { background-image: url('../images/icon-jagged-edge.png'); } + + &.is-edge-bookmark::after { + background-image: url('../images/icon-bookmark.png'); + + &.is-bottom-edge-type- { + display: none; + } + } } span { diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.jsx b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.jsx index c6a272b8de..676ade542d 100644 --- a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.jsx +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.jsx @@ -11,7 +11,9 @@ import ExcerptEditControls from '../excerpt-edit-controls' import Common from 'obojobo-document-engine/src/scripts/common' const { Button } = Common.components -const getEdgeOptionsForBodyStyle = bodyStyle => { +const edgeOptions = {top: [], bottom: []} + +const getEdgeOptionsForBodyStyle = (bodyStyle, edgeOptions) => { switch (bodyStyle) { case 'callout-try-it': case 'callout-practice': @@ -19,7 +21,9 @@ const getEdgeOptionsForBodyStyle = bodyStyle => { case 'callout-example': case 'callout-hint': case 'none': - return [] + edgeOptions.top = [] + edgeOptions.bottom = [] + return edgeOptions case 'filled-box': case 'bordered-box': @@ -29,10 +33,14 @@ const getEdgeOptionsForBodyStyle = bodyStyle => { case 'light-yellow-paper': case 'dark-yellow-paper': case 'aged-paper': - return ['normal', 'fade', 'jagged'] + edgeOptions.top = ['normal', 'fade', 'jagged', 'bookmark'] + edgeOptions.bottom = ['normal', 'fade', 'jagged'] + return edgeOptions default: - return ['normal', 'fade'] + edgeOptions.top = ['normal', 'fade'] + edgeOptions.bottom = ['normal', 'fade'] + return edgeOptions } } @@ -76,7 +84,7 @@ const ExcerptContent = props => { return Transforms.removeNodes(props.editor, { at: parentPath }) } - const edgeOptions = getEdgeOptionsForBodyStyle(props.element.content.bodyStyle) + getEdgeOptionsForBodyStyle(props.element.content.bodyStyle, edgeOptions) const shouldShowEdgeControls = props.selected && Range.isCollapsed(props.editor.selection) @@ -86,13 +94,13 @@ const ExcerptContent = props => { onChangeContentValue('topEdge', edgeType)} /> onChangeContentValue('bottomEdge', edgeType)} /> diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.test.js b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.test.js index d3e8672638..da8f46a884 100644 --- a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.test.js +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.test.js @@ -352,14 +352,14 @@ describe('Excerpt Content Node', () => { assertEdgeControlsHaveOptions('callout-example', []) assertEdgeControlsHaveOptions('callout-hint', []) assertEdgeControlsHaveOptions('none', []) - assertEdgeControlsHaveOptions('filled-box', ['normal', 'fade', 'jagged']) - assertEdgeControlsHaveOptions('bordered-box', ['normal', 'fade', 'jagged']) - assertEdgeControlsHaveOptions('card', ['normal', 'fade', 'jagged']) - assertEdgeControlsHaveOptions('white-paper', ['normal', 'fade', 'jagged']) - assertEdgeControlsHaveOptions('modern-paper', ['normal', 'fade', 'jagged']) - assertEdgeControlsHaveOptions('light-yellow-paper', ['normal', 'fade', 'jagged']) - assertEdgeControlsHaveOptions('dark-yellow-paper', ['normal', 'fade', 'jagged']) - assertEdgeControlsHaveOptions('aged-paper', ['normal', 'fade', 'jagged']) + assertEdgeControlsHaveOptions('filled-box', ['normal', 'fade', 'jagged', 'bookmark']) + assertEdgeControlsHaveOptions('bordered-box', ['normal', 'fade', 'jagged', 'bookmark']) + assertEdgeControlsHaveOptions('card', ['normal', 'fade', 'jagged', 'bookmark']) + assertEdgeControlsHaveOptions('white-paper', ['normal', 'fade', 'jagged', 'bookmark']) + assertEdgeControlsHaveOptions('modern-paper', ['normal', 'fade', 'jagged', 'bookmark']) + assertEdgeControlsHaveOptions('light-yellow-paper', ['normal', 'fade', 'jagged', 'bookmark']) + assertEdgeControlsHaveOptions('dark-yellow-paper', ['normal', 'fade', 'jagged', 'bookmark']) + assertEdgeControlsHaveOptions('aged-paper', ['normal', 'fade', 'jagged', 'bookmark']) assertEdgeControlsHaveOptions('', ['normal', 'fade']) }) }) diff --git a/packages/obonode/obojobo-chunks-excerpt/images/icon-bookmark.png b/packages/obonode/obojobo-chunks-excerpt/images/icon-bookmark.png new file mode 100644 index 0000000000..98ee4871d1 Binary files /dev/null and b/packages/obonode/obojobo-chunks-excerpt/images/icon-bookmark.png differ diff --git a/packages/obonode/obojobo-chunks-excerpt/package.json b/packages/obonode/obojobo-chunks-excerpt/package.json index c940ceeec2..4b2d465251 100644 --- a/packages/obonode/obojobo-chunks-excerpt/package.json +++ b/packages/obonode/obojobo-chunks-excerpt/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-excerpt", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Excerpt content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-excerpt/viewer-component.scss b/packages/obonode/obojobo-chunks-excerpt/viewer-component.scss index 6d60ce0efc..e848f2617d 100644 --- a/packages/obonode/obojobo-chunks-excerpt/viewer-component.scss +++ b/packages/obonode/obojobo-chunks-excerpt/viewer-component.scss @@ -44,6 +44,10 @@ since it is not compatible with stylelint's indentation rules $callout-blue: rgb(29, 119, 252); + $bookmark-border-white: white white white #efefef; + $bookmark-border-yellow: white white white #f9f6f2; + $bookmark-border-aged: white white white #cdb7a1; + position: relative; > blockquote { @@ -437,7 +441,7 @@ since it is not compatible with stylelint's indentation rules left: 2em; top: 0; transform: translate(0, -100%); - background: url(./images/tab.svg); + background: url('./images/tab.svg'); background-size: cover; } } @@ -489,7 +493,7 @@ since it is not compatible with stylelint's indentation rules position: absolute; left: 0; top: -1.2em; - background: url(./images/retro-document-top.svg); + background: url('./images/retro-document-top.svg'); } &.is-showing-effect { @@ -797,7 +801,6 @@ since it is not compatible with stylelint's indentation rules */ // stylelint-disable-next-line no-descending-specificity > blockquote > .excerpt-content { - background: #fbfbfd; background: #f9f8f7; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); border: 1px solid rgba(0, 0, 0, 0.03); @@ -996,7 +999,7 @@ since it is not compatible with stylelint's indentation rules border-top-right-radius: 0; &::before { - background: url(./images/jagged-edge-bordered-box.svg); + background: url('./images/jagged-edge-bordered-box.svg'); } } } @@ -1026,7 +1029,7 @@ since it is not compatible with stylelint's indentation rules border-bottom-right-radius: 0; &::after { - background: url(./images/jagged-edge-bordered-box.svg); + background: url('./images/jagged-edge-bordered-box.svg'); } } } @@ -1035,74 +1038,205 @@ since it is not compatible with stylelint's indentation rules &.is-top-edge-type-jagged.is-body-style-type-filled-box, &.is-top-edge-type-jagged.is-body-style-type-card { > blockquote > .excerpt-content::before { - background: url(./images/jagged-edge-filled-box.svg); + background: url('./images/jagged-edge-filled-box.svg'); } } &.is-top-edge-type-jagged.is-body-style-type-white-paper { > blockquote > .excerpt-content::before { - background: url(./images/jagged-edge-white-paper.svg); + background: url('./images/jagged-edge-white-paper.svg'); } } &.is-top-edge-type-jagged.is-body-style-type-modern-paper { > blockquote > .excerpt-content::before { - background: url(./images/jagged-edge-modern-paper.svg); + background: url('./images/jagged-edge-modern-paper.svg'); } } &.is-top-edge-type-jagged.is-body-style-type-light-yellow-paper { > blockquote > .excerpt-content::before { - background: url(./images/jagged-edge-light-yellow-paper.svg); + background: url('./images/jagged-edge-light-yellow-paper.svg'); } } &.is-top-edge-type-jagged.is-body-style-type-dark-yellow-paper { > blockquote > .excerpt-content::before { - background: url(./images/jagged-edge-dark-yellow-paper.svg); + background: url('./images/jagged-edge-dark-yellow-paper.svg'); } } &.is-top-edge-type-jagged.is-body-style-type-aged-paper { > blockquote > .excerpt-content::before { - background: url(./images/jagged-edge-aged-paper.svg); + background: url('./images/jagged-edge-aged-paper.svg'); } } &.is-bottom-edge-type-jagged.is-body-style-type-filled-box, &.is-bottom-edge-type-jagged.is-body-style-type-card { > blockquote > .excerpt-content::after { - background: url(./images/jagged-edge-filled-box.svg); + background: url('./images/jagged-edge-filled-box.svg'); } } &.is-bottom-edge-type-jagged.is-body-style-type-white-paper { > blockquote > .excerpt-content::after { - background: url(./images/jagged-edge-white-paper.svg); + background: url('./images/jagged-edge-white-paper.svg'); } } &.is-bottom-edge-type-jagged.is-body-style-type-modern-paper { > blockquote > .excerpt-content::after { - background: url(./images/jagged-edge-modern-paper.svg); + background: url('./images/jagged-edge-modern-paper.svg'); } } &.is-bottom-edge-type-jagged.is-body-style-type-light-yellow-paper { > blockquote > .excerpt-content::after { - background: url(./images/jagged-edge-light-yellow-paper.svg); + background: url('./images/jagged-edge-light-yellow-paper.svg'); } } &.is-bottom-edge-type-jagged.is-body-style-type-dark-yellow-paper { > blockquote > .excerpt-content::after { - background: url(./images/jagged-edge-dark-yellow-paper.svg); + background: url('./images/jagged-edge-dark-yellow-paper.svg'); } } &.is-bottom-edge-type-jagged.is-body-style-type-aged-paper { > blockquote > .excerpt-content::after { - background: url(./images/jagged-edge-aged-paper.svg); + background: url('./images/jagged-edge-aged-paper.svg'); + } + } + + &.is-top-edge-type-bookmark { + /* + stylelint complains about this rule's specificity, but fixing it would + require deconstructing every style hierarchy so we're ignoring it instead + */ + // stylelint-disable-next-line no-descending-specificity + > blockquote > .excerpt-content::before { + content: ' '; + width: 100%; + // this only seems to work at 10px for some reason + // stylelint-disable-next-line unit-disallowed-list + height: 10px; + position: absolute; + left: 0; + top: 0; + outline: none; + } + + &.is-body-style-type-bordered-box { + > blockquote > .excerpt-content { + border-top-left-radius: 0; + border-top-right-radius: 0; + + &::before { + width: 0; + height: 0; + border-style: solid; + border-width: 1.25em 0 0 1.25em; + border-color: $bookmark-border-white; + position: absolute; + top: -0.12em; + left: -0.12em; + filter: drop-shadow(-0.125em 0.125em 0.063em rgba(0, 0, 0, 0.25)); + transform: rotate(-0.25turn); + } + } + } + } + + &.is-top-edge-type-bookmark.is-body-style-type-filled-box, + &.is-top-edge-type-bookmark.is-body-style-type-card { + > blockquote > .excerpt-content::before { + width: 0; + height: 0; + border-style: solid; + border-width: 1.25em 0 0 1.25em; + border-color: $bookmark-border-white; + position: absolute; + top: -0.12em; + left: -0.12em; + filter: drop-shadow(-0.125em 0.125em 0.063em rgba(0, 0, 0, 0.25)); + transform: rotate(-0.25turn); + } + } + + &.is-top-edge-type-bookmark.is-body-style-type-white-paper { + > blockquote > .excerpt-content::before { + width: 0; + height: 0; + border-style: solid; + border-width: 1.25em 0 0 1.25em; + border-color: $bookmark-border-white; + position: absolute; + top: -0.12em; + left: -0.12em; + filter: drop-shadow(-0.125em 0.125em 0.063em rgba(0, 0, 0, 0.25)); + transform: rotate(-0.25turn); + } + } + + &.is-top-edge-type-bookmark.is-body-style-type-modern-paper { + > blockquote > .excerpt-content::before { + width: 0; + height: 0; + border-style: solid; + border-width: 1.25em 0 0 1.25em; + border-color: $bookmark-border-white; + position: absolute; + top: -0.12em; + left: -0.12em; + filter: drop-shadow(-0.125em 0.125em 0.063em rgba(0, 0, 0, 0.25)); + transform: rotate(-0.25turn); + } + } + + &.is-top-edge-type-bookmark.is-body-style-type-light-yellow-paper { + > blockquote > .excerpt-content::before { + width: 0; + height: 0; + border-style: solid; + border-width: 1.25em 0 0 1.25em; + border-color: $bookmark-border-yellow; + position: absolute; + top: -0.12em; + left: -0.12em; + filter: drop-shadow(-0.125em 0.125em 0.063em rgba(0, 0, 0, 0.25)); + transform: rotate(-0.25turn); + } + } + + &.is-top-edge-type-bookmark.is-body-style-type-dark-yellow-paper { + > blockquote > .excerpt-content::before { + width: 0; + height: 0; + border-style: solid; + border-width: 1.25em 0 0 1.25em; + border-color: $bookmark-border-yellow; + position: absolute; + top: -0.12em; + left: -0.12em; + filter: drop-shadow(-0.125em 0.125em 0.063em rgba(0, 0, 0, 0.25)); + transform: rotate(-0.25turn); + } + } + + &.is-top-edge-type-bookmark.is-body-style-type-aged-paper { + > blockquote > .excerpt-content::before { + width: 0; + height: 0; + border-style: solid; + border-width: 1.25em 0 0 1.25em; + outline: none; + border-color: $bookmark-border-aged; + position: absolute; + top: -0.12em; + left: -0.12em; + filter: drop-shadow(-0.125em 0.125em 0.063em rgba(0, 0, 0, 0.25)); + transform: rotate(-0.25turn); } } } diff --git a/packages/obonode/obojobo-chunks-figure/choose-image-modal.scss b/packages/obonode/obojobo-chunks-figure/choose-image-modal.scss index 5f027a2417..fbd84b46df 100644 --- a/packages/obonode/obojobo-chunks-figure/choose-image-modal.scss +++ b/packages/obonode/obojobo-chunks-figure/choose-image-modal.scss @@ -71,7 +71,7 @@ .choose-image--divider { /* stylelint-disable-next-line unit-disallowed-list */ height: 1px; - /* stylelint-disable-next-line sh-waqar/declaration-use-variable */ + /* stylelint-disable-next-line scale-unlimited/declaration-strict-value */ background-color: darken($color-bg2, 30%); margin: 0.5rem 0; } diff --git a/packages/obonode/obojobo-chunks-figure/package.json b/packages/obonode/obojobo-chunks-figure/package.json index b25036012c..72635b0c06 100644 --- a/packages/obonode/obojobo-chunks-figure/package.json +++ b/packages/obonode/obojobo-chunks-figure/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-figure", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Figure content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-heading/package.json b/packages/obonode/obojobo-chunks-heading/package.json index f2a69b2b14..bf3d841ff3 100644 --- a/packages/obonode/obojobo-chunks-heading/package.json +++ b/packages/obonode/obojobo-chunks-heading/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-heading", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Heading content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-html/package.json b/packages/obonode/obojobo-chunks-html/package.json index a9c56b0243..656f2198e4 100644 --- a/packages/obonode/obojobo-chunks-html/package.json +++ b/packages/obonode/obojobo-chunks-html/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-html", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "HTML content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-iframe/package.json b/packages/obonode/obojobo-chunks-iframe/package.json index 392c9676f2..31738d98e7 100644 --- a/packages/obonode/obojobo-chunks-iframe/package.json +++ b/packages/obonode/obojobo-chunks-iframe/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-iframe", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "IFrame content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-list/package.json b/packages/obonode/obojobo-chunks-list/package.json index 0c7faf0083..df92a3c006 100644 --- a/packages/obonode/obojobo-chunks-list/package.json +++ b/packages/obonode/obojobo-chunks-list/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-list", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "List content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-materia/package.json b/packages/obonode/obojobo-chunks-materia/package.json index 160fc2cdca..e1f5dc0fde 100644 --- a/packages/obonode/obojobo-chunks-materia/package.json +++ b/packages/obonode/obojobo-chunks-materia/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-materia", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Materia content chunk for Obojobo", "scripts": { @@ -35,9 +35,9 @@ "lint-staged": "^10.5.4" }, "peerDependencies": { - "obojobo-chunks-iframe": "^17.0.0-alpha.0", - "obojobo-document-engine": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-chunks-iframe": "^17.0.0", + "obojobo-document-engine": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-math-equation/package.json b/packages/obonode/obojobo-chunks-math-equation/package.json index 0e2e91a41c..098fe4ed1b 100644 --- a/packages/obonode/obojobo-chunks-math-equation/package.json +++ b/packages/obonode/obojobo-chunks-math-equation/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-math-equation", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "MathEquation content chunk for Obojobo", "scripts": { @@ -30,7 +30,7 @@ "katex": "^0.13.1" }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-math-equation/viewer-component.scss b/packages/obonode/obojobo-chunks-math-equation/viewer-component.scss index 87efb321ce..64c6f3ae07 100644 --- a/packages/obonode/obojobo-chunks-math-equation/viewer-component.scss +++ b/packages/obonode/obojobo-chunks-math-equation/viewer-component.scss @@ -25,12 +25,18 @@ left: 0; top: 0; font-size: 0.8em; - color: lighten($color-text, 30%); // stylelint-disable-line sh-waqar/declaration-use-variable + color: lighten( + $color-text, + 30% + ); // stylelint-disable-line scale-unlimited/declaration-strict-value &::before { content: 'Equation '; font-style: italic; - color: lighten($color-text, 50%); // stylelint-disable-line sh-waqar/declaration-use-variable + color: lighten( + $color-text, + 50% + ); // stylelint-disable-line scale-unlimited/declaration-strict-value } } diff --git a/packages/obonode/obojobo-chunks-multiple-choice-assessment/package.json b/packages/obonode/obojobo-chunks-multiple-choice-assessment/package.json index 19f86cf7c7..d2dc7c845d 100644 --- a/packages/obonode/obojobo-chunks-multiple-choice-assessment/package.json +++ b/packages/obonode/obojobo-chunks-multiple-choice-assessment/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-multiple-choice-assessment", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "MCAssessment chunk for Obojobo", "scripts": { @@ -27,8 +27,8 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-chunks-question": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-chunks-question": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-numeric-assessment/package.json b/packages/obonode/obojobo-chunks-numeric-assessment/package.json index a50e83eea5..7326fa972a 100644 --- a/packages/obonode/obojobo-chunks-numeric-assessment/package.json +++ b/packages/obonode/obojobo-chunks-numeric-assessment/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-numeric-assessment", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "NumericAssessment chunk for Obojobo", "scripts": { @@ -30,8 +30,8 @@ "big.js": "^6.1.1" }, "peerDependencies": { - "obojobo-chunks-question": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-chunks-question": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-question-bank/package.json b/packages/obonode/obojobo-chunks-question-bank/package.json index a8d969e289..42e275a02b 100644 --- a/packages/obonode/obojobo-chunks-question-bank/package.json +++ b/packages/obonode/obojobo-chunks-question-bank/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-question-bank", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "QuestionBank chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-question/package.json b/packages/obonode/obojobo-chunks-question/package.json index 4b40db0d60..cc18cfd8ae 100644 --- a/packages/obonode/obojobo-chunks-question/package.json +++ b/packages/obonode/obojobo-chunks-question/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-question", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Question content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-table/package.json b/packages/obonode/obojobo-chunks-table/package.json index 19d25e079b..f908b95153 100644 --- a/packages/obonode/obojobo-chunks-table/package.json +++ b/packages/obonode/obojobo-chunks-table/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-table", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Table content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-table/table-menu.scss b/packages/obonode/obojobo-chunks-table/table-menu.scss index c1b139ad9c..c6e419a453 100644 --- a/packages/obonode/obojobo-chunks-table/table-menu.scss +++ b/packages/obonode/obojobo-chunks-table/table-menu.scss @@ -12,7 +12,7 @@ &:hover { z-index: 2; - // stylelint-disable-next-line sh-waqar/declaration-use-variable + // stylelint-disable-next-line scale-unlimited/declaration-strict-value background-color: white; //$color-action; ul { @@ -58,7 +58,7 @@ font-size: 0.9em; height: auto; background: white; - // stylelint-disable-next-line sh-waqar/declaration-use-variable, font-family-no-missing-generic-family-keyword + // stylelint-disable-next-line scale-unlimited/declaration-strict-value, font-family-no-missing-generic-family-keyword font-family: 'Noto Sans'; font-weight: normal; list-style-type: none; @@ -78,7 +78,7 @@ &:hover { background: $color-action; - // stylelint-disable-next-line sh-waqar/declaration-use-variable + // stylelint-disable-next-line scale-unlimited/declaration-strict-value color: white; } diff --git a/packages/obonode/obojobo-chunks-text/package.json b/packages/obonode/obojobo-chunks-text/package.json index 53cf1a95d5..ce44221daa 100644 --- a/packages/obonode/obojobo-chunks-text/package.json +++ b/packages/obonode/obojobo-chunks-text/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-text", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Text content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-chunks-youtube/package.json b/packages/obonode/obojobo-chunks-youtube/package.json index cf257e2eaf..a9980db228 100644 --- a/packages/obonode/obojobo-chunks-youtube/package.json +++ b/packages/obonode/obojobo-chunks-youtube/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-chunks-youtube", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Youtube content chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-modules-module/package.json b/packages/obonode/obojobo-modules-module/package.json index c880c04cdf..81502c46f4 100644 --- a/packages/obonode/obojobo-modules-module/package.json +++ b/packages/obonode/obojobo-modules-module/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-modules-module", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Default Module node for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-pages-page/package.json b/packages/obonode/obojobo-pages-page/package.json index 59876685db..a65e66fed9 100644 --- a/packages/obonode/obojobo-pages-page/package.json +++ b/packages/obonode/obojobo-pages-page/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-pages-page", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Page chunk for Obojobo", "scripts": { @@ -27,8 +27,8 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-document-engine": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-document-engine": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-sections-assessment/components/rubric/rubric-modal.scss b/packages/obonode/obojobo-sections-assessment/components/rubric/rubric-modal.scss index 239364d669..454abe11e6 100644 --- a/packages/obonode/obojobo-sections-assessment/components/rubric/rubric-modal.scss +++ b/packages/obonode/obojobo-sections-assessment/components/rubric/rubric-modal.scss @@ -113,7 +113,7 @@ .obojobo-draft--components--button { .button { color: $color-action; - background-color: inherit; /* stylelint-disable-line sh-waqar/declaration-use-variable */ + background-color: inherit; /* stylelint-disable-line scale-unlimited/declaration-strict-value */ border-color: $color-action; border-width: 2px; diff --git a/packages/obonode/obojobo-sections-assessment/package.json b/packages/obonode/obojobo-sections-assessment/package.json index 6d25c9a738..002ccedbb7 100644 --- a/packages/obonode/obojobo-sections-assessment/package.json +++ b/packages/obonode/obojobo-sections-assessment/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-sections-assessment", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Assessment section for Obojobo", "scripts": { @@ -27,8 +27,8 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-document-engine": "^17.0.0-alpha.0", - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-document-engine": "^17.0.0", + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/obonode/obojobo-sections-assessment/server/attempt-resume.js b/packages/obonode/obojobo-sections-assessment/server/attempt-resume.js index 007a7be5c3..908af0644e 100644 --- a/packages/obonode/obojobo-sections-assessment/server/attempt-resume.js +++ b/packages/obonode/obojobo-sections-assessment/server/attempt-resume.js @@ -46,6 +46,7 @@ const resumeAttempt = async ( await Promise.all(attemptStart.getSendToClientPromises(assessmentNode, attempt.state, req, res)) attempt.questions = [] + attempt.questionResponses = [] for (const node of attempt.state.chosen) { if (node.type === QUESTION_NODE_TYPE) { @@ -54,6 +55,13 @@ const resumeAttempt = async ( } } + const responsesRaw = await AssessmentModel.fetchResponsesForAttempts([attempt.id]) + if (responsesRaw.get(attempt.id)) { + for (const response of responsesRaw.get(attempt.id)) { + attempt.questionResponses.push(response) + } + } + await insertEvent({ action: 'assessment:attemptResume', actorTime: new Date().toISOString(), diff --git a/packages/obonode/obojobo-sections-assessment/server/attempt-resume.test.js b/packages/obonode/obojobo-sections-assessment/server/attempt-resume.test.js index e19e726a58..892b0c2e2c 100644 --- a/packages/obonode/obojobo-sections-assessment/server/attempt-resume.test.js +++ b/packages/obonode/obojobo-sections-assessment/server/attempt-resume.test.js @@ -35,7 +35,7 @@ describe('Resume Attempt Route', () => { insertEvent.mockReset() }) - test('returns attempt with chosen questions', async () => { + test('returns attempt with chosen questions, questions do not have responses', async () => { expect.hasAssertions() const questionNode1 = { @@ -82,6 +82,9 @@ describe('Resume Attempt Route', () => { const mockCurrentUser = { id: 1 } AssessmentModel.fetchAttemptById.mockResolvedValue(mockAttempt) + const mockResponses = new Map() + AssessmentModel.fetchResponsesForAttempts.mockResolvedValue(mockResponses) + const result = await resumeAttempt( mockCurrentUser, mockCurrentVisit, @@ -98,6 +101,122 @@ describe('Resume Attempt Route', () => { "draftContentId": "mockContentId", "draftId": "mockDraftId", "number": "mockAttemptNumber", + "questionResponses": Array [], + "questions": Array [ + "mock-to-object", + "mock-to-object", + ], + "state": Object { + "chosen": Array [ + Object { + "id": "node1", + "type": "ObojoboDraft.Chunks.Question", + }, + Object { + "id": "node2", + "type": "ObojoboDraft.Chunks.Question", + }, + Object { + "id": "node3", + "type": "NOT_A_QUESTION_NODE_TYPE", + }, + ], + }, + } + `) + }) + + test('returns attempt with chosen questions, questions have responses', async () => { + expect.hasAssertions() + + const questionNode1 = { + id: 'node1', + type: QUESTION_NODE_TYPE + } + + const questionNode2 = { + id: 'node2', + type: QUESTION_NODE_TYPE + } + + const nonQuestionNode = { + id: 'node3', + type: 'NOT_A_QUESTION_NODE_TYPE' + } + + const mockAttempt = { + assessmentId: 'mockAssessmentId', + id: 'mockAttemptId', + number: 'mockAttemptNumber', + draftId: 'mockDraftId', + draftContentId: 'mockContentId', + state: { + chosen: [questionNode1, questionNode2, nonQuestionNode] + } + } + + const mockAssessmentNode = { + draftTree: { + // called to get question nodes by id + getChildNodeById: jest.fn().mockReturnValue({ + toObject: () => 'mock-to-object' + }) + } + } + + const mockCurrentDocument = { + draftId: 'mockDraftId', + contentId: 'mockContentId', + getChildNodeById: jest.fn().mockReturnValue(mockAssessmentNode) + } + const mockCurrentVisit = { id: 'mockVisitId', is_preview: 'mockIsPreview' } + const mockCurrentUser = { id: 1 } + AssessmentModel.fetchAttemptById.mockResolvedValue(mockAttempt) + + const mockResponsesMap = new Map() + mockResponsesMap.set(mockAttempt.id, [ + { + id: 0, + created_at: 0, + attempt_id: 'mock-attempt-id', + assessment_id: 'mock-assessment-id', + question_id: 'mock-question-id', + score: 0, + response: { value: 'mock-value-1' } + } + ]) + const mockResponses = mockResponsesMap + AssessmentModel.fetchResponsesForAttempts.mockResolvedValue(mockResponses) + + const result = await resumeAttempt( + mockCurrentUser, + mockCurrentVisit, + mockCurrentDocument, + 'mockAttemptId', + 'mockHostName', + 'mockRemoteAddress' + ) + + expect(result).toMatchInlineSnapshot(` + Object { + "assessmentId": "mockAssessmentId", + "attemptId": "mockAttemptId", + "draftContentId": "mockContentId", + "draftId": "mockDraftId", + "number": "mockAttemptNumber", + "questionResponses": Array [ + Object { + "assessment_id": "mock-assessment-id", + "attempt_id": "mock-attempt-id", + "created_at": 0, + "id": 0, + "question_id": "mock-question-id", + "response": Object { + "value": "mock-value-1", + }, + "score": 0, + }, + ], "questions": Array [ "mock-to-object", "mock-to-object", @@ -145,6 +264,11 @@ describe('Resume Attempt Route', () => { const mockCurrentUser = { id: 1 } AssessmentModel.fetchAttemptById.mockResolvedValue(mockAttempt) + const mockResponsesMap = new Map() + mockResponsesMap.set(mockAttempt.id, []) + const mockResponses = mockResponsesMap + AssessmentModel.fetchResponsesForAttempts.mockResolvedValue(mockResponses) + await resumeAttempt( mockCurrentUser, mockCurrentVisit, @@ -189,6 +313,11 @@ describe('Resume Attempt Route', () => { const mockCurrentVisit = { id: 'mockVisitId', is_preview: 'mockIsPreview' } const mockCurrentUser = { id: 1 } AssessmentModel.fetchAttemptById.mockResolvedValue(mockAttempt) + + const mockResponsesMap = new Map() + mockResponsesMap.set(mockAttempt.id, []) + const mockResponses = mockResponsesMap + AssessmentModel.fetchResponsesForAttempts.mockResolvedValue(mockResponses) // const mockAttempt = await AssessmentModel.getAttempt() await resumeAttempt( diff --git a/packages/obonode/obojobo-sections-assessment/server/attempt-save.js b/packages/obonode/obojobo-sections-assessment/server/attempt-save.js new file mode 100644 index 0000000000..0515570868 --- /dev/null +++ b/packages/obonode/obojobo-sections-assessment/server/attempt-save.js @@ -0,0 +1,20 @@ +const AssessmentModel = require('./models/assessment') + +const saveAttempt = async (req, res) => { + try { + await AssessmentModel.saveAttempt( + req.body.assessmentId, + req.params.attemptId, + req.currentUser.id, + req.body.draftId, + req.body.draftContentId, + req.body.state + ) + } catch { + return res.unexpected() + } + + return res.success() +} + +module.exports = saveAttempt diff --git a/packages/obonode/obojobo-sections-assessment/server/attempt-save.test.js b/packages/obonode/obojobo-sections-assessment/server/attempt-save.test.js new file mode 100644 index 0000000000..04a7406122 --- /dev/null +++ b/packages/obonode/obojobo-sections-assessment/server/attempt-save.test.js @@ -0,0 +1,55 @@ +jest.mock('./models/assessment') + +const AssessmentModel = require('./models/assessment') + +const saveAttempt = require('./attempt-save.js') + +describe('save attempt route', () => { + let mockRes + let mockReq + + beforeEach(() => { + jest.resetAllMocks() + + mockReq = { + body: { + draftId: 'mockDraftId', + draftContentId: 'mockDraftContentId', + assessmentId: 'mockAssessmentId', + state: { + currentQuestion: 0, + chosen: [] + } + }, + params: { + attemptId: 'mockAttemptId' + }, + currentUser: { id: 4 } + } + + mockRes = { + success: jest.fn(), + unexpected: jest.fn() + } + }) + + test('saveAttempt calls AssessmentModel.saveAttempt, no errors', () => { + AssessmentModel.saveAttempt.mockResolvedValueOnce(true) + + return saveAttempt(mockReq, mockRes).then(() => { + expect(AssessmentModel.saveAttempt).toHaveBeenCalled() + expect(mockRes.success).toHaveBeenCalled() + expect(mockRes.unexpected).not.toHaveBeenCalled() + }) + }) + + test('saveAttempt calls AssessmentModel.saveAttempt, errors', async () => { + AssessmentModel.saveAttempt.mockRejectedValueOnce('error') + + return await saveAttempt(mockReq, mockRes).then(() => { + expect(AssessmentModel.saveAttempt).toHaveBeenCalled() + expect(mockRes.success).not.toHaveBeenCalled() + expect(mockRes.unexpected).toHaveBeenCalled() + }) + }) +}) diff --git a/packages/obonode/obojobo-sections-assessment/server/events.js b/packages/obonode/obojobo-sections-assessment/server/events.js index e50a2f5422..e757e1b2e2 100644 --- a/packages/obonode/obojobo-sections-assessment/server/events.js +++ b/packages/obonode/obojobo-sections-assessment/server/events.js @@ -6,7 +6,7 @@ const db = require('obojobo-express/server/db') const eventRecordResponse = 'client:question:setResponse' const { isTrueParam } = require('obojobo-express/server/util/is_true_param') -// Store question responces +// Store question responses oboEvents.on(eventRecordResponse, async (event, req) => { try { if (!event.payload.attemptId) return // assume we're in practice diff --git a/packages/obonode/obojobo-sections-assessment/server/express.js b/packages/obonode/obojobo-sections-assessment/server/express.js index 0fb760fd11..0936f1cbf0 100644 --- a/packages/obonode/obojobo-sections-assessment/server/express.js +++ b/packages/obonode/obojobo-sections-assessment/server/express.js @@ -4,6 +4,7 @@ const AssessmentModel = require('./models/assessment') const lti = require('obojobo-express/server/lti') const logger = require('obojobo-express/server/logger') const { startAttempt } = require('./attempt-start') +const saveAttempt = require('./attempt-save') const resumeAttempt = require('./attempt-resume') const endAttempt = require('./attempt-end/attempt-end') const { attemptReview } = require('./attempt-review') @@ -78,6 +79,18 @@ router ]) .post(startAttempt) +router + .route('/api/assessments/attempt/:attemptId/save') + .post([ + requireCurrentUser, + requireCurrentVisit, + requireCurrentDocument, + requireAttemptId, + requireAssessmentId, + checkValidationRules + ]) + .post(saveAttempt) + router .route('/api/assessments/attempt/:attemptId/resume') .post([ diff --git a/packages/obonode/obojobo-sections-assessment/server/express.test.js b/packages/obonode/obojobo-sections-assessment/server/express.test.js index 37e3103aa4..bb8e526434 100644 --- a/packages/obonode/obojobo-sections-assessment/server/express.test.js +++ b/packages/obonode/obojobo-sections-assessment/server/express.test.js @@ -1,6 +1,7 @@ jest.mock('./models/assessment') jest.mock('obojobo-express/server/lti') jest.mock('./attempt-start') +jest.mock('./attempt-save') jest.mock('./attempt-resume') jest.mock('./attempt-end/attempt-end') jest.mock('./attempt-review') @@ -25,6 +26,7 @@ const request = require('supertest') const { attemptReview } = require('./attempt-review') const AssessmentModel = require('./models/assessment') const { startAttempt } = require('./attempt-start') +const saveAttempt = require('./attempt-save') const resumeAttempt = require('./attempt-resume') const endAttempt = require('./attempt-end/attempt-end') const lti = require('obojobo-express/server/lti') @@ -168,6 +170,32 @@ describe('server/express', () => { }) }) + test('POST /api/assessments/attempt/mock-attempt-id/save', async () => { + expect.hasAssertions() + const mockReturnValue = {} + saveAttempt.mockImplementationOnce((req, res, next) => { + res.success(mockReturnValue) + }) + + const response = await request(app) + .post('/api/assessments/attempt/mock-attempt-id/save') + .type('application/json') + + expect(response.statusCode).toBe(200) + // verify validations ran + expect(requireCurrentDocument).toHaveBeenCalledTimes(1) + expect(requireCurrentVisit).toHaveBeenCalledTimes(1) + expect(requireCurrentUser).toHaveBeenCalledTimes(1) + expect(requireAssessmentId).toHaveBeenCalledTimes(1) + expect(requireAttemptId).toHaveBeenCalledTimes(1) + + expect(saveAttempt).toHaveBeenCalledTimes(1) + expect(response.body).toEqual({ + status: 'ok', + value: mockReturnValue + }) + }) + test('POST /api/assessments/attempt/mock-attempt-id/resume', async () => { expect.hasAssertions() const mockReturnValue = {} diff --git a/packages/obonode/obojobo-sections-assessment/server/models/assessment.js b/packages/obonode/obojobo-sections-assessment/server/models/assessment.js index c72a747a2d..6cc5b35a35 100644 --- a/packages/obonode/obojobo-sections-assessment/server/models/assessment.js +++ b/packages/obonode/obojobo-sections-assessment/server/models/assessment.js @@ -59,6 +59,30 @@ class AssessmentModel { // @TODO: return hydrated Assessment objects } + // Save the state of an attempt in progress + static saveAttempt(assessmentId, attemptId, userId, draftId, draftContentId, state) { + return db + .none( + ` + UPDATE attempts + SET + updated_at = now(), + state = $[state] + WHERE + id = $[attemptId] + AND user_id = $[userId] + AND draft_id = $[draftId] + AND assessment_id = $[assessmentId] + AND draft_content_id = $[draftContentId] + `, + { attemptId, assessmentId, userId, draftId, draftContentId, state } + ) + .catch(error => { + logger.error('Assessment saveAttempt Error', error.message) + return Promise.reject(error) + }) + } + // Finish an attempt and write a new assessment score record static completeAttempt( assessmentId, @@ -426,7 +450,7 @@ class AssessmentModel { return db .manyOrNone( ` - SELECT id, attempt_id, assessment_id, question_id, score, response + SELECT id, created_at, attempt_id, assessment_id, question_id, score, response FROM attempts_question_responses WHERE attempt_id IN ($[attemptIds:csv]) ORDER BY updated_at`, diff --git a/packages/obonode/obojobo-sections-assessment/server/models/assessment.test.js b/packages/obonode/obojobo-sections-assessment/server/models/assessment.test.js index 52d32f1a8b..9ecfbfb9f4 100644 --- a/packages/obonode/obojobo-sections-assessment/server/models/assessment.test.js +++ b/packages/obonode/obojobo-sections-assessment/server/models/assessment.test.js @@ -115,6 +115,52 @@ describe('AssessmentModel', () => { `) }) + test('saveAttempt calls db.none', () => { + AssessmentModel.saveAttempt( + 'mock-assessment-id', + 'mock-attempt-id', + 'mock-user-id', + 'mock-draft-id', + 'mock-draft-content-id', + { + currentQuestion: 0, + chosen: [] + } + ) + + expect(db.none).toHaveBeenCalledTimes(1) + expect(db.none.mock.calls[0][1]).toMatchInlineSnapshot(` + Object { + "assessmentId": "mock-assessment-id", + "attemptId": "mock-attempt-id", + "draftContentId": "mock-draft-content-id", + "draftId": "mock-draft-id", + "state": Object { + "chosen": Array [], + "currentQuestion": 0, + }, + "userId": "mock-user-id", + } + `) + }) + + test('saveAttempt errors', () => { + db.none.mockRejectedValueOnce('mock-error') + return expect( + AssessmentModel.saveAttempt( + 'mock-assessment-id', + 'mock-attempt-id', + 'mock-user-id', + 'mock-draft-id', + 'mock-draft-content-id', + { + currentQuestion: 0, + chosen: [] + } + ) + ).rejects.toBe('mock-error') + }) + test('fetchAttemptHistoryDetails calls db.manyOrNone', () => { AssessmentModel.fetchAttemptHistoryDetails('mock-draft-id') diff --git a/packages/obonode/obojobo-sections-content/package.json b/packages/obonode/obojobo-sections-content/package.json index d93cf9f052..965bddbff4 100644 --- a/packages/obonode/obojobo-sections-content/package.json +++ b/packages/obonode/obojobo-sections-content/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-sections-content", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Content section chunk for Obojobo", "scripts": { @@ -27,7 +27,7 @@ "singleQuote": true }, "peerDependencies": { - "obojobo-lib-utils": "^17.0.0-alpha.0" + "obojobo-lib-utils": "^17.0.0" }, "jest": { "testMatch": [ diff --git a/packages/util/eslint-config-obojobo/package.json b/packages/util/eslint-config-obojobo/package.json index 4b1c396f4f..6e813cffc1 100644 --- a/packages/util/eslint-config-obojobo/package.json +++ b/packages/util/eslint-config-obojobo/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-obojobo", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "scripts": { "lint": "echo 'not implemented'", diff --git a/packages/util/obojobo-lib-utils/package.json b/packages/util/obojobo-lib-utils/package.json index 9140e0e299..b6f5ddd8bc 100644 --- a/packages/util/obojobo-lib-utils/package.json +++ b/packages/util/obojobo-lib-utils/package.json @@ -1,6 +1,6 @@ { "name": "obojobo-lib-utils", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "description": "Assortment of reusable parts for obojobo libraries.", "scripts": { @@ -37,6 +37,7 @@ "@babel/preset-env": "^7.13.12", "@babel/preset-react": "^7.13.13", "@babel/runtime": "^7.13.10", + "autoprefixer": "^10.4.14", "babel-core": "^7.0.0-bridge", "babel-eslint": "^10.1.0", "babel-jest": "26.6.3", @@ -48,9 +49,10 @@ "lint-staged": "^10.5.4", "prettier": "1.18.2", "react-test-renderer": "16.13.1", - "stylelint": "^13.12.0", + "stylelint": "^15.10.0", "stylelint-config-standard": "^21.0.0", - "stylelint-declaration-use-variable": "^1.7.2" + "stylelint-config-standard-scss": "^10.0.0", + "stylelint-declaration-strict-value": "^1.9.2" }, "bin": { "obojobo-migrate": "./bin/obojobo-migrate" diff --git a/packages/util/stylelint-config-obojobo/index.js b/packages/util/stylelint-config-obojobo/index.js index 64faae5f5f..7f2caf0a70 100644 --- a/packages/util/stylelint-config-obojobo/index.js +++ b/packages/util/stylelint-config-obojobo/index.js @@ -1,8 +1,7 @@ module.exports = { - "extends": "stylelint-config-standard", - "plugins": ["stylelint-declaration-use-variable"], + "extends": "stylelint-config-standard-scss", + "plugins": ["stylelint-declaration-strict-value"], "rules": { - "indentation": "tab", "at-rule-no-unknown": [ true, { @@ -18,6 +17,32 @@ module.exports = { } ], "color-hex-length": "long", - "sh-waqar/declaration-use-variable": [["/color/", "font-family"]] + "scale-unlimited/declaration-strict-value": [["/color/", "font-family"]], + + // all below are new rule configurations following the jump to version 15 + // consider overhauling all of the styles to validate against the defaults for these rules + // rather than ignore the rules entirely + "shorthand-property-no-redundant-values": null, + "color-function-notation": "legacy", + "media-feature-range-notation": "prefix", + "property-no-vendor-prefix": null, + "alpha-value-notation": "number", + "declaration-block-no-redundant-longhand-properties": null, + "number-max-precision": 10, + "no-invalid-position-at-import-rule": null, + + // allow for 'thing', 'thing-name', 'parent-name--thing-name', 'parent-name--thing-name--child-name' etc. + "selector-class-pattern": /([\w]+[-]{0,2})+[\w]+/, + "selector-id-pattern": /([\w]+[-]{0,2})+[\w]+/, + "keyframes-name-pattern": /([\w]+[-]{0,2})+[\w]+/, + + "scss/no-global-function-names": null, + "scss/at-mixin-argumentless-call-parentheses": null, + "scss/double-slash-comment-empty-line-before": null, + "scss/double-slash-comment-whitespace-inside": null, + "scss/dollar-variable-empty-line-before": null, + "scss/at-import-partial-extension": null, + "scss/at-import-no-partial-leading-underscore": null, + "scss/operator-no-unspaced": null, } } diff --git a/packages/util/stylelint-config-obojobo/package.json b/packages/util/stylelint-config-obojobo/package.json index 292416610e..76efdb2cd8 100644 --- a/packages/util/stylelint-config-obojobo/package.json +++ b/packages/util/stylelint-config-obojobo/package.json @@ -1,6 +1,6 @@ { "name": "stylelint-config-obojobo", - "version": "17.0.0-alpha.0", + "version": "17.0.0", "license": "AGPL-3.0-only", "scripts": { "lint": "echo 'not implemented'", @@ -12,7 +12,9 @@ "precommit": "echo 'not implemented'" }, "devDependencies": { + "autoprefixer": "^10.4.14", "stylelint-config-standard": "^21.0.0", - "stylelint-declaration-use-variable": "^1.7.2" + "stylelint-config-standard-scss": "^10.0.0", + "stylelint-declaration-strict-value": "^1.9.2" } } diff --git a/yarn.lock b/yarn.lock index 0de5b198c2..7695c177fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,19 +16,20 @@ dependencies: "@babel/highlight" "^7.12.13" -"@babel/code-frame@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== +"@babel/code-frame@^7.22.13": + version "7.22.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== dependencies: - "@babel/highlight" "^7.14.5" + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" "@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.8": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1" integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ== -"@babel/core@>=7.2.2", "@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5": +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5": version "7.13.10" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== @@ -71,7 +72,7 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.13.0", "@babel/generator@^7.13.9": +"@babel/generator@^7.13.9": version "7.13.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== @@ -80,14 +81,15 @@ jsesc "^2.5.1" source-map "^0.5.0" -"@babel/generator@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" - integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== +"@babel/generator@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.23.0" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" - source-map "^0.5.0" "@babel/helper-annotate-as-pure@^7.0.0": version "7.14.5" @@ -164,6 +166,11 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + "@babel/helper-explode-assignable-expression@^7.12.13": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f" @@ -180,14 +187,13 @@ "@babel/template" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/helper-function-name@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" - integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/helper-get-function-arity" "^7.14.5" - "@babel/template" "^7.14.5" - "@babel/types" "^7.14.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" "@babel/helper-get-function-arity@^7.12.13": version "7.12.13" @@ -196,13 +202,6 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-get-function-arity@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" - integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== - dependencies: - "@babel/types" "^7.14.5" - "@babel/helper-hoist-variables@^7.13.0": version "7.13.0" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.0.tgz#5d5882e855b5c5eda91e0cadc26c6e7a2c8593d8" @@ -211,12 +210,12 @@ "@babel/traverse" "^7.13.0" "@babel/types" "^7.13.0" -"@babel/helper-hoist-variables@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" - integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.22.5" "@babel/helper-member-expression-to-functions@^7.13.0", "@babel/helper-member-expression-to-functions@^7.13.12": version "7.13.12" @@ -319,12 +318,17 @@ dependencies: "@babel/types" "^7.12.13" -"@babel/helper-split-export-declaration@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" - integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: - "@babel/types" "^7.14.5" + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== "@babel/helper-validator-identifier@^7.12.11": version "7.12.11" @@ -336,6 +340,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-option@^7.12.17": version "7.12.17" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" @@ -369,16 +378,16 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10", "@babel/parser@^7.7.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.13.10", "@babel/parser@^7.7.0": version "7.13.12" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.12.tgz#ba320059420774394d3b0c0233ba40e4250b81d1" integrity sha512-4T7Pb244rxH24yR116LAuJ+adxXXnHhZaLJjegJVKSdoNCe4x1eDBaud5YIcQFcqzsaD5BHvJw5BQ0AZapdCRw== @@ -388,10 +397,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df" integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw== -"@babel/parser@^7.14.5": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.6.tgz#d85cc68ca3cac84eae384c06f032921f5227f4b2" - integrity sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12": version "7.13.12" @@ -1049,56 +1058,28 @@ "@babel/parser" "^7.12.13" "@babel/types" "^7.12.13" -"@babel/template@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" - integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.14.5" - "@babel/types" "^7.14.5" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.7.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" - integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.0" - "@babel/helper-function-name" "^7.12.13" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.13.0" - "@babel/types" "^7.13.0" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - -"@babel/traverse@^7.13.13": - version "7.13.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d" - integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.9" - "@babel/helper-function-name" "^7.12.13" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.13.13" - "@babel/types" "^7.13.13" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.4.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.5.tgz#c111b0f58afab4fea3d3385a406f692748c59870" - integrity sha512-G3BiS15vevepdmFqmUc9X+64y0viZYygubAMO8SvBmKARuF6CPSZtH4Ng9vi/lrWlZFGe3FWdXNy835akH8Glg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.5" - "@babel/helper-function-name" "^7.14.5" - "@babel/helper-hoist-variables" "^7.14.5" - "@babel/helper-split-export-declaration" "^7.14.5" - "@babel/parser" "^7.14.5" - "@babel/types" "^7.14.5" +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== + dependencies: + "@babel/code-frame" "^7.22.13" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" @@ -1111,7 +1092,7 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@babel/types@^7.13.13", "@babel/types@^7.13.14": +"@babel/types@^7.13.14": version "7.13.14" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d" integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ== @@ -1128,6 +1109,15 @@ "@babel/helper-validator-identifier" "^7.14.5" to-fast-properties "^2.0.0" +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1141,6 +1131,26 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@csstools/css-parser-algorithms@^2.3.0": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.1.tgz#ec4fc764ba45d2bb7ee2774667e056aa95003f3a" + integrity sha512-xrvsmVUtefWMWQsGgFffqWSK03pZ1vfDki4IVIIUxxDKnGBzqNgv0A7SB1oXtVNEkcVO8xi1ZrTL29HhSu5kGA== + +"@csstools/css-tokenizer@^2.1.1": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.2.0.tgz#9d70e6dcbe94e44c7400a2929928db35c4de32b5" + integrity sha512-wErmsWCbsmig8sQKkM6pFhr/oPha1bHfvxsUY5CYSQxwyhA9Ulrs8EqCgClhg4Tgg2XapVstGqSVcz0xOYizZA== + +"@csstools/media-query-list-parser@^2.1.2": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.3.tgz#4471ebd436a22019378fe9c8ac8c0f30c4fbb796" + integrity sha512-ATul1u+pic4aVpstgueqxEv4MsObEbszAxfTXpx9LHaeD3LAh+wFqdCteyegWmjk0k5rkSCAvIOaJe9U3DD09w== + +"@csstools/selector-specificity@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-3.0.0.tgz#798622546b63847e82389e473fd67f2707d82247" + integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g== + "@discoveryjs/json-ext@^0.5.0": version "0.5.2" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" @@ -1384,6 +1394,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": version "3.1.0" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" @@ -2109,14 +2128,6 @@ semver "^7.3.5" tar "^6.1.11" -"@mrmlnc/readdir-enhanced@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" - integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== - dependencies: - call-me-maybe "^1.0.1" - glob-to-regexp "^0.3.0" - "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -2130,11 +2141,6 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== -"@nodelib/fs.stat@^1.1.2": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" - integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== - "@nodelib/fs.walk@^1.2.3": version "1.2.6" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" @@ -2353,21 +2359,6 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@stylelint/postcss-css-in-js@^0.37.2": - version "0.37.2" - resolved "https://registry.yarnpkg.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz#7e5a84ad181f4234a2480803422a47b8749af3d2" - integrity sha512-nEhsFoJurt8oUmieT8qy4nk81WRHmJynmVwn/Vts08PL9fhgIsMhk1GId5yAN643OzqEEb5S/6At2TZW7pqPDA== - dependencies: - "@babel/core" ">=7.9.0" - -"@stylelint/postcss-markdown@^0.36.2": - version "0.36.2" - resolved "https://registry.yarnpkg.com/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz#0a540c4692f8dcdfc13c8e352c17e7bfee2bb391" - integrity sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ== - dependencies: - remark "^13.0.0" - unist-util-find-all-after "^3.0.2" - "@svgr/babel-plugin-add-jsx-attribute@^5.4.0": version "5.4.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" @@ -2619,13 +2610,6 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== -"@types/mdast@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.3.tgz#2d7d671b1cd1ea3deb306ea75036c2a0407d2deb" - integrity sha512-SXPBMnFVQg1s00dlMCc/jCdvPqdE4mXaMMCeRlxLDmTAEoegHT53xKtkDnzDTOcmMHUfcjyf36/YYZ6SxRdnsw== - dependencies: - "@types/unist" "*" - "@types/minimatch@*", "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -2636,6 +2620,11 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== +"@types/minimist@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" + integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== + "@types/node@*": version "14.14.35" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313" @@ -2712,27 +2701,6 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff" integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw== -"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" - integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== - -"@types/vfile-message@*": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/vfile-message/-/vfile-message-2.0.0.tgz#690e46af0fdfc1f9faae00cd049cc888957927d5" - integrity sha512-GpTIuDpb9u4zIO165fUy9+fXcULdD8HFRNli04GehoMVbeNq7D6OBnqSmg3lxZnC+UvgUhEWKxdKiwYUkGltIw== - dependencies: - vfile-message "*" - -"@types/vfile@^3.0.0": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/vfile/-/vfile-3.0.2.tgz#19c18cd232df11ce6fa6ad80259bc86c366b09b9" - integrity sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw== - dependencies: - "@types/node" "*" - "@types/unist" "*" - "@types/vfile-message" "*" - "@types/yargs-parser@*": version "20.2.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" @@ -3020,7 +2988,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -3180,6 +3148,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -3236,7 +3209,7 @@ array-includes@^3.1.2, array-includes@^3.1.3: get-intrinsic "^1.1.1" is-string "^1.0.5" -array-union@^1.0.1, array-union@^1.0.2: +array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= @@ -3322,11 +3295,6 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== - astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -3384,18 +3352,17 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^9.5.1, autoprefixer@^9.8.6: - version "9.8.6" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" - integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== +autoprefixer@^10.4.14: + version "10.4.14" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" + integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ== dependencies: - browserslist "^4.12.0" - caniuse-lite "^1.0.30001109" - colorette "^1.2.1" + browserslist "^4.21.5" + caniuse-lite "^1.0.30001464" + fraction.js "^4.2.0" normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.32" - postcss-value-parser "^4.1.0" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" aws-sign2@~0.7.0: version "0.7.0" @@ -3407,6 +3374,11 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +b4a@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" + integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== + babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -4141,11 +4113,6 @@ backbone@^1.3.3: dependencies: underscore ">=1.8.3" -bail@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" - integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -4339,7 +4306,7 @@ browserslist@^3.2.6: caniuse-lite "^1.0.30000844" electron-to-chromium "^1.3.47" -browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.3: +browserslist@^4.14.5, browserslist@^4.16.3: version "4.16.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== @@ -4350,6 +4317,16 @@ browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.3: escalade "^3.1.1" node-releases "^1.1.70" +browserslist@^4.21.5: + version "4.21.10" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== + dependencies: + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" + update-browserslist-db "^1.0.11" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -4520,30 +4497,6 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -call-me-maybe@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" - integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -4557,15 +4510,6 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" - integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c= - dependencies: - camelcase "^4.1.0" - map-obj "^2.0.0" - quick-lru "^1.0.0" - camelcase-keys@^6.1.0, camelcase-keys@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" @@ -4575,16 +4519,21 @@ camelcase-keys@^6.1.0, camelcase-keys@^6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" +camelcase-keys@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-7.0.2.tgz#d048d8c69448745bb0de6fc4c1c52a30dfbe7252" + integrity sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg== + dependencies: + camelcase "^6.3.0" + map-obj "^4.1.0" + quick-lru "^5.1.1" + type-fest "^1.2.1" + camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= -camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -4595,16 +4544,26 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== +camelcase@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + camelize@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= -caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001181: +caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001181: version "1.0.30001204" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz#256c85709a348ec4d175e847a3b515c66e79f2aa" integrity sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ== +caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001517: + version "1.0.30001519" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" + integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== + canvas@^2.6.1: version "2.11.2" resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860" @@ -4626,11 +4585,6 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -ccount@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" - integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== - chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -4642,7 +4596,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4659,7 +4613,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -4672,26 +4626,6 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -character-entities-html4@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" - integrity sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g== - -character-entities-legacy@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" - integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== - -character-entities@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" - integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== - -character-reference-invalid@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" - integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -4874,13 +4808,6 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-regexp@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-2.2.0.tgz#7d65e00885cd8796405c35a737e7a86b7429e36f" - integrity sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q== - dependencies: - is-regexp "^2.0.0" - clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -4924,11 +4851,6 @@ codemirror@^5.60.0: resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.60.0.tgz#00a8cfd287d5d8737ceb73987f04aee2fe5860da" integrity sha512-AEL7LhFOlxPlCL8IdTcJDblJm8yrAGib7I+DErJPdZd4l6imx8IMgKK3RblVgBQqz3TZJR4oknQ03bz+uNjBYA== -collapse-white-space@^1.0.2: - version "1.0.6" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" - integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== - collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" @@ -4987,6 +4909,11 @@ color@^4.2.3: color-convert "^2.0.1" color-string "^1.9.0" +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + colorette@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" @@ -5292,16 +5219,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" @@ -5324,6 +5241,16 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" + integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== + dependencies: + import-fresh "^3.2.1" + js-yaml "^4.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + create-jest-runner@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/create-jest-runner/-/create-jest-runner-0.6.0.tgz#9ca6583d969acc15cdc21cd07d430945daf83de6" @@ -5375,6 +5302,16 @@ css-color-keywords@^1.0.0: resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU= +css-color-names@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q== + +css-functions-list@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.2.0.tgz#8290b7d064bf483f48d6559c10e98dc4d1ad19ee" + integrity sha512-d/jBMPyYybkkLVypgtGv12R+pIFw4/f/IHtCTxWpZc8ofTYOPigIgmA6vu5rMHartZC+WuXhBUHfnyNUIQSYrg== + css-loader@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.0.tgz#a9ecda190500863673ce4434033710404efbff00" @@ -5419,6 +5356,11 @@ css-select@^3.1.2: domutils "^2.4.3" nth-check "^2.0.0" +css-shorthand-properties@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-shorthand-properties/-/css-shorthand-properties-1.1.1.tgz#1c808e63553c283f289f2dd56fcee8f3337bd935" + integrity sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A== + css-to-react-native@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" @@ -5444,6 +5386,23 @@ css-tree@^1.1.2: mdn-data "2.0.14" source-map "^0.6.1" +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-values@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/css-values/-/css-values-0.1.0.tgz#128b7ce103d4dc027a814a5d5995c54781d7b4c6" + integrity sha512-hQ6JSn4t/70aOCvdlP9zTOsFFUifMSKWz3PX7rz5NZl+uEHCqTFVJJvfP07isErCGEVHYoc8Orja8wLKZRvOeg== + dependencies: + css-color-names "0.0.4" + ends-with "^0.2.0" + postcss-value-parser "^3.3.0" + css-what@^3.2.1: version "3.4.2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" @@ -5605,7 +5564,7 @@ debug@2.6.9, debug@^2.1.0, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6. dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@~4.3.1: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@~4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -5619,7 +5578,7 @@ debug@^3.1.1, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.3.3: +debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -5631,7 +5590,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: +decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -5644,6 +5603,11 @@ decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decamelize@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.1.tgz#db11a92e58c741ef339fb0a2868d8a06a9a7b1e9" + integrity sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA== + decimal.js@^10.2.1: version "10.2.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" @@ -5836,10 +5800,10 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== -detect-libc@^2.0.0, detect-libc@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" - integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== +detect-libc@^2.0.0, detect-libc@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== detect-newline@^3.0.0: version "3.1.0" @@ -5872,13 +5836,6 @@ diff-sequences@^26.6.2: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== -dir-glob@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" - integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== - dependencies: - path-type "^3.0.0" - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -5955,7 +5912,7 @@ dom-serializer@^1.0.1, dom-serializer@~1.2.0: domhandler "^4.0.0" entities "^2.0.0" -domelementtype@1, domelementtype@^1.3.1: +domelementtype@1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== @@ -5972,13 +5929,6 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== - dependencies: - domelementtype "1" - domhandler@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" @@ -5986,7 +5936,7 @@ domhandler@^4.0.0: dependencies: domelementtype "^2.1.0" -domutils@^1.5.1, domutils@^1.7.0: +domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== @@ -6072,6 +6022,11 @@ electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.649: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.699.tgz#854eea9db8bc8109c409a4807bfdb200dd75a2c7" integrity sha512-fjt43CPXdPYwD9ybmKbNeLwZBmCVdLY2J5fGZub7/eMPuiqQznOGNXv/wurnpXIlE7ScHnvG9Zi+H4/i6uMKmw== +electron-to-chromium@^1.4.477: + version "1.4.484" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.484.tgz#770358eba089471c5dae5719db3a5a4fbf02bfb2" + integrity sha512-nO3ZEomTK2PO/3TUXgEx0A97xZTpKVf4p427lABHuCpT1IQ2N+njVh29DkQkCk6Q4m2wjU+faK4xAcfFndwjvw== + emittery@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" @@ -6111,6 +6066,11 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" +ends-with@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ends-with/-/ends-with-0.2.0.tgz#2f9da98d57a50cfda4571ce4339000500f4e6b8a" + integrity sha512-lRppY4dK3VkqBdR242sKcAJeYc8Gf/DhoX9AWvWI2RzccmLnqBQfwm2k4oSDv5MPDjUqawCauXhZkyWxkVhRsg== + enhanced-resolve@^5.15.0: version "5.15.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" @@ -6126,11 +6086,6 @@ enquirer@^2.3.5, enquirer@^2.3.6: dependencies: ansi-colors "^4.1.1" -entities@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== - entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" @@ -6516,13 +6471,6 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execall@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45" - integrity sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow== - dependencies: - clone-regexp "^2.1.0" - exenv@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" @@ -6659,7 +6607,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.2: +extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -6707,19 +6655,12 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^2.2.6: - version "2.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" - integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== - dependencies: - "@mrmlnc/readdir-enhanced" "^2.2.1" - "@nodelib/fs.stat" "^1.1.2" - glob-parent "^3.1.0" - is-glob "^4.0.0" - merge2 "^1.2.3" - micromatch "^3.1.10" +fast-fifo@^1.1.0, fast-fifo@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== -fast-glob@^3.2.5, fast-glob@^3.2.9: +fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -6730,6 +6671,17 @@ fast-glob@^3.2.5, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -6755,6 +6707,11 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== +fastest-levenshtein@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + fastq@^1.6.0: version "1.11.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" @@ -6783,13 +6740,6 @@ figures@^3.0.0, figures@^3.2.0: dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== - dependencies: - flat-cache "^2.0.1" - file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -6900,14 +6850,13 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + locate-path "^6.0.0" + path-exists "^4.0.0" flat-cache@^3.0.4: version "3.0.4" @@ -6917,11 +6866,6 @@ flat-cache@^3.0.4: flatted "^3.1.0" rimraf "^3.0.2" -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - flatted@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" @@ -6970,6 +6914,11 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -7147,16 +7096,6 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= -get-stdin@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" - integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== - -get-stdin@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" - integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== - get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -7257,11 +7196,6 @@ glob-parent@^5.0.0, glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -glob-to-regexp@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" - integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= - glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -7349,7 +7283,7 @@ globals@^9.18.0: resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== -globby@^11.0.2, globby@^11.0.3: +globby@^11.0.2, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -7372,20 +7306,6 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" - integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^1.0.2" - dir-glob "^2.2.2" - fast-glob "^2.2.6" - glob "^7.1.3" - ignore "^4.0.3" - pify "^4.0.1" - slash "^2.0.0" - globjoin@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" @@ -7400,13 +7320,6 @@ globule@^1.0.0: lodash "~4.17.10" minimatch "~3.0.2" -gonzales-pe@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" - integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== - dependencies: - minimist "^1.2.5" - got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -7606,22 +7519,10 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -html-tags@^3.0.0, html-tags@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" - integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== - -htmlparser2@^3.10.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== - dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" +html-tags@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== htmlparser2@^6.0.0: version "6.0.1" @@ -7791,12 +7692,12 @@ ignore-walk@^3.0.3: dependencies: minimatch "^3.0.4" -ignore@^4.0.3, ignore@^4.0.6: +ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.6, ignore@^5.1.8, ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.2.4: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -7806,14 +7707,6 @@ immer@^5.0.0: resolved "https://registry.yarnpkg.com/immer/-/immer-5.3.6.tgz#51eab8cbbeb13075fe2244250f221598818cac04" integrity sha512-pqWQ6ozVfNOUDjrLfm4Pt7q4Q12cGw2HUZgry4Q5+Myxu9nmHRkWBpI0J4+MK0AxbdFtdMTwEGVl7Vd+vEiK+A== -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -7868,20 +7761,15 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= - indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= +indent-string@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-5.0.0.tgz#4fd2980fccaf8622d14c64d694f4cf33c81951a5" + integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== infer-owner@^1.0.4: version "1.0.4" @@ -7901,7 +7789,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -8032,24 +7920,6 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-alphabetical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" - integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== - -is-alphanumeric@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4" - integrity sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ= - -is-alphanumerical@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" - integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== - dependencies: - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-arguments@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" @@ -8098,11 +7968,6 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" @@ -8122,6 +7987,13 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" +is-core-module@^2.5.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" @@ -8148,11 +8020,6 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== -is-decimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" - integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== - is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -8171,11 +8038,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - is-docker@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" @@ -8239,11 +8101,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-hexadecimal@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" - integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== - is-hotkey@^0.1.6: version "0.1.8" resolved "https://registry.yarnpkg.com/is-hotkey/-/is-hotkey-0.1.8.tgz#6b1f4b2d0e5639934e20c05ed24d623a21d36d25" @@ -8368,11 +8225,6 @@ is-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= -is-regexp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" - integrity sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA== - is-ssh@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.2.tgz#a4b82ab63d73976fd8263cceee27f99a88bdae2b" @@ -8436,21 +8288,11 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-whitespace-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" - integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-word-character@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" - integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== - is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" @@ -8955,6 +8797,11 @@ jest@26.6.3: import-local "^3.0.2" jest-cli "^26.6.3" +jiti@^1.18.2: + version "1.19.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.19.1.tgz#fa99e4b76a23053e0e7cde098efe1704a14c16f1" + integrity sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg== + js-base64@^2.4.9: version "2.6.4" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" @@ -8989,6 +8836,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -9197,15 +9051,10 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== -known-css-properties@^0.16.0: - version "0.16.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.16.0.tgz#3f0597214db11a460df77cd44bcb39e263b9da6c" - integrity sha512-0g5vDDPvNnQk7WM/aE92dTDxXJoOE0biiIcUb3qkn/F6h/ZQZPlZIbE2XSXH2vFPfphkgCxuR2vH6HHnobEOaQ== - -known-css-properties@^0.21.0: - version "0.21.0" - resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.21.0.tgz#15fbd0bbb83447f3ce09d8af247ed47c68ede80d" - integrity sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw== +known-css-properties@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.27.0.tgz#82a9358dda5fe7f7bd12b5e7142c0a205393c0c5" + integrity sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg== latest-version@^5.0.0: version "5.1.0" @@ -9398,6 +9247,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -9468,21 +9324,7 @@ lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== - dependencies: - chalk "^2.4.2" - -log-symbols@^4.0.0, log-symbols@^4.1.0: +log-symbols@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -9505,11 +9347,6 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== -longest-streak@^2.0.0, longest-streak@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" - integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -9652,16 +9489,16 @@ map-obj@^1.0.0, map-obj@^1.0.1: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= -map-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" - integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= - map-obj@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.0.tgz#0e8bc823e2aaca8a0942567d12ed14f389eec153" integrity sha512-NAq0fCmZYGz9UFEQyndp7sisrow4GroyGeKluyKC/chuITZsPyOyC1UJZPJlVFImhXdROIP5xqouRLThT3BbpQ== +map-obj@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" @@ -9669,61 +9506,21 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-escapes@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" - integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== - -markdown-table@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" - integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== - -mathml-tag-names@^2.1.0, mathml-tag-names@^2.1.3: +mathml-tag-names@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -mdast-util-compact@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.4.tgz#d531bb7667b5123abf20859be086c4d06c894593" - integrity sha512-3YDMQHI5vRiS2uygEFYaqckibpJtKq5Sj2c8JioeOQBU6INpKbdWzfyLqFFnDwEcEnRFIdMsguzs5pC1Jp4Isg== - dependencies: - unist-util-visit "^1.1.0" - -mdast-util-from-markdown@^0.8.0: - version "0.8.5" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c" - integrity sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ== - dependencies: - "@types/mdast" "^3.0.0" - mdast-util-to-string "^2.0.0" - micromark "~2.11.0" - parse-entities "^2.0.0" - unist-util-stringify-position "^2.0.0" - -mdast-util-to-markdown@^0.6.0: - version "0.6.5" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz#b33f67ca820d69e6cc527a93d4039249b504bebe" - integrity sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ== - dependencies: - "@types/unist" "^2.0.0" - longest-streak "^2.0.0" - mdast-util-to-string "^2.0.0" - parse-entities "^2.0.0" - repeat-string "^1.0.0" - zwitch "^1.0.0" - -mdast-util-to-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" - integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== - mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + mdn-data@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" @@ -9742,6 +9539,24 @@ memory-fs@^0.4.1: errno "^0.1.3" readable-stream "^2.0.1" +meow@^10.1.5: + version "10.1.5" + resolved "https://registry.yarnpkg.com/meow/-/meow-10.1.5.tgz#be52a1d87b5f5698602b0f32875ee5940904aa7f" + integrity sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw== + dependencies: + "@types/minimist" "^1.2.2" + camelcase-keys "^7.0.0" + decamelize "^5.0.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.2" + read-pkg-up "^8.0.0" + redent "^4.0.0" + trim-newlines "^4.0.2" + type-fest "^1.2.2" + yargs-parser "^20.2.9" + meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -9758,21 +9573,6 @@ meow@^3.3.0: redent "^1.0.0" trim-newlines "^1.0.0" -meow@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" - integrity sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig== - dependencies: - camelcase-keys "^4.0.0" - decamelize-keys "^1.0.0" - loud-rejection "^1.0.0" - minimist-options "^3.0.1" - normalize-package-data "^2.3.4" - read-pkg-up "^3.0.0" - redent "^2.0.0" - trim-newlines "^2.0.0" - yargs-parser "^10.0.0" - meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -9818,7 +9618,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -9828,14 +9628,6 @@ methods@^1.1.2, methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromark@~2.11.0: - version "2.11.4" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.11.4.tgz#d13436138eea826383e822449c9a5c50ee44665a" - integrity sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA== - dependencies: - debug "^4.0.0" - parse-entities "^2.0.0" - micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" @@ -9855,7 +9647,7 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -9910,7 +9702,7 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -min-indent@^1.0.0: +min-indent@^1.0.0, min-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== @@ -9952,14 +9744,6 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist-options@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" - integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== - dependencies: - arrify "^1.0.1" - is-plain-obj "^1.1.0" - minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.3, minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -10264,10 +10048,10 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" -node-addon-api@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501" - integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA== +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== node-fetch@^2.6.1: version "2.6.7" @@ -10336,6 +10120,11 @@ node-releases@^1.1.70: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + node-sass@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-8.0.0.tgz#c80d52148db0ce88610bcf1e1d112027393c13e1" @@ -10406,6 +10195,16 @@ normalize-package-data@^3.0.0: semver "^7.3.4" validate-npm-package-license "^3.0.1" +normalize-package-data@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -10421,12 +10220,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= - -normalize-selector@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" - integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== normalize-url@^3.3.0: version "3.3.0" @@ -10528,7 +10322,7 @@ npm-run-path@^4.0.0, npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^4.0.1, npmlog@^4.1.2: +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -10572,11 +10366,6 @@ nth-check@^2.0.0: dependencies: boolbase "^1.0.0" -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -10810,6 +10599,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -10831,6 +10627,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" @@ -10951,30 +10754,6 @@ parse-database-url@~0.3.0: dependencies: mongodb-uri ">= 0.9.7" -parse-entities@^1.0.2, parse-entities@^1.1.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.2.tgz#c31bf0f653b6661354f8973559cb86dd1d5edf50" - integrity sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg== - dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" - -parse-entities@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" - integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== - dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" - parse-github-repo-url@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" @@ -10995,7 +10774,7 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -11196,11 +10975,6 @@ pgpass@1.x: dependencies: split2 "^3.1.1" -picocolors@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" - integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -11290,43 +11064,14 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= -postcss-html@^0.36.0: - version "0.36.0" - resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-0.36.0.tgz#b40913f94eaacc2453fd30a1327ad6ee1f88b204" - integrity sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw== - dependencies: - htmlparser2 "^3.10.0" - -postcss-jsx@^0.36.3: - version "0.36.4" - resolved "https://registry.yarnpkg.com/postcss-jsx/-/postcss-jsx-0.36.4.tgz#37a68f300a39e5748d547f19a747b3257240bd50" - integrity sha512-jwO/7qWUvYuWYnpOb0+4bIIgJt7003pgU3P6nETBLaOyBXuTD55ho21xnals5nBrlpTIFodyd3/jBi6UO3dHvA== - dependencies: - "@babel/core" ">=7.2.2" - -postcss-less@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-3.1.4.tgz#369f58642b5928ef898ffbc1a6e93c958304c5ad" - integrity sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA== - dependencies: - postcss "^7.0.14" - -postcss-loader@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-5.2.0.tgz#ccd6668a778902d653602289c765a8bc481986dc" - integrity sha512-uSuCkENFeUaOYsKrXm0eNNgVIxc71z8RcckLMbVw473rGojFnrUeqEz6zBgXsH2q1EIzXnO/4pEz9RhALjlITA== - dependencies: - cosmiconfig "^7.0.0" - klona "^2.0.4" - semver "^7.3.4" - -postcss-markdown@^0.36.0: - version "0.36.0" - resolved "https://registry.yarnpkg.com/postcss-markdown/-/postcss-markdown-0.36.0.tgz#7f22849ae0e3db18820b7b0d5e7833f13a447560" - integrity sha512-rl7fs1r/LNSB2bWRhyZ+lM/0bwKv9fhl38/06gF6mKMo/NPnp55+K1dSTosSVjFZc0e1ppBlu+WT91ba0PMBfQ== +postcss-loader@^7.3.3: + version "7.3.3" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.3.tgz#6da03e71a918ef49df1bb4be4c80401df8e249dd" + integrity sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA== dependencies: - remark "^10.0.1" - unist-util-find-all-after "^1.0.2" + cosmiconfig "^8.2.0" + jiti "^1.18.2" + semver "^7.3.8" postcss-media-query-parser@^0.2.3: version "0.2.3" @@ -11361,53 +11106,30 @@ postcss-modules-values@^4.0.0: dependencies: icss-utils "^5.0.0" -postcss-reporter@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-6.0.1.tgz#7c055120060a97c8837b4e48215661aafb74245f" - integrity sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw== - dependencies: - chalk "^2.4.1" - lodash "^4.17.11" - log-symbols "^2.2.0" - postcss "^7.0.7" - postcss-resolve-nested-selector@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" integrity sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4= -postcss-safe-parser@^4.0.1, postcss-safe-parser@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96" - integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g== - dependencies: - postcss "^7.0.26" - -postcss-sass@^0.4.1, postcss-sass@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.4.4.tgz#91f0f3447b45ce373227a98b61f8d8f0785285a3" - integrity sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg== - dependencies: - gonzales-pe "^4.3.0" - postcss "^7.0.21" +postcss-safe-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz#bb4c29894171a94bc5c996b9a30317ef402adaa1" + integrity sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ== -postcss-scss@^2.0.0, postcss-scss@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-2.1.1.tgz#ec3a75fa29a55e016b90bf3269026c53c1d2b383" - integrity sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA== - dependencies: - postcss "^7.0.6" +postcss-scss@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.6.tgz#5d62a574b950a6ae12f2aa89b60d63d9e4432bfd" + integrity sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ== -postcss-selector-parser@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" - integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== +postcss-selector-parser@^6.0.13: + version "6.0.13" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== dependencies: - dot-prop "^5.2.0" - indexes-of "^1.0.1" - uniq "^1.0.1" + cssesc "^3.0.0" + util-deprecate "^1.0.2" -postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5: +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.11" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== @@ -11415,28 +11137,25 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss-syntax@^0.36.2: - version "0.36.2" - resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz#f08578c7d95834574e5593a82dfbfa8afae3b51c" - integrity sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w== +postcss-value-parser@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" + integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.35, postcss@^7.0.6, postcss@^7.0.7: - version "7.0.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" - integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== - dependencies: - picocolors "^0.2.1" - source-map "^0.6.1" +postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.2.8: - version "8.4.24" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" - integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== +postcss@^8.2.8, postcss@^8.4.25: + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -11464,10 +11183,10 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -prebuild-install@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.0.tgz#991b6ac16c81591ba40a6d5de93fb33673ac1370" - integrity sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA== +prebuild-install@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== dependencies: detect-libc "^2.0.0" expand-template "^2.0.3" @@ -11476,7 +11195,6 @@ prebuild-install@^7.1.0: mkdirp-classic "^0.5.3" napi-build-utils "^1.0.1" node-abi "^3.3.0" - npmlog "^4.0.1" pump "^3.0.0" rc "^1.2.7" simple-get "^4.0.0" @@ -11690,16 +11408,21 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -quick-lru@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" - integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" @@ -11938,6 +11661,15 @@ read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" +read-pkg-up@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-8.0.0.tgz#72f595b65e66110f43b052dd9af4de6b10534670" + integrity sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ== + dependencies: + find-up "^5.0.0" + read-pkg "^6.0.0" + type-fest "^1.0.1" + read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -11966,6 +11698,16 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +read-pkg@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-6.0.0.tgz#a67a7d6a1c2b0c3cd6aa2ea521f40c458a4a504c" + integrity sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^3.0.2" + parse-json "^5.2.0" + type-fest "^1.0.1" + read@1, read@1.0.x, read@~1.0.1: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" @@ -12061,14 +11803,6 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" -redent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" - integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= - dependencies: - indent-string "^3.0.0" - strip-indent "^2.0.0" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -12077,6 +11811,14 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +redent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-4.0.0.tgz#0c0ba7caabb24257ab3bb7a4fd95dd1d5c5681f9" + integrity sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag== + dependencies: + indent-string "^5.0.0" + strip-indent "^4.0.0" + redux-pack@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/redux-pack/-/redux-pack-0.1.5.tgz#1973b26ef749dfc020e8a61d54cf3e55ec8ae4cd" @@ -12222,79 +11964,6 @@ regjsparser@^0.6.4: dependencies: jsesc "~0.5.0" -remark-parse@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-6.0.3.tgz#c99131052809da482108413f87b0ee7f52180a3a" - integrity sha512-QbDXWN4HfKTUC0hHa4teU463KclLAnwpn/FBn87j9cKYJWWawbiLgMfP2Q4XwhxxuuuOxHlw+pSN0OKuJwyVvg== - dependencies: - collapse-white-space "^1.0.2" - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - is-word-character "^1.0.0" - markdown-escapes "^1.0.0" - parse-entities "^1.1.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - trim "0.0.1" - trim-trailing-lines "^1.0.0" - unherit "^1.0.4" - unist-util-remove-position "^1.0.0" - vfile-location "^2.0.0" - xtend "^4.0.1" - -remark-parse@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-9.0.0.tgz#4d20a299665880e4f4af5d90b7c7b8a935853640" - integrity sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw== - dependencies: - mdast-util-from-markdown "^0.8.0" - -remark-stringify@^6.0.0: - version "6.0.4" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-6.0.4.tgz#16ac229d4d1593249018663c7bddf28aafc4e088" - integrity sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg== - dependencies: - ccount "^1.0.0" - is-alphanumeric "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - longest-streak "^2.0.1" - markdown-escapes "^1.0.0" - markdown-table "^1.1.0" - mdast-util-compact "^1.0.0" - parse-entities "^1.0.2" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - stringify-entities "^1.0.1" - unherit "^1.0.4" - xtend "^4.0.1" - -remark-stringify@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-9.0.1.tgz#576d06e910548b0a7191a71f27b33f1218862894" - integrity sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg== - dependencies: - mdast-util-to-markdown "^0.6.0" - -remark@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/remark/-/remark-10.0.1.tgz#3058076dc41781bf505d8978c291485fe47667df" - integrity sha512-E6lMuoLIy2TyiokHprMjcWNJ5UxfGQjaMSMhV+f4idM625UjjK4j798+gPs5mfjzDE6vL0oFKVeZM6gZVSVrzQ== - dependencies: - remark-parse "^6.0.0" - remark-stringify "^6.0.0" - unified "^7.0.0" - -remark@^13.0.0: - version "13.0.0" - resolved "https://registry.yarnpkg.com/remark/-/remark-13.0.0.tgz#d15d9bf71a402f40287ebe36067b66d54868e425" - integrity sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA== - dependencies: - remark-parse "^9.0.0" - remark-stringify "^9.0.0" - unified "^9.1.0" - remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -12305,7 +11974,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== -repeat-string@^1.0.0, repeat-string@^1.5.4, repeat-string@^1.6.1: +repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -12317,11 +11986,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -replace-ext@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= - request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -12478,13 +12142,6 @@ revalidator@0.1.x: resolved "https://registry.yarnpkg.com/revalidator/-/revalidator-0.1.8.tgz#fece61bfa0c1b52a206bd6b18198184bdd523a3b" integrity sha1-/s5hv6DBtSoga9axgZgYS91SOjs= -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - rimraf@2.x.x, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -12716,7 +12373,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: +semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.8, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -12820,18 +12477,18 @@ shallowequal@^1.1.0: resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== -sharp@^0.30.5: - version "0.30.5" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.30.5.tgz#81c36fd05624978384ac6bd2744d23f9c82edefd" - integrity sha512-0T28KxqY4DzUMLSAp1/IhGVeHpPIQyp1xt7esmuXCAfyi/+6tYMUeRhQok+E/+E52Yk5yFjacXp90cQOkmkl4w== +sharp@^0.32.6: + version "0.32.6" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" + integrity sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== dependencies: color "^4.2.3" - detect-libc "^2.0.1" - node-addon-api "^5.0.0" - prebuild-install "^7.1.0" - semver "^7.3.7" + detect-libc "^2.0.2" + node-addon-api "^6.1.0" + prebuild-install "^7.1.1" + semver "^7.5.4" simple-get "^4.0.1" - tar-fs "^2.1.1" + tar-fs "^3.0.4" tunnel-agent "^0.6.0" shebang-command@^1.2.0: @@ -12880,6 +12537,13 @@ short-uuid@^3.1.1: any-base "^1.1.0" uuid "^3.3.2" +shortcss@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/shortcss/-/shortcss-0.1.3.tgz#ee2a7904d80b7f5502c98408f4a2f313faadfb48" + integrity sha512-MIOoTd99CIGTrAuGiMUx9VZrnrZmWzEHuKbGM/w+ia/w98cezhlN9w4aQOVSxswdoqkUnWrMw3tThOi3sevZAg== + dependencies: + css-shorthand-properties "^1.0.0" + shortid@^2.2.16: version "2.2.16" resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.16.tgz#b742b8f0cb96406fd391c76bfc18a67a57fe5608" @@ -12911,6 +12575,11 @@ signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -12951,11 +12620,6 @@ slash@^1.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -12998,15 +12662,6 @@ slate@0.57.2: is-plain-object "^3.0.0" tiny-warning "^1.0.3" -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== - dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" - slice-ansi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" @@ -13158,7 +12813,7 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^1.0.2: +source-map-js@^1.0.1, source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== @@ -13258,11 +12913,6 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" -specificity@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" - integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== - spex@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/spex/-/spex-3.2.0.tgz#fa4a21922407e112624977b445a6d634578a1127" @@ -13361,11 +13011,6 @@ stack-utils@^2.0.2: dependencies: escape-string-regexp "^2.0.0" -state-toggle@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" - integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== - static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -13401,6 +13046,14 @@ streamsearch@0.1.2, streamsearch@~0.1.2: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= +streamx@^2.15.0: + version "2.15.5" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.15.5.tgz#87bcef4dc7f0b883f9359671203344a4e004c7f1" + integrity sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg== + dependencies: + fast-fifo "^1.1.0" + queue-tick "^1.0.1" + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -13436,7 +13089,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13511,16 +13164,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-entities@^1.0.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.2.tgz#a98417e5471fd227b3e45d3db1861c11caf668f7" - integrity sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A== - dependencies: - character-entities-html4 "^1.0.0" - character-entities-legacy "^1.0.0" - is-alphanumerical "^1.0.0" - is-hexadecimal "^1.0.0" - stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" @@ -13592,11 +13235,6 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-indent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" - integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= - strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -13604,6 +13242,13 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-4.0.0.tgz#b41379433dd06f5eae805e21d631e07ee670d853" + integrity sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA== + dependencies: + min-indent "^1.0.1" + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -13660,11 +13305,33 @@ styled-components@^5.3.0: shallowequal "^1.1.0" supports-color "^5.5.0" +stylelint-config-recommended-scss@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-12.0.0.tgz#9d9e82c46012649f11bfebcbc788f58e61860f33" + integrity sha512-5Bb2mlGy6WLa30oNeKpZvavv2lowJUsUJO25+OA68GFTemlwd1zbFsL7q0bReKipOSU3sG47hKneZ6Nd+ctrFA== + dependencies: + postcss-scss "^4.0.6" + stylelint-config-recommended "^12.0.0" + stylelint-scss "^5.0.0" + +stylelint-config-recommended@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-12.0.0.tgz#d0993232fca017065fd5acfcb52dd8a188784ef4" + integrity sha512-x6x8QNARrGO2sG6iURkzqL+Dp+4bJorPMMRNPScdvaUK8PsynriOcMW7AFDKqkWAS5wbue/u8fUT/4ynzcmqdQ== + stylelint-config-recommended@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-4.0.0.tgz#665a0034065e6704d5032ba51bf4efa37d328ef9" integrity sha512-sgna89Ng+25Hr9kmmaIxpGWt2LStVm1xf1807PdcWasiPDaOTkOHRL61sINw0twky7QMzafCGToGDnHT/kTHtQ== +stylelint-config-standard-scss@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-10.0.0.tgz#159a54a01b80649bf0143fa7ba086b676a1a749e" + integrity sha512-bChBEo1p3xUVWh/wenJI+josoMk21f2yuLDGzGjmKYcALfl2u3DFltY+n4UHswYiXghqXaA8mRh+bFy/q1hQlg== + dependencies: + stylelint-config-recommended-scss "^12.0.0" + stylelint-config-standard "^33.0.0" + stylelint-config-standard@^21.0.0: version "21.0.0" resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-21.0.0.tgz#4942cfa27301eb6702fa8fc46a44da35d1a5cfd7" @@ -13672,127 +13339,76 @@ stylelint-config-standard@^21.0.0: dependencies: stylelint-config-recommended "^4.0.0" -stylelint-declaration-use-variable@^1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/stylelint-declaration-use-variable/-/stylelint-declaration-use-variable-1.7.2.tgz#476b2959ed062c407e1d3d507e33fb2dfe05113d" - integrity sha512-mZgrK7VCx7TYSaZbhWgcdsXxmNhlTkOza3pd2ad+sPNQWWP41R6vlRUonGWVc8BNcBXRSXPTAbLgieq1BXIGcQ== +stylelint-config-standard@^33.0.0: + version "33.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-33.0.0.tgz#1f7bb299153a53874073e93829e37a475842f0f9" + integrity sha512-eyxnLWoXImUn77+ODIuW9qXBDNM+ALN68L3wT1lN2oNspZ7D9NVGlNHb2QCUn4xDug6VZLsh0tF8NyoYzkgTzg== dependencies: - stylelint "11.1.1" + stylelint-config-recommended "^12.0.0" -stylelint@11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-11.1.1.tgz#6dbbb348036576ac6b033ddfedba72a877c1d6bb" - integrity sha512-Vx6TAJsxG6qksiFvxQTKriQhp1CqUWdpTDITEkAjTR+l+8Af7qNlvrUDXfpuFJgXh/ayF8xdMSKE+SstcsPmMA== +stylelint-declaration-strict-value@^1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/stylelint-declaration-strict-value/-/stylelint-declaration-strict-value-1.9.2.tgz#f2a884c669974a73f82c9f24b05beb81bc337480" + integrity sha512-Z/2yr7g4tq2iGOUWhZLzHL2g2GJYJGcPkfjDh++zI8ukLxW0tcLGJjo64XYCDjja6YcECPDUWbpN+OAoAtAYvw== dependencies: - autoprefixer "^9.5.1" - balanced-match "^1.0.0" - chalk "^2.4.2" - cosmiconfig "^5.2.0" - debug "^4.1.1" - execall "^2.0.0" - file-entry-cache "^5.0.1" - get-stdin "^7.0.0" - global-modules "^2.0.0" - globby "^9.2.0" - globjoin "^0.1.4" - html-tags "^3.0.0" - ignore "^5.0.6" - import-lazy "^4.0.0" - imurmurhash "^0.1.4" - known-css-properties "^0.16.0" - leven "^3.1.0" - lodash "^4.17.14" - log-symbols "^3.0.0" - mathml-tag-names "^2.1.0" - meow "^5.0.0" - micromatch "^4.0.0" - normalize-selector "^0.2.0" - postcss "^7.0.14" - postcss-html "^0.36.0" - postcss-jsx "^0.36.3" - postcss-less "^3.1.4" - postcss-markdown "^0.36.0" - postcss-media-query-parser "^0.2.3" - postcss-reporter "^6.0.1" - postcss-resolve-nested-selector "^0.1.1" - postcss-safe-parser "^4.0.1" - postcss-sass "^0.4.1" - postcss-scss "^2.0.0" - postcss-selector-parser "^3.1.0" - postcss-syntax "^0.36.2" - postcss-value-parser "^4.0.2" - resolve-from "^5.0.0" - signal-exit "^3.0.2" - slash "^3.0.0" - specificity "^0.4.1" - string-width "^4.1.0" - strip-ansi "^5.2.0" - style-search "^0.1.0" - sugarss "^2.0.0" - svg-tags "^1.0.0" - table "^5.2.3" - v8-compile-cache "^2.1.0" + css-values "^0.1.0" + shortcss "^0.1.3" -stylelint@^13.12.0: - version "13.13.1" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-13.13.1.tgz#fca9c9f5de7990ab26a00f167b8978f083a18f3c" - integrity sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ== +stylelint-scss@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-5.0.1.tgz#b33a6580b5734eace083cfc2cc3021225e28547f" + integrity sha512-n87iCRZrr2J7//I/QFsDXxFLnHKw633U4qvWZ+mOW6KDAp/HLj06H+6+f9zOuTYy+MdGdTuCSDROCpQIhw5fvQ== dependencies: - "@stylelint/postcss-css-in-js" "^0.37.2" - "@stylelint/postcss-markdown" "^0.36.2" - autoprefixer "^9.8.6" + postcss-media-query-parser "^0.2.3" + postcss-resolve-nested-selector "^0.1.1" + postcss-selector-parser "^6.0.13" + postcss-value-parser "^4.2.0" + +stylelint@^15.10.0: + version "15.10.2" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-15.10.2.tgz#0ee5a8371d3a2e1ff27fefd48309d3ddef7c3405" + integrity sha512-UxqSb3hB74g4DTO45QhUHkJMjKKU//lNUAOWyvPBVPZbCknJ5HjOWWZo+UDuhHa9FLeVdHBZXxu43eXkjyIPWg== + dependencies: + "@csstools/css-parser-algorithms" "^2.3.0" + "@csstools/css-tokenizer" "^2.1.1" + "@csstools/media-query-list-parser" "^2.1.2" + "@csstools/selector-specificity" "^3.0.0" balanced-match "^2.0.0" - chalk "^4.1.1" - cosmiconfig "^7.0.0" - debug "^4.3.1" - execall "^2.0.0" - fast-glob "^3.2.5" - fastest-levenshtein "^1.0.12" + colord "^2.9.3" + cosmiconfig "^8.2.0" + css-functions-list "^3.2.0" + css-tree "^2.3.1" + debug "^4.3.4" + fast-glob "^3.3.0" + fastest-levenshtein "^1.0.16" file-entry-cache "^6.0.1" - get-stdin "^8.0.0" global-modules "^2.0.0" - globby "^11.0.3" + globby "^11.1.0" globjoin "^0.1.4" - html-tags "^3.1.0" - ignore "^5.1.8" + html-tags "^3.3.1" + ignore "^5.2.4" import-lazy "^4.0.0" imurmurhash "^0.1.4" - known-css-properties "^0.21.0" - lodash "^4.17.21" - log-symbols "^4.1.0" + is-plain-object "^5.0.0" + known-css-properties "^0.27.0" mathml-tag-names "^2.1.3" - meow "^9.0.0" - micromatch "^4.0.4" - normalize-selector "^0.2.0" - postcss "^7.0.35" - postcss-html "^0.36.0" - postcss-less "^3.1.4" - postcss-media-query-parser "^0.2.3" + meow "^10.1.5" + micromatch "^4.0.5" + normalize-path "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.25" postcss-resolve-nested-selector "^0.1.1" - postcss-safe-parser "^4.0.2" - postcss-sass "^0.4.4" - postcss-scss "^2.1.1" - postcss-selector-parser "^6.0.5" - postcss-syntax "^0.36.2" - postcss-value-parser "^4.1.0" + postcss-safe-parser "^6.0.0" + postcss-selector-parser "^6.0.13" + postcss-value-parser "^4.2.0" resolve-from "^5.0.0" - slash "^3.0.0" - specificity "^0.4.1" - string-width "^4.2.2" - strip-ansi "^6.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" style-search "^0.1.0" - sugarss "^2.0.0" + supports-hyperlinks "^3.0.0" svg-tags "^1.0.0" - table "^6.6.0" - v8-compile-cache "^2.3.0" - write-file-atomic "^3.0.3" - -sugarss@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-2.0.0.tgz#ddd76e0124b297d40bf3cca31c8b22ecb43bc61d" - integrity sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ== - dependencies: - postcss "^7.0.2" + table "^6.8.1" + write-file-atomic "^5.0.1" superagent@^6.1.0: version "6.1.0" @@ -13860,6 +13476,14 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-hyperlinks@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz#c711352a5c89070779b4dad54c05a2f14b15c94b" + integrity sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -13909,17 +13533,7 @@ sysend@^1.3.5: resolved "https://registry.yarnpkg.com/sysend/-/sysend-1.10.0.tgz#af5f7d52b00947563fc9f4e2fbeb6db52912dd41" integrity sha512-kQDpqW60fvgbNLnWnTRaJ2KGX3aW5fThu9St8T4h+j8XC1YIDXhWVqS8Ho+WYgasmtrP7RvcRRhs+SVCb9o7wA== -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -table@^6.0.4, table@^6.6.0: +table@^6.0.4, table@^6.8.1: version "6.8.1" resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== @@ -13935,7 +13549,7 @@ tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== -tar-fs@^2.0.0, tar-fs@^2.1.1: +tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -13945,6 +13559,15 @@ tar-fs@^2.0.0, tar-fs@^2.1.1: pump "^3.0.0" tar-stream "^2.1.4" +tar-fs@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf" + integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w== + dependencies: + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^3.1.5" + tar-stream@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -13956,6 +13579,15 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" +tar-stream@^3.1.5: + version "3.1.6" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.6.tgz#6520607b55a06f4a2e2e04db360ba7d338cc5bab" + integrity sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + tar@^6.0.2, tar@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" @@ -14206,16 +13838,16 @@ trim-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= -trim-newlines@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" - integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= - trim-newlines@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== +trim-newlines@^4.0.2: + version "4.1.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-4.1.1.tgz#28c88deb50ed10c7ba6dc2474421904a00139125" + integrity sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ== + trim-off-newlines@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.3.tgz#8df24847fcb821b0ab27d58ab6efec9f2fe961a1" @@ -14226,21 +13858,6 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -trim-trailing-lines@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" - integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== - -trim@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= - -trough@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" - integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== - "true-case-path@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" @@ -14321,6 +13938,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^1.0.1, type-fest@^1.2.1, type-fest@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + type-is@^1.6.4, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -14385,14 +14007,6 @@ underscore@1.12.1, underscore@>=1.8.3: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== -unherit@^1.0.4: - version "1.1.3" - resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" - integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== - dependencies: - inherits "^2.0.0" - xtend "^4.0.0" - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -14416,32 +14030,6 @@ unicode-property-aliases-ecmascript@^1.0.4: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== -unified@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/unified/-/unified-7.1.0.tgz#5032f1c1ee3364bd09da12e27fdd4a7553c7be13" - integrity sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw== - dependencies: - "@types/unist" "^2.0.0" - "@types/vfile" "^3.0.0" - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^1.1.0" - trough "^1.0.0" - vfile "^3.0.0" - x-is-string "^0.1.0" - -unified@^9.1.0: - version "9.2.1" - resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.1.tgz#ae18d5674c114021bfdbdf73865ca60f410215a3" - integrity sha512-juWjuI8Z4xFg8pJbnEZ41b5xjGUWGHqXALmBZ3FC3WX0PIx1CZBIIJ6mXbYMcf6Yw4Fi0rFUTA1cdz/BglbOhA== - dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-buffer "^2.0.0" - is-plain-obj "^2.0.0" - trough "^1.0.0" - vfile "^4.0.0" - union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -14452,11 +14040,6 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -14492,63 +14075,6 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -unist-util-find-all-after@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-1.0.5.tgz#5751a8608834f41d117ad9c577770c5f2f1b2899" - integrity sha512-lWgIc3rrTMTlK1Y0hEuL+k+ApzFk78h+lsaa2gHf63Gp5Ww+mt11huDniuaoq1H+XMK2lIIjjPkncxXcDp3QDw== - dependencies: - unist-util-is "^3.0.0" - -unist-util-find-all-after@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz#fdfecd14c5b7aea5e9ef38d5e0d5f774eeb561f6" - integrity sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ== - dependencies: - unist-util-is "^4.0.0" - -unist-util-is@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" - integrity sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A== - -unist-util-is@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" - integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== - -unist-util-remove-position@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz#ec037348b6102c897703eee6d0294ca4755a2020" - integrity sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A== - dependencies: - unist-util-visit "^1.1.0" - -unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" - integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ== - -unist-util-stringify-position@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" - integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== - dependencies: - "@types/unist" "^2.0.2" - -unist-util-visit-parents@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz#25e43e55312166f3348cae6743588781d112c1e9" - integrity sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g== - dependencies: - unist-util-is "^3.0.0" - -unist-util-visit@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" - integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== - dependencies: - unist-util-visit-parents "^2.0.0" - universal-user-agent@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" @@ -14592,6 +14118,14 @@ upath@^2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + update-notifier@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" @@ -14717,7 +14251,7 @@ uuid@~3.1.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" integrity sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g== -v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.0, v8-compile-cache@^2.2.0, v8-compile-cache@^2.3.0: +v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== @@ -14765,46 +14299,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vfile-location@^2.0.0: - version "2.0.6" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.6.tgz#8a274f39411b8719ea5728802e10d9e0dff1519e" - integrity sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA== - -vfile-message@*, vfile-message@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" - integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== - dependencies: - "@types/unist" "^2.0.0" - unist-util-stringify-position "^2.0.0" - -vfile-message@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.1.1.tgz#5833ae078a1dfa2d96e9647886cd32993ab313e1" - integrity sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA== - dependencies: - unist-util-stringify-position "^1.1.1" - -vfile@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-3.0.1.tgz#47331d2abe3282424f4a4bb6acd20a44c4121803" - integrity sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ== - dependencies: - is-buffer "^2.0.0" - replace-ext "1.0.0" - unist-util-stringify-position "^1.0.0" - vfile-message "^1.0.0" - -vfile@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" - integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== - dependencies: - "@types/unist" "^2.0.0" - is-buffer "^2.0.0" - unist-util-stringify-position "^2.0.0" - vfile-message "^2.0.0" - w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -15192,6 +14686,14 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write-file-atomic@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^4.0.1" + write-json-file@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-3.2.0.tgz#65bbdc9ecd8a1458e15952770ccbadfcff5fe62a" @@ -15225,13 +14727,6 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - ws@^6.2.1: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" @@ -15244,11 +14739,6 @@ ws@^7.4.4: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== -x-is-string@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" - integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= - xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" @@ -15314,7 +14804,7 @@ xstate@^4.13.0: resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.13.0.tgz#0be22ceb8bae2bc6a025fab330fe44204d76771c" integrity sha512-UnUJJzP2KTPqnmxIoD/ymXtpy/hehZnUlO6EXqWC/72XkPb15p9Oz/X4WhS3QE+by7NP+6b5bCi/GTGFzm5D+A== -xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: +xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -15349,13 +14839,6 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" - integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== - dependencies: - camelcase "^4.1.0" - yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -15377,6 +14860,11 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== +yargs-parser@^20.2.9: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" @@ -15441,12 +14929,7 @@ yargs@^17.2.1: y18n "^5.0.5" yargs-parser "^21.1.1" -yarn@^1.22.19: - version "1.22.19" - resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.19.tgz#4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" - integrity sha512-/0V5q0WbslqnwP91tirOvldvYISzaqhClxzyUKXYxs07yUILIs5jx/k6CFe8bvKSkds5w+eiOqta39Wk3WxdcQ== - -zwitch@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" - integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==