Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions src/utils/modifiers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test'
import { platform } from 'os'

const isDarwin = platform() === 'darwin'

describe('modifiers — modifiers-napi stub fail-closed', () => {
let originalPlatform:PropertyDescriptor | undefined
let requireMock: ReturnType<typeof mock>

beforeEach(() => {
// Stub require('modifiers-napi') to return the reservation-stub shape
// (no exports) that's published on public npm as modifiers-napi@0.0.1.
requireMock = mock((moduleName: string) => {
if (moduleName !== 'modifiers-napi') {
throw new Error(`unexpected require: ${moduleName}`)
}
return {} as { isModifierPressed?: (m: string) => boolean }
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(globalThis as any).require = requireMock
})

afterEach(() => {
mock.restore()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (globalThis as any).require
})

test('isModifierPressed returns false instead of throwing when the native export is missing', async () => {
const originalPlatformDesc = Object.getOwnPropertyDescriptor(process, 'platform')
if (!isDarwin) {
Object.defineProperty(process, 'platform', { value: 'darwin', configurable: true })
}
try {
const { isModifierPressed } = await import('./modifiers.js')
// Must not throw TypeError. Stub has no isModifierPressed export, so the
// guard path runs and yields false (modifier reported as not pressed).
expect(() => isModifierPressed('shift')).not.toThrow()
expect(isModifierPressed('shift')).toBe(false)
expect(isModifierPressed('command')).toBe(false)
} finally {
if (originalPlatformDesc) {
Object.defineProperty(process, 'platform', originalPlatformDesc)
}
}
})

test('isModifierPressed returns false on non-darwin platforms regardless of stub', async () => {
Object.defineProperty(process, 'platform', { value: 'linux', configurable: true })
try {
const { isModifierPressed } = await import('./modifiers.js')
expect(isModifierPressed('shift')).toBe(false)
// The stub require() must not have been consulted on non-darwin.
expect(requireMock).toHaveBeenCalledTimes(0)
} finally {
if (originalPlatform) {
Object.defineProperty(process, 'platform', originalPlatform)
}
}
})
})
14 changes: 12 additions & 2 deletions src/utils/modifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,24 @@ export function prewarmModifiers(): void {

/**
* Check if a specific modifier key is currently pressed (synchronous).
*
* `modifiers-napi` is a reserved stub (`0.0.1`, no exports) on public npm. When
* the native module is unavailable we fail closed: report the modifier as not
* pressed. That disables Apple_Terminal's best-effort Shift+Enter newline
* detection (a nicety) without breaking Enter submit (which the stub otherwise
* crashes on every keystroke).
*/
export function isModifierPressed(modifier: ModifierKey): boolean {
if (process.platform !== 'darwin') {
return false
}
// Dynamic import to avoid loading native module at top level
const { isModifierPressed: nativeIsModifierPressed } =
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('modifiers-napi') as { isModifierPressed: (m: string) => boolean }
require('modifiers-napi') as {
isModifierPressed?: (m: string) => boolean
}
if (typeof nativeIsModifierPressed !== 'function') {
return false
}
return nativeIsModifierPressed(modifier)
}
Loading