diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DownloadFile.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DownloadFile.svelte
new file mode 100644
index 0000000000..babaf2815b
--- /dev/null
+++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/DownloadFile.svelte
@@ -0,0 +1,110 @@
+
+
+
+
+
+ {#if parameters.type === "attachment"}
+
+
+
+
diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/index.js b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/index.js
index eb354d6557..587993377d 100644
--- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/index.js
+++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/index.js
@@ -22,3 +22,4 @@ export { default as PromptUser } from "./PromptUser.svelte"
export { default as OpenSidePanel } from "./OpenSidePanel.svelte"
export { default as CloseSidePanel } from "./CloseSidePanel.svelte"
export { default as ClearRowSelection } from "./ClearRowSelection.svelte"
+export { default as DownloadFile } from "./DownloadFile.svelte"
diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/manifest.json b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/manifest.json
index 9391baf3dc..2840a0d662 100644
--- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/manifest.json
+++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/manifest.json
@@ -161,6 +161,11 @@
"name": "Clear Row Selection",
"type": "data",
"component": "ClearRowSelection"
+ },
+ {
+ "name": "Download File",
+ "type": "data",
+ "component": "DownloadFile"
}
]
}
diff --git a/packages/builder/src/components/start/ExportAppModal.svelte b/packages/builder/src/components/start/ExportAppModal.svelte
index ec0cf42fe0..3a995be7ae 100644
--- a/packages/builder/src/components/start/ExportAppModal.svelte
+++ b/packages/builder/src/components/start/ExportAppModal.svelte
@@ -8,6 +8,7 @@
Input,
notifications,
} from "@budibase/bbui"
+ import { downloadFile } from "@budibase/frontend-core"
import { createValidationStore } from "helpers/validation/yup"
export let app
@@ -55,40 +56,13 @@
const exportApp = async () => {
const id = published ? app.prodId : app.devId
const url = `/api/backups/export?appId=${id}`
- await downloadFile(url, {
- excludeRows: !includeInternalTablesRows,
- encryptPassword: password,
- })
- }
- async function downloadFile(url, body) {
try {
- const response = await fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify(body),
+ const downloaded = await downloadFile(url, {
+ excludeRows: !includeInternalTablesRows,
+ encryptPassword: password,
})
-
- if (response.ok) {
- const contentDisposition = response.headers.get("Content-Disposition")
-
- const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(
- contentDisposition
- )
-
- const filename = matches[1].replace(/['"]/g, "")
-
- const url = URL.createObjectURL(await response.blob())
-
- const link = document.createElement("a")
- link.href = url
- link.download = filename
- link.click()
-
- URL.revokeObjectURL(url)
- } else {
+ if (!downloaded) {
notifications.error("Error exporting the app.")
}
} catch (error) {
diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js
index 68478b76ac..d883ee1b55 100644
--- a/packages/client/src/utils/buttonActions.js
+++ b/packages/client/src/utils/buttonActions.js
@@ -1,5 +1,6 @@
import { get } from "svelte/store"
import download from "downloadjs"
+import { downloadStream } from "@budibase/frontend-core"
import {
routeStore,
builderStore,
@@ -400,6 +401,51 @@ const closeSidePanelHandler = () => {
sidePanelStore.actions.close()
}
+const downloadFileHandler = async action => {
+ const { url, fileName } = action.parameters
+ try {
+ const { type } = action.parameters
+ if (type === "attachment") {
+ const { tableId, rowId, attachmentColumn } = action.parameters
+ const res = await API.downloadAttachment(
+ tableId,
+ rowId,
+ attachmentColumn,
+ { suppressErrors: true }
+ )
+ await downloadStream(res)
+ return
+ }
+
+ const response = await fetch(url)
+
+ if (!response.ok) {
+ notificationStore.actions.error(
+ `Failed to download from '${url}'. Server returned status code: ${response.status}`
+ )
+ return
+ }
+
+ const objectUrl = URL.createObjectURL(await response.blob())
+
+ const link = document.createElement("a")
+ link.href = objectUrl
+ link.download = fileName
+ link.click()
+
+ URL.revokeObjectURL(objectUrl)
+ } catch (e) {
+ console.error(e)
+ if (e.status) {
+ notificationStore.actions.error(
+ `Failed to download from '${url}'. Server returned status code: ${e.status}`
+ )
+ } else {
+ notificationStore.actions.error(`Failed to download from '${url}'.`)
+ }
+ }
+}
+
const handlerMap = {
["Fetch Row"]: fetchRowHandler,
["Save Row"]: saveRowHandler,
@@ -418,6 +464,7 @@ const handlerMap = {
["Prompt User"]: promptUserHandler,
["Open Side Panel"]: openSidePanelHandler,
["Close Side Panel"]: closeSidePanelHandler,
+ ["Download File"]: downloadFileHandler,
}
const confirmTextMap = {
diff --git a/packages/client/tsconfig.json b/packages/client/tsconfig.json
new file mode 100644
index 0000000000..81f1657f48
--- /dev/null
+++ b/packages/client/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "strict": true,
+ "outDir": "dist",
+ "paths": {
+ "@budibase/*": [
+ "../*/src/index.ts",
+ "../*/src/index.js",
+ "../*",
+ "../../node_modules/@budibase/*"
+ ],
+ "*": ["./src/*"]
+ }
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/frontend-core/src/api/attachments.js b/packages/frontend-core/src/api/attachments.js
index f79b461574..cff466c86f 100644
--- a/packages/frontend-core/src/api/attachments.js
+++ b/packages/frontend-core/src/api/attachments.js
@@ -88,5 +88,19 @@ export const buildAttachmentEndpoints = API => {
},
})
},
+
+ /**
+ * Download an attachment from a row given its column name.
+ * @param datasourceId the ID of the datasource to download from
+ * @param rowId the ID of the row to download from
+ * @param columnName the column name to download
+ */
+ downloadAttachment: async (datasourceId, rowId, columnName, options) => {
+ return await API.get({
+ url: `/api/${datasourceId}/rows/${rowId}/attachment/${columnName}`,
+ parseResponse: response => response,
+ suppressErrors: options?.suppressErrors,
+ })
+ },
}
}
diff --git a/packages/frontend-core/src/utils/download.js b/packages/frontend-core/src/utils/download.js
index 89c8572253..fb9990ff30 100644
--- a/packages/frontend-core/src/utils/download.js
+++ b/packages/frontend-core/src/utils/download.js
@@ -1,3 +1,5 @@
+const extractFileNameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
+
export function downloadText(filename, text) {
if (typeof text === "object") {
text = JSON.stringify(text)
@@ -17,9 +19,7 @@ export async function downloadStream(streamResponse) {
const contentDisposition = streamResponse.headers.get("Content-Disposition")
- const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(
- contentDisposition
- )
+ const matches = extractFileNameRegex.exec(contentDisposition)
const filename = matches[1].replace(/['"]/g, "")
@@ -34,3 +34,33 @@ export async function downloadStream(streamResponse) {
URL.revokeObjectURL(blobUrl)
}
+
+export async function downloadFile(url, body) {
+ const response = await fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ })
+
+ if (!response.ok) {
+ return false
+ } else {
+ const contentDisposition = response.headers.get("Content-Disposition")
+
+ const matches = extractFileNameRegex.exec(contentDisposition)
+
+ const filename = matches[1].replace(/['"]/g, "")
+
+ const url = URL.createObjectURL(await response.blob())
+
+ const link = document.createElement("a")
+ link.href = url
+ link.download = filename
+ link.click()
+
+ URL.revokeObjectURL(url)
+ return true
+ }
+}
diff --git a/packages/server/package.json b/packages/server/package.json
index 59f06ea399..ad03033e67 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -50,10 +50,10 @@
"@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "0.0.0",
"@budibase/client": "0.0.0",
+ "@budibase/frontend-core": "0.0.0",
"@budibase/pro": "0.0.0",
"@budibase/shared-core": "0.0.0",
"@budibase/string-templates": "0.0.0",
- "@budibase/frontend-core": "0.0.0",
"@budibase/types": "0.0.0",
"@bull-board/api": "5.10.2",
"@bull-board/koa": "5.10.2",
@@ -63,6 +63,7 @@
"@socket.io/redis-adapter": "^8.2.1",
"airtable": "0.10.1",
"arangojs": "7.2.0",
+ "archiver": "7.0.1",
"aws-sdk": "2.1030.0",
"bcrypt": "5.1.0",
"bcryptjs": "2.4.3",
@@ -124,6 +125,7 @@
"@babel/preset-env": "7.16.11",
"@swc/core": "1.3.71",
"@swc/jest": "0.2.27",
+ "@types/archiver": "^6.0.2",
"@types/global-agent": "2.1.1",
"@types/google-spreadsheet": "3.1.5",
"@types/jest": "29.5.5",
diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts
index 7dc70a3009..c3d1f2cb47 100644
--- a/packages/server/src/api/controllers/row/index.ts
+++ b/packages/server/src/api/controllers/row/index.ts
@@ -1,4 +1,8 @@
+import stream from "stream"
+import archiver from "archiver"
+
import { quotas } from "@budibase/pro"
+import { objectStore } from "@budibase/backend-core"
import * as internal from "./internal"
import * as external from "./external"
import { isExternalTableID } from "../../../integrations/utils"
@@ -9,10 +13,12 @@ import {
DeleteRows,
ExportRowsRequest,
ExportRowsResponse,
+ FieldType,
GetRowResponse,
PatchRowRequest,
PatchRowResponse,
Row,
+ RowAttachment,
RowSearchParams,
SearchRowRequest,
SearchRowResponse,
@@ -251,3 +257,59 @@ export const exportRows = async (
ctx.attachment(fileName)
ctx.body = apiFileReturn(content)
}
+
+export async function downloadAttachment(ctx: UserCtx) {
+ const { columnName } = ctx.params
+
+ const tableId = utils.getTableId(ctx)
+ const row = await pickApi(tableId).find(ctx)
+
+ const table = await sdk.tables.getTable(tableId)
+ const columnSchema = table.schema[columnName]
+ if (!columnSchema) {
+ ctx.throw(400, `'${columnName}' is not valid`)
+ }
+
+ const columnType = columnSchema.type
+
+ if (
+ columnType !== FieldType.ATTACHMENTS &&
+ columnType !== FieldType.ATTACHMENT_SINGLE
+ ) {
+ ctx.throw(404, `'${columnName}' is not valid attachment column`)
+ }
+
+ const attachments: RowAttachment[] =
+ columnType === FieldType.ATTACHMENTS ? row[columnName] : [row[columnName]]
+
+ if (!attachments?.length) {
+ ctx.throw(404)
+ }
+
+ if (attachments.length === 1) {
+ const attachment = attachments[0]
+ ctx.attachment(attachment.name)
+ ctx.body = await objectStore.getReadStream(
+ objectStore.ObjectStoreBuckets.APPS,
+ attachment.key
+ )
+ } else {
+ const passThrough = new stream.PassThrough()
+ const archive = archiver.create("zip")
+ archive.pipe(passThrough)
+
+ for (const attachment of attachments) {
+ const attachmentStream = await objectStore.getReadStream(
+ objectStore.ObjectStoreBuckets.APPS,
+ attachment.key
+ )
+ archive.append(attachmentStream, { name: attachment.name })
+ }
+
+ const displayName = row[table.primaryDisplay || "_id"]
+ ctx.attachment(`${displayName}_${columnName}.zip`)
+ archive.finalize()
+ ctx.body = passThrough
+ ctx.type = "zip"
+ }
+}
diff --git a/packages/server/src/api/routes/row.ts b/packages/server/src/api/routes/row.ts
index 516bfd20c6..f1aa39a461 100644
--- a/packages/server/src/api/routes/row.ts
+++ b/packages/server/src/api/routes/row.ts
@@ -77,6 +77,12 @@ router
authorized(PermissionType.TABLE, PermissionLevel.WRITE),
rowController.exportRows
)
+ .get(
+ "/api/:sourceId/rows/:rowId/attachment/:columnName",
+ paramSubResource("sourceId", "rowId"),
+ authorized(PermissionType.TABLE, PermissionLevel.READ),
+ rowController.downloadAttachment
+ )
router.post(
"/api/v2/views/:viewId/search",
diff --git a/packages/string-templates/src/index.ts b/packages/string-templates/src/index.ts
index 1ac91edd28..847567cb5a 100644
--- a/packages/string-templates/src/index.ts
+++ b/packages/string-templates/src/index.ts
@@ -12,11 +12,12 @@ import {
} from "./utilities"
import { convertHBSBlock } from "./conversion"
import { setJSRunner, removeJSRunner } from "./helpers/javascript"
-import { helpersToRemoveForJs } from "./helpers/list"
import manifest from "./manifest.json"
import { ProcessOptions } from "./types"
+export { helpersToRemoveForJs } from "./helpers/list"
+export { FIND_ANY_HBS_REGEX } from "./utilities"
export { setJSRunner, setOnErrorLog } from "./helpers/javascript"
export { iifeWrapper } from "./iife"
@@ -412,15 +413,9 @@ export function convertToJS(hbs: string) {
return `${varBlock}${js}`
}
-const _FIND_ANY_HBS_REGEX = FIND_ANY_HBS_REGEX
-export { _FIND_ANY_HBS_REGEX as FIND_ANY_HBS_REGEX }
-
export { JsErrorTimeout } from "./errors"
-const _helpersToRemoveForJs = helpersToRemoveForJs
-export { _helpersToRemoveForJs as helpersToRemoveForJs }
-
-function defaultJSSetup() {
+export function defaultJSSetup() {
if (!isBackendService()) {
/**
* Use polyfilled vm to run JS scripts in a browser Env
@@ -440,6 +435,3 @@ function defaultJSSetup() {
}
}
defaultJSSetup()
-
-const _defaultJSSetup = defaultJSSetup
-export { _defaultJSSetup as defaultJSSetup }
diff --git a/packages/string-templates/test/basic.spec.ts b/packages/string-templates/test/basic.spec.ts
index 00058d4ecd..ddea54c2bf 100644
--- a/packages/string-templates/test/basic.spec.ts
+++ b/packages/string-templates/test/basic.spec.ts
@@ -126,6 +126,63 @@ describe("Test that the object processing works correctly", () => {
})
})
+describe("check arrays", () => {
+ describe("index with square brackets", () => {
+ it.each([
+ [0, "1"],
+ [1, "2"],
+ ])("should handle an array of primitive types", async (index, expected) => {
+ const json = [1, 2, 3]
+ const output = await processString(`{{ testing.[${index}] }}`, {
+ testing: json,
+ })
+ expect(output).toEqual(expected)
+ })
+
+ it("should handle an array of objects", async () => {
+ const json = [{ value: 1 }, { value: 2 }, { value: 3 }]
+ const output = await processString("{{ testing.[1] }}", {
+ testing: json,
+ })
+ expect(output).toEqual('{"value":2}')
+ })
+
+ it("should handle nesting properties in an array of objects", async () => {
+ const json = [{ value: 1 }, { value: 2 }, { value: 3 }]
+ const output = await processString("{{ testing.[1].value }}", {
+ testing: json,
+ })
+ expect(output).toEqual("2")
+ })
+ })
+
+ describe("index without square brackets", () => {
+ it("should not handle an array of primitive types", async () => {
+ const json = [1, 2, 3]
+ const output = await processString(`{{ testing.1 }}`, {
+ testing: json,
+ })
+ expect(output).toEqual("{{ testing.1 }}")
+ })
+
+ it("should not handle an array of objects", async () => {
+ const json = [{ value: 1 }, { value: 2 }, { value: 3 }]
+ const output = await processString("{{ testing.1 }}", {
+ testing: json,
+ })
+ expect(output).toEqual("{{ testing.1 }}")
+ })
+
+ it("should handle nesting properties in an array of object types", async () => {
+ const json = [{ value: 1 }, { value: 2 }, { value: 3 }]
+ const output = await processString("{{ testing.1.value }}", {
+ testing: json,
+ })
+ expect(output).toEqual("2")
+ })
+ })
+})
+
describe("check returning objects", () => {
it("should handle an array of objects", async () => {
const json = [{ a: 1 }, { a: 2 }]
diff --git a/yarn.lock b/yarn.lock
index 3ac0351e64..a36b54d3be 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5174,6 +5174,13 @@
dependencies:
"@types/node" "*"
+"@types/archiver@^6.0.2":
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-6.0.2.tgz#0daf8c83359cbde69de1e4b33dcade6a48a929e2"
+ integrity sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==
+ dependencies:
+ "@types/readdir-glob" "*"
+
"@types/aria-query@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc"
@@ -5923,6 +5930,13 @@
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
+"@types/readdir-glob@*":
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/@types/readdir-glob/-/readdir-glob-1.1.5.tgz#21a4a98898fc606cb568ad815f2a0eedc24d412a"
+ integrity sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==
+ dependencies:
+ "@types/node" "*"
+
"@types/redis@^2.8.0":
version "2.8.32"
resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.32.tgz#1d3430219afbee10f8cfa389dad2571a05ecfb11"
@@ -6993,6 +7007,32 @@ archiver-utils@^3.0.4:
normalize-path "^3.0.0"
readable-stream "^3.6.0"
+archiver-utils@^5.0.0, archiver-utils@^5.0.2:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-5.0.2.tgz#63bc719d951803efc72cf961a56ef810760dd14d"
+ integrity sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==
+ dependencies:
+ glob "^10.0.0"
+ graceful-fs "^4.2.0"
+ is-stream "^2.0.1"
+ lazystream "^1.0.0"
+ lodash "^4.17.15"
+ normalize-path "^3.0.0"
+ readable-stream "^4.0.0"
+
+archiver@7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/archiver/-/archiver-7.0.1.tgz#c9d91c350362040b8927379c7aa69c0655122f61"
+ integrity sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==
+ dependencies:
+ archiver-utils "^5.0.2"
+ async "^3.2.4"
+ buffer-crc32 "^1.0.0"
+ readable-stream "^4.0.0"
+ readdir-glob "^1.1.2"
+ tar-stream "^3.0.0"
+ zip-stream "^6.0.1"
+
archiver@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.2.tgz#99991d5957e53bd0303a392979276ac4ddccf3b0"
@@ -7794,6 +7834,11 @@ buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
+buffer-crc32@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405"
+ integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==
+
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
@@ -8630,6 +8675,17 @@ compress-commons@^4.1.2:
normalize-path "^3.0.0"
readable-stream "^3.6.0"
+compress-commons@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-6.0.2.tgz#26d31251a66b9d6ba23a84064ecd3a6a71d2609e"
+ integrity sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==
+ dependencies:
+ crc-32 "^1.2.0"
+ crc32-stream "^6.0.0"
+ is-stream "^2.0.1"
+ normalize-path "^3.0.0"
+ readable-stream "^4.0.0"
+
compressible@^2.0.0, compressible@^2.0.12:
version "2.0.18"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
@@ -8922,6 +8978,14 @@ crc32-stream@^4.0.2:
crc-32 "^1.2.0"
readable-stream "^3.4.0"
+crc32-stream@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-6.0.0.tgz#8529a3868f8b27abb915f6c3617c0fadedbf9430"
+ integrity sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==
+ dependencies:
+ crc-32 "^1.2.0"
+ readable-stream "^4.0.0"
+
crc@^3.4.4:
version "3.8.0"
resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6"
@@ -11800,6 +11864,17 @@ glob@7.1.6:
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@^10.0.0:
+ version "10.3.12"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b"
+ integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==
+ dependencies:
+ foreground-child "^3.1.0"
+ jackspeak "^2.3.6"
+ minimatch "^9.0.1"
+ minipass "^7.0.4"
+ path-scurry "^1.10.2"
+
glob@^10.2.2:
version "10.2.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-10.2.7.tgz#9dd2828cd5bc7bd861e7738d91e7113dda41d7d8"
@@ -13186,7 +13261,7 @@ is-stream@^1.1.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
-is-stream@^2.0.0:
+is-stream@^2.0.0, is-stream@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
@@ -13432,6 +13507,15 @@ jackspeak@^2.0.3:
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"
+jackspeak@^2.3.6:
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8"
+ integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==
+ dependencies:
+ "@isaacs/cliui" "^8.0.2"
+ optionalDependencies:
+ "@pkgjs/parseargs" "^0.11.0"
+
jake@^10.8.5:
version "10.8.5"
resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46"
@@ -15209,6 +15293,11 @@ lowercase-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+lru-cache@^10.2.0:
+ version "10.2.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3"
+ integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==
+
lru-cache@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -15834,6 +15923,11 @@ minipass@^5.0.0:
resolved "https://registry.yarnpkg.com/minipass/-/minipass-6.0.2.tgz#542844b6c4ce95b202c0995b0a471f1229de4c81"
integrity sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4:
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c"
+ integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==
+
minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
@@ -17392,6 +17486,14 @@ path-parser@^6.1.0:
search-params "3.0.0"
tslib "^1.10.0"
+path-scurry@^1.10.2:
+ version "1.10.2"
+ resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7"
+ integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==
+ dependencies:
+ lru-cache "^10.2.0"
+ minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
+
path-scurry@^1.6.1, path-scurry@^1.7.0:
version "1.9.2"
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.9.2.tgz#90f9d296ac5e37e608028e28a447b11d385b3f63"
@@ -21037,7 +21139,7 @@ tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@^2.2.0, tar-stream@~2.2.0:
inherits "^2.0.3"
readable-stream "^3.1.1"
-tar-stream@^3.1.5:
+tar-stream@^3.0.0, tar-stream@^3.1.5:
version "3.1.7"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b"
integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==
@@ -22941,3 +23043,12 @@ zip-stream@^4.1.0:
archiver-utils "^3.0.4"
compress-commons "^4.1.2"
readable-stream "^3.6.0"
+
+zip-stream@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-6.0.1.tgz#e141b930ed60ccaf5d7fa9c8260e0d1748a2bbfb"
+ integrity sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==
+ dependencies:
+ archiver-utils "^5.0.0"
+ compress-commons "^6.0.2"
+ readable-stream "^4.0.0"