mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-24 16:01:42 +00:00
fix: flow status reactivity improvement (#6402)
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
} from '$lib/gen'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { setCopilotInfo, userStore, workspaceStore } from '$lib/stores'
|
||||
import { emptySchema, readFieldsRecursively, sendUserToast } from '$lib/utils'
|
||||
import { emptySchema, readFieldsRecursively, sendUserToast, type StateStore } from '$lib/utils'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import { onDestroy, onMount, setContext, untrack } from 'svelte'
|
||||
import DarkModeToggle from '$lib/components/sidebar/DarkModeToggle.svelte'
|
||||
@@ -27,7 +27,7 @@
|
||||
import FlowModuleSchemaMap from './flows/map/FlowModuleSchemaMap.svelte'
|
||||
import FlowEditorPanel from './flows/content/FlowEditorPanel.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { writable } from 'svelte/store'
|
||||
import type { FlowState } from './flows/flowState'
|
||||
import { initHistory } from '$lib/history.svelte'
|
||||
import type { FlowEditorContext, FlowInput, FlowInputEditorState } from './flows/types'
|
||||
@@ -51,7 +51,6 @@
|
||||
import { TestSteps } from './flows/testSteps.svelte'
|
||||
import { ModulesTestStates } from './modulesTest.svelte'
|
||||
import type { GraphModuleState } from './graph'
|
||||
import { updateDerivedModuleStatesFromTestJobs } from './flows/utils'
|
||||
|
||||
let flowCopilotContext: FlowCopilotContext = {
|
||||
shouldUpdatePropertyType: writable<{
|
||||
@@ -116,7 +115,6 @@
|
||||
const flowPreviewContent = $derived(flowPreviewButtons?.getFlowPreviewContent())
|
||||
const job: Job | undefined = $derived(flowPreviewContent?.getJob())
|
||||
let showJobStatus = $state(false)
|
||||
let testModuleId: string | undefined = $state(undefined)
|
||||
|
||||
type LastEditScript = {
|
||||
content: string
|
||||
@@ -453,7 +451,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const flowStateStore = writable({} as FlowState)
|
||||
const flowStateStore = $state({ val: {} }) as StateStore<FlowState>
|
||||
|
||||
const previewArgsStore = $state({ val: {} })
|
||||
const scriptEditorDrawer = writable(undefined)
|
||||
@@ -464,8 +462,6 @@
|
||||
const triggersCount = writable<TriggersCount | undefined>(undefined)
|
||||
const modulesTestStates = new ModulesTestStates((moduleId) => {
|
||||
// Update the derived store with test job states
|
||||
delete $derivedModuleStates[moduleId]
|
||||
testModuleId = moduleId
|
||||
showJobStatus = false
|
||||
})
|
||||
const outputPickerOpenFns: Record<string, () => void> = $state({})
|
||||
@@ -538,11 +534,11 @@
|
||||
}
|
||||
|
||||
mod.value.input_transforms = input_transforms
|
||||
if (!deepEqual(schema, $flowStateStore[mod.id]?.schema)) {
|
||||
if (!$flowStateStore[mod.id]) {
|
||||
$flowStateStore[mod.id] = { schema }
|
||||
if (!deepEqual(schema, flowStateStore.val[mod.id]?.schema)) {
|
||||
if (!flowStateStore.val[mod.id]) {
|
||||
flowStateStore.val[mod.id] = { schema }
|
||||
} else {
|
||||
$flowStateStore[mod.id].schema = schema
|
||||
flowStateStore.val[mod.id].schema = schema
|
||||
}
|
||||
reload++
|
||||
}
|
||||
@@ -586,25 +582,12 @@
|
||||
$selectedIdStore && untrack(() => inferModuleArgs($selectedIdStore))
|
||||
})
|
||||
|
||||
const localModuleStates: Writable<Record<string, GraphModuleState>> = $derived(
|
||||
flowPreviewContent?.getLocalModuleStates() ?? writable({})
|
||||
)
|
||||
let localModuleStates: Record<string, GraphModuleState> = $state({})
|
||||
|
||||
const suspendStatus: Writable<Record<string, { job: Job; nb: number }>> = $derived(
|
||||
flowPreviewContent?.getSuspendStatus() ?? writable({})
|
||||
)
|
||||
let suspendStatus: StateStore<Record<string, { job: Job; nb: number }>> = $state({ val: {} })
|
||||
|
||||
// Create a derived store that only shows the module states when showModuleStatus is true
|
||||
// this store can also be updated
|
||||
let derivedModuleStates = writable<Record<string, GraphModuleState>>({})
|
||||
$effect(() => {
|
||||
derivedModuleStates.update((currentStates) => {
|
||||
return showJobStatus ? $localModuleStates : currentStates
|
||||
})
|
||||
})
|
||||
$effect(() => {
|
||||
updateDerivedModuleStatesFromTestJobs(testModuleId, modulesTestStates, derivedModuleStates)
|
||||
})
|
||||
|
||||
let flowModuleSchemaMap: FlowModuleSchemaMap | undefined = $state()
|
||||
function onJobDone() {
|
||||
@@ -639,14 +622,9 @@
|
||||
}
|
||||
|
||||
function resetModulesStates() {
|
||||
derivedModuleStates.set({})
|
||||
showJobStatus = false
|
||||
}
|
||||
|
||||
const individualStepTests = $derived(
|
||||
!(showJobStatus && job) && Object.keys($derivedModuleStates).length > 0
|
||||
)
|
||||
|
||||
const flowHasChanged = $derived(flowPreviewContent?.flowHasChanged())
|
||||
</script>
|
||||
|
||||
@@ -785,7 +763,7 @@
|
||||
bind:this={flowPreviewButtons}
|
||||
{onJobDone}
|
||||
onRunPreview={() => {
|
||||
localModuleStates.set({})
|
||||
localModuleStates = {}
|
||||
showJobStatus = true
|
||||
}}
|
||||
/>
|
||||
@@ -800,19 +778,20 @@
|
||||
disableTutorials
|
||||
smallErrorHandler={true}
|
||||
disableStaticInputs
|
||||
localModuleStates={derivedModuleStates}
|
||||
{localModuleStates}
|
||||
onTestUpTo={flowPreviewButtons?.testUpTo}
|
||||
testModuleStates={modulesTestStates}
|
||||
isOwner={flowPreviewContent?.getIsOwner?.()}
|
||||
onTestFlow={flowPreviewButtons?.runPreview}
|
||||
isRunning={flowPreviewContent?.getIsRunning?.()}
|
||||
onCancelTestFlow={flowPreviewContent?.cancelTest}
|
||||
onOpenPreview={flowPreviewButtons?.openPreview}
|
||||
onHideJobStatus={resetModulesStates}
|
||||
{individualStepTests}
|
||||
flowJob={job}
|
||||
{showJobStatus}
|
||||
onDelete={(id) => {
|
||||
delete $derivedModuleStates[id]
|
||||
delete localModuleStates[id]
|
||||
delete modulesTestStates.states[id]
|
||||
}}
|
||||
{flowHasChanged}
|
||||
/>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
let mod: any | undefined = $state(undefined)
|
||||
async function loadSchema() {
|
||||
try {
|
||||
const res = await getFirstStepSchema($flowStateStore, flowStore.val)
|
||||
const res = await getFirstStepSchema(flowStateStore.val, flowStore.val)
|
||||
schema = res.schema
|
||||
mod = res.mod
|
||||
dispatch('connectFirstNode', { connectFirstNode: res.connectFirstNode })
|
||||
@@ -28,7 +28,7 @@
|
||||
}
|
||||
}
|
||||
$effect(() => {
|
||||
flowStore.val && $flowStateStore && untrack(() => loadSchema())
|
||||
flowStore.val && flowStateStore && untrack(() => loadSchema())
|
||||
})
|
||||
|
||||
function handleClick() {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
orderedJsonStringify,
|
||||
readFieldsRecursively,
|
||||
replaceFalseWithUndefined,
|
||||
type StateStore,
|
||||
type Value
|
||||
} from '$lib/utils'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
@@ -33,7 +34,7 @@
|
||||
import AIChangesWarningModal from '$lib/components/copilot/chat/flow/AIChangesWarningModal.svelte'
|
||||
|
||||
import { onMount, setContext, untrack, type ComponentType } from 'svelte'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { writable } from 'svelte/store'
|
||||
import CenteredPage from './CenteredPage.svelte'
|
||||
import { Badge, Button, UndoRedo } from './common'
|
||||
import FlowEditor from './flows/FlowEditor.svelte'
|
||||
@@ -42,7 +43,7 @@
|
||||
import FlowImportExportMenu from './flows/header/FlowImportExportMenu.svelte'
|
||||
import FlowPreviewButtons from './flows/header/FlowPreviewButtons.svelte'
|
||||
import type { FlowEditorContext, FlowInput, FlowInputEditorState } from './flows/types'
|
||||
import { cleanInputs, updateDerivedModuleStatesFromTestJobs } from './flows/utils'
|
||||
import { cleanInputs } from './flows/utils'
|
||||
import {
|
||||
Calendar,
|
||||
Pen,
|
||||
@@ -577,13 +578,7 @@
|
||||
}
|
||||
|
||||
let insertButtonOpen = writable<boolean>(false)
|
||||
let testModuleId: string | undefined = $state(undefined)
|
||||
let modulesTestStates = new ModulesTestStates((moduleId) => {
|
||||
// Update the derived store with test job states
|
||||
delete $derivedModuleStates[moduleId]
|
||||
testModuleId = moduleId
|
||||
showJobStatus = false
|
||||
})
|
||||
let modulesTestStates = new ModulesTestStates()
|
||||
let outputPickerOpenFns: Record<string, () => void> = $state({})
|
||||
let flowEditor: FlowEditor | undefined = $state(undefined)
|
||||
|
||||
@@ -933,33 +928,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
const localModuleStates: Writable<Record<string, GraphModuleState>> = $derived(
|
||||
flowPreviewContent?.getLocalModuleStates() ?? writable({})
|
||||
)
|
||||
const suspendStatus: Writable<Record<string, { job: Job; nb: number }>> = $derived(
|
||||
flowPreviewContent?.getSuspendStatus() ?? writable({})
|
||||
)
|
||||
|
||||
// Create a derived store that only shows the module states when showModuleStatus is true
|
||||
// this store can also be updated
|
||||
let derivedModuleStates = writable<Record<string, GraphModuleState>>({})
|
||||
$effect(() => {
|
||||
derivedModuleStates.update((currentStates) => {
|
||||
return showJobStatus ? $localModuleStates : currentStates
|
||||
})
|
||||
})
|
||||
$effect(() => {
|
||||
updateDerivedModuleStatesFromTestJobs(testModuleId, modulesTestStates, derivedModuleStates)
|
||||
})
|
||||
|
||||
function resetModulesStates() {
|
||||
derivedModuleStates.set({})
|
||||
showJobStatus = false
|
||||
}
|
||||
|
||||
const individualStepTests = $derived(
|
||||
!(showJobStatus && job) && Object.keys($derivedModuleStates).length > 0
|
||||
)
|
||||
let localModuleStates: Record<string, GraphModuleState> = $state({})
|
||||
let suspendStatus: StateStore<Record<string, { job: Job; nb: number }>> = $state({ val: {} })
|
||||
|
||||
const flowHasChanged = $derived(flowPreviewContent?.flowHasChanged())
|
||||
</script>
|
||||
@@ -1025,7 +995,7 @@
|
||||
for (const mod of restoredModules) {
|
||||
if (mod) {
|
||||
try {
|
||||
loadFlowModuleState(mod).then((state) => ($flowStateStore[mod.id] = state))
|
||||
loadFlowModuleState(mod).then((state) => (flowStateStore.val[mod.id] = state))
|
||||
} catch (e) {
|
||||
console.error('Error loading state for restored node', e)
|
||||
}
|
||||
@@ -1155,10 +1125,12 @@
|
||||
showCaptureHint.set(true)
|
||||
}}
|
||||
{onJobDone}
|
||||
bind:localModuleStates
|
||||
bind:this={flowPreviewButtons}
|
||||
{loading}
|
||||
onRunPreview={() => {
|
||||
localModuleStates.set({})
|
||||
modulesTestStates.hideJobsInGraph()
|
||||
localModuleStates = {}
|
||||
showJobStatus = true
|
||||
}}
|
||||
/>
|
||||
@@ -1185,7 +1157,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- metadata -->
|
||||
{#if $flowStateStore}
|
||||
{#if flowStateStore}
|
||||
<FlowEditor
|
||||
bind:this={flowEditor}
|
||||
{disabledFlowInputs}
|
||||
@@ -1228,18 +1200,22 @@
|
||||
showFlowAiButton={!disableAi && customUi?.topBar?.aiBuilder != false}
|
||||
toggleAiChat={() => aiChatManager.toggleOpen()}
|
||||
onOpenPreview={flowPreviewButtons?.openPreview}
|
||||
localModuleStates={derivedModuleStates}
|
||||
localModuleStates={showJobStatus ? localModuleStates : {}}
|
||||
{showJobStatus}
|
||||
testModuleStates={modulesTestStates}
|
||||
isOwner={flowPreviewContent?.getIsOwner()}
|
||||
onTestFlow={flowPreviewButtons?.runPreview}
|
||||
isRunning={flowPreviewContent?.getIsRunning()}
|
||||
onCancelTestFlow={flowPreviewContent?.cancelTest}
|
||||
onHideJobStatus={resetModulesStates}
|
||||
{individualStepTests}
|
||||
onHideJobStatus={() => {
|
||||
modulesTestStates.hideJobsInGraph()
|
||||
showJobStatus = false
|
||||
}}
|
||||
{job}
|
||||
{suspendStatus}
|
||||
{showJobStatus}
|
||||
onDelete={(id) => {
|
||||
delete $derivedModuleStates[id]
|
||||
delete localModuleStates[id]
|
||||
delete modulesTestStates.states[id]
|
||||
}}
|
||||
{flowHasChanged}
|
||||
/>
|
||||
|
||||
@@ -21,13 +21,12 @@
|
||||
import FlowJobsMenu from './flows/map/FlowJobsMenu.svelte'
|
||||
import BarsStaggered from './icons/BarsStaggered.svelte'
|
||||
import type { GraphModuleState } from './graph/model'
|
||||
import type { Writable } from 'svelte/store'
|
||||
|
||||
type RootJobData = Partial<Job>
|
||||
|
||||
interface Props {
|
||||
modules: FlowModule[]
|
||||
localModuleStates: Writable<Record<string, GraphModuleState>>
|
||||
localModuleStates: Record<string, GraphModuleState>
|
||||
rootJob: RootJobData
|
||||
flowStatus: FlowStatusModule['type'] | undefined
|
||||
expandedRows: Record<string, boolean>
|
||||
@@ -122,7 +121,7 @@
|
||||
}
|
||||
|
||||
function hasEmptySubflow(stepId: string, stepType: FlowModuleValue['type'] | undefined): boolean {
|
||||
const state = $localModuleStates[stepId]
|
||||
const state = localModuleStates[stepId]
|
||||
|
||||
if (!state || !stepType) return false
|
||||
return (
|
||||
@@ -172,7 +171,7 @@
|
||||
}
|
||||
|
||||
// Check if this entry itself has an error (but don't flag it - only its parents)
|
||||
const stepStatus = $localModuleStates[module.id]?.type
|
||||
const stepStatus = localModuleStates[module.id]?.type
|
||||
if (stepStatus === 'Failure') {
|
||||
currentEntryHasError = true
|
||||
// Don't add the entry itself to parentsWithErrors
|
||||
@@ -402,7 +401,7 @@
|
||||
{#if modules.length > 0}
|
||||
{#each modules as module (module.id)}
|
||||
{@const isLeafStep = !hasSubflows(module)}
|
||||
{@const status = $localModuleStates[module.id]?.type}
|
||||
{@const status = localModuleStates[module.id]?.type}
|
||||
{@const isRunning = status === 'InProgress' || status === 'WaitingForExecutor'}
|
||||
{@const hasEmptySubflowValue = hasEmptySubflow(module.id, module.value.type)}
|
||||
{@const isCollapsible = !hasEmptySubflowValue}
|
||||
@@ -479,7 +478,7 @@
|
||||
</span>
|
||||
{/if}
|
||||
</span>
|
||||
{#if !hasEmptySubflowValue && $localModuleStates[module.id]?.flow_jobs && (module.value.type === 'forloopflow' || module.value.type === 'whileloopflow')}
|
||||
{#if !hasEmptySubflowValue && localModuleStates[module.id]?.flow_jobs && (module.value.type === 'forloopflow' || module.value.type === 'whileloopflow')}
|
||||
<span
|
||||
class="text-xs font-mono font-medium inline-flex items-center grow min-w-0 -my-2"
|
||||
>
|
||||
@@ -488,18 +487,18 @@
|
||||
moduleId={module.id}
|
||||
id={module.id}
|
||||
{onSelectedIteration}
|
||||
flowJobsSuccess={$localModuleStates[module.id]
|
||||
flowJobsSuccess={localModuleStates[module.id]
|
||||
?.flow_jobs_success}
|
||||
flowJobs={$localModuleStates[module.id]?.flow_jobs}
|
||||
selected={$localModuleStates[module.id]?.selectedForloopIndex ??
|
||||
flowJobs={localModuleStates[module.id]?.flow_jobs}
|
||||
selected={localModuleStates[module.id]?.selectedForloopIndex ??
|
||||
0}
|
||||
selectedManually={$localModuleStates[module.id]
|
||||
selectedManually={localModuleStates[module.id]
|
||||
?.selectedForLoopSetManually ?? false}
|
||||
showIcon={false}
|
||||
/>
|
||||
</span>
|
||||
{#if module.value.type === 'forloopflow'}
|
||||
{`/${$localModuleStates[module.id]?.iteration_total ?? 0}`}
|
||||
{`/${localModuleStates[module.id]?.iteration_total ?? 0}`}
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -507,7 +506,7 @@
|
||||
</div>
|
||||
|
||||
{#if isLeafStep}
|
||||
{@const jobId = $localModuleStates[module.id]?.job_id}
|
||||
{@const jobId = localModuleStates[module.id]?.job_id}
|
||||
<a
|
||||
href={getJobLink(jobId ?? '')}
|
||||
class="text-xs text-primary hover:underline font-mono"
|
||||
@@ -520,24 +519,24 @@
|
||||
</div>
|
||||
|
||||
{#if isCollapsible && isExpanded(module.id, isRunning)}
|
||||
{@const args = $localModuleStates[module.id]?.args}
|
||||
{@const logs = $localModuleStates[module.id]?.logs}
|
||||
{@const result = $localModuleStates[module.id]?.result}
|
||||
{@const jobId = $localModuleStates[module.id]?.job_id}
|
||||
{@const args = localModuleStates[module.id]?.args}
|
||||
{@const logs = localModuleStates[module.id]?.logs}
|
||||
{@const result = localModuleStates[module.id]?.result}
|
||||
{@const jobId = localModuleStates[module.id]?.job_id}
|
||||
<div class="my-1 transition-all duration-200 ease-in-out">
|
||||
<!-- Show child steps if they exist -->
|
||||
{#each getSubflows(module) as subflow}
|
||||
{@const subflowJob = {
|
||||
id: jobId,
|
||||
type:
|
||||
$localModuleStates[module.id]?.type === 'Failure' ||
|
||||
$localModuleStates[module.id]?.type === 'Success'
|
||||
localModuleStates[module.id]?.type === 'Failure' ||
|
||||
localModuleStates[module.id]?.type === 'Success'
|
||||
? 'CompletedJob'
|
||||
: ('QueuedJob' as Job['type']),
|
||||
logs,
|
||||
result,
|
||||
args,
|
||||
success: $localModuleStates[module.id]?.type === 'Success'
|
||||
success: localModuleStates[module.id]?.type === 'Success'
|
||||
}}
|
||||
<div class="border-l mb-2">
|
||||
<!-- Recursively render child steps using FlowLogViewer -->
|
||||
@@ -545,7 +544,7 @@
|
||||
modules={subflow.modules}
|
||||
{localModuleStates}
|
||||
rootJob={subflowJob}
|
||||
flowStatus={$localModuleStates[module.id]?.type}
|
||||
flowStatus={localModuleStates[module.id]?.type}
|
||||
{expandedRows}
|
||||
{allExpanded}
|
||||
{showResultsInputs}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { Job } from '$lib/gen'
|
||||
import { type Writable } from 'svelte/store'
|
||||
import type { GraphModuleState } from './graph'
|
||||
import FlowLogViewer from './FlowLogViewer.svelte'
|
||||
import { untrack } from 'svelte'
|
||||
@@ -9,7 +8,7 @@
|
||||
|
||||
interface Props {
|
||||
job: Job
|
||||
localModuleStates: Writable<Record<string, GraphModuleState>>
|
||||
localModuleStates: Record<string, GraphModuleState>
|
||||
workspaceId: string | undefined
|
||||
render: boolean
|
||||
onSelectedIteration: (
|
||||
@@ -44,7 +43,7 @@
|
||||
}
|
||||
|
||||
function getSelectedIteration(stepId: string): number {
|
||||
return $localModuleStates[stepId]?.selectedForloopIndex ?? 0
|
||||
return localModuleStates[stepId]?.selectedForloopIndex ?? 0
|
||||
}
|
||||
|
||||
function toggleExpandAll() {
|
||||
|
||||
@@ -176,10 +176,10 @@
|
||||
<div class="pt-4 grow">
|
||||
{#if jobId}
|
||||
<FlowStatusViewer
|
||||
{flowStateStore}
|
||||
bind:flowStateStore={flowStateStore.val}
|
||||
{jobId}
|
||||
on:jobsLoaded={({ detail }) => {
|
||||
job = detail
|
||||
onJobsLoaded={({ job: newJob }) => {
|
||||
job = newJob
|
||||
}}
|
||||
bind:selectedJobStep
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { stopPropagation } from 'svelte/legacy'
|
||||
|
||||
import { type Job, JobService, type RestartedFrom, type OpenFlow, type ScriptLang } from '$lib/gen'
|
||||
import {
|
||||
type Job,
|
||||
JobService,
|
||||
type RestartedFrom,
|
||||
type OpenFlow,
|
||||
type ScriptLang
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { Badge, Button } from './common'
|
||||
import Popover from '$lib/components/meltComponents/Popover.svelte'
|
||||
@@ -13,14 +19,13 @@
|
||||
import FlowStatusViewer from '../components/FlowStatusViewer.svelte'
|
||||
import FlowProgressBar from './flows/FlowProgressBar.svelte'
|
||||
import { AlertTriangle, ArrowRight, CornerDownLeft, Play, RefreshCw, X } from 'lucide-svelte'
|
||||
import { emptyString, sendUserToast } from '$lib/utils'
|
||||
import { emptyString, sendUserToast, type StateStore } from '$lib/utils'
|
||||
import { dfs } from './flows/dfs'
|
||||
import { sliceModules } from './flows/flowStateUtils.svelte'
|
||||
import InputSelectedBadge from './schema/InputSelectedBadge.svelte'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import JsonInputs from './JsonInputs.svelte'
|
||||
import FlowHistoryJobPicker from './FlowHistoryJobPicker.svelte'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import type { DurationStatus, GraphModuleState } from './graph'
|
||||
import { getStepHistoryLoaderContext } from './stepHistoryLoader.svelte'
|
||||
import { aiChatManager } from './copilot/chat/AIChatManager.svelte'
|
||||
@@ -39,8 +44,8 @@
|
||||
rightColumnSelect?: 'timeline' | 'node_status' | 'node_definition' | 'user_states'
|
||||
branchOrIterationN?: number
|
||||
scrollTop?: number
|
||||
localModuleStates?: Writable<Record<string, GraphModuleState>>
|
||||
localDurationStatuses?: Writable<Record<string, DurationStatus>>
|
||||
localModuleStates?: Record<string, GraphModuleState>
|
||||
localDurationStatuses?: Record<string, DurationStatus>
|
||||
onRunPreview?: () => void
|
||||
render?: boolean
|
||||
onJobDone?: () => void
|
||||
@@ -63,8 +68,8 @@
|
||||
rightColumnSelect = $bindable('timeline'),
|
||||
branchOrIterationN = $bindable(0),
|
||||
scrollTop = $bindable(0),
|
||||
localModuleStates = $bindable(writable({})),
|
||||
localDurationStatuses = $bindable(writable({})),
|
||||
localModuleStates = $bindable({}),
|
||||
localDurationStatuses = $bindable({}),
|
||||
onRunPreview,
|
||||
render = false,
|
||||
onJobDone,
|
||||
@@ -77,7 +82,7 @@
|
||||
let jsonEditor: JsonInputs | undefined = $state(undefined)
|
||||
let schemaHeight = $state(0)
|
||||
let isValid: boolean = $state(true)
|
||||
let suspendStatus: Writable<Record<string, { job: Job; nb: number }>> = $state(writable({}))
|
||||
let suspendStatus: StateStore<Record<string, { job: Job; nb: number }>> = $state({ val: {} })
|
||||
let isOwner: boolean = $state(false)
|
||||
|
||||
export function test() {
|
||||
@@ -568,9 +573,9 @@
|
||||
bind:suspendStatus
|
||||
hideDownloadInGraph={customUi?.downloadLogs === false}
|
||||
wideResults
|
||||
{flowStateStore}
|
||||
bind:flowStateStore={flowStateStore.val}
|
||||
{jobId}
|
||||
on:done={(x) => {
|
||||
onDone={() => {
|
||||
isRunning = false
|
||||
$executionCount = $executionCount + 1
|
||||
onJobDone?.()
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
import FlowPreviewStatus from './preview/FlowPreviewStatus.svelte'
|
||||
import FlowStatusWaitingForEvents from './FlowStatusWaitingForEvents.svelte'
|
||||
import type { FlowStatusModule, Job } from '$lib/gen'
|
||||
import { emptyString } from '$lib/utils'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import { emptyString, type StateStore } from '$lib/utils'
|
||||
import Badge from './common/badge/Badge.svelte'
|
||||
|
||||
interface Props {
|
||||
@@ -14,8 +13,8 @@
|
||||
isOwner: boolean
|
||||
hideFlowResult: boolean
|
||||
hideDownloadLogs: boolean
|
||||
innerModules: FlowStatusModule[]
|
||||
suspendStatus: Writable<Record<string, { job: Job; nb: number }>>
|
||||
innerModules: FlowStatusModule[] | undefined
|
||||
suspendStatus: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
hideJobId?: boolean
|
||||
extra?: import('svelte').Snippet
|
||||
result_streams?: Record<string, string | undefined>
|
||||
@@ -57,9 +56,9 @@
|
||||
{/if}
|
||||
{:else if job.flow_status?.modules?.[job?.flow_status?.step]?.type === 'WaitingForEvents'}
|
||||
<FlowStatusWaitingForEvents {workspaceId} {job} {isOwner} />
|
||||
{:else if $suspendStatus && Object.keys($suspendStatus).length > 0}
|
||||
{:else if suspendStatus.val && Object.keys(suspendStatus.val).length > 0}
|
||||
<div class="flex gap-2 flex-col">
|
||||
{#each Object.values($suspendStatus) as suspendCount (suspendCount.job.id)}
|
||||
{#each Object.values(suspendStatus.val) as suspendCount (suspendCount.job.id)}
|
||||
<div>
|
||||
<div class="text-sm">
|
||||
Flow suspended, waiting for {suspendCount.nb} events
|
||||
@@ -74,7 +73,7 @@
|
||||
>
|
||||
<pre class="w-full">{job.logs}</pre>
|
||||
</div>
|
||||
{:else if innerModules?.length > 0}
|
||||
{:else if innerModules && innerModules?.length > 0}
|
||||
<div class="flex flex-col gap-1">
|
||||
{#each innerModules as mod, i (mod.id)}
|
||||
{#if mod.type == 'InProgress'}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import FlowStatusViewerInner from './FlowStatusViewerInner.svelte'
|
||||
import type { FlowState } from './flows/flowState'
|
||||
import { createEventDispatcher, setContext, untrack } from 'svelte'
|
||||
import { setContext, untrack } from 'svelte'
|
||||
import type { DurationStatus, FlowStatusViewerContext, GraphModuleState } from './graph'
|
||||
import { isOwner as loadIsOwner } from '$lib/utils'
|
||||
import { isOwner as loadIsOwner, type StateStore } from '$lib/utils'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import type { Job } from '$lib/gen'
|
||||
import type { CompletedJob, Job } from '$lib/gen'
|
||||
|
||||
interface Props {
|
||||
jobId: string
|
||||
initialJob?: Job | undefined
|
||||
workspaceId?: string | undefined
|
||||
flowStateStore?: Writable<FlowState>
|
||||
flowStateStore?: FlowState
|
||||
selectedJobStep?: string | undefined
|
||||
hideFlowResult?: boolean
|
||||
hideTimeline?: boolean
|
||||
@@ -23,21 +22,24 @@
|
||||
rightColumnSelect?: 'timeline' | 'node_status' | 'node_definition' | 'user_states'
|
||||
isOwner?: boolean
|
||||
wideResults?: boolean
|
||||
localModuleStates?: Writable<Record<string, GraphModuleState>>
|
||||
localDurationStatuses?: Writable<Record<string, DurationStatus>>
|
||||
localModuleStates?: Record<string, GraphModuleState>
|
||||
localDurationStatuses?: Record<string, DurationStatus>
|
||||
job?: Job | undefined
|
||||
render?: boolean
|
||||
suspendStatus?: any
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
customUi?: {
|
||||
tagLabel?: string | undefined
|
||||
}
|
||||
onStart?: () => void
|
||||
onJobsLoaded?: ({ job, force }: { job: Job; force: boolean }) => void
|
||||
onDone?: ({ job }: { job: CompletedJob }) => void
|
||||
}
|
||||
|
||||
let {
|
||||
jobId,
|
||||
initialJob = undefined,
|
||||
workspaceId = undefined,
|
||||
flowStateStore = writable({}),
|
||||
flowStateStore = $bindable({}),
|
||||
selectedJobStep = $bindable(undefined),
|
||||
hideFlowResult = false,
|
||||
hideTimeline = false,
|
||||
@@ -48,17 +50,24 @@
|
||||
rightColumnSelect = $bindable('timeline'),
|
||||
isOwner = $bindable(false),
|
||||
wideResults = false,
|
||||
localModuleStates = $bindable(writable({})),
|
||||
localDurationStatuses = $bindable(writable({})),
|
||||
localModuleStates = $bindable({}),
|
||||
localDurationStatuses = $bindable({}),
|
||||
job = $bindable(undefined),
|
||||
render = true,
|
||||
suspendStatus = $bindable(writable({})),
|
||||
customUi
|
||||
suspendStatus = $bindable({ val: {} }),
|
||||
customUi,
|
||||
onStart,
|
||||
onJobsLoaded,
|
||||
onDone
|
||||
}: Props = $props()
|
||||
|
||||
let lastJobId: string = jobId
|
||||
|
||||
let retryStatus = writable({})
|
||||
let retryStatus = $state({ val: {} })
|
||||
let globalRefreshes: Record<string, ((clear, root) => Promise<void>)[]> = $state({})
|
||||
|
||||
let globalIterationBounds = $state({})
|
||||
|
||||
setContext<FlowStatusViewerContext>('FlowStatusViewer', {
|
||||
flowStateStore,
|
||||
suspendStatus,
|
||||
@@ -77,13 +86,13 @@
|
||||
async function updateJobId() {
|
||||
if (jobId !== lastJobId) {
|
||||
lastJobId = jobId
|
||||
$retryStatus = {}
|
||||
$suspendStatus = {}
|
||||
retryStatus.val = {}
|
||||
suspendStatus.val = {}
|
||||
globalRefreshes = {}
|
||||
globalIterationBounds = {}
|
||||
}
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let lastScriptPath: string | undefined = $state(undefined)
|
||||
|
||||
$effect.pre(() => {
|
||||
@@ -92,25 +101,33 @@
|
||||
jobId && updateJobId()
|
||||
})
|
||||
})
|
||||
|
||||
let refreshGlobal = async (moduleId: string, clear: boolean, root: string) => {
|
||||
let allFns = globalRefreshes?.[moduleId]?.map((x) => x(clear, root)) ?? []
|
||||
await Promise.all(allFns)
|
||||
}
|
||||
|
||||
let updateGlobalRefresh = (moduleId: string, updateFn: (clear, root) => Promise<void>) => {
|
||||
globalRefreshes[moduleId] = [...(globalRefreshes[moduleId] ?? []), updateFn]
|
||||
}
|
||||
</script>
|
||||
|
||||
<FlowStatusViewerInner
|
||||
{hideFlowResult}
|
||||
on:jobsLoaded={({ detail }) => {
|
||||
let { job } = detail
|
||||
onJobsLoaded={({ job, force }) => {
|
||||
if (job.script_path != lastScriptPath && job.script_path) {
|
||||
lastScriptPath = job.script_path
|
||||
loadOwner(lastScriptPath ?? '')
|
||||
}
|
||||
dispatch('jobsLoaded', job)
|
||||
onJobsLoaded?.({ job, force })
|
||||
}}
|
||||
globalModuleStates={[]}
|
||||
globalDurationStatuses={[]}
|
||||
{localModuleStates}
|
||||
{localDurationStatuses}
|
||||
{globalIterationBounds}
|
||||
bind:localModuleStates
|
||||
bind:selectedNode={selectedJobStep}
|
||||
on:start
|
||||
on:done
|
||||
bind:localDurationStatuses
|
||||
{onStart}
|
||||
{onDone}
|
||||
bind:job
|
||||
{initialJob}
|
||||
{jobId}
|
||||
@@ -122,4 +139,6 @@
|
||||
{customUi}
|
||||
graphTabOpen={true}
|
||||
isNodeSelected={true}
|
||||
{refreshGlobal}
|
||||
{updateGlobalRefresh}
|
||||
/>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { debounce, displayDate, msToSec } from '$lib/utils'
|
||||
import { debounce, displayDate, msToSec, readFieldsRecursively } from '$lib/utils'
|
||||
import { onDestroy, untrack } from 'svelte'
|
||||
import { getDbClockNow } from '$lib/forLater'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import TimelineBar from './TimelineBar.svelte'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import WaitTimeWarning from './common/waitTimeWarning/WaitTimeWarning.svelte'
|
||||
import type { GlobalIterationBounds } from './graph'
|
||||
|
||||
interface Props {
|
||||
selfWaitTime?: number | undefined
|
||||
aggregateWaitTime?: number | undefined
|
||||
flowModules: string[]
|
||||
durationStatuses: Writable<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
byJob: Record<string, { created_at?: number; started_at?: number; duration_ms?: number }>
|
||||
iteration_from?: number
|
||||
iteration_total?: number
|
||||
}
|
||||
>
|
||||
durationStatuses: Record<
|
||||
string,
|
||||
{
|
||||
byJob: Record<string, { created_at?: number; started_at?: number; duration_ms?: number }>
|
||||
}
|
||||
>
|
||||
flowDone?: boolean
|
||||
decreaseIterationFrom?: (key: string, amount: number) => void
|
||||
buildSubflowKey: (key: string) => string
|
||||
globalIterationBounds: Record<string, GlobalIterationBounds>
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -29,7 +28,10 @@
|
||||
aggregateWaitTime = undefined,
|
||||
flowModules,
|
||||
durationStatuses,
|
||||
flowDone = false
|
||||
flowDone = false,
|
||||
decreaseIterationFrom,
|
||||
buildSubflowKey,
|
||||
globalIterationBounds
|
||||
}: Props = $props()
|
||||
|
||||
let min: undefined | number = $state(undefined)
|
||||
@@ -43,15 +45,16 @@
|
||||
>
|
||||
| undefined = $state(undefined)
|
||||
|
||||
let { debounced, clearDebounce } = debounce(() => computeItems($durationStatuses), 30)
|
||||
let { debounced, clearDebounce } = debounce(() => computeItems(durationStatuses), 30)
|
||||
$effect(() => {
|
||||
flowDone != undefined && $durationStatuses && untrack(() => debounced())
|
||||
readFieldsRecursively(durationStatuses)
|
||||
flowDone != undefined && durationStatuses && untrack(() => debounced())
|
||||
})
|
||||
|
||||
export function reset() {
|
||||
min = undefined
|
||||
max = undefined
|
||||
items = computeItems($durationStatuses)
|
||||
items = computeItems(durationStatuses)
|
||||
}
|
||||
|
||||
function computeItems(
|
||||
@@ -172,20 +175,17 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#each Object.values(flowModules) as k (k)}
|
||||
{@const iterationFrom = globalIterationBounds[buildSubflowKey(k)]?.iteration_from ?? 0}
|
||||
<div class="overflow-auto max-h-60 shadow-inner dark:shadow-gray-700 relative">
|
||||
{#if ($durationStatuses?.[k]?.iteration_from ?? 0) > 0}
|
||||
{#if iterationFrom > 0}
|
||||
<div class="w-full flex flex-row-reverse sticky top-0">
|
||||
<button
|
||||
class="!text-secondary underline mr-2 text-2xs text-right whitespace-nowrap"
|
||||
onclick={() => {
|
||||
let r = $durationStatuses[k]
|
||||
if (r.iteration_from) {
|
||||
r.iteration_from -= 20
|
||||
$durationStatuses = $durationStatuses
|
||||
}
|
||||
decreaseIterationFrom?.(k, 20)
|
||||
}}
|
||||
>Viewing iterations {$durationStatuses[k].iteration_from} to {$durationStatuses[k]
|
||||
.iteration_total}. Load more
|
||||
>Viewing iterations {iterationFrom} to {globalIterationBounds[buildSubflowKey(k)]
|
||||
?.iteration_total}. Load more
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script context="module" lang="ts">
|
||||
<script module lang="ts">
|
||||
import pLimit from 'p-limit'
|
||||
|
||||
const plimit = pLimit(5)
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
let args = $state(<Record<string, any>>{})
|
||||
|
||||
onMount(() => {
|
||||
testSteps?.updateStepArgs(mod.id, $flowStateStore, flowStore?.val, previewArgs?.val)
|
||||
testSteps?.updateStepArgs(mod.id, flowStateStore, flowStore?.val, previewArgs?.val)
|
||||
args = testSteps?.getStepArgs(mod.id) ?? { value: {} }
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -35,14 +35,13 @@
|
||||
}
|
||||
|
||||
export function loadArgsAndRunTest() {
|
||||
testSteps?.updateStepArgs(mod.id, $flowStateStore, flowStore?.val, previewArgs?.val)
|
||||
testSteps?.updateStepArgs(mod.id, flowStateStore, flowStore?.val, previewArgs?.val)
|
||||
runTest(testSteps.getStepArgs(mod.id)?.value)
|
||||
}
|
||||
|
||||
export async function runTest(args: any) {
|
||||
// Not defined if JobProgressBar not loaded
|
||||
if (jobProgressReset) jobProgressReset()
|
||||
|
||||
if (modulesTestStates.states[mod.id]) {
|
||||
modulesTestStates.states[mod.id].cancel = async () => {
|
||||
await jobLoader?.cancelJob()
|
||||
@@ -92,16 +91,19 @@
|
||||
|
||||
function jobDone(testJob: Job & { result?: any }) {
|
||||
if (testJob && !testJob.canceled && testJob.type == 'CompletedJob') {
|
||||
if ($flowStateStore[mod.id]) {
|
||||
$flowStateStore[mod.id].previewResult = testJob.result
|
||||
$flowStateStore[mod.id].previewSuccess = testJob.success
|
||||
$flowStateStore[mod.id].previewJobId = testJob.id
|
||||
$flowStateStore[mod.id].previewWorkspaceId = testJob.workspace_id
|
||||
$flowStateStore = $flowStateStore
|
||||
if (flowStateStore.val[mod.id]) {
|
||||
flowStateStore.val[mod.id] = {
|
||||
...flowStateStore.val[mod.id],
|
||||
previewResult: testJob.result,
|
||||
previewSuccess: testJob.success,
|
||||
previewJobId: testJob.id
|
||||
}
|
||||
}
|
||||
stepHistoryLoader?.resetInitial(mod.id)
|
||||
}
|
||||
modulesTestStates.states[mod.id].testJob = undefined
|
||||
if (modulesTestStates.states[mod.id]) {
|
||||
modulesTestStates.states[mod.id].testJob = testJob
|
||||
}
|
||||
}
|
||||
|
||||
export function cancelJob() {
|
||||
@@ -110,16 +112,16 @@
|
||||
|
||||
$effect(() => {
|
||||
// Update testIsLoading to read the state from parent components
|
||||
testIsLoading = modulesTestStates.states[mod.id]?.loading ?? false
|
||||
testIsLoading = modulesTestStates.states?.[mod.id]?.loading ?? false
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
// Update testJob to read the state from parent components
|
||||
testJob = modulesTestStates.states[mod.id]?.testJob
|
||||
testJob = modulesTestStates.states?.[mod.id]?.testJob
|
||||
})
|
||||
|
||||
modulesTestStates.states[mod.id] = {
|
||||
...(modulesTestStates.states[mod.id] ?? { loading: false }),
|
||||
...(modulesTestStates.states?.[mod.id] ?? { loading: false }),
|
||||
loading: testIsLoading,
|
||||
testJob: testJob
|
||||
}
|
||||
@@ -134,13 +136,13 @@
|
||||
() => modulesTestStates.states[mod.id]?.loading ?? false,
|
||||
(v) => {
|
||||
let newLoading = v ?? false
|
||||
if (modulesTestStates.states[mod.id]?.loading !== newLoading) {
|
||||
if (modulesTestStates.states && modulesTestStates.states?.[mod.id]?.loading !== newLoading) {
|
||||
modulesTestStates.states[mod.id] = {
|
||||
...(modulesTestStates.states[mod.id] ?? {}),
|
||||
loading: newLoading
|
||||
...(modulesTestStates.states?.[mod.id] ?? {}),
|
||||
loading: newLoading,
|
||||
hiddenInGraph: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bind:job={modulesTestStates.states[mod.id].testJob}
|
||||
/>
|
||||
|
||||
@@ -98,13 +98,13 @@
|
||||
<FlowStatusViewer
|
||||
workspaceId={workspace}
|
||||
{jobId}
|
||||
on:start={() => {
|
||||
onStart={() => {
|
||||
outputs?.jobId.set(jobId)
|
||||
outputs?.loading.set(true)
|
||||
}}
|
||||
on:done={(e) => {
|
||||
onDone={({ job }) => {
|
||||
outputs?.loading.set(false)
|
||||
outputs?.result.set(e?.detail?.result)
|
||||
outputs?.result.set(job?.result as any)
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
|
||||
@@ -264,8 +264,8 @@
|
||||
{#if job?.id}
|
||||
<FlowStatusViewer
|
||||
jobId={job?.id}
|
||||
on:jobsLoaded={({ detail }) => {
|
||||
job = detail
|
||||
onJobsLoaded={({ job: newJob }) => {
|
||||
job = newJob
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
|
||||
@@ -117,7 +117,7 @@ class AIChatManager {
|
||||
return (
|
||||
estimatedTokens >
|
||||
modelContextWindow -
|
||||
Math.max(modelContextWindow * MAX_TOKENS_THRESHOLD_PERCENTAGE, MAX_TOKENS_HARD_LIMIT)
|
||||
Math.max(modelContextWindow * MAX_TOKENS_THRESHOLD_PERCENTAGE, MAX_TOKENS_HARD_LIMIT)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -547,8 +547,8 @@ class AIChatManager {
|
||||
onNewToken: (token: string) => {
|
||||
reply += token
|
||||
},
|
||||
onMessageEnd: () => {},
|
||||
setToolStatus: () => {}
|
||||
onMessageEnd: () => { },
|
||||
setToolStatus: () => { }
|
||||
},
|
||||
systemMessage
|
||||
}
|
||||
@@ -733,8 +733,8 @@ class AIChatManager {
|
||||
} else {
|
||||
// Create new tool message with metadata
|
||||
const newMessage: ToolDisplayMessage = {
|
||||
role: 'tool',
|
||||
tool_call_id: id,
|
||||
role: 'tool',
|
||||
tool_call_id: id,
|
||||
content: metadata?.content ?? metadata?.error ?? '',
|
||||
...(metadata || {})
|
||||
}
|
||||
@@ -923,20 +923,20 @@ class AIChatManager {
|
||||
const module = getModule(id)
|
||||
|
||||
if (module && module.value.type === 'rawscript') {
|
||||
const moduleState: FlowModuleState | undefined = flowStateStore[module.id]
|
||||
const moduleState: FlowModuleState | undefined = flowStateStore.val[module.id]
|
||||
|
||||
const editorRelated =
|
||||
currentEditor && currentEditor.type === 'script' && currentEditor.stepId === module.id
|
||||
? {
|
||||
diffMode: currentEditor.diffMode,
|
||||
lastDeployedCode: currentEditor.lastDeployedCode,
|
||||
lastSavedCode: undefined
|
||||
}
|
||||
diffMode: currentEditor.diffMode,
|
||||
lastDeployedCode: currentEditor.lastDeployedCode,
|
||||
lastSavedCode: undefined
|
||||
}
|
||||
: {
|
||||
diffMode: false,
|
||||
lastDeployedCode: undefined,
|
||||
lastSavedCode: undefined
|
||||
}
|
||||
diffMode: false,
|
||||
lastDeployedCode: undefined,
|
||||
lastSavedCode: undefined
|
||||
}
|
||||
|
||||
return {
|
||||
args: moduleState?.previewArgs ?? {},
|
||||
|
||||
@@ -200,10 +200,10 @@
|
||||
module.value.input_transforms = input_transforms
|
||||
refreshStateStore(flowStore)
|
||||
|
||||
if ($flowStateStore[id]) {
|
||||
$flowStateStore[id].schema = schema
|
||||
if (flowStateStore.val[id]) {
|
||||
flowStateStore.val[id].schema = schema
|
||||
} else {
|
||||
$flowStateStore[id] = {
|
||||
flowStateStore.val[id] = {
|
||||
schema
|
||||
}
|
||||
}
|
||||
@@ -305,7 +305,6 @@
|
||||
}
|
||||
|
||||
if (location.type === 'preprocessor' || location.type === 'failure') {
|
||||
$flowStateStore = $flowStateStore
|
||||
refreshStateStore(flowStore)
|
||||
|
||||
setModuleStatus(location.type, 'added')
|
||||
@@ -322,7 +321,6 @@
|
||||
await flowModuleSchemaMap?.addBranch(newModule.id)
|
||||
}
|
||||
|
||||
$flowStateStore = $flowStateStore
|
||||
refreshStateStore(flowStore)
|
||||
|
||||
setModuleStatus(newModule.id, 'added')
|
||||
@@ -516,7 +514,7 @@
|
||||
const cleanup = aiChatManager.listenForSelectedIdChanges(
|
||||
$selectedId,
|
||||
flowStore.val,
|
||||
$flowStateStore,
|
||||
flowStateStore,
|
||||
$currentEditor
|
||||
)
|
||||
return cleanup
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { OpenFlow } from '$lib/gen'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type { FlowState } from './flows/flowState'
|
||||
import type { FlowWithDraftAndDraftTriggers, Trigger } from './triggers/utils'
|
||||
import type { DiffDrawerI } from './diff_drawer'
|
||||
@@ -16,7 +15,7 @@ export type FlowBuilderProps = {
|
||||
initialArgs?: Record<string, any>
|
||||
loading?: boolean
|
||||
flowStore: StateStore<OpenFlow>
|
||||
flowStateStore: Writable<FlowState>
|
||||
flowStateStore: StateStore<FlowState>
|
||||
savedFlow?: FlowWithDraftAndDraftTriggers | undefined
|
||||
diffDrawer?: DiffDrawerI | undefined
|
||||
customUi?: FlowBuilderWhitelabelCustomUi
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { getContext, onDestroy, onMount, setContext } from 'svelte'
|
||||
import type { FlowEditorContext } from './types'
|
||||
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { writable } from 'svelte/store'
|
||||
import type { PropPickerContext, FlowPropPickerConfig } from '$lib/components/prop_picker'
|
||||
import type { PickableProperties } from '$lib/components/flows/previousResults'
|
||||
import type { Flow, Job } from '$lib/gen'
|
||||
@@ -16,6 +16,8 @@
|
||||
import { aiChatManager, AIMode } from '../copilot/chat/AIChatManager.svelte'
|
||||
import type { GraphModuleState } from '../graph'
|
||||
import { triggerableByAI } from '$lib/actions/triggerableByAI.svelte'
|
||||
import type { ModulesTestStates } from '../modulesTest.svelte'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
const { flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
interface Props {
|
||||
@@ -27,6 +29,7 @@
|
||||
disabledFlowInputs?: boolean
|
||||
smallErrorHandler?: boolean
|
||||
newFlow?: boolean
|
||||
showJobStatus?: boolean
|
||||
savedFlow?:
|
||||
| (Flow & {
|
||||
draft?: Flow | undefined
|
||||
@@ -40,7 +43,8 @@
|
||||
aiChatOpen?: boolean
|
||||
showFlowAiButton?: boolean
|
||||
toggleAiChat?: () => void
|
||||
localModuleStates?: Writable<Record<string, GraphModuleState>>
|
||||
localModuleStates?: Record<string, GraphModuleState>
|
||||
testModuleStates?: ModulesTestStates
|
||||
isOwner?: boolean
|
||||
onTestFlow?: () => void
|
||||
isRunning?: boolean
|
||||
@@ -49,8 +53,7 @@
|
||||
onHideJobStatus?: () => void
|
||||
individualStepTests?: boolean
|
||||
job?: Job
|
||||
suspendStatus?: Writable<Record<string, { job: Job; nb: number }>>
|
||||
showJobStatus?: boolean
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
onDelete?: (id: string) => void
|
||||
flowHasChanged?: boolean
|
||||
}
|
||||
@@ -63,6 +66,7 @@
|
||||
disableSettings = false,
|
||||
disabledFlowInputs = false,
|
||||
smallErrorHandler = false,
|
||||
showJobStatus = false,
|
||||
newFlow = false,
|
||||
savedFlow = undefined,
|
||||
onDeployTrigger = () => {},
|
||||
@@ -70,7 +74,8 @@
|
||||
onEditInput = undefined,
|
||||
forceTestTab,
|
||||
highlightArg,
|
||||
localModuleStates = writable({}),
|
||||
localModuleStates = {},
|
||||
testModuleStates = undefined,
|
||||
aiChatOpen,
|
||||
showFlowAiButton,
|
||||
toggleAiChat,
|
||||
@@ -83,7 +88,6 @@
|
||||
individualStepTests = false,
|
||||
job,
|
||||
suspendStatus,
|
||||
showJobStatus,
|
||||
onDelete,
|
||||
flowHasChanged
|
||||
}: Props = $props()
|
||||
@@ -134,6 +138,7 @@
|
||||
{disableSettings}
|
||||
{smallErrorHandler}
|
||||
{newFlow}
|
||||
{showJobStatus}
|
||||
on:reload
|
||||
on:generateStep={({ detail }) => {
|
||||
if (!aiChatManager.open) {
|
||||
@@ -144,6 +149,7 @@
|
||||
{onTestUpTo}
|
||||
{onEditInput}
|
||||
{localModuleStates}
|
||||
{testModuleStates}
|
||||
{aiChatOpen}
|
||||
{showFlowAiButton}
|
||||
{toggleAiChat}
|
||||
@@ -155,7 +161,6 @@
|
||||
{onHideJobStatus}
|
||||
{individualStepTests}
|
||||
flowJob={job}
|
||||
{showJobStatus}
|
||||
{suspendStatus}
|
||||
{onDelete}
|
||||
{flowHasChanged}
|
||||
|
||||
@@ -10,28 +10,34 @@
|
||||
import { Pen } from 'lucide-svelte'
|
||||
import PredicateGen from '$lib/components/copilot/PredicateGen.svelte'
|
||||
|
||||
export let branch: {
|
||||
summary?: string
|
||||
expr: string
|
||||
modules: Array<FlowModule>
|
||||
interface Props {
|
||||
branch: {
|
||||
summary?: string
|
||||
expr: string
|
||||
modules: Array<FlowModule>
|
||||
}
|
||||
parentModule: FlowModule
|
||||
previousModule: FlowModule | undefined
|
||||
enableAi?: boolean
|
||||
}
|
||||
export let parentModule: FlowModule
|
||||
export let previousModule: FlowModule | undefined
|
||||
export let enableAi = false
|
||||
|
||||
let { branch = $bindable(), parentModule, previousModule, enableAi = false }: Props = $props()
|
||||
|
||||
const { previewArgs, flowStateStore, flowStore } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let editor: SimpleEditor | undefined = undefined
|
||||
let open = false
|
||||
$: stepPropPicker = getStepPropPicker(
|
||||
$flowStateStore,
|
||||
parentModule,
|
||||
previousModule,
|
||||
parentModule.id,
|
||||
flowStore.val,
|
||||
previewArgs.val,
|
||||
false
|
||||
let editor: SimpleEditor | undefined = $state(undefined)
|
||||
let open = $state(false)
|
||||
let stepPropPicker = $derived(
|
||||
getStepPropPicker(
|
||||
flowStateStore.val,
|
||||
parentModule,
|
||||
previousModule,
|
||||
parentModule.id,
|
||||
flowStore.val,
|
||||
previewArgs.val,
|
||||
false
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
m.id,
|
||||
Object.entries(v.input_transforms)
|
||||
.map((x) => {
|
||||
let schema = flowStateStore[m.id]?.schema
|
||||
let schema = flowStateStore.val[m.id]?.schema
|
||||
let val: { argName: string; type: string } | undefined = undefined
|
||||
|
||||
const [k, inputTransform] = x
|
||||
@@ -75,7 +75,7 @@
|
||||
Object.entries(v.input_transforms)
|
||||
.filter((x) => {
|
||||
const shouldDisplay = hideOptional
|
||||
? $flowStateStore[m.id]?.schema?.required?.includes(x[0])
|
||||
? flowStateStore.val[m.id]?.schema?.required?.includes(x[0])
|
||||
: true
|
||||
return x[1].type == 'static' && shouldDisplay
|
||||
})
|
||||
@@ -146,7 +146,7 @@
|
||||
noDynamicToggle
|
||||
{filter}
|
||||
class="mt-2"
|
||||
schema={$flowStateStore[m.id]?.schema ?? {}}
|
||||
schema={flowStateStore.val[m.id]?.schema ?? {}}
|
||||
bind:args={steps[index][0]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import { handleSelectTriggerFromKind, type Trigger } from '$lib/components/triggers/utils'
|
||||
import { computeMissingInputWarnings } from '../missingInputWarnings'
|
||||
import FlowResult from './FlowResult.svelte'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
|
||||
interface Props {
|
||||
noEditor?: boolean
|
||||
@@ -32,7 +32,7 @@
|
||||
onTestFlow?: () => void
|
||||
job?: Job
|
||||
isOwner?: boolean
|
||||
suspendStatus?: Writable<Record<string, { job: Job; nb: number }>>
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
onOpenDetails?: () => void
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
computeMissingInputWarnings(flowStore, $flowStateStore, flowInputsStore)
|
||||
computeMissingInputWarnings(flowStore, flowStateStore, flowInputsStore)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
let stepPropPicker = $derived(
|
||||
getStepPropPicker(
|
||||
$flowStateStore,
|
||||
flowStateStore.val,
|
||||
parentModule,
|
||||
previousModule,
|
||||
mod.id,
|
||||
@@ -72,7 +72,7 @@
|
||||
let iteratorFieldFocused = $state(false)
|
||||
let iteratorGen: IteratorGen | undefined = $state(undefined)
|
||||
|
||||
let previewIterationArgs = $derived($flowStateStore[mod.id]?.previewArgs ?? {})
|
||||
let previewIterationArgs = $derived(flowStateStore.val[mod.id]?.previewArgs ?? {})
|
||||
|
||||
function setExpr(code: string) {
|
||||
if (mod.value.type === 'forloopflow') {
|
||||
|
||||
@@ -169,11 +169,11 @@
|
||||
}
|
||||
}
|
||||
await tick()
|
||||
if (!deepEqual(schema, $flowStateStore[flowModule.id]?.schema)) {
|
||||
if (!$flowStateStore[flowModule.id]) {
|
||||
$flowStateStore[flowModule.id] = { schema }
|
||||
if (!deepEqual(schema, flowStateStore.val[flowModule.id]?.schema)) {
|
||||
if (!flowStateStore.val[flowModule.id]) {
|
||||
flowStateStore.val[flowModule.id] = { schema }
|
||||
} else {
|
||||
$flowStateStore[flowModule.id].schema = schema
|
||||
flowStateStore.val[flowModule.id].schema = schema
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -195,7 +195,7 @@
|
||||
let lastJobId: string | undefined = undefined
|
||||
|
||||
function onSelectedIdChange() {
|
||||
if (!$flowStateStore?.[$selectedId]?.schema && flowModule) {
|
||||
if (!flowStateStore?.val?.[$selectedId]?.schema && flowModule) {
|
||||
reload(flowModule)
|
||||
}
|
||||
lastJobId = undefined
|
||||
@@ -203,31 +203,30 @@
|
||||
|
||||
async function getLastJob() {
|
||||
if (
|
||||
!$flowStateStore ||
|
||||
!flowStateStore ||
|
||||
!flowModule.id ||
|
||||
$flowStateStore[flowModule.id]?.previewResult === 'never tested this far' ||
|
||||
!$flowStateStore[flowModule.id]?.previewJobId ||
|
||||
!$flowStateStore[flowModule.id]?.previewWorkspaceId
|
||||
flowStateStore.val[flowModule.id]?.previewResult === 'never tested this far' ||
|
||||
!flowStateStore.val[flowModule.id]?.previewJobId
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
lastJobId == $flowStateStore[flowModule.id]?.previewJobId ||
|
||||
lastJob?.id == $flowStateStore[flowModule.id]?.previewJobId ||
|
||||
$flowStateStore[flowModule.id]?.previewSuccess == undefined
|
||||
lastJobId == flowStateStore.val[flowModule.id]?.previewJobId ||
|
||||
lastJob?.id == flowStateStore.val[flowModule.id]?.previewJobId ||
|
||||
flowStateStore.val[flowModule.id]?.previewSuccess == undefined
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastJobId = $flowStateStore[flowModule.id]?.previewJobId
|
||||
lastJobId = flowStateStore.val[flowModule.id]?.previewJobId
|
||||
|
||||
const job = await JobService.getJob({
|
||||
workspace: $flowStateStore[flowModule.id]?.previewWorkspaceId ?? '',
|
||||
id: $flowStateStore[flowModule.id]?.previewJobId ?? '',
|
||||
workspace: $workspaceStore ?? '',
|
||||
id: flowStateStore.val[flowModule.id]?.previewJobId ?? '',
|
||||
noCode: true
|
||||
})
|
||||
if (job && job.type === 'CompletedJob') {
|
||||
lastJobId = $flowStateStore[flowModule.id]?.previewJobId
|
||||
lastJobId = flowStateStore.val[flowModule.id]?.previewJobId
|
||||
lastJob = job
|
||||
}
|
||||
}
|
||||
@@ -251,9 +250,9 @@
|
||||
|
||||
let stepPropPicker = $derived(
|
||||
$executionCount != undefined && failureModule
|
||||
? getFailureStepPropPicker($flowStateStore, flowStore.val, previewArgs.val)
|
||||
? getFailureStepPropPicker(flowStateStore, flowStore.val, previewArgs.val)
|
||||
: getStepPropPicker(
|
||||
$flowStateStore,
|
||||
flowStateStore.val,
|
||||
parentModule,
|
||||
previousModule,
|
||||
flowModule.id,
|
||||
@@ -269,7 +268,7 @@
|
||||
$effect(() => {
|
||||
if (testJob && testJob.type === 'CompletedJob') {
|
||||
lastJob = $state.snapshot(testJob)
|
||||
} else if ($workspaceStore && $pathStore && flowModule?.id && $flowStateStore) {
|
||||
} else if ($workspaceStore && $pathStore && flowModule?.id && flowStateStore) {
|
||||
untrack(() => getLastJob())
|
||||
}
|
||||
})
|
||||
@@ -361,7 +360,7 @@
|
||||
on:fork={async () => {
|
||||
const [module, state] = await fork(flowModule)
|
||||
flowModule = module
|
||||
$flowStateStore[module.id] = state
|
||||
flowStateStore.val[module.id] = state
|
||||
}}
|
||||
on:reload={async () => {
|
||||
if (flowModule.value.type == 'script') {
|
||||
@@ -380,14 +379,14 @@
|
||||
const [module, state] = await createScriptFromInlineScript(
|
||||
flowModule,
|
||||
$selectedId,
|
||||
$flowStateStore[flowModule.id].schema,
|
||||
flowStateStore.val[flowModule.id].schema,
|
||||
$pathStore
|
||||
)
|
||||
if (flowModule.value.type == 'rawscript') {
|
||||
module.value.input_transforms = flowModule.value.input_transforms
|
||||
}
|
||||
flowModule = module
|
||||
$flowStateStore[module.id] = state
|
||||
flowStateStore.val[module.id] = state
|
||||
}}
|
||||
/>
|
||||
{/snippet}
|
||||
@@ -539,7 +538,7 @@
|
||||
class="px-1 xl:px-2"
|
||||
bind:this={inputTransformSchemaForm}
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
schema={$flowStateStore[$selectedId]?.schema ?? {}}
|
||||
schema={flowStateStore.val[$selectedId]?.schema ?? {}}
|
||||
previousModuleId={previousModule?.id}
|
||||
bind:args={
|
||||
() => {
|
||||
@@ -567,7 +566,7 @@
|
||||
bind:this={modulePreview}
|
||||
mod={flowModule}
|
||||
{noEditor}
|
||||
schema={$flowStateStore[$selectedId]?.schema ?? {}}
|
||||
schema={flowStateStore.val[$selectedId]?.schema ?? {}}
|
||||
bind:testJob
|
||||
bind:testIsLoading
|
||||
bind:scriptProgress
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
let editor: SimpleEditor | undefined = $state(undefined)
|
||||
let stepPropPicker = $derived(
|
||||
getStepPropPicker(
|
||||
$flowStateStore,
|
||||
flowStateStore.val,
|
||||
undefined,
|
||||
undefined,
|
||||
flowModule.id,
|
||||
@@ -55,7 +55,7 @@
|
||||
let isBranchAll = $derived(flowModule.value.type === 'branchall')
|
||||
let isStopAfterIfEnabled = $derived(Boolean(flowModule.stop_after_if))
|
||||
let isStopAfterAllIterationsEnabled = $derived(Boolean(flowModule.stop_after_all_iters_if))
|
||||
let result = $derived($flowStateStore[flowModule.id]?.previewResult ?? NEVER_TESTED_THIS_FAR)
|
||||
let result = $derived(flowStateStore.val[flowModule.id]?.previewResult ?? NEVER_TESTED_THIS_FAR)
|
||||
let parentLoopId = $derived(checkIfParentLoop(flowStore.val))
|
||||
</script>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
let editor: SimpleEditor | undefined = $state(undefined)
|
||||
let stepPropPicker = $derived(
|
||||
getStepPropPicker(
|
||||
$flowStateStore,
|
||||
flowStateStore.val,
|
||||
parentModule,
|
||||
previousModule,
|
||||
flowModule.id,
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
let editor: SimpleEditor | undefined = $state(undefined)
|
||||
|
||||
const result = $flowStateStore[$selectedId]?.previewResult ?? {}
|
||||
const result = flowStateStore.val[$selectedId]?.previewResult ?? {}
|
||||
|
||||
let isSleepEnabled = $derived(Boolean(flowModule.sleep))
|
||||
</script>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
import AddProperty from '$lib/components/schema/AddProperty.svelte'
|
||||
|
||||
const { selectedId, flowStateStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const result = $flowStateStore[$selectedId]?.previewResult ?? {}
|
||||
const result = flowStateStore.val[$selectedId]?.previewResult ?? {}
|
||||
let editor: SimpleEditor | undefined = $state(undefined)
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
}
|
||||
|
||||
flowModule = module
|
||||
$flowStateStore[module.id] = state
|
||||
flowStateStore.val[module.id] = state
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
const [module, state] = await pickFlow(path, summary, flowModule.id)
|
||||
|
||||
flowModule = module
|
||||
$flowStateStore[module.id] = state
|
||||
flowStateStore.val[module.id] = state
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
@@ -182,7 +182,7 @@
|
||||
}
|
||||
|
||||
flowModule = module
|
||||
$flowStateStore[module.id] = state
|
||||
flowStateStore.val[module.id] = state
|
||||
}}
|
||||
failureModule={$selectedId === 'failure'}
|
||||
preprocessorModule={$selectedId === 'preprocessor'}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
import FlowPreviewResult from '$lib/components/FlowPreviewResult.svelte'
|
||||
import type { Job } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import FlowCard from '../common/FlowCard.svelte'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
|
||||
interface Props {
|
||||
job?: Job
|
||||
isOwner?: boolean
|
||||
suspendStatus?: Writable<Record<string, { job: Job; nb: number }>>
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
noEditor: boolean
|
||||
onOpenDetails?: () => void
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
let jobId: string | undefined = $state(undefined)
|
||||
let job: Job | undefined = $state(undefined)
|
||||
|
||||
let previewIterationArgs = $derived($flowStateStore[mod.id]?.previewArgs ?? {})
|
||||
let previewIterationArgs = $derived(flowStateStore.val[mod.id]?.previewArgs ?? {})
|
||||
</script>
|
||||
|
||||
<Drawer bind:open={previewOpen} alwaysOpen size="75%">
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import type { Flow, FlowModule } from '$lib/gen'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import { loadFlowModuleState } from './flowStateUtils.svelte'
|
||||
import { emptyFlowModuleState } from './utils'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
|
||||
export type FlowModuleState = {
|
||||
schema?: Schema
|
||||
previewResult?: any
|
||||
previewArgs?: any
|
||||
previewJobId?: string
|
||||
previewWorkspaceId?: string
|
||||
previewSuccess?: boolean
|
||||
}
|
||||
|
||||
@@ -21,7 +20,7 @@ export type FlowState = Record<string, FlowModuleState>
|
||||
* We also hold the data of the results of a test job, ran by the user.
|
||||
*/
|
||||
|
||||
export async function initFlowState(flow: Flow, flowStateStore: Writable<FlowState>) {
|
||||
export async function initFlowState(flow: Flow, flowStateStore: StateStore<FlowState>) {
|
||||
const modulesState: FlowState = {}
|
||||
|
||||
await mapFlowModules(flow.value.modules, modulesState)
|
||||
@@ -30,10 +29,10 @@ export async function initFlowState(flow: Flow, flowStateStore: Writable<FlowSta
|
||||
? await loadFlowModuleState(flow.value.failure_module)
|
||||
: emptyFlowModuleState()
|
||||
|
||||
flowStateStore.set({
|
||||
flowStateStore.val = {
|
||||
...modulesState,
|
||||
failure: failureModule
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import { initialCode } from '$lib/script_helpers'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { getScriptByPath } from '$lib/scripts'
|
||||
import { get, type Writable } from 'svelte/store'
|
||||
import { get } from 'svelte/store'
|
||||
import type { FlowModuleState, FlowState } from './flowState'
|
||||
import { emptyFlowModuleState } from './utils'
|
||||
import { NEVER_TESTED_THIS_FAR } from './models'
|
||||
@@ -260,11 +260,8 @@ export async function createScriptFromInlineScript(
|
||||
return pickScript(availablePath, flowModule.summary ?? '', flowModule.id, hash)
|
||||
}
|
||||
|
||||
export function deleteFlowStateById(id: string, flowStateStore: Writable<FlowState>) {
|
||||
flowStateStore.update((fss) => {
|
||||
delete fss[id]
|
||||
return fss
|
||||
})
|
||||
export function deleteFlowStateById(id: string, flowStateStore: FlowState) {
|
||||
delete flowStateStore.val[id]
|
||||
}
|
||||
|
||||
export function sliceModules(
|
||||
@@ -298,7 +295,7 @@ export function sliceModules(
|
||||
|
||||
export async function insertNewPreprocessorModule(
|
||||
flowStore: StateStore<ExtendedOpenFlow>,
|
||||
flowStateStore: Writable<FlowState>,
|
||||
flowStateStore: FlowState,
|
||||
inlineScript?: {
|
||||
language: RawScript['language']
|
||||
},
|
||||
@@ -323,15 +320,12 @@ export async function insertNewPreprocessorModule(
|
||||
|
||||
flowStore.val.value.preprocessor_module = module
|
||||
|
||||
flowStateStore.update((fss) => {
|
||||
fss[module.id] = state
|
||||
return fss
|
||||
})
|
||||
flowStateStore.val[module.id] = state
|
||||
}
|
||||
|
||||
export async function insertNewFailureModule(
|
||||
flowStore: StateStore<ExtendedOpenFlow>,
|
||||
flowStateStore: Writable<FlowState>,
|
||||
flowStateStore: FlowState,
|
||||
inlineScript?: {
|
||||
language: RawScript['language']
|
||||
subkind: 'pgsql' | 'flow'
|
||||
@@ -361,8 +355,5 @@ export async function insertNewFailureModule(
|
||||
|
||||
flowStore.val.value.failure_module = module
|
||||
|
||||
flowStateStore.update((fss) => {
|
||||
fss[module.id] = state
|
||||
return fss
|
||||
})
|
||||
flowStateStore.val[module.id] = state
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Flow, OpenFlow } from '$lib/gen'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { writable } from 'svelte/store'
|
||||
import { initFlowState, type FlowState } from './flowState'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
@@ -11,7 +11,7 @@ export const importFlowStore = writable<Flow | undefined>(undefined)
|
||||
export async function initFlow(
|
||||
flow: Flow,
|
||||
flowStore: StateStore<Flow>,
|
||||
flowStateStore: Writable<FlowState>
|
||||
flowStateStore: StateStore<FlowState>
|
||||
) {
|
||||
await initFlowState(flow, flowStateStore)
|
||||
flowStore.val = flow
|
||||
|
||||
@@ -9,14 +9,21 @@
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import { Play } from 'lucide-svelte'
|
||||
import { aiChatManager } from '$lib/components/copilot/chat/AIChatManager.svelte'
|
||||
import type { GraphModuleState } from '$lib/components/graph'
|
||||
|
||||
interface Props {
|
||||
loading?: boolean
|
||||
onRunPreview?: () => void
|
||||
onJobDone?: () => void
|
||||
localModuleStates?: Record<string, GraphModuleState>
|
||||
}
|
||||
|
||||
let { loading = false, onRunPreview, onJobDone }: Props = $props()
|
||||
let {
|
||||
loading = false,
|
||||
onRunPreview,
|
||||
onJobDone,
|
||||
localModuleStates = $bindable({})
|
||||
}: Props = $props()
|
||||
|
||||
const { selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
@@ -157,6 +164,7 @@
|
||||
bind:selectedJobStepType
|
||||
bind:branchOrIterationN
|
||||
bind:rightColumnSelect
|
||||
bind:localModuleStates
|
||||
on:close={() => {
|
||||
// keep the data in the preview content
|
||||
deferContent = true
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<span transition:fade={{ duration: 100 }} class="text-xs">Test flow</span>
|
||||
{/if}
|
||||
</Button>
|
||||
{#if wide && (flowPreviewJob || individualStepTests)}
|
||||
{#if wide && flowPreviewJob}
|
||||
<div
|
||||
class="flex flex-row items-center shadow-sm rounded-md mt-1"
|
||||
in:fade={{ duration: 100, delay: 200 }}
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
let isOpen = $state(false)
|
||||
|
||||
$effect(() => {
|
||||
filter
|
||||
isOpen && flowJobs && untrack(() => updateItems())
|
||||
})
|
||||
</script>
|
||||
@@ -150,7 +151,8 @@
|
||||
class={twMerge(
|
||||
'text-primary text-xs w-full text-left py-1 pl-2 hover:bg-surface-hover whitespace-nowrap flex flex-row gap-2 items-center',
|
||||
items[idx].success == false ? 'text-red-400' : '',
|
||||
'data-[highlighted]:bg-surface-hover'
|
||||
'data-[highlighted]:bg-surface-hover',
|
||||
items[idx].index == selected ? 'bg-surface-selected' : ''
|
||||
)}
|
||||
onClick={() => {
|
||||
onSelectedIteration({
|
||||
|
||||
@@ -164,31 +164,34 @@
|
||||
connectingData =
|
||||
flowPropPickerConfig && pickableIds && Object.keys(pickableIds).includes(id)
|
||||
? pickableIds[id]
|
||||
: (flowStateStore?.[id]?.previewResult ?? {})
|
||||
: (flowStateStore?.val?.[id]?.previewResult ?? {})
|
||||
}
|
||||
$effect(() => {
|
||||
const args = [id, pickableIds, $flowPropPickerConfig, $flowStateStore] as const
|
||||
const args = [id, pickableIds, $flowPropPickerConfig, flowStateStore] as const
|
||||
untrack(() => updateConnectingData(...args))
|
||||
})
|
||||
|
||||
function updateLastJob(flowStateStore: any | undefined) {
|
||||
if (!flowStateStore || !id || flowStateStore[id]?.previewResult === 'never tested this far') {
|
||||
if (
|
||||
!flowStateStore ||
|
||||
!id ||
|
||||
flowStateStore.val[id]?.previewResult === 'never tested this far'
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastJob = {
|
||||
id: flowStateStore[id]?.previewJobId ?? '',
|
||||
result: flowStateStore[id]?.previewResult,
|
||||
id: flowStateStore.val[id]?.previewJobId ?? '',
|
||||
result: flowStateStore.val[id]?.previewResult,
|
||||
type: 'CompletedJob' as const,
|
||||
workspace_id: flowStateStore[id]?.previewWorkspaceId ?? '',
|
||||
success: flowStateStore[id]?.previewSuccess ?? undefined
|
||||
success: flowStateStore.val[id]?.previewSuccess ?? undefined
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (testJob && testJob.type === 'CompletedJob') {
|
||||
lastJob = $state.snapshot(testJob)
|
||||
} else if (flowStateStore && $flowStateStore) {
|
||||
untrack(() => updateLastJob($flowStateStore))
|
||||
} else if (flowStateStore && flowStateStore) {
|
||||
untrack(() => updateLastJob(flowStateStore))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -268,7 +271,7 @@
|
||||
{#if deletable && id && flowEditorContext?.flowStore && outputPickerVisible}
|
||||
{@const flowStore = flowEditorContext?.flowStore.val}
|
||||
{@const mod = flowStore?.value ? dfsPreviousResults(id, flowStore, false)[0] : undefined}
|
||||
{#if mod && $flowStateStore?.[id]}
|
||||
{#if mod && flowStateStore?.val?.[id]}
|
||||
<ModuleTest bind:this={moduleTest} {mod} bind:testIsLoading bind:testJob />
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -36,9 +36,10 @@
|
||||
import type { InlineScript, InsertKind } from '$lib/components/graph/graphBuilder.svelte'
|
||||
import { refreshStateStore } from '$lib/svelte5Utils.svelte'
|
||||
import type { GraphModuleState } from '$lib/components/graph'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import FlowStickyNode from './FlowStickyNode.svelte'
|
||||
import { getStepHistoryLoaderContext } from '$lib/components/stepHistoryLoader.svelte'
|
||||
import { ModulesTestStates } from '$lib/components/modulesTest.svelte'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
|
||||
interface Props {
|
||||
sidebarSize?: number | undefined
|
||||
@@ -51,7 +52,8 @@
|
||||
workspace?: string | undefined
|
||||
onTestUpTo?: ((id: string) => void) | undefined
|
||||
onEditInput?: (moduleId: string, key: string) => void
|
||||
localModuleStates?: Writable<Record<string, GraphModuleState>>
|
||||
localModuleStates?: Record<string, GraphModuleState>
|
||||
testModuleStates?: ModulesTestStates
|
||||
aiChatOpen?: boolean
|
||||
showFlowAiButton?: boolean
|
||||
toggleAiChat?: () => void
|
||||
@@ -64,7 +66,7 @@
|
||||
individualStepTests?: boolean
|
||||
flowJob?: Job | undefined
|
||||
showJobStatus?: boolean
|
||||
suspendStatus?: Writable<Record<string, { job: Job; nb: number }>>
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
onDelete?: (id: string) => void
|
||||
flowHasChanged?: boolean
|
||||
}
|
||||
@@ -80,7 +82,8 @@
|
||||
workspace = $workspaceStore,
|
||||
onTestUpTo,
|
||||
onEditInput,
|
||||
localModuleStates = writable({}),
|
||||
localModuleStates = {},
|
||||
testModuleStates = new ModulesTestStates(),
|
||||
aiChatOpen,
|
||||
showFlowAiButton,
|
||||
toggleAiChat,
|
||||
@@ -93,7 +96,7 @@
|
||||
individualStepTests = false,
|
||||
flowJob = undefined,
|
||||
showJobStatus = false,
|
||||
suspendStatus = writable({}),
|
||||
suspendStatus = $bindable({ val: {} }),
|
||||
onDelete,
|
||||
flowHasChanged
|
||||
}: Props = $props()
|
||||
@@ -115,9 +118,9 @@
|
||||
inlineScript?: InlineScript
|
||||
): Promise<FlowModule[]> {
|
||||
push(history, flowStore.val)
|
||||
let module = emptyModule($flowStateStore, flowStore.val, kind == 'flow')
|
||||
let module = emptyModule(flowStateStore.val, flowStore.val, kind == 'flow')
|
||||
let state = emptyFlowModuleState()
|
||||
$flowStateStore[module.id] = state
|
||||
flowStateStore.val[module.id] = state
|
||||
if (wsFlow) {
|
||||
;[module, state] = await pickFlow(wsFlow.path, wsFlow.summary, module.id)
|
||||
} else if (wsScript) {
|
||||
@@ -139,14 +142,14 @@
|
||||
} else if (inlineScript) {
|
||||
const { language, kind, subkind, summary } = inlineScript
|
||||
;[module, state] = await createInlineScriptModule(language, kind, subkind, module.id, summary)
|
||||
$flowStateStore[module.id] = state
|
||||
flowStateStore.val[module.id] = state
|
||||
if (kind == 'trigger') {
|
||||
module.summary = 'Trigger'
|
||||
} else if (kind == 'approval') {
|
||||
module.summary = 'Approval'
|
||||
}
|
||||
}
|
||||
$flowStateStore[module.id] = state
|
||||
flowStateStore.val[module.id] = state
|
||||
|
||||
if (kind == 'approval') {
|
||||
module.suspend = { required_events: 1, timeout: 1800 }
|
||||
@@ -303,18 +306,16 @@
|
||||
id: previousJobId[0].id
|
||||
})
|
||||
if ('result' in getJobResult) {
|
||||
$flowStateStore[moduleId] = {
|
||||
...($flowStateStore[moduleId] ?? {}),
|
||||
flowStateStore.val[moduleId] = {
|
||||
...(flowStateStore.val[moduleId] ?? {}),
|
||||
previewResult: getJobResult.result,
|
||||
previewJobId: previousJobId[0].id,
|
||||
previewWorkspaceId: previousJobId[0].workspace_id,
|
||||
previewSuccess: getJobResult.success
|
||||
}
|
||||
if (stepHistoryLoader) {
|
||||
stepHistoryLoader.stepStates[moduleId].loadingJobs = false
|
||||
}
|
||||
}
|
||||
$flowStateStore = $flowStateStore
|
||||
}
|
||||
}
|
||||
$effect(() => {
|
||||
@@ -388,12 +389,13 @@
|
||||
editMode
|
||||
{onTestUpTo}
|
||||
{onEditInput}
|
||||
flowModuleStates={$localModuleStates}
|
||||
flowModuleStates={localModuleStates}
|
||||
{testModuleStates}
|
||||
{isOwner}
|
||||
{individualStepTests}
|
||||
{flowJob}
|
||||
{showJobStatus}
|
||||
{suspendStatus}
|
||||
suspendStatus={suspendStatus.val}
|
||||
{flowHasChanged}
|
||||
onDelete={(id) => {
|
||||
dependents = getDependentComponents(id, flowStore.val)
|
||||
@@ -408,7 +410,7 @@
|
||||
}
|
||||
refreshStateStore(flowStore)
|
||||
onDelete?.(id)
|
||||
delete $flowStateStore[id]
|
||||
delete flowStateStore.val[id]
|
||||
}
|
||||
|
||||
if (Object.keys(dependents).length > 0) {
|
||||
@@ -520,7 +522,6 @@
|
||||
if (['branchone', 'branchall'].includes(detail.kind)) {
|
||||
await addBranch(targetModules[detail.index ?? 0].id)
|
||||
}
|
||||
$flowStateStore = $flowStateStore
|
||||
refreshStateStore(flowStore)
|
||||
dispatch('change')
|
||||
}
|
||||
@@ -570,9 +571,8 @@
|
||||
mod.id = newId
|
||||
}
|
||||
})
|
||||
$flowStateStore[newId] = $flowStateStore[id]
|
||||
delete $flowStateStore[id]
|
||||
$flowStateStore = $flowStateStore
|
||||
flowStateStore.val[newId] = flowStateStore.val[id]
|
||||
delete flowStateStore.val[id]
|
||||
refreshStateStore(flowStore)
|
||||
$selectedId = newId
|
||||
}}
|
||||
|
||||
@@ -70,7 +70,6 @@ function getFlowInput(
|
||||
const topFlowInput = schemaToObject(schema, args)
|
||||
|
||||
const parentState = parentModule ? flowState[parentModule.id] : undefined
|
||||
|
||||
if (parentState && parentModule) {
|
||||
if (
|
||||
parentState.previewArgs &&
|
||||
@@ -276,8 +275,9 @@ declare const results = ${JSON.stringify(results)};
|
||||
*/
|
||||
declare const previous_result: ${previousId ? JSON.stringify(results[previousId]) : 'any'};
|
||||
|
||||
${resume
|
||||
? `
|
||||
${
|
||||
resume
|
||||
? `
|
||||
/**
|
||||
* resume payload
|
||||
*/
|
||||
@@ -288,8 +288,8 @@ declare const resume: any
|
||||
*/
|
||||
declare const approvers: string
|
||||
`
|
||||
: ''
|
||||
}
|
||||
: ''
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
getContext<FlowEditorContext | undefined>('FlowEditorContext') || {}
|
||||
|
||||
onMount(() => {
|
||||
testSteps?.updateStepArgs(id, $flowStateStore, flowStore?.val, previewArgs?.val)
|
||||
testSteps?.updateStepArgs(id, flowStateStore, flowStore?.val, previewArgs?.val)
|
||||
})
|
||||
|
||||
const input = $derived(testSteps?.getStepArgs(id)?.value)
|
||||
@@ -44,7 +44,7 @@
|
||||
{#if testSteps?.isArgManuallySet(id, key)}
|
||||
<button
|
||||
onclick={() => {
|
||||
testSteps?.evalArg(id, key, $flowStateStore, flowStore?.val, previewArgs?.val)
|
||||
testSteps?.evalArg(id, key, flowStateStore, flowStore?.val, previewArgs?.val)
|
||||
}}
|
||||
title="Re-evaluate input"
|
||||
class="-my-1 ml-0.5 hover:text-primary dark:hover:text-primary dark:text-gray-500 text-gray-300"
|
||||
|
||||
@@ -37,31 +37,31 @@ export type ExtendedOpenFlow = OpenFlow & {
|
||||
|
||||
export type FlowInputEditorState = {
|
||||
selectedTab:
|
||||
| 'inputEditor'
|
||||
| 'history'
|
||||
| 'savedInputs'
|
||||
| 'json'
|
||||
| 'captures'
|
||||
| 'firstStepInputs'
|
||||
| undefined
|
||||
| 'inputEditor'
|
||||
| 'history'
|
||||
| 'savedInputs'
|
||||
| 'json'
|
||||
| 'captures'
|
||||
| 'firstStepInputs'
|
||||
| undefined
|
||||
editPanelSize: number | undefined
|
||||
payloadData: Record<string, any> | undefined
|
||||
}
|
||||
|
||||
export type CurrentEditor =
|
||||
| ((
|
||||
| {
|
||||
type: 'script'
|
||||
editor: Editor
|
||||
showDiffMode: () => void
|
||||
hideDiffMode: () => void
|
||||
diffMode: boolean
|
||||
lastDeployedCode: string | undefined
|
||||
}
|
||||
| { type: 'iterator'; editor: SimpleEditor }
|
||||
) & {
|
||||
stepId: string
|
||||
})
|
||||
| {
|
||||
type: 'script'
|
||||
editor: Editor
|
||||
showDiffMode: () => void
|
||||
hideDiffMode: () => void
|
||||
diffMode: boolean
|
||||
lastDeployedCode: string | undefined
|
||||
}
|
||||
| { type: 'iterator'; editor: SimpleEditor }
|
||||
) & {
|
||||
stepId: string
|
||||
})
|
||||
| undefined
|
||||
|
||||
export type FlowEditorContext = {
|
||||
@@ -74,7 +74,7 @@ export type FlowEditorContext = {
|
||||
pathStore: Writable<string>
|
||||
flowStore: StateStore<ExtendedOpenFlow>
|
||||
flowInputEditorState: Writable<FlowInputEditorState>
|
||||
flowStateStore: Writable<FlowState>
|
||||
flowStateStore: StateStore<FlowState>
|
||||
testSteps: TestSteps
|
||||
saveDraft: () => void
|
||||
initialPathStore: Writable<string>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { cleanExpr, emptySchema } from '$lib/utils'
|
||||
import { get, type Writable } from 'svelte/store'
|
||||
import { get } from 'svelte/store'
|
||||
import type { FlowModuleState } from './flowState'
|
||||
import { type PickableProperties, dfs } from './previousResults'
|
||||
import { NEVER_TESTED_THIS_FAR } from './models'
|
||||
@@ -206,7 +206,7 @@ export function checkIfParentLoop(
|
||||
export function updateDerivedModuleStatesFromTestJobs(
|
||||
moduleId: string | undefined,
|
||||
moduleTestStates: ModulesTestStates | undefined,
|
||||
moduleStates: Writable<Record<string, GraphModuleState>> | undefined
|
||||
moduleStates: Record<string, GraphModuleState> | undefined
|
||||
) {
|
||||
if (!moduleId || !moduleTestStates || !moduleStates) {
|
||||
return
|
||||
@@ -238,9 +238,8 @@ export function updateDerivedModuleStatesFromTestJobs(
|
||||
}
|
||||
}
|
||||
|
||||
// Update the store with test job states
|
||||
moduleStates.update((currentStates) => ({
|
||||
...currentStates,
|
||||
return {
|
||||
...moduleStates,
|
||||
...newStates
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
import AssetsOverflowedNode from './renderers/nodes/AssetsOverflowedNode.svelte'
|
||||
import type { FlowGraphAssetContext } from '../flows/types'
|
||||
import { ChangeTracker } from '$lib/svelte5Utils.svelte'
|
||||
import type { ModulesTestStates } from '../modulesTest.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
let useDataflow: Writable<boolean | undefined> = writable<boolean | undefined>(false)
|
||||
|
||||
@@ -73,6 +75,7 @@
|
||||
maxHeight?: number | undefined
|
||||
notSelectable?: boolean
|
||||
flowModuleStates?: Record<string, GraphModuleState> | undefined
|
||||
testModuleStates?: ModulesTestStates
|
||||
selectedId?: Writable<string | undefined>
|
||||
path?: string | undefined
|
||||
newFlow?: boolean
|
||||
@@ -95,7 +98,7 @@
|
||||
individualStepTests?: boolean
|
||||
flowJob?: Job | undefined
|
||||
showJobStatus?: boolean
|
||||
suspendStatus?: Writable<Record<string, { job: Job; nb: number }>>
|
||||
suspendStatus?: Record<string, { job: Job; nb: number }>
|
||||
onDelete?: (id: string) => void
|
||||
onInsert?: (detail: {
|
||||
sourceId?: string
|
||||
@@ -144,6 +147,7 @@
|
||||
maxHeight = undefined,
|
||||
notSelectable = false,
|
||||
flowModuleStates = undefined,
|
||||
testModuleStates = undefined,
|
||||
selectedId = writable<string | undefined>(undefined),
|
||||
path = undefined,
|
||||
newFlow = false,
|
||||
@@ -171,7 +175,7 @@
|
||||
individualStepTests = false,
|
||||
flowJob = undefined,
|
||||
showJobStatus = false,
|
||||
suspendStatus = writable({}),
|
||||
suspendStatus = {},
|
||||
flowHasChanged = false
|
||||
}: Props = $props()
|
||||
|
||||
@@ -207,12 +211,16 @@
|
||||
)
|
||||
}
|
||||
|
||||
let lastNodes: [NodeLayout[], (Node & NodeLayout)[]] | undefined = undefined
|
||||
function layoutNodes(nodes: NodeLayout[]): (Node & NodeLayout)[] {
|
||||
type NodeDep = { id: string; parentIds?: string[]; offset?: number }
|
||||
type NodePos = { position: { x: number; y: number } }
|
||||
let lastNodes: [NodeDep[], (NodeDep & NodePos)[]] | undefined = undefined
|
||||
function layoutNodes(nodes: NodeDep[]): (NodeDep & NodePos)[] {
|
||||
let lastResult = lastNodes?.[1]
|
||||
if (lastResult && nodes === lastNodes?.[0]) {
|
||||
if (lastResult && deepEqual(nodes, lastNodes?.[0])) {
|
||||
console.debug('layoutNodes', 'same nodes')
|
||||
return lastResult
|
||||
}
|
||||
console.debug('layoutNodes', nodes.length)
|
||||
let seenId: string[] = []
|
||||
for (const n of nodes) {
|
||||
if (seenId.includes(n.id)) {
|
||||
@@ -222,7 +230,7 @@
|
||||
}
|
||||
|
||||
let nodeWidths: Record<string, number> = {}
|
||||
const nodes2 = nodes.map((n) => {
|
||||
const nodes2: (NodeDep & NodePos)[] = nodes.map((n) => {
|
||||
return { ...n, position: { x: 0, y: 0 } }
|
||||
})
|
||||
for (const n of topologicalSort(nodes)) {
|
||||
@@ -238,7 +246,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
const dag = dagStratify().id(({ id }: Node) => id)(nodes2)
|
||||
const dag = dagStratify().id(({ id }: NodeDep & NodePos) => id)(nodes2)
|
||||
|
||||
let boxSize: any
|
||||
try {
|
||||
@@ -262,12 +270,11 @@
|
||||
|
||||
const yOffset = insertable ? 100 : 0
|
||||
const newNodes = dag.descendants().map((des) => ({
|
||||
...des.data,
|
||||
id: des.data.id,
|
||||
position: {
|
||||
x: des.x
|
||||
? // @ts-ignore
|
||||
(des.data.data.offset ?? 0) +
|
||||
(des.data.offset ?? 0) +
|
||||
// @ts-ignore
|
||||
des.x +
|
||||
(fullSize ? fullWidth : width) / 2 -
|
||||
@@ -349,7 +356,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
let moduleTracker = new ChangeTracker($state.snapshot(modules))
|
||||
let moduleTracker = new ChangeTracker(
|
||||
$state.snapshot([modules, failureModule, preprocessorModule])
|
||||
)
|
||||
|
||||
let nodes = $state.raw<Node[]>([])
|
||||
let edges = $state.raw<Edge[]>([])
|
||||
@@ -372,10 +381,23 @@
|
||||
if (graph.error) {
|
||||
return
|
||||
}
|
||||
let newGraph = graph
|
||||
newGraph.nodes.sort((a, b) => b.id.localeCompare(a.id))
|
||||
// console.log('compute')
|
||||
;[nodes, edges] = computeAssetNodes(layoutNodes(newGraph.nodes), newGraph.edges)
|
||||
|
||||
let layoutedNodes = layoutNodes(
|
||||
Object.values(graph.nodes).map((n) => ({
|
||||
id: n.id,
|
||||
parentIds: n.parentIds,
|
||||
offset: n.data.offset ?? 0
|
||||
}))
|
||||
)
|
||||
let newNodes: (Node & NodeLayout)[] = layoutedNodes.map((n) => {
|
||||
return {
|
||||
...n,
|
||||
...graph.nodes[n.id]
|
||||
}
|
||||
})
|
||||
;[nodes, edges] = computeAssetNodes(newNodes, graph.edges)
|
||||
console.log('nodes', nodes)
|
||||
await tick()
|
||||
height = Math.max(...nodes.map((n) => n.position.y + NODE.height + 100), minHeight)
|
||||
}
|
||||
@@ -420,8 +442,11 @@
|
||||
})
|
||||
$effect(() => {
|
||||
readFieldsRecursively(modules)
|
||||
untrack(() => moduleTracker.track($state.snapshot(modules)))
|
||||
untrack(() =>
|
||||
moduleTracker.track($state.snapshot([modules, failureModule, preprocessorModule]))
|
||||
)
|
||||
})
|
||||
|
||||
let graph = $derived.by(() => {
|
||||
moduleTracker.counter
|
||||
return graphBuilder(
|
||||
@@ -429,8 +454,9 @@
|
||||
{
|
||||
disableAi,
|
||||
insertable,
|
||||
flowModuleStates,
|
||||
selectedId: $selectedId,
|
||||
flowModuleStates: untrack(() => flowModuleStates),
|
||||
testModuleStates: untrack(() => testModuleStates),
|
||||
selectedId: untrack(() => $selectedId),
|
||||
path,
|
||||
newFlow,
|
||||
cache,
|
||||
@@ -445,18 +471,19 @@
|
||||
flowHasChanged,
|
||||
additionalAssetsMap: flowGraphAssetsCtx?.val.additionalAssetsMap
|
||||
},
|
||||
failureModule,
|
||||
preprocessorModule,
|
||||
untrack(() => failureModule),
|
||||
untrack(() => preprocessorModule),
|
||||
eventHandler,
|
||||
success,
|
||||
$useDataflow,
|
||||
$selectedId,
|
||||
untrack(() => $selectedId),
|
||||
moving,
|
||||
simplifiableFlow,
|
||||
triggerNode ? path : undefined,
|
||||
expandedSubflows
|
||||
)
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
;[graph, allowSimplifiedPoll]
|
||||
untrack(() => updateStores())
|
||||
|
||||
@@ -5,8 +5,8 @@ import { dfsByModule } from '../flows/previousResults'
|
||||
import { defaultIfEmptyString } from '$lib/utils'
|
||||
import type { GraphModuleState } from './model'
|
||||
import { getFlowModuleAssets, type AssetWithAltAccessType } from '../assets/lib'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import { assetDisplaysAsOutputInFlowGraph } from './renderers/nodes/AssetNode.svelte'
|
||||
import type { ModulesTestStates, ModuleTestState } from '../modulesTest.svelte'
|
||||
|
||||
export type InsertKind =
|
||||
| 'script'
|
||||
@@ -80,6 +80,9 @@ export function buildPrefix(prefix: string | undefined, id: string): string {
|
||||
export type NodeLayout = {
|
||||
id: string
|
||||
parentIds?: string[]
|
||||
data: {
|
||||
offset?: number
|
||||
}
|
||||
} & FlowNode
|
||||
|
||||
export type FlowNode =
|
||||
@@ -129,7 +132,8 @@ export type ModuleN = {
|
||||
parentIds: string[]
|
||||
eventHandlers: GraphEventHandlers
|
||||
moving: string | undefined
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
flowModuleState: GraphModuleState | undefined
|
||||
testModuleState: ModuleTestState | undefined
|
||||
insertable: boolean
|
||||
editMode: boolean
|
||||
flowJob: Job | undefined
|
||||
@@ -146,7 +150,7 @@ export type BranchAllStartN = {
|
||||
id: string
|
||||
branchIndex: number
|
||||
eventHandlers: GraphEventHandlers
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
flowModuleState: GraphModuleState | undefined
|
||||
insertable: boolean
|
||||
branchOne: boolean
|
||||
}
|
||||
@@ -158,7 +162,7 @@ export type BranchAllEndN = {
|
||||
offset: number
|
||||
id: string
|
||||
eventHandlers: GraphEventHandlers
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
flowModuleState: GraphModuleState | undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +173,7 @@ export type ForLoopEndN = {
|
||||
id: string
|
||||
eventHandlers: GraphEventHandlers
|
||||
simplifiedTriggerView: boolean
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
flowModuleState: GraphModuleState | undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +183,7 @@ export type ForLoopStartN = {
|
||||
offset: number
|
||||
id: string
|
||||
eventHandlers: GraphEventHandlers
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
flowModuleState: GraphModuleState | undefined
|
||||
selectedId: string | undefined
|
||||
editMode: boolean
|
||||
simplifiedTriggerView: boolean
|
||||
@@ -219,7 +223,7 @@ export type BranchOneStartN = {
|
||||
offset: number
|
||||
id: string
|
||||
eventHandlers: GraphEventHandlers
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
flowModuleState: GraphModuleState | undefined
|
||||
selected: boolean
|
||||
insertable: boolean
|
||||
label: string
|
||||
@@ -235,7 +239,7 @@ export type BranchOneEndN = {
|
||||
offset: number
|
||||
id: string
|
||||
eventHandlers: GraphEventHandlers
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
flowModuleState: GraphModuleState | undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +262,7 @@ export type NoBranchN = {
|
||||
offset: number
|
||||
id: string
|
||||
eventHandlers: GraphEventHandlers
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
flowModuleState: GraphModuleState | undefined
|
||||
branchOne: boolean
|
||||
label: string
|
||||
branchIndex: number
|
||||
@@ -291,22 +295,22 @@ export type AssetsOverflowedN = {
|
||||
}
|
||||
}
|
||||
|
||||
export function topologicalSort(nodes: NodeLayout[]): NodeLayout[] {
|
||||
const nodeMap = new Map(nodes.map(n => [n.id, n]));
|
||||
const result: NodeLayout[] = [];
|
||||
const visited = new Set<string>();
|
||||
export function topologicalSort(nodes: { id: string; parentIds?: string[] }[]): { id: string; parentIds?: string[] }[] {
|
||||
const nodeMap = new Map(nodes.map((n) => [n.id, n]))
|
||||
const result: { id: string; parentIds?: string[] }[] = []
|
||||
const visited = new Set<string>()
|
||||
|
||||
function visit(id: string): void {
|
||||
if (visited.has(id)) return;
|
||||
visited.add(id);
|
||||
if (visited.has(id)) return
|
||||
visited.add(id)
|
||||
|
||||
const node = nodeMap.get(id)!;
|
||||
node.parentIds?.forEach(visit);
|
||||
result.push(node);
|
||||
const node = nodeMap.get(id)!
|
||||
node.parentIds?.forEach(visit)
|
||||
result.push(node)
|
||||
}
|
||||
|
||||
nodes.forEach(n => visit(n.id));
|
||||
return result.reverse();
|
||||
nodes.forEach((n) => visit(n.id))
|
||||
return result.reverse()
|
||||
}
|
||||
|
||||
// input2: InputNode,
|
||||
@@ -330,6 +334,7 @@ export function graphBuilder(
|
||||
disableAi: boolean
|
||||
insertable: boolean
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
testModuleStates: ModulesTestStates | undefined
|
||||
selectedId: string | undefined
|
||||
path: string | undefined
|
||||
newFlow: boolean
|
||||
@@ -341,7 +346,7 @@ export function graphBuilder(
|
||||
individualStepTests: boolean
|
||||
flowJob: Job | undefined
|
||||
showJobStatus: boolean
|
||||
suspendStatus: Writable<Record<string, { job: Job; nb: number }>>
|
||||
suspendStatus: Record<string, { job: Job; nb: number }>
|
||||
flowHasChanged: boolean
|
||||
additionalAssetsMap?: Record<string, AssetWithAltAccessType[]>
|
||||
},
|
||||
@@ -360,18 +365,20 @@ export function graphBuilder(
|
||||
// flowIsSimplifiable?: boolean
|
||||
// }
|
||||
): {
|
||||
nodes: NodeLayout[]
|
||||
nodes: { [key: string]: NodeLayout }
|
||||
edges: Edge[]
|
||||
error?: string | undefined
|
||||
} {
|
||||
console.debug('Building graph')
|
||||
const nodes: NodeLayout[] = []
|
||||
const edges: Edge[] = []
|
||||
|
||||
try {
|
||||
if (!modules) {
|
||||
return { nodes, edges }
|
||||
return { nodes: {}, edges: [] }
|
||||
}
|
||||
|
||||
const nodes: NodeLayout[] = []
|
||||
const edges: Edge[] = []
|
||||
|
||||
function addNode(module: FlowModule, offset: number) {
|
||||
const duplicated = nodes.find((n) => n.id === module.id)
|
||||
if (duplicated) {
|
||||
@@ -392,7 +399,8 @@ export function graphBuilder(
|
||||
parentIds: [],
|
||||
eventHandlers: eventHandlers,
|
||||
moving: moving,
|
||||
flowModuleStates: extra.flowModuleStates,
|
||||
flowModuleState: extra.flowModuleStates?.[module.id],
|
||||
testModuleState: extra.testModuleStates?.states?.[module.id],
|
||||
insertable: extra.insertable,
|
||||
editMode: extra.editMode,
|
||||
isOwner: extra.isOwner,
|
||||
@@ -612,7 +620,7 @@ export function graphBuilder(
|
||||
offset: currentOffset,
|
||||
id: module.id,
|
||||
eventHandlers: eventHandlers,
|
||||
flowModuleStates: extra.flowModuleStates
|
||||
flowModuleState: extra.flowModuleStates?.[module.id]
|
||||
},
|
||||
type: 'branchAllEnd'
|
||||
}
|
||||
@@ -628,7 +636,7 @@ export function graphBuilder(
|
||||
id: module.id,
|
||||
branchIndex: -1,
|
||||
eventHandlers: eventHandlers,
|
||||
flowModuleStates: extra.flowModuleStates,
|
||||
flowModuleState: extra.flowModuleStates?.[module.id],
|
||||
branchOne: false,
|
||||
label: 'No branches'
|
||||
},
|
||||
@@ -655,7 +663,7 @@ export function graphBuilder(
|
||||
id: module.id,
|
||||
branchIndex: branchIndex,
|
||||
eventHandlers: eventHandlers,
|
||||
flowModuleStates: extra.flowModuleStates,
|
||||
flowModuleState: extra.flowModuleStates?.[module.id],
|
||||
insertable: extra.insertable,
|
||||
branchOne: false
|
||||
},
|
||||
@@ -703,7 +711,7 @@ export function graphBuilder(
|
||||
simplifiedTriggerView,
|
||||
eventHandlers: eventHandlers,
|
||||
editMode: extra.editMode,
|
||||
flowModuleStates: extra.flowModuleStates,
|
||||
flowModuleState: extra.flowModuleStates?.[module.id],
|
||||
selectedId: extra.selectedId
|
||||
},
|
||||
type: 'forLoopStart'
|
||||
@@ -726,7 +734,7 @@ export function graphBuilder(
|
||||
id: module.id,
|
||||
eventHandlers: eventHandlers,
|
||||
simplifiedTriggerView,
|
||||
flowModuleStates: extra.flowModuleStates
|
||||
flowModuleState: extra.flowModuleStates?.[module.id]
|
||||
},
|
||||
type: 'forLoopEnd'
|
||||
}
|
||||
@@ -800,7 +808,7 @@ export function graphBuilder(
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
eventHandlers: eventHandlers,
|
||||
flowModuleStates: extra.flowModuleStates,
|
||||
flowModuleState: extra.flowModuleStates?.[module.id],
|
||||
id: module.id
|
||||
},
|
||||
type: 'branchOneEnd'
|
||||
@@ -832,7 +840,7 @@ export function graphBuilder(
|
||||
eventHandlers: eventHandlers,
|
||||
insertable: extra.insertable,
|
||||
preLabel: undefined,
|
||||
flowModuleStates: extra.flowModuleStates,
|
||||
flowModuleState: extra.flowModuleStates?.[module.id],
|
||||
selected: false,
|
||||
modules: module.value.default
|
||||
},
|
||||
@@ -870,7 +878,7 @@ export function graphBuilder(
|
||||
branchIndex: branchIndex,
|
||||
eventHandlers: eventHandlers,
|
||||
insertable: extra.insertable,
|
||||
flowModuleStates: extra.flowModuleStates,
|
||||
flowModuleState: extra.flowModuleStates?.[module.id],
|
||||
selected: false,
|
||||
modules: branch.modules
|
||||
},
|
||||
@@ -1059,10 +1067,10 @@ export function graphBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
return { nodes, edges }
|
||||
return { nodes: Object.fromEntries(nodes.map((n) => [n.id, n])), edges }
|
||||
} catch (e) {
|
||||
return {
|
||||
nodes: [],
|
||||
nodes: {},
|
||||
edges: [],
|
||||
error: e
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FlowStatusModule, Job } from '$lib/gen'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
import type { FlowState } from '../flows/flowState'
|
||||
|
||||
export type ModuleHost = 'workspace' | 'inline' | 'hub'
|
||||
@@ -24,15 +24,18 @@ export type GraphModuleStates = {
|
||||
}
|
||||
|
||||
export type DurationStatus = {
|
||||
iteration_from?: number
|
||||
iteration_total?: number
|
||||
byJob: Record<string, { created_at?: number; started_at?: number; duration_ms?: number }>
|
||||
}
|
||||
|
||||
export type GlobalIterationBounds = {
|
||||
iteration_from?: number
|
||||
iteration_total?: number
|
||||
}
|
||||
|
||||
export type FlowStatusViewerContext = {
|
||||
flowStateStore?: Writable<FlowState>
|
||||
retryStatus: Writable<Record<string, number | undefined>>
|
||||
suspendStatus: Writable<Record<string, { nb: number; job: Job }>>
|
||||
flowStateStore?: FlowState
|
||||
retryStatus: StateStore<Record<string, number | undefined>>
|
||||
suspendStatus: StateStore<Record<string, { nb: number; job: Job }>>
|
||||
hideDownloadInGraph?: boolean
|
||||
hideTimeline?: boolean
|
||||
hideNodeDefinition?: boolean
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
flowModuleStates: Record<string, GraphModuleState> | undefined
|
||||
isOwner: boolean
|
||||
flowJob: Job | undefined
|
||||
suspendStatus?: Writable<Record<string, { job: Job; nb: number }>>
|
||||
suspendStatus?: Record<string, { job: Job; nb: number }>
|
||||
shouldOffsetInsertBtnDueToAssetNode?: boolean
|
||||
}
|
||||
} = $props()
|
||||
@@ -73,7 +73,7 @@
|
||||
data?.flowModuleStates?.[`${data.sourceId}-v`]?.type === 'WaitingForEvents'
|
||||
)
|
||||
|
||||
const suspendStatus: Writable<Record<string, { job: Job; nb: number }>> | undefined = $derived(
|
||||
let suspendStatus: Record<string, { job: Job; nb: number }> | undefined = $derived(
|
||||
data?.suspendStatus
|
||||
)
|
||||
</script>
|
||||
@@ -176,9 +176,9 @@
|
||||
isOwner={data.isOwner}
|
||||
light
|
||||
/>
|
||||
{:else if $suspendStatus && Object.keys($suspendStatus).length > 0}
|
||||
{:else if suspendStatus && Object.keys(suspendStatus).length > 0}
|
||||
<div class="flex gap-2 flex-col">
|
||||
{#each Object.values($suspendStatus) as suspendCount (suspendCount.job.id)}
|
||||
{#each Object.values(suspendStatus) as suspendCount (suspendCount.job.id)}
|
||||
<FlowStatusWaitingForEvents
|
||||
job={suspendCount.job}
|
||||
workspaceId={$workspaceStore!}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
selected={false}
|
||||
bgColor={getStateColor(undefined, darkMode)}
|
||||
bgHoverColor={getStateHoverColor(undefined, darkMode)}
|
||||
borderColor={getStateColor(data?.flowModuleStates?.[data?.id]?.type, darkMode)}
|
||||
borderColor={getStateColor(data?.flowModuleState?.type, darkMode)}
|
||||
on:select={(e) => {
|
||||
data?.eventHandlers?.select(e.detail)
|
||||
}}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
let { data }: Props = $props()
|
||||
|
||||
let borderStatus = $derived(
|
||||
computeBorderStatus(data.branchIndex, 'branchall', data.flowModuleStates?.[data.id])
|
||||
computeBorderStatus(data.branchIndex, 'branchall', data.flowModuleState)
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
let { data }: Props = $props()
|
||||
|
||||
let borderStatus = $derived(
|
||||
computeBorderStatus(data.branchIndex + 1, 'branchone', data.flowModuleStates?.[data.id])
|
||||
computeBorderStatus(data.branchIndex + 1, 'branchone', data.flowModuleState)
|
||||
)
|
||||
</script>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
id={data.id}
|
||||
hideId
|
||||
bgColor={getStateColor(undefined, darkMode)}
|
||||
borderColor={getStateColor(data.flowModuleStates?.[data.id]?.type, darkMode)}
|
||||
borderColor={getStateColor(data.flowModuleState?.type, darkMode)}
|
||||
on:select={(e) => {
|
||||
setTimeout(() => data?.eventHandlers?.select(e.detail))
|
||||
}}
|
||||
@@ -33,7 +33,7 @@
|
||||
selected={false}
|
||||
id={data.id}
|
||||
bgColor={getStateColor(undefined, darkMode)}
|
||||
borderColor={getStateColor(data.flowModuleStates?.[data.id]?.type, darkMode)}
|
||||
borderColor={getStateColor(data.flowModuleState?.type, darkMode)}
|
||||
on:select={(e) => {
|
||||
setTimeout(() => data?.eventHandlers?.select(e.detail))
|
||||
}}
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
hideId
|
||||
bgColor={getStateColor(undefined, darkMode)}
|
||||
bgHoverColor={getStateHoverColor(undefined, darkMode)}
|
||||
borderColor={getStateColor(computeStatus(data.flowModuleStates?.[data.id]), darkMode)}
|
||||
borderColor={getStateColor(computeStatus(data.flowModuleState), darkMode)}
|
||||
on:select={(e) => {
|
||||
setTimeout(() => data?.eventHandlers?.select(e.detail))
|
||||
}}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import NodeWrapper from './NodeWrapper.svelte'
|
||||
import { getStateColor, getStateHoverColor } from '../../util'
|
||||
import type { ModuleN } from '../../graphBuilder.svelte'
|
||||
import { jobToGraphModuleState } from '$lib/components/modulesTest.svelte'
|
||||
|
||||
interface Props {
|
||||
data: ModuleN['data']
|
||||
@@ -13,20 +14,25 @@
|
||||
|
||||
let { data }: Props = $props()
|
||||
|
||||
let moduleState = $derived(data.flowModuleStates?.[data.id])
|
||||
let state = $derived.by(() => {
|
||||
return data.testModuleState
|
||||
? (jobToGraphModuleState(data.testModuleState) ?? data.flowModuleState)
|
||||
: data.flowModuleState
|
||||
})
|
||||
|
||||
let flowJobs = $derived(
|
||||
moduleState?.flow_jobs
|
||||
state?.flow_jobs
|
||||
? {
|
||||
flowJobs: moduleState?.flow_jobs,
|
||||
selected: moduleState?.selectedForloopIndex ?? 0,
|
||||
selectedManually: moduleState?.selectedForLoopSetManually,
|
||||
flowJobsSuccess: moduleState?.flow_jobs_success
|
||||
flowJobs: state?.flow_jobs,
|
||||
selected: state?.selectedForloopIndex ?? 0,
|
||||
selectedManually: state?.selectedForLoopSetManually,
|
||||
flowJobsSuccess: state?.flow_jobs_success
|
||||
}
|
||||
: (undefined as any)
|
||||
)
|
||||
|
||||
let type = $derived.by(() => {
|
||||
let typ = data.flowModuleStates?.[data.id]?.type
|
||||
let typ = state?.type
|
||||
if (!typ && flowJobs) {
|
||||
return 'InProgress'
|
||||
}
|
||||
@@ -59,27 +65,22 @@
|
||||
annotation={flowJobs &&
|
||||
(data.module.value.type === 'forloopflow' || data.module.value.type === 'whileloopflow')
|
||||
? 'Iteration: ' +
|
||||
((moduleState?.selectedForloopIndex ?? 0) >= 0
|
||||
? (moduleState?.selectedForloopIndex ?? 0) + 1
|
||||
: moduleState?.flow_jobs?.length) +
|
||||
((state?.selectedForloopIndex ?? 0) >= 0
|
||||
? (state?.selectedForloopIndex ?? 0) + 1
|
||||
: state?.flow_jobs?.length) +
|
||||
'/' +
|
||||
(moduleState?.iteration_total ?? '?')
|
||||
(state?.iteration_total ?? '?')
|
||||
: ''}
|
||||
bgColor={getStateColor(
|
||||
data.editMode ? undefined : type,
|
||||
darkMode,
|
||||
true,
|
||||
moduleState?.skipped
|
||||
)}
|
||||
bgColor={getStateColor(data.editMode ? undefined : type, darkMode, true, state?.skipped)}
|
||||
bgHoverColor={getStateHoverColor(
|
||||
data.editMode ? undefined : type,
|
||||
darkMode,
|
||||
true,
|
||||
moduleState?.skipped
|
||||
state?.skipped
|
||||
)}
|
||||
moving={data.moving}
|
||||
duration_ms={moduleState?.duration_ms}
|
||||
retries={moduleState?.retries}
|
||||
duration_ms={state?.duration_ms}
|
||||
retries={state?.retries}
|
||||
{flowJobs}
|
||||
on:delete={(e) => {
|
||||
data.eventHandlers.delete(e.detail, '')
|
||||
@@ -108,7 +109,7 @@
|
||||
isOwner={data.isOwner}
|
||||
{type}
|
||||
{darkMode}
|
||||
skipped={moduleState?.skipped}
|
||||
skipped={state?.skipped}
|
||||
/>
|
||||
|
||||
<div class="absolute -bottom-10 left-1/2 transform -translate-x-1/2 z-10">
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
selected={false}
|
||||
bgColor={getStateColor(undefined, darkMode)}
|
||||
bgHoverColor={getStateHoverColor(undefined, darkMode)}
|
||||
borderColor={getStateColor(data?.flowModuleStates?.[data?.id]?.type, darkMode)}
|
||||
borderColor={getStateColor(data?.flowModuleState?.type, darkMode)}
|
||||
on:select={(e) => {
|
||||
setTimeout(() => data?.eventHandlers?.select(e.detail))
|
||||
}}
|
||||
|
||||
@@ -1,17 +1,50 @@
|
||||
import type { Job } from '$lib/gen'
|
||||
import type { GraphModuleState } from './graph'
|
||||
|
||||
type moduleTestState = {
|
||||
export type ModuleTestState = {
|
||||
loading: boolean
|
||||
cancel?: () => Promise<void>
|
||||
testJob?: Job
|
||||
hiddenInGraph?: boolean
|
||||
}
|
||||
|
||||
export class ModulesTestStates {
|
||||
states: Record<string, moduleTestState> = $state({})
|
||||
states: Record<string, ModuleTestState> = $state({})
|
||||
runTestCb?: (moduleId: string) => void
|
||||
|
||||
hideJobsInGraph() {
|
||||
for (const state of Object.values(this.states)) {
|
||||
state.hiddenInGraph = true
|
||||
}
|
||||
}
|
||||
constructor(runTestCb?: (moduleId: string) => void) {
|
||||
this.states = {}
|
||||
this.runTestCb = runTestCb
|
||||
}
|
||||
}
|
||||
|
||||
export function jobToGraphModuleState(testState: ModuleTestState): GraphModuleState | undefined {
|
||||
if (testState.hiddenInGraph) {
|
||||
return undefined
|
||||
} else if (testState.loading) {
|
||||
return {
|
||||
type: 'InProgress',
|
||||
args: {}
|
||||
}
|
||||
} else if (testState.testJob) {
|
||||
return {
|
||||
args: testState.testJob.args,
|
||||
type:
|
||||
testState.testJob.type === 'QueuedJob'
|
||||
? 'InProgress'
|
||||
: testState.testJob['success']
|
||||
? 'Success'
|
||||
: 'Failure',
|
||||
job_id: testState.testJob.id,
|
||||
tag: testState.testJob.tag,
|
||||
duration_ms: testState.testJob['duration_ms'],
|
||||
started_at: testState.testJob.started_at
|
||||
? new Date(testState.testJob.started_at).getTime()
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { type Writable, get } from 'svelte/store'
|
||||
import type { FlowState } from './flows/flowState'
|
||||
import { NEVER_TESTED_THIS_FAR } from './flows/models'
|
||||
import { JobService, type Flow, type FlowModule } from '$lib/gen'
|
||||
@@ -76,7 +75,7 @@ export class StepHistoryLoader {
|
||||
|
||||
async loadIndividualStepsStates(
|
||||
flow: Flow,
|
||||
flowStateStore: Writable<FlowState>,
|
||||
flowStateStore: FlowState,
|
||||
workspaceId: string,
|
||||
initialPath: string,
|
||||
path: string
|
||||
@@ -84,7 +83,7 @@ export class StepHistoryLoader {
|
||||
// Collect all modules that need loading
|
||||
const modulesToLoad: FlowModule[] = []
|
||||
dfs(flow.value.modules, (module) => {
|
||||
const prev = get(flowStateStore)[module.id]?.previewResult
|
||||
const prev = flowStateStore.val[module.id]?.previewResult
|
||||
if (!prev || prev === NEVER_TESTED_THIS_FAR) {
|
||||
modulesToLoad.push(module)
|
||||
// Initialize step state if it doesn't exist
|
||||
@@ -128,16 +127,12 @@ export class StepHistoryLoader {
|
||||
})
|
||||
|
||||
if ('result' in getJobResult) {
|
||||
flowStateStore.update((state) => ({
|
||||
...state,
|
||||
[module.id]: {
|
||||
...(state[module.id] ?? {}),
|
||||
previewResult: getJobResult.result,
|
||||
previewJobId: previousJobId[0].id,
|
||||
previewWorkspaceId: previousJobId[0].workspace_id,
|
||||
previewSuccess: getJobResult.success
|
||||
}
|
||||
}))
|
||||
flowStateStore.val[module.id] = {
|
||||
...(flowStateStore.val[module.id] ?? {}),
|
||||
previewResult: getJobResult.result,
|
||||
previewJobId: previousJobId[0].id,
|
||||
previewSuccess: getJobResult.success
|
||||
}
|
||||
this.#stepStates[module.id].initial =
|
||||
this.#stepStates[module.id].initial !== undefined
|
||||
? this.#stepStates[module.id].initial
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
on:error
|
||||
on:skipAll
|
||||
getSteps={(driver, options) => {
|
||||
const id = nextId($flowStateStore, flowStore.val)
|
||||
const id = nextId(flowStateStore, flowStore.val)
|
||||
const index = options?.indexToInsertAt ?? flowStore.val.value.modules.length
|
||||
|
||||
const steps = [
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
on:error
|
||||
on:skipAll
|
||||
getSteps={(driver, options) => {
|
||||
const id = nextId($flowStateStore, flowStore.val)
|
||||
const id = nextId(flowStateStore, flowStore.val)
|
||||
const index = options?.indexToInsertAt ?? flowStore.val.value.modules.length
|
||||
|
||||
let tempId = ''
|
||||
@@ -109,7 +109,7 @@
|
||||
'We can refer to the result of previous steps using the results object: results.a or use static values like [1,2,3] in this case.',
|
||||
onNextClick: () => {
|
||||
updateFlowModuleById(flowStore.val, id, (module) => {
|
||||
const newId = nextId($flowStateStore, flowStore.val)
|
||||
const newId = nextId(flowStateStore, flowStore.val)
|
||||
tempId = newId
|
||||
|
||||
if (module.value.type === 'forloopflow') {
|
||||
@@ -134,8 +134,8 @@
|
||||
]
|
||||
}
|
||||
|
||||
$flowStateStore[newId] = emptyFlowModuleState()
|
||||
let schema = $flowStateStore[newId].schema ?? { properties: {} }
|
||||
flowStateStore.val[newId] = emptyFlowModuleState()
|
||||
let schema = flowStateStore.val[newId].schema ?? { properties: {} }
|
||||
schema.properties = {
|
||||
x: {
|
||||
type: 'string',
|
||||
|
||||
@@ -5,14 +5,12 @@
|
||||
|
||||
import FlowBuilder from '$lib/components/FlowBuilder.svelte'
|
||||
import UnsavedConfirmationModal from '$lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte'
|
||||
import type { FlowState } from '$lib/components/flows/flowState'
|
||||
import { importFlowStore, initFlow } from '$lib/components/flows/flowStore'
|
||||
import { FlowService, type Flow } from '$lib/gen'
|
||||
import { initialArgsStore, userStore, workspaceStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { decodeState, emptySchema, type StateStore } from '$lib/utils'
|
||||
import { tick } from 'svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import { replaceScriptPlaceholderWithItsValues } from '$lib/hub'
|
||||
import type { Trigger } from '$lib/components/triggers/utils'
|
||||
|
||||
@@ -56,7 +54,7 @@
|
||||
schema: emptySchema()
|
||||
}
|
||||
})
|
||||
const flowStateStore = writable<FlowState>({})
|
||||
const flowStateStore = $state({ val: {} })
|
||||
|
||||
let draftTriggersFromUrl: Trigger[] | undefined = $state(undefined)
|
||||
let selectedTriggerIndexFromUrl: number | undefined = $state(undefined)
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
import { initFlow } from '$lib/components/flows/flowStore'
|
||||
import { goto } from '$lib/navigation'
|
||||
import { afterNavigate, replaceState } from '$app/navigation'
|
||||
import { writable } from 'svelte/store'
|
||||
import type { FlowState } from '$lib/components/flows/flowState'
|
||||
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import DiffDrawer from '$lib/components/DiffDrawer.svelte'
|
||||
import UnsavedConfirmationModal from '$lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte'
|
||||
@@ -60,7 +59,7 @@
|
||||
schema: emptySchema()
|
||||
}
|
||||
})
|
||||
const flowStateStore = writable<FlowState>({})
|
||||
const flowStateStore = $state({ val: {} })
|
||||
|
||||
let loading = $state(false)
|
||||
|
||||
|
||||
@@ -1006,11 +1006,11 @@
|
||||
{#if job?.id}
|
||||
<FlowStatusViewer
|
||||
jobId={job?.id ?? ''}
|
||||
on:jobsLoaded={({ detail }) => {
|
||||
job = detail
|
||||
onJobsLoaded={({ job: newJob }) => {
|
||||
job = newJob
|
||||
}}
|
||||
on:done={(e) => {
|
||||
job = e.detail
|
||||
onDone={({ job: newJob }) => {
|
||||
job = newJob
|
||||
}}
|
||||
initialJob={job}
|
||||
workspaceId={$workspaceStore}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { emptySchema, sendUserToast } from '$lib/utils'
|
||||
import { emptySchema, sendUserToast, type StateStore } from '$lib/utils'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import { onDestroy, onMount, setContext, untrack } from 'svelte'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
@@ -68,7 +68,7 @@
|
||||
})
|
||||
|
||||
let initialCode = JSON.stringify(flowStore, null, 4)
|
||||
const flowStateStore = writable({} as FlowState)
|
||||
const flowStateStore = $state({ val: {} }) as StateStore<FlowState>
|
||||
|
||||
const previewArgsStore = $state({ val: {} })
|
||||
const scriptEditorDrawer = writable(undefined)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import FlowWrapper from '$lib/components/FlowWrapper.svelte'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { getUserExt } from '$lib/user'
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
loadUser()
|
||||
|
||||
@@ -31,10 +30,10 @@
|
||||
}
|
||||
})
|
||||
|
||||
let flowStateStore = writable({})
|
||||
let flowStateStore = $state({ val: {} })
|
||||
|
||||
let customUi: FlowBuilderWhitelabelCustomUi = {
|
||||
tagLabel: 'agent',
|
||||
tagLabel: 'agent'
|
||||
// disableAi: true
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user