import type { InputVar } from '@/app/components/workflow/types' import type { I18nKeysByPrefix } from '@/types/i18n' import { CONTEXT_PLACEHOLDER_TEXT, HISTORY_PLACEHOLDER_TEXT, PRE_PROMPT_PLACEHOLDER_TEXT, QUERY_PLACEHOLDER_TEXT, } from '@/app/components/base/prompt-editor/constants' import { InputVarType } from '@/app/components/workflow/types' import { getMaxVarNameLength, MARKETPLACE_URL_PREFIX, MAX_VAR_KEY_LENGTH, VAR_ITEM_TEMPLATE, VAR_ITEM_TEMPLATE_IN_WORKFLOW, } from '@/config' import { env } from '@/env' const otherAllowedRegex = /^\w+$/ export const getNewVar = (key: string, type: string) => { const { ...rest } = VAR_ITEM_TEMPLATE if (type !== 'string') { return { ...rest, type: type || 'string', key, name: key.slice(0, getMaxVarNameLength(key)), } } return { ...VAR_ITEM_TEMPLATE, type: type || 'string', key, name: key.slice(0, getMaxVarNameLength(key)), } } export const getNewVarInWorkflow = (key: string, type = InputVarType.textInput): InputVar => { const { ...rest } = VAR_ITEM_TEMPLATE_IN_WORKFLOW if (type !== InputVarType.textInput) { return { ...rest, type, variable: key, label: key.slice(0, getMaxVarNameLength(key)), } } return { ...VAR_ITEM_TEMPLATE_IN_WORKFLOW, type, variable: key, label: key.slice(0, getMaxVarNameLength(key)), placeholder: '', default: '', hint: '', } } type VarKeyErrorMessageKey = I18nKeysByPrefix<'appDebug', 'varKeyError.'> export const checkKey = ( key: string, canBeEmpty?: boolean, _keys?: string[], ): true | VarKeyErrorMessageKey => { if (key.length === 0 && !canBeEmpty) return 'canNoBeEmpty' if (canBeEmpty && key === '') return true if (key.length > MAX_VAR_KEY_LENGTH) return 'tooLong' if (otherAllowedRegex.test(key)) { if (/\d/.test(key[0]!)) return 'notStartWithNumber' return true } return 'notValid' } type CheckKeysResult = | { isValid: true; errorKey: ''; errorMessageKey: '' } | { isValid: false; errorKey: string; errorMessageKey: VarKeyErrorMessageKey } export const checkKeys = (keys: string[], canBeEmpty?: boolean): CheckKeysResult => { let isValid = true let errorKey = '' let errorMessageKey: VarKeyErrorMessageKey | '' = '' keys.forEach((key) => { if (!isValid) return const res = checkKey(key, canBeEmpty) if (res !== true) { isValid = false errorKey = key errorMessageKey = res } }) return { isValid, errorKey, errorMessageKey } as CheckKeysResult } export const hasDuplicateStr = (strArr: string[]) => { const strObj: Record = {} strArr.forEach((str) => { if (strObj[str]) strObj[str] += 1 else strObj[str] = 1 }) return !!Object.keys(strObj).find((key) => strObj[key]! > 1) } const varRegex = /\{\{([a-z_]\w*)\}\}/gi export const getVars = (value: string) => { if (!value) return [] const keys = value .match(varRegex) ?.filter((item) => { return ![ CONTEXT_PLACEHOLDER_TEXT, HISTORY_PLACEHOLDER_TEXT, QUERY_PLACEHOLDER_TEXT, PRE_PROMPT_PLACEHOLDER_TEXT, ].includes(item) }) .map((item) => { return item.replace('{{', '').replace('}}', '') }) .filter((key) => key.length <= MAX_VAR_KEY_LENGTH) || [] const keyObj: Record = {} // remove duplicate keys const res: string[] = [] keys.forEach((key) => { if (keyObj[key]) return keyObj[key] = true res.push(key) }) return res } // Set the value of basePath // example: /dify export const basePath = env.NEXT_PUBLIC_BASE_PATH type MarketplaceUrlOptions = { source?: string } const getUrlOrigin = (url?: string) => { if (!url) return undefined try { return new URL(url).origin } catch { return undefined } } const marketplaceSource = getUrlOrigin(env.NEXT_PUBLIC_WEB_PREFIX) export function getMarketplaceUrl( path: string, params?: Record, options?: MarketplaceUrlOptions, ) { const searchParams = new URLSearchParams() const source = getUrlOrigin(options?.source) ?? marketplaceSource if (source) searchParams.set('source', source) if (params) { Object.keys(params).forEach((key) => { const value = params[key] if (value !== undefined && value !== null) searchParams.append(key, value) }) } const queryString = searchParams.toString() return queryString ? `${MARKETPLACE_URL_PREFIX}${path}?${queryString}` : `${MARKETPLACE_URL_PREFIX}${path}` } export const replaceSpaceWithUnderscoreInVarNameInput = (input: HTMLInputElement) => { const start = input.selectionStart const end = input.selectionEnd input.value = input.value.replaceAll(' ', '_') if (start !== null && end !== null) input.setSelectionRange(start, end) }