@budibase/frontend-core 3.23.37 → 3.23.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@budibase/frontend-core",
3
- "version": "3.23.37",
3
+ "version": "3.23.47",
4
4
  "description": "Budibase frontend core libraries used in builder and client",
5
5
  "author": "Budibase",
6
6
  "license": "MPL-2.0",
@@ -18,5 +18,5 @@
18
18
  "shortid": "2.2.15",
19
19
  "socket.io-client": "^4.7.5"
20
20
  },
21
- "gitHead": "6d158cf61da4778f9ce8d78c63da72593791237d"
21
+ "gitHead": "534845c38ed0fe209406ebe45fd87f3de79f1da5"
22
22
  }
package/src/api/agents.ts CHANGED
@@ -1,14 +1,11 @@
1
1
  import {
2
2
  AgentChat,
3
- AgentToolSource,
4
- AgentToolSourceWithTools,
5
3
  ChatAgentRequest,
6
4
  CreateAgentRequest,
7
5
  CreateAgentResponse,
8
- CreateToolSourceRequest,
9
6
  FetchAgentHistoryResponse,
10
7
  FetchAgentsResponse,
11
- Tool,
8
+ ToolMetadata,
12
9
  UpdateAgentRequest,
13
10
  UpdateAgentResponse,
14
11
  } from "@budibase/types"
@@ -27,19 +24,23 @@ export interface AgentEndpoints {
27
24
  removeChat: (chatId: string) => Promise<void>
28
25
  fetchChats: (agentId: string) => Promise<FetchAgentHistoryResponse>
29
26
 
30
- fetchToolSources: (agentId: string) => Promise<AgentToolSourceWithTools[]>
31
- fetchAvailableTools: (toolSourceType: string) => Promise<Tool[]>
32
- createToolSource: (
33
- toolSource: CreateToolSourceRequest
34
- ) => Promise<{ created: true }>
35
- updateToolSource: (toolSource: AgentToolSource) => Promise<AgentToolSource>
36
- deleteToolSource: (toolSourceId: string) => Promise<{ deleted: true }>
27
+ fetchTools: () => Promise<ToolMetadata[]>
37
28
  fetchAgents: () => Promise<FetchAgentsResponse>
38
29
  createAgent: (agent: CreateAgentRequest) => Promise<CreateAgentResponse>
39
30
  updateAgent: (agent: UpdateAgentRequest) => Promise<UpdateAgentResponse>
40
31
  deleteAgent: (agentId: string) => Promise<{ deleted: true }>
41
32
  }
42
33
 
34
+ const throwOnErrorChunk = () =>
35
+ new TransformStream<UIMessageChunk, UIMessageChunk>({
36
+ transform(chunk, controller) {
37
+ if (chunk.type === "error") {
38
+ throw new Error(chunk.errorText || "Agent action failed")
39
+ }
40
+ controller.enqueue(chunk)
41
+ },
42
+ })
43
+
43
44
  export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
44
45
  agentChatStream: async (chat, workspaceId) => {
45
46
  const body: ChatAgentRequest = chat
@@ -69,8 +70,12 @@ export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
69
70
  const chunkStream = response.body
70
71
  .pipeThrough(new TextDecoderStream())
71
72
  .pipeThrough(createSseToJsonTransformStream<UIMessageChunk>())
73
+ .pipeThrough(throwOnErrorChunk())
72
74
 
73
- return readUIMessageStream({ stream: chunkStream })
75
+ return readUIMessageStream({
76
+ stream: chunkStream,
77
+ terminateOnError: true,
78
+ })
74
79
  },
75
80
 
76
81
  removeChat: async (chatId: string) => {
@@ -85,35 +90,9 @@ export const buildAgentEndpoints = (API: BaseAPIClient): AgentEndpoints => ({
85
90
  })
86
91
  },
87
92
 
88
- fetchToolSources: async (agentId: string) => {
93
+ fetchTools: async () => {
89
94
  return await API.get({
90
- url: `/api/agent/${agentId}/toolsource`,
91
- })
92
- },
93
-
94
- fetchAvailableTools: async (toolSourceType: string) => {
95
- return await API.get({
96
- url: `/api/agent/toolsource/${toolSourceType}/tools`,
97
- })
98
- },
99
-
100
- createToolSource: async (toolSource: CreateToolSourceRequest) => {
101
- return await API.post({
102
- url: "/api/agent/toolsource",
103
- body: toolSource as any,
104
- })
105
- },
106
-
107
- updateToolSource: async (toolSource: AgentToolSource) => {
108
- return await API.put({
109
- url: "/api/agent/toolsource",
110
- body: toolSource as any,
111
- })
112
- },
113
-
114
- deleteToolSource: async (toolSourceId: string) => {
115
- return await API.delete({
116
- url: `/api/agent/toolsource/${toolSourceId}`,
95
+ url: `/api/agent/tools`,
117
96
  })
118
97
  },
119
98
 
@@ -10,6 +10,7 @@ import {
10
10
  SearchAutomationLogsResponse,
11
11
  TestAutomationRequest,
12
12
  TestAutomationResponse,
13
+ TestProgressState,
13
14
  TriggerAutomationRequest,
14
15
  TriggerAutomationResponse,
15
16
  UpdateAutomationRequest,
@@ -40,8 +41,10 @@ export interface AutomationEndpoints {
40
41
  ) => Promise<TriggerAutomationResponse>
41
42
  testAutomation: (
42
43
  automationdId: string,
43
- data: TestAutomationRequest
44
+ data: TestAutomationRequest,
45
+ options?: { async?: boolean }
44
46
  ) => Promise<TestAutomationResponse>
47
+ getAutomationTestStatus: (automationId: string) => Promise<TestProgressState>
45
48
  getAutomationDefinitions: () => Promise<GetAutomationStepDefinitionsResponse>
46
49
  getAutomationLogs: (
47
50
  options: SearchAutomationLogsRequest
@@ -69,13 +72,25 @@ export const buildAutomationEndpoints = (
69
72
  * @param automationId the ID of the automation to test
70
73
  * @param data the test data to run against the automation
71
74
  */
72
- testAutomation: async (automationId, data) => {
75
+ testAutomation: async (automationId, data, options = {}) => {
76
+ const params = new URLSearchParams()
77
+ if (options.async) {
78
+ params.set("async", "true")
79
+ }
80
+ const qs = params.toString()
81
+ const url = `/api/automations/${automationId}/test${qs ? `?${qs}` : ""}`
73
82
  return await API.post({
74
- url: `/api/automations/${automationId}/test`,
83
+ url,
75
84
  body: data,
76
85
  })
77
86
  },
78
87
 
88
+ getAutomationTestStatus: async automationId => {
89
+ return await API.get({
90
+ url: `/api/automations/${automationId}/test/status`,
91
+ })
92
+ },
93
+
79
94
  /**
80
95
  * Gets a list of all automations.
81
96
  */
@@ -7,6 +7,8 @@ import {
7
7
  DeleteDatasourceResponse,
8
8
  FetchDatasourceInfoRequest,
9
9
  FetchDatasourceInfoResponse,
10
+ FetchDatasourceRelationshipInfoRequest,
11
+ FetchDatasourceRelationshipInfoResponse,
10
12
  FetchDatasourceViewInfoRequest,
11
13
  FetchDatasourceViewInfoResponse,
12
14
  UpdateDatasourceRequest,
@@ -41,6 +43,9 @@ export interface DatasourceEndpoints {
41
43
  fetchViewInfoForDatasource: (
42
44
  datasource: Datasource
43
45
  ) => Promise<FetchDatasourceViewInfoResponse>
46
+ fetchRelationshipInfoForDatasource: (
47
+ datasource: Datasource
48
+ ) => Promise<FetchDatasourceRelationshipInfoResponse>
44
49
  }
45
50
 
46
51
  export const buildDatasourceEndpoints = (
@@ -134,4 +139,17 @@ export const buildDatasourceEndpoints = (
134
139
  body: { datasource },
135
140
  })
136
141
  },
142
+
143
+ /**
144
+ * Fetch relationship names available within the datasource
145
+ */
146
+ fetchRelationshipInfoForDatasource: async (datasource: Datasource) => {
147
+ return await API.post<
148
+ FetchDatasourceRelationshipInfoRequest,
149
+ FetchDatasourceRelationshipInfoResponse
150
+ >({
151
+ url: `/api/datasources/relationships`,
152
+ body: { datasource },
153
+ })
154
+ },
137
155
  })
package/src/api/tables.ts CHANGED
@@ -18,6 +18,8 @@ import {
18
18
  MigrateTableResponse,
19
19
  MigrateTableRequest,
20
20
  DeleteTableResponse,
21
+ PublishTableRequest,
22
+ PublishTableResponse,
21
23
  } from "@budibase/types"
22
24
  import { BaseAPIClient } from "./types"
23
25
 
@@ -52,6 +54,10 @@ export interface TableEndpoints {
52
54
  oldColumn: string,
53
55
  newColumn: string
54
56
  ) => Promise<MigrateTableResponse>
57
+ publishTable: (
58
+ tableId: string,
59
+ opts?: PublishTableRequest
60
+ ) => Promise<PublishTableResponse>
55
61
  }
56
62
 
57
63
  export const buildTableEndpoints = (API: BaseAPIClient): TableEndpoints => ({
@@ -191,6 +197,13 @@ export const buildTableEndpoints = (API: BaseAPIClient): TableEndpoints => ({
191
197
  })
192
198
  },
193
199
 
200
+ publishTable: async (tableId, opts) => {
201
+ return await API.post<PublishTableRequest, PublishTableResponse>({
202
+ url: `/api/tables/${tableId}/publish`,
203
+ body: opts,
204
+ })
205
+ },
206
+
194
207
  /**
195
208
  * Duplicates a table without its data.
196
209
  * @param tableId the ID of the table to duplicate
@@ -16,12 +16,16 @@
16
16
  export let chat: AgentChat
17
17
  export let loading: boolean = false
18
18
 
19
- const dispatch = createEventDispatcher<{ chatSaved: { chatId: string } }>()
19
+ const dispatch = createEventDispatcher<{
20
+ chatSaved: { chatId?: string; chat: AgentChat }
21
+ }>()
20
22
 
21
23
  let inputValue = ""
22
24
  let chatAreaElement: HTMLDivElement
23
25
  let observer: MutationObserver
24
26
  let textareaElement: HTMLTextAreaElement
27
+ let lastFocusedChatId: string | undefined
28
+ let lastFocusedNewChat: AgentChat | undefined
25
29
 
26
30
  $: if (chat?.messages?.length) {
27
31
  scrollToBottom()
@@ -75,7 +79,7 @@
75
79
  }
76
80
 
77
81
  loading = false
78
- dispatch("chatSaved", { chatId: chat._id || "" })
82
+ dispatch("chatSaved", { chatId: chat._id, chat })
79
83
  } catch (err: any) {
80
84
  console.error(err)
81
85
  notifications.error(err.message)
@@ -111,6 +115,22 @@
111
115
  }
112
116
  })
113
117
 
118
+ $: {
119
+ const currentId = chat?._id
120
+ const isNewChat =
121
+ !currentId && (!chat?.messages || chat.messages.length === 0)
122
+ const shouldFocus =
123
+ textareaElement &&
124
+ ((currentId && currentId !== lastFocusedChatId) ||
125
+ (isNewChat && chat && chat !== lastFocusedNewChat))
126
+
127
+ if (shouldFocus) {
128
+ tick().then(() => textareaElement?.focus())
129
+ lastFocusedChatId = currentId
130
+ lastFocusedNewChat = isNewChat ? chat : undefined
131
+ }
132
+ }
133
+
114
134
  onDestroy(() => {
115
135
  observer.disconnect()
116
136
  })
@@ -6,7 +6,12 @@
6
6
  import { getColumnIcon } from "../../../utils/schema"
7
7
  import MigrationModal from "../controls/MigrationModal.svelte"
8
8
  import { debounce } from "../../../utils/utils"
9
- import { FieldType, FormulaType, SortOrder } from "@budibase/types"
9
+ import {
10
+ FieldType,
11
+ FormulaType,
12
+ SortOrder,
13
+ isStaticFormula,
14
+ } from "@budibase/types"
10
15
  import { TableNames } from "../../../constants"
11
16
  import GridPopover from "../overlays/GridPopover.svelte"
12
17
 
@@ -73,7 +78,11 @@
73
78
  descending: "high-low",
74
79
  }
75
80
  }
76
- switch (column?.schema?.type) {
81
+ let schemaType = column.schema?.type
82
+ if (isStaticFormula(column.schema)) {
83
+ schemaType = column.schema.responseType
84
+ }
85
+ switch (schemaType) {
77
86
  case FieldType.NUMBER:
78
87
  case FieldType.BIGINT:
79
88
  return {
@@ -11,10 +11,19 @@
11
11
 
12
12
  let input
13
13
  let active = false
14
+ let isComposing = false
14
15
 
15
16
  $: editable = focused && !readonly
16
17
  $: displayValue = format?.(value) ?? value ?? ""
17
18
 
19
+ const handleCompositionStart = () => {
20
+ isComposing = true
21
+ }
22
+
23
+ const handleCompositionEnd = () => {
24
+ isComposing = false
25
+ }
26
+
18
27
  const handleChange = e => {
19
28
  onChange(e.target.value)
20
29
  }
@@ -23,6 +32,9 @@
23
32
  if (!active) {
24
33
  return false
25
34
  }
35
+ if (e.key === "Enter" && (isComposing || e.isComposing)) {
36
+ return true
37
+ }
26
38
  if (e.key === "Enter") {
27
39
  input?.blur()
28
40
  const event = new KeyboardEvent("keydown", { key: "ArrowDown" })
@@ -46,6 +58,8 @@
46
58
  bind:this={input}
47
59
  on:focus={() => (active = true)}
48
60
  on:blur={() => (active = false)}
61
+ on:compositionstart={handleCompositionStart}
62
+ on:compositionend={handleCompositionEnd}
49
63
  {type}
50
64
  value={value ?? ""}
51
65
  on:change={handleChange}
@@ -46,6 +46,10 @@
46
46
  }
47
47
  }
48
48
 
49
+ if (e.key === "Enter" && e.isComposing) {
50
+ return
51
+ }
52
+
49
53
  // Sugar for preventing default
50
54
  const handle = fn => {
51
55
  e.preventDefault()
@@ -35,6 +35,7 @@ interface RowStore {
35
35
  interface RowDerivedStore {
36
36
  rows: RowStore["rows"]
37
37
  rowLookupMap: Readable<Record<string, IndexedUIRow>>
38
+ rowCount: Readable<number>
38
39
  }
39
40
 
40
41
  interface RowActionStore {
@@ -168,12 +169,15 @@ export const deriveStores = (context: StoreContext): RowDerivedStore => {
168
169
  return map
169
170
  })
170
171
 
172
+ const rowCount = derived(rows, $rows => $rows.length)
173
+
171
174
  return {
172
175
  rows: {
173
176
  ...rows,
174
177
  subscribe: enrichedRows.subscribe,
175
178
  },
176
179
  rowLookupMap,
180
+ rowCount,
177
181
  }
178
182
  }
179
183
 
@@ -241,6 +241,7 @@ export default abstract class BaseDataFetch<
241
241
  if (
242
242
  fieldSchema?.type === FieldType.NUMBER ||
243
243
  fieldSchema?.type === FieldType.BIGINT ||
244
+ fieldSchema?.responseType === FieldType.NUMBER ||
244
245
  ("calculationType" in fieldSchema && fieldSchema?.calculationType)
245
246
  ) {
246
247
  this.options.sortType = SortType.NUMBER
@@ -1,6 +1,7 @@
1
1
  import * as sharedCore from "@budibase/shared-core"
2
+ import { UIFieldSchema, isStaticFormula } from "@budibase/types"
2
3
 
3
- export function canBeDisplayColumn(column) {
4
+ export function canBeDisplayColumn(column: UIFieldSchema) {
4
5
  if (!sharedCore.canBeDisplayColumn(column.type)) {
5
6
  return false
6
7
  }
@@ -11,16 +12,20 @@ export function canBeDisplayColumn(column) {
11
12
  return true
12
13
  }
13
14
 
14
- export function canBeSortColumn(column) {
15
+ export function canBeSortColumn(columnSchema: UIFieldSchema) {
15
16
  // Allow sorting by calculation columns
16
- if (column.calculationType) {
17
+ if (columnSchema.calculationType) {
17
18
  return true
18
19
  }
19
- if (!sharedCore.canBeSortColumn(column.type)) {
20
+ // Allow static-only formula columns to be sorted
21
+ if (isStaticFormula(columnSchema)) {
22
+ return true
23
+ }
24
+ if (!sharedCore.canBeSortColumn(columnSchema.type)) {
20
25
  return false
21
26
  }
22
27
  // If it's a related column (only available in the frontend), don't allow using it as display column
23
- if (column.related) {
28
+ if (columnSchema.related) {
24
29
  return false
25
30
  }
26
31
  return true