Merge branch 'master' into BUDI-9038/validate-hbs
This commit is contained in:
commit
db0c344965
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||||
"version": "3.4.6",
|
"version": "3.4.9",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"concurrency": 20,
|
"concurrency": 20,
|
||||||
"command": {
|
"command": {
|
||||||
|
|
|
@ -247,3 +247,7 @@ export function hasCircularStructure(json: any) {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function urlHasProtocol(url: string): boolean {
|
||||||
|
return !!url.match(/^.+:\/\/.+$/)
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import "@spectrum-css/typography/dist/index-vars.css"
|
import "@spectrum-css/typography/dist/index-vars.css"
|
||||||
|
|
||||||
export let size = "M"
|
export let size: "XS" | "S" | "M" | "L" | "XL" = "M"
|
||||||
export let serif = false
|
export let serif: boolean = false
|
||||||
export let weight = null
|
export let weight: string | null = null
|
||||||
export let textAlign = null
|
export let textAlign: string | null = null
|
||||||
export let color = null
|
export let color: string | null = null
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import { Input, Label } from "@budibase/bbui"
|
||||||
|
import { previewStore, selectedScreen } from "@/stores/builder"
|
||||||
|
import type { ComponentContext } from "@budibase/types"
|
||||||
|
|
||||||
|
export let baseRoute = ""
|
||||||
|
|
||||||
|
let testValue: string | undefined
|
||||||
|
|
||||||
|
$: routeParams = baseRoute.match(/:[a-zA-Z]+/g) || []
|
||||||
|
$: hasUrlParams = routeParams.length > 0
|
||||||
|
$: placeholder = getPlaceholder(baseRoute)
|
||||||
|
$: baseInput = createBaseInput(baseRoute)
|
||||||
|
$: updateTestValueFromContext($previewStore.selectedComponentContext)
|
||||||
|
$: if ($selectedScreen) {
|
||||||
|
testValue = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPlaceholder = (route: string) => {
|
||||||
|
const trimmed = route.replace(/\/$/, "")
|
||||||
|
if (trimmed.startsWith("/:")) {
|
||||||
|
return "1"
|
||||||
|
}
|
||||||
|
const segments = trimmed.split("/").slice(2)
|
||||||
|
let count = 1
|
||||||
|
return segments
|
||||||
|
.map(segment => (segment.startsWith(":") ? count++ : segment))
|
||||||
|
.join("/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is needed to repopulate the test value from componentContext
|
||||||
|
// when a user navigates to another component and then back again
|
||||||
|
const updateTestValueFromContext = (context: ComponentContext | null) => {
|
||||||
|
if (context?.url && !testValue) {
|
||||||
|
const { wild, ...urlParams } = context.url
|
||||||
|
const queryParams = context.query
|
||||||
|
if (Object.values(urlParams).some(v => Boolean(v))) {
|
||||||
|
let value = baseRoute
|
||||||
|
.split("/")
|
||||||
|
.slice(2)
|
||||||
|
.map(segment =>
|
||||||
|
segment.startsWith(":")
|
||||||
|
? urlParams[segment.slice(1)] || ""
|
||||||
|
: segment
|
||||||
|
)
|
||||||
|
.join("/")
|
||||||
|
const qs = new URLSearchParams(queryParams).toString()
|
||||||
|
if (qs) {
|
||||||
|
value += `?${qs}`
|
||||||
|
}
|
||||||
|
testValue = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createBaseInput = (baseRoute: string) => {
|
||||||
|
return baseRoute === "/" || baseRoute.split("/")[1]?.startsWith(":")
|
||||||
|
? "/"
|
||||||
|
: `/${baseRoute.split("/")[1]}/`
|
||||||
|
}
|
||||||
|
|
||||||
|
const onVariableChange = (e: CustomEvent) => {
|
||||||
|
previewStore.setUrlTestData({ route: baseRoute, testValue: e.detail })
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
previewStore.requestComponentContext()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if hasUrlParams}
|
||||||
|
<div class="url-test-section">
|
||||||
|
<div class="info">
|
||||||
|
<Label size="M">Set temporary URL variables for design preview</Label>
|
||||||
|
</div>
|
||||||
|
<div class="url-test-container">
|
||||||
|
<div class="base-input">
|
||||||
|
<Input disabled={true} value={baseInput} />
|
||||||
|
</div>
|
||||||
|
<div class="variable-input">
|
||||||
|
<Input value={testValue} on:change={onVariableChange} {placeholder} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.url-test-section {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
margin-bottom: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-test-container {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-input {
|
||||||
|
width: 98px;
|
||||||
|
margin-right: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.base-input :global(.spectrum-Textfield-input) {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
background-color: var(--spectrum-global-color-gray-200);
|
||||||
|
color: var(--spectrum-global-color-gray-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-input :global(.spectrum-Textfield-input) {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -15,6 +15,7 @@
|
||||||
import ButtonActionEditor from "@/components/design/settings/controls/ButtonActionEditor/ButtonActionEditor.svelte"
|
import ButtonActionEditor from "@/components/design/settings/controls/ButtonActionEditor/ButtonActionEditor.svelte"
|
||||||
import { getBindableProperties } from "@/dataBinding"
|
import { getBindableProperties } from "@/dataBinding"
|
||||||
import BarButtonList from "@/components/design/settings/controls/BarButtonList.svelte"
|
import BarButtonList from "@/components/design/settings/controls/BarButtonList.svelte"
|
||||||
|
import URLVariableTestInput from "@/components/design/settings/controls/URLVariableTestInput.svelte"
|
||||||
|
|
||||||
$: bindings = getBindableProperties($selectedScreen, null)
|
$: bindings = getBindableProperties($selectedScreen, null)
|
||||||
$: screenSettings = getScreenSettings($selectedScreen)
|
$: screenSettings = getScreenSettings($selectedScreen)
|
||||||
|
@ -93,6 +94,13 @@
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "urlTest",
|
||||||
|
control: URLVariableTestInput,
|
||||||
|
props: {
|
||||||
|
baseRoute: screen.routing?.route,
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import { writable, get } from "svelte/store"
|
import { writable, get } from "svelte/store"
|
||||||
import { findComponentParent, findComponentPath } from "@/helpers/components"
|
import { findComponentParent, findComponentPath } from "@/helpers/components"
|
||||||
import { selectedScreen, componentStore } from "@/stores/builder"
|
import { selectedScreen, componentStore } from "@/stores/builder"
|
||||||
|
import { DropPosition } from "@budibase/types"
|
||||||
export const DropPosition = {
|
export { DropPosition } from "@budibase/types"
|
||||||
ABOVE: "above",
|
|
||||||
BELOW: "below",
|
|
||||||
INSIDE: "inside",
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
source: null,
|
source: null,
|
||||||
|
|
|
@ -311,8 +311,6 @@ export class ComponentStore extends BudiStore<ComponentState> {
|
||||||
component[setting.key] = fieldOptions[0]
|
component[setting.key] = fieldOptions[0]
|
||||||
component.label = fieldOptions[0]
|
component.label = fieldOptions[0]
|
||||||
}
|
}
|
||||||
} else if (setting.type === "icon") {
|
|
||||||
component[setting.key] = "ri-star-fill"
|
|
||||||
} else if (useDefaultValues && setting.defaultValue !== undefined) {
|
} else if (useDefaultValues && setting.defaultValue !== undefined) {
|
||||||
// Use default value where required
|
// Use default value where required
|
||||||
component[setting.key] = setting.defaultValue
|
component[setting.key] = setting.defaultValue
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import { layoutStore } from "./layouts.js"
|
import { layoutStore } from "./layouts"
|
||||||
import { appStore } from "./app.js"
|
import { appStore } from "./app"
|
||||||
import { componentStore, selectedComponent } from "./components"
|
import { componentStore, selectedComponent } from "./components"
|
||||||
import { navigationStore } from "./navigation.js"
|
import { navigationStore } from "./navigation"
|
||||||
import { themeStore } from "./theme.js"
|
import { themeStore } from "./theme"
|
||||||
import { screenStore, selectedScreen, sortedScreens } from "./screens"
|
import { screenStore, selectedScreen, sortedScreens } from "./screens"
|
||||||
import { builderStore } from "./builder.js"
|
import { builderStore } from "./builder"
|
||||||
import { hoverStore } from "./hover.js"
|
import { hoverStore } from "./hover"
|
||||||
import { previewStore } from "./preview.js"
|
import { previewStore } from "./preview"
|
||||||
import {
|
import {
|
||||||
automationStore,
|
automationStore,
|
||||||
selectedAutomation,
|
selectedAutomation,
|
||||||
automationHistoryStore,
|
automationHistoryStore,
|
||||||
} from "./automations.js"
|
} from "./automations"
|
||||||
import { userStore, userSelectedResourceMap, isOnlyUser } from "./users.js"
|
import { userStore, userSelectedResourceMap, isOnlyUser } from "./users"
|
||||||
import { deploymentStore } from "./deployments.js"
|
import { deploymentStore } from "./deployments"
|
||||||
import { contextMenuStore } from "./contextMenu.js"
|
import { contextMenuStore } from "./contextMenu"
|
||||||
import { snippets } from "./snippets"
|
import { snippets } from "./snippets"
|
||||||
import {
|
import {
|
||||||
screenComponentsList,
|
screenComponentsList,
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { BudiStore } from "../BudiStore"
|
import { BudiStore } from "../BudiStore"
|
||||||
|
import { PreviewDevice, ComponentContext } from "@budibase/types"
|
||||||
|
|
||||||
type PreviewDevice = "desktop" | "tablet" | "mobile"
|
|
||||||
type PreviewEventHandler = (name: string, payload?: any) => void
|
type PreviewEventHandler = (name: string, payload?: any) => void
|
||||||
type ComponentContext = Record<string, any>
|
|
||||||
|
|
||||||
interface PreviewState {
|
interface PreviewState {
|
||||||
previewDevice: PreviewDevice
|
previewDevice: PreviewDevice
|
||||||
|
@ -86,6 +85,10 @@ export class PreviewStore extends BudiStore<PreviewState> {
|
||||||
this.sendEvent("builder-state", data)
|
this.sendEvent("builder-state", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setUrlTestData(data: Record<string, any>) {
|
||||||
|
this.sendEvent("builder-url-test-data", data)
|
||||||
|
}
|
||||||
|
|
||||||
requestComponentContext() {
|
requestComponentContext() {
|
||||||
this.sendEvent("request-context")
|
this.sendEvent("request-context")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1455,7 +1455,8 @@
|
||||||
"type": "icon",
|
"type": "icon",
|
||||||
"label": "Icon",
|
"label": "Icon",
|
||||||
"key": "icon",
|
"key": "icon",
|
||||||
"required": true
|
"required": true,
|
||||||
|
"defaultValue": "ri-star-fill"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "select",
|
"type": "select",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onDestroy, onMount, setContext } from "svelte"
|
import { getContext, onDestroy, onMount, setContext } from "svelte"
|
||||||
import { builderStore } from "@/stores/builder.js"
|
import { builderStore } from "@/stores/builder"
|
||||||
import { blockStore } from "@/stores/blocks"
|
import { blockStore } from "@/stores/blocks"
|
||||||
|
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onDestroy } from "svelte"
|
import { getContext, onDestroy } from "svelte"
|
||||||
import { generate } from "shortid"
|
import { generate } from "shortid"
|
||||||
import { builderStore } from "../stores/builder.js"
|
import { builderStore } from "../stores/builder"
|
||||||
import Component from "@/components/Component.svelte"
|
import Component from "@/components/Component.svelte"
|
||||||
|
|
||||||
export let type
|
export let type
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
import UserBindingsProvider from "./context/UserBindingsProvider.svelte"
|
import UserBindingsProvider from "./context/UserBindingsProvider.svelte"
|
||||||
import DeviceBindingsProvider from "./context/DeviceBindingsProvider.svelte"
|
import DeviceBindingsProvider from "./context/DeviceBindingsProvider.svelte"
|
||||||
import StateBindingsProvider from "./context/StateBindingsProvider.svelte"
|
import StateBindingsProvider from "./context/StateBindingsProvider.svelte"
|
||||||
|
import TestUrlBindingsProvider from "./context/TestUrlBindingsProvider.svelte"
|
||||||
import RowSelectionProvider from "./context/RowSelectionProvider.svelte"
|
import RowSelectionProvider from "./context/RowSelectionProvider.svelte"
|
||||||
import QueryParamsProvider from "./context/QueryParamsProvider.svelte"
|
import QueryParamsProvider from "./context/QueryParamsProvider.svelte"
|
||||||
import SettingsBar from "./preview/SettingsBar.svelte"
|
import SettingsBar from "./preview/SettingsBar.svelte"
|
||||||
|
@ -169,108 +170,110 @@
|
||||||
<StateBindingsProvider>
|
<StateBindingsProvider>
|
||||||
<RowSelectionProvider>
|
<RowSelectionProvider>
|
||||||
<QueryParamsProvider>
|
<QueryParamsProvider>
|
||||||
<SnippetsProvider>
|
<TestUrlBindingsProvider>
|
||||||
<!-- Settings bar can be rendered outside of device preview -->
|
<SnippetsProvider>
|
||||||
<!-- Key block needs to be outside the if statement or it breaks -->
|
<!-- Settings bar can be rendered outside of device preview -->
|
||||||
{#key $builderStore.selectedComponentId}
|
<!-- Key block needs to be outside the if statement or it breaks -->
|
||||||
{#if $builderStore.inBuilder}
|
{#key $builderStore.selectedComponentId}
|
||||||
<SettingsBar />
|
{#if $builderStore.inBuilder}
|
||||||
{/if}
|
<SettingsBar />
|
||||||
{/key}
|
|
||||||
|
|
||||||
<!-- Clip boundary for selection indicators -->
|
|
||||||
<div
|
|
||||||
id="clip-root"
|
|
||||||
class:preview={$builderStore.inBuilder}
|
|
||||||
class:tablet-preview={$builderStore.previewDevice ===
|
|
||||||
"tablet"}
|
|
||||||
class:mobile-preview={$builderStore.previewDevice ===
|
|
||||||
"mobile"}
|
|
||||||
>
|
|
||||||
<!-- Actual app -->
|
|
||||||
<div id="app-root">
|
|
||||||
{#if showDevTools}
|
|
||||||
<DevToolsHeader />
|
|
||||||
{/if}
|
{/if}
|
||||||
|
{/key}
|
||||||
|
|
||||||
<div id="app-body">
|
<!-- Clip boundary for selection indicators -->
|
||||||
{#if permissionError}
|
<div
|
||||||
<div class="error">
|
id="clip-root"
|
||||||
<Layout justifyItems="center" gap="S">
|
class:preview={$builderStore.inBuilder}
|
||||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
class:tablet-preview={$builderStore.previewDevice ===
|
||||||
{@html ErrorSVG}
|
"tablet"}
|
||||||
<Heading size="L">
|
class:mobile-preview={$builderStore.previewDevice ===
|
||||||
You don't have permission to use this app
|
"mobile"}
|
||||||
</Heading>
|
>
|
||||||
<Body size="S">
|
<!-- Actual app -->
|
||||||
Ask your administrator to grant you access
|
<div id="app-root">
|
||||||
</Body>
|
{#if showDevTools}
|
||||||
</Layout>
|
<DevToolsHeader />
|
||||||
</div>
|
|
||||||
{:else if !$screenStore.activeLayout}
|
|
||||||
<div class="error">
|
|
||||||
<Layout justifyItems="center" gap="S">
|
|
||||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
||||||
{@html ErrorSVG}
|
|
||||||
<Heading size="L">
|
|
||||||
Something went wrong rendering your app
|
|
||||||
</Heading>
|
|
||||||
<Body size="S">
|
|
||||||
Get in touch with support if this issue
|
|
||||||
persists
|
|
||||||
</Body>
|
|
||||||
</Layout>
|
|
||||||
</div>
|
|
||||||
{:else if embedNoScreens}
|
|
||||||
<div class="error">
|
|
||||||
<Layout justifyItems="center" gap="S">
|
|
||||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
||||||
{@html ErrorSVG}
|
|
||||||
<Heading size="L">
|
|
||||||
This Budibase app is not publicly accessible
|
|
||||||
</Heading>
|
|
||||||
</Layout>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<CustomThemeWrapper>
|
|
||||||
{#key $screenStore.activeLayout._id}
|
|
||||||
<Component
|
|
||||||
isLayout
|
|
||||||
instance={$screenStore.activeLayout.props}
|
|
||||||
/>
|
|
||||||
{/key}
|
|
||||||
|
|
||||||
<!-- Layers on top of app -->
|
|
||||||
<NotificationDisplay />
|
|
||||||
<ConfirmationDisplay />
|
|
||||||
<PeekScreenDisplay />
|
|
||||||
</CustomThemeWrapper>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if showDevTools}
|
<div id="app-body">
|
||||||
<DevTools />
|
{#if permissionError}
|
||||||
|
<div class="error">
|
||||||
|
<Layout justifyItems="center" gap="S">
|
||||||
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||||
|
{@html ErrorSVG}
|
||||||
|
<Heading size="L">
|
||||||
|
You don't have permission to use this app
|
||||||
|
</Heading>
|
||||||
|
<Body size="S">
|
||||||
|
Ask your administrator to grant you access
|
||||||
|
</Body>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
{:else if !$screenStore.activeLayout}
|
||||||
|
<div class="error">
|
||||||
|
<Layout justifyItems="center" gap="S">
|
||||||
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||||
|
{@html ErrorSVG}
|
||||||
|
<Heading size="L">
|
||||||
|
Something went wrong rendering your app
|
||||||
|
</Heading>
|
||||||
|
<Body size="S">
|
||||||
|
Get in touch with support if this issue
|
||||||
|
persists
|
||||||
|
</Body>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
{:else if embedNoScreens}
|
||||||
|
<div class="error">
|
||||||
|
<Layout justifyItems="center" gap="S">
|
||||||
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||||
|
{@html ErrorSVG}
|
||||||
|
<Heading size="L">
|
||||||
|
This Budibase app is not publicly accessible
|
||||||
|
</Heading>
|
||||||
|
</Layout>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<CustomThemeWrapper>
|
||||||
|
{#key $screenStore.activeLayout._id}
|
||||||
|
<Component
|
||||||
|
isLayout
|
||||||
|
instance={$screenStore.activeLayout.props}
|
||||||
|
/>
|
||||||
|
{/key}
|
||||||
|
|
||||||
|
<!-- Layers on top of app -->
|
||||||
|
<NotificationDisplay />
|
||||||
|
<ConfirmationDisplay />
|
||||||
|
<PeekScreenDisplay />
|
||||||
|
</CustomThemeWrapper>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if showDevTools}
|
||||||
|
<DevTools />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if !$builderStore.inBuilder && $featuresStore.logoEnabled}
|
||||||
|
<FreeFooter />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if !$builderStore.inBuilder && $featuresStore.logoEnabled}
|
<!-- Preview and dev tools utilities -->
|
||||||
<FreeFooter />
|
{#if $appStore.isDevApp}
|
||||||
|
<SelectionIndicator />
|
||||||
|
{/if}
|
||||||
|
{#if $builderStore.inBuilder || $devToolsStore.allowSelection}
|
||||||
|
<HoverIndicator />
|
||||||
|
{/if}
|
||||||
|
{#if $builderStore.inBuilder}
|
||||||
|
<DNDHandler />
|
||||||
|
<GridDNDHandler />
|
||||||
|
<DNDSelectionIndicators />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
</SnippetsProvider>
|
||||||
<!-- Preview and dev tools utilities -->
|
</TestUrlBindingsProvider>
|
||||||
{#if $appStore.isDevApp}
|
|
||||||
<SelectionIndicator />
|
|
||||||
{/if}
|
|
||||||
{#if $builderStore.inBuilder || $devToolsStore.allowSelection}
|
|
||||||
<HoverIndicator />
|
|
||||||
{/if}
|
|
||||||
{#if $builderStore.inBuilder}
|
|
||||||
<DNDHandler />
|
|
||||||
<GridDNDHandler />
|
|
||||||
<DNDSelectionIndicators />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</SnippetsProvider>
|
|
||||||
</QueryParamsProvider>
|
</QueryParamsProvider>
|
||||||
</RowSelectionProvider>
|
</RowSelectionProvider>
|
||||||
</StateBindingsProvider>
|
</StateBindingsProvider>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import Field from "./Field.svelte"
|
import Field from "./Field.svelte"
|
||||||
import { CoreDropzone, ProgressCircle, Helpers } from "@budibase/bbui"
|
import { CoreDropzone, ProgressCircle, Helpers } from "@budibase/bbui"
|
||||||
import { getContext, onMount, onDestroy } from "svelte"
|
import { getContext, onMount, onDestroy } from "svelte"
|
||||||
import { builderStore } from "@/stores/builder.js"
|
import { builderStore } from "@/stores/builder"
|
||||||
import { processStringSync } from "@budibase/string-templates"
|
import { processStringSync } from "@budibase/string-templates"
|
||||||
|
|
||||||
export let datasourceId
|
export let datasourceId
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import Provider from "./Provider.svelte"
|
||||||
|
import { routeStore } from "@/stores"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Provider key="url" data={$routeStore.testUrlParams}>
|
||||||
|
<slot />
|
||||||
|
</Provider>
|
|
@ -0,0 +1,142 @@
|
||||||
|
import ClientApp from "./components/ClientApp.svelte"
|
||||||
|
import UpdatingApp from "./components/UpdatingApp.svelte"
|
||||||
|
import {
|
||||||
|
builderStore,
|
||||||
|
appStore,
|
||||||
|
blockStore,
|
||||||
|
componentStore,
|
||||||
|
environmentStore,
|
||||||
|
dndStore,
|
||||||
|
eventStore,
|
||||||
|
hoverStore,
|
||||||
|
stateStore,
|
||||||
|
routeStore,
|
||||||
|
} from "./stores"
|
||||||
|
import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-vite.js"
|
||||||
|
import { get } from "svelte/store"
|
||||||
|
import { initWebsocket } from "./websocket.js"
|
||||||
|
|
||||||
|
// Provide svelte and svelte/internal as globals for custom components
|
||||||
|
import * as svelte from "svelte"
|
||||||
|
import * as internal from "svelte/internal"
|
||||||
|
|
||||||
|
window.svelte_internal = internal
|
||||||
|
window.svelte = svelte
|
||||||
|
|
||||||
|
// Initialise spectrum icons
|
||||||
|
loadSpectrumIcons()
|
||||||
|
|
||||||
|
let app
|
||||||
|
|
||||||
|
const loadBudibase = async () => {
|
||||||
|
// Update builder store with any builder flags
|
||||||
|
builderStore.set({
|
||||||
|
...get(builderStore),
|
||||||
|
inBuilder: !!window["##BUDIBASE_IN_BUILDER##"],
|
||||||
|
layout: window["##BUDIBASE_PREVIEW_LAYOUT##"],
|
||||||
|
screen: window["##BUDIBASE_PREVIEW_SCREEN##"],
|
||||||
|
selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"],
|
||||||
|
previewId: window["##BUDIBASE_PREVIEW_ID##"],
|
||||||
|
theme: window["##BUDIBASE_PREVIEW_THEME##"],
|
||||||
|
customTheme: window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"],
|
||||||
|
previewDevice: window["##BUDIBASE_PREVIEW_DEVICE##"],
|
||||||
|
navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"],
|
||||||
|
hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"],
|
||||||
|
usedPlugins: window["##BUDIBASE_USED_PLUGINS##"],
|
||||||
|
location: window["##BUDIBASE_LOCATION##"],
|
||||||
|
snippets: window["##BUDIBASE_SNIPPETS##"],
|
||||||
|
componentErrors: window["##BUDIBASE_COMPONENT_ERRORS##"],
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set app ID - this window flag is set by both the preview and the real
|
||||||
|
// server rendered app HTML
|
||||||
|
appStore.actions.setAppId(window["##BUDIBASE_APP_ID##"])
|
||||||
|
|
||||||
|
// Set the flag used to determine if the app is being loaded via an iframe
|
||||||
|
appStore.actions.setAppEmbedded(
|
||||||
|
window["##BUDIBASE_APP_EMBEDDED##"] === "true"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (window.MIGRATING_APP) {
|
||||||
|
new UpdatingApp({
|
||||||
|
target: window.document.body,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch environment info
|
||||||
|
if (!get(environmentStore)?.loaded) {
|
||||||
|
await environmentStore.actions.fetchEnvironment()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register handler for runtime events from the builder
|
||||||
|
window.handleBuilderRuntimeEvent = (type, data) => {
|
||||||
|
if (!window["##BUDIBASE_IN_BUILDER##"]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (type === "event-completed") {
|
||||||
|
eventStore.actions.resolveEvent(data)
|
||||||
|
} else if (type === "eject-block") {
|
||||||
|
const block = blockStore.actions.getBlock(data)
|
||||||
|
block?.eject()
|
||||||
|
} else if (type === "dragging-new-component") {
|
||||||
|
const { dragging, component } = data
|
||||||
|
if (dragging) {
|
||||||
|
const definition =
|
||||||
|
componentStore.actions.getComponentDefinition(component)
|
||||||
|
dndStore.actions.startDraggingNewComponent({ component, definition })
|
||||||
|
} else {
|
||||||
|
dndStore.actions.reset()
|
||||||
|
}
|
||||||
|
} else if (type === "request-context") {
|
||||||
|
const { selectedComponentInstance, screenslotInstance } =
|
||||||
|
get(componentStore)
|
||||||
|
const instance = selectedComponentInstance || screenslotInstance
|
||||||
|
const context = instance?.getDataContext()
|
||||||
|
let stringifiedContext = null
|
||||||
|
try {
|
||||||
|
stringifiedContext = JSON.stringify(context)
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore - invalid context
|
||||||
|
}
|
||||||
|
eventStore.actions.dispatchEvent("provide-context", {
|
||||||
|
context: stringifiedContext,
|
||||||
|
})
|
||||||
|
} else if (type === "hover-component") {
|
||||||
|
hoverStore.actions.hoverComponent(data, false)
|
||||||
|
} else if (type === "builder-meta") {
|
||||||
|
builderStore.actions.setMetadata(data)
|
||||||
|
} else if (type === "builder-state") {
|
||||||
|
const [[key, value]] = Object.entries(data)
|
||||||
|
stateStore.actions.setValue(key, value)
|
||||||
|
} else if (type === "builder-url-test-data") {
|
||||||
|
const { route, testValue } = data
|
||||||
|
routeStore.actions.setTestUrlParams(route, testValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register any custom components
|
||||||
|
if (window["##BUDIBASE_CUSTOM_COMPONENTS##"]) {
|
||||||
|
window["##BUDIBASE_CUSTOM_COMPONENTS##"].forEach(component => {
|
||||||
|
componentStore.actions.registerCustomComponent(component)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a callback available for custom component bundles to register
|
||||||
|
// themselves at runtime
|
||||||
|
window.registerCustomComponent =
|
||||||
|
componentStore.actions.registerCustomComponent
|
||||||
|
|
||||||
|
// Initialise websocket
|
||||||
|
initWebsocket()
|
||||||
|
|
||||||
|
// Create app if one hasn't been created yet
|
||||||
|
if (!app) {
|
||||||
|
app = new ClientApp({
|
||||||
|
target: window.document.body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach to window so the HTML template can call this when it loads
|
||||||
|
window.loadBudibase = loadBudibase
|
|
@ -10,6 +10,7 @@ import {
|
||||||
eventStore,
|
eventStore,
|
||||||
hoverStore,
|
hoverStore,
|
||||||
stateStore,
|
stateStore,
|
||||||
|
routeStore,
|
||||||
} from "@/stores"
|
} from "@/stores"
|
||||||
import { get } from "svelte/store"
|
import { get } from "svelte/store"
|
||||||
import { initWebsocket } from "@/websocket"
|
import { initWebsocket } from "@/websocket"
|
||||||
|
@ -18,7 +19,6 @@ import type { ActionTypes } from "@/constants"
|
||||||
import { Readable } from "svelte/store"
|
import { Readable } from "svelte/store"
|
||||||
import {
|
import {
|
||||||
Screen,
|
Screen,
|
||||||
Layout,
|
|
||||||
Theme,
|
Theme,
|
||||||
AppCustomTheme,
|
AppCustomTheme,
|
||||||
PreviewDevice,
|
PreviewDevice,
|
||||||
|
@ -47,7 +47,6 @@ declare global {
|
||||||
// Data from builder
|
// Data from builder
|
||||||
"##BUDIBASE_APP_ID##"?: string
|
"##BUDIBASE_APP_ID##"?: string
|
||||||
"##BUDIBASE_IN_BUILDER##"?: true
|
"##BUDIBASE_IN_BUILDER##"?: true
|
||||||
"##BUDIBASE_PREVIEW_LAYOUT##"?: Layout
|
|
||||||
"##BUDIBASE_PREVIEW_SCREEN##"?: Screen
|
"##BUDIBASE_PREVIEW_SCREEN##"?: Screen
|
||||||
"##BUDIBASE_SELECTED_COMPONENT_ID##"?: string
|
"##BUDIBASE_SELECTED_COMPONENT_ID##"?: string
|
||||||
"##BUDIBASE_PREVIEW_ID##"?: number
|
"##BUDIBASE_PREVIEW_ID##"?: number
|
||||||
|
@ -58,13 +57,8 @@ declare global {
|
||||||
"##BUDIBASE_PREVIEW_NAVIGATION##"?: AppNavigation
|
"##BUDIBASE_PREVIEW_NAVIGATION##"?: AppNavigation
|
||||||
"##BUDIBASE_HIDDEN_COMPONENT_IDS##"?: string[]
|
"##BUDIBASE_HIDDEN_COMPONENT_IDS##"?: string[]
|
||||||
"##BUDIBASE_USED_PLUGINS##"?: Plugin[]
|
"##BUDIBASE_USED_PLUGINS##"?: Plugin[]
|
||||||
"##BUDIBASE_LOCATION##"?: {
|
|
||||||
protocol: string
|
|
||||||
hostname: string
|
|
||||||
port: string
|
|
||||||
}
|
|
||||||
"##BUDIBASE_SNIPPETS##"?: Snippet[]
|
"##BUDIBASE_SNIPPETS##"?: Snippet[]
|
||||||
"##BUDIBASE_COMPONENT_ERRORS##"?: Record<string, UIComponentError>[]
|
"##BUDIBASE_COMPONENT_ERRORS##"?: Record<string, UIComponentError[]>
|
||||||
"##BUDIBASE_CUSTOM_COMPONENTS##"?: CustomComponent[]
|
"##BUDIBASE_CUSTOM_COMPONENTS##"?: CustomComponent[]
|
||||||
|
|
||||||
// Other flags
|
// Other flags
|
||||||
|
@ -114,7 +108,6 @@ const loadBudibase = async () => {
|
||||||
builderStore.set({
|
builderStore.set({
|
||||||
...get(builderStore),
|
...get(builderStore),
|
||||||
inBuilder: !!window["##BUDIBASE_IN_BUILDER##"],
|
inBuilder: !!window["##BUDIBASE_IN_BUILDER##"],
|
||||||
layout: window["##BUDIBASE_PREVIEW_LAYOUT##"],
|
|
||||||
screen: window["##BUDIBASE_PREVIEW_SCREEN##"],
|
screen: window["##BUDIBASE_PREVIEW_SCREEN##"],
|
||||||
selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"],
|
selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"],
|
||||||
previewId: window["##BUDIBASE_PREVIEW_ID##"],
|
previewId: window["##BUDIBASE_PREVIEW_ID##"],
|
||||||
|
@ -124,7 +117,6 @@ const loadBudibase = async () => {
|
||||||
navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"],
|
navigation: window["##BUDIBASE_PREVIEW_NAVIGATION##"],
|
||||||
hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"],
|
hiddenComponentIds: window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"],
|
||||||
usedPlugins: window["##BUDIBASE_USED_PLUGINS##"],
|
usedPlugins: window["##BUDIBASE_USED_PLUGINS##"],
|
||||||
location: window["##BUDIBASE_LOCATION##"],
|
|
||||||
snippets: window["##BUDIBASE_SNIPPETS##"],
|
snippets: window["##BUDIBASE_SNIPPETS##"],
|
||||||
componentErrors: window["##BUDIBASE_COMPONENT_ERRORS##"],
|
componentErrors: window["##BUDIBASE_COMPONENT_ERRORS##"],
|
||||||
})
|
})
|
||||||
|
@ -188,6 +180,9 @@ const loadBudibase = async () => {
|
||||||
} else if (type === "builder-state") {
|
} else if (type === "builder-state") {
|
||||||
const [[key, value]] = Object.entries(data)
|
const [[key, value]] = Object.entries(data)
|
||||||
stateStore.actions.setValue(key, value)
|
stateStore.actions.setValue(key, value)
|
||||||
|
} else if (type === "builder-url-test-data") {
|
||||||
|
const { route, testValue } = data
|
||||||
|
routeStore.actions.setTestUrlParams(route, testValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,39 @@ import { writable, get } from "svelte/store"
|
||||||
import { API } from "@/api"
|
import { API } from "@/api"
|
||||||
import { devToolsStore } from "./devTools.js"
|
import { devToolsStore } from "./devTools.js"
|
||||||
import { eventStore } from "./events.js"
|
import { eventStore } from "./events.js"
|
||||||
|
import {
|
||||||
|
ComponentDefinition,
|
||||||
|
DropPosition,
|
||||||
|
PingSource,
|
||||||
|
PreviewDevice,
|
||||||
|
Screen,
|
||||||
|
Theme,
|
||||||
|
AppCustomTheme,
|
||||||
|
AppNavigation,
|
||||||
|
Plugin,
|
||||||
|
Snippet,
|
||||||
|
UIComponentError,
|
||||||
|
} from "@budibase/types"
|
||||||
|
|
||||||
|
interface BuilderStore {
|
||||||
|
inBuilder: boolean
|
||||||
|
screen?: Screen | null
|
||||||
|
selectedComponentId?: string | null
|
||||||
|
editMode: boolean
|
||||||
|
previewId?: number | null
|
||||||
|
theme?: Theme | null
|
||||||
|
customTheme?: AppCustomTheme | null
|
||||||
|
previewDevice?: PreviewDevice
|
||||||
|
navigation?: AppNavigation | null
|
||||||
|
hiddenComponentIds?: string[]
|
||||||
|
usedPlugins?: Plugin[] | null
|
||||||
|
metadata: { componentId: string; step: number } | null
|
||||||
|
snippets?: Snippet[] | null
|
||||||
|
componentErrors?: Record<string, UIComponentError[]>
|
||||||
|
}
|
||||||
|
|
||||||
const createBuilderStore = () => {
|
const createBuilderStore = () => {
|
||||||
const initialState = {
|
const initialState: BuilderStore = {
|
||||||
inBuilder: false,
|
inBuilder: false,
|
||||||
screen: null,
|
screen: null,
|
||||||
selectedComponentId: null,
|
selectedComponentId: null,
|
||||||
|
@ -16,17 +46,13 @@ const createBuilderStore = () => {
|
||||||
navigation: null,
|
navigation: null,
|
||||||
hiddenComponentIds: [],
|
hiddenComponentIds: [],
|
||||||
usedPlugins: null,
|
usedPlugins: null,
|
||||||
eventResolvers: {},
|
|
||||||
metadata: null,
|
metadata: null,
|
||||||
snippets: null,
|
snippets: null,
|
||||||
componentErrors: {},
|
componentErrors: {},
|
||||||
|
|
||||||
// Legacy - allow the builder to specify a layout
|
|
||||||
layout: null,
|
|
||||||
}
|
}
|
||||||
const store = writable(initialState)
|
const store = writable(initialState)
|
||||||
const actions = {
|
const actions = {
|
||||||
selectComponent: id => {
|
selectComponent: (id: string) => {
|
||||||
if (id === get(store).selectedComponentId) {
|
if (id === get(store).selectedComponentId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -38,46 +64,59 @@ const createBuilderStore = () => {
|
||||||
devToolsStore.actions.setAllowSelection(false)
|
devToolsStore.actions.setAllowSelection(false)
|
||||||
eventStore.actions.dispatchEvent("select-component", { id })
|
eventStore.actions.dispatchEvent("select-component", { id })
|
||||||
},
|
},
|
||||||
updateProp: (prop, value) => {
|
updateProp: (prop: string, value: any) => {
|
||||||
eventStore.actions.dispatchEvent("update-prop", { prop, value })
|
eventStore.actions.dispatchEvent("update-prop", { prop, value })
|
||||||
},
|
},
|
||||||
updateStyles: async (styles, id) => {
|
updateStyles: async (styles: Record<string, any>, id: string) => {
|
||||||
await eventStore.actions.dispatchEvent("update-styles", {
|
await eventStore.actions.dispatchEvent("update-styles", {
|
||||||
styles,
|
styles,
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
keyDown: (key, ctrlKey) => {
|
keyDown: (key: string, ctrlKey: boolean) => {
|
||||||
eventStore.actions.dispatchEvent("key-down", { key, ctrlKey })
|
eventStore.actions.dispatchEvent("key-down", { key, ctrlKey })
|
||||||
},
|
},
|
||||||
duplicateComponent: (id, mode = "below", selectComponent = true) => {
|
duplicateComponent: (
|
||||||
|
id: string,
|
||||||
|
mode = DropPosition.BELOW,
|
||||||
|
selectComponent = true
|
||||||
|
) => {
|
||||||
eventStore.actions.dispatchEvent("duplicate-component", {
|
eventStore.actions.dispatchEvent("duplicate-component", {
|
||||||
id,
|
id,
|
||||||
mode,
|
mode,
|
||||||
selectComponent,
|
selectComponent,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
deleteComponent: id => {
|
deleteComponent: (id: string) => {
|
||||||
eventStore.actions.dispatchEvent("delete-component", { id })
|
eventStore.actions.dispatchEvent("delete-component", { id })
|
||||||
},
|
},
|
||||||
notifyLoaded: () => {
|
notifyLoaded: () => {
|
||||||
eventStore.actions.dispatchEvent("preview-loaded")
|
eventStore.actions.dispatchEvent("preview-loaded")
|
||||||
},
|
},
|
||||||
analyticsPing: async ({ embedded }) => {
|
analyticsPing: async ({ embedded }: { embedded: boolean }) => {
|
||||||
try {
|
try {
|
||||||
await API.analyticsPing({ source: "app", embedded })
|
await API.analyticsPing({ source: PingSource.APP, embedded })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
moveComponent: async (componentId, destinationComponentId, mode) => {
|
moveComponent: async (
|
||||||
|
componentId: string,
|
||||||
|
destinationComponentId: string,
|
||||||
|
mode: DropPosition
|
||||||
|
) => {
|
||||||
await eventStore.actions.dispatchEvent("move-component", {
|
await eventStore.actions.dispatchEvent("move-component", {
|
||||||
componentId,
|
componentId,
|
||||||
destinationComponentId,
|
destinationComponentId,
|
||||||
mode,
|
mode,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
dropNewComponent: (component, parent, index, props) => {
|
dropNewComponent: (
|
||||||
|
component: string,
|
||||||
|
parent: string,
|
||||||
|
index: number,
|
||||||
|
props: Record<string, any>
|
||||||
|
) => {
|
||||||
eventStore.actions.dispatchEvent("drop-new-component", {
|
eventStore.actions.dispatchEvent("drop-new-component", {
|
||||||
component,
|
component,
|
||||||
parent,
|
parent,
|
||||||
|
@ -85,7 +124,7 @@ const createBuilderStore = () => {
|
||||||
props,
|
props,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
setEditMode: enabled => {
|
setEditMode: (enabled: boolean) => {
|
||||||
if (enabled === get(store).editMode) {
|
if (enabled === get(store).editMode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -94,18 +133,18 @@ const createBuilderStore = () => {
|
||||||
requestAddComponent: () => {
|
requestAddComponent: () => {
|
||||||
eventStore.actions.dispatchEvent("request-add-component")
|
eventStore.actions.dispatchEvent("request-add-component")
|
||||||
},
|
},
|
||||||
highlightSetting: setting => {
|
highlightSetting: (setting: string) => {
|
||||||
eventStore.actions.dispatchEvent("highlight-setting", { setting })
|
eventStore.actions.dispatchEvent("highlight-setting", { setting })
|
||||||
},
|
},
|
||||||
ejectBlock: (id, definition) => {
|
ejectBlock: (id: string, definition: ComponentDefinition) => {
|
||||||
eventStore.actions.dispatchEvent("eject-block", { id, definition })
|
eventStore.actions.dispatchEvent("eject-block", { id, definition })
|
||||||
},
|
},
|
||||||
updateUsedPlugin: (name, hash) => {
|
updateUsedPlugin: (name: string, hash: string) => {
|
||||||
// Check if we used this plugin
|
// Check if we used this plugin
|
||||||
const used = get(store)?.usedPlugins?.find(x => x.name === name)
|
const used = get(store)?.usedPlugins?.find(x => x.name === name)
|
||||||
if (used) {
|
if (used) {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.usedPlugins = state.usedPlugins.filter(x => x.name !== name)
|
state.usedPlugins = state.usedPlugins!.filter(x => x.name !== name)
|
||||||
state.usedPlugins.push({
|
state.usedPlugins.push({
|
||||||
...used,
|
...used,
|
||||||
hash,
|
hash,
|
||||||
|
@ -117,13 +156,13 @@ const createBuilderStore = () => {
|
||||||
// Notify the builder so we can reload component definitions
|
// Notify the builder so we can reload component definitions
|
||||||
eventStore.actions.dispatchEvent("reload-plugin")
|
eventStore.actions.dispatchEvent("reload-plugin")
|
||||||
},
|
},
|
||||||
addParentComponent: (componentId, parentType) => {
|
addParentComponent: (componentId: string, parentType: string) => {
|
||||||
eventStore.actions.dispatchEvent("add-parent-component", {
|
eventStore.actions.dispatchEvent("add-parent-component", {
|
||||||
componentId,
|
componentId,
|
||||||
parentType,
|
parentType,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
setMetadata: metadata => {
|
setMetadata: (metadata: { componentId: string; step: number }) => {
|
||||||
store.update(state => ({
|
store.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
metadata,
|
metadata,
|
||||||
|
@ -132,7 +171,7 @@ const createBuilderStore = () => {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...store,
|
...store,
|
||||||
set: state => store.set({ ...initialState, ...state }),
|
set: (state: BuilderStore) => store.set({ ...initialState, ...state }),
|
||||||
actions,
|
actions,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import { derived } from "svelte/store"
|
import { derived } from "svelte/store"
|
||||||
import { appStore } from "../app.js"
|
import { appStore } from "../app.js"
|
||||||
import { builderStore } from "../builder.js"
|
import { builderStore } from "../builder"
|
||||||
|
|
||||||
export const devToolsEnabled = derived(
|
export const devToolsEnabled = derived(
|
||||||
[appStore, builderStore],
|
[appStore, builderStore],
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { appStore } from "../app.js"
|
import { appStore } from "../app.js"
|
||||||
import { builderStore } from "../builder.js"
|
import { builderStore } from "../builder"
|
||||||
import { derivedMemo } from "@budibase/frontend-core"
|
import { derivedMemo } from "@budibase/frontend-core"
|
||||||
|
|
||||||
export const snippets = derivedMemo(
|
export const snippets = derivedMemo(
|
||||||
|
|
|
@ -119,7 +119,39 @@ const createRouteStore = () => {
|
||||||
const base = window.location.href.split("#")[0]
|
const base = window.location.href.split("#")[0]
|
||||||
return `${base}#${relativeURL}`
|
return `${base}#${relativeURL}`
|
||||||
}
|
}
|
||||||
|
const setTestUrlParams = (route: string, testValue: string) => {
|
||||||
|
if (route === "/") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const [pathPart, queryPart] = testValue.split("?")
|
||||||
|
const routeSegments = route.split("/").filter(Boolean)
|
||||||
|
|
||||||
|
// If first segment happens to be a parameter (e.g. /:foo), include it
|
||||||
|
const startIndex = routeSegments[0]?.startsWith(":") ? 0 : 1
|
||||||
|
const segments = routeSegments.slice(startIndex)
|
||||||
|
const testSegments = pathPart.split("/")
|
||||||
|
|
||||||
|
const params: Record<string, string> = {}
|
||||||
|
segments.forEach((segment, index) => {
|
||||||
|
if (segment.startsWith(":") && index < testSegments.length) {
|
||||||
|
params[segment.slice(1)] = testSegments[index]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const queryParams: Record<string, string> = {}
|
||||||
|
if (queryPart) {
|
||||||
|
queryPart.split("&").forEach(param => {
|
||||||
|
const [key, value] = param.split("=")
|
||||||
|
if (key && value) {
|
||||||
|
queryParams[key] = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setQueryParams({ ...queryParams })
|
||||||
|
store.update(state => ({ ...state, testUrlParams: params }))
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
subscribe: store.subscribe,
|
subscribe: store.subscribe,
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -130,6 +162,7 @@ const createRouteStore = () => {
|
||||||
setQueryParams,
|
setQueryParams,
|
||||||
setActiveRoute,
|
setActiveRoute,
|
||||||
setRouterLoaded,
|
setRouterLoaded,
|
||||||
|
setTestUrlParams,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,6 @@ const createScreenStore = () => {
|
||||||
activeScreen = Helpers.cloneDeep($builderStore.screen)
|
activeScreen = Helpers.cloneDeep($builderStore.screen)
|
||||||
screens = [activeScreen]
|
screens = [activeScreen]
|
||||||
|
|
||||||
// Legacy - allow the builder to specify a layout
|
|
||||||
if ($builderStore.layout) {
|
|
||||||
activeLayout = $builderStore.layout
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach meta
|
// Attach meta
|
||||||
const errors = $builderStore.componentErrors || {}
|
const errors = $builderStore.componentErrors || {}
|
||||||
const attachComponentMeta = component => {
|
const attachComponentMeta = component => {
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
"jsonschema": "1.4.0",
|
"jsonschema": "1.4.0",
|
||||||
"jsonwebtoken": "9.0.2",
|
"jsonwebtoken": "9.0.2",
|
||||||
"knex": "2.4.2",
|
"knex": "2.4.2",
|
||||||
"koa": "2.13.4",
|
"koa": "2.15.4",
|
||||||
"koa-body": "4.2.0",
|
"koa-body": "4.2.0",
|
||||||
"koa-compress": "4.0.1",
|
"koa-compress": "4.0.1",
|
||||||
"koa-send": "5.0.1",
|
"koa-send": "5.0.1",
|
||||||
|
|
|
@ -339,10 +339,13 @@ export const getSignedUploadURL = async function (
|
||||||
ctx.throw(400, "bucket and key values are required")
|
ctx.throw(400, "bucket and key values are required")
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
let endpoint = datasource?.config?.endpoint
|
||||||
|
if (endpoint && !utils.urlHasProtocol(endpoint)) {
|
||||||
|
endpoint = `https://${endpoint}`
|
||||||
|
}
|
||||||
const s3 = new S3({
|
const s3 = new S3({
|
||||||
region: awsRegion,
|
region: awsRegion,
|
||||||
endpoint: datasource?.config?.endpoint || undefined,
|
endpoint: endpoint,
|
||||||
|
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: datasource?.config?.accessKeyId as string,
|
accessKeyId: datasource?.config?.accessKeyId as string,
|
||||||
secretAccessKey: datasource?.config?.secretAccessKey as string,
|
secretAccessKey: datasource?.config?.secretAccessKey as string,
|
||||||
|
@ -350,8 +353,8 @@ export const getSignedUploadURL = async function (
|
||||||
})
|
})
|
||||||
const params = { Bucket: bucket, Key: key }
|
const params = { Bucket: bucket, Key: key }
|
||||||
signedUrl = await getSignedUrl(s3, new PutObjectCommand(params))
|
signedUrl = await getSignedUrl(s3, new PutObjectCommand(params))
|
||||||
if (datasource?.config?.endpoint) {
|
if (endpoint) {
|
||||||
publicUrl = `${datasource.config.endpoint}/${bucket}/${key}`
|
publicUrl = `${endpoint}/${bucket}/${key}`
|
||||||
} else {
|
} else {
|
||||||
publicUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com/${key}`
|
publicUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com/${key}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,6 @@
|
||||||
// Set some flags so the app knows we're in the builder
|
// Set some flags so the app knows we're in the builder
|
||||||
window["##BUDIBASE_IN_BUILDER##"] = true
|
window["##BUDIBASE_IN_BUILDER##"] = true
|
||||||
window["##BUDIBASE_APP_ID##"] = appId
|
window["##BUDIBASE_APP_ID##"] = appId
|
||||||
window["##BUDIBASE_PREVIEW_LAYOUT##"] = layout
|
|
||||||
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
|
window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
|
||||||
window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
|
window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
|
||||||
window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
|
window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
|
||||||
|
@ -90,7 +89,6 @@
|
||||||
window["##BUDIBASE_PREVIEW_NAVIGATION##"] = navigation
|
window["##BUDIBASE_PREVIEW_NAVIGATION##"] = navigation
|
||||||
window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"] = hiddenComponentIds
|
window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"] = hiddenComponentIds
|
||||||
window["##BUDIBASE_USED_PLUGINS##"] = usedPlugins
|
window["##BUDIBASE_USED_PLUGINS##"] = usedPlugins
|
||||||
window["##BUDIBASE_LOCATION##"] = location
|
|
||||||
window["##BUDIBASE_SNIPPETS##"] = snippets
|
window["##BUDIBASE_SNIPPETS##"] = snippets
|
||||||
window['##BUDIBASE_COMPONENT_ERRORS##'] = componentErrors
|
window['##BUDIBASE_COMPONENT_ERRORS##'] = componentErrors
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
ConnectionInfo,
|
ConnectionInfo,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
import { S3 } from "@aws-sdk/client-s3"
|
import { S3, S3ClientConfig } from "@aws-sdk/client-s3"
|
||||||
import csv from "csvtojson"
|
import csv from "csvtojson"
|
||||||
import stream from "stream"
|
import stream from "stream"
|
||||||
|
|
||||||
|
@ -157,13 +157,20 @@ const SCHEMA: Integration = {
|
||||||
}
|
}
|
||||||
|
|
||||||
class S3Integration implements IntegrationBase {
|
class S3Integration implements IntegrationBase {
|
||||||
private readonly config: S3Config
|
private readonly config: S3ClientConfig
|
||||||
private client
|
private client: S3
|
||||||
|
|
||||||
constructor(config: S3Config) {
|
constructor(config: S3Config) {
|
||||||
this.config = config
|
this.config = {
|
||||||
if (this.config.endpoint) {
|
forcePathStyle: config.s3ForcePathStyle || true,
|
||||||
this.config.s3ForcePathStyle = true
|
credentials: {
|
||||||
|
accessKeyId: config.accessKeyId,
|
||||||
|
secretAccessKey: config.secretAccessKey,
|
||||||
|
},
|
||||||
|
region: config.region,
|
||||||
|
}
|
||||||
|
if (config.endpoint) {
|
||||||
|
this.config.forcePathStyle = true
|
||||||
} else {
|
} else {
|
||||||
delete this.config.endpoint
|
delete this.config.endpoint
|
||||||
}
|
}
|
||||||
|
@ -176,7 +183,9 @@ class S3Integration implements IntegrationBase {
|
||||||
connected: false,
|
connected: false,
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.client.listBuckets()
|
await this.client.listBuckets({
|
||||||
|
MaxBuckets: 1,
|
||||||
|
})
|
||||||
response.connected = true
|
response.connected = true
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
response.error = e.message as string
|
response.error = e.message as string
|
||||||
|
@ -253,7 +262,7 @@ class S3Integration implements IntegrationBase {
|
||||||
.on("error", () => {
|
.on("error", () => {
|
||||||
csvError = true
|
csvError = true
|
||||||
})
|
})
|
||||||
fileStream.on("finish", () => {
|
fileStream.on("end", () => {
|
||||||
resolve(response)
|
resolve(response)
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
|
|
@ -120,7 +120,7 @@ export function areRESTVariablesValid(datasource: Datasource) {
|
||||||
|
|
||||||
export function checkDatasourceTypes(schema: Integration, config: any) {
|
export function checkDatasourceTypes(schema: Integration, config: any) {
|
||||||
for (let key of Object.keys(config)) {
|
for (let key of Object.keys(config)) {
|
||||||
if (!schema.datasource[key]) {
|
if (!schema.datasource?.[key]) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const type = schema.datasource[key].type
|
const type = schema.datasource[key].type
|
||||||
|
@ -149,7 +149,9 @@ async function enrichDatasourceWithValues(
|
||||||
) as Datasource
|
) as Datasource
|
||||||
processed.entities = entities
|
processed.entities = entities
|
||||||
const definition = await getDefinition(processed.source)
|
const definition = await getDefinition(processed.source)
|
||||||
processed.config = checkDatasourceTypes(definition!, processed.config)
|
if (definition) {
|
||||||
|
processed.config = checkDatasourceTypes(definition, processed.config)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
datasource: processed,
|
datasource: processed,
|
||||||
envVars: env as Record<string, string>,
|
envVars: env as Record<string, string>,
|
||||||
|
|
|
@ -157,7 +157,7 @@ export interface Integration {
|
||||||
friendlyName: string
|
friendlyName: string
|
||||||
type?: string
|
type?: string
|
||||||
iconUrl?: string
|
iconUrl?: string
|
||||||
datasource: DatasourceConfig
|
datasource?: DatasourceConfig
|
||||||
query: {
|
query: {
|
||||||
[key: string]: QueryDefinition
|
[key: string]: QueryDefinition
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,8 @@
|
||||||
// type purely to capture structures that the type is unknown, but maybe known later
|
// type purely to capture structures that the type is unknown, but maybe known later
|
||||||
export type UIObject = Record<string, any>
|
export type UIObject = Record<string, any>
|
||||||
|
|
||||||
|
export const enum DropPosition {
|
||||||
|
ABOVE = "above",
|
||||||
|
BELOW = "below",
|
||||||
|
INSIDE = "inside",
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
export type PreviewDevice = "desktop" | "tablet" | "mobile"
|
export type PreviewDevice = "desktop" | "tablet" | "mobile"
|
||||||
|
export type ComponentContext = Record<string, any>
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
"joi": "17.6.0",
|
"joi": "17.6.0",
|
||||||
"jsonwebtoken": "9.0.2",
|
"jsonwebtoken": "9.0.2",
|
||||||
"knex": "2.4.2",
|
"knex": "2.4.2",
|
||||||
"koa": "2.13.4",
|
"koa": "2.15.4",
|
||||||
"koa-body": "4.2.0",
|
"koa-body": "4.2.0",
|
||||||
"koa-compress": "4.0.1",
|
"koa-compress": "4.0.1",
|
||||||
"koa-passport": "4.1.4",
|
"koa-passport": "4.1.4",
|
||||||
|
|
65
yarn.lock
65
yarn.lock
|
@ -9421,7 +9421,7 @@ cookiejar@^2.1.4:
|
||||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
|
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
|
||||||
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
|
||||||
|
|
||||||
cookies@0.8.0, cookies@~0.8.0:
|
cookies@0.8.0:
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90"
|
resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90"
|
||||||
integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==
|
integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==
|
||||||
|
@ -9429,6 +9429,14 @@ cookies@0.8.0, cookies@~0.8.0:
|
||||||
depd "~2.0.0"
|
depd "~2.0.0"
|
||||||
keygrip "~1.1.0"
|
keygrip "~1.1.0"
|
||||||
|
|
||||||
|
cookies@~0.9.0:
|
||||||
|
version "0.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.9.1.tgz#3ffed6f60bb4fb5f146feeedba50acc418af67e3"
|
||||||
|
integrity sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==
|
||||||
|
dependencies:
|
||||||
|
depd "~2.0.0"
|
||||||
|
keygrip "~1.1.0"
|
||||||
|
|
||||||
copyfiles@2.4.1:
|
copyfiles@2.4.1:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5"
|
resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5"
|
||||||
|
@ -9820,10 +9828,10 @@ dd-trace@5.26.0:
|
||||||
shell-quote "^1.8.1"
|
shell-quote "^1.8.1"
|
||||||
tlhunter-sorted-set "^0.1.0"
|
tlhunter-sorted-set "^0.1.0"
|
||||||
|
|
||||||
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4:
|
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5:
|
||||||
version "4.3.7"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
||||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.3"
|
ms "^2.1.3"
|
||||||
|
|
||||||
|
@ -9841,10 +9849,10 @@ debug@^3.1.0, debug@^3.2.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
debug@^4.3.5:
|
debug@~4.3.1, debug@~4.3.2, debug@~4.3.4:
|
||||||
version "4.4.0"
|
version "4.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.3"
|
ms "^2.1.3"
|
||||||
|
|
||||||
|
@ -14732,45 +14740,16 @@ koa2-ratelimit@1.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/koa2-ratelimit/-/koa2-ratelimit-1.1.1.tgz#9c1d8257770e4a0a08063ba2ddcaf690fd457d23"
|
resolved "https://registry.yarnpkg.com/koa2-ratelimit/-/koa2-ratelimit-1.1.1.tgz#9c1d8257770e4a0a08063ba2ddcaf690fd457d23"
|
||||||
integrity sha512-IpxGMdZqEhMykW0yYKGVB4vDEacPvSBH4hNpDL38ABj3W2KHNLujAljGEDg7eEjXvrRbXRSWXzANhV3c9v7nyg==
|
integrity sha512-IpxGMdZqEhMykW0yYKGVB4vDEacPvSBH4hNpDL38ABj3W2KHNLujAljGEDg7eEjXvrRbXRSWXzANhV3c9v7nyg==
|
||||||
|
|
||||||
koa@2.13.4:
|
koa@2.15.4, koa@^2.13.1, koa@^2.13.4:
|
||||||
version "2.13.4"
|
version "2.15.4"
|
||||||
resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e"
|
resolved "https://registry.yarnpkg.com/koa/-/koa-2.15.4.tgz#7000b3d8354558671adb1ba1b1c09bedb5f8da75"
|
||||||
integrity sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g==
|
integrity sha512-7fNBIdrU2PEgLljXoPWoyY4r1e+ToWCmzS/wwMPbUNs7X+5MMET1ObhJBlUkF5uZG9B6QhM2zS1TsH6adegkiQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
accepts "^1.3.5"
|
accepts "^1.3.5"
|
||||||
cache-content-type "^1.0.0"
|
cache-content-type "^1.0.0"
|
||||||
content-disposition "~0.5.2"
|
content-disposition "~0.5.2"
|
||||||
content-type "^1.0.4"
|
content-type "^1.0.4"
|
||||||
cookies "~0.8.0"
|
cookies "~0.9.0"
|
||||||
debug "^4.3.2"
|
|
||||||
delegates "^1.0.0"
|
|
||||||
depd "^2.0.0"
|
|
||||||
destroy "^1.0.4"
|
|
||||||
encodeurl "^1.0.2"
|
|
||||||
escape-html "^1.0.3"
|
|
||||||
fresh "~0.5.2"
|
|
||||||
http-assert "^1.3.0"
|
|
||||||
http-errors "^1.6.3"
|
|
||||||
is-generator-function "^1.0.7"
|
|
||||||
koa-compose "^4.1.0"
|
|
||||||
koa-convert "^2.0.0"
|
|
||||||
on-finished "^2.3.0"
|
|
||||||
only "~0.0.2"
|
|
||||||
parseurl "^1.3.2"
|
|
||||||
statuses "^1.5.0"
|
|
||||||
type-is "^1.6.16"
|
|
||||||
vary "^1.1.2"
|
|
||||||
|
|
||||||
koa@^2.13.1, koa@^2.13.4:
|
|
||||||
version "2.14.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/koa/-/koa-2.14.1.tgz#defb9589297d8eb1859936e777f3feecfc26925c"
|
|
||||||
integrity sha512-USJFyZgi2l0wDgqkfD27gL4YGno7TfUkcmOe6UOLFOVuN+J7FwnNu4Dydl4CUQzraM1lBAiGed0M9OVJoT0Kqw==
|
|
||||||
dependencies:
|
|
||||||
accepts "^1.3.5"
|
|
||||||
cache-content-type "^1.0.0"
|
|
||||||
content-disposition "~0.5.2"
|
|
||||||
content-type "^1.0.4"
|
|
||||||
cookies "~0.8.0"
|
|
||||||
debug "^4.3.2"
|
debug "^4.3.2"
|
||||||
delegates "^1.0.0"
|
delegates "^1.0.0"
|
||||||
depd "^2.0.0"
|
depd "^2.0.0"
|
||||||
|
|
Loading…
Reference in New Issue