mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 00:08:55 +00:00
## 📦 Weekly NPM Dependencies Update This automated PR updates `web/` npm packages to their latest versions and regenerates `package-lock.json`. ### Updated Packages - **@rudderstack/analytics-js**: 3.6.0 → 3.31.6 - **@tanstack/vue-form**: ^1.29.1 → ^1.33.3 - **@tanstack/vue-table**: ^8.21.3 → ^9.0.0 - **@tanstack/vue-virtual**: ^3.13.16 → ^3.13.35 - **@vue-flow/core**: ^1.48.1 → ^1.48.2 - **acorn**: ^8.15.0 → ^8.18.0 - **acorn-walk**: ^8.3.4 → ^8.3.5 - **cron-parser**: ^5.4.0 → ^5.7.0 - **date-fns**: ^4.1.0 → ^4.4.0 - **dompurify**: ^3.4.12 → ^3.4.13 - **gridstack**: ^12.4.2 → ^13.0.2 - **js-beautify**: ^1.15.4 → ^2.0.3 - **js-cookie**: ^3.0.7 → ^3.0.8 - **js-yaml**: ^4.3.0 → ^5.2.3 - **lucide-vue-next**: ^0.562.0 → ^1.0.0 - **marked**: ^17.0.1 → ^18.0.9 - **moment-timezone**: ^0.6.0 → ^0.6.3 - **monaco-editor**: ^0.52.2 → ^0.56.0 - **node-polyfill-webpack-plugin**: ^4.0.0 → ^4.1.0 - **reka-ui**: ^2.9.6 → ^2.10.1 - **reodotdev**: ^1.0.0 → ^1.1.0 - **sql-formatter**: ^15.6.12 → ^15.8.2 - **uuid**: ^14.0.0 → ^14.0.1 - **vue**: ^3.5.26 → ^3.5.41 - **vue-i18n**: ^11.2.8 → ^11.4.8 - **vue-router**: ^4.6.4 → ^5.2.0 - **@cypress/vue**: ^6.0.2 → ^6.0.3 - **@cypress/webpack-dev-server**: ^5.4.1 → ^5.6.2 - **@iconify-json/majesticons**: ^1.2.4 → ^1.2.5 - **@iconify-json/material-symbols**: ^1.2.72 → ^1.2.88 - **@iconify-json/octicon**: ^1.2.25 → ^1.2.31 - **@intlify/core-base**: ^11.2.8 → ^11.4.8 - **@rushstack/eslint-patch**: ^1.15.0 → ^1.16.1 - **@tailwindcss/postcss**: ^4.1.18 → ^4.3.3 - **@tailwindcss/typography**: ^0.5.19 → ^0.5.20 - **@tailwindcss/vite**: ^4.1.18 → ^4.3.3 - **@types/jsdom**: ^27.0.0 → ^28.0.3 - **@types/leaflet**: ^1.9.21 → ^1.9.22 - **@types/luxon**: ^3.7.1 → ^3.7.3 - **@types/node**: ^25.0.3 → ^26.1.2 - **@vitejs/plugin-vue**: ^6.0.3 → ^6.0.8 - **@vitejs/plugin-vue-jsx**: ^5.1.3 → ^5.1.6 - **@vitest/coverage-v8**: ^4.0.16 → ^4.1.10 - **@vitest/ui**: ^4.0.16 → ^4.1.10 - **@vue/eslint-config-typescript**: ^14.6.0 → ^14.9.0 - **@vue/test-utils**: ^2.4.6 → ^2.4.11 - **@vue/tsconfig**: ^0.8.1 → ^0.9.1 - **autoprefixer**: ^10.4.23 → ^10.5.4 - **c8**: ^10.1.3 → ^12.0.0 - **cypress**: ^15.19.0 → ^15.20.0 - **cypress-localstorage-commands**: ^2.2.8 → ^2.3.0 - **dotenv**: ^17.2.3 → ^17.4.2 - **eslint**: ^9.39.2 → ^10.8.0 - **eslint-plugin-cypress**: ^5.2.0 → ^7.0.0 - **eslint-plugin-vue**: ^10.6.2 → ^10.10.0 - **fs-extra**: ^11.3.3 → ^11.4.0 - **happy-dom**: ^20.8.9 → ^20.11.1 - **jsdom**: ^27.4.0 → ^30.0.1 - **msw**: ^2.12.7 → ^2.15.0 - **postcss**: ^8.5.10 → ^8.5.26 - **postcss-html**: ^1.8.1 → ^2.0.0 - **prettier**: ^3.7.4 → ^3.9.6 - **rollup-plugin-visualizer**: ^6.0.5 → ^7.0.1 - **sass**: ^1.97.1 → ^1.102.0 - **sass-loader**: ^16.0.6 → ^17.0.0 - **start-server-and-test**: ^2.1.3 → ^3.0.12 - **stylelint**: ^17.14.0 → ^17.14.1 - **vite**: ^7.3.2 → ^8.2.0 - **vite-plugin-istanbul**: ^7.2.1 → ^9.0.1 - **vitest-canvas-mock**: ^1.1.3 → ^1.1.4 - **vue-tsc**: ^3.2.1 → ^3.3.9 - **typescript**: ~5.9.3 → ~7.0.2 - **vitest**: ^4.0.16 → ^4.1.10 ### Validation This workflow does **not** run any checks — type check, unit tests and build all run as normal PR checks below. Review the results there before merging; a red check usually means one of the upgrades above needs to be pinned back or accompanied by a code change. --- 🤖 This PR was automatically created by the Weekly NPM Update workflow --------- Co-authored-by: Bhargav <551527+bjp232004@users.noreply.github.com> Co-authored-by: Bhargav <BJP232004@GMAIL.COM> Co-authored-by: ktx-abhay <abhay.padamani@kiara.tech> Co-authored-by: Abhay Padamani <141122205+ktx-abhay@users.noreply.github.com>
381 lines
16 KiB
Vue
381 lines
16 KiB
Vue
<!-- Copyright 2026 OpenObserve Inc.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-->
|
|
|
|
<!--
|
|
Shared "pick a function" body for the flow canvases (Pipelines + Workflows).
|
|
Presentation + logic only — the surrounding chrome (drawer / modal + Save/Cancel)
|
|
lives in each module's wrapper, which reads the result via the exposed
|
|
`getPayload()`.
|
|
|
|
Behaviour:
|
|
- Toggle between picking a saved function and creating one inline (AddFunction).
|
|
- Read-only definition preview of the selected function.
|
|
- Optional "After Flattening" (RAF/RBF) toggle — shown when `showFlatten`.
|
|
- Self-fetches the function list, filtered to the host's language (`language`):
|
|
a pipeline runs VRL, a workflow Function node runs JS — so each only offers
|
|
functions it can actually execute.
|
|
|
|
⚠️ The `data-test` hooks below (create-function-toggle,
|
|
associate-function-select-function-input, associate-function-definition-section,
|
|
associate-function-after-flattening-toggle) are the ORIGINAL names this markup
|
|
had while it lived in pipeline/NodeForm/AssociateFunction.vue. The pipeline e2e
|
|
suite locates by them, so they are a CONTRACT — extracting this component moved
|
|
the markup, not the behaviour, so renaming them would only break tests for no
|
|
gain. Keep them as-is; don't "namespace" them to the new file name.
|
|
|
|
Props:
|
|
initialName — preselected function name (edit mode)
|
|
initialAfterFlatten — initial RAF/RBF value (default true)
|
|
showFlatten — show the After-Flattening toggle + guidelines (default true)
|
|
isUpdating — lock the select (edit-an-existing-function mode)
|
|
duplicateNames — names that are already used → shows "already associated"
|
|
|
|
Emits:
|
|
expand(boolean) — inline-create mode toggled (host can widen the drawer)
|
|
created(fn) — a new function was created inline
|
|
|
|
Exposes:
|
|
getPayload() — { name, after_flatten? } or null (and surfaces validation)
|
|
-->
|
|
<template>
|
|
<div
|
|
data-test="function-picker"
|
|
class="flex w-full flex-col gap-4"
|
|
:class="createNewFunction ? 'h-full min-h-0 gap-0' : ''"
|
|
>
|
|
<OSpinner v-if="loading" size="md" class="mx-auto my-8" />
|
|
|
|
<template v-else>
|
|
<!-- create / pick toggle -->
|
|
<div class="flex items-center gap-3" :class="createNewFunction ? 'shrink-0 px-4 pt-4' : ''">
|
|
<OSwitch
|
|
v-model="createNewFunction"
|
|
:label="t('flow.function.createNew')"
|
|
data-test="create-function-toggle"
|
|
/>
|
|
<!-- Saving here creates the function and drops the user back on the
|
|
select form — surprising enough that it needs saying up front, so
|
|
the note lives at the toggle, not at the save. The host owns the
|
|
wording: this component is shared, and a workflow must not be told
|
|
its function is being associated with a "pipeline". -->
|
|
<div
|
|
v-if="createNewFunction"
|
|
class="text-text-secondary text-sm"
|
|
data-test="create-function-note"
|
|
>
|
|
({{
|
|
wantsJs
|
|
? t("flow.function.createNewNoteWorkflow")
|
|
: t("alerts.newFunctionAssociationMsg")
|
|
}})
|
|
</div>
|
|
</div>
|
|
|
|
<!-- inline function editor (full-width; its own toolbar owns save/cancel) -->
|
|
<div v-if="createNewFunction" class="flow-add-function min-h-0 w-full flex-1">
|
|
<!-- ALWAYS a fresh function. AddFunction's `is-updated` means "editing an
|
|
EXISTING function" and disables its name input (`:disable-name`), so
|
|
it must never be fed our `isUpdating` — that flag means "editing the
|
|
NODE", which is a different thing. Conflating them left the user
|
|
unable to name the function they were creating. -->
|
|
<AddFunction
|
|
ref="addFunctionRef"
|
|
:is-updated="false"
|
|
:height-offset="75"
|
|
:sample-events="sampleEvents"
|
|
:forced-language="language"
|
|
:default-code="defaultCode"
|
|
@update:list="onFunctionCreation"
|
|
@cancel:hideform="cancelFunctionCreation"
|
|
/>
|
|
</div>
|
|
|
|
<OForm v-if="!createNewFunction" :form="form" class="flex flex-col gap-4">
|
|
<!-- required + "already associated" are both enforced by the shared
|
|
AssociateFunction schema, rendered inline on the field. -->
|
|
<OFormSelect
|
|
name="selectedFunction"
|
|
:options="functionOptions"
|
|
:label="t('flow.function.select')"
|
|
required
|
|
searchable
|
|
:readonly="isUpdating"
|
|
:disabled="isUpdating"
|
|
data-test="associate-function-select-function-input"
|
|
/>
|
|
|
|
<!-- read-only definition preview -->
|
|
<div
|
|
v-if="selectedFunction && selectedDefinition"
|
|
data-test="associate-function-definition-section"
|
|
class="mt-4 mb-4"
|
|
>
|
|
<!-- No `isDark` branching: every colour below is a --color-* token,
|
|
which already resolves per theme. (The old comment here claimed
|
|
`dark:` follows the OS prefers-color-scheme — that is stale;
|
|
tailwind.css binds it to the app's `.dark` class via
|
|
`@custom-variant dark`.) The header's two-stop gradient is now a
|
|
flat token surface — a gradient can't be expressed in tokens, and
|
|
the flat fill matches the rest of the app's card headers. -->
|
|
<OCard
|
|
class="function-definition-card border-border-default bg-surface-base rounded-default overflow-hidden border shadow-[0_0.125rem_0.25rem_color-mix(in_srgb,var(--color-black)_5%,transparent)]"
|
|
>
|
|
<OCardSection role="header" class="border-b-border-default bg-surface-subtle border-b">
|
|
<div class="text-text-heading text-base font-semibold">
|
|
{{ t("function.function_definition") }}
|
|
</div>
|
|
</OCardSection>
|
|
<OSeparator />
|
|
<OCardSection class="p-0">
|
|
<div
|
|
class="function-code-container bg-surface-subtle border-border-default relative max-h-[15.625rem] overflow-y-auto border"
|
|
>
|
|
<pre
|
|
class="text-compact text-text-code m-0 cursor-default border-0 bg-transparent p-4 font-mono leading-normal font-normal break-words whitespace-pre-wrap select-text"
|
|
>{{ selectedDefinition }}</pre>
|
|
</div>
|
|
</OCardSection>
|
|
</OCard>
|
|
</div>
|
|
|
|
<!-- After-Flattening (RAF/RBF) toggle + guidelines -->
|
|
<div v-if="showFlatten" class="flex w-full flex-col gap-3">
|
|
<OFormSwitch
|
|
name="afterFlattening"
|
|
:label="t('flow.function.flatten')"
|
|
data-test="associate-function-after-flattening-toggle"
|
|
/>
|
|
<!-- Same theme-aware banner tokens the pipeline + workflow Condition
|
|
notes use, so all three match and work in dark mode (the old
|
|
#f9f290/#2d3748 were light-only). These were --color-note-* until
|
|
#13173 removed that set; banner-warning-* is its replacement.
|
|
The `border-banner-warning-border` matters in LIGHT mode: the bg
|
|
(warning-50, ~#fefce8) is near-white, so without the border the
|
|
callout blends into the page and reads as unstyled — the canonical
|
|
warning banners (logstream/schema, settings/*) all carry it. -->
|
|
<div
|
|
class="bg-banner-warning-bg border-banner-warning-border text-banner-warning-text rounded-default flex w-full flex-col gap-2 border p-3"
|
|
>
|
|
<div class="text-sm">
|
|
{{ t("flow.function.guidelinesTitle") }}
|
|
</div>
|
|
<div class="flex flex-col gap-1 text-sm">
|
|
<div class="flex items-start gap-2">
|
|
<OIcon name="info" size="sm" class="text-status-warning-text mt-0.5 shrink-0" />
|
|
<span>
|
|
<span class="text-text-link font-bold">{{ t("flow.function.rbf") }}</span>
|
|
{{ t("flow.function.rbfDesc") }}
|
|
</span>
|
|
</div>
|
|
<div class="flex items-start gap-2">
|
|
<OIcon name="info" size="sm" class="text-status-warning-text mt-0.5 shrink-0" />
|
|
<span>
|
|
<span class="text-text-link font-bold">{{ t("flow.function.raf") }}</span>
|
|
{{ t("flow.function.rafDesc") }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</OForm>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { computed, defineAsyncComponent, nextTick, onMounted, ref, watch } from "vue";
|
|
import { useI18nTyped } from "@/types/i18n";
|
|
import { useStore } from "vuex";
|
|
import OSwitch from "@/lib/forms/Switch/OSwitch.vue";
|
|
import OForm from "@/lib/forms/Form/OForm.vue";
|
|
import OFormSelect from "@/lib/forms/Select/OFormSelect.vue";
|
|
import OFormSwitch from "@/lib/forms/Switch/OFormSwitch.vue";
|
|
import { useOForm } from "@/lib/forms/Form/useOForm";
|
|
import {
|
|
makeAssociateFunctionSchema,
|
|
type AssociateFunctionForm,
|
|
} from "@/components/pipeline/NodeForm/AssociateFunction.schema";
|
|
import OSpinner from "@/lib/feedback/Spinner/OSpinner.vue";
|
|
import OCard from "@/lib/core/Card/OCard.vue";
|
|
import OCardSection from "@/lib/core/Card/OCardSection.vue";
|
|
import OSeparator from "@/lib/core/Separator/OSeparator.vue";
|
|
import OIcon from "@/lib/core/Icon/OIcon.vue";
|
|
import { toast } from "@/lib/feedback/Toast/useToast";
|
|
import functionsService from "@/services/jstransform";
|
|
import { isJsFunction } from "@/utils/functionLanguage";
|
|
|
|
const AddFunction = defineAsyncComponent(() => import("@/components/functions/AddFunction.vue"));
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
initialName?: string;
|
|
initialAfterFlatten?: boolean;
|
|
showFlatten?: boolean;
|
|
isUpdating?: boolean;
|
|
duplicateNames?: string[];
|
|
// Sample events to seed the inline function editor's "Events" panel (e.g. the
|
|
// workflow alert payload). Omitted → the generic log sample.
|
|
sampleEvents?: any[];
|
|
// The host's execution language ('vrl' | 'javascript'). Two effects:
|
|
// 1. filters the selectable list to functions of that language, and
|
|
// 2. locks the inline editor to it, hiding the VRL/JS toggle.
|
|
// Workflow Function nodes pass 'javascript'; pipelines pass 'vrl'.
|
|
language?: string;
|
|
// Seed code for a fresh inline function (replaces the typewriter placeholder).
|
|
defaultCode?: string;
|
|
}>(),
|
|
{
|
|
initialName: "",
|
|
initialAfterFlatten: true,
|
|
showFlatten: true,
|
|
isUpdating: false,
|
|
duplicateNames: () => [],
|
|
sampleEvents: undefined,
|
|
language: "",
|
|
defaultCode: "",
|
|
},
|
|
);
|
|
|
|
const emit = defineEmits<{
|
|
(e: "expand", value: boolean): void;
|
|
(e: "created", fn: any): void;
|
|
}>();
|
|
|
|
const { t } = useI18nTyped();
|
|
const store = useStore();
|
|
|
|
const loading = ref(false);
|
|
const functionOptions = ref<string[]>([]);
|
|
const functionDefs = ref<Record<string, string>>({});
|
|
|
|
const createNewFunction = ref(false);
|
|
const addFunctionRef = ref<any>(null);
|
|
|
|
watch(createNewFunction, (v) => emit("expand", v));
|
|
|
|
// ── OForm wiring (OWNER pattern) ─────────────────────────────────────────────
|
|
// Owned here so the definition preview can read the live selection reactively.
|
|
// The shared AssociateFunction schema enforces BOTH rules that used to be
|
|
// hand-rolled: required (min(1)) and "already associated" (superRefine over
|
|
// duplicateNames) — both now render inline on the select.
|
|
const validated = ref<AssociateFunctionForm | null>(null);
|
|
|
|
const form = useOForm<AssociateFunctionForm>({
|
|
defaultValues: {
|
|
selectedFunction: props.initialName || "",
|
|
afterFlattening: props.initialAfterFlatten,
|
|
},
|
|
schema: makeAssociateFunctionSchema(
|
|
t,
|
|
() => props.duplicateNames,
|
|
() => props.isUpdating,
|
|
),
|
|
onSubmit: (values) => {
|
|
validated.value = values;
|
|
},
|
|
});
|
|
|
|
// Reactive view of the SAME form (no mirror ref).
|
|
const selectedFunction = form.useStore((s: any) => s.values?.selectedFunction ?? "");
|
|
|
|
const selectedDefinition = computed(() => functionDefs.value[selectedFunction.value] || "");
|
|
|
|
// Only functions written in the host's language are selectable: a pipeline runs
|
|
// VRL, a workflow node runs JS. Offering the other kind would let a user attach a
|
|
// function the node can't execute. (isJsFunction handles the transType /
|
|
// trans_type field-name split — see utils/functionLanguage.)
|
|
const wantsJs = computed(() => props.language === "javascript");
|
|
const matchesHostLanguage = (func: any) =>
|
|
wantsJs.value ? isJsFunction(func) : !isJsFunction(func);
|
|
|
|
const getFunctions = async () => {
|
|
loading.value = true;
|
|
try {
|
|
const res = await functionsService.list(
|
|
1,
|
|
100000,
|
|
"name",
|
|
false,
|
|
"",
|
|
store.state.selectedOrganization.identifier,
|
|
);
|
|
const names: string[] = [];
|
|
const defs: Record<string, string> = {};
|
|
(res.data?.list || []).forEach((func: any) => {
|
|
if (matchesHostLanguage(func)) {
|
|
names.push(func.name);
|
|
defs[func.name] = func.function;
|
|
}
|
|
});
|
|
functionOptions.value = names.sort((a, b) => a.localeCompare(b));
|
|
functionDefs.value = defs;
|
|
} catch (e) {
|
|
toast({ variant: "error", message: t("flow.function.loadError") });
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
onMounted(getFunctions);
|
|
|
|
const onFunctionCreation = async (fn: any) => {
|
|
createNewFunction.value = false;
|
|
emit("created", fn);
|
|
await getFunctions();
|
|
await nextTick();
|
|
// Push the just-created function into the form (the select re-mounts with it).
|
|
if (fn?.name) form.setFieldValue("selectedFunction", fn.name);
|
|
};
|
|
|
|
// Inline editor back/cancel: return to the picker (don't close the host drawer).
|
|
const cancelFunctionCreation = () => {
|
|
createNewFunction.value = false;
|
|
};
|
|
|
|
// Host bridge: validate through the schema and return the node payload, or null
|
|
// when invalid (OForm renders required / already-associated inline on the field).
|
|
const submit = async () => {
|
|
if (createNewFunction.value) return null; // still in the inline editor
|
|
validated.value = null;
|
|
await form.handleSubmit();
|
|
const values = validated.value as AssociateFunctionForm | null;
|
|
if (!values?.selectedFunction) return null;
|
|
return props.showFlatten
|
|
? { name: values.selectedFunction, after_flatten: !!values.afterFlattening }
|
|
: { name: values.selectedFunction };
|
|
};
|
|
|
|
defineExpose({ submit, createNewFunction, form });
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* keep(lib-override:AddFunction): only AddFunction's own chrome is suppressed here — :deep() reaches a child
|
|
component's internals, which utilities cannot.
|
|
The `.function-code` font-family became the `font-mono` utility, and the
|
|
::-webkit-scrollbar rules were DELETED: component.css already styles
|
|
scrollbars globally from tokens WITH `.dark` variants, so these light-only
|
|
hex rules were duplicating it and overriding the themed version in dark. */
|
|
.flow-add-function :deep(.add-function-back-btn),
|
|
.flow-add-function :deep(.add-function-fullscreen-btn),
|
|
.flow-add-function :deep(.add-function-title) {
|
|
display: none;
|
|
}
|
|
.flow-add-function :deep(.add-function-name-input) {
|
|
width: 100%;
|
|
margin-left: 0 !important;
|
|
}
|
|
</style>
|