Merge branch 'develop' of github.com:Budibase/budibase into data-section-multidev

This commit is contained in:
Andrew Kingston 2023-05-26 09:39:06 +01:00
commit 520158c8f9
41 changed files with 579 additions and 121 deletions

View File

@ -37,7 +37,7 @@ jobs:
with: with:
node-version: 14.x node-version: 14.x
- run: yarn - run: yarn install --frozen-lockfile
- name: Update versions - name: Update versions
run: | run: |
version=$(cat lerna.json \ version=$(cat lerna.json \

View File

@ -42,7 +42,7 @@ jobs:
with: with:
node-version: 14.x node-version: 14.x
- run: yarn - run: yarn install --frozen-lockfile
- name: Update versions - name: Update versions
run: | run: |
version=$(cat lerna.json \ version=$(cat lerna.json \

View File

@ -1,5 +1,5 @@
{ {
"version": "2.6.19-alpha.7", "version": "2.6.19-alpha.12",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/backend-core", "packages/backend-core",

View File

@ -3,6 +3,7 @@
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@esbuild-plugins/node-resolve": "^0.2.2", "@esbuild-plugins/node-resolve": "^0.2.2",
"@esbuild-plugins/tsconfig-paths": "^0.1.2",
"@nx/esbuild": "16.2.1", "@nx/esbuild": "16.2.1",
"@nx/js": "16.2.1", "@nx/js": "16.2.1",
"@rollup/plugin-json": "^4.0.2", "@rollup/plugin-json": "^4.0.2",

View File

@ -32,6 +32,7 @@
<Grid <Grid
{API} {API}
tableId={id} tableId={id}
tableType={$tables.selected?.type}
allowAddRows={!isUsersTable} allowAddRows={!isUsersTable}
allowDeleteRows={!isUsersTable} allowDeleteRows={!isUsersTable}
schemaOverrides={isUsersTable ? userSchemaOverrides : null} schemaOverrides={isUsersTable ? userSchemaOverrides : null}

View File

@ -3,6 +3,7 @@
import ImportModal from "../modals/ImportModal.svelte" import ImportModal from "../modals/ImportModal.svelte"
export let tableId export let tableId
export let tableType
export let disabled export let disabled
let modal let modal
@ -12,5 +13,5 @@
Import Import
</ActionButton> </ActionButton>
<Modal bind:this={modal}> <Modal bind:this={modal}>
<ImportModal {tableId} on:importrows /> <ImportModal {tableId} {tableType} on:importrows />
</Modal> </Modal>

View File

@ -4,11 +4,12 @@
export let disabled = false export let disabled = false
const { rows, tableId } = getContext("grid") const { rows, tableId, tableType } = getContext("grid")
</script> </script>
<ImportButton <ImportButton
{disabled} {disabled}
tableId={$tableId} tableId={$tableId}
{tableType}
on:importrows={rows.actions.refreshData} on:importrows={rows.actions.refreshData}
/> />

View File

@ -113,17 +113,26 @@
}) })
download(data, `export.${exportFormat}`) download(data, `export.${exportFormat}`)
} else if (filters || sorting) { } else if (filters || sorting) {
const data = await API.exportRows({ let response
tableId: view, try {
format: exportFormat, response = await API.exportRows({
search: { tableId: view,
query: luceneFilter, format: exportFormat,
sort: sorting?.sortColumn, search: {
sortOrder: sorting?.sortOrder, query: luceneFilter,
paginate: false, sort: sorting?.sortColumn,
}, sortOrder: sorting?.sortOrder,
}) paginate: false,
download(data, `export.${exportFormat}`) },
})
} catch (e) {
console.error("Failed to export", e)
notifications.error("Export Failed")
}
if (response) {
download(response, `export.${exportFormat}`)
notifications.success("Export Successful")
}
} else { } else {
await exportView() await exportView()
} }

View File

@ -13,15 +13,18 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
export let tableId export let tableId
export let tableType
let rows = [] let rows = []
let allValid = false let allValid = false
let displayColumn = null let displayColumn = null
let identifierFields = []
async function importData() { async function importData() {
try { try {
await API.importTableData({ await API.importTableData({
tableId, tableId,
rows, rows,
identifierFields,
}) })
notifications.success("Rows successfully imported") notifications.success("Rows successfully imported")
} catch (error) { } catch (error) {
@ -45,6 +48,13 @@
</Body> </Body>
<Layout gap="XS" noPadding> <Layout gap="XS" noPadding>
<Label grey extraSmall>CSV or JSON file to import</Label> <Label grey extraSmall>CSV or JSON file to import</Label>
<TableDataImport {tableId} bind:rows bind:allValid bind:displayColumn /> <TableDataImport
{tableId}
{tableType}
bind:rows
bind:allValid
bind:displayColumn
bind:identifierFields
/>
</Layout> </Layout>
</ModalContent> </ModalContent>

View File

@ -1,5 +1,5 @@
<script> <script>
import { Select } from "@budibase/bbui" import { Select, Toggle, Multiselect } from "@budibase/bbui"
import { FIELDS } from "constants/backend" import { FIELDS } from "constants/backend"
import { API } from "api" import { API } from "api"
import { parseFile } from "./utils" import { parseFile } from "./utils"
@ -9,14 +9,17 @@
let fileType = null let fileType = null
let loading = false let loading = false
let updateExistingRows = false
let validation = {} let validation = {}
let validateHash = "" let validateHash = ""
let schema = null let schema = null
let invalidColumns = [] let invalidColumns = []
export let tableId = null export let tableId = null
export let tableType
export let rows = [] export let rows = []
export let allValid = false export let allValid = false
export let identifierFields = []
const typeOptions = [ const typeOptions = [
{ {
@ -159,6 +162,22 @@
</div> </div>
{/each} {/each}
</div> </div>
{#if tableType === "internal"}
<br />
<Toggle
bind:value={updateExistingRows}
on:change={() => (identifierFields = [])}
thin
text="Update existing rows"
/>
{#if updateExistingRows}
<Multiselect
label="Identifier field(s)"
options={Object.keys(validation)}
bind:value={identifierFields}
/>
{/if}
{/if}
{#if invalidColumns.length > 0} {#if invalidColumns.length > 0}
<p class="spectrum-FieldLabel spectrum-FieldLabel--sizeM"> <p class="spectrum-FieldLabel spectrum-FieldLabel--sizeM">
The following columns are present in the data you wish to import, but do The following columns are present in the data you wish to import, but do

View File

@ -62,13 +62,15 @@ export const buildTableEndpoints = API => ({
/** /**
* Imports data into an existing table * Imports data into an existing table
* @param tableId the table ID to import to * @param tableId the table ID to import to
* @param data the data import object * @param rows the data import object
* @param identifierFields column names to be used as keys for overwriting existing rows
*/ */
importTableData: async ({ tableId, rows }) => { importTableData: async ({ tableId, rows, identifierFields }) => {
return await API.post({ return await API.post({
url: `/api/tables/${tableId}/import`, url: `/api/tables/${tableId}/import`,
body: { body: {
rows, rows,
identifierFields,
}, },
}) })
}, },

View File

@ -37,6 +37,9 @@
.boolean-cell { .boolean-cell {
padding: 2px var(--cell-padding); padding: 2px var(--cell-padding);
pointer-events: none; pointer-events: none;
flex: 1 1 auto;
display: flex;
justify-content: center;
} }
.boolean-cell.editable { .boolean-cell.editable {
pointer-events: all; pointer-events: all;

View File

@ -11,6 +11,7 @@
export let selected export let selected
export let rowFocused export let rowFocused
export let rowIdx export let rowIdx
export let topRow = false
export let focused export let focused
export let selectedUser export let selectedUser
export let column export let column
@ -68,6 +69,7 @@
{highlighted} {highlighted}
{selected} {selected}
{rowIdx} {rowIdx}
{topRow}
{focused} {focused}
{selectedUser} {selectedUser}
{readonly} {readonly}

View File

@ -6,6 +6,7 @@
export let selectedUser = null export let selectedUser = null
export let error = null export let error = null
export let rowIdx export let rowIdx
export let topRow = false
export let defaultHeight = false export let defaultHeight = false
export let center = false export let center = false
export let readonly = false export let readonly = false
@ -31,13 +32,14 @@
class:readonly class:readonly
class:default-height={defaultHeight} class:default-height={defaultHeight}
class:selected-other={selectedUser != null} class:selected-other={selectedUser != null}
class:alt={rowIdx % 2 === 1}
class:top={topRow}
on:focus on:focus
on:mousedown on:mousedown
on:mouseup on:mouseup
on:click on:click
on:contextmenu on:contextmenu
{style} {style}
data-row={rowIdx}
> >
{#if error} {#if error}
<div class="label"> <div class="label">
@ -70,6 +72,9 @@
width: 0; width: 0;
--cell-color: transparent; --cell-color: transparent;
} }
.cell.alt {
--cell-background: var(--cell-background-alt);
}
.cell.default-height { .cell.default-height {
height: var(--default-row-height); height: var(--default-row-height);
} }
@ -97,7 +102,7 @@
.cell.error:after { .cell.error:after {
border-radius: 0 2px 2px 2px; border-radius: 0 2px 2px 2px;
} }
.cell[data-row="0"].error:after { .cell.top.error:after {
border-radius: 2px 2px 2px 0; border-radius: 2px 2px 2px 0;
} }
.cell.selected-other:not(.focused):after { .cell.selected-other:not(.focused):after {
@ -162,7 +167,7 @@
overflow: hidden; overflow: hidden;
user-select: none; user-select: none;
} }
.cell[data-row="0"] .label { .cell.top .label {
bottom: auto; bottom: auto;
top: 100%; top: 100%;
padding: 2px 4px 2px 4px; padding: 2px 4px 2px 4px;

View File

@ -21,16 +21,7 @@
svelteDispatch("select") svelteDispatch("select")
const id = row?._id const id = row?._id
if (id) { if (id) {
selectedRows.update(state => { selectedRows.actions.toggleRow(id)
let newState = {
...state,
[id]: !state[id],
}
if (!newState[id]) {
delete newState[id]
}
return newState
})
} }
} }
@ -47,6 +38,7 @@
highlighted={rowFocused || rowHovered} highlighted={rowFocused || rowHovered}
selected={rowSelected} selected={rowSelected}
{defaultHeight} {defaultHeight}
rowIdx={row?.__idx}
> >
<div class="gutter"> <div class="gutter">
{#if $$slots.default} {#if $$slots.default}

View File

@ -196,7 +196,11 @@
<MenuItem disabled={!canMoveRight} icon="ChevronRight" on:click={moveRight}> <MenuItem disabled={!canMoveRight} icon="ChevronRight" on:click={moveRight}>
Move right Move right
</MenuItem> </MenuItem>
<MenuItem icon="VisibilityOff" on:click={hideColumn}>Hide column</MenuItem> <MenuItem
disabled={idx === "sticky"}
icon="VisibilityOff"
on:click={hideColumn}>Hide column</MenuItem
>
</Menu> </Menu>
</Popover> </Popover>

View File

@ -34,6 +34,7 @@
export let API = null export let API = null
export let tableId = null export let tableId = null
export let tableType = null
export let schemaOverrides = null export let schemaOverrides = null
export let allowAddRows = true export let allowAddRows = true
export let allowAddColumns = true export let allowAddColumns = true
@ -41,6 +42,7 @@
export let allowExpandRows = true export let allowExpandRows = true
export let allowEditRows = true export let allowEditRows = true
export let allowDeleteRows = true export let allowDeleteRows = true
export let stripeRows = false
export let collaboration = true export let collaboration = true
export let showAvatars = true export let showAvatars = true
@ -57,6 +59,7 @@
allowExpandRows, allowExpandRows,
allowEditRows, allowEditRows,
allowDeleteRows, allowDeleteRows,
stripeRows,
}) })
// Build up context // Build up context
@ -65,6 +68,7 @@
rand, rand,
config, config,
tableId: tableIdStore, tableId: tableIdStore,
tableType,
schemaOverrides: schemaOverridesStore, schemaOverrides: schemaOverridesStore,
} }
context = { ...context, ...createEventManagers() } context = { ...context, ...createEventManagers() }
@ -91,6 +95,7 @@
allowExpandRows, allowExpandRows,
allowEditRows, allowEditRows,
allowDeleteRows, allowDeleteRows,
stripeRows,
}) })
// Set context for children to consume // Set context for children to consume
@ -112,6 +117,7 @@
id="grid-{rand}" id="grid-{rand}"
class:is-resizing={$isResizing} class:is-resizing={$isResizing}
class:is-reordering={$isReordering} class:is-reordering={$isReordering}
class:stripe={$config.stripeRows}
style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px; --max-cell-render-width-overflow:{MaxCellRenderWidthOverflow}px; --content-lines:{$contentLines};" style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px; --max-cell-render-width-overflow:{MaxCellRenderWidthOverflow}px; --content-lines:{$contentLines};"
> >
<div class="controls"> <div class="controls">
@ -176,6 +182,7 @@
/* Variables */ /* Variables */
--cell-background: var(--spectrum-global-color-gray-50); --cell-background: var(--spectrum-global-color-gray-50);
--cell-background-hover: var(--spectrum-global-color-gray-100); --cell-background-hover: var(--spectrum-global-color-gray-100);
--cell-background-alt: var(--cell-background);
--cell-padding: 8px; --cell-padding: 8px;
--cell-spacing: 4px; --cell-spacing: 4px;
--cell-border: 1px solid var(--spectrum-global-color-gray-200); --cell-border: 1px solid var(--spectrum-global-color-gray-200);
@ -192,6 +199,9 @@
.grid.is-reordering :global(*) { .grid.is-reordering :global(*) {
cursor: grabbing !important; cursor: grabbing !important;
} }
.grid.stripe {
--cell-background-alt: var(--spectrum-global-color-gray-75);
}
.grid-data-outer, .grid-data-outer,
.grid-data-inner { .grid-data-inner {

View File

@ -36,7 +36,11 @@
<div bind:this={body} class="grid-body"> <div bind:this={body} class="grid-body">
<GridScrollWrapper scrollHorizontally scrollVertically wheelInteractive> <GridScrollWrapper scrollHorizontally scrollVertically wheelInteractive>
{#each $renderedRows as row, idx} {#each $renderedRows as row, idx}
<GridRow {row} {idx} invertY={idx >= $rowVerticalInversionIndex} /> <GridRow
{row}
top={idx === 0}
invertY={idx >= $rowVerticalInversionIndex}
/>
{/each} {/each}
{#if $config.allowAddRows && $renderedColumns.length} {#if $config.allowAddRows && $renderedColumns.length}
<div <div

View File

@ -3,7 +3,7 @@
import DataCell from "../cells/DataCell.svelte" import DataCell from "../cells/DataCell.svelte"
export let row export let row
export let idx export let top = false
export let invertY = false export let invertY = false
const { const {
@ -41,7 +41,8 @@
invertX={columnIdx >= $columnHorizontalInversionIndex} invertX={columnIdx >= $columnHorizontalInversionIndex}
highlighted={rowHovered || rowFocused || reorderSource === column.name} highlighted={rowHovered || rowFocused || reorderSource === column.name}
selected={rowSelected} selected={rowSelected}
rowIdx={idx} rowIdx={row.__idx}
topRow={top}
focused={$focusedCellId === cellId} focused={$focusedCellId === cellId}
selectedUser={$selectedCellMap[cellId]} selectedUser={$selectedCellMap[cellId]}
width={column.width} width={column.width}

View File

@ -61,7 +61,7 @@
border-right: var(--cell-border); border-right: var(--cell-border);
border-bottom: var(--cell-border); border-bottom: var(--cell-border);
background: var(--spectrum-global-color-gray-100); background: var(--spectrum-global-color-gray-100);
z-index: 20; z-index: 1;
} }
.add:hover { .add:hover {
background: var(--spectrum-global-color-gray-200); background: var(--spectrum-global-color-gray-200);

View File

@ -38,7 +38,7 @@
padding: 2px 6px; padding: 2px 6px;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
background-color: var(--spectrum-global-color-gray-200); background-color: var(--spectrum-global-color-gray-300);
color: var(--spectrum-global-color-gray-700); color: var(--spectrum-global-color-gray-700);
border-radius: 4px; border-radius: 4px;
text-align: center; text-align: center;

View File

@ -167,7 +167,7 @@
focused={$focusedCellId === cellId} focused={$focusedCellId === cellId}
width={$stickyColumn.width} width={$stickyColumn.width}
{updateValue} {updateValue}
rowIdx={offset === 0 ? 0 : 1} topRow={offset === 0}
{invertY} {invertY}
> >
{#if $stickyColumn?.schema?.autocolumn} {#if $stickyColumn?.schema?.autocolumn}
@ -193,7 +193,7 @@
row={newRow} row={newRow}
focused={$focusedCellId === cellId} focused={$focusedCellId === cellId}
width={column.width} width={column.width}
rowIdx={offset === 0 ? 0 : 1} topRow={offset === 0}
invertX={columnIdx >= $columnHorizontalInversionIndex} invertX={columnIdx >= $columnHorizontalInversionIndex}
{invertY} {invertY}
> >
@ -219,7 +219,7 @@
<Button size="M" secondary newStyles on:click={clear}> <Button size="M" secondary newStyles on:click={clear}>
<div class="button-with-keys"> <div class="button-with-keys">
Cancel Cancel
<KeyboardShortcut overlay keybind="Esc" /> <KeyboardShortcut keybind="Esc" />
</div> </div>
</Button> </Button>
</div> </div>

View File

@ -82,7 +82,8 @@
{rowFocused} {rowFocused}
selected={rowSelected} selected={rowSelected}
highlighted={rowHovered || rowFocused} highlighted={rowHovered || rowFocused}
rowIdx={idx} rowIdx={row.__idx}
topRow={idx === 0}
focused={$focusedCellId === cellId} focused={$focusedCellId === cellId}
selectedUser={$selectedCellMap[cellId]} selectedUser={$selectedCellMap[cellId]}
width={$stickyColumn.width} width={$stickyColumn.width}

View File

@ -224,10 +224,7 @@
if (!id || id === NewRowID) { if (!id || id === NewRowID) {
return return
} }
selectedRows.update(state => { selectedRows.actions.toggleRow(id)
state[id] = !state[id]
return state
})
} }
onMount(() => { onMount(() => {

View File

@ -4,9 +4,10 @@ const reorderInitialState = {
sourceColumn: null, sourceColumn: null,
targetColumn: null, targetColumn: null,
breakpoints: [], breakpoints: [],
initialMouseX: null,
scrollLeft: 0,
gridLeft: 0, gridLeft: 0,
width: 0,
latestX: 0,
increment: 0,
} }
export const createStores = () => { export const createStores = () => {
@ -23,14 +24,24 @@ export const createStores = () => {
} }
export const deriveStores = context => { export const deriveStores = context => {
const { reorder, columns, visibleColumns, scroll, bounds, stickyColumn, ui } = const {
context reorder,
columns,
visibleColumns,
scroll,
bounds,
stickyColumn,
ui,
maxScrollLeft,
} = context
let autoScrollInterval
let isAutoScrolling
// Callback when dragging on a colum header and starting reordering // Callback when dragging on a colum header and starting reordering
const startReordering = (column, e) => { const startReordering = (column, e) => {
const $visibleColumns = get(visibleColumns) const $visibleColumns = get(visibleColumns)
const $bounds = get(bounds) const $bounds = get(bounds)
const $scroll = get(scroll)
const $stickyColumn = get(stickyColumn) const $stickyColumn = get(stickyColumn)
ui.actions.blur() ui.actions.blur()
@ -51,9 +62,8 @@ export const deriveStores = context => {
sourceColumn: column, sourceColumn: column,
targetColumn: null, targetColumn: null,
breakpoints, breakpoints,
initialMouseX: e.clientX,
scrollLeft: $scroll.left,
gridLeft: $bounds.left, gridLeft: $bounds.left,
width: $bounds.width,
}) })
// Add listeners to handle mouse movement // Add listeners to handle mouse movement
@ -66,12 +76,44 @@ export const deriveStores = context => {
// Callback when moving the mouse when reordering columns // Callback when moving the mouse when reordering columns
const onReorderMouseMove = e => { const onReorderMouseMove = e => {
// Immediately handle the current position
const x = e.clientX
reorder.update(state => ({
...state,
latestX: x,
}))
considerReorderPosition()
// Check if we need to start auto-scrolling
const $reorder = get(reorder) const $reorder = get(reorder)
const proximityCutoff = 140
const speedFactor = 8
const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x)
const leftProximity = Math.max(0, x - $reorder.gridLeft)
if (rightProximity < proximityCutoff) {
const weight = proximityCutoff - rightProximity
const increment = (weight / proximityCutoff) * speedFactor
reorder.update(state => ({ ...state, increment }))
startAutoScroll()
} else if (leftProximity < proximityCutoff) {
const weight = -1 * (proximityCutoff - leftProximity)
const increment = (weight / proximityCutoff) * speedFactor
reorder.update(state => ({ ...state, increment }))
startAutoScroll()
} else {
stopAutoScroll()
}
}
// Actual logic to consider the current position and determine the new order
const considerReorderPosition = () => {
const $reorder = get(reorder)
const $scroll = get(scroll)
// Compute the closest breakpoint to the current position // Compute the closest breakpoint to the current position
let targetColumn let targetColumn
let minDistance = Number.MAX_SAFE_INTEGER let minDistance = Number.MAX_SAFE_INTEGER
const mouseX = e.clientX - $reorder.gridLeft + $reorder.scrollLeft const mouseX = $reorder.latestX - $reorder.gridLeft + $scroll.left
$reorder.breakpoints.forEach(point => { $reorder.breakpoints.forEach(point => {
const distance = Math.abs(point.x - mouseX) const distance = Math.abs(point.x - mouseX)
if (distance < minDistance) { if (distance < minDistance) {
@ -79,7 +121,6 @@ export const deriveStores = context => {
targetColumn = point.column targetColumn = point.column
} }
}) })
if (targetColumn !== $reorder.targetColumn) { if (targetColumn !== $reorder.targetColumn) {
reorder.update(state => ({ reorder.update(state => ({
...state, ...state,
@ -88,8 +129,35 @@ export const deriveStores = context => {
} }
} }
// Commences auto-scrolling in a certain direction, triggered when the mouse
// approaches the edges of the grid
const startAutoScroll = () => {
if (isAutoScrolling) {
return
}
isAutoScrolling = true
autoScrollInterval = setInterval(() => {
const $maxLeft = get(maxScrollLeft)
const { increment } = get(reorder)
scroll.update(state => ({
...state,
left: Math.max(0, Math.min($maxLeft, state.left + increment)),
}))
considerReorderPosition()
}, 10)
}
// Stops auto scrolling
const stopAutoScroll = () => {
isAutoScrolling = false
clearInterval(autoScrollInterval)
}
// Callback when stopping reordering columns // Callback when stopping reordering columns
const stopReordering = async () => { const stopReordering = async () => {
// Ensure auto-scrolling is stopped
stopAutoScroll()
// Swap position of columns // Swap position of columns
let { sourceColumn, targetColumn } = get(reorder) let { sourceColumn, targetColumn } = get(reorder)
moveColumn(sourceColumn, targetColumn) moveColumn(sourceColumn, targetColumn)

View File

@ -25,14 +25,33 @@ export const createStores = () => {
null null
) )
// Toggles whether a certain row ID is selected or not
const toggleSelectedRow = id => {
selectedRows.update(state => {
let newState = {
...state,
[id]: !state[id],
}
if (!newState[id]) {
delete newState[id]
}
return newState
})
}
return { return {
focusedCellId, focusedCellId,
focusedCellAPI, focusedCellAPI,
focusedRowId, focusedRowId,
previousFocusedRowId, previousFocusedRowId,
selectedRows,
hoveredRowId, hoveredRowId,
rowHeight, rowHeight,
selectedRows: {
...selectedRows,
actions: {
toggleRow: toggleSelectedRow,
},
},
} }
} }

View File

@ -47,7 +47,7 @@
"@apidevtools/swagger-parser": "10.0.3", "@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "0.0.1", "@budibase/backend-core": "0.0.1",
"@budibase/client": "0.0.1", "@budibase/client": "0.0.1",
"@budibase/pro": "0.0.1", "@budibase/pro": "develop",
"@budibase/shared-core": "0.0.1", "@budibase/shared-core": "0.0.1",
"@budibase/string-templates": "0.0.1", "@budibase/string-templates": "0.0.1",
"@budibase/types": "0.0.1", "@budibase/types": "0.0.1",
@ -100,6 +100,7 @@
"mysql2": "2.3.3", "mysql2": "2.3.3",
"node-fetch": "2.6.7", "node-fetch": "2.6.7",
"open": "8.4.0", "open": "8.4.0",
"openai": "^3.2.1",
"pg": "8.10.0", "pg": "8.10.0",
"posthog-node": "1.3.0", "posthog-node": "1.3.0",
"pouchdb": "7.3.0", "pouchdb": "7.3.0",

View File

@ -415,7 +415,7 @@ export async function exportRows(ctx: UserCtx) {
result = await outputProcessing(table, response) result = await outputProcessing(table, response)
} else if (query) { } else if (query) {
let searchResponse = await exports.search(ctx) let searchResponse = await search(ctx)
result = searchResponse.rows result = searchResponse.rows
} }

View File

@ -186,11 +186,7 @@ export async function destroy(ctx: any) {
export async function bulkImport(ctx: any) { export async function bulkImport(ctx: any) {
const db = context.getAppDB() const db = context.getAppDB()
const table = await sdk.tables.getTable(ctx.params.tableId) const table = await sdk.tables.getTable(ctx.params.tableId)
const { rows } = ctx.request.body const { rows, identifierFields } = ctx.request.body
await handleDataImport(ctx.user, table, rows) await handleDataImport(ctx.user, table, rows, identifierFields)
// Ensure auto id and other table updates are persisted
await db.put(table)
return table return table
} }

View File

@ -149,7 +149,12 @@ export function importToRows(
return finalData return finalData
} }
export async function handleDataImport(user: any, table: any, rows: any) { export async function handleDataImport(
user: any,
table: any,
rows: any,
identifierFields: Array<string> = []
) {
const schema: unknown = table.schema const schema: unknown = table.schema
if (!rows || !isRows(rows) || !isSchema(schema)) { if (!rows || !isRows(rows) || !isSchema(schema)) {
@ -161,6 +166,32 @@ export async function handleDataImport(user: any, table: any, rows: any) {
let finalData: any = importToRows(data, table, user) let finalData: any = importToRows(data, table, user)
//Set IDs of finalData to match existing row if an update is expected
if (identifierFields.length > 0) {
const allDocs = await db.allDocs(
getRowParams(table._id, null, {
include_docs: true,
})
)
allDocs.rows
.map(existingRow => existingRow.doc)
.forEach((doc: any) => {
finalData.forEach((finalItem: any) => {
let match = true
for (const field of identifierFields) {
if (finalItem[field] !== doc[field]) {
match = false
break
}
}
if (match) {
finalItem._id = doc._id
finalItem._rev = doc._rev
}
})
})
}
await quotas.addRows(finalData.length, () => db.bulkDocs(finalData), { await quotas.addRows(finalData.length, () => db.bulkDocs(finalData), {
tableId: table._id, tableId: table._id,
}) })

View File

@ -71,10 +71,15 @@ export const BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> =
// ran at all // ran at all
if (env.SELF_HOSTED) { if (env.SELF_HOSTED) {
const bash = require("./steps/bash") const bash = require("./steps/bash")
const openai = require("./steps/openai")
// @ts-ignore // @ts-ignore
ACTION_IMPLS["EXECUTE_BASH"] = bash.run ACTION_IMPLS["EXECUTE_BASH"] = bash.run
// @ts-ignore // @ts-ignore
BUILTIN_ACTION_DEFINITIONS["EXECUTE_BASH"] = bash.definition BUILTIN_ACTION_DEFINITIONS["EXECUTE_BASH"] = bash.definition
ACTION_IMPLS.OPENAI = openai.run
BUILTIN_ACTION_DEFINITIONS.OPENAI = openai.definition
} }
export async function getActionDefinitions() { export async function getActionDefinitions() {

View File

@ -0,0 +1,105 @@
import { Configuration, OpenAIApi } from "openai"
import {
AutomationActionStepId,
AutomationStepSchema,
AutomationStepInput,
AutomationStepType,
AutomationIOType,
} from "@budibase/types"
import * as automationUtils from "../automationUtils"
import environment from "../../environment"
enum Model {
GPT_35_TURBO = "gpt-3.5-turbo",
// will only work with api keys that have access to the GPT4 API
GPT_4 = "gpt-4",
}
export const definition: AutomationStepSchema = {
name: "OpenAI",
tagline: "Send prompts to ChatGPT",
icon: "Algorithm",
description: "Interact with the OpenAI ChatGPT API.",
type: AutomationStepType.ACTION,
internal: true,
stepId: AutomationActionStepId.OPENAI,
inputs: {
prompt: "",
},
schema: {
inputs: {
properties: {
prompt: {
type: AutomationIOType.STRING,
title: "Prompt",
},
model: {
type: AutomationIOType.STRING,
title: "Model",
enum: Object.values(Model),
},
},
required: ["prompt", "model"],
},
outputs: {
properties: {
success: {
type: AutomationIOType.BOOLEAN,
description: "Whether the action was successful",
},
response: {
type: AutomationIOType.STRING,
description: "What was output",
},
},
required: ["success", "response"],
},
},
}
export async function run({ inputs, context }: AutomationStepInput) {
if (!environment.OPENAI_API_KEY) {
return {
success: false,
response:
"OpenAI API Key not configured - please add the OPENAI_API_KEY environment variable.",
}
}
if (inputs.prompt == null) {
return {
success: false,
response: "Budibase OpenAI Automation Failed: No prompt supplied",
}
}
try {
const configuration = new Configuration({
apiKey: environment.OPENAI_API_KEY,
})
const openai = new OpenAIApi(configuration)
const completion = await openai.createChatCompletion({
model: inputs.model,
messages: [
{
role: "user",
content: inputs.prompt,
},
],
})
const response = completion?.data?.choices[0]?.message?.content
return {
response,
success: true,
}
} catch (err) {
return {
success: false,
response: automationUtils.getError(err),
}
}
}

View File

@ -0,0 +1,86 @@
const setup = require("./utilities")
import environment from "../../environment"
import openai from "openai"
jest.mock(
"openai",
jest.fn(() => ({
Configuration: jest.fn(),
OpenAIApi: jest.fn(() => ({
createChatCompletion: jest.fn(() => ({
data: {
choices: [
{
message: {
content: "This is a test",
},
},
],
},
})),
})),
}))
)
const OPENAI_PROMPT = "What is the meaning of life?"
describe("test the openai action", () => {
let config = setup.getConfig()
beforeAll(async () => {
await config.init()
})
beforeEach(() => {
environment.OPENAI_API_KEY = "abc123"
})
afterAll(setup.afterAll)
it("should present the correct error message when the OPENAI_API_KEY variable isn't set", async () => {
delete environment.OPENAI_API_KEY
let res = await setup.runStep("OPENAI", {
prompt: OPENAI_PROMPT,
})
expect(res.response).toEqual(
"OpenAI API Key not configured - please add the OPENAI_API_KEY environment variable."
)
expect(res.success).toBeFalsy()
})
it("should be able to receive a response from ChatGPT given a prompt", async () => {
const res = await setup.runStep("OPENAI", {
prompt: OPENAI_PROMPT,
})
expect(res.response).toEqual("This is a test")
expect(res.success).toBeTruthy()
})
it("should present the correct error message when a prompt is not provided", async () => {
const res = await setup.runStep("OPENAI", {
prompt: null,
})
expect(res.response).toEqual(
"Budibase OpenAI Automation Failed: No prompt supplied"
)
expect(res.success).toBeFalsy()
})
it("should present the correct error message when an error is thrown from the createChatCompletion call", async () => {
openai.OpenAIApi.mockImplementation(() => ({
createChatCompletion: jest.fn(() => {
throw new Error("An error occurred while calling createChatCompletion")
}),
}))
const res = await setup.runStep("OPENAI", {
prompt: OPENAI_PROMPT,
})
expect(res.response).toEqual(
"Error: An error occurred while calling createChatCompletion"
)
expect(res.success).toBeFalsy()
})
})

View File

@ -71,6 +71,7 @@ const environment = {
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL, BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD, BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
PLUGINS_DIR: process.env.PLUGINS_DIR || "/plugins", PLUGINS_DIR: process.env.PLUGINS_DIR || "/plugins",
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
// flags // flags
ALLOW_DEV_AUTOMATIONS: process.env.ALLOW_DEV_AUTOMATIONS, ALLOW_DEV_AUTOMATIONS: process.env.ALLOW_DEV_AUTOMATIONS,
DISABLE_THREADING: process.env.DISABLE_THREADING, DISABLE_THREADING: process.env.DISABLE_THREADING,

View File

@ -5,14 +5,7 @@
"declaration": true, "declaration": true,
"sourceMap": true, "sourceMap": true,
"baseUrl": ".", "baseUrl": ".",
"outDir": "dist", "outDir": "dist"
"paths": {
"@budibase/types": ["../types/src"],
"@budibase/backend-core": ["../backend-core/src"],
"@budibase/backend-core/*": ["../backend-core/*"],
"@budibase/shared-core": ["../shared-core/src"],
"@budibase/pro": ["../pro/packages/pro/src"]
}
}, },
"ts-node": { "ts-node": {
"require": ["tsconfig-paths/register"], "require": ["tsconfig-paths/register"],

View File

@ -57,6 +57,7 @@ export enum AutomationActionStepId {
FILTER = "FILTER", FILTER = "FILTER",
QUERY_ROWS = "QUERY_ROWS", QUERY_ROWS = "QUERY_ROWS",
LOOP = "LOOP", LOOP = "LOOP",
OPENAI = "OPENAI",
// these used to be lowercase step IDs, maintain for backwards compat // these used to be lowercase step IDs, maintain for backwards compat
discord = "discord", discord = "discord",
slack = "slack", slack = "slack",

View File

@ -38,7 +38,7 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "0.0.1", "@budibase/backend-core": "0.0.1",
"@budibase/pro": "0.0.1", "@budibase/pro": "develop",
"@budibase/string-templates": "0.0.1", "@budibase/string-templates": "0.0.1",
"@budibase/types": "0.0.1", "@budibase/types": "0.0.1",
"@koa/router": "8.0.8", "@koa/router": "8.0.8",

View File

@ -17,6 +17,7 @@
"output": "." "output": "."
} }
], ],
"external": ["graphql/*", "deasync", "mock-aws-s3", "nock"],
"format": ["cjs"], "format": ["cjs"],
"esbuildOptions": { "esbuildOptions": {
"outExtension": { "outExtension": {

View File

@ -4,13 +4,7 @@
"composite": true, "composite": true,
"declaration": true, "declaration": true,
"sourceMap": true, "sourceMap": true,
"baseUrl": ".", "baseUrl": "."
"paths": {
"@budibase/types": ["../types/src"],
"@budibase/backend-core": ["../backend-core/src"],
"@budibase/backend-core/*": ["../backend-core/*"],
"@budibase/pro": ["../pro/packages/pro/src"]
}
}, },
"ts-node": { "ts-node": {
"require": ["tsconfig-paths/register"], "require": ["tsconfig-paths/register"],

View File

@ -9,6 +9,9 @@ const path = require("path")
const { build } = require("esbuild") const { build } = require("esbuild")
const { default: NodeResolve } = require("@esbuild-plugins/node-resolve") const { default: NodeResolve } = require("@esbuild-plugins/node-resolve")
const {
default: TsconfigPathsPlugin,
} = require("@esbuild-plugins/tsconfig-paths")
var argv = require("minimist")(process.argv.slice(2)) var argv = require("minimist")(process.argv.slice(2))
@ -23,6 +26,7 @@ function runBuild(entry, outfile) {
sourcemap: isDev, sourcemap: isDev,
tsconfig, tsconfig,
plugins: [ plugins: [
TsconfigPathsPlugin({ tsconfig }),
NodeResolve({ NodeResolve({
extensions: [".ts", ".js"], extensions: [".ts", ".js"],
onResolved: resolved => { onResolved: resolved => {

168
yarn.lock
View File

@ -1728,6 +1728,47 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.6.19-alpha.4":
version "2.6.19-alpha.4"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.6.19-alpha.4.tgz#525dd7ba6c5db4404cf00d1165f79d34a1093826"
integrity sha512-1pfOr+J9xYawedVmvqpQ4b/8C2SQP4cKhFmSz5uErM2SCgbRj+JuzOUTPNX0vzAXPvat/kEegt79xThummDvhA==
dependencies:
"@budibase/nano" "10.1.2"
"@budibase/pouchdb-replication-stream" "1.2.10"
"@budibase/types" "2.6.19-alpha.4"
"@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0"
aws-sdk "2.1030.0"
bcrypt "5.0.1"
bcryptjs "2.4.3"
bull "4.10.1"
correlation-id "4.0.0"
dotenv "16.0.1"
emitter-listener "1.1.2"
ioredis "4.28.0"
joi "17.6.0"
jsonwebtoken "9.0.0"
koa-passport "4.1.4"
koa-pino-logger "4.0.0"
lodash "4.17.21"
lodash.isarguments "3.1.0"
node-fetch "2.6.7"
passport-google-oauth "2.0.0"
passport-jwt "4.0.0"
passport-local "1.0.0"
passport-oauth2-refresh "^2.1.0"
pino "8.11.0"
pino-http "8.3.3"
posthog-node "1.3.0"
pouchdb "7.3.0"
pouchdb-find "7.2.2"
redlock "4.2.0"
sanitize-s3-objectkey "0.0.1"
semver "7.3.7"
tar-fs "2.1.1"
uuid "8.3.2"
"@budibase/bbui@^0.9.139": "@budibase/bbui@^0.9.139":
version "0.9.190" version "0.9.190"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.190.tgz#e1ec400ac90f556bfbc80fc23a04506f1585ea81" resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.190.tgz#e1ec400ac90f556bfbc80fc23a04506f1585ea81"
@ -1828,6 +1869,32 @@
pouchdb-promise "^6.0.4" pouchdb-promise "^6.0.4"
through2 "^2.0.0" through2 "^2.0.0"
"@budibase/pro@develop":
version "2.6.19-alpha.4"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.6.19-alpha.4.tgz#5d4c885ac9ac4ccfb2f8896961aca903c1178750"
integrity sha512-iu2QzV8Z77c00muBSK+NVsZdug3lLD0lR+vcKancGEz1PPE4yNIH7g8jB6i8h9agArbx9S2ICeHQqGb6nQaGHQ==
dependencies:
"@budibase/backend-core" "2.6.19-alpha.4"
"@budibase/shared-core" "2.6.19-alpha.4"
"@budibase/string-templates" "2.6.19-alpha.4"
"@budibase/types" "2.6.19-alpha.4"
"@koa/router" "8.0.8"
bull "4.10.1"
joi "17.6.0"
jsonwebtoken "8.5.1"
lru-cache "^7.14.1"
memorystream "^0.3.1"
node-fetch "^2.6.1"
scim-patch "^0.7.0"
scim2-parse-filter "^0.2.8"
"@budibase/shared-core@2.6.19-alpha.4":
version "2.6.19-alpha.4"
resolved "https://registry.yarnpkg.com/@budibase/shared-core/-/shared-core-2.6.19-alpha.4.tgz#dd22dd0a18ee4d6739b629f461e5caec90706282"
integrity sha512-ac6iWSsgz70OYbdA+QHPLpTnRbIZ4OecVc6Y7gnEZ78hZ4S5da8a+73jswuy0/t4YsiT/gjukjzjoihg3NemVg==
dependencies:
"@budibase/types" "2.6.19-alpha.4"
"@budibase/standard-components@^0.9.139": "@budibase/standard-components@^0.9.139":
version "0.9.139" version "0.9.139"
resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.139.tgz#cf8e2b759ae863e469e50272b3ca87f2827e66e3" resolved "https://registry.yarnpkg.com/@budibase/standard-components/-/standard-components-0.9.139.tgz#cf8e2b759ae863e469e50272b3ca87f2827e66e3"
@ -1846,6 +1913,25 @@
svelte-apexcharts "^1.0.2" svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0" svelte-flatpickr "^3.1.0"
"@budibase/string-templates@2.6.19-alpha.4":
version "2.6.19-alpha.4"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.6.19-alpha.4.tgz#92ebd69a6841174b8af91f338c4754ca7c402707"
integrity sha512-KsH3NlQcJibRj98Q8zQ3KQHhfSIWPQfvR80MmBTIe05llEZGox4re4pQQUnlMafaUEyNNtIqVnbTJ1XP0LmFng==
dependencies:
"@budibase/handlebars-helpers" "^0.11.8"
dayjs "^1.10.4"
handlebars "^4.7.6"
handlebars-utils "^1.0.6"
lodash "^4.17.20"
vm2 "^3.9.15"
"@budibase/types@2.6.19-alpha.4":
version "2.6.19-alpha.4"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.6.19-alpha.4.tgz#bcf81699329d3f8509e4b0a489211f35b6cfa7ce"
integrity sha512-qFsXHZTSigcfCv02aTZGsf17vBT/MC+zK9ky7WZVX4h0sJiE0li4A66/tMaSDz3/vQ7ToPRhJK/p+LOWA/oceg==
dependencies:
scim-patch "^0.7.0"
"@bull-board/api@3.7.0": "@bull-board/api@3.7.0":
version "3.7.0" version "3.7.0"
resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.7.0.tgz#231f687187c0cb34e0b97f463917b6aaeb4ef6af" resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-3.7.0.tgz#231f687187c0cb34e0b97f463917b6aaeb4ef6af"
@ -2047,6 +2133,15 @@
escape-string-regexp "^4.0.0" escape-string-regexp "^4.0.0"
resolve "^1.19.0" resolve "^1.19.0"
"@esbuild-plugins/tsconfig-paths@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@esbuild-plugins/tsconfig-paths/-/tsconfig-paths-0.1.2.tgz#1955de0a124ecf4364717a2fadbfbea876955232"
integrity sha512-TusFR26Y+Ze+Zm+NdfqZTSG4XyrXKxIaAfYCL3jASEI/gHjSdoCujATjzNWaaXs6Sk6Bv2D7NLr4Jdz1gysy/Q==
dependencies:
debug "^4.3.1"
find-up "^5.0.0"
strip-json-comments "^3.1.1"
"@esbuild/android-arm64@0.16.17": "@esbuild/android-arm64@0.16.17":
version "0.16.17" version "0.16.17"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23"
@ -3448,11 +3543,6 @@
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
"@jridgewell/sourcemap-codec@^1.4.13":
version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@0.3.9": "@jridgewell/trace-mapping@0.3.9":
version "0.3.9" version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
@ -4233,7 +4323,7 @@
dependencies: dependencies:
slash "^3.0.0" slash "^3.0.0"
"@rollup/plugin-commonjs@16.0.0", "@rollup/plugin-commonjs@^16.0.0": "@rollup/plugin-commonjs@^16.0.0":
version "16.0.0" version "16.0.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz#169004d56cd0f0a1d0f35915d31a036b0efe281f"
integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw== integrity sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw==
@ -4316,22 +4406,6 @@
"@rollup/pluginutils" "^3.1.0" "@rollup/pluginutils" "^3.1.0"
magic-string "^0.25.7" magic-string "^0.25.7"
"@rollup/plugin-replace@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.2.tgz#45f53501b16311feded2485e98419acb8448c61d"
integrity sha512-M9YXNekv/C/iHHK+cvORzfRYfPbq0RDD8r0G+bMiTXjNGKulPnCT9O3Ss46WfhI6ZOCgApOP7xAdmCQJ+U2LAA==
dependencies:
"@rollup/pluginutils" "^5.0.1"
magic-string "^0.27.0"
"@rollup/plugin-typescript@8.3.0":
version "8.3.0"
resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz#bc1077fa5897b980fc27e376c4e377882c63e68b"
integrity sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==
dependencies:
"@rollup/pluginutils" "^3.1.0"
resolve "^1.17.0"
"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0":
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
@ -12094,6 +12168,14 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0" locate-path "^5.0.0"
path-exists "^4.0.0" path-exists "^4.0.0"
find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
dependencies:
locate-path "^6.0.0"
path-exists "^4.0.0"
findit2@^2.2.3: findit2@^2.2.3:
version "2.2.3" version "2.2.3"
resolved "https://registry.yarnpkg.com/findit2/-/findit2-2.2.3.tgz#58a466697df8a6205cdfdbf395536b8bd777a5f6" resolved "https://registry.yarnpkg.com/findit2/-/findit2-2.2.3.tgz#58a466697df8a6205cdfdbf395536b8bd777a5f6"
@ -12360,7 +12442,7 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2:
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@ -17021,6 +17103,13 @@ locate-path@^5.0.0:
dependencies: dependencies:
p-locate "^4.1.0" p-locate "^4.1.0"
locate-path@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
dependencies:
p-locate "^5.0.0"
lodash-es@^4.17.11: lodash-es@^4.17.11:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
@ -17401,13 +17490,6 @@ magic-string@^0.26.2:
dependencies: dependencies:
sourcemap-codec "^1.4.8" sourcemap-codec "^1.4.8"
magic-string@^0.27.0:
version "0.27.0"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3"
integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.13"
make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0: make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
@ -19163,6 +19245,14 @@ open@^8.4.0:
is-docker "^2.1.1" is-docker "^2.1.1"
is-wsl "^2.2.0" is-wsl "^2.2.0"
openai@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/openai/-/openai-3.2.1.tgz#1fa35bdf979cbde8453b43f2dd3a7d401ee40866"
integrity sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==
dependencies:
axios "^0.26.0"
form-data "^4.0.0"
openapi-response-validator@^9.2.0: openapi-response-validator@^9.2.0:
version "9.3.1" version "9.3.1"
resolved "https://registry.yarnpkg.com/openapi-response-validator/-/openapi-response-validator-9.3.1.tgz#54284d8be608ef53283cbe7448accce8106b1c56" resolved "https://registry.yarnpkg.com/openapi-response-validator/-/openapi-response-validator-9.3.1.tgz#54284d8be608ef53283cbe7448accce8106b1c56"
@ -19385,7 +19475,7 @@ p-limit@^2.0.0, p-limit@^2.2.0:
dependencies: dependencies:
p-try "^2.0.0" p-try "^2.0.0"
p-limit@^3.1.0: p-limit@^3.0.2, p-limit@^3.1.0:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
@ -19420,6 +19510,13 @@ p-locate@^4.1.0:
dependencies: dependencies:
p-limit "^2.2.0" p-limit "^2.2.0"
p-locate@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
dependencies:
p-limit "^3.0.2"
p-map-series@2.1.0: p-map-series@2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-2.1.0.tgz#7560d4c452d9da0c07e692fdbfe6e2c81a2a91f2"
@ -22224,13 +22321,6 @@ rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.6.0,
dependencies: dependencies:
estree-walker "^0.6.1" estree-walker "^0.6.1"
rollup@2.45.2:
version "2.45.2"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.45.2.tgz#8fb85917c9f35605720e92328f3ccbfba6f78b48"
integrity sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ==
optionalDependencies:
fsevents "~2.3.1"
rollup@^2.36.2, rollup@^2.44.0, rollup@^2.45.2, rollup@^2.79.1: rollup@^2.36.2, rollup@^2.44.0, rollup@^2.45.2, rollup@^2.79.1:
version "2.79.1" version "2.79.1"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7"
@ -24236,7 +24326,7 @@ timed-out@^4.0.1:
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==
timekeeper@2.2.0, timekeeper@^2.2.0: timekeeper@2.2.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368" resolved "https://registry.yarnpkg.com/timekeeper/-/timekeeper-2.2.0.tgz#9645731fce9e3280a18614a57a9d1b72af3ca368"
integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A== integrity sha512-W3AmPTJWZkRwu+iSNxPIsLZ2ByADsOLbbLxe46UJyWj3mlYLlwucKiq+/dPm0l9wTzqoF3/2PH0AGFCebjq23A==