mirror of
https://github.com/simstudioai/sim.git
synced 2026-03-15 03:00:33 -04:00
Compare commits
2 Commits
v0.5.104
...
waleedlati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
136d5b5679 | ||
|
|
550e29d1af |
@@ -31,7 +31,12 @@ import {
|
||||
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
|
||||
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
|
||||
import type { WandControlHandlers } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block'
|
||||
import { restoreCursorAfterInsertion } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/utils'
|
||||
import {
|
||||
restoreCursorAfterInsertion,
|
||||
sanitizeForParsing,
|
||||
validateJavaScript,
|
||||
validatePython,
|
||||
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/utils'
|
||||
import { WandPromptBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-prompt-bar'
|
||||
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
|
||||
import { useWand } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-wand'
|
||||
@@ -166,7 +171,7 @@ interface CodeProps {
|
||||
defaultCollapsed?: boolean
|
||||
defaultValue?: string | number | boolean | Record<string, unknown> | Array<unknown>
|
||||
showCopyButton?: boolean
|
||||
onValidationChange?: (isValid: boolean) => void
|
||||
onValidationChange?: (isValid: boolean, errorMessage?: string | null) => void
|
||||
wandConfig: {
|
||||
enabled: boolean
|
||||
prompt: string
|
||||
@@ -250,6 +255,18 @@ export const Code = memo(function Code({
|
||||
}
|
||||
}, [shouldValidateJson, trimmedCode])
|
||||
|
||||
const syntaxError = useMemo(() => {
|
||||
if (effectiveLanguage === 'json' || !trimmedCode) return null
|
||||
const sanitized = sanitizeForParsing(trimmedCode)
|
||||
if (effectiveLanguage === 'javascript') {
|
||||
return validateJavaScript(sanitized)
|
||||
}
|
||||
if (effectiveLanguage === 'python') {
|
||||
return validatePython(sanitized)
|
||||
}
|
||||
return null
|
||||
}, [effectiveLanguage, trimmedCode])
|
||||
|
||||
const gutterWidthPx = useMemo(() => {
|
||||
const lineCount = code.split('\n').length
|
||||
return calculateGutterWidth(lineCount)
|
||||
@@ -341,19 +358,21 @@ export const Code = memo(function Code({
|
||||
useEffect(() => {
|
||||
if (!onValidationChange) return
|
||||
|
||||
const isValid = !shouldValidateJson || isValidJson
|
||||
const isValid = (!shouldValidateJson || isValidJson) && !syntaxError
|
||||
|
||||
if (isValid) {
|
||||
onValidationChange(true)
|
||||
onValidationChange(true, null)
|
||||
return
|
||||
}
|
||||
|
||||
const errorMessage = !isValidJson ? 'Invalid JSON' : syntaxError
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
onValidationChange(false)
|
||||
onValidationChange(false, errorMessage)
|
||||
}, 150)
|
||||
|
||||
return () => clearTimeout(timeoutId)
|
||||
}, [isValidJson, onValidationChange, shouldValidateJson])
|
||||
}, [isValidJson, syntaxError, onValidationChange, shouldValidateJson])
|
||||
|
||||
useEffect(() => {
|
||||
handleStreamStartRef.current = () => {
|
||||
|
||||
@@ -189,7 +189,7 @@ const getPreviewValue = (
|
||||
* Renders the label with optional validation and description tooltips.
|
||||
*
|
||||
* @param config - The sub-block configuration defining the label content
|
||||
* @param isValidJson - Whether the JSON content is valid (for code blocks)
|
||||
* @param codeValidation - Validation state for code blocks (valid flag + optional error message)
|
||||
* @param subBlockValues - Current values of all subblocks for evaluating conditional requirements
|
||||
* @param wandState - State and handlers for the inline AI generate feature
|
||||
* @param canonicalToggle - Metadata and handlers for the basic/advanced mode toggle
|
||||
@@ -200,7 +200,7 @@ const getPreviewValue = (
|
||||
*/
|
||||
const renderLabel = (
|
||||
config: SubBlockConfig,
|
||||
isValidJson: boolean,
|
||||
codeValidation: { isValid: boolean; errorMessage: string | null },
|
||||
subBlockValues?: Record<string, any>,
|
||||
wandState?: {
|
||||
isSearchActive: boolean
|
||||
@@ -250,21 +250,18 @@ const renderLabel = (
|
||||
{config.title}
|
||||
{required && <span className='ml-0.5'>*</span>}
|
||||
{labelSuffix}
|
||||
{config.type === 'code' &&
|
||||
config.language === 'json' &&
|
||||
!isValidJson &&
|
||||
!wandState?.isStreaming && (
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<span className='inline-flex'>
|
||||
<AlertTriangle className='h-3 w-3 flex-shrink-0 cursor-pointer text-destructive' />
|
||||
</span>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side='top'>
|
||||
<p>Invalid JSON</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)}
|
||||
{config.type === 'code' && !codeValidation.isValid && !wandState?.isStreaming && (
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<span className='inline-flex'>
|
||||
<AlertTriangle className='h-3 w-3 flex-shrink-0 cursor-pointer text-destructive' />
|
||||
</span>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side='top'>
|
||||
<p>{codeValidation.errorMessage ?? 'Syntax error'}</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)}
|
||||
</Label>
|
||||
<div className='flex min-w-0 flex-1 items-center justify-end gap-[6px]'>
|
||||
{showCopy && (
|
||||
@@ -466,7 +463,8 @@ function SubBlockComponent({
|
||||
const params = useParams()
|
||||
const workspaceId = params.workspaceId as string
|
||||
|
||||
const [isValidJson, setIsValidJson] = useState(true)
|
||||
const [isValidCode, setIsValidCode] = useState(true)
|
||||
const [codeErrorMessage, setCodeErrorMessage] = useState<string | null>(null)
|
||||
const [isSearchActive, setIsSearchActive] = useState(false)
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [copied, setCopied] = useState(false)
|
||||
@@ -484,8 +482,9 @@ function SubBlockComponent({
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
const handleValidationChange = (isValid: boolean): void => {
|
||||
setIsValidJson(isValid)
|
||||
const handleValidationChange = (isValid: boolean, errorMessage?: string | null): void => {
|
||||
setIsValidCode(isValid)
|
||||
setCodeErrorMessage(errorMessage ?? null)
|
||||
}
|
||||
|
||||
const isWandEnabled = config.wandConfig?.enabled ?? false
|
||||
@@ -1151,7 +1150,7 @@ function SubBlockComponent({
|
||||
<div onMouseDown={handleMouseDown} className='subblock-content flex flex-col gap-[10px]'>
|
||||
{renderLabel(
|
||||
config,
|
||||
isValidJson,
|
||||
{ isValid: isValidCode, errorMessage: codeErrorMessage },
|
||||
subBlockValues,
|
||||
{
|
||||
isSearchActive,
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { sanitizeForParsing, validateJavaScript, validatePython } from './utils'
|
||||
|
||||
describe('sanitizeForParsing', () => {
|
||||
it('replaces <Block.output> references with valid identifiers', () => {
|
||||
const result = sanitizeForParsing('const x = <Block.output>')
|
||||
expect(result).not.toContain('<')
|
||||
expect(result).not.toContain('>')
|
||||
expect(result).toContain('__placeholder_')
|
||||
})
|
||||
|
||||
it('replaces {{ENV_VAR}} with valid identifiers', () => {
|
||||
const result = sanitizeForParsing('const url = {{API_URL}}')
|
||||
expect(result).not.toContain('{{')
|
||||
expect(result).not.toContain('}}')
|
||||
expect(result).toContain('__placeholder_')
|
||||
})
|
||||
|
||||
it('replaces nested path references like <Block.output[0].field>', () => {
|
||||
const result = sanitizeForParsing('const x = <Agent.response.choices[0].text>')
|
||||
expect(result).not.toContain('<Agent')
|
||||
})
|
||||
|
||||
it('replaces loop/parallel context references', () => {
|
||||
const result = sanitizeForParsing('const item = <loop.currentItem>')
|
||||
expect(result).not.toContain('<loop')
|
||||
})
|
||||
|
||||
it('replaces variable references', () => {
|
||||
const result = sanitizeForParsing('const v = <variable.myVar>')
|
||||
expect(result).not.toContain('<variable')
|
||||
})
|
||||
|
||||
it('handles multiple references in one string', () => {
|
||||
const code = 'const a = <Block1.out>; const b = {{SECRET}}; const c = <Block2.value>'
|
||||
const result = sanitizeForParsing(code)
|
||||
expect(result).not.toContain('<Block1')
|
||||
expect(result).not.toContain('{{SECRET}}')
|
||||
expect(result).not.toContain('<Block2')
|
||||
expect(result.match(/__placeholder_/g)?.length).toBe(3)
|
||||
})
|
||||
|
||||
it('does not replace regular JS comparison operators', () => {
|
||||
const code = 'if (a < b && c > d) {}'
|
||||
const result = sanitizeForParsing(code)
|
||||
expect(result).toBe(code)
|
||||
})
|
||||
|
||||
it('does not replace HTML tags that are not references', () => {
|
||||
const code = 'const html = "<div>hello</div>"'
|
||||
const result = sanitizeForParsing(code)
|
||||
expect(result).toBe(code)
|
||||
})
|
||||
})
|
||||
|
||||
describe('validateJavaScript', () => {
|
||||
it('returns null for valid JavaScript', () => {
|
||||
expect(validateJavaScript('const x = 1')).toBeNull()
|
||||
expect(validateJavaScript('function foo() { return 42 }')).toBeNull()
|
||||
expect(validateJavaScript('const arr = [1, 2, 3].map(x => x * 2)')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for valid async/await code', () => {
|
||||
expect(validateJavaScript('async function foo() { await bar() }')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for bare return statements (function block wraps in async fn)', () => {
|
||||
expect(validateJavaScript('return 42')).toBeNull()
|
||||
expect(validateJavaScript(sanitizeForParsing('return <Block.output>'))).toBeNull()
|
||||
expect(validateJavaScript('const x = 1\nreturn x')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for await at top level (wrapped in async fn)', () => {
|
||||
expect(validateJavaScript('const res = await fetch("url")')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for valid ES module syntax', () => {
|
||||
expect(validateJavaScript('import { foo } from "bar"')).toBeNull()
|
||||
expect(validateJavaScript('export default function() {}')).toBeNull()
|
||||
})
|
||||
|
||||
it('detects missing closing brace', () => {
|
||||
const result = validateJavaScript('function foo() {')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result).toContain('Syntax error')
|
||||
})
|
||||
|
||||
it('detects missing closing paren', () => {
|
||||
const result = validateJavaScript('console.log("hello"')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result).toContain('Syntax error')
|
||||
})
|
||||
|
||||
it('detects unexpected token', () => {
|
||||
const result = validateJavaScript('const = 5')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result).toContain('Syntax error')
|
||||
})
|
||||
|
||||
it('includes adjusted line and column in error message', () => {
|
||||
const result = validateJavaScript('const x = 1\nconst = 5')
|
||||
expect(result).toMatch(/line 2/)
|
||||
expect(result).toMatch(/col \d+/)
|
||||
})
|
||||
|
||||
it('returns null for empty code', () => {
|
||||
expect(validateJavaScript('')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not error on sanitized references', () => {
|
||||
const code = sanitizeForParsing('const x = <Block.output> + {{ENV_VAR}}')
|
||||
expect(validateJavaScript(code)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('validatePython', () => {
|
||||
it('returns null for valid Python', () => {
|
||||
expect(validatePython('x = 1')).toBeNull()
|
||||
expect(validatePython('def foo():\n return 42')).toBeNull()
|
||||
expect(validatePython('arr = [1, 2, 3]')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for Python with comments', () => {
|
||||
expect(validatePython('x = 1 # this is a comment')).toBeNull()
|
||||
expect(validatePython('# full line comment\nx = 1')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for Python with strings containing brackets', () => {
|
||||
expect(validatePython('x = "hello (world)"')).toBeNull()
|
||||
expect(validatePython("x = 'brackets [here] {too}'")).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for triple-quoted strings', () => {
|
||||
expect(validatePython('x = """hello\nworld"""')).toBeNull()
|
||||
expect(validatePython("x = '''multi\nline\nstring'''")).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for triple-quoted strings with brackets', () => {
|
||||
expect(validatePython('x = """has { and ( inside"""')).toBeNull()
|
||||
})
|
||||
|
||||
it('detects unmatched opening paren', () => {
|
||||
const result = validatePython('foo(1, 2')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result).toContain("'('")
|
||||
})
|
||||
|
||||
it('detects unmatched closing paren', () => {
|
||||
const result = validatePython('foo)')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result).toContain("')'")
|
||||
})
|
||||
|
||||
it('detects unmatched bracket', () => {
|
||||
const result = validatePython('arr = [1, 2')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result).toContain("'['")
|
||||
})
|
||||
|
||||
it('detects unterminated string', () => {
|
||||
const result = validatePython('x = "hello')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result).toContain('Unterminated string')
|
||||
})
|
||||
|
||||
it('detects unterminated triple-quoted string', () => {
|
||||
const result = validatePython('x = """hello')
|
||||
expect(result).not.toBeNull()
|
||||
expect(result).toContain('Unterminated triple-quoted string')
|
||||
})
|
||||
|
||||
it('includes line number in error', () => {
|
||||
const result = validatePython('x = 1\ny = (2')
|
||||
expect(result).toMatch(/line 2/)
|
||||
})
|
||||
|
||||
it('handles escaped quotes in strings', () => {
|
||||
expect(validatePython('x = "hello \\"world\\""')).toBeNull()
|
||||
expect(validatePython("x = 'it\\'s fine'")).toBeNull()
|
||||
})
|
||||
|
||||
it('handles brackets inside comments', () => {
|
||||
expect(validatePython('x = 1 # unmatched ( here')).toBeNull()
|
||||
})
|
||||
|
||||
it('returns null for empty code', () => {
|
||||
expect(validatePython('')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not error on sanitized references', () => {
|
||||
const code = sanitizeForParsing('x = <Block.output> + {{ENV_VAR}}')
|
||||
expect(validatePython(code)).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -1,3 +1,17 @@
|
||||
import { parse } from 'acorn'
|
||||
|
||||
/**
|
||||
* Matches Sim block references: `<word.path>`, `<word.path[0].nested>`, `<loop.index>`, etc.
|
||||
* Must contain a dot (.) to distinguish from HTML tags or comparison operators.
|
||||
*/
|
||||
const REFERENCE_PATTERN = /<[a-zA-Z]\w*(?:\.\w+(?:\[\d+\])?)+>/g
|
||||
|
||||
/**
|
||||
* Matches Sim env-var placeholders: `{{WORD}}`, `{{MY_VAR}}`.
|
||||
* Only allows word characters (no spaces, special chars).
|
||||
*/
|
||||
const ENV_VAR_PATTERN = /\{\{\w+\}\}/g
|
||||
|
||||
/**
|
||||
* Restores the cursor position in a textarea after a dropdown insertion.
|
||||
* Schedules a macrotask (via setTimeout) that runs after React's controlled-component commit
|
||||
@@ -18,3 +32,132 @@ export function restoreCursorAfterInsertion(
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces `<Block.output>` references and `{{ENV_VAR}}` placeholders with
|
||||
* valid JS/Python identifiers so the code can be parsed without false errors.
|
||||
*/
|
||||
export function sanitizeForParsing(code: string): string {
|
||||
let counter = 0
|
||||
let sanitized = code.replace(ENV_VAR_PATTERN, () => `__placeholder_${counter++}__`)
|
||||
sanitized = sanitized.replace(REFERENCE_PATTERN, () => `__placeholder_${counter++}__`)
|
||||
return sanitized
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates JavaScript code for syntax errors using acorn.
|
||||
*
|
||||
* Tries two parse strategies to match the Function block's runtime behavior:
|
||||
* 1. As a module (`import`/`export` are valid at top level)
|
||||
* 2. Wrapped in `async () => { ... }` (bare `return`/`await` are valid)
|
||||
*
|
||||
* Only reports an error if both strategies fail, using the wrapped error
|
||||
* since that's the primary execution context.
|
||||
*
|
||||
* @returns Error message string, or null if valid.
|
||||
*/
|
||||
export function validateJavaScript(code: string): string | null {
|
||||
try {
|
||||
parse(code, { ecmaVersion: 'latest', sourceType: 'module' })
|
||||
return null
|
||||
} catch {
|
||||
// Module parse failed — try as function body (allows bare return/await)
|
||||
}
|
||||
|
||||
const wrapped = `(async () => {\n${code}\n})()`
|
||||
try {
|
||||
parse(wrapped, { ecmaVersion: 'latest', sourceType: 'script' })
|
||||
return null
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof SyntaxError) {
|
||||
const msg = e.message
|
||||
const match = msg.match(/\((\d+):(\d+)\)/)
|
||||
if (match) {
|
||||
const adjustedLine = Number(match[1]) - 1
|
||||
if (adjustedLine < 1) return null
|
||||
return `Syntax error at line ${adjustedLine}, col ${match[2]}: ${msg.replace(/\s*\(\d+:\d+\)/, '')}`
|
||||
}
|
||||
return `Syntax error: ${msg}`
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates Python code for common syntax errors: unmatched brackets/parens,
|
||||
* unterminated strings (single-line and triple-quoted).
|
||||
* Processes the entire code string as a stream to correctly handle
|
||||
* multiline triple-quoted strings.
|
||||
*
|
||||
* @returns Error message string, or null if no issues detected.
|
||||
*/
|
||||
export function validatePython(code: string): string | null {
|
||||
const stack: { char: string; line: number }[] = []
|
||||
const openers: Record<string, string> = { ')': '(', ']': '[', '}': '{' }
|
||||
const closers = new Set([')', ']', '}'])
|
||||
const openChars = new Set(['(', '[', '{'])
|
||||
|
||||
let line = 1
|
||||
let i = 0
|
||||
|
||||
while (i < code.length) {
|
||||
const ch = code[i]
|
||||
|
||||
if (ch === '\n') {
|
||||
line++
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if (ch === '#') {
|
||||
const newline = code.indexOf('\n', i)
|
||||
i = newline === -1 ? code.length : newline
|
||||
continue
|
||||
}
|
||||
|
||||
if (ch === '"' || ch === "'") {
|
||||
const tripleQuote = ch.repeat(3)
|
||||
if (code.slice(i, i + 3) === tripleQuote) {
|
||||
const startLine = line
|
||||
const endIdx = code.indexOf(tripleQuote, i + 3)
|
||||
if (endIdx === -1) {
|
||||
return `Unterminated triple-quoted string starting at line ${startLine}`
|
||||
}
|
||||
for (let k = i; k < endIdx + 3; k++) {
|
||||
if (code[k] === '\n') line++
|
||||
}
|
||||
i = endIdx + 3
|
||||
continue
|
||||
}
|
||||
|
||||
const startLine = line
|
||||
i++
|
||||
while (i < code.length && code[i] !== ch && code[i] !== '\n') {
|
||||
if (code[i] === '\\') i++
|
||||
i++
|
||||
}
|
||||
if (i >= code.length || code[i] === '\n') {
|
||||
return `Unterminated string at line ${startLine}`
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if (openChars.has(ch)) {
|
||||
stack.push({ char: ch, line })
|
||||
} else if (closers.has(ch)) {
|
||||
if (stack.length === 0 || stack[stack.length - 1].char !== openers[ch]) {
|
||||
return `Unmatched '${ch}' at line ${line}`
|
||||
}
|
||||
stack.pop()
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if (stack.length > 0) {
|
||||
const unmatched = stack[stack.length - 1]
|
||||
return `Unmatched '${unmatched.char}' opened at line ${unmatched.line}`
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
"@trigger.dev/sdk": "4.1.2",
|
||||
"@types/react-window": "2.0.0",
|
||||
"@types/three": "0.177.0",
|
||||
"acorn": "8.16.0",
|
||||
"better-auth": "1.3.12",
|
||||
"binary-extensions": "^2.0.0",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
|
||||
13
bun.lock
13
bun.lock
@@ -115,6 +115,7 @@
|
||||
"@trigger.dev/sdk": "4.1.2",
|
||||
"@types/react-window": "2.0.0",
|
||||
"@types/three": "0.177.0",
|
||||
"acorn": "8.16.0",
|
||||
"better-auth": "1.3.12",
|
||||
"binary-extensions": "^2.0.0",
|
||||
"browser-image-compression": "^2.0.2",
|
||||
@@ -1641,7 +1642,7 @@
|
||||
|
||||
"accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
"acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||
|
||||
"acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="],
|
||||
|
||||
@@ -3825,6 +3826,8 @@
|
||||
|
||||
"@langchain/core/uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
|
||||
|
||||
"@mdx-js/mdx/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"@modelcontextprotocol/sdk/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
|
||||
"@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="],
|
||||
@@ -4079,6 +4082,8 @@
|
||||
|
||||
"engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||
|
||||
"esast-util-from-js/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
@@ -4139,6 +4144,8 @@
|
||||
|
||||
"imapflow/pino": ["pino@10.1.0", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w=="],
|
||||
|
||||
"import-in-the-middle/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"inquirer/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"inquirer/ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="],
|
||||
@@ -4173,8 +4180,12 @@
|
||||
|
||||
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
|
||||
|
||||
"micromark-extension-mdxjs/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"mlly/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"neo4j-driver-bolt-connection/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||
|
||||
Reference in New Issue
Block a user