Removing export all apps functionality from cloud - this was a very niche feature and often confusing, now there is a consistent flow for starting out in self host.

This commit is contained in:
mike12345567 2023-04-20 17:19:17 +01:00
parent 55ce83c444
commit 060dc05afa
13 changed files with 11 additions and 478 deletions

View File

@ -1,46 +0,0 @@
<script>
import { notifications, ModalContent, Dropzone, Body } from "@budibase/bbui"
import { API } from "api"
import { admin } from "stores/portal"
let submitting = false
$: value = { file: null }
async function importApps() {
submitting = true
try {
// Create form data to create app
let data = new FormData()
data.append("importFile", value.file)
// Create App
await API.importApps(data)
await admin.checkImportComplete()
notifications.success("Import complete, please finish registration!")
} catch (error) {
notifications.error("Failed to import apps")
}
submitting = false
}
</script>
<ModalContent
title="Import apps"
confirmText="Import apps"
onConfirm={importApps}
disabled={!value.file}
>
<Body>
Please upload the file that was exported from your Cloud environment to get
started
</Body>
<Dropzone
gallery={false}
label="File to import"
value={[value.file]}
on:change={e => {
value.file = e.detail?.[0]
}}
/>
</ModalContent>

View File

@ -10,10 +10,8 @@
import { goto } from "@roxi/routify"
import { API } from "api"
import { admin, auth } from "stores/portal"
import ImportAppsModal from "./_components/ImportAppsModal.svelte"
import Logo from "assets/bb-emblem.svg"
import { onMount } from "svelte"
import { FancyForm, FancyInput, ActionButton } from "@budibase/bbui"
import { FancyForm, FancyInput } from "@budibase/bbui"
import { TestimonialPage } from "@budibase/frontend-core/src/components"
import { passwordsMatch, handleError } from "../auth/_components/utils"
@ -24,8 +22,6 @@
let submitted = false
$: tenantId = $auth.tenantId
$: cloud = $admin.cloud
$: imported = $admin.importComplete
async function save() {
form.validate()
@ -46,22 +42,8 @@
notifications.error("Failed to create admin user")
}
}
onMount(async () => {
if (!cloud) {
try {
await admin.checkImportComplete()
} catch (error) {
notifications.error("Error checking import status")
}
}
})
</script>
<Modal bind:this={modal} padding={false} width="600px">
<ImportAppsModal />
</Modal>
<TestimonialPage>
<Layout gap="M" noPadding>
<Layout justifyItems="center" noPadding>
@ -156,20 +138,6 @@
Create super admin user
</Button>
</Layout>
<Layout gap="XS" noPadding justifyItems="center">
<div class="user-actions">
{#if !cloud && !imported}
<ActionButton
quiet
on:click={() => {
modal.show()
}}
>
Import from cloud
</ActionButton>
{/if}
</div>
</Layout>
</Layout>
</TestimonialPage>

View File

@ -9,14 +9,11 @@
notifications,
Notification,
Body,
Icon,
Search,
InlineAlert,
} from "@budibase/bbui"
import Spinner from "components/common/Spinner.svelte"
import CreateAppModal from "components/start/CreateAppModal.svelte"
import AppLimitModal from "components/portal/licensing/AppLimitModal.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { store, automationStore } from "builderStore"
import { API } from "api"
@ -33,11 +30,9 @@
let appLimitModal
let creatingApp = false
let searchTerm = ""
let cloud = $admin.cloud
let creatingFromTemplate = false
let automationErrors
let accessFilterList = null
let confirmDownloadDialog
$: welcomeHeader = `Welcome ${$auth?.user?.firstName || "back"}`
$: enrichedApps = enrichApps($apps, $auth.user, sortBy)
@ -123,15 +118,6 @@
}
}
const initiateAppsExport = () => {
try {
window.location = `/api/cloud/export`
notifications.success("Apps exported successfully")
} catch (err) {
notifications.error(`Error exporting apps: ${err}`)
}
}
const initiateAppImport = () => {
template = { fromFile: true }
creationModal.show()
@ -264,13 +250,6 @@
</div>
{#if enrichedApps.length > 1}
<div class="app-actions">
{#if cloud}
<Icon
name="Download"
hoverable
on:click={confirmDownloadDialog.show}
/>
{/if}
<Select
autoWidth
bind:value={sortBy}
@ -316,18 +295,6 @@
<AppLimitModal bind:this={appLimitModal} />
<ConfirmDialog
bind:this={confirmDownloadDialog}
okText="Continue"
onOk={initiateAppsExport}
warning={false}
title="Download all apps"
>
<InlineAlert
header="Do not share your budibase application exports publicly as they may contain sensitive information such as database credentials or secret keys."
/>
</ConfirmDialog>
<style>
.title {
display: flex;

View File

@ -37,14 +37,6 @@ export function createAdminStore() {
})
}
async function checkImportComplete() {
const result = await API.checkImportComplete()
admin.update(store => {
store.importComplete = result ? result.imported : false
return store
})
}
async function getEnvironment() {
const environment = await API.getEnvironment()
admin.update(store => {
@ -92,7 +84,6 @@ export function createAdminStore() {
return {
subscribe: admin.subscribe,
init,
checkImportComplete,
unload,
getChecklist,
}

View File

@ -24,7 +24,6 @@ vi.mock("svelte/store", () => {
vi.mock("api", () => {
return {
API: {
checkImportComplete: vi.fn(),
getEnvironment: vi.fn(),
getSystemStatus: vi.fn(),
getChecklist: vi.fn(),
@ -55,7 +54,6 @@ describe("admin store", () => {
expect(ctx.returnedStore).toEqual({
subscribe: expect.toBe(ctx.writableReturn.subscribe),
init: expect.toBeFunc(),
checkImportComplete: expect.toBeFunc(),
unload: expect.toBeFunc(),
getChecklist: expect.toBeFunc(),
})
@ -205,37 +203,6 @@ describe("admin store", () => {
})
})
describe("checkImportComplete", () => {
describe("import complete", () => {
beforeEach(async ctx => {
API.checkImportComplete.mockReturnValue({ imported: true })
await ctx.returnedStore.checkImportComplete()
})
it("updates the store's importComplete parameter", ctx => {
expect(ctx.writableReturn.update.calls[0][0]({ foo: "foo" })).toEqual({
foo: "foo",
importComplete: true,
})
})
})
describe("import not complete", () => {
beforeEach(async ctx => {
// Can be null
API.checkImportComplete.mockReturnValue(null)
await ctx.returnedStore.checkImportComplete()
})
it("updates the store's importComplete parameter", ctx => {
expect(ctx.writableReturn.update.calls[0][0]({ foo: "foo" })).toEqual({
foo: "foo",
importComplete: false,
})
})
})
})
describe("unload", () => {
beforeEach(ctx => {
ctx.returnedStore.unload()

View File

@ -1,13 +1,4 @@
export const buildOtherEndpoints = API => ({
/**
* TODO: find out what this is
*/
checkImportComplete: async () => {
return await API.get({
url: "/api/cloud/import/complete",
})
},
/**
* Gets the current environment details.
*/

View File

@ -1,119 +0,0 @@
import env from "../../environment"
import { db as dbCore, tenancy } from "@budibase/backend-core"
import { streamFile } from "../../utilities/fileSystem"
import { stringToReadStream } from "../../utilities"
import { getDocParams, DocumentType, isDevAppID } from "../../db/utils"
import { create } from "./application"
import { join } from "path"
import sdk from "../../sdk"
import { App, Ctx, Database } from "@budibase/types"
async function createApp(appName: string, appDirectory: string) {
const ctx = {
request: {
body: {
useTemplate: true,
name: appName,
},
files: {
templateFile: {
path: appDirectory,
},
},
},
}
// @ts-ignore
return create(ctx)
}
async function getAllDocType(db: Database, docType: string) {
const response = await db.allDocs(
getDocParams(docType, null, {
include_docs: true,
})
)
return response.rows.map(row => row.doc)
}
export async function exportApps(ctx: Ctx) {
if (env.SELF_HOSTED || !env.MULTI_TENANCY) {
ctx.throw(400, "Exporting only allowed in multi-tenant cloud environments.")
}
const apps = (await dbCore.getAllApps({ all: true })) as App[]
const globalDBString = await sdk.backups.exportDB(dbCore.getGlobalDBName(), {
filter: (doc: any) => !doc._id.startsWith(DocumentType.USER),
})
// only export the dev apps as they will be the latest, the user can republish the apps
// in their self-hosted environment
let appMetadata = apps
.filter((app: App) => isDevAppID(app.appId || app._id))
.map((app: App) => ({ appId: (app.appId || app._id)!, name: app.name }))
const tmpPath = await sdk.backups.exportMultipleApps(
appMetadata,
globalDBString
)
const filename = `cloud-export-${new Date().getTime()}.tar.gz`
ctx.attachment(filename)
ctx.body = streamFile(tmpPath)
}
async function checkHasBeenImported() {
if (!env.SELF_HOSTED) {
return true
}
const apps = await dbCore.getAllApps({ all: true })
return apps.length !== 0
}
export async function hasBeenImported(ctx: Ctx) {
ctx.body = {
imported: await checkHasBeenImported(),
}
}
export async function importApps(ctx: Ctx) {
if (!env.SELF_HOSTED) {
ctx.throw(400, "Importing only allowed in self hosted environments.")
}
const beenImported = await checkHasBeenImported()
if (beenImported || !ctx.request.files || !ctx.request.files.importFile) {
ctx.throw(
400,
"Import file is required and environment must be fresh to import apps."
)
}
const file = ctx.request.files.importFile as any
if (Array.isArray(file)) {
ctx.throw(400, "Single file is required")
}
if (file.type !== "application/gzip" && file.type !== "application/x-gzip") {
ctx.throw(400, "Import file must be a gzipped tarball.")
}
// initially get all the app databases out of the tarball
const tmpPath = sdk.backups.untarFile(file)
const globalDbImport = sdk.backups.getGlobalDBFile(tmpPath)
const appNames = sdk.backups.getListOfAppsInMulti(tmpPath)
const globalDb = tenancy.getGlobalDB()
// load the global db first
await globalDb.load(stringToReadStream(globalDbImport))
for (let appName of appNames) {
await createApp(appName, join(tmpPath, appName))
}
// if there are any users make sure to remove them
let users = await getAllDocType(globalDb, DocumentType.USER)
let userDeletionPromises = []
for (let user of users) {
userDeletionPromises.push(globalDb.remove(user._id, user._rev))
}
if (userDeletionPromises.length > 0) {
await Promise.all(userDeletionPromises)
}
await globalDb.bulkDocs(users)
ctx.body = {
message: "Apps successfully imported.",
}
}

View File

@ -4,7 +4,6 @@ import currentApp from "../middleware/currentapp"
import zlib from "zlib"
import { mainRoutes, staticRoutes, publicRoutes } from "./routes"
import pkg from "../../package.json"
import env from "../environment"
import { middleware as pro } from "@budibase/pro"
export { shutdown } from "./routes/public"
const compress = require("koa-compress")

View File

@ -1,18 +0,0 @@
import Router from "@koa/router"
import * as controller from "../controllers/cloud"
import authorized from "../../middleware/authorized"
import { permissions } from "@budibase/backend-core"
const router: Router = new Router()
router
.get(
"/api/cloud/export",
authorized(permissions.BUILDER),
controller.exportApps
)
// has to be public, only run if apps don't exist
.post("/api/cloud/import", controller.importApps)
.get("/api/cloud/import/complete", controller.hasBeenImported)
export default router

View File

@ -22,7 +22,6 @@ import queryRoutes from "./query"
import backupRoutes from "./backup"
import metadataRoutes from "./metadata"
import devRoutes from "./dev"
import cloudRoutes from "./cloud"
import migrationRoutes from "./migrations"
import pluginRoutes from "./plugin"
import opsRoutes from "./ops"
@ -60,7 +59,6 @@ export const mainRoutes: Router[] = [
queryRoutes,
metadataRoutes,
devRoutes,
cloudRoutes,
rowRoutes,
migrationRoutes,
pluginRoutes,

View File

@ -1,54 +0,0 @@
import { App } from "@budibase/types"
jest.setTimeout(30000)
import { AppStatus } from "../../../db/utils"
import * as setup from "./utilities"
import { wipeDb } from "./utilities/TestFunctions"
import { tenancy } from "@budibase/backend-core"
describe("/cloud", () => {
let request = setup.getRequest()!
let config = setup.getConfig()
afterAll(setup.afterAll)
beforeAll(async () => {
// Importing is only allowed in self hosted environments
await config.init()
config.modeSelf()
})
describe("import", () => {
it("should be able to import apps", async () => {
// first we need to delete any existing apps on the system so it looks clean otherwise the
// import will not run
await wipeDb()
// Perform the import
const res = await request
.post(`/api/cloud/import`)
.set(config.publicHeaders())
.attach("importFile", "src/api/routes/tests/data/export-test.tar.gz")
.expect(200)
expect(res.body.message).toEqual("Apps successfully imported.")
// get a count of apps after the import
const postImportApps = await request
.get(`/api/applications?status=${AppStatus.ALL}`)
.set(config.publicHeaders())
.expect("Content-Type", /json/)
.expect(200)
const apps = postImportApps.body as App[]
// There are two apps in the file that was imported so check for this
expect(apps.length).toEqual(2)
// The new tenant id was assigned to the imported apps
expect(tenancy.getTenantIDFromAppID(apps[0].appId)).toBe(
config.getTenantId()
)
})
})
})

View File

@ -148,41 +148,6 @@ export async function exportApp(appId: string, config?: ExportOpts) {
}
}
/**
* Export all apps + global DB (if supplied) to a single tarball, this includes
* the attachments for each app as well.
* @param {object[]} appMetadata The IDs and names of apps to export.
* @param {string} globalDbContents The contents of the global DB to export as well.
* @return {string} The path to the tarball.
*/
export async function exportMultipleApps(
appMetadata: { appId: string; name: string }[],
globalDbContents?: string
) {
const tmpPath = join(budibaseTempDir(), uuid())
fs.mkdirSync(tmpPath)
let exportPromises: Promise<void>[] = []
// export each app to a directory, then move it into the complete export
const exportAndMove = async (appId: string, appName: string) => {
const path = await exportApp(appId)
await fs.promises.rename(path, join(tmpPath, appName))
}
for (let metadata of appMetadata) {
exportPromises.push(exportAndMove(metadata.appId, metadata.name))
}
// wait for all exports to finish
await Promise.all(exportPromises)
// add the global DB contents
if (globalDbContents) {
fs.writeFileSync(join(tmpPath, GLOBAL_DB_EXPORT_FILE), globalDbContents)
}
const appNames = appMetadata.map(metadata => metadata.name)
const tarPath = tarFilesToTmp(tmpPath, [...appNames, GLOBAL_DB_EXPORT_FILE])
// clear up the tmp path now tarball generated
fs.rmSync(tmpPath, { recursive: true, force: true })
return tarPath
}
/**
* Streams a backup of the database state for an app
* @param {string} appId The ID of the app which is to be backed up.

View File

@ -1386,45 +1386,6 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.5.6-alpha.3":
version "2.5.6-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.5.6-alpha.3.tgz#686d1533a3218507e4c82a58bf73caf875e0d895"
integrity sha512-TYaGj1G1h3K/QNwSanEFbwxlA04T3dc0DDHDgauyvqX8kHXYhY11BDn8xqrCQrpqXC7XlC4nL78NrhZiDJ+eFA==
dependencies:
"@budibase/nano" "10.1.2"
"@budibase/pouchdb-replication-stream" "1.2.10"
"@budibase/types" "2.5.6-alpha.3"
"@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0"
aws-sdk "2.1030.0"
bcrypt "5.0.1"
bcryptjs "2.4.3"
bull "4.10.1"
correlation-id "4.0.0"
dotenv "16.0.1"
emitter-listener "1.1.2"
ioredis "4.28.0"
joi "17.6.0"
jsonwebtoken "9.0.0"
koa-passport "4.1.4"
koa-pino-logger "4.0.0"
lodash "4.17.21"
lodash.isarguments "3.1.0"
node-fetch "2.6.7"
passport-google-oauth "2.0.0"
passport-jwt "4.0.0"
passport-local "1.0.0"
passport-oauth2-refresh "^2.1.0"
posthog-node "1.3.0"
pouchdb "7.3.0"
pouchdb-find "7.2.2"
redlock "4.2.0"
sanitize-s3-objectkey "0.0.1"
semver "7.3.7"
tar-fs "2.1.1"
uuid "8.3.2"
"@budibase/bbui@^0.9.139":
version "0.9.190"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.190.tgz#e1ec400ac90f556bfbc80fc23a04506f1585ea81"
@ -1525,15 +1486,15 @@
pouchdb-promise "^6.0.4"
through2 "^2.0.0"
"@budibase/pro@2.5.6-alpha.5":
version "2.5.6-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.5.tgz#8136ea50e06aab8a23e48359d58c6c84ba386263"
integrity sha512-YEcDRJU4Up7g/fMSnO8K8YH6gObO/Sy0Jh28Ms+Sxb9RQ/zqlcobonsICF6h2ntfzLZH2cfArLGFEvIT10/PvA==
"@budibase/pro@2.5.6-alpha.6":
version "2.5.6-alpha.6"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.6.tgz#079c9f6ee1e0efdc1487d841d24f765eb8c3ca40"
integrity sha512-7R51B1FIlXVolMoWa/WmDrpDvSqtrbZywIzOHq6NRbdvVSBCW3sD+biDIRXo9MFvNT9sLJXB21A4DLjfIYDpOA==
dependencies:
"@budibase/backend-core" "2.5.6-alpha.5"
"@budibase/backend-core" "2.5.6-alpha.6"
"@budibase/shared-core" "2.4.44-alpha.1"
"@budibase/string-templates" "2.4.44-alpha.1"
"@budibase/types" "2.5.6-alpha.5"
"@budibase/types" "2.5.6-alpha.6"
"@koa/router" "8.0.8"
bull "4.10.1"
joi "17.6.0"
@ -1586,13 +1547,6 @@
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.1.tgz#1679657aa180d9c59afa1dffa611bff0638bd933"
integrity sha512-Sq+8HfM75EBMoOvKYFwELdlxmVN6wNZMofDjT/2G+9aF+Zfe5Tzw69C+unmdBgcGGjGCHEYWSz4mF0v8FPAGbg==
"@budibase/types@2.5.6-alpha.3":
version "2.5.6-alpha.3"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.5.6-alpha.3.tgz#fd7656599d388d2d5a21806b9058a2656793ec87"
integrity sha512-SD9WTXb2A+3jqEEk8HoEDwphm5tiwfQWiBFOzTZlooWiS5M6UGxKCgXzPv0Ad9Pfxi2oGxfGh7qJcNgHQDoE3Q==
dependencies:
scim-patch "^0.7.0"
"@bull-board/api@3.7.0":
version "3.7.0"
resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.7.0.tgz#231f687187c0cb34e0b97f463917b6aaeb4ef6af"
@ -3053,7 +3007,7 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
@ -3763,7 +3717,7 @@
dependencies:
slash "^3.0.0"
"@rollup/plugin-commonjs@16.0.0", "@rollup/plugin-commonjs@^16.0.0":
"@rollup/plugin-commonjs@^16.0.0":
version "16.0.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f"
integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw==
@ -3846,22 +3800,6 @@
"@rollup/pluginutils" "^3.1.0"
magic-string "^0.25.7"
"@rollup/plugin-replace@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz#45f53501b16311feded2485e98419acb8448c61d"
integrity sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==
dependencies:
"@rollup/pluginutils" "^5.0.1"
magic-string "^0.27.0"
"@rollup/plugin-typescript@8.3.0":
version "8.3.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz#bc1077fa5897b980fc27e376c4e377882c63e68b"
integrity sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==
dependencies:
"@rollup/pluginutils" "^3.1.0"
resolve "^1.17.0"
"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
@ -11760,7 +11698,7 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2:
fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@ -16750,13 +16688,6 @@ magic-string@^0.26.2:
dependencies:
sourcemap-codec "^1.4.8"
magic-string@^0.27.0:
version "0.27.0"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3"
integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.13"
make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@ -21486,13 +21417,6 @@ rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0,
dependencies:
estree-walker "^0.6.1"
rollup@2.45.2:
version "2.45.2"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.45.2.tgz#8fb85917c9f35605720e92328f3ccbfba6f78b48"
integrity sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ==
optionalDependencies:
fsevents "~2.3.1"
rollup@^2.36.2, rollup@^2.44.0, rollup@^2.45.2, rollup@^2.79.1:
version "2.79.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7"
@ -23455,7 +23379,7 @@ timed-out@^4.0.1:
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==
timekeeper@2.2.0, timekeeper@^2.2.0:
timekeeper@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368"
integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A==