mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-30 06:12:06 -07:00
fix: tighten type safety in boot services
- Add AppLifecycleShape and OverlayModalInputStateShape constraints
so TAppLifecycleApp and TOverlayModalInputState generics are bounded
- Remove unsafe `as { handleModalInputStateChange? }` cast — now
directly callable via the constraint
- Use `satisfies AppLifecycleShape` for structural validation on the
appLifecycleApp object literal
- Document Electron App.on incompatibility with simple signatures
This commit is contained in:
@@ -24,7 +24,7 @@ test('createMainBootServices builds boot-phase service bundle', () => {
|
|||||||
{ scope: string; warn: () => void; info: () => void; error: () => void },
|
{ scope: string; warn: () => void; info: () => void; error: () => void },
|
||||||
{ registry: boolean },
|
{ registry: boolean },
|
||||||
{ getModalWindow: () => null },
|
{ getModalWindow: () => null },
|
||||||
{ inputState: boolean },
|
{ inputState: boolean; getModalInputExclusive: () => boolean; handleModalInputStateChange: (isActive: boolean) => void },
|
||||||
{ measurementStore: boolean },
|
{ measurementStore: boolean },
|
||||||
{ modalRuntime: boolean },
|
{ modalRuntime: boolean },
|
||||||
{ mpvSocketPath: string; texthookerPort: number },
|
{ mpvSocketPath: string; texthookerPort: number },
|
||||||
@@ -50,7 +50,7 @@ test('createMainBootServices builds boot-phase service bundle', () => {
|
|||||||
setPathValue = value;
|
setPathValue = value;
|
||||||
},
|
},
|
||||||
quit: () => {},
|
quit: () => {},
|
||||||
on: (event) => {
|
on: (event: string) => {
|
||||||
appOnCalls.push(event);
|
appOnCalls.push(event);
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
@@ -80,7 +80,7 @@ test('createMainBootServices builds boot-phase service bundle', () => {
|
|||||||
createOverlayManager: () => ({
|
createOverlayManager: () => ({
|
||||||
getModalWindow: () => null,
|
getModalWindow: () => null,
|
||||||
}),
|
}),
|
||||||
createOverlayModalInputState: () => ({ inputState: true }),
|
createOverlayModalInputState: () => ({ inputState: true, getModalInputExclusive: () => false, handleModalInputStateChange: () => {} }),
|
||||||
createOverlayContentMeasurementStore: () => ({ measurementStore: true }),
|
createOverlayContentMeasurementStore: () => ({ measurementStore: true }),
|
||||||
getSyncOverlayShortcutsForModal: () => () => {},
|
getSyncOverlayShortcutsForModal: () => () => {},
|
||||||
getSyncOverlayVisibilityForModal: () => () => {},
|
getSyncOverlayVisibilityForModal: () => () => {},
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
import { BrowserWindow } from 'electron';
|
import type { BrowserWindow } from 'electron';
|
||||||
import { ConfigStartupParseError } from '../../config';
|
import { ConfigStartupParseError } from '../../config';
|
||||||
|
|
||||||
|
export interface AppLifecycleShape {
|
||||||
|
requestSingleInstanceLock: () => boolean;
|
||||||
|
quit: () => void;
|
||||||
|
on: (event: string, listener: (...args: unknown[]) => void) => unknown;
|
||||||
|
whenReady: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OverlayModalInputStateShape {
|
||||||
|
getModalInputExclusive: () => boolean;
|
||||||
|
handleModalInputStateChange: (isActive: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MainBootServicesParams<
|
export interface MainBootServicesParams<
|
||||||
TConfigService,
|
TConfigService,
|
||||||
TAnilistTokenStore,
|
TAnilistTokenStore,
|
||||||
@@ -38,7 +50,8 @@ export interface MainBootServicesParams<
|
|||||||
app: {
|
app: {
|
||||||
setPath: (name: string, value: string) => void;
|
setPath: (name: string, value: string) => void;
|
||||||
quit: () => void;
|
quit: () => void;
|
||||||
on: (event: any, listener: (...args: unknown[]) => void) => any;
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- Electron App.on has 50+ overloaded signatures
|
||||||
|
on: Function;
|
||||||
whenReady: () => Promise<void>;
|
whenReady: () => Promise<void>;
|
||||||
};
|
};
|
||||||
shouldBypassSingleInstanceLock: () => boolean;
|
shouldBypassSingleInstanceLock: () => boolean;
|
||||||
@@ -124,11 +137,11 @@ export function createMainBootServices<
|
|||||||
TLogger,
|
TLogger,
|
||||||
TRuntimeRegistry,
|
TRuntimeRegistry,
|
||||||
TOverlayManager extends { getModalWindow: () => BrowserWindow | null },
|
TOverlayManager extends { getModalWindow: () => BrowserWindow | null },
|
||||||
TOverlayModalInputState,
|
TOverlayModalInputState extends OverlayModalInputStateShape,
|
||||||
TOverlayContentMeasurementStore,
|
TOverlayContentMeasurementStore,
|
||||||
TOverlayModalRuntime,
|
TOverlayModalRuntime,
|
||||||
TAppState,
|
TAppState,
|
||||||
TAppLifecycleApp,
|
TAppLifecycleApp extends AppLifecycleShape,
|
||||||
>(
|
>(
|
||||||
params: MainBootServicesParams<
|
params: MainBootServicesParams<
|
||||||
TConfigService,
|
TConfigService,
|
||||||
@@ -212,8 +225,7 @@ export function createMainBootServices<
|
|||||||
overlayManager,
|
overlayManager,
|
||||||
overlayModalInputState,
|
overlayModalInputState,
|
||||||
onModalStateChange: (isActive: boolean) =>
|
onModalStateChange: (isActive: boolean) =>
|
||||||
(overlayModalInputState as { handleModalInputStateChange?: (isActive: boolean) => void })
|
overlayModalInputState.handleModalInputStateChange(isActive),
|
||||||
.handleModalInputStateChange?.(isActive),
|
|
||||||
});
|
});
|
||||||
const appState = params.createAppState({
|
const appState = params.createAppState({
|
||||||
mpvSocketPath: params.getDefaultSocketPath(),
|
mpvSocketPath: params.getDefaultSocketPath(),
|
||||||
@@ -242,7 +254,7 @@ export function createMainBootServices<
|
|||||||
return appLifecycleApp;
|
return appLifecycleApp;
|
||||||
},
|
},
|
||||||
whenReady: () => params.app.whenReady(),
|
whenReady: () => params.app.whenReady(),
|
||||||
} as TAppLifecycleApp;
|
} satisfies AppLifecycleShape as TAppLifecycleApp;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
configDir,
|
configDir,
|
||||||
|
|||||||
Reference in New Issue
Block a user