Merge branch 'master' into BUDI-9296/new-screen-as-modal
This commit is contained in:
commit
fded47b163
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "3.11.0",
|
||||
"version": "3.11.1",
|
||||
"npmClient": "yarn",
|
||||
"concurrency": 20,
|
||||
"command": {
|
||||
|
|
|
@ -64,9 +64,9 @@
|
|||
--rounded-medium: 8px;
|
||||
--rounded-large: 16px;
|
||||
|
||||
--font-sans: "Source Sans Pro", -apple-system, BlinkMacSystemFont, Segoe UI,
|
||||
--font-sans: "Source Sans 3", -apple-system, BlinkMacSystemFont, Segoe UI,
|
||||
"Inter", "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||
--font-accent: "Source Sans Pro", -apple-system, BlinkMacSystemFont, Segoe UI,
|
||||
--font-accent: "Source Sans 3", -apple-system, BlinkMacSystemFont, Segoe UI,
|
||||
"Inter", "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
||||
--font-serif: "Georgia", Cambria, Times New Roman, Times, serif;
|
||||
--font-mono: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,15 @@
|
|||
@font-face{
|
||||
font-family: "Source Sans 3";
|
||||
font-weight: 200 900;
|
||||
font-style: normal;
|
||||
font-stretch: normal;
|
||||
src: url("/builder/fonts/source-sans-3/SourceSans3VF-Upright.ttf.woff2") format("woff2-variations");
|
||||
}
|
||||
|
||||
@font-face{
|
||||
font-family: "Source Sans 3";
|
||||
font-weight: 200 900;
|
||||
font-style: italic;
|
||||
font-stretch: normal;
|
||||
src: url("/builder/fonts/source-sans-3/SourceSans3VF-Italic.ttf.woff2") format("woff2-variations");
|
||||
}
|
|
@ -5,9 +5,7 @@
|
|||
<meta charset='utf8'>
|
||||
<meta name='viewport' content='width=device-width'>
|
||||
<title>Budibase</title>
|
||||
<link href="/builder/fonts/source-sans-pro/400.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/source-sans-pro/600.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/source-sans-pro/700.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/source-sans-3/source-sans-3.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/remixicon.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@codemirror/view": "^6.11.2",
|
||||
"@dagrejs/dagre": "1.1.4",
|
||||
"@fontsource/source-sans-pro": "^5.0.3",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
|
|
|
@ -100,6 +100,20 @@
|
|||
comp: ExecuteScriptV2,
|
||||
fullWidth: true,
|
||||
},
|
||||
[SchemaFieldTypes.LONGFORM]: {
|
||||
comp: DrawerBindableInput,
|
||||
props: (opts: FieldProps = {} as FieldProps) => {
|
||||
const { key, field } = opts
|
||||
return {
|
||||
title: field.title ?? getFieldLabel(key, field),
|
||||
panel: AutomationBindingPanel,
|
||||
type: field.customType,
|
||||
updateOnChange: false,
|
||||
multiline: true,
|
||||
placeholder: field?.description,
|
||||
}
|
||||
},
|
||||
},
|
||||
[SchemaFieldTypes.BOOL]: {
|
||||
comp: Checkbox,
|
||||
},
|
||||
|
@ -309,7 +323,7 @@
|
|||
*/
|
||||
const getFieldType = (
|
||||
field: BaseIOStructure,
|
||||
block?: AutomationStep | AutomationTrigger
|
||||
block: AutomationStep | AutomationTrigger
|
||||
) => {
|
||||
// Direct customType map
|
||||
const customType = field.customType && customTypeToSchema[field.customType]
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
</script>
|
||||
|
||||
{#if customLayout}
|
||||
<!-- Render custom layout 1 or more components in a custom layout -->
|
||||
<!-- Render 1 or more components in a custom layout -->
|
||||
<AutomationCustomLayout {context} {bindings} {block} layout={customLayout} />
|
||||
{:else}
|
||||
<!-- Render Automation Step Schema > [string, BaseIOStructure][] -->
|
||||
|
|
|
@ -47,7 +47,8 @@
|
|||
"icon",
|
||||
"embed",
|
||||
"backgroundimage",
|
||||
"embeddedmap"
|
||||
"embeddedmap",
|
||||
"codegenerator"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -180,7 +180,7 @@
|
|||
}
|
||||
|
||||
thead th {
|
||||
font-family: "Source Sans Pro";
|
||||
font-family: "Source Sans 3";
|
||||
color: var(--lightText);
|
||||
white-space: nowrap;
|
||||
padding: 12px;
|
||||
|
|
|
@ -24,6 +24,7 @@ export enum DataMode {
|
|||
}
|
||||
|
||||
export enum SchemaFieldTypes {
|
||||
LONGFORM = "longform",
|
||||
JSON = "json",
|
||||
ENUM = "enum",
|
||||
BOOL = "boolean",
|
||||
|
@ -160,6 +161,7 @@ export const customTypeToSchema: Record<string, SchemaFieldTypes> = {
|
|||
export const typeToSchema: Partial<Record<AutomationIOType, SchemaFieldTypes>> =
|
||||
{
|
||||
[AutomationIOType.BOOLEAN]: SchemaFieldTypes.BOOL,
|
||||
[AutomationIOType.LONGFORM]: SchemaFieldTypes.LONGFORM,
|
||||
[AutomationIOType.DATE]: SchemaFieldTypes.DATE,
|
||||
[AutomationIOType.JSON]: SchemaFieldTypes.JSON,
|
||||
[AutomationIOType.ATTACHMENT]: SchemaFieldTypes.FILE,
|
||||
|
|
|
@ -17,7 +17,7 @@ const copyFonts = dest =>
|
|||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: "../../node_modules/@fontsource/source-sans-pro",
|
||||
src: "./assets/source-sans-3",
|
||||
dest,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -4558,6 +4558,108 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"codegenerator": {
|
||||
"name": "Barcode/QR Generator",
|
||||
"icon": "Vignette",
|
||||
"new": true,
|
||||
"settings": [
|
||||
{
|
||||
"type": "radio",
|
||||
"label": "Barcode/QR",
|
||||
"key": "codeType",
|
||||
"options": ["QR Code", "Barcode"],
|
||||
"defaultValue": "QR Code"
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Size",
|
||||
"key": "size",
|
||||
"defaultValue": 200,
|
||||
"required": true,
|
||||
"options": [
|
||||
{
|
||||
"label": "X Small",
|
||||
"value": 75
|
||||
},
|
||||
{
|
||||
"label": "Small",
|
||||
"value": 150
|
||||
},
|
||||
{
|
||||
"label": "Medium",
|
||||
"value": 200
|
||||
},
|
||||
{
|
||||
"label": "Large",
|
||||
"value": 250
|
||||
},
|
||||
{
|
||||
"label": "X Large",
|
||||
"value": 300
|
||||
},
|
||||
{
|
||||
"label": "XX Large",
|
||||
"value": 350
|
||||
},
|
||||
{
|
||||
"label": "XXX Large",
|
||||
"value": 400
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "value",
|
||||
"label": "Value",
|
||||
"defaultValue": "https://docs.budibase.com/"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "showValue",
|
||||
"label": "Display value",
|
||||
"defaultValue": true
|
||||
},
|
||||
|
||||
{
|
||||
"section": true,
|
||||
"name": "Customisation",
|
||||
"settings": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "showLogo",
|
||||
"label": "Show Logo",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "customLogo",
|
||||
"label": "Logo",
|
||||
"dependsOn": {
|
||||
"setting": "showLogo",
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"section": true,
|
||||
"name": "Appearance",
|
||||
"settings": [
|
||||
{
|
||||
"type": "color",
|
||||
"label": "Primary color",
|
||||
"key": "primColor",
|
||||
"defaultValue": "#000",
|
||||
"dependsOn": {
|
||||
"setting": "codeType",
|
||||
"value": "QR Code",
|
||||
"invert": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"signaturesinglefield": {
|
||||
"name": "Signature",
|
||||
"icon": "AnnotatePen",
|
||||
|
@ -5545,12 +5647,26 @@
|
|||
"wide": true
|
||||
},
|
||||
{
|
||||
"label": "",
|
||||
"type": "filterConfiguration",
|
||||
"key": "filterConfig",
|
||||
"nested": true,
|
||||
"dependsOn": "targetComponent",
|
||||
"resetOn": "targetComponent"
|
||||
"type": "select",
|
||||
"label": "Size",
|
||||
"showInBar": true,
|
||||
"key": "size",
|
||||
"options": [
|
||||
{
|
||||
"label": "Small",
|
||||
"value": "S"
|
||||
},
|
||||
{
|
||||
"label": "Medium",
|
||||
"value": "M"
|
||||
},
|
||||
{
|
||||
"label": "Large",
|
||||
"value": "L"
|
||||
}
|
||||
],
|
||||
"defaultValue": "M",
|
||||
"wide": true
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
|
@ -5563,6 +5679,14 @@
|
|||
"label": "Clear filters",
|
||||
"key": "showClear",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"label": "",
|
||||
"type": "filterConfiguration",
|
||||
"key": "filterConfig",
|
||||
"nested": true,
|
||||
"dependsOn": "targetComponent",
|
||||
"resetOn": "targetComponent"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -35,7 +35,9 @@
|
|||
"sanitize-html": "^2.13.0",
|
||||
"screenfull": "^6.0.1",
|
||||
"shortid": "^2.2.15",
|
||||
"svelte-spa-router": "^4.0.1"
|
||||
"svelte-spa-router": "^4.0.1",
|
||||
"jsbarcode": "^3.11.6",
|
||||
"@svelte-put/qr": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "1.4.0",
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from "svelte"
|
||||
import { onMount } from "svelte"
|
||||
import JsBarcode from "jsbarcode"
|
||||
import { createQrSvgString } from "@svelte-put/qr"
|
||||
|
||||
export let value: string
|
||||
export let codeType: "QR Code" | "Barcode"
|
||||
export let showValue: boolean
|
||||
export let showLogo: boolean
|
||||
export let customLogo: string | undefined
|
||||
export let size: number
|
||||
export let primColor: string
|
||||
|
||||
const { styleable } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
let barcodeElement: SVGSVGElement
|
||||
|
||||
function generateBarcode() {
|
||||
if (barcodeElement && value) {
|
||||
JsBarcode(barcodeElement, value, {
|
||||
displayValue: false, // Hide the library's built in value, optionally display it later
|
||||
width: size / 100,
|
||||
height: size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let qrContainer: HTMLElement
|
||||
|
||||
const generateQr = () => {
|
||||
if (qrContainer && codeType === "QR Code" && value) {
|
||||
const svg = createQrSvgString({
|
||||
data: value,
|
||||
logo: showLogo ? customLogo : "",
|
||||
moduleFill: primColor,
|
||||
anchorOuterFill: primColor,
|
||||
anchorInnerFill: primColor,
|
||||
width: size,
|
||||
height: size,
|
||||
})
|
||||
qrContainer.innerHTML = svg
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (codeType === "Barcode") {
|
||||
generateBarcode()
|
||||
} else {
|
||||
generateQr()
|
||||
}
|
||||
})
|
||||
|
||||
$: if (codeType === "Barcode" && value && barcodeElement && size) {
|
||||
generateBarcode()
|
||||
}
|
||||
|
||||
$: if (
|
||||
codeType === "QR Code" &&
|
||||
value &&
|
||||
qrContainer &&
|
||||
(showLogo !== undefined || customLogo !== undefined || size || primColor)
|
||||
) {
|
||||
generateQr()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="overall"
|
||||
use:styleable={$component.styles}
|
||||
styles="border: 3px solid red; width: 100px; height: 200px;"
|
||||
>
|
||||
{#if value}
|
||||
{#if codeType === "QR Code"}
|
||||
<div class="qr-container">
|
||||
<div bind:this={qrContainer} />
|
||||
<div class="qr-value" style="color: {primColor}; max-width: {size}px;">
|
||||
{showValue ? value : ""}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="barcode-container">
|
||||
<div class="logo-and-barcode" style="height: {size}px, width: 100%">
|
||||
{#if showLogo && customLogo}
|
||||
<img
|
||||
class="custom-logo"
|
||||
src={customLogo}
|
||||
alt="logo"
|
||||
style="height: {size}px"
|
||||
/>
|
||||
{/if}
|
||||
{#if value}
|
||||
<svg class="barcode" bind:this={barcodeElement} height={size} />
|
||||
{/if}
|
||||
</div>
|
||||
{#if showValue}
|
||||
<div class="barcode-value">
|
||||
<p>{value}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<p>
|
||||
Please add a value to generate your {codeType === "QR Code"
|
||||
? "QR Code"
|
||||
: "Barcode"}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.overall {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
.qr-value {
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
text-align: center;
|
||||
}
|
||||
.qr-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.barcode-container {
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
padding: 12px;
|
||||
background-color: white;
|
||||
}
|
||||
.logo-and-barcode {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.logo-and-barcode img {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.barcode-value p {
|
||||
margin: 0;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { getContext } from "svelte"
|
||||
import { getContext, onDestroy } from "svelte"
|
||||
import { Pagination, ProgressCircle } from "@budibase/bbui"
|
||||
import { fetchData, QueryUtils } from "@budibase/frontend-core"
|
||||
import type {
|
||||
|
@ -175,6 +175,10 @@
|
|||
interval = setInterval(fetch.refresh, Math.max(10000, autoRefresh * 1000))
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
clearInterval(interval) // Clears auto-refresh when navigating away
|
||||
})
|
||||
</script>
|
||||
|
||||
<div use:styleable={$component.styles} class="container">
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
export let showClear: boolean | undefined = false
|
||||
export let filterConfig: FilterConfig[] | undefined = []
|
||||
export let targetComponent: any
|
||||
export let size: string | undefined = "M"
|
||||
|
||||
const memoFilters = memo({} as Record<string, SearchFilter>)
|
||||
const component = getContext("component")
|
||||
|
@ -405,6 +406,7 @@
|
|||
{#each visibleFilters || [] as config}
|
||||
{@const filter = $memoFilters[config.field]}
|
||||
<FilterButton
|
||||
{size}
|
||||
{config}
|
||||
{filter}
|
||||
{schema}
|
||||
|
@ -424,7 +426,7 @@
|
|||
/>
|
||||
{/each}
|
||||
{#if showClear && Object.keys(filters).length}
|
||||
<Button size={"S"} secondary on:click={clearAll}>Clear all</Button>
|
||||
<Button {size} secondary on:click={clearAll}>Clear all</Button>
|
||||
{/if}
|
||||
</Container>
|
||||
</div>
|
||||
|
|
|
@ -170,7 +170,7 @@
|
|||
}}
|
||||
>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-Icon--sizeS"
|
||||
class="spectrum-Icon spectrum-Icon--size{size.toUpperCase()}"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
aria-label={icon}
|
||||
|
@ -200,7 +200,7 @@
|
|||
z-index: 1;
|
||||
}
|
||||
.toggle-wrap svg {
|
||||
width: 12px;
|
||||
scale: 90%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.filter-button-wrap.inactive .spectrum-Button .spectrum-Icon {
|
||||
|
@ -212,6 +212,17 @@
|
|||
display: flex;
|
||||
gap: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.spectrum-Button--sizeM,
|
||||
.spectrum-Button--sizeL {
|
||||
gap: var(--spacing-s);
|
||||
}
|
||||
|
||||
.spectrum-Button--sizeL {
|
||||
padding-left: calc(1em * 0.9);
|
||||
padding-right: calc(1em * 0.9);
|
||||
}
|
||||
|
||||
.spectrum-Button.is-disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ export { default as textv2 } from "./Text.svelte"
|
|||
export { default as filter } from "./filter/Filter.svelte"
|
||||
export { default as accordion } from "./Accordion.svelte"
|
||||
export { default as singlerowprovider } from "./SingleRowProvider.svelte"
|
||||
export { default as codegenerator } from "./CodeGenerator.svelte"
|
||||
export * from "./charts"
|
||||
export * from "./forms"
|
||||
export * from "./blocks"
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
mix-blend-mode: multiply;
|
||||
background: rgb(0 0 0);
|
||||
font-size: 30px;
|
||||
font-family: Source Sans Pro;
|
||||
font-family: Source Sans 3;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f2cbe5aff7645eb9b0b4e864924a1e1171ad85bf
|
||||
Subproject commit 271b8677c3ea814395cd67a10137900ec3a34dc7
|
|
@ -39,9 +39,10 @@
|
|||
<link rel="icon" type="image/png" href="/builder/bblogo.png" />
|
||||
{/if}
|
||||
|
||||
<link href="/builder/fonts/source-sans-pro/400.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/source-sans-pro/600.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/source-sans-pro/700.css" rel="stylesheet" />
|
||||
<link
|
||||
href="/builder/fonts/source-sans-3/source-sans-3.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link href="/builder/fonts/remixicon.css" rel="stylesheet" />
|
||||
|
||||
<style>
|
||||
|
@ -65,7 +66,7 @@
|
|||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: none;
|
||||
font-family: "Source Sans Pro", sans-serif;
|
||||
font-family: "Source Sans 3", sans-serif;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<title>Budibase Builder Preview</title>
|
||||
<link href="/builder/fonts/source-sans-pro/400.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/source-sans-pro/600.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/source-sans-pro/700.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/source-sans-3/source-sans-3.css" rel="stylesheet" />
|
||||
<link href="/builder/fonts/remixicon.css" rel="stylesheet" />
|
||||
<style>
|
||||
html, body {
|
||||
|
|
|
@ -41,7 +41,7 @@ export async function run({
|
|||
|
||||
try {
|
||||
let response
|
||||
const llm = await ai.getLLM(inputs.model)
|
||||
const llm = await ai.getLLM({ model: inputs.model })
|
||||
response = llm
|
||||
? (await llm.prompt(inputs.prompt)).message
|
||||
: await legacyOpenAIPrompt(inputs)
|
||||
|
|
|
@ -43,8 +43,10 @@ export const definition: AutomationStepDefinition = {
|
|||
title: "Email Subject",
|
||||
},
|
||||
contents: {
|
||||
type: AutomationIOType.STRING,
|
||||
title: "HTML Contents",
|
||||
type: AutomationIOType.LONGFORM,
|
||||
title: "Message",
|
||||
description:
|
||||
"Use markdown or HTML for rich text formatting: **bold**, _italics_, # Headings, * Bullets, <br> for line breaks, and more.",
|
||||
},
|
||||
addInvite: {
|
||||
type: AutomationIOType.BOOLEAN,
|
||||
|
|
|
@ -16,6 +16,7 @@ export enum AutomationIOType {
|
|||
DATE = "date",
|
||||
DATETIME = "datetime",
|
||||
ATTACHMENT = "attachment",
|
||||
LONGFORM = "longform",
|
||||
}
|
||||
|
||||
export enum AutomationCustomIOType {
|
||||
|
|
|
@ -95,6 +95,7 @@ export type AIColumnSchema =
|
|||
export interface LLMConfigOptions {
|
||||
model: string
|
||||
apiKey?: string
|
||||
maxTokens?: number
|
||||
}
|
||||
|
||||
export interface LLMProviderConfig extends LLMConfigOptions {
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"dotenv": "8.6.0",
|
||||
"email-validator": "^2.0.4",
|
||||
"global-agent": "3.0.0",
|
||||
"http-graceful-shutdown": "^3.1.12",
|
||||
"ical-generator": "4.1.0",
|
||||
"joi": "17.6.0",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
|
@ -68,6 +69,7 @@
|
|||
"koa-static": "5.0.0",
|
||||
"koa-useragent": "^4.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"marked": "^15.0.11",
|
||||
"node-fetch": "2.6.7",
|
||||
"nodemailer": "6.9.9",
|
||||
"passport-google-oauth": "2.0.0",
|
||||
|
@ -75,8 +77,7 @@
|
|||
"pouchdb": "7.3.0",
|
||||
"pouchdb-all-dbs": "1.1.1",
|
||||
"server-destroy": "1.0.1",
|
||||
"uuid": "^8.3.2",
|
||||
"http-graceful-shutdown": "^3.1.12"
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
|
@ -92,6 +93,7 @@
|
|||
"@types/server-destroy": "1.0.1",
|
||||
"@types/supertest": "2.0.14",
|
||||
"@types/uuid": "8.3.4",
|
||||
"cheerio": "^1.0.0",
|
||||
"jest": "29.7.0",
|
||||
"maildev": "^2.2.1",
|
||||
"nock": "^13.5.4",
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
stopMailserver,
|
||||
} from "../../../../tests/mocks/email"
|
||||
import { objectStore } from "@budibase/backend-core"
|
||||
import * as cheerio from "cheerio"
|
||||
|
||||
describe("/api/global/email", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
@ -266,4 +267,118 @@ describe("/api/global/email", () => {
|
|||
`DTEND:${formatDate(endTime)}`
|
||||
)
|
||||
})
|
||||
|
||||
it("Should parse valid markdown content from automation steps into valid HTML.", async () => {
|
||||
// Basic verification that the markdown is being processed.
|
||||
const email = await captureEmail(mailserver, async () => {
|
||||
const res = await config.api.emails.sendEmail({
|
||||
email: "to@example.com",
|
||||
subject: "Test",
|
||||
userId: config.user!._id,
|
||||
contents: `test@home.com [Call Me!](tel:1111111)`,
|
||||
purpose: EmailTemplatePurpose.CUSTOM,
|
||||
})
|
||||
expect(res.message).toBeDefined()
|
||||
})
|
||||
|
||||
const $ = cheerio.load(email.html)
|
||||
|
||||
// Verify the email body rendered
|
||||
const emailBody = $("td.email-body").first()
|
||||
expect(emailBody.length).toBe(1)
|
||||
|
||||
// Verify a valid link was generated and is queryable
|
||||
const emailLink = $("a[href^='mailto:']").first()
|
||||
expect(emailLink.length).toBe(1)
|
||||
expect(emailLink.text()).toBe("test@home.com")
|
||||
|
||||
// Verify the markdown link has been built correctly
|
||||
const phoneLink = $("a[href^='tel:']").first()
|
||||
expect(phoneLink.length).toBe(1)
|
||||
expect(phoneLink.text()).toBe("Call Me!")
|
||||
expect(phoneLink.attr("href")).toBe("tel:1111111")
|
||||
})
|
||||
|
||||
it("Should ignore invalid markdown content and return nothing", async () => {
|
||||
// The only failure case for a parse with marked is 'undefined'
|
||||
// It should be caught and resolve to nothing.
|
||||
const email = await captureEmail(mailserver, async () => {
|
||||
const res = await config.api.emails.sendEmail({
|
||||
email: "to@example.com",
|
||||
subject: "Test",
|
||||
userId: config.user!._id,
|
||||
contents: undefined,
|
||||
purpose: EmailTemplatePurpose.CUSTOM,
|
||||
})
|
||||
expect(res.message).toBeDefined()
|
||||
})
|
||||
|
||||
const $ = cheerio.load(email.html)
|
||||
const emailBody = $("td.email-body").first()
|
||||
expect(emailBody.length).toBe(1)
|
||||
|
||||
const bodyText = emailBody.text().trim()
|
||||
expect(bodyText).toBe("")
|
||||
})
|
||||
|
||||
it("Should render a mixture of content. Plain text, markdown and HTML", async () => {
|
||||
// A more involved check to ensure all content types are still respected
|
||||
const email = await captureEmail(mailserver, async () => {
|
||||
const res = await config.api.emails.sendEmail({
|
||||
email: "to@example.com",
|
||||
subject: "Test",
|
||||
userId: config.user!._id,
|
||||
contents: `<div class="html-content"><strong>Some content</strong></div>
|
||||
|
||||
# A heading
|
||||
- This should be list entry 1
|
||||
- This should be list entry 2
|
||||
|
||||
Some plain text`,
|
||||
purpose: EmailTemplatePurpose.CUSTOM,
|
||||
})
|
||||
expect(res.message).toBeDefined()
|
||||
})
|
||||
|
||||
const $ = cheerio.load(email.html)
|
||||
const emailBody = $("td.email-body").first()
|
||||
expect(emailBody.length).toBe(1)
|
||||
|
||||
const divEle = emailBody.find("div.html-content").first()
|
||||
expect(divEle.length).toBe(1)
|
||||
expect(divEle.text()).toBe("Some content")
|
||||
|
||||
const heading = emailBody.find("h1").first()
|
||||
expect(heading.length).toBe(1)
|
||||
expect(heading.text()).toBe("A heading")
|
||||
|
||||
// Both list items rendered
|
||||
const listEles = emailBody.find("ul li")
|
||||
expect(listEles.length).toBe(2)
|
||||
|
||||
const plainText = emailBody.find("p")
|
||||
expect(plainText.length).toBe(1)
|
||||
expect(plainText.text()).toBe("Some plain text")
|
||||
})
|
||||
|
||||
it("Should only parse markdown content for the CUSTOM email template used in automation steps", async () => {
|
||||
const email = await captureEmail(mailserver, async () => {
|
||||
const res = await config.api.emails.sendEmail({
|
||||
email: "to@example.com",
|
||||
subject: "Test",
|
||||
userId: config.user!._id,
|
||||
purpose: EmailTemplatePurpose.INVITATION,
|
||||
})
|
||||
expect(res.message).toBeDefined()
|
||||
})
|
||||
|
||||
const $ = cheerio.load(email.html)
|
||||
const emailBody = $("td.email-body").first()
|
||||
expect(emailBody.length).toBe(1)
|
||||
|
||||
const heading = emailBody.find("h1").first()
|
||||
expect(heading.length).toBe(1)
|
||||
// The email should not be parsed as markdown.
|
||||
expect(heading.text()).toBe("Hi, to@example.com!")
|
||||
})
|
||||
})
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
body,
|
||||
td,
|
||||
th {
|
||||
font-family: "Source Sans Pro", Helvetica, Arial, sans-serif;
|
||||
font-family: "Source Sans 3", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
import { configs, cache, objectStore, HTTPError } from "@budibase/backend-core"
|
||||
import ical from "ical-generator"
|
||||
import _ from "lodash"
|
||||
import { marked } from "marked"
|
||||
|
||||
import nodemailer from "nodemailer"
|
||||
import SMTPTransport from "nodemailer/lib/smtp-transport"
|
||||
|
@ -97,7 +98,10 @@ async function buildEmail(
|
|||
}
|
||||
context = {
|
||||
...context,
|
||||
contents,
|
||||
contents:
|
||||
purpose === EmailTemplatePurpose.CUSTOM
|
||||
? marked.parse(contents || "")
|
||||
: contents,
|
||||
email,
|
||||
name,
|
||||
user: user || {},
|
||||
|
|
145
yarn.lock
145
yarn.lock
|
@ -3279,11 +3279,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
|
||||
integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
|
||||
|
||||
"@fontsource/source-sans-pro@^5.0.3":
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/source-sans-pro/-/source-sans-pro-5.0.3.tgz#7d6e84a8169ba12fa5e6ce70757aa2ca7e74d855"
|
||||
integrity sha512-mQnjuif/37VxwRloHZ+wQdoozd2VPWutbFSt1AuSkk7nFXIBQxHJLw80rgCF/osL0t7N/3Gx1V7UJuOX2zxzhQ==
|
||||
|
||||
"@fortawesome/fontawesome-common-types@6.4.2":
|
||||
version "6.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz#1766039cad33f8ad87f9467b98e0d18fbc8f01c5"
|
||||
|
@ -6102,6 +6097,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@spectrum-css/vars/-/vars-4.3.1.tgz#d333fa41909f691c8750b5c15ad9ba029df2248e"
|
||||
integrity sha512-rX6Iasu9BsFMVgEN0vGRPm9dmSxva+IK/uqQAa9HM0lliwqUiFrJxrFXHHpiAgNuux/U4srEJwbSpGzfF+CegQ==
|
||||
|
||||
"@svelte-put/qr@^1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@svelte-put/qr/-/qr-1.2.1.tgz#d785848c7b38ceb076df743b97cfed641dab86da"
|
||||
integrity sha512-VIAze6mCVWdiyq6xnMFhbD3dbTvz9CDFVN3lVkDvx6SYFcstgHuIyb7Pi3aJIr2ClDc4U+W2U+SGhFuQBskvBg==
|
||||
dependencies:
|
||||
qrcode-generator "^1.4.4"
|
||||
|
||||
"@svelte-put/shortcut@^3.1.0":
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@svelte-put/shortcut/-/shortcut-3.1.1.tgz#aba4d7407024d5cff38727e12925c8f81e877079"
|
||||
|
@ -8539,6 +8541,11 @@ body-parser@1.20.3:
|
|||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
|
||||
boolbase@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
|
||||
|
||||
boolean@^3.0.1:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b"
|
||||
|
@ -8999,6 +9006,35 @@ check-error@^1.0.3:
|
|||
dependencies:
|
||||
get-func-name "^2.0.2"
|
||||
|
||||
cheerio-select@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4"
|
||||
integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
css-select "^5.1.0"
|
||||
css-what "^6.1.0"
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.3"
|
||||
domutils "^3.0.1"
|
||||
|
||||
cheerio@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81"
|
||||
integrity sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==
|
||||
dependencies:
|
||||
cheerio-select "^2.1.0"
|
||||
dom-serializer "^2.0.0"
|
||||
domhandler "^5.0.3"
|
||||
domutils "^3.1.0"
|
||||
encoding-sniffer "^0.2.0"
|
||||
htmlparser2 "^9.1.0"
|
||||
parse5 "^7.1.2"
|
||||
parse5-htmlparser2-tree-adapter "^7.0.0"
|
||||
parse5-parser-stream "^7.1.2"
|
||||
undici "^6.19.5"
|
||||
whatwg-mimetype "^4.0.0"
|
||||
|
||||
chokidar@3.5.3, chokidar@^3.5.2, chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
|
@ -9766,6 +9802,17 @@ css-line-break@^2.1.0:
|
|||
dependencies:
|
||||
utrie "^1.0.2"
|
||||
|
||||
css-select@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
|
||||
integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
css-what "^6.1.0"
|
||||
domhandler "^5.0.2"
|
||||
domutils "^3.0.1"
|
||||
nth-check "^2.0.1"
|
||||
|
||||
css-tree@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
|
||||
|
@ -9774,6 +9821,11 @@ css-tree@^2.3.1:
|
|||
mdn-data "2.0.30"
|
||||
source-map-js "^1.0.1"
|
||||
|
||||
css-what@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
|
||||
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
|
||||
|
||||
css.escape@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
|
||||
|
@ -10669,7 +10721,7 @@ domexception@^4.0.0:
|
|||
dependencies:
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
domhandler@^5.0.1, domhandler@^5.0.2:
|
||||
domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
|
||||
integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
|
||||
|
@ -10692,6 +10744,15 @@ domutils@^3.0.1:
|
|||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.1"
|
||||
|
||||
domutils@^3.1.0:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78"
|
||||
integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==
|
||||
dependencies:
|
||||
dom-serializer "^2.0.0"
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.3"
|
||||
|
||||
dot-prop@^5.1.0, dot-prop@^5.2.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
|
||||
|
@ -10884,6 +10945,14 @@ encoding-down@^6.3.0:
|
|||
level-codec "^9.0.0"
|
||||
level-errors "^2.0.0"
|
||||
|
||||
encoding-sniffer@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5"
|
||||
integrity sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==
|
||||
dependencies:
|
||||
iconv-lite "^0.6.3"
|
||||
whatwg-encoding "^3.1.1"
|
||||
|
||||
encoding@^0.1.13:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
|
||||
|
@ -10957,6 +11026,11 @@ entities@^4.2.0, entities@^4.3.0, entities@^4.4.0, entities@^4.5.0:
|
|||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
entities@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.0.tgz#09c9e29cb79b0a6459a9b9db9efb418ac5bb8e51"
|
||||
integrity sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==
|
||||
|
||||
env-paths@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||
|
@ -13116,6 +13190,16 @@ htmlparser2@^8.0.0:
|
|||
domutils "^3.0.1"
|
||||
entities "^4.3.0"
|
||||
|
||||
htmlparser2@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23"
|
||||
integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==
|
||||
dependencies:
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.3"
|
||||
domutils "^3.1.0"
|
||||
entities "^4.5.0"
|
||||
|
||||
http-assert@^1.3.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f"
|
||||
|
@ -14650,6 +14734,11 @@ js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.14.1:
|
|||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsbarcode@^3.11.6:
|
||||
version "3.11.6"
|
||||
resolved "https://registry.yarnpkg.com/jsbarcode/-/jsbarcode-3.11.6.tgz#96e8fbc3395476e162982a6064b98a09b5ea02c0"
|
||||
integrity sha512-G5TKGyKY1zJo0ZQKFM1IIMfy0nF2rs92BLlCz+cU4/TazIc4ZH+X1GYeDRt7TKjrYqmPfTjwTBkU/QnQlsYiuA==
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
|
@ -15971,6 +16060,11 @@ map-obj@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a"
|
||||
integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==
|
||||
|
||||
marked@^15.0.11:
|
||||
version "15.0.11"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-15.0.11.tgz#08a8d12c285e16259e44287b89ce0d871c9d55e8"
|
||||
integrity sha512-1BEXAU2euRCG3xwgLVT1y0xbJEld1XOrmRJpUwRCcy7rxhSCwMrmEu9LXoPhHSCJG41V7YcQ2mjKRr5BA3ITIA==
|
||||
|
||||
marked@^15.0.8:
|
||||
version "15.0.8"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-15.0.8.tgz#39873a3fdf91a520111e48aeb2ef3746d58d7166"
|
||||
|
@ -16942,6 +17036,13 @@ npmlog@^6.0.0, npmlog@^6.0.2:
|
|||
gauge "^4.0.3"
|
||||
set-blocking "^2.0.0"
|
||||
|
||||
nth-check@^2.0.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
|
||||
integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
|
||||
dependencies:
|
||||
boolbase "^1.0.0"
|
||||
|
||||
nunjucks@^3.2.3:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-3.2.4.tgz#f0878eef528ce7b0aa35d67cc6898635fd74649e"
|
||||
|
@ -17607,6 +17708,21 @@ parse-url@^8.1.0:
|
|||
dependencies:
|
||||
parse-path "^7.0.0"
|
||||
|
||||
parse5-htmlparser2-tree-adapter@^7.0.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz#b5a806548ed893a43e24ccb42fbb78069311e81b"
|
||||
integrity sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==
|
||||
dependencies:
|
||||
domhandler "^5.0.3"
|
||||
parse5 "^7.0.0"
|
||||
|
||||
parse5-parser-stream@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz#d7c20eadc37968d272e2c02660fff92dd27e60e1"
|
||||
integrity sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==
|
||||
dependencies:
|
||||
parse5 "^7.0.0"
|
||||
|
||||
parse5@6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||
|
@ -17617,6 +17733,13 @@ parse5@^1.5.1:
|
|||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
|
||||
integrity sha512-w2jx/0tJzvgKwZa58sj2vAYq/S/K1QJfIB3cWYea/Iu1scFPDQQ3IQiVZTHWtRBwAjv2Yd7S/xeZf3XqLDb3bA==
|
||||
|
||||
parse5@^7.0.0:
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05"
|
||||
integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==
|
||||
dependencies:
|
||||
entities "^6.0.0"
|
||||
|
||||
parse5@^7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32"
|
||||
|
@ -18739,6 +18862,11 @@ q@^1.1.2:
|
|||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||
integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
|
||||
|
||||
qrcode-generator@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/qrcode-generator/-/qrcode-generator-1.4.4.tgz#63f771224854759329a99048806a53ed278740e7"
|
||||
integrity sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==
|
||||
|
||||
qs@6.13.0, qs@^6.10.3, qs@^6.11.0, qs@^6.4.0:
|
||||
version "6.13.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
|
||||
|
@ -21600,6 +21728,11 @@ undici@^5.28.4:
|
|||
dependencies:
|
||||
"@fastify/busboy" "^2.0.0"
|
||||
|
||||
undici@^6.19.5:
|
||||
version "6.21.2"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-6.21.2.tgz#49c5884e8f9039c65a89ee9018ef3c8e2f1f4928"
|
||||
integrity sha512-uROZWze0R0itiAKVPsYhFov9LxrPMHLMEQFszeI2gCN6bnIIZ8twzBCJcN2LJrBBLfrP0t1FW0g+JmKVl8Vk1g==
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
|
||||
|
|
Loading…
Reference in New Issue