mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-08 14:43:54 -05:00
fix(condition): fixed condition block to resolve envvars and vars (#718)
* fix(condition): fixed condition block to resolve envvars and vars * upgrade turbo * fixed starter block input not resolving as string
This commit is contained in:
@@ -110,6 +110,8 @@ export const setupExecutorCoreMocks = () => {
|
||||
InputResolver: vi.fn().mockImplementation(() => ({
|
||||
resolveInputs: vi.fn().mockReturnValue({}),
|
||||
resolveBlockReferences: vi.fn().mockImplementation((value) => value),
|
||||
resolveVariableReferences: vi.fn().mockImplementation((value) => value),
|
||||
resolveEnvVariables: vi.fn().mockImplementation((value) => value),
|
||||
})),
|
||||
}))
|
||||
|
||||
|
||||
@@ -85,8 +85,10 @@ describe('ConditionBlockHandler', () => {
|
||||
{}
|
||||
) as Mocked<InputResolver>
|
||||
|
||||
// Ensure the method exists as a mock function on the instance
|
||||
// Ensure the methods exist as mock functions on the instance
|
||||
mockResolver.resolveBlockReferences = vi.fn()
|
||||
mockResolver.resolveVariableReferences = vi.fn()
|
||||
mockResolver.resolveEnvVariables = vi.fn()
|
||||
|
||||
handler = new ConditionBlockHandler(mockPathTracker, mockResolver)
|
||||
|
||||
@@ -147,16 +149,23 @@ describe('ConditionBlockHandler', () => {
|
||||
selectedConditionId: 'cond1',
|
||||
}
|
||||
|
||||
// Mock directly in the test
|
||||
// Mock the full resolution pipeline
|
||||
mockResolver.resolveVariableReferences.mockReturnValue('context.value > 5')
|
||||
mockResolver.resolveBlockReferences.mockReturnValue('context.value > 5')
|
||||
mockResolver.resolveEnvVariables.mockReturnValue('context.value > 5')
|
||||
|
||||
const result = await handler.execute(mockBlock, inputs, mockContext)
|
||||
|
||||
expect(mockResolver.resolveVariableReferences).toHaveBeenCalledWith(
|
||||
'context.value > 5',
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveBlockReferences).toHaveBeenCalledWith(
|
||||
'context.value > 5',
|
||||
mockContext,
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveEnvVariables).toHaveBeenCalledWith('context.value > 5', true)
|
||||
expect(result).toEqual(expectedOutput)
|
||||
expect(mockContext.decisions.condition.get(mockBlock.id)).toBe('cond1')
|
||||
})
|
||||
@@ -180,16 +189,23 @@ describe('ConditionBlockHandler', () => {
|
||||
selectedConditionId: 'else1',
|
||||
}
|
||||
|
||||
// Mock directly in the test
|
||||
// Mock the full resolution pipeline
|
||||
mockResolver.resolveVariableReferences.mockReturnValue('context.value < 0')
|
||||
mockResolver.resolveBlockReferences.mockReturnValue('context.value < 0')
|
||||
mockResolver.resolveEnvVariables.mockReturnValue('context.value < 0')
|
||||
|
||||
const result = await handler.execute(mockBlock, inputs, mockContext)
|
||||
|
||||
expect(mockResolver.resolveVariableReferences).toHaveBeenCalledWith(
|
||||
'context.value < 0',
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveBlockReferences).toHaveBeenCalledWith(
|
||||
'context.value < 0',
|
||||
mockContext,
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveEnvVariables).toHaveBeenCalledWith('context.value < 0', true)
|
||||
expect(result).toEqual(expectedOutput)
|
||||
expect(mockContext.decisions.condition.get(mockBlock.id)).toBe('else1')
|
||||
})
|
||||
@@ -209,16 +225,77 @@ describe('ConditionBlockHandler', () => {
|
||||
]
|
||||
const inputs = { conditions: JSON.stringify(conditions) }
|
||||
|
||||
// Mock directly in the test
|
||||
// Mock the full resolution pipeline
|
||||
mockResolver.resolveVariableReferences.mockReturnValue('{{source-block-1.value}} > 5')
|
||||
mockResolver.resolveBlockReferences.mockReturnValue('10 > 5')
|
||||
mockResolver.resolveEnvVariables.mockReturnValue('10 > 5')
|
||||
|
||||
const _result = await handler.execute(mockBlock, inputs, mockContext)
|
||||
await handler.execute(mockBlock, inputs, mockContext)
|
||||
|
||||
expect(mockResolver.resolveVariableReferences).toHaveBeenCalledWith(
|
||||
'{{source-block-1.value}} > 5',
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveBlockReferences).toHaveBeenCalledWith(
|
||||
'{{source-block-1.value}} > 5',
|
||||
mockContext,
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveEnvVariables).toHaveBeenCalledWith('10 > 5', true)
|
||||
expect(mockContext.decisions.condition.get(mockBlock.id)).toBe('cond1')
|
||||
})
|
||||
|
||||
it('should resolve variable references in conditions', async () => {
|
||||
const conditions = [
|
||||
{ id: 'cond1', title: 'if', value: '<variable.userName> !== null' },
|
||||
{ id: 'else1', title: 'else', value: '' },
|
||||
]
|
||||
const inputs = { conditions: JSON.stringify(conditions) }
|
||||
|
||||
// Mock the full resolution pipeline for variable resolution
|
||||
mockResolver.resolveVariableReferences.mockReturnValue('"john" !== null')
|
||||
mockResolver.resolveBlockReferences.mockReturnValue('"john" !== null')
|
||||
mockResolver.resolveEnvVariables.mockReturnValue('"john" !== null')
|
||||
|
||||
await handler.execute(mockBlock, inputs, mockContext)
|
||||
|
||||
expect(mockResolver.resolveVariableReferences).toHaveBeenCalledWith(
|
||||
'<variable.userName> !== null',
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveBlockReferences).toHaveBeenCalledWith(
|
||||
'"john" !== null',
|
||||
mockContext,
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveEnvVariables).toHaveBeenCalledWith('"john" !== null', true)
|
||||
expect(mockContext.decisions.condition.get(mockBlock.id)).toBe('cond1')
|
||||
})
|
||||
|
||||
it('should resolve environment variables in conditions', async () => {
|
||||
const conditions = [
|
||||
{ id: 'cond1', title: 'if', value: '{{POOP}} === "hi"' },
|
||||
{ id: 'else1', title: 'else', value: '' },
|
||||
]
|
||||
const inputs = { conditions: JSON.stringify(conditions) }
|
||||
|
||||
// Mock the full resolution pipeline for env variable resolution
|
||||
mockResolver.resolveVariableReferences.mockReturnValue('{{POOP}} === "hi"')
|
||||
mockResolver.resolveBlockReferences.mockReturnValue('{{POOP}} === "hi"')
|
||||
mockResolver.resolveEnvVariables.mockReturnValue('"hi" === "hi"')
|
||||
|
||||
await handler.execute(mockBlock, inputs, mockContext)
|
||||
|
||||
expect(mockResolver.resolveVariableReferences).toHaveBeenCalledWith(
|
||||
'{{POOP}} === "hi"',
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveBlockReferences).toHaveBeenCalledWith(
|
||||
'{{POOP}} === "hi"',
|
||||
mockContext,
|
||||
mockBlock
|
||||
)
|
||||
expect(mockResolver.resolveEnvVariables).toHaveBeenCalledWith('{{POOP}} === "hi"', true)
|
||||
expect(mockContext.decisions.condition.get(mockBlock.id)).toBe('cond1')
|
||||
})
|
||||
|
||||
@@ -230,8 +307,8 @@ describe('ConditionBlockHandler', () => {
|
||||
const inputs = { conditions: JSON.stringify(conditions) }
|
||||
|
||||
const resolutionError = new Error('Could not resolve reference: invalid-ref')
|
||||
// Mock directly in the test
|
||||
mockResolver.resolveBlockReferences.mockImplementation(() => {
|
||||
// Mock the pipeline to throw at the variable resolution stage
|
||||
mockResolver.resolveVariableReferences.mockImplementation(() => {
|
||||
throw resolutionError
|
||||
})
|
||||
|
||||
@@ -247,8 +324,12 @@ describe('ConditionBlockHandler', () => {
|
||||
]
|
||||
const inputs = { conditions: JSON.stringify(conditions) }
|
||||
|
||||
// Mock directly in the test
|
||||
// Mock the full resolution pipeline
|
||||
mockResolver.resolveVariableReferences.mockReturnValue(
|
||||
'context.nonExistentProperty.doSomething()'
|
||||
)
|
||||
mockResolver.resolveBlockReferences.mockReturnValue('context.nonExistentProperty.doSomething()')
|
||||
mockResolver.resolveEnvVariables.mockReturnValue('context.nonExistentProperty.doSomething()')
|
||||
|
||||
await expect(handler.execute(mockBlock, inputs, mockContext)).rejects.toThrow(
|
||||
/^Evaluation error in condition "if": Cannot read properties of undefined \(reading 'doSomething'\)\. \(Resolved: context\.nonExistentProperty\.doSomething\(\)\)$/
|
||||
@@ -271,8 +352,10 @@ describe('ConditionBlockHandler', () => {
|
||||
|
||||
mockContext.workflow!.blocks = [mockSourceBlock, mockBlock, mockTargetBlock2]
|
||||
|
||||
// Mock directly in the test
|
||||
// Mock the full resolution pipeline
|
||||
mockResolver.resolveVariableReferences.mockReturnValue('true')
|
||||
mockResolver.resolveBlockReferences.mockReturnValue('true')
|
||||
mockResolver.resolveEnvVariables.mockReturnValue('true')
|
||||
|
||||
await expect(handler.execute(mockBlock, inputs, mockContext)).rejects.toThrow(
|
||||
`Target block ${mockTargetBlock1.id} not found`
|
||||
@@ -295,10 +378,16 @@ describe('ConditionBlockHandler', () => {
|
||||
},
|
||||
]
|
||||
|
||||
// Mock directly in the test
|
||||
// Mock the full resolution pipeline
|
||||
mockResolver.resolveVariableReferences
|
||||
.mockReturnValueOnce('false')
|
||||
.mockReturnValueOnce('context.value === 99')
|
||||
mockResolver.resolveBlockReferences
|
||||
.mockReturnValueOnce('false')
|
||||
.mockReturnValueOnce('context.value === 99')
|
||||
mockResolver.resolveEnvVariables
|
||||
.mockReturnValueOnce('false')
|
||||
.mockReturnValueOnce('context.value === 99')
|
||||
|
||||
await expect(handler.execute(mockBlock, inputs, mockContext)).rejects.toThrow(
|
||||
`No matching path found for condition block "${mockBlock.metadata?.name}", and no 'else' block exists.`
|
||||
@@ -314,8 +403,10 @@ describe('ConditionBlockHandler', () => {
|
||||
|
||||
mockContext.loopItems.set(mockBlock.id, { item: 'apple' })
|
||||
|
||||
// Mock directly in the test
|
||||
// Mock the full resolution pipeline
|
||||
mockResolver.resolveVariableReferences.mockReturnValue('context.item === "apple"')
|
||||
mockResolver.resolveBlockReferences.mockReturnValue('context.item === "apple"')
|
||||
mockResolver.resolveEnvVariables.mockReturnValue('context.item === "apple"')
|
||||
|
||||
const result = await handler.execute(mockBlock, inputs, mockContext)
|
||||
|
||||
|
||||
@@ -105,12 +105,10 @@ export class ConditionBlockHandler implements BlockHandler {
|
||||
// 2. Resolve references WITHIN the specific condition's value string
|
||||
let resolvedConditionValue = condition.value
|
||||
try {
|
||||
// Use the resolver instance to process block references within the condition string
|
||||
resolvedConditionValue = this.resolver.resolveBlockReferences(
|
||||
condition.value,
|
||||
context,
|
||||
block // Pass the current condition block as context
|
||||
)
|
||||
// Use full resolution pipeline: variables -> block references -> env vars
|
||||
const resolvedVars = this.resolver.resolveVariableReferences(condition.value, block)
|
||||
const resolvedRefs = this.resolver.resolveBlockReferences(resolvedVars, context, block)
|
||||
resolvedConditionValue = this.resolver.resolveEnvVariables(resolvedRefs, true)
|
||||
logger.info(
|
||||
`Resolved condition "${condition.title}" (${condition.id}): from "${condition.value}" to "${resolvedConditionValue}"`
|
||||
)
|
||||
|
||||
@@ -1446,7 +1446,130 @@ describe('InputResolver', () => {
|
||||
}
|
||||
|
||||
const result = connectionResolver.resolveInputs(testBlock, contextWithConnections)
|
||||
expect(result.code).toBe('return Hello World') // Should not be quoted for function blocks
|
||||
expect(result.code).toBe('return "Hello World"') // Should be quoted for function blocks
|
||||
})
|
||||
|
||||
it('should format start.input properly for different block types', () => {
|
||||
// Test function block - should quote strings
|
||||
const functionBlock: SerializedBlock = {
|
||||
id: 'test-function',
|
||||
metadata: { id: BlockType.FUNCTION, name: 'Test Function' },
|
||||
position: { x: 100, y: 100 },
|
||||
config: {
|
||||
tool: BlockType.FUNCTION,
|
||||
params: {
|
||||
code: 'return <start.input>',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
// Test condition block - should quote strings
|
||||
const conditionBlock: SerializedBlock = {
|
||||
id: 'test-condition',
|
||||
metadata: { id: BlockType.CONDITION, name: 'Test Condition' },
|
||||
position: { x: 200, y: 100 },
|
||||
config: {
|
||||
tool: BlockType.CONDITION,
|
||||
params: {
|
||||
conditions: JSON.stringify([
|
||||
{ id: 'cond1', title: 'if', value: '<start.input> === "Hello World"' },
|
||||
]),
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
// Test response block - should use raw string
|
||||
const responseBlock: SerializedBlock = {
|
||||
id: 'test-response',
|
||||
metadata: { id: BlockType.RESPONSE, name: 'Test Response' },
|
||||
position: { x: 300, y: 100 },
|
||||
config: {
|
||||
tool: BlockType.RESPONSE,
|
||||
params: {
|
||||
content: '<start.input>',
|
||||
},
|
||||
},
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const functionResult = connectionResolver.resolveInputs(functionBlock, contextWithConnections)
|
||||
expect(functionResult.code).toBe('return "Hello World"') // Quoted for function
|
||||
|
||||
const conditionResult = connectionResolver.resolveInputs(
|
||||
conditionBlock,
|
||||
contextWithConnections
|
||||
)
|
||||
expect(conditionResult.conditions).toBe(
|
||||
'[{"id":"cond1","title":"if","value":"<start.input> === \\"Hello World\\""}]'
|
||||
) // Conditions not resolved at input level
|
||||
|
||||
const responseResult = connectionResolver.resolveInputs(responseBlock, contextWithConnections)
|
||||
expect(responseResult.content).toBe('Hello World') // Raw string for response
|
||||
})
|
||||
|
||||
it('should properly format start.input when resolved directly via resolveBlockReferences', () => {
|
||||
// Test that start.input gets proper formatting for different block types
|
||||
const functionBlock: SerializedBlock = {
|
||||
id: 'test-function',
|
||||
metadata: { id: BlockType.FUNCTION, name: 'Test Function' },
|
||||
position: { x: 100, y: 100 },
|
||||
config: { tool: BlockType.FUNCTION, params: {} },
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const conditionBlock: SerializedBlock = {
|
||||
id: 'test-condition',
|
||||
metadata: { id: BlockType.CONDITION, name: 'Test Condition' },
|
||||
position: { x: 200, y: 100 },
|
||||
config: { tool: BlockType.CONDITION, params: {} },
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
// Test function block - should quote strings
|
||||
const functionResult = connectionResolver.resolveBlockReferences(
|
||||
'return <start.input>',
|
||||
contextWithConnections,
|
||||
functionBlock
|
||||
)
|
||||
expect(functionResult).toBe('return "Hello World"')
|
||||
|
||||
// Test condition block - should quote strings
|
||||
const conditionResult = connectionResolver.resolveBlockReferences(
|
||||
'<start.input> === "test"',
|
||||
contextWithConnections,
|
||||
conditionBlock
|
||||
)
|
||||
expect(conditionResult).toBe('"Hello World" === "test"')
|
||||
|
||||
// Test other block types - should use raw string
|
||||
const otherBlock: SerializedBlock = {
|
||||
id: 'test-other',
|
||||
metadata: { id: 'other', name: 'Other Block' },
|
||||
position: { x: 300, y: 100 },
|
||||
config: { tool: 'other', params: {} },
|
||||
inputs: {},
|
||||
outputs: {},
|
||||
enabled: true,
|
||||
}
|
||||
|
||||
const otherResult = connectionResolver.resolveBlockReferences(
|
||||
'content: <start.input>',
|
||||
contextWithConnections,
|
||||
otherBlock
|
||||
)
|
||||
expect(otherResult).toBe('content: Hello World')
|
||||
})
|
||||
|
||||
it('should provide helpful error messages for unconnected blocks', () => {
|
||||
|
||||
@@ -449,8 +449,18 @@ export class InputResolver {
|
||||
formattedValue = JSON.stringify(replacementValue)
|
||||
}
|
||||
} else {
|
||||
// For primitive values
|
||||
formattedValue = String(replacementValue)
|
||||
// For primitive values, format based on target block type
|
||||
if (blockType === 'function') {
|
||||
formattedValue = this.formatValueForCodeContext(
|
||||
replacementValue,
|
||||
currentBlock,
|
||||
isInTemplateLiteral
|
||||
)
|
||||
} else if (blockType === 'condition') {
|
||||
formattedValue = this.stringifyForCondition(replacementValue)
|
||||
} else {
|
||||
formattedValue = String(replacementValue)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Standard handling for non-input references
|
||||
|
||||
16
bun.lock
16
bun.lock
@@ -20,7 +20,7 @@
|
||||
"lint-staged": "16.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"turbo": "2.5.4",
|
||||
"turbo": "2.5.5",
|
||||
},
|
||||
},
|
||||
"apps/docs": {
|
||||
@@ -2953,19 +2953,19 @@
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"turbo": ["turbo@2.5.4", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.4", "turbo-darwin-arm64": "2.5.4", "turbo-linux-64": "2.5.4", "turbo-linux-arm64": "2.5.4", "turbo-windows-64": "2.5.4", "turbo-windows-arm64": "2.5.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA=="],
|
||||
"turbo": ["turbo@2.5.5", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.5", "turbo-darwin-arm64": "2.5.5", "turbo-linux-64": "2.5.5", "turbo-linux-arm64": "2.5.5", "turbo-windows-64": "2.5.5", "turbo-windows-arm64": "2.5.5" }, "bin": { "turbo": "bin/turbo" } }, "sha512-eZ7wI6KjtT1eBqCnh2JPXWNUAxtoxxfi6VdBdZFvil0ychCOTxbm7YLRBi1JSt7U3c+u3CLxpoPxLdvr/Npr3A=="],
|
||||
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.5.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ah6YnH2dErojhFooxEzmvsoZQTMImaruZhFPfMKPBq8sb+hALRdvBNLqfc8NWlZq576FkfRZ/MSi4SHvVFT9PQ=="],
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.5.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-RYnTz49u4F5tDD2SUwwtlynABNBAfbyT2uU/brJcyh5k6lDLyNfYKdKmqd3K2ls4AaiALWrFKVSBsiVwhdFNzQ=="],
|
||||
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2+Nx6LAyuXw2MdXb7pxqle3MYignLvS7OwtsP9SgtSBaMlnNlxl9BovzqdYAgkUW3AsYiQMJ/wBRb7d+xemM5A=="],
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Tk+ZeSNdBobZiMw9aFypQt0DlLsWSFWu1ymqsAdJLuPoAH05qCfYtRxE1pJuYHcJB5pqI+/HOxtJoQ40726Btw=="],
|
||||
|
||||
"turbo-linux-64": ["turbo-linux-64@2.5.4", "", { "os": "linux", "cpu": "x64" }, "sha512-5May2kjWbc8w4XxswGAl74GZ5eM4Gr6IiroqdLhXeXyfvWEdm2mFYCSWOzz0/z5cAgqyGidF1jt1qzUR8hTmOA=="],
|
||||
"turbo-linux-64": ["turbo-linux-64@2.5.5", "", { "os": "linux", "cpu": "x64" }, "sha512-2/XvMGykD7VgsvWesZZYIIVXMlgBcQy+ZAryjugoTcvJv8TZzSU/B1nShcA7IAjZ0q7OsZ45uP2cOb8EgKT30w=="],
|
||||
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.5.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-/2yqFaS3TbfxV3P5yG2JUI79P7OUQKOUvAnx4MV9Bdz6jqHsHwc9WZPpO4QseQm+NvmgY6ICORnoVPODxGUiJg=="],
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.5.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-DW+8CjCjybu0d7TFm9dovTTVg1VRnlkZ1rceO4zqsaLrit3DgHnN4to4uwyuf9s2V/BwS3IYcRy+HG9BL596Iw=="],
|
||||
|
||||
"turbo-windows-64": ["turbo-windows-64@2.5.4", "", { "os": "win32", "cpu": "x64" }, "sha512-EQUO4SmaCDhO6zYohxIjJpOKRN3wlfU7jMAj3CgcyTPvQR/UFLEKAYHqJOnJtymbQmiiM/ihX6c6W6Uq0yC7mA=="],
|
||||
"turbo-windows-64": ["turbo-windows-64@2.5.5", "", { "os": "win32", "cpu": "x64" }, "sha512-q5p1BOy8ChtSZfULuF1BhFMYIx6bevXu4fJ+TE/hyNfyHJIfjl90Z6jWdqAlyaFLmn99X/uw+7d6T/Y/dr5JwQ=="],
|
||||
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.5.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-oQ8RrK1VS8lrxkLriotFq+PiF7iiGgkZtfLKF4DDKsmdbPo0O9R2mQxm7jHLuXraRCuIQDWMIw6dpcr7Iykf4A=="],
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.5.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-AXbF1KmpHUq3PKQwddMGoKMYhHsy5t1YBQO8HZ04HLMR0rWv9adYlQ8kaeQJTko1Ay1anOBFTqaxfVOOsu7+1Q=="],
|
||||
|
||||
"type-fest": ["type-fest@0.7.1", "", {}, "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="],
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"lint-staged": "16.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"turbo": "2.5.4"
|
||||
"turbo": "2.5.5"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,json,css,scss}": [
|
||||
|
||||
Reference in New Issue
Block a user