Skip to content

Commit e3211dd

Browse files
authored
chore(lint): delete unused variables and parameters; enforce both rules repo-wide (#8554)
* chore(lint): remove unused catch bindings, dead private types, and unused loggers * chore(lint): delete unused variables and parameters across apps Remove dead locals, constants, helpers, test mock aliases, and unused function parameters (updating callers), drop the unused BlockConfig type parameter and the response types that only fed it. * improvement(lint): enforce noUnusedVariables and noUnusedFunctionParameters repo-wide Enable both rules as errors at the root with no autofix, so bun run lint never mass-renames bindings to _x, and ignoreRestSiblings so the { a, ...rest } omit idiom stays legal. The packages/** override is now redundant and removed. Document the rule in CLAUDE.md. * docs: drop BlockConfig type argument from contributing example; fix stale run-options note * fix(resume): update the per-context form cache outside the state updater
1 parent 401e039 commit e3211dd

681 files changed

Lines changed: 1001 additions & 4888 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/CONTRIBUTING.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,8 @@ In addition, you will need to update the registries:
354354
// apps/sim/blocks/blocks/pinecone.ts
355355
import { PineconeIcon } from '@/components/icons'
356356
import type { BlockConfig } from '@/blocks/types'
357-
import type { PineconeResponse } from '@/tools/pinecone/types'
358357

359-
export const PineconeBlock: BlockConfig<PineconeResponse> = {
358+
export const PineconeBlock: BlockConfig = {
360359
type: 'pinecone',
361360
name: 'Pinecone',
362361
description: 'Use Pinecone vector database',

‎CLAUDE.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ The `'use client'` server boundary, the app/worker runtime env split, and featur
8888
- **Naming**: components PascalCase (`WorkflowList`); hooks `use*`; files kebab-case (`workflow-list.tsx`); constants SCREAMING_SNAKE_CASE; interfaces PascalCase with a suffix (`WorkflowListProps`); stores `stores/<feature>/store.ts`.
8989
- **Imports**: absolute (`@/...`) only, never relative. A folder with 3+ exports gets an `index.ts` barrel; never re-export from a non-barrel file. `import type` for type-only imports. Order and lazy-loading through barrels: `.claude/rules/sim-imports.md`.
9090
- **TypeScript**: no `any` (use precise types or `unknown` with guards); a props interface for every component; `as const` for constant objects/arrays; explicit ref types (`useRef<HTMLDivElement>(null)`).
91+
- **Unused bindings** fail lint (biome `noUnusedVariables`, `noUnusedFunctionParameters`): delete the dead variable, import, or parameter and update callers; write `catch {}` when the error is unused. Prefix `_` only for a parameter that must hold its position because a later one is used. `const { a, ...rest } = obj` to omit keys is allowed. The rules carry no autofix, so `bun run lint` will not rename anything for you.
9192
- **Components**: `'use client'` only for hooks or browser APIs. Structure order, extraction thresholds, and list-render rules: `.claude/rules/sim-components.md`. Render-performance idioms (lazy-init refs, hoisting, `Map` pre-indexing, `[...arr].sort()` never `toSorted()` on client paths): `.claude/rules/sim-react-performance.md`. For effect/state/memo/callback anti-patterns use the `/you-might-not-need-*` skills and verify against the running UI.
9293
- **State ownership**: React Query owns server data — never `useState` + `fetch`; shareable client view-state (tabs, filters, search, pagination, selected id) lives in the URL via `nuqs`; Zustand owns global client state; `useState` owns UI-only state. Hooks: `.claude/rules/sim-hooks.md`. Stores (`devtools`, `persist` only with an explicit `partialize` whitelist, workflow value invariants): `.claude/rules/sim-stores.md`. URL state: `.claude/rules/sim-url-state.md`.
9394
- **Utils**: inline a helper with one consumer; create `utils.ts` when 2+ files share it — in `lib/` (app-wide) or `feature/utils/` (feature-scoped). Check `lib/` before writing a new one.

‎apps/desktop/src/main/browser-agent/driver.ts‎

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,7 +1738,7 @@ function requireSnapshotForElementAction(): void {
17381738
)
17391739
}
17401740

1741-
function pageTargetForElement(contents: WebContents, elementId: number): PageExecutionTarget {
1741+
function pageTargetForElement(elementId: number): PageExecutionTarget {
17421742
requireSnapshotForElementAction()
17431743
const target = driverScopeState().snapshotTargets.get(elementId)
17441744
if (!target || ('isDestroyed' in target && target.isDestroyed())) {
@@ -2351,7 +2351,7 @@ async function captureSnapshot(
23512351
if (tab.view.webContents !== contents) {
23522352
throw new ToolError('The active tab changed before the snapshot started. Try again.')
23532353
}
2354-
if (elementId !== undefined && pageTargetForElement(contents, elementId) !== contents) {
2354+
if (elementId !== undefined && pageTargetForElement(elementId) !== contents) {
23552355
throw new ToolError(
23562356
'Scoped snapshots require a top-page element. Omit elementId to capture framed content.'
23572357
)
@@ -2745,7 +2745,7 @@ async function executeToolInner(
27452745
const contents = session.requireAutomationTab().view.webContents
27462746
const elementId = requireNum(params, 'elementId')
27472747
const paths = uploadPaths(params)
2748-
const target = pageTargetForElement(contents, elementId)
2748+
const target = pageTargetForElement(elementId)
27492749
assertCurrentExecution()
27502750
const frame = 'getURL' in target ? target.mainFrame : target
27512751
const expression = `(${String(resolveFileInputTarget)})(${elementId})`
@@ -2816,8 +2816,7 @@ async function executeToolInner(
28162816
}
28172817
const waitedTab = session.requireAutomationTab()
28182818
const contents = waitedTab.view.webContents
2819-
const elementTarget =
2820-
elementId === undefined ? undefined : pageTargetForElement(contents, elementId)
2819+
const elementTarget = elementId === undefined ? undefined : pageTargetForElement(elementId)
28212820
if (elementTarget && elementTarget !== contents) {
28222821
throw new ToolError(
28232822
'Element-state waits are limited to the top page. Use a text or URL condition for framed content.'
@@ -2946,7 +2945,7 @@ async function executeToolInner(
29462945
const contents = session.requireAutomationTab().view.webContents
29472946
const elementId = num(params, 'elementId')
29482947
if (elementId === undefined) return await readWholePageText(contents, executionDeadline)
2949-
const target = pageTargetForElement(contents, elementId)
2948+
const target = pageTargetForElement(elementId)
29502949
return unwrapPageResult(
29512950
await execInPage(target, readPageText, [elementId], false, executionDeadline)
29522951
)
@@ -2961,7 +2960,7 @@ async function executeToolInner(
29612960
const elementId = num(params, 'elementId')
29622961
let elementClip: Record<string, unknown> | undefined
29632962
if (elementId !== undefined) {
2964-
const target = pageTargetForElement(contents, elementId)
2963+
const target = pageTargetForElement(elementId)
29652964
if (target !== contents) {
29662965
throw new ToolError(
29672966
'Element screenshots are limited to the top page. Use browser_screenshot without elementId for framed content.'
@@ -3119,7 +3118,7 @@ async function executeToolInner(
31193118
const contents = clickedTab.view.webContents
31203119
const elementId = requireNum(params, 'elementId')
31213120
const click = pointerClick(params)
3122-
const target = pageTargetForElement(contents, elementId)
3121+
const target = pageTargetForElement(elementId)
31233122
const targetFrame = frameExecutionTarget(target, contents)
31243123
let trusted = false
31253124
let activation = 'synthetic-pointer'
@@ -3596,7 +3595,7 @@ async function executeToolInner(
35963595
let stoppedIndex = 0
35973596
let dispatchStarted = false
35983597
const readField = async (field: FormField) => {
3599-
const target = pageTargetForElement(contents, field.elementId)
3598+
const target = pageTargetForElement(field.elementId)
36003599
if (target !== contents)
36013600
throw new ToolError(
36023601
'Form batches require top-page fields; use individual tools for framed fields.'
@@ -3757,7 +3756,7 @@ async function executeToolInner(
37573756
if (typeof text !== 'string') throw new ToolError('Missing required parameter "text"')
37583757
const submit = params.submit === true
37593758
const contents = session.requireAutomationTab().view.webContents
3760-
const target = pageTargetForElement(contents, elementId)
3759+
const target = pageTargetForElement(elementId)
37613760
const targetFrame = frameExecutionTarget(target, contents)
37623761

37633762
// Native path: focus + select current content, then insert through the
@@ -4307,9 +4306,7 @@ async function executeToolInner(
43074306
const contents = session.requireAutomationTab().view.webContents
43084307
const elementId = num(params, 'elementId')
43094308
const target =
4310-
elementId !== undefined
4311-
? pageTargetForElement(contents, elementId)
4312-
: focusedPageTarget(contents)
4309+
elementId !== undefined ? pageTargetForElement(elementId) : focusedPageTarget(contents)
43134310
const targetFrame = frameExecutionTarget(target, contents)
43144311
assertCurrentExecution()
43154312
if (elementId !== undefined) assertElementActionCurrent(contents, elementId, target)
@@ -4355,7 +4352,7 @@ async function executeToolInner(
43554352
const selection = values === undefined ? requireStr(params, 'value') : (values as string[])
43564353
const contents = session.requireAutomationTab().view.webContents
43574354
const elementId = requireNum(params, 'elementId')
4358-
const target = pageTargetForElement(contents, elementId)
4355+
const target = pageTargetForElement(elementId)
43594356
const targetFrame = frameExecutionTarget(target, contents)
43604357
assertCurrentExecution()
43614358
assertElementActionCurrent(contents, elementId, target)
@@ -4434,8 +4431,7 @@ async function executeToolInner(
44344431
throw new ToolError('Missing required boolean parameter "checked"')
44354432
}
44364433
const checked = params.checked
4437-
const contents = session.requireAutomationTab().view.webContents
4438-
const target = pageTargetForElement(contents, elementId)
4434+
const target = pageTargetForElement(elementId)
44394435
const before = toRecord(
44404436
unwrapPageResult(
44414437
await execInPage(target, readCheckableElementState, [elementId], false, executionDeadline)
@@ -4562,7 +4558,7 @@ async function executeToolInner(
45624558
)
45634559
}
45644560
const elementId = requireNum(params, 'elementId')
4565-
const target = pageTargetForElement(contents, elementId)
4561+
const target = pageTargetForElement(elementId)
45664562
const targetFrame = frameExecutionTarget(target, contents)
45674563
let beforePage = await pageActionState(target, true, elementId)
45684564
let beforeElement = await activeElementState(target)
@@ -4939,7 +4935,7 @@ async function executeToolInner(
49394935
): Promise<{ x: number; y: number; element?: string }> => {
49404936
const elementId = num(params, `${which}ElementId`)
49414937
if (elementId !== undefined) {
4942-
const target = pageTargetForElement(contents, elementId)
4938+
const target = pageTargetForElement(elementId)
49434939
if (frameExecutionTarget(target, contents)) {
49444940
throw new ToolError(
49454941
`Dragging elements inside embedded frames is not supported. Use ${which}X/${which}Y viewport coordinates instead.`

‎apps/desktop/src/main/browser-credentials/os-auth.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ async function promptForSecret(reason: string, action: string): Promise<boolean>
147147
? await showShellDialog(parent, options)
148148
: await showShellDialog(options)
149149
return response === 1
150-
} catch (error) {
150+
} catch {
151151
// Fail closed: if the confirmation cannot be shown, nothing is revealed.
152152
logger.warn('Could not present the credential confirmation')
153153
return false

‎apps/sim/app/(interfaces)/resume/[workflowId]/[executionId]/resume-page-client.tsx‎

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,7 @@ export default function ResumeExecutionPage({
208208
const queuePosition = selectedDetail?.pausePoint.queuePosition
209209
const resumeInputsRef = useRef<Record<string, string>>({})
210210
const [resumeInput, setResumeInput] = useState('')
211-
const [formValuesByContext, setFormValuesByContext] = useState<
212-
Record<string, Record<string, string>>
213-
>({})
211+
const formValuesByContextRef = useRef<Record<string, Record<string, string>>>({})
214212
const [formValues, setFormValues] = useState<Record<string, string>>({})
215213
const [formErrors, setFormErrors] = useState<Record<string, string>>({})
216214
const [loadingAction, setLoadingAction] = useState(false)
@@ -308,17 +306,6 @@ export default function ResumeExecutionPage({
308306
[formatValueForInputField]
309307
)
310308

311-
const formatStructureValue = useCallback((value: any): string => {
312-
if (value === null || value === undefined) return '—'
313-
if (typeof value === 'string') return value
314-
if (typeof value === 'number' || typeof value === 'boolean') return String(value)
315-
try {
316-
return JSON.stringify(value, null, 2)
317-
} catch {
318-
return String(value)
319-
}
320-
}, [])
321-
322309
const parseFormValue = useCallback(
323310
(field: NormalizedInputField, rawValue: string): { value: any; error?: string } => {
324311
const value = rawValue ?? ''
@@ -358,11 +345,12 @@ export default function ResumeExecutionPage({
358345
const handleFormFieldChange = useCallback(
359346
(fieldName: string, newValue: string) => {
360347
if (!selectedContextId) return
361-
setFormValues((prev) => {
362-
const updated = { ...prev, [fieldName]: newValue }
363-
setFormValuesByContext((map) => ({ ...map, [selectedContextId]: updated }))
364-
return updated
365-
})
348+
const updated = {
349+
...formValuesByContextRef.current[selectedContextId],
350+
[fieldName]: newValue,
351+
}
352+
formValuesByContextRef.current[selectedContextId] = updated
353+
setFormValues(updated)
366354
setFormErrors((prev) => {
367355
if (!prev[fieldName]) return prev
368356
const { [fieldName]: _, ...rest } = prev
@@ -516,12 +504,9 @@ export default function ResumeExecutionPage({
516504
: undefined
517505
if (operation === 'human' && fetchedInputFields.length > 0) {
518506
const baseValues = buildInitialFormValues(fetchedInputFields, submission)
519-
let mergedValues = baseValues
520-
setFormValuesByContext((prev) => {
521-
const existingValues = prev[detail.pausePoint.contextId]
522-
if (existingValues) mergedValues = { ...baseValues, ...existingValues }
523-
return { ...prev, [detail.pausePoint.contextId]: mergedValues }
524-
})
507+
const existingValues = formValuesByContextRef.current[detail.pausePoint.contextId]
508+
const mergedValues = existingValues ? { ...baseValues, ...existingValues } : baseValues
509+
formValuesByContextRef.current[detail.pausePoint.contextId] = mergedValues
525510
setFormValues(mergedValues)
526511
setFormErrors({})
527512
if (resumeInputsRef.current[detail.pausePoint.contextId] !== undefined) {

‎apps/sim/app/api/auth/sso/register/route.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ describe('POST /api/auth/sso/register', () => {
374374

375375
it('does not SSRF-validate userInfoEndpoint when skipUserInfoEndpoint is requested', async () => {
376376
queueMembers([{ organizationId: 'org1', role: 'owner' }])
377-
mockValidateUrlWithDNS.mockImplementation(async (url: string, label: string) => {
377+
mockValidateUrlWithDNS.mockImplementation(async (_url: string, label: string) => {
378378
if (label === 'OIDC userInfoEndpoint') {
379379
return { isValid: false, error: 'resolves to a private IP address' }
380380
}

‎apps/sim/app/api/chat/[identifier]/otp/route.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ async function deliverOtp(requestId: string, deploymentId: string, title: string
6060
const otp = generateOTP()
6161
await storeOTP('chat', deploymentId, email, otp)
6262

63-
const emailHtml = await renderOTPEmail(otp, email, 'email-verification', title)
63+
const emailHtml = await renderOTPEmail(otp, 'email-verification', title)
6464
const emailResult = await sendEmail({
6565
to: email,
6666
subject: getOtpSubject(title),

‎apps/sim/app/api/copilot/feedback/route.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
117117
* GET /api/copilot/feedback
118118
* Get feedback records for the authenticated user
119119
*/
120-
export const GET = withRouteHandler(async (req: NextRequest) => {
120+
export const GET = withRouteHandler(async () => {
121121
const tracker = createRequestTracker()
122122

123123
try {

‎apps/sim/app/api/environment/route.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export const POST = withRouteHandler(async (req: NextRequest) => {
115115
}
116116
})
117117

118-
export const GET = withRouteHandler(async (request: Request) => {
118+
export const GET = withRouteHandler(async () => {
119119
const requestId = generateRequestId()
120120

121121
try {

‎apps/sim/app/api/files/authorization.test.ts‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ describe('verifyKBFileWriteAccess (binding-only delete authorization)', () => {
142142

143143
describe('public-context access (profile-pictures / og-images / workspace-logos)', () => {
144144
function write(cloudKey: string, context: 'profile-pictures' | 'og-images' | 'workspace-logos') {
145-
return verifyFileAccess(cloudKey, USER_ID, undefined, context, false, { requireWrite: true })
145+
return verifyFileAccess(cloudKey, USER_ID, undefined, context, { requireWrite: true })
146146
}
147147

148148
it('allows organization logo reads and denies generic deletes even for the uploader', async () => {
@@ -152,10 +152,10 @@ describe('public-context access (profile-pictures / og-images / workspace-logos)
152152
true
153153
)
154154
await expect(
155-
verifyFileAccess(key, USER_ID, undefined, 'organization-logos', false, { requireWrite: true })
155+
verifyFileAccess(key, USER_ID, undefined, 'organization-logos', { requireWrite: true })
156156
).resolves.toBe(false)
157157
await expect(
158-
verifyFileAccess(key, USER_ID, undefined, 'general', false, { requireWrite: true })
158+
verifyFileAccess(key, USER_ID, undefined, 'general', { requireWrite: true })
159159
).resolves.toBe(false)
160160
expect(mockGetFileMetadata).not.toHaveBeenCalled()
161161
expect(mockGetUserEntityPermissions).not.toHaveBeenCalled()
@@ -203,7 +203,7 @@ describe('workspace-scoped access (workspace files and mothership attachments)',
203203
})
204204

205205
function read(cloudKey: string, context: 'workspace' | 'mothership') {
206-
return verifyFileAccess(cloudKey, USER_ID, undefined, context, false)
206+
return verifyFileAccess(cloudKey, USER_ID, undefined, context)
207207
}
208208

209209
interface BoundRow {
@@ -315,7 +315,7 @@ describe('organization connector cache access', () => {
315315
'denies the uploader a raw download even with a forged %s context',
316316
async (context) => {
317317
await expect(
318-
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, context, false, { knowledgeAccess: 'user' })
318+
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, context, { knowledgeAccess: 'user' })
319319
).resolves.toBe(false)
320320
expect(mockGetFileMetadata).not.toHaveBeenCalled()
321321
expect(mockGetUserEntityPermissions).not.toHaveBeenCalled()
@@ -325,7 +325,7 @@ describe('organization connector cache access', () => {
325325
it('denies system reads after the cache loses its active document reference', async () => {
326326
dbChainMockFns.limit.mockResolvedValue([])
327327
await expect(
328-
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', false, {
328+
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', {
329329
knowledgeAccess: SYSTEM_ACCESS_SCOPE,
330330
})
331331
).resolves.toBe(false)
@@ -338,7 +338,7 @@ describe('organization connector cache access', () => {
338338
deletedAt: null,
339339
})
340340
await expect(
341-
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', false, {
341+
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', {
342342
knowledgeAccess: SYSTEM_ACCESS_SCOPE,
343343
})
344344
).resolves.toBe(false)
@@ -347,7 +347,7 @@ describe('organization connector cache access', () => {
347347

348348
it('does not let a raw download endpoint delete organization caches', async () => {
349349
await expect(
350-
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'general', false, {
350+
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'general', {
351351
requireWrite: true,
352352
knowledgeAccess: SYSTEM_ACCESS_SCOPE,
353353
})
@@ -378,7 +378,7 @@ describe('KB file live source authorization', () => {
378378
queueTableRows(schemaMock.knowledgeConnector, [{ connectorId: 'confluence-source' }])
379379
queueTableRows(schemaMock.document, allowed ? [{ id: 'doc-1' }] : [])
380380
await expect(
381-
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', false, {
381+
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', {
382382
knowledgeAccess: access,
383383
})
384384
).resolves.toBe(allowed)
@@ -415,7 +415,7 @@ describe('KB file live source authorization', () => {
415415
mockGetUserEntityPermissions.mockResolvedValue(null)
416416
const get = vi.fn()
417417
await expect(
418-
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', false, {
418+
verifyFileAccess(CLOUD_KEY, USER_ID, undefined, 'knowledge-base', {
419419
knowledgeAccess: { get, getForConnectors: vi.fn(), getForDocuments: vi.fn() },
420420
})
421421
).resolves.toBe(false)

0 commit comments

Comments
 (0)