From 6ea90e7c7b23a910efbdb948e9393cdc376705b8 Mon Sep 17 00:00:00 2001 From: Bhavin K <58818598+techbhavin@users.noreply.github.com> Date: Mon, 29 Nov 2021 21:07:49 +0530 Subject: [PATCH] feat: add document viewer widget (#7666) * created new widget * new lib for document viewer * added cypress tests * updated icon * handling different url types with viewer * add to ignore react-documents in jest transformIgnorePatterns * added jest test, updated comments * required changes * updated cypress test * updated icon svg * doc viewer new renderers added * comment as required * updated test case * added url / base64 validations * updated url checking condition for space and updated test case * cypress selector update * updated url validations * Merge branch 'release' into feature/new-document-viewer * lazy load external lib --- .../cypress/fixtures/DocumentViewerDsl.json | 45 ++ .../DisplayWidgets/DocumentViewer_spec.js | 18 + app/client/jest.config.js | 2 +- app/client/package.json | 3 + app/client/src/utils/WidgetRegistry.tsx | 4 + .../component/DocViewer.tsx | 53 ++ .../component/XlsxViewer.tsx | 204 ++++++++ .../component/index.test.tsx | 68 +++ .../DocumentViewerWidget/component/index.tsx | 220 +++++++++ .../widgets/DocumentViewerWidget/constants.ts | 23 + .../src/widgets/DocumentViewerWidget/icon.svg | 6 + .../src/widgets/DocumentViewerWidget/index.ts | 27 ++ .../widget/index.test.tsx | 127 +++++ .../DocumentViewerWidget/widget/index.tsx | 121 +++++ app/client/yarn.lock | 451 +++++++++++++++++- 15 files changed, 1349 insertions(+), 23 deletions(-) create mode 100644 app/client/cypress/fixtures/DocumentViewerDsl.json create mode 100644 app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/DocumentViewer_spec.js create mode 100644 app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx create mode 100644 app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx create mode 100644 app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx create mode 100644 app/client/src/widgets/DocumentViewerWidget/component/index.tsx create mode 100644 app/client/src/widgets/DocumentViewerWidget/constants.ts create mode 100644 app/client/src/widgets/DocumentViewerWidget/icon.svg create mode 100644 app/client/src/widgets/DocumentViewerWidget/index.ts create mode 100644 app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx create mode 100644 app/client/src/widgets/DocumentViewerWidget/widget/index.tsx diff --git a/app/client/cypress/fixtures/DocumentViewerDsl.json b/app/client/cypress/fixtures/DocumentViewerDsl.json new file mode 100644 index 0000000000..08067ae4f6 --- /dev/null +++ b/app/client/cypress/fixtures/DocumentViewerDsl.json @@ -0,0 +1,45 @@ +{ + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1280, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1230, + "containerStyle": "none", + "snapRows": 125, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 39, + "minHeight": 1240, + "parentColumnSpace": 1, + "dynamicTriggerPathList": [], + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "widgetName": "DocumentViewer1", + "rightColumn": 36, + "isCanvas": false, + "displayName": "Document Viewer", + "iconSVG": "/static/media/icon.5c440258.svg", + "widgetId": "0vgeledo9u", + "topRow": 20, + "bottomRow": 60, + "parentRowSpace": 10, + "isVisible": true, + "type": "DOCUMENT_VIEWER_WIDGET", + "version": 1, + "hideCard": false, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "parentColumnSpace": 19.8125, + "leftColumn": 12, + "docUrl": "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf", + "key": "gprnooq82y" + } + ] + } \ No newline at end of file diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/DocumentViewer_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/DocumentViewer_spec.js new file mode 100644 index 0000000000..935f5b01d3 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/DisplayWidgets/DocumentViewer_spec.js @@ -0,0 +1,18 @@ +const dsl = require("../../../../fixtures/DocumentViewerDsl.json"); +const explorer = require("../../../../locators/explorerlocators.json"); + +describe("DocumentViewer Widget Functionality", function() { + beforeEach(() => { + cy.addDsl(dsl); + }); + + it("Add new DocumentViewer", () => { + cy.get(explorer.addWidget).click(); + cy.dragAndDropToCanvas("documentviewerwidget", { x: 300, y: 300 }); + cy.get(".t--widget-documentviewerwidget").should("exist"); + }); + + it("Open Existing DocumentViewer from Widgets list", () => { + cy.get(".t--widget-documentviewerwidget").should("exist"); + }); +}); diff --git a/app/client/jest.config.js b/app/client/jest.config.js index f390ccddc2..b14ad020e2 100644 --- a/app/client/jest.config.js +++ b/app/client/jest.config.js @@ -15,7 +15,7 @@ module.exports = { moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "css"], moduleDirectories: ["node_modules", "src", "test"], transformIgnorePatterns: [ - "/node_modules/(?!codemirror|react-dnd|dnd-core|@babel|(@blueprintjs/core/lib/esnext)|(@blueprintjs/core/lib/esm)|@github|lodash-es|@draft-js-plugins)", + "/node_modules/(?!codemirror|react-dnd|dnd-core|@babel|(@blueprintjs/core/lib/esnext)|(@blueprintjs/core/lib/esm)|@github|lodash-es|@draft-js-plugins|react-documents)", ], moduleNameMapper: { "\\.(css|less)$": "/test/__mocks__/styleMock.js", diff --git a/app/client/package.json b/app/client/package.json index eb67cf3085..30fcb59862 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -51,6 +51,7 @@ "draft-js": "^0.11.7", "emoji-mart": "^3.0.1", "eslint": "^7.11.0", + "exceljs-lightweight": "^1.14.0", "fast-deep-equal": "^3.1.1", "fast-xml-parser": "^3.17.5", "flow-bin": "^0.148.0", @@ -74,6 +75,7 @@ "lodash-move": "^1.1.1", "loglevel": "^1.7.1", "lottie-web": "^5.7.4", + "mammoth": "^1.4.19", "marked": "^2.0.0", "memoize-one": "^5.2.1", "moment": "^2.24.0", @@ -97,6 +99,7 @@ "react-dnd": "^9.3.4", "react-dnd-html5-backend": "^9.3.4", "react-dnd-touch-backend": "^9.4.0", + "react-documents": "^1.0.4", "react-dom": "^16.7.0", "react-google-maps": "^9.4.5", "react-google-recaptcha": "^2.1.0", diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index a4e2955c28..ba1fabdbee 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -97,6 +97,9 @@ import AudioWidget, { import AudioRecorderWidget, { CONFIG as AUDIO_RECORDER_WIDGET_CONFIG, } from "widgets/AudioRecorderWidget"; +import DocumentViewerWidget, { + CONFIG as DOCUMENT_VIEWER_WIDGET_CONFIG, +} from "widgets/DocumentViewerWidget"; import ButtonGroupWidget, { CONFIG as BUTTON_GROUP_CONFIG, } from "widgets/ButtonGroupWidget"; @@ -147,6 +150,7 @@ export const registerWidgets = () => { registerWidget(FilePickerWidgetV2, FILEPICKER_WIDGET_V2_CONFIG); registerWidget(StatboxWidget, STATBOX_WIDGET_CONFIG); registerWidget(AudioRecorderWidget, AUDIO_RECORDER_WIDGET_CONFIG); + registerWidget(DocumentViewerWidget, DOCUMENT_VIEWER_WIDGET_CONFIG); registerWidget(ButtonGroupWidget, BUTTON_GROUP_CONFIG); registerWidget(MultiSelectTreeWidget, MULTI_SELECT_TREE_WIDGET_CONFIG); registerWidget(SingleSelectTreeWidget, SINGLE_SELECT_TREE_WIDGET_CONFIG); diff --git a/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx b/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx new file mode 100644 index 0000000000..196fcd0d0a --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/component/DocViewer.tsx @@ -0,0 +1,53 @@ +import React, { useEffect, useState } from "react"; +import mammoth from "mammoth"; +import styled from "styled-components"; +import Interweave from "interweave"; + +const StyledViewer = styled.div` + width: 100%; + height: 100%; + background: #fff; + overflow: auto; +`; +export default function DocViewer(props: { blob?: Blob }) { + const [state, setState] = useState({ isLoading: false, isError: false }); + const [htmlContent, setHtmlContent] = useState(""); + // when DocViewer gets new Blob of uploaded file convert it to html for preview + useEffect(() => { + setState({ isLoading: true, isError: false }); + setHtmlContent(""); + props.blob + ?.arrayBuffer() + .then((buffer) => { + mammoth + .convertToHtml( + { arrayBuffer: buffer }, + { includeEmbeddedStyleMap: true, includeDefaultStyleMap: true }, + ) + .then((result) => { + setState({ isLoading: false, isError: false }); + setHtmlContent(result.value); + }) + .catch(() => { + setHtmlContent(""); + setState({ isLoading: false, isError: true }); + }); + }) + .catch(() => { + setState({ isLoading: false, isError: false }); + setHtmlContent(""); + }); + }, [props.blob]); + return ( + + + {state.isLoading ? ( +
Loading...
+ ) : state.isError ? ( +
Failed to read docx content
+ ) : ( +
+ )} + + ); +} diff --git a/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx b/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx new file mode 100644 index 0000000000..ab034f2edd --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/component/XlsxViewer.tsx @@ -0,0 +1,204 @@ +import React, { useEffect, useState, useRef, useCallback } from "react"; +import styled from "styled-components"; +import Excel from "exceljs-lightweight"; +import { useTable, Column } from "react-table"; +import _ from "lodash"; + +const StyledViewer = styled.div` + width: 100%; + height: 100%; + background: #fff; + overflow: auto; + + table { + border: 1px solid #b0cbef; + border-width: 1px 0 0 1px; + border-spacing: 0; + border-collapse: collapse; + padding: 10px; + + th { + font-weight: 700; + font-size: 14px; + border: 1px solid #9eb6ce; + border-width: 0 1px 1px 0; + height: 17px; + line-height: 17px; + text-align: center; + background: #9eb6ce4d; + } + + td { + background-color: #fff; + padding: 0 4px 0 2px; + border: 1px solid #d0d7e5; + border-width: 0 1px 1px 0; + } + } +`; + +const chars = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", +]; + +// get excel column name from index, e.g. A,B,...,AA,AB +const numberToExcelHeader = (index: number): string => { + index -= 1; + const quotient = Math.floor(index / 26); + if (quotient > 0) { + return numberToExcelHeader(quotient) + chars[index % 26]; + } + return chars[index % 26]; +}; + +type sheetsDataType = { + name: string; + id: number; +}; + +export default function XlsxViewer(props: { blob?: Blob }) { + const [sheets, setSheets] = useState([] as sheetsDataType[]); + const [tableData, setTableData] = useState([]); + const [headerData, setHeaderData] = useState([] as Column[]); + const workbook = useRef(new Excel.Workbook()); + + useEffect(() => { + props.blob?.arrayBuffer().then((buffer) => { + // read excel + workbook.current.xlsx.load(buffer).then(() => { + const newSheets = [] as any; + // get all sheets from excel + workbook.current.eachSheet((sheet, id) => { + newSheets.push({ name: sheet.name, id }); + }); + setSheets(newSheets); + // get 1st sheet data + getSheetData(1); + }); + }); + }, [props.blob]); + + // get provided sheet data, read all row and columns + const getSheetData = useCallback((sheetId: number) => { + const worksheet = workbook.current.getWorksheet(sheetId); + // collect all row data + const data = [] as any; + worksheet.eachRow({ includeEmpty: true }, (row) => { + const currRow = {} as any; + // read value of each cell of current row + row.eachCell((cell) => { + // value can be merged value | Date | formula result | string | number + let value: any; + if (cell.isMerged) { + value = _.get(cell, "_mergeCount") ? cell.value : ""; + } else { + value = cell.value; + } + if (_.isDate(value)) { + value = value.toDateString(); + } else if (_.isObject(value) && _.has(value, "result")) { + value = _.get(value, "result", ""); + } + currRow[String(numberToExcelHeader(Number(cell.col)))] = value; + }); + data.push(currRow); + }); + setTableData(data); + if (data.length) { + // create header letters based on columnCount + const newHeader = []; + for (let index = 1; index <= worksheet.columnCount; index++) { + const currHeader = numberToExcelHeader(index); + newHeader.push({ + Header: currHeader, + accessor: currHeader, + }); + } + setHeaderData(newHeader); + } else { + setHeaderData([]); + } + }, []); + + // when user click on another sheet, re-generate data + const updateSheet = useCallback( + (sheetId) => () => { + getSheetData(sheetId); + }, + [], + ); + + const { + getTableBodyProps, + getTableProps, + headerGroups, + prepareRow, + rows, + } = useTable({ columns: headerData, data: tableData }); + + return ( + +
+ {sheets.map((sheet) => ( + + ))} +
+ + + {headerGroups.map((headerGroup, hgInd) => ( + + {headerGroup.headers.map((column, colId) => ( + + ))} + + ))} + + + {rows.map((row, rInd) => { + prepareRow(row); + return ( + + {row.cells.map((cell, ind) => { + return ( + + ); + })} + + ); + })} + +
+ {column.render("Header")} +
+ {cell.render("Cell")} +
+
+ ); +} diff --git a/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx b/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx new file mode 100644 index 0000000000..a6a2c4e402 --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/component/index.test.tsx @@ -0,0 +1,68 @@ +import { getDocViewerConfigs } from "widgets/DocumentViewerWidget/component"; +import { Renderers } from "../constants"; + +describe("validate document viewer url", () => { + it("validate correct config should return for extension based urls", () => { + const input = [ + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.docx", + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.odt", + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.rtf", + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.pdf", + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.txt", + ]; + + const expected = [ + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.docx", + viewer: "office", + errorMessage: "", + renderer: Renderers.DOCUMENT_VIEWER, + }, + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.odt", + viewer: "url", + errorMessage: "Current file type is not supported", + renderer: Renderers.ERROR, + }, + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.rtf", + viewer: "url", + errorMessage: "Current file type is not supported", + renderer: Renderers.ERROR, + }, + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.pdf", + viewer: "url", + errorMessage: "", + renderer: Renderers.DOCUMENT_VIEWER, + }, + { + url: + "https://roteemealplancover.s3.ap-south-1.amazonaws.com/sample/Project+proposal.txt", + viewer: "url", + errorMessage: "", + renderer: Renderers.DOCUMENT_VIEWER, + }, + ]; + + for (let index = 0; index < input.length; index++) { + const result = getDocViewerConfigs(input[index]); + expect(result).toStrictEqual(expected[index]); + } + }); + + it("validate errorMessage should return for empty url", () => { + const input = ""; + const result = getDocViewerConfigs(input); + expect(result).toStrictEqual({ + url: "", + viewer: "url", + errorMessage: "No document url provided for viewer", + renderer: Renderers.ERROR, + }); + }); +}); diff --git a/app/client/src/widgets/DocumentViewerWidget/component/index.tsx b/app/client/src/widgets/DocumentViewerWidget/component/index.tsx new file mode 100644 index 0000000000..a923f5a84f --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/component/index.tsx @@ -0,0 +1,220 @@ +import React, { Suspense, lazy } from "react"; +import styled from "styled-components"; +import { DocumentViewer } from "react-documents"; +import { includes, replace, split, get } from "lodash"; +import { + SUPPORTED_EXTENSIONS, + Renderers, + Renderer, + ViewerType, +} from "../constants"; +import { retryPromise } from "utils/AppsmithUtils"; +import Skeleton from "components/utils/Skeleton"; + +const DocViewer = lazy(() => retryPromise(() => import("./DocViewer"))); +const XlsxViewer = lazy(() => retryPromise(() => import("./XlsxViewer"))); + +const ErrorWrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; + background: #fff; +`; + +const checkUrlExtension = (docUrl: string) => { + // Remove everything to the last slash in URL + let url = docUrl.substr(1 + docUrl.lastIndexOf("/")); + // Break URL at ? and take first part (file name, extension) + url = url.split("?")[0]; + // Sometimes URL doesn't have ? but #, so we should aslo do the same for # + url = url.split("#")[0]; + // Now we have only filename and extension + const chunkList = url.split("."); + if (chunkList.length > 1) { + const ext = chunkList[chunkList.length - 1]; + // check extension is valid or not + const validExtension = SUPPORTED_EXTENSIONS.includes(ext); + return { + hasExtension: true, + validExtension: validExtension, + extension: ext, + }; + } else { + // might be preview url + return { hasExtension: false }; + } +}; + +const getBlob = (docUrl: string) => { + // Split into two parts + const parts = docUrl.split(";base64,"); + // Hold the content type + const mimeType = parts[0].split(":")[1]; + + try { + // Decode Base64 string + const decodedData = window.atob(parts[1]); + // Create UNIT8ARRAY of size same as row data length + const uInt8Array = new Uint8Array(decodedData.length); + // Insert all character code into uInt8Array + for (let i = 0; i < decodedData.length; ++i) { + uInt8Array[i] = decodedData.charCodeAt(i); + } + // Return BLOB image after conversion + const blob = new Blob([uInt8Array], { type: mimeType }); + return blob; + } catch (error) { + return; + } +}; + +// get extension from base64 +const getFileExtensionFromBase64 = (docUrl: string) => { + let extension = ""; + const fileType = docUrl.split(";")[0].split("/")[1]; + + switch (fileType) { + case "vnd.openxmlformats-officedocument.wordprocessingml.document": + extension = "docx"; + break; + case "vnd.openxmlformats-officedocument.spreadsheetml.sheet": + extension = "xlsx"; + break; + case "plain": + extension = "txt"; + break; + case "pdf": + extension = "pdf"; + break; + default: + break; + } + + return extension; +}; + +interface ConfigResponse { + url: string; + blob?: Blob; + viewer: ViewerType; + renderer: Renderer; + errorMessage?: string; +} + +export const getDocViewerConfigs = (docUrl: string): ConfigResponse => { + /** + * From user provided document url decide on which viewer to use + * handle different cases for url hosts + * Case 1: Dropbox + * if user exported download url than change that to raw url + * Case 2: Onedrive + * change url to embeded url to show in viewer + * Case 3: + * All other urls will be either Google Preview or urls with file extentions ( e.x. from aws s3 ) + * need to verify urls with extention and use default viewer "url viewer" for them + */ + let viewer = "url" as ViewerType; + let url = docUrl; + let blob; + let errorMessage = !!docUrl ? "" : "No document url provided for viewer"; + let renderer: Renderer = errorMessage + ? Renderers.ERROR + : Renderers.DOCUMENT_VIEWER; + + if (docUrl && includes(docUrl, "base64")) { + const extension = getFileExtensionFromBase64(docUrl); + const isValidExtension = SUPPORTED_EXTENSIONS.includes(extension); + if (isValidExtension) { + blob = getBlob(docUrl); + if (blob) { + if (extension === "docx") { + renderer = Renderers.DOCX_VIEWER; + } else if (extension === "xlsx") { + renderer = Renderers.XLSX_VIEWER; + } + } else { + errorMessage = "invalid base64 data"; + renderer = Renderers.ERROR; + } + } else { + errorMessage = "Current file type is not supported " + extension; + renderer = Renderers.ERROR; + } + return { blob, url, viewer, errorMessage, renderer }; + } + + // handled dropbox url + if (includes(url, "dropbox.com")) { + if (includes(url, "?dl=0") || !includes(url, "?raw=1")) { + url = replace(url, "?dl=0", ""); + url = url + "?raw=1"; + } + return { url, viewer, renderer }; + } + // handled onedrive url + if (includes(url, "onedrive.live.com")) { + const onedriveUrl = new URL(url); + const onedriveUrlParamList = split(onedriveUrl.search, /[?&=]+/); + const cid = get( + onedriveUrlParamList, + onedriveUrlParamList.indexOf("cid") + 1, + ); + const resid = get( + onedriveUrlParamList, + onedriveUrlParamList.indexOf("id") + 1, + ); + + url = `https://onedrive.live.com/embed?cid=${cid}&resid=${resid}&em=2`; + return { url, viewer, renderer }; + } + + // check url extension and if it is supported + const { extension, hasExtension, validExtension } = checkUrlExtension(url); + if (hasExtension) { + if (validExtension) { + if (!(extension === "txt" || extension === "pdf")) { + viewer = "office"; + renderer = Renderers.DOCUMENT_VIEWER; + } + } else { + errorMessage = "Current file type is not supported"; + renderer = Renderers.ERROR; + } + } + + return { url, viewer, errorMessage, renderer }; +}; + +function DocumentViewerComponent(props: DocumentViewerComponentProps) { + const { blob, errorMessage, renderer, url, viewer } = React.useMemo( + () => getDocViewerConfigs(props.docUrl), + [props.docUrl], + ); + switch (renderer) { + case Renderers.ERROR: + return {errorMessage}; + case Renderers.DOCX_VIEWER: + return ( + }> + + + ); + case Renderers.XLSX_VIEWER: + return ( + }> + + + ); + case Renderers.DOCUMENT_VIEWER: + return ; + + default: + return null; + } +} + +export interface DocumentViewerComponentProps { + docUrl: string; +} + +export default DocumentViewerComponent; diff --git a/app/client/src/widgets/DocumentViewerWidget/constants.ts b/app/client/src/widgets/DocumentViewerWidget/constants.ts new file mode 100644 index 0000000000..fc70832c8c --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/constants.ts @@ -0,0 +1,23 @@ +// This file contains common constants which can be used across the widget configuration file (index.ts), widget and component folders. +export const DOCUMENTVIEWER_WIDGET_CONSTANT = ""; +// txt and pdf handle by viewerType = "url" +// and other types handle by viewerType = "office" +export const SUPPORTED_EXTENSIONS = [ + "txt", + "pdf", + "docx", + "ppt", + "pptx", + "xlsx", +]; + +export const Renderers = { + DOCUMENT_VIEWER: "DOCUMENT_VIEWER", + DOCX_VIEWER: "DOCX_VIEWER", + XLSX_VIEWER: "XLSX_VIEWER", + ERROR: "ERROR", +}; + +export type Renderer = typeof Renderers[keyof typeof Renderers]; + +export type ViewerType = "google" | "office" | "mammoth" | "pdf" | "url"; diff --git a/app/client/src/widgets/DocumentViewerWidget/icon.svg b/app/client/src/widgets/DocumentViewerWidget/icon.svg new file mode 100644 index 0000000000..b48db5cb0a --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/client/src/widgets/DocumentViewerWidget/index.ts b/app/client/src/widgets/DocumentViewerWidget/index.ts new file mode 100644 index 0000000000..a492c6c3cb --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/index.ts @@ -0,0 +1,27 @@ +import Widget from "./widget"; +import IconSVG from "./icon.svg"; +import { GRID_DENSITY_MIGRATION_V1 } from "widgets/constants"; + +export const CONFIG = { + type: Widget.getWidgetType(), + name: "Document Viewer", // The display name which will be made in uppercase and show in the widgets panel ( can have spaces ) + iconSVG: IconSVG, + needsMeta: false, // Defines if this widget adds any meta properties + isCanvas: false, // Defines if this widget has a canvas within in which we can drop other widgets + defaults: { + widgetName: "DocumentViewer", + docUrl: + "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf", + rows: 10 * GRID_DENSITY_MIGRATION_V1, + columns: 6 * GRID_DENSITY_MIGRATION_V1, + version: 1, + }, + properties: { + derived: Widget.getDerivedPropertiesMap(), + default: Widget.getDefaultPropertiesMap(), + meta: Widget.getMetaPropertiesMap(), + config: Widget.getPropertyPaneConfig(), + }, +}; + +export default Widget; diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx new file mode 100644 index 0000000000..19aab1c529 --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.test.tsx @@ -0,0 +1,127 @@ +import { documentUrlValidation } from "."; + +describe("validate propertypane input : docUrl", () => { + it("validation for empty or space value", () => { + const input1 = ""; + const expected1 = { + isValid: true, + parsed: "", + messages: [""], + }; + + const result = documentUrlValidation(input1); + expect(result).toStrictEqual(expected1); + + const input2 = "https: //www.example.com"; + const expected2 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result1 = documentUrlValidation(input2); + expect(result1).toStrictEqual(expected2); + + const input3 = "https://www.exam ple.com"; + const expected3 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result2 = documentUrlValidation(input3); + expect(result2).toStrictEqual(expected3); + + const input4 = "https://examplecom"; + const expected4 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result3 = documentUrlValidation(input4); + expect(result3).toStrictEqual(expected4); + + const input6 = "://www.appsmith.com/docs/sample.pdf"; + const expected6 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result5 = documentUrlValidation(input6); + expect(result5).toStrictEqual(expected6); + }); + + it("validation for invalid url or base64 value", () => { + const input1 = "htt"; + const expected1 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result1 = documentUrlValidation(input1); + expect(result1).toStrictEqual(expected1); + + const input2 = "data:application/pdf;base64"; + const expected2 = { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + + const result2 = documentUrlValidation(input2); + expect(result2).toStrictEqual(expected2); + }); + + it("validation for valid url or base64 value", () => { + const input1 = "https://www.example.com"; + const expected1 = { + isValid: true, + parsed: "https://www.example.com/", + }; + + const result1 = documentUrlValidation(input1); + expect(result1).toStrictEqual(expected1); + + const input2 = + "data:application/pdf;base64,JVBERi0xLjIgCjkgMCBvYmoKPDwKPj4Kc3RyZWFtCkJULyAzMiBUZiggIFlPVVIgVEVYVCBIRVJFICAgKScgRVQKZW5kc3RyZWFtCmVuZG9iago0IDAgb2JqCjw8Ci9UeXBlIC9QYWdlCi9QYXJlbnQgNSAwIFIKL0NvbnRlbnRzIDkgMCBSCj4+CmVuZG9iago1IDAgb2JqCjw8Ci9LaWRzIFs0IDAgUiBdCi9Db3VudCAxCi9UeXBlIC9QYWdlcwovTWVkaWFCb3ggWyAwIDAgMjUwIDUwIF0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1BhZ2VzIDUgMCBSCi9UeXBlIC9DYXRhbG9nCj4+CmVuZG9iagp0cmFpbGVyCjw8Ci9Sb290IDMgMCBSCj4+CiUlRU9G"; + const expected2 = { + isValid: true, + parsed: input2, + }; + + const result2 = documentUrlValidation(input2); + expect(result2).toStrictEqual(expected2); + + const input3 = "https:www.appsmith.com/docs/sample.pdf"; + const expected3 = { + isValid: true, + parsed: "https://www.appsmith.com/docs/sample.pdf", + }; + + const result3 = documentUrlValidation(input3); + expect(result3).toStrictEqual(expected3); + + const input4 = "https://www.apsmith.com/docs/sample"; + const expected4 = { + isValid: true, + parsed: "https://www.apsmith.com/docs/sample", + }; + + const result4 = documentUrlValidation(input4); + expect(result4).toStrictEqual(expected4); + + const input5 = + "www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf"; + const expected5 = { + isValid: true, + parsed: + "https://www.learningcontainer.com/wp-content/uploads/2019/09/sample-pdf-file.pdf", + }; + + const result5 = documentUrlValidation(input5); + expect(result5).toStrictEqual(expected5); + }); +}); diff --git a/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx new file mode 100644 index 0000000000..b296327c30 --- /dev/null +++ b/app/client/src/widgets/DocumentViewerWidget/widget/index.tsx @@ -0,0 +1,121 @@ +import React from "react"; +import BaseWidget, { WidgetProps, WidgetState } from "widgets/BaseWidget"; +import DocumentViewerComponent from "../component"; +import { + ValidationTypes, + ValidationResponse, +} from "constants/WidgetValidation"; +import { AutocompleteDataType } from "utils/autocomplete/TernServer"; + +export function documentUrlValidation(value: unknown): ValidationResponse { + // applied validations if value exist + if (value) { + const whiteSpaceRegex = /\s/g; + const urlRegex = /(?:https:\/\/|www)?([\da-z.-]+)\.([a-z.]{2,6})[/\w .-]*\/?/; + const base64Regex = /^\s*data:([a-z]+\/[a-z]+(;[a-z\-]+\=[a-z\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i; + if ( + urlRegex.test(value as string) && + !whiteSpaceRegex.test(value as string) + ) { + if ((value as string).startsWith("www")) { + return { + isValid: true, + parsed: "https://" + value, + }; + } + try { + const newUrl = new URL(value as string); + // URL is valid + return { + isValid: true, + parsed: newUrl.href, + }; + } catch (error) { + return { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + } + } else if (base64Regex.test(value as string)) { + // base 64 is valid + return { + isValid: true, + parsed: value, + }; + } else { + // value is not valid URL / Base64 + return { + isValid: false, + parsed: "", + messages: ["Provided URL / Base64 is invalid."], + }; + } + } + // value is empty here + return { + isValid: true, + parsed: "", + messages: [""], + }; +} + +class DocumentViewerWidget extends BaseWidget< + DocumentViewerWidgetProps, + WidgetState +> { + static getPropertyPaneConfig() { + return [ + { + sectionName: "General", + children: [ + { + helpText: + "Document url for preview. for URL, supported extensions are txt, pdf, docx, ppt, pptx, xlsx. ppt is currently not supported by base64.", + propertyName: "docUrl", + label: "Document Link", + controlType: "INPUT_TEXT", + placeholderText: "URL / Base64", + isBindProperty: true, + isTriggerProperty: false, + validation: { + type: ValidationTypes.FUNCTION, + params: { + fn: documentUrlValidation, + expected: { + type: "URL / Base64", + example: "https://www.example.com", + autocompleteDataType: AutocompleteDataType.STRING, + }, + }, + }, + }, + { + helpText: "Controls visibility of the widget", + propertyName: "isVisible", + label: "Visible", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + ], + }, + ]; + } + + getPageView() { + return ; + } + + static getWidgetType(): string { + return "DOCUMENT_VIEWER_WIDGET"; + } +} + +export interface DocumentViewerWidgetProps extends WidgetProps { + docUrl: string; +} + +export default DocumentViewerWidget; diff --git a/app/client/yarn.lock b/app/client/yarn.lock index 1c74d8455a..3d46eabaaa 100644 --- a/app/client/yarn.lock +++ b/app/client/yarn.lock @@ -4075,6 +4075,35 @@ arch@^2.2.0: resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== +archiver-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" + integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== + dependencies: + glob "^7.1.4" + graceful-fs "^4.2.0" + lazystream "^1.0.0" + lodash.defaults "^4.2.0" + lodash.difference "^4.5.0" + lodash.flatten "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.union "^4.6.0" + normalize-path "^3.0.0" + readable-stream "^2.0.0" + +archiver@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-3.1.1.tgz#9db7819d4daf60aec10fe86b16cb9258ced66ea0" + integrity sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg== + dependencies: + archiver-utils "^2.1.0" + async "^2.6.3" + buffer-crc32 "^0.2.1" + glob "^7.1.4" + readable-stream "^3.4.0" + tar-stream "^2.1.0" + zip-stream "^2.1.2" + are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz" @@ -4093,6 +4122,14 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +arguments-extended@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/arguments-extended/-/arguments-extended-0.0.3.tgz#6107e4917d0eb6f0a4dd66320fc15afc72ef4946" + integrity sha1-YQfkkX0OtvCk3WYyD8Fa/HLvSUY= + dependencies: + extended "~0.0.3" + is-extended "~0.0.8" + aria-query@^4.2.2: version "4.2.2" resolved "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz" @@ -4121,6 +4158,15 @@ array-each@^1.0.1: resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= +array-extended@~0.0.3, array-extended@~0.0.4, array-extended@~0.0.5: + version "0.0.11" + resolved "https://registry.yarnpkg.com/array-extended/-/array-extended-0.0.11.tgz#d7144ae748de93ca726f121009dbff1626d164bd" + integrity sha1-1xRK50jek8pybxIQCdv/FibRZL0= + dependencies: + arguments-extended "~0.0.3" + extended "~0.0.3" + is-extended "~0.0.3" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" @@ -4267,9 +4313,10 @@ async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz" -async@^2.6.2: +async@^2.6.2, async@^2.6.3: version "2.6.3" - resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== dependencies: lodash "^4.17.14" @@ -4528,6 +4575,11 @@ base64-js@^1.0.2: version "1.3.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz" +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + base@^0.11.1: version "0.11.2" resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" @@ -4569,6 +4621,11 @@ bfj@^7.0.2: hoopy "^0.1.4" tryer "^1.0.1" +big-integer@^1.6.17: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" @@ -4581,12 +4638,29 @@ binary-extensions@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz" +binary@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" + integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk= + dependencies: + buffers "~0.1.1" + chainsaw "~0.1.0" + bindings@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" dependencies: file-uri-to-path "1.0.0" +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + blob-util@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" @@ -4596,6 +4670,11 @@ bluebird@^3.5.5, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" +bluebird@~3.4.0, bluebird@~3.4.1: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" @@ -4759,14 +4838,20 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -buffer-crc32@~0.2.3: +buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3: version "0.2.13" - resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz" +buffer-indexof-polyfill@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" + integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== + buffer-indexof@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz" @@ -4783,6 +4868,19 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.1.0, buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffers@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" + integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= + builtin-modules@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz" @@ -4965,6 +5063,13 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" +chainsaw@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" + integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg= + dependencies: + traverse ">=0.3.0 <0.4" + chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" @@ -5466,6 +5571,16 @@ compose-function@3.0.3: dependencies: arity-n "^1.0.4" +compress-commons@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-2.1.1.tgz#9410d9a534cf8435e3fbbb7c6ce48de2dc2f0610" + integrity sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q== + dependencies: + buffer-crc32 "^0.2.13" + crc32-stream "^3.0.1" + normalize-path "^3.0.0" + readable-stream "^2.3.6" + compressible@~2.0.16: version "2.0.18" resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" @@ -5688,6 +5803,21 @@ craco-babel-loader@^0.1.4: dependencies: "@craco/craco" "^5.0.0" +crc32-stream@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-3.0.1.tgz#cae6eeed003b0e44d739d279de5ae63b171b4e85" + integrity sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w== + dependencies: + crc "^3.4.4" + readable-stream "^3.4.0" + +crc@^3.4.4: + version "3.8.0" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" + integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== + dependencies: + buffer "^5.1.0" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz" @@ -6130,6 +6260,15 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-extended@~0.0.3: + version "0.0.6" + resolved "https://registry.yarnpkg.com/date-extended/-/date-extended-0.0.6.tgz#23802d57dd1bf7818813fe0c32e851a86da267c9" + integrity sha1-I4AtV90b94GIE/4MMuhRqG2iZ8k= + dependencies: + array-extended "~0.0.3" + extended "~0.0.3" + is-extended "~0.0.3" + date-fns@^1.27.2: version "1.30.1" resolved "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz" @@ -6147,6 +6286,11 @@ dayjs@^1.10.4, dayjs@^1.10.6: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== +dayjs@^1.8.15: + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -6200,6 +6344,11 @@ decimal.js@^10.2.0: version "10.2.1" resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz" +declare.js@~0.0.4: + version "0.0.8" + resolved "https://registry.yarnpkg.com/declare.js/-/declare.js-0.0.8.tgz#0478adff9564c004f51df73d8bc134019d28dcde" + integrity sha1-BHit/5VkwAT1Hfc9i8E0AZ0o3N4= + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" @@ -6391,6 +6540,11 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +dingbat-to-unicode@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz#5091dd673241453e6b5865e26e5a4452cdef5c83" + integrity sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" @@ -6579,6 +6733,20 @@ draft-js@^0.11.7: immutable "~3.7.4" object-assign "^4.1.1" +duck@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/duck/-/duck-0.1.12.tgz#de7adf758421230b6d7aee799ce42670586b9efa" + integrity sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg== + dependencies: + underscore "^1.13.1" + +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= + dependencies: + readable-stream "^2.0.2" + duplexer@^0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" @@ -6682,9 +6850,10 @@ encoding@^0.1.11: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" - resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" @@ -6854,6 +7023,11 @@ es6-iterator@2.0.3, es6-iterator@~2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" +es6-promise@^3.0.2: + version "3.3.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + integrity sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM= + es6-symbol@^3.1.1, es6-symbol@~3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz" @@ -7235,6 +7409,20 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +exceljs-lightweight@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/exceljs-lightweight/-/exceljs-lightweight-1.14.0.tgz#0535209784ef00159b7820fbf12012f0be56255d" + integrity sha512-piaCFqJVC4d/8iaiH4RhGmRHDMf5cDDe4DZT8dwoIb2fxH0sYzQ4NsdgyTZ713A9rt2I3XM2G20m+Or37vmfxA== + dependencies: + archiver "^3.0.0" + dayjs "^1.8.15" + fast-csv "^2.4.1" + jszip "^3.1.5" + promish "^5.1.1" + sax "^1.2.4" + tmp "^0.1.0" + unzipper "^0.9.12" + exec-sh@^0.3.2: version "0.3.4" resolved "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz" @@ -7381,6 +7569,20 @@ extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" +extended@0.0.6, extended@~0.0.3: + version "0.0.6" + resolved "https://registry.yarnpkg.com/extended/-/extended-0.0.6.tgz#7fb8bf7b9dae397586e48570acfd642c78e50669" + integrity sha1-f7i/e52uOXWG5IVwrP1kLHjlBmk= + dependencies: + extender "~0.0.5" + +extender@~0.0.5: + version "0.0.10" + resolved "https://registry.yarnpkg.com/extender/-/extender-0.0.10.tgz#589c07482be61a1460b6d81f9c24aa67e8f324cd" + integrity sha1-WJwHSCvmGhRgttgfnCSqZ+jzJM0= + dependencies: + declare.js "~0.0.4" + external-editor@^3.0.3: version "3.1.0" resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" @@ -7428,6 +7630,17 @@ factory.ts@^0.5.1: clone-deep "^4.0.1" source-map-support "^0.5.9" +fast-csv@^2.4.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/fast-csv/-/fast-csv-2.5.0.tgz#5332dfede3f59340cb8e9f46b2e6dff1e7612005" + integrity sha512-M/9ezLU9/uDwvDZTt9sNFJa0iLDUsbhYJwPtnE0D9MjeuB6DY9wRCyUPZta9iI6cSz5wBWGaUPL61QH8h92cNA== + dependencies: + extended "0.0.6" + is-extended "0.0.10" + object-extended "0.0.7" + safer-buffer "^2.1.2" + string-extended "0.0.8" + fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -7819,6 +8032,11 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-extra@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" @@ -7898,6 +8116,16 @@ fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" +fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + fsu@^1.0.2: version "1.1.1" resolved "https://registry.npmjs.org/fsu/-/fsu-1.1.1.tgz" @@ -8633,6 +8861,11 @@ identity-obj-proxy@3.0.0: dependencies: harmony-reflect "^1.4.6" +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + ieee754@^1.1.4: version "1.1.13" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz" @@ -8747,7 +8980,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -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: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" @@ -9052,6 +9285,13 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-extended@0.0.10, is-extended@~0.0.3, is-extended@~0.0.8: + version "0.0.10" + resolved "https://registry.yarnpkg.com/is-extended/-/is-extended-0.0.10.tgz#244e140df75bb1c9a3106f412ff182fb534a6d62" + integrity sha1-JE4UDfdbscmjEG9BL/GC+1NKbWI= + dependencies: + extended "~0.0.3" + is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" @@ -10033,6 +10273,16 @@ jszip@^3.1.3: readable-stream "~2.3.6" set-immediate-shim "~1.0.1" +jszip@^3.1.5, jszip@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9" + integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + set-immediate-shim "~1.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz" @@ -10102,6 +10352,13 @@ lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz" +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -10194,6 +10451,11 @@ lint-staged@^9.2.5: string-argv "^0.3.0" stringify-object "^3.3.0" +listenercount@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" + integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc= + listr-silent-renderer@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz" @@ -10340,10 +10602,25 @@ lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + +lodash.difference@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" + integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw= + lodash.escape@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz" +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + lodash.flow@^3.3.0: version "3.5.0" resolved "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz" @@ -10407,6 +10684,11 @@ lodash.topath@^4.5.2: resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= +lodash.union@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" @@ -10479,6 +10761,15 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3 dependencies: js-tokens "^3.0.0 || ^4.0.0" +lop@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/lop/-/lop-0.4.1.tgz#744f1696ef480e68ce1947fe557b09db5af2a738" + integrity sha512-9xyho9why2A2tzm5aIcMWKvzqKsnxrf9B5I+8O30olh6lQU8PH978LqZoI4++37RBgS1Em5i54v1TFs/3wnmXQ== + dependencies: + duck "^0.1.12" + option "~0.2.1" + underscore "^1.13.1" + lottie-web@^5.7.4: version "5.7.4" resolved "https://registry.npmjs.org/lottie-web/-/lottie-web-5.7.4.tgz" @@ -10579,6 +10870,21 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" +mammoth@^1.4.19: + version "1.4.19" + resolved "https://registry.yarnpkg.com/mammoth/-/mammoth-1.4.19.tgz#7a4d82e0656a824a568746df5fafefdee78c892e" + integrity sha512-VgqsTvBeA1JrNDYMLp+QX5LQPkVPOgl+TKCDklRBedb9Kuv2i7jT+Tgwst8k6mqzH3AchuViiHmBd875Msfivg== + dependencies: + argparse "~1.0.3" + bluebird "~3.4.0" + dingbat-to-unicode "^1.0.1" + jszip "^3.7.1" + lop "^0.4.1" + path-is-absolute "^1.0.0" + sax "~1.1.1" + underscore "^1.13.1" + xmlbuilder "^10.0.0" + map-cache@^0.2.0, map-cache@^0.2.2: version "0.2.2" resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" @@ -10920,7 +11226,7 @@ mkdirp@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" -mkdirp@0.5.5, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@0.5.5, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" dependencies: @@ -11440,6 +11746,15 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-extended@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/object-extended/-/object-extended-0.0.7.tgz#84fd23f56b15582aeb3e88b05cb55d2432d68a33" + integrity sha1-hP0j9WsVWCrrPoiwXLVdJDLWijM= + dependencies: + array-extended "~0.0.4" + extended "~0.0.3" + is-extended "~0.0.3" + object-hash@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" @@ -11633,6 +11948,11 @@ optimize-css-assets-webpack-plugin@5.0.4: cssnano "^4.1.10" last-call-webpack-plugin "^3.0.0" +option@~0.2.1: + version "0.2.4" + resolved "https://registry.yarnpkg.com/option/-/option-0.2.4.tgz#fd475cdf98dcabb3cb397a3ba5284feb45edbfe4" + integrity sha1-/Udc35jcq7PLOXo7pShP60Xtv+Q= + optionator@^0.8.1: version "0.8.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" @@ -12924,6 +13244,13 @@ promise@^8.0.3, promise@^8.1.0: dependencies: asap "~2.0.6" +promish@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/promish/-/promish-5.1.1.tgz#a5fdfc508d1233087cda33f1bb27b23d5b46d70f" + integrity sha512-37xEzvSas6JIYI/BcKh5TwhaqWepI44u/hC+tQStkX1sxMf+L756beESPgSWirxRCPqtXHzosoNzpjLnTnP8FA== + dependencies: + es6-promise "^3.0.2" + prompts@2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz" @@ -13396,6 +13723,11 @@ react-docgen-typescript@^1.15.0: version "1.20.5" resolved "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-1.20.5.tgz" +react-documents@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/react-documents/-/react-documents-1.0.4.tgz#f1dee4d4a558210140f151b16a147eece4d85b54" + integrity sha512-EpoY+MZEu3hPffIWA4FadUYu8daubNkr+LK2zuoPkCAVtyNY+z+/RuzzTriuhjcDydKXzgzp42kQTfAD2j3Mxw== + react-dom@^16.7.0, react-dom@^16.8.5: version "16.13.1" resolved "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz" @@ -13874,7 +14206,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" dependencies: @@ -13895,9 +14227,10 @@ readable-stream@1.1: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0: +readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -14316,15 +14649,15 @@ rgba-regex@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz" -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" +rimraf@2, rimraf@^2.5.4, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" dependencies: glob "^7.1.3" -rimraf@^2.5.4, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" dependencies: glob "^7.1.3" @@ -14421,9 +14754,10 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sane@^4.0.3: version "4.1.0" @@ -14462,9 +14796,15 @@ sass-loader@8.0.2: schema-utils "^2.6.1" semver "^6.3.0" -sax@~1.2.4: +sax@^1.2.4, sax@~1.2.4: version "1.2.4" - resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +sax@~1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.6.tgz#5d616be8a5e607d54e114afae55b7eaf2fcc3240" + integrity sha1-XWFr6KXmB9VOEUr65Vt+ry/MMkA= saxes@^5.0.0: version "5.0.1" @@ -14650,7 +14990,7 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" -setimmediate@^1.0.4, setimmediate@^1.0.5: +setimmediate@^1.0.4, setimmediate@^1.0.5, setimmediate@~1.0.4: version "1.0.5" resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" @@ -15121,6 +15461,16 @@ string-argv@^0.3.0: version "0.3.1" resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" +string-extended@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/string-extended/-/string-extended-0.0.8.tgz#741957dff487b0272a79eec5a44f239ee6f17ccd" + integrity sha1-dBlX3/SHsCcqee7FpE8jnubxfM0= + dependencies: + array-extended "~0.0.5" + date-extended "~0.0.3" + extended "~0.0.3" + is-extended "~0.0.3" + string-length@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz" @@ -15490,6 +15840,17 @@ tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" +tar-stream@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + tar@^6.0.2: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" @@ -15658,6 +16019,13 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +tmp@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.1.0.tgz#ee434a4e22543082e294ba6201dcc6eafefa2877" + integrity sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw== + dependencies: + rimraf "^2.6.3" + tmp@^0.2.1, tmp@~0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz" @@ -15753,6 +16121,11 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" +"traverse@>=0.3.0 <0.4": + version "0.3.9" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" + integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz" @@ -15961,6 +16334,11 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= +underscore@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" + integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== + unescape-js@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/unescape-js/-/unescape-js-1.1.4.tgz" @@ -16053,6 +16431,21 @@ untildify@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz" +unzipper@^0.9.12: + version "0.9.15" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.9.15.tgz#97d99203dad17698ee39882483c14e4845c7549c" + integrity sha512-2aaUvO4RAeHDvOCuEtth7jrHFaCKTSXPqUkXwADaLBzGbgZGzUDccoEdJ5lW+3RmfpOZYNx0Rw6F6PUzM6caIA== + dependencies: + big-integer "^1.6.17" + binary "~0.3.0" + bluebird "~3.4.1" + buffer-indexof-polyfill "~1.0.0" + duplexer2 "~0.1.4" + fstream "^1.0.12" + listenercount "~1.0.1" + readable-stream "~2.3.6" + setimmediate "~1.0.4" + upath@^1.1.1, upath@^1.1.2, upath@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz" @@ -16746,6 +17139,11 @@ xml@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz" +xmlbuilder@^10.0.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz#8cae6688cc9b38d850b7c8d3c0a4161dcaf475b0" + integrity sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" @@ -16904,6 +17302,15 @@ yjs@^13.5.12: dependencies: lib0 "^0.2.41" +zip-stream@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b" + integrity sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q== + dependencies: + archiver-utils "^2.1.0" + compress-commons "^2.1.1" + readable-stream "^3.4.0" + zipcelx@^1.6.2: version "1.6.2" resolved "https://registry.npmjs.org/zipcelx/-/zipcelx-1.6.2.tgz"